1
0
Fork 0

Char/Misc driver patches for 5.5-rc1

Here is the big set of char/misc and other driver patches for 5.5-rc1
 
 Loads of different things in here, this feels like the catch-all of
 driver subsystems these days.  Full details are in the shortlog, but
 nothing major overall, just lots of driver updates and additions.
 
 All of these have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXd6ewA8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ymNXACfebVkDrFOH9EqDgFArPvZ1i9EmZ4AoLbE1Wki
 ftJApk+Ov1BT2TvClOza
 =cXqg
 -----END PGP SIGNATURE-----

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

Pull char/misc driver updates from Greg KH:
 "Here is the big set of char/misc and other driver patches for 5.5-rc1

  Loads of different things in here, this feels like the catch-all of
  driver subsystems these days. Full details are in the shortlog, but
  nothing major overall, just lots of driver updates and additions.

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

* tag 'char-misc-5.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (198 commits)
  char: Fix Kconfig indentation, continued
  habanalabs: add more protection of device during reset
  habanalabs: flush EQ workers in hard reset
  habanalabs: make the reset code more consistent
  habanalabs: expose reset counters via existing INFO IOCTL
  habanalabs: make code more concise
  habanalabs: use defines for F/W files
  habanalabs: remove prints on successful device initialization
  habanalabs: remove unnecessary checks
  habanalabs: invalidate MMU cache only once
  habanalabs: skip VA block list update in reset flow
  habanalabs: optimize MMU unmap
  habanalabs: prevent read/write from/to the device during hard reset
  habanalabs: split MMU properties to PCI/DRAM
  habanalabs: re-factor MMU masks and documentation
  habanalabs: type specific MMU cache invalidation
  habanalabs: re-factor memory module code
  habanalabs: export uapi defines to user-space
  habanalabs: don't print error when queues are full
  habanalabs: increase max jobs number to 512
  ...
alistair/sunxi64-5.5-dsi
Linus Torvalds 2019-11-27 10:53:50 -08:00
commit 8f56e4ebe0
192 changed files with 11032 additions and 1716 deletions

View File

@ -1,25 +1,25 @@
What: /sys/bus/platform/devices/fsi-master/rescan
What: /sys/bus/platform/devices/../fsi-master/fsi0/rescan
Date: May 2017
KernelVersion: 4.12
Contact: cbostic@linux.vnet.ibm.com
Contact: linux-fsi@lists.ozlabs.org
Description:
Initiates a FSI master scan for all connected slave devices
on its links.
What: /sys/bus/platform/devices/fsi-master/break
What: /sys/bus/platform/devices/../fsi-master/fsi0/break
Date: May 2017
KernelVersion: 4.12
Contact: cbostic@linux.vnet.ibm.com
Contact: linux-fsi@lists.ozlabs.org
Description:
Sends an FSI BREAK command on a master's communication
link to any connnected slaves. A BREAK resets connected
device's logic and preps it to receive further commands
from the master.
What: /sys/bus/platform/devices/fsi-master/slave@00:00/term
What: /sys/bus/platform/devices/../fsi-master/fsi0/slave@00:00/term
Date: May 2017
KernelVersion: 4.12
Contact: cbostic@linux.vnet.ibm.com
Contact: linux-fsi@lists.ozlabs.org
Description:
Sends an FSI terminate command from the master to its
connected slave. A terminate resets the slave's state machines
@ -29,10 +29,10 @@ Description:
ongoing operation in case of an expired 'Master Time Out'
timer.
What: /sys/bus/platform/devices/fsi-master/slave@00:00/raw
What: /sys/bus/platform/devices/../fsi-master/fsi0/slave@00:00/raw
Date: May 2017
KernelVersion: 4.12
Contact: cbostic@linux.vnet.ibm.com
Contact: linux-fsi@lists.ozlabs.org
Description:
Provides a means of reading/writing a 32 bit value from/to a
specified FSI bus address.

View File

@ -4,7 +4,7 @@ KernelVersion: 3.10
Contact: Samuel Ortiz <sameo@linux.intel.com>
linux-mei@linux.intel.com
Description: Stores the same MODALIAS value emitted by uevent
Format: mei:<mei device name>:<device uuid>:
Format: mei:<mei device name>:<device uuid>:<protocol version>
What: /sys/bus/mei/devices/.../name
Date: May 2015
@ -26,3 +26,24 @@ KernelVersion: 4.3
Contact: Tomas Winkler <tomas.winkler@intel.com>
Description: Stores mei client protocol version
Format: %d
What: /sys/bus/mei/devices/.../max_conn
Date: Nov 2019
KernelVersion: 5.5
Contact: Tomas Winkler <tomas.winkler@intel.com>
Description: Stores mei client maximum number of connections
Format: %d
What: /sys/bus/mei/devices/.../fixed
Date: Nov 2019
KernelVersion: 5.5
Contact: Tomas Winkler <tomas.winkler@intel.com>
Description: Stores mei client fixed address, if any
Format: %d
What: /sys/bus/mei/devices/.../max_len
Date: Nov 2019
KernelVersion: 5.5
Contact: Tomas Winkler <tomas.winkler@intel.com>
Description: Stores mei client maximum message length
Format: %d

View File

@ -80,6 +80,14 @@ Contact: thunderbolt-software@lists.01.org
Description: This attribute contains 1 if Thunderbolt device was already
authorized on boot and 0 otherwise.
What: /sys/bus/thunderbolt/devices/.../generation
Date: Jan 2020
KernelVersion: 5.5
Contact: Christian Kellner <christian@kellner.me>
Description: This attribute contains the generation of the Thunderbolt
controller associated with the device. It will contain 4
for USB4.
What: /sys/bus/thunderbolt/devices/.../key
Date: Sep 2017
KernelVersion: 4.13
@ -104,6 +112,34 @@ Contact: thunderbolt-software@lists.01.org
Description: This attribute contains name of this device extracted from
the device DROM.
What: /sys/bus/thunderbolt/devices/.../rx_speed
Date: Jan 2020
KernelVersion: 5.5
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
Description: This attribute reports the device RX speed per lane.
All RX lanes run at the same speed.
What: /sys/bus/thunderbolt/devices/.../rx_lanes
Date: Jan 2020
KernelVersion: 5.5
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
Description: This attribute reports number of RX lanes the device is
using simultaneusly through its upstream port.
What: /sys/bus/thunderbolt/devices/.../tx_speed
Date: Jan 2020
KernelVersion: 5.5
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
Description: This attribute reports the TX speed per lane.
All TX lanes run at the same speed.
What: /sys/bus/thunderbolt/devices/.../tx_lanes
Date: Jan 2020
KernelVersion: 5.5
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
Description: This attribute reports number of TX lanes the device is
using simultaneusly through its upstream port.
What: /sys/bus/thunderbolt/devices/.../vendor
Date: Sep 2017
KernelVersion: 4.13

View File

@ -80,3 +80,13 @@ Description: Display the ME device state.
DISABLED
POWER_DOWN
POWER_UP
What: /sys/class/mei/meiN/trc
Date: Nov 2019
KernelVersion: 5.5
Contact: Tomas Winkler <tomas.winkler@intel.com>
Description: Display trc status register content
The ME FW writes Glitch Detection HW (TRC)
status information into trc status register
for BIOS and OS to monitor fw health.

View File

@ -106,3 +106,135 @@ KernelVersion: 5.4
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-only. Read this file to get the second error detected by
hardware.
What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/name
Date: October 2019
KernelVersion: 5.5
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-Only. Read this file to get the name of hwmon device, it
supports values:
'dfl_fme_thermal' - thermal hwmon device name
'dfl_fme_power' - power hwmon device name
What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_input
Date: October 2019
KernelVersion: 5.5
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-Only. It returns FPGA device temperature in millidegrees
Celsius.
What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_max
Date: October 2019
KernelVersion: 5.5
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-Only. It returns hardware threshold1 temperature in
millidegrees Celsius. If temperature rises at or above this
threshold, hardware starts 50% or 90% throttling (see
'temp1_max_policy').
What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_crit
Date: October 2019
KernelVersion: 5.5
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-Only. It returns hardware threshold2 temperature in
millidegrees Celsius. If temperature rises at or above this
threshold, hardware starts 100% throttling.
What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_emergency
Date: October 2019
KernelVersion: 5.5
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-Only. It returns hardware trip threshold temperature in
millidegrees Celsius. If temperature rises at or above this
threshold, a fatal event will be triggered to board management
controller (BMC) to shutdown FPGA.
What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_max_alarm
Date: October 2019
KernelVersion: 5.5
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-only. It returns 1 if temperature is currently at or above
hardware threshold1 (see 'temp1_max'), otherwise 0.
What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_crit_alarm
Date: October 2019
KernelVersion: 5.5
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-only. It returns 1 if temperature is currently at or above
hardware threshold2 (see 'temp1_crit'), otherwise 0.
What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_max_policy
Date: October 2019
KernelVersion: 5.5
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-Only. Read this file to get the policy of hardware threshold1
(see 'temp1_max'). It only supports two values (policies):
0 - AP2 state (90% throttling)
1 - AP1 state (50% throttling)
What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_input
Date: October 2019
KernelVersion: 5.5
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-Only. It returns current FPGA power consumption in uW.
What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_max
Date: October 2019
KernelVersion: 5.5
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-Write. Read this file to get current hardware power
threshold1 in uW. If power consumption rises at or above
this threshold, hardware starts 50% throttling.
Write this file to set current hardware power threshold1 in uW.
As hardware only accepts values in Watts, so input value will
be round down per Watts (< 1 watts part will be discarded) and
clamped within the range from 0 to 127 Watts. Write fails with
-EINVAL if input parsing fails.
What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_crit
Date: October 2019
KernelVersion: 5.5
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-Write. Read this file to get current hardware power
threshold2 in uW. If power consumption rises at or above
this threshold, hardware starts 90% throttling.
Write this file to set current hardware power threshold2 in uW.
As hardware only accepts values in Watts, so input value will
be round down per Watts (< 1 watts part will be discarded) and
clamped within the range from 0 to 127 Watts. Write fails with
-EINVAL if input parsing fails.
What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_max_alarm
Date: October 2019
KernelVersion: 5.5
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-only. It returns 1 if power consumption is currently at or
above hardware threshold1 (see 'power1_max'), otherwise 0.
What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_crit_alarm
Date: October 2019
KernelVersion: 5.5
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-only. It returns 1 if power consumption is currently at or
above hardware threshold2 (see 'power1_crit'), otherwise 0.
What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_xeon_limit
Date: October 2019
KernelVersion: 5.5
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-Only. It returns power limit for XEON in uW.
What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_fpga_limit
Date: October 2019
KernelVersion: 5.5
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-Only. It returns power limit for FPGA in uW.
What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_ltr
Date: October 2019
KernelVersion: 5.5
Contact: Wu Hao <hao.wu@intel.com>
Description: Read-only. Read this file to get current Latency Tolerance
Reporting (ltr) value. It returns 1 if all Accelerated
Function Units (AFUs) can tolerate latency >= 40us for memory
access or 0 if any AFU is latency sensitive (< 40us).

View File

@ -87,6 +87,15 @@ its hardware characteristcs.
* port or ports: see "Graph bindings for Coresight" below.
* Optional properties for all components:
* arm,coresight-loses-context-with-cpu : boolean. Indicates that the
hardware will lose register context on CPU power down (e.g. CPUIdle).
An example of where this may be needed are systems which contain a
coresight component and CPU in the same power domain. When the CPU
powers down the coresight component also powers down and loses its
context. This property is currently only used for the ETM 4.x driver.
* Optional properties for ETM/PTMs:
* arm,cp14: must be present if the system accesses ETM/PTM management

View File

@ -0,0 +1,24 @@
Device-tree bindings for AST2600 FSI master
-------------------------------------------
The AST2600 contains two identical FSI masters. They share a clock and have a
separate interrupt line and output pins.
Required properties:
- compatible: "aspeed,ast2600-fsi-master"
- reg: base address and length
- clocks: phandle and clock number
- interrupts: platform dependent interrupt description
- pinctrl-0: phandle to pinctrl node
- pinctrl-names: pinctrl state
Examples:
fsi-master {
compatible = "aspeed,ast2600-fsi-master", "fsi-master";
reg = <0x1e79b000 0x94>;
interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_fsi1_default>;
clocks = <&syscon ASPEED_CLK_GATE_FSICLK>;
};

View File

@ -0,0 +1,62 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interconnect/qcom,msm8974.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm MSM8974 Network-On-Chip Interconnect
maintainers:
- Brian Masney <masneyb@onstation.org>
description: |
The Qualcomm MSM8974 interconnect providers support setting system
bandwidth requirements between various network-on-chip fabrics.
properties:
reg:
maxItems: 1
compatible:
enum:
- qcom,msm8974-bimc
- qcom,msm8974-cnoc
- qcom,msm8974-mmssnoc
- qcom,msm8974-ocmemnoc
- qcom,msm8974-pnoc
- qcom,msm8974-snoc
'#interconnect-cells':
const: 1
clock-names:
items:
- const: bus
- const: bus_a
clocks:
items:
- description: Bus Clock
- description: Bus A Clock
required:
- compatible
- reg
- '#interconnect-cells'
- clock-names
- clocks
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,rpmcc.h>
bimc: interconnect@fc380000 {
reg = <0xfc380000 0x6a000>;
compatible = "qcom,msm8974-bimc";
#interconnect-cells = <1>;
clock-names = "bus", "bus_a";
clocks = <&rpmcc RPM_SMD_BIMC_CLK>,
<&rpmcc RPM_SMD_BIMC_A_CLK>;
};

View File

@ -0,0 +1,25 @@
Rockchip internal OTP (One Time Programmable) memory device tree bindings
Required properties:
- compatible: Should be one of the following.
- "rockchip,px30-otp" - for PX30 SoCs.
- "rockchip,rk3308-otp" - for RK3308 SoCs.
- reg: Should contain the registers location and size
- clocks: Must contain an entry for each entry in clock-names.
- clock-names: Should be "otp", "apb_pclk" and "phy".
- resets: Must contain an entry for each entry in reset-names.
See ../../reset/reset.txt for details.
- reset-names: Should be "phy".
See nvmem.txt for more information.
Example:
otp: otp@ff290000 {
compatible = "rockchip,px30-otp";
reg = <0x0 0xff290000 0x0 0x4000>;
#address-cells = <1>;
#size-cells = <1>;
clocks = <&cru SCLK_OTP_USR>, <&cru PCLK_OTP_NS>,
<&cru PCLK_OTP_PHY>;
clock-names = "otp", "apb_pclk", "phy";
};

View File

@ -0,0 +1,39 @@
= 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";
};

View File

@ -0,0 +1,47 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
# Copyright 2019 Ondrej Jirman <megous@megous.com>
%YAML 1.2
---
$id: "http://devicetree.org/schemas/phy/allwinner,sun50i-h6-usb3-phy.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Allwinner H6 USB3 PHY
maintainers:
- Ondrej Jirman <megous@megous.com>
properties:
compatible:
enum:
- allwinner,sun50i-h6-usb3-phy
reg:
maxItems: 1
clocks:
maxItems: 1
resets:
maxItems: 1
"#phy-cells":
const: 0
required:
- compatible
- reg
- clocks
- resets
- "#phy-cells"
examples:
- |
#include <dt-bindings/clock/sun50i-h6-ccu.h>
#include <dt-bindings/reset/sun50i-h6-ccu.h>
phy@5210000 {
compatible = "allwinner,sun50i-h6-usb3-phy";
reg = <0x5210000 0x10000>;
clocks = <&ccu CLK_USB_PHY1>;
resets = <&ccu RST_USB_PHY1>;
#phy-cells = <0>;
};

View File

@ -2,6 +2,7 @@ ROCKCHIP USB2.0 PHY WITH INNO IP BLOCK
Required properties (phy (parent) node):
- compatible : should be one of the listed compatibles:
* "rockchip,px30-usb2phy"
* "rockchip,rk3228-usb2phy"
* "rockchip,rk3328-usb2phy"
* "rockchip,rk3366-usb2phy"

View File

@ -14,7 +14,8 @@ Required properties:
"qcom,msm8998-qmp-pcie-phy" for PCIe QMP phy on msm8998,
"qcom,sdm845-qmp-usb3-phy" for USB3 QMP V3 phy on sdm845,
"qcom,sdm845-qmp-usb3-uni-phy" for USB3 QMP V3 UNI phy on sdm845,
"qcom,sdm845-qmp-ufs-phy" for UFS QMP phy on sdm845.
"qcom,sdm845-qmp-ufs-phy" for UFS QMP phy on sdm845,
"qcom,sm8150-qmp-ufs-phy" for UFS QMP phy on sm8150.
- reg:
- index 0: address and length of register set for PHY's common
@ -57,6 +58,8 @@ Required properties:
"aux", "cfg_ahb", "ref", "com_aux".
For "qcom,sdm845-qmp-ufs-phy" must contain:
"ref", "ref_aux".
For "qcom,sm8150-qmp-ufs-phy" must contain:
"ref", "ref_aux".
- resets: a list of phandles and reset controller specifier pairs,
one for each entry in reset-names.
@ -83,6 +86,8 @@ Required properties:
"phy", "common".
For "qcom,sdm845-qmp-ufs-phy": must contain:
"ufsphy".
For "qcom,sm8150-qmp-ufs-phy": must contain:
"ufsphy".
- vdda-phy-supply: Phandle to a regulator supply to PHY core block.
- vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.

View File

@ -0,0 +1,75 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/rockchip,px30-dsi-dphy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Rockchip MIPI DPHY with additional LVDS/TTL modes
maintainers:
- Heiko Stuebner <heiko@sntech.de>
properties:
"#phy-cells":
const: 0
"#clock-cells":
const: 0
compatible:
enum:
- rockchip,px30-dsi-dphy
- rockchip,rk3128-dsi-dphy
- rockchip,rk3368-dsi-dphy
reg:
maxItems: 1
clocks:
items:
- description: PLL reference clock
- description: Module clock
clock-names:
items:
- const: ref
- const: pclk
power-domains:
maxItems: 1
description: phandle to the associated power domain
resets:
items:
- description: exclusive PHY reset line
reset-names:
items:
- const: apb
required:
- "#phy-cells"
- "#clock-cells"
- compatible
- reg
- clocks
- clock-names
- resets
- reset-names
additionalProperties: false
examples:
- |
dsi_dphy: phy@ff2e0000 {
compatible = "rockchip,px30-video-phy";
reg = <0x0 0xff2e0000 0x0 0x10000>;
clocks = <&pmucru 13>, <&cru 12>;
clock-names = "ref", "pclk";
#clock-cells = <0>;
resets = <&cru 12>;
reset-names = "apb";
#phy-cells = <0>;
};
...

View File

@ -108,6 +108,16 @@ More functions are exposed through sysfs
error reporting sysfs interfaces allow user to read errors detected by the
hardware, and clear the logged errors.
Power management (dfl_fme_power hwmon)
power management hwmon sysfs interfaces allow user to read power management
information (power consumption, thresholds, threshold status, limits, etc.)
and configure power thresholds for different throttling levels.
Thermal management (dfl_fme_thermal hwmon)
thermal management hwmon sysfs interfaces allow user to read thermal
management information (current temperature, thresholds, threshold status,
etc.).
FIU - PORT
==========

View File

@ -44,7 +44,8 @@ Documentation/trace/stm.rst for more information on that.
MSU can be configured to collect trace data into a system memory
buffer, which can later on be read from its device nodes via read() or
mmap() interface.
mmap() interface and directed to a "software sink" driver that will
consume the data and/or relay it further.
On the whole, Intel(R) Trace Hub does not require any special
userspace software to function; everything can be configured, started
@ -122,3 +123,28 @@ In order to enable the host mode, set the 'host_mode' parameter of the
will show up on the intel_th bus. Also, trace configuration and
capture controlling attribute groups of the 'gth' device will not be
exposed. The 'sth' device will operate as usual.
Software Sinks
--------------
The Memory Storage Unit (MSU) driver provides an in-kernel API for
drivers to register themselves as software sinks for the trace data.
Such drivers can further export the data via other devices, such as
USB device controllers or network cards.
The API has two main parts::
- notifying the software sink that a particular window is full, and
"locking" that window, that is, making it unavailable for the trace
collection; when this happens, the MSU driver will automatically
switch to the next window in the buffer if it is unlocked, or stop
the trace capture if it's not;
- tracking the "locked" state of windows and providing a way for the
software sink driver to notify the MSU driver when a window is
unlocked and can be used again to collect trace data.
An example sink driver, msu-sink illustrates the implementation of a
software sink. Functionally, it simply unlocks windows as soon as they
are full, keeping the MSU running in a circular buffer mode. Unlike the
"multi" mode, it will fill out all the windows in the buffer as opposed
to just the first one. It can be enabled by writing "sink" to the "mode"
file (assuming msu-sink.ko is loaded).

View File

@ -65,6 +65,7 @@
#include <linux/ratelimit.h>
#include <linux/syscalls.h>
#include <linux/task_work.h>
#include <linux/sizes.h>
#include <uapi/linux/android/binder.h>
#include <uapi/linux/android/binderfs.h>
@ -92,11 +93,6 @@ static atomic_t binder_last_id;
static int proc_show(struct seq_file *m, void *unused);
DEFINE_SHOW_ATTRIBUTE(proc);
/* This is only defined in include/asm-arm/sizes.h */
#ifndef SZ_1K
#define SZ_1K 0x400
#endif
#define FORBIDDEN_MMAP_FLAGS (VM_WRITE)
enum {

View File

@ -268,7 +268,6 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
alloc->pages_high = index + 1;
trace_binder_alloc_page_end(alloc, index);
/* vm_insert_page does not seem to increment the refcount */
}
if (mm) {
up_read(&mm->mmap_sem);
@ -277,8 +276,7 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
return 0;
free_range:
for (page_addr = end - PAGE_SIZE; page_addr >= start;
page_addr -= PAGE_SIZE) {
for (page_addr = end - PAGE_SIZE; 1; page_addr -= PAGE_SIZE) {
bool ret;
size_t index;
@ -291,6 +289,8 @@ free_range:
WARN_ON(!ret);
trace_binder_free_lru_end(alloc, index);
if (page_addr == start)
break;
continue;
err_vm_insert_page_failed:
@ -298,7 +298,8 @@ err_vm_insert_page_failed:
page->page_ptr = NULL;
err_alloc_page_failed:
err_page_ptr_cleared:
;
if (page_addr == start)
break;
}
err_no_vma:
if (mm) {
@ -681,17 +682,17 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc,
struct binder_buffer *buffer;
mutex_lock(&binder_alloc_mmap_lock);
if (alloc->buffer) {
if (alloc->buffer_size) {
ret = -EBUSY;
failure_string = "already mapped";
goto err_already_mapped;
}
alloc->buffer = (void __user *)vma->vm_start;
mutex_unlock(&binder_alloc_mmap_lock);
alloc->buffer_size = min_t(unsigned long, vma->vm_end - vma->vm_start,
SZ_4M);
mutex_unlock(&binder_alloc_mmap_lock);
alloc->buffer = (void __user *)vma->vm_start;
alloc->pages = kcalloc(alloc->buffer_size / PAGE_SIZE,
sizeof(alloc->pages[0]),
GFP_KERNEL);
@ -722,8 +723,9 @@ err_alloc_buf_struct_failed:
kfree(alloc->pages);
alloc->pages = NULL;
err_alloc_pages_failed:
mutex_lock(&binder_alloc_mmap_lock);
alloc->buffer = NULL;
mutex_lock(&binder_alloc_mmap_lock);
alloc->buffer_size = 0;
err_already_mapped:
mutex_unlock(&binder_alloc_mmap_lock);
binder_alloc_debug(BINDER_DEBUG_USER_ERROR,
@ -841,14 +843,20 @@ void binder_alloc_print_pages(struct seq_file *m,
int free = 0;
mutex_lock(&alloc->mutex);
for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
page = &alloc->pages[i];
if (!page->page_ptr)
free++;
else if (list_empty(&page->lru))
active++;
else
lru++;
/*
* Make sure the binder_alloc is fully initialized, otherwise we might
* read inconsistent state.
*/
if (binder_alloc_get_vma(alloc) != NULL) {
for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
page = &alloc->pages[i];
if (!page->page_ptr)
free++;
else if (list_empty(&page->lru))
active++;
else
lru++;
}
}
mutex_unlock(&alloc->mutex);
seq_printf(m, " pages: %d:%d:%d\n", active, lru, free);

View File

@ -439,8 +439,8 @@ config RAW_DRIVER
Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O.
See the raw(8) manpage for more details.
Applications should preferably open the device (eg /dev/hda1)
with the O_DIRECT flag.
Applications should preferably open the device (eg /dev/hda1)
with the O_DIRECT flag.
config MAX_RAW_DEVS
int "Maximum number of RAW devices to support (1-65536)"
@ -559,4 +559,4 @@ config RANDOM_TRUST_BOOTLOADER
device randomness. Say Y here to assume the entropy provided by the
booloader is trustworthy so it will be added to the kernel's entropy
pool. Otherwise, say N here so it will be regarded as device input that
only mixes the entropy pool.
only mixes the entropy pool.

View File

@ -63,7 +63,7 @@ config AGP_AMD64
This option gives you AGP support for the GLX component of
X using the on-CPU northbridge of the AMD Athlon64/Opteron CPUs.
You still need an external AGP bridge like the AMD 8151, VIA
K8T400M, SiS755. It may also support other AGP bridges when loaded
K8T400M, SiS755. It may also support other AGP bridges when loaded
with agp_try_unsupported=1.
config AGP_INTEL

View File

@ -386,17 +386,17 @@ config HW_RANDOM_MESON
If unsure, say Y.
config HW_RANDOM_CAVIUM
tristate "Cavium ThunderX Random Number Generator support"
depends on HW_RANDOM && PCI && (ARM64 || (COMPILE_TEST && 64BIT))
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on Cavium SoCs.
tristate "Cavium ThunderX Random Number Generator support"
depends on HW_RANDOM && PCI && (ARM64 || (COMPILE_TEST && 64BIT))
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on Cavium SoCs.
To compile this driver as a module, choose M here: the
module will be called cavium_rng.
To compile this driver as a module, choose M here: the
module will be called cavium_rng.
If unsure, say Y.
If unsure, say Y.
config HW_RANDOM_MTK
tristate "Mediatek Random Number Generator support"

View File

@ -4,38 +4,38 @@
#
menuconfig IPMI_HANDLER
tristate 'IPMI top-level message handler'
depends on HAS_IOMEM
select IPMI_DMI_DECODE if DMI
help
This enables the central IPMI message handler, required for IPMI
to work.
tristate 'IPMI top-level message handler'
depends on HAS_IOMEM
select IPMI_DMI_DECODE if DMI
help
This enables the central IPMI message handler, required for IPMI
to work.
IPMI is a standard for managing sensors (temperature,
voltage, etc.) in a system.
IPMI is a standard for managing sensors (temperature,
voltage, etc.) in a system.
See <file:Documentation/IPMI.txt> for more details on the driver.
See <file:Documentation/IPMI.txt> for more details on the driver.
If unsure, say N.
If unsure, say N.
config IPMI_DMI_DECODE
select IPMI_PLAT_DATA
bool
select IPMI_PLAT_DATA
bool
config IPMI_PLAT_DATA
bool
bool
if IPMI_HANDLER
config IPMI_PANIC_EVENT
bool 'Generate a panic event to all BMCs on a panic'
help
When a panic occurs, this will cause the IPMI message handler to,
by default, generate an IPMI event describing the panic to each
interface registered with the message handler. This is always
available, the module parameter for ipmi_msghandler named
panic_op can be set to "event" to chose this value, this config
simply causes the default value to be set to "event".
bool 'Generate a panic event to all BMCs on a panic'
help
When a panic occurs, this will cause the IPMI message handler to,
by default, generate an IPMI event describing the panic to each
interface registered with the message handler. This is always
available, the module parameter for ipmi_msghandler named
panic_op can be set to "event" to chose this value, this config
simply causes the default value to be set to "event".
config IPMI_PANIC_STRING
bool 'Generate OEM events containing the panic string'
@ -54,43 +54,43 @@ config IPMI_PANIC_STRING
causes the default value to be set to "string".
config IPMI_DEVICE_INTERFACE
tristate 'Device interface for IPMI'
help
This provides an IOCTL interface to the IPMI message handler so
userland processes may use IPMI. It supports poll() and select().
tristate 'Device interface for IPMI'
help
This provides an IOCTL interface to the IPMI message handler so
userland processes may use IPMI. It supports poll() and select().
config IPMI_SI
tristate 'IPMI System Interface handler'
select IPMI_PLAT_DATA
help
Provides a driver for System Interfaces (KCS, SMIC, BT).
Currently, only KCS and SMIC are supported. If
you are using IPMI, you should probably say "y" here.
tristate 'IPMI System Interface handler'
select IPMI_PLAT_DATA
help
Provides a driver for System Interfaces (KCS, SMIC, BT).
Currently, only KCS and SMIC are supported. If
you are using IPMI, you should probably say "y" here.
config IPMI_SSIF
tristate 'IPMI SMBus handler (SSIF)'
select I2C
help
Provides a driver for a SMBus interface to a BMC, meaning that you
have a driver that must be accessed over an I2C bus instead of a
standard interface. This module requires I2C support.
tristate 'IPMI SMBus handler (SSIF)'
select I2C
help
Provides a driver for a SMBus interface to a BMC, meaning that you
have a driver that must be accessed over an I2C bus instead of a
standard interface. This module requires I2C support.
config IPMI_POWERNV
depends on PPC_POWERNV
tristate 'POWERNV (OPAL firmware) IPMI interface'
help
Provides a driver for OPAL firmware-based IPMI interfaces.
depends on PPC_POWERNV
tristate 'POWERNV (OPAL firmware) IPMI interface'
help
Provides a driver for OPAL firmware-based IPMI interfaces.
config IPMI_WATCHDOG
tristate 'IPMI Watchdog Timer'
help
This enables the IPMI watchdog timer.
tristate 'IPMI Watchdog Timer'
help
This enables the IPMI watchdog timer.
config IPMI_POWEROFF
tristate 'IPMI Poweroff'
help
This enables a function to power off the system with IPMI if
the IPMI management controller is capable of this.
tristate 'IPMI Poweroff'
help
This enables a function to power off the system with IPMI if
the IPMI management controller is capable of this.
endif # IPMI_HANDLER
@ -126,7 +126,7 @@ config NPCM7XX_KCS_IPMI_BMC
config ASPEED_BT_IPMI_BMC
depends on ARCH_ASPEED || COMPILE_TEST
depends on REGMAP && REGMAP_MMIO && MFD_SYSCON
depends on REGMAP && REGMAP_MMIO && MFD_SYSCON
tristate "BT IPMI bmc driver"
help
Provides a driver for the BT (Block Transfer) IPMI interface

View File

@ -713,6 +713,10 @@ static int lp_set_timeout64(unsigned int minor, void __user *arg)
if (copy_from_user(karg, arg, sizeof(karg)))
return -EFAULT;
/* sparc64 suseconds_t is 32-bit only */
if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())
karg[1] >>= 32;
return lp_set_timeout(minor, karg[0], karg[1]);
}

View File

@ -619,20 +619,27 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (copy_from_user(time32, argp, sizeof(time32)))
return -EFAULT;
if ((time32[0] < 0) || (time32[1] < 0))
return -EINVAL;
return pp_set_timeout(pp->pdev, time32[0], time32[1]);
case PPSETTIME64:
if (copy_from_user(time64, argp, sizeof(time64)))
return -EFAULT;
if ((time64[0] < 0) || (time64[1] < 0))
return -EINVAL;
if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())
time64[1] >>= 32;
return pp_set_timeout(pp->pdev, time64[0], time64[1]);
case PPGETTIME32:
jiffies_to_timespec64(pp->pdev->timeout, &ts);
time32[0] = ts.tv_sec;
time32[1] = ts.tv_nsec / NSEC_PER_USEC;
if ((time32[0] < 0) || (time32[1] < 0))
return -EINVAL;
if (copy_to_user(argp, time32, sizeof(time32)))
return -EFAULT;
@ -643,8 +650,9 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
jiffies_to_timespec64(pp->pdev->timeout, &ts);
time64[0] = ts.tv_sec;
time64[1] = ts.tv_nsec / NSEC_PER_USEC;
if ((time64[0] < 0) || (time64[1] < 0))
return -EINVAL;
if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())
time64[1] <<= 32;
if (copy_to_user(argp, time64, sizeof(time64)))
return -EFAULT;

View File

@ -116,7 +116,6 @@ static int xilly_drv_probe(struct platform_device *op)
struct xilly_endpoint *endpoint;
int rc;
int irq;
struct resource *res;
struct xilly_endpoint_hardware *ephw = &of_hw;
if (of_property_read_bool(dev->of_node, "dma-coherent"))
@ -129,9 +128,7 @@ static int xilly_drv_probe(struct platform_device *op)
dev_set_drvdata(dev, endpoint);
res = platform_get_resource(op, IORESOURCE_MEM, 0);
endpoint->registers = devm_ioremap_resource(dev, res);
endpoint->registers = devm_platform_ioremap_resource(op, 0);
if (IS_ERR(endpoint->registers))
return PTR_ERR(endpoint->registers);

View File

@ -338,6 +338,7 @@ static int cht_wc_extcon_probe(struct platform_device *pdev)
struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
struct cht_wc_extcon_data *ext;
unsigned long mask = ~(CHT_WC_PWRSRC_VBUS | CHT_WC_PWRSRC_USBID_MASK);
int pwrsrc_sts, id;
int irq, ret;
irq = platform_get_irq(pdev, 0);
@ -387,8 +388,19 @@ static int cht_wc_extcon_probe(struct platform_device *pdev)
goto disable_sw_control;
}
/* Route D+ and D- to PMIC for initial charger detection */
cht_wc_extcon_set_phymux(ext, MUX_SEL_PMIC);
ret = regmap_read(ext->regmap, CHT_WC_PWRSRC_STS, &pwrsrc_sts);
if (ret) {
dev_err(ext->dev, "Error reading pwrsrc status: %d\n", ret);
goto disable_sw_control;
}
/*
* If no USB host or device connected, route D+ and D- to PMIC for
* initial charger detection
*/
id = cht_wc_extcon_get_id(ext, pwrsrc_sts);
if (id != INTEL_USB_ID_GND)
cht_wc_extcon_set_phymux(ext, MUX_SEL_PMIC);
/* Get initial state */
cht_wc_extcon_pwrsrc_event(ext);

View File

@ -65,6 +65,10 @@ struct sm5502_muic_info {
/* Default value of SM5502 register to bring up MUIC device. */
static struct reg_data sm5502_reg_data[] = {
{
.reg = SM5502_REG_RESET,
.val = SM5502_REG_RESET_MASK,
.invert = true,
}, {
.reg = SM5502_REG_CONTROL,
.val = SM5502_REG_CONTROL_MASK_INT_MASK,
.invert = false,
@ -272,7 +276,7 @@ static int sm5502_muic_set_path(struct sm5502_muic_info *info,
/* Return cable type of attached or detached accessories */
static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info)
{
unsigned int cable_type = -1, adc, dev_type1;
unsigned int cable_type, adc, dev_type1;
int ret;
/* Read ADC value according to external cable or button */

View File

@ -237,6 +237,8 @@ enum sm5502_reg {
#define DM_DP_SWITCH_UART ((DM_DP_CON_SWITCH_UART <<SM5502_REG_MANUAL_SW1_DP_SHIFT) \
| (DM_DP_CON_SWITCH_UART <<SM5502_REG_MANUAL_SW1_DM_SHIFT))
#define SM5502_REG_RESET_MASK (0x1)
/* SM5502 Interrupts */
enum sm5502_irq {
/* INT1 */

View File

@ -20,7 +20,6 @@
#define RSU_VERSION_MASK GENMASK_ULL(63, 32)
#define RSU_ERROR_LOCATION_MASK GENMASK_ULL(31, 0)
#define RSU_ERROR_DETAIL_MASK GENMASK_ULL(63, 32)
#define RSU_FW_VERSION_MASK GENMASK_ULL(15, 0)
#define RSU_TIMEOUT (msecs_to_jiffies(SVC_RSU_REQUEST_TIMEOUT_MS))
@ -109,9 +108,12 @@ static void rsu_command_callback(struct stratix10_svc_client *client,
{
struct stratix10_rsu_priv *priv = client->priv;
if (data->status != BIT(SVC_STATUS_RSU_OK))
dev_err(client->dev, "RSU returned status is %i\n",
data->status);
if (data->status == BIT(SVC_STATUS_RSU_NO_SUPPORT))
dev_warn(client->dev, "Secure FW doesn't support notify\n");
else if (data->status == BIT(SVC_STATUS_RSU_ERROR))
dev_err(client->dev, "Failure, returned status is %lu\n",
BIT(data->status));
complete(&priv->completion);
}
@ -133,9 +135,11 @@ static void rsu_retry_callback(struct stratix10_svc_client *client,
if (data->status == BIT(SVC_STATUS_RSU_OK))
priv->retry_counter = *counter;
else if (data->status == BIT(SVC_STATUS_RSU_NO_SUPPORT))
dev_warn(client->dev, "Secure FW doesn't support retry\n");
else
dev_err(client->dev, "Failed to get retry counter %i\n",
data->status);
dev_err(client->dev, "Failed to get retry counter %lu\n",
BIT(data->status));
complete(&priv->completion);
}
@ -333,15 +337,10 @@ static ssize_t notify_store(struct device *dev,
return ret;
}
/* only 19.3 or late version FW supports retry counter feature */
if (FIELD_GET(RSU_FW_VERSION_MASK, priv->status.version)) {
ret = rsu_send_msg(priv, COMMAND_RSU_RETRY,
0, rsu_retry_callback);
if (ret) {
dev_err(dev,
"Error, getting RSU retry %i\n", ret);
return ret;
}
ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, rsu_retry_callback);
if (ret) {
dev_err(dev, "Error, getting RSU retry %i\n", ret);
return ret;
}
return count;
@ -413,15 +412,10 @@ static int stratix10_rsu_probe(struct platform_device *pdev)
stratix10_svc_free_channel(priv->chan);
}
/* only 19.3 or late version FW supports retry counter feature */
if (FIELD_GET(RSU_FW_VERSION_MASK, priv->status.version)) {
ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0,
rsu_retry_callback);
if (ret) {
dev_err(dev,
"Error, getting RSU retry %i\n", ret);
stratix10_svc_free_channel(priv->chan);
}
ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, rsu_retry_callback);
if (ret) {
dev_err(dev, "Error, getting RSU retry %i\n", ret);
stratix10_svc_free_channel(priv->chan);
}
return ret;

View File

@ -493,8 +493,24 @@ static int svc_normal_to_secure_thread(void *data)
pdata->chan->scl->receive_cb(pdata->chan->scl, cbdata);
break;
default:
pr_warn("it shouldn't happen\n");
pr_warn("Secure firmware doesn't support...\n");
/*
* be compatible with older version firmware which
* doesn't support RSU notify or retry
*/
if ((pdata->command == COMMAND_RSU_RETRY) ||
(pdata->command == COMMAND_RSU_NOTIFY)) {
cbdata->status =
BIT(SVC_STATUS_RSU_NO_SUPPORT);
cbdata->kaddr1 = NULL;
cbdata->kaddr2 = NULL;
cbdata->kaddr3 = NULL;
pdata->chan->scl->receive_cb(
pdata->chan->scl, cbdata);
}
break;
}
};

View File

@ -156,7 +156,7 @@ config FPGA_DFL
config FPGA_DFL_FME
tristate "FPGA DFL FME Driver"
depends on FPGA_DFL
depends on FPGA_DFL && HWMON
help
The FPGA Management Engine (FME) is a feature device implemented
under Device Feature List (DFL) framework. Select this option to

View File

@ -14,6 +14,8 @@
* Henry Mitchel <henry.mitchel@intel.com>
*/
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/uaccess.h>
@ -181,6 +183,381 @@ static const struct dfl_feature_ops fme_hdr_ops = {
.ioctl = fme_hdr_ioctl,
};
#define FME_THERM_THRESHOLD 0x8
#define TEMP_THRESHOLD1 GENMASK_ULL(6, 0)
#define TEMP_THRESHOLD1_EN BIT_ULL(7)
#define TEMP_THRESHOLD2 GENMASK_ULL(14, 8)
#define TEMP_THRESHOLD2_EN BIT_ULL(15)
#define TRIP_THRESHOLD GENMASK_ULL(30, 24)
#define TEMP_THRESHOLD1_STATUS BIT_ULL(32) /* threshold1 reached */
#define TEMP_THRESHOLD2_STATUS BIT_ULL(33) /* threshold2 reached */
/* threshold1 policy: 0 - AP2 (90% throttle) / 1 - AP1 (50% throttle) */
#define TEMP_THRESHOLD1_POLICY BIT_ULL(44)
#define FME_THERM_RDSENSOR_FMT1 0x10
#define FPGA_TEMPERATURE GENMASK_ULL(6, 0)
#define FME_THERM_CAP 0x20
#define THERM_NO_THROTTLE BIT_ULL(0)
#define MD_PRE_DEG
static bool fme_thermal_throttle_support(void __iomem *base)
{
u64 v = readq(base + FME_THERM_CAP);
return FIELD_GET(THERM_NO_THROTTLE, v) ? false : true;
}
static umode_t thermal_hwmon_attrs_visible(const void *drvdata,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
const struct dfl_feature *feature = drvdata;
/* temperature is always supported, and check hardware cap for others */
if (attr == hwmon_temp_input)
return 0444;
return fme_thermal_throttle_support(feature->ioaddr) ? 0444 : 0;
}
static int thermal_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct dfl_feature *feature = dev_get_drvdata(dev);
u64 v;
switch (attr) {
case hwmon_temp_input:
v = readq(feature->ioaddr + FME_THERM_RDSENSOR_FMT1);
*val = (long)(FIELD_GET(FPGA_TEMPERATURE, v) * 1000);
break;
case hwmon_temp_max:
v = readq(feature->ioaddr + FME_THERM_THRESHOLD);
*val = (long)(FIELD_GET(TEMP_THRESHOLD1, v) * 1000);
break;
case hwmon_temp_crit:
v = readq(feature->ioaddr + FME_THERM_THRESHOLD);
*val = (long)(FIELD_GET(TEMP_THRESHOLD2, v) * 1000);
break;
case hwmon_temp_emergency:
v = readq(feature->ioaddr + FME_THERM_THRESHOLD);
*val = (long)(FIELD_GET(TRIP_THRESHOLD, v) * 1000);
break;
case hwmon_temp_max_alarm:
v = readq(feature->ioaddr + FME_THERM_THRESHOLD);
*val = (long)FIELD_GET(TEMP_THRESHOLD1_STATUS, v);
break;
case hwmon_temp_crit_alarm:
v = readq(feature->ioaddr + FME_THERM_THRESHOLD);
*val = (long)FIELD_GET(TEMP_THRESHOLD2_STATUS, v);
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static const struct hwmon_ops thermal_hwmon_ops = {
.is_visible = thermal_hwmon_attrs_visible,
.read = thermal_hwmon_read,
};
static const struct hwmon_channel_info *thermal_hwmon_info[] = {
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_EMERGENCY |
HWMON_T_MAX | HWMON_T_MAX_ALARM |
HWMON_T_CRIT | HWMON_T_CRIT_ALARM),
NULL
};
static const struct hwmon_chip_info thermal_hwmon_chip_info = {
.ops = &thermal_hwmon_ops,
.info = thermal_hwmon_info,
};
static ssize_t temp1_max_policy_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature *feature = dev_get_drvdata(dev);
u64 v;
v = readq(feature->ioaddr + FME_THERM_THRESHOLD);
return sprintf(buf, "%u\n",
(unsigned int)FIELD_GET(TEMP_THRESHOLD1_POLICY, v));
}
static DEVICE_ATTR_RO(temp1_max_policy);
static struct attribute *thermal_extra_attrs[] = {
&dev_attr_temp1_max_policy.attr,
NULL,
};
static umode_t thermal_extra_attrs_visible(struct kobject *kobj,
struct attribute *attr, int index)
{
struct device *dev = kobj_to_dev(kobj);
struct dfl_feature *feature = dev_get_drvdata(dev);
return fme_thermal_throttle_support(feature->ioaddr) ? attr->mode : 0;
}
static const struct attribute_group thermal_extra_group = {
.attrs = thermal_extra_attrs,
.is_visible = thermal_extra_attrs_visible,
};
__ATTRIBUTE_GROUPS(thermal_extra);
static int fme_thermal_mgmt_init(struct platform_device *pdev,
struct dfl_feature *feature)
{
struct device *hwmon;
/*
* create hwmon to allow userspace monitoring temperature and other
* threshold information.
*
* temp1_input -> FPGA device temperature
* temp1_max -> hardware threshold 1 -> 50% or 90% throttling
* temp1_crit -> hardware threshold 2 -> 100% throttling
* temp1_emergency -> hardware trip_threshold to shutdown FPGA
* temp1_max_alarm -> hardware threshold 1 alarm
* temp1_crit_alarm -> hardware threshold 2 alarm
*
* create device specific sysfs interfaces, e.g. read temp1_max_policy
* to understand the actual hardware throttling action (50% vs 90%).
*
* If hardware doesn't support automatic throttling per thresholds,
* then all above sysfs interfaces are not visible except temp1_input
* for temperature.
*/
hwmon = devm_hwmon_device_register_with_info(&pdev->dev,
"dfl_fme_thermal", feature,
&thermal_hwmon_chip_info,
thermal_extra_groups);
if (IS_ERR(hwmon)) {
dev_err(&pdev->dev, "Fail to register thermal hwmon\n");
return PTR_ERR(hwmon);
}
return 0;
}
static const struct dfl_feature_id fme_thermal_mgmt_id_table[] = {
{.id = FME_FEATURE_ID_THERMAL_MGMT,},
{0,}
};
static const struct dfl_feature_ops fme_thermal_mgmt_ops = {
.init = fme_thermal_mgmt_init,
};
#define FME_PWR_STATUS 0x8
#define FME_LATENCY_TOLERANCE BIT_ULL(18)
#define PWR_CONSUMED GENMASK_ULL(17, 0)
#define FME_PWR_THRESHOLD 0x10
#define PWR_THRESHOLD1 GENMASK_ULL(6, 0) /* in Watts */
#define PWR_THRESHOLD2 GENMASK_ULL(14, 8) /* in Watts */
#define PWR_THRESHOLD_MAX 0x7f /* in Watts */
#define PWR_THRESHOLD1_STATUS BIT_ULL(16)
#define PWR_THRESHOLD2_STATUS BIT_ULL(17)
#define FME_PWR_XEON_LIMIT 0x18
#define XEON_PWR_LIMIT GENMASK_ULL(14, 0) /* in 0.1 Watts */
#define XEON_PWR_EN BIT_ULL(15)
#define FME_PWR_FPGA_LIMIT 0x20
#define FPGA_PWR_LIMIT GENMASK_ULL(14, 0) /* in 0.1 Watts */
#define FPGA_PWR_EN BIT_ULL(15)
static int power_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct dfl_feature *feature = dev_get_drvdata(dev);
u64 v;
switch (attr) {
case hwmon_power_input:
v = readq(feature->ioaddr + FME_PWR_STATUS);
*val = (long)(FIELD_GET(PWR_CONSUMED, v) * 1000000);
break;
case hwmon_power_max:
v = readq(feature->ioaddr + FME_PWR_THRESHOLD);
*val = (long)(FIELD_GET(PWR_THRESHOLD1, v) * 1000000);
break;
case hwmon_power_crit:
v = readq(feature->ioaddr + FME_PWR_THRESHOLD);
*val = (long)(FIELD_GET(PWR_THRESHOLD2, v) * 1000000);
break;
case hwmon_power_max_alarm:
v = readq(feature->ioaddr + FME_PWR_THRESHOLD);
*val = (long)FIELD_GET(PWR_THRESHOLD1_STATUS, v);
break;
case hwmon_power_crit_alarm:
v = readq(feature->ioaddr + FME_PWR_THRESHOLD);
*val = (long)FIELD_GET(PWR_THRESHOLD2_STATUS, v);
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int power_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev->parent);
struct dfl_feature *feature = dev_get_drvdata(dev);
int ret = 0;
u64 v;
val = clamp_val(val / 1000000, 0, PWR_THRESHOLD_MAX);
mutex_lock(&pdata->lock);
switch (attr) {
case hwmon_power_max:
v = readq(feature->ioaddr + FME_PWR_THRESHOLD);
v &= ~PWR_THRESHOLD1;
v |= FIELD_PREP(PWR_THRESHOLD1, val);
writeq(v, feature->ioaddr + FME_PWR_THRESHOLD);
break;
case hwmon_power_crit:
v = readq(feature->ioaddr + FME_PWR_THRESHOLD);
v &= ~PWR_THRESHOLD2;
v |= FIELD_PREP(PWR_THRESHOLD2, val);
writeq(v, feature->ioaddr + FME_PWR_THRESHOLD);
break;
default:
ret = -EOPNOTSUPP;
break;
}
mutex_unlock(&pdata->lock);
return ret;
}
static umode_t power_hwmon_attrs_visible(const void *drvdata,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
switch (attr) {
case hwmon_power_input:
case hwmon_power_max_alarm:
case hwmon_power_crit_alarm:
return 0444;
case hwmon_power_max:
case hwmon_power_crit:
return 0644;
}
return 0;
}
static const struct hwmon_ops power_hwmon_ops = {
.is_visible = power_hwmon_attrs_visible,
.read = power_hwmon_read,
.write = power_hwmon_write,
};
static const struct hwmon_channel_info *power_hwmon_info[] = {
HWMON_CHANNEL_INFO(power, HWMON_P_INPUT |
HWMON_P_MAX | HWMON_P_MAX_ALARM |
HWMON_P_CRIT | HWMON_P_CRIT_ALARM),
NULL
};
static const struct hwmon_chip_info power_hwmon_chip_info = {
.ops = &power_hwmon_ops,
.info = power_hwmon_info,
};
static ssize_t power1_xeon_limit_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature *feature = dev_get_drvdata(dev);
u16 xeon_limit = 0;
u64 v;
v = readq(feature->ioaddr + FME_PWR_XEON_LIMIT);
if (FIELD_GET(XEON_PWR_EN, v))
xeon_limit = FIELD_GET(XEON_PWR_LIMIT, v);
return sprintf(buf, "%u\n", xeon_limit * 100000);
}
static ssize_t power1_fpga_limit_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature *feature = dev_get_drvdata(dev);
u16 fpga_limit = 0;
u64 v;
v = readq(feature->ioaddr + FME_PWR_FPGA_LIMIT);
if (FIELD_GET(FPGA_PWR_EN, v))
fpga_limit = FIELD_GET(FPGA_PWR_LIMIT, v);
return sprintf(buf, "%u\n", fpga_limit * 100000);
}
static ssize_t power1_ltr_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature *feature = dev_get_drvdata(dev);
u64 v;
v = readq(feature->ioaddr + FME_PWR_STATUS);
return sprintf(buf, "%u\n",
(unsigned int)FIELD_GET(FME_LATENCY_TOLERANCE, v));
}
static DEVICE_ATTR_RO(power1_xeon_limit);
static DEVICE_ATTR_RO(power1_fpga_limit);
static DEVICE_ATTR_RO(power1_ltr);
static struct attribute *power_extra_attrs[] = {
&dev_attr_power1_xeon_limit.attr,
&dev_attr_power1_fpga_limit.attr,
&dev_attr_power1_ltr.attr,
NULL
};
ATTRIBUTE_GROUPS(power_extra);
static int fme_power_mgmt_init(struct platform_device *pdev,
struct dfl_feature *feature)
{
struct device *hwmon;
hwmon = devm_hwmon_device_register_with_info(&pdev->dev,
"dfl_fme_power", feature,
&power_hwmon_chip_info,
power_extra_groups);
if (IS_ERR(hwmon)) {
dev_err(&pdev->dev, "Fail to register power hwmon\n");
return PTR_ERR(hwmon);
}
return 0;
}
static const struct dfl_feature_id fme_power_mgmt_id_table[] = {
{.id = FME_FEATURE_ID_POWER_MGMT,},
{0,}
};
static const struct dfl_feature_ops fme_power_mgmt_ops = {
.init = fme_power_mgmt_init,
};
static struct dfl_feature_driver fme_feature_drvs[] = {
{
.id_table = fme_hdr_id_table,
@ -194,6 +571,14 @@ static struct dfl_feature_driver fme_feature_drvs[] = {
.id_table = fme_global_err_id_table,
.ops = &fme_global_err_ops,
},
{
.id_table = fme_thermal_mgmt_id_table,
.ops = &fme_thermal_mgmt_ops,
},
{
.id_table = fme_power_mgmt_id_table,
.ops = &fme_power_mgmt_ops,
},
{
.ops = NULL,
},

View File

@ -578,10 +578,8 @@ static int zynq_fpga_probe(struct platform_device *pdev)
init_completion(&priv->dma_done);
priv->irq = platform_get_irq(pdev, 0);
if (priv->irq < 0) {
dev_err(dev, "No IRQ available\n");
if (priv->irq < 0)
return priv->irq;
}
priv->clk = devm_clk_get(dev, "ref_clk");
if (IS_ERR(priv->clk)) {

View File

@ -53,6 +53,14 @@ config FSI_MASTER_AST_CF
lines driven by the internal ColdFire coprocessor. This requires
the corresponding machine specific ColdFire firmware to be available.
config FSI_MASTER_ASPEED
tristate "FSI ASPEED master"
help
This option enables a FSI master that is present behind an OPB bridge
in the AST2600.
Enable it for your BMC kernel in an OpenPower or IBM Power system.
config FSI_SCOM
tristate "SCOM FSI client device driver"
---help---

View File

@ -2,6 +2,7 @@
obj-$(CONFIG_FSI) += fsi-core.o
obj-$(CONFIG_FSI_MASTER_HUB) += fsi-master-hub.o
obj-$(CONFIG_FSI_MASTER_ASPEED) += fsi-master-aspeed.o
obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
obj-$(CONFIG_FSI_MASTER_AST_CF) += fsi-master-ast-cf.o
obj-$(CONFIG_FSI_SCOM) += fsi-scom.o

View File

@ -544,6 +544,31 @@ static int fsi_slave_scan(struct fsi_slave *slave)
return 0;
}
static unsigned long aligned_access_size(size_t offset, size_t count)
{
unsigned long offset_unit, count_unit;
/* Criteria:
*
* 1. Access size must be less than or equal to the maximum access
* width or the highest power-of-two factor of offset
* 2. Access size must be less than or equal to the amount specified by
* count
*
* The access width is optimal if we can calculate 1 to be strictly
* equal while still satisfying 2.
*/
/* Find 1 by the bottom bit of offset (with a 4 byte access cap) */
offset_unit = BIT(__builtin_ctzl(offset | 4));
/* Find 2 by the top bit of count */
count_unit = BIT(8 * sizeof(unsigned long) - 1 - __builtin_clzl(count));
/* Constrain the maximum access width to the minimum of both criteria */
return BIT(__builtin_ctzl(offset_unit | count_unit));
}
static ssize_t fsi_slave_sysfs_raw_read(struct file *file,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
@ -559,8 +584,7 @@ static ssize_t fsi_slave_sysfs_raw_read(struct file *file,
return -EINVAL;
for (total_len = 0; total_len < count; total_len += read_len) {
read_len = min_t(size_t, count, 4);
read_len -= off & 0x3;
read_len = aligned_access_size(off, count - total_len);
rc = fsi_slave_read(slave, off, buf + total_len, read_len);
if (rc)
@ -587,8 +611,7 @@ static ssize_t fsi_slave_sysfs_raw_write(struct file *file,
return -EINVAL;
for (total_len = 0; total_len < count; total_len += write_len) {
write_len = min_t(size_t, count, 4);
write_len -= off & 0x3;
write_len = aligned_access_size(off, count - total_len);
rc = fsi_slave_write(slave, off, buf + total_len, write_len);
if (rc)
@ -1241,6 +1264,19 @@ static ssize_t master_break_store(struct device *dev,
static DEVICE_ATTR(break, 0200, NULL, master_break_store);
static struct attribute *master_attrs[] = {
&dev_attr_break.attr,
&dev_attr_rescan.attr,
NULL
};
ATTRIBUTE_GROUPS(master);
static struct class fsi_master_class = {
.name = "fsi-master",
.dev_groups = master_groups,
};
int fsi_master_register(struct fsi_master *master)
{
int rc;
@ -1249,6 +1285,7 @@ int fsi_master_register(struct fsi_master *master)
mutex_init(&master->scan_lock);
master->idx = ida_simple_get(&master_ida, 0, INT_MAX, GFP_KERNEL);
dev_set_name(&master->dev, "fsi%d", master->idx);
master->dev.class = &fsi_master_class;
rc = device_register(&master->dev);
if (rc) {
@ -1256,20 +1293,6 @@ int fsi_master_register(struct fsi_master *master)
return rc;
}
rc = device_create_file(&master->dev, &dev_attr_rescan);
if (rc) {
device_del(&master->dev);
ida_simple_remove(&master_ida, master->idx);
return rc;
}
rc = device_create_file(&master->dev, &dev_attr_break);
if (rc) {
device_del(&master->dev);
ida_simple_remove(&master_ida, master->idx);
return rc;
}
np = dev_of_node(&master->dev);
if (!of_property_read_bool(np, "no-scan-on-init")) {
mutex_lock(&master->scan_lock);
@ -1350,8 +1373,15 @@ static int __init fsi_init(void)
rc = bus_register(&fsi_bus_type);
if (rc)
goto fail_bus;
rc = class_register(&fsi_master_class);
if (rc)
goto fail_class;
return 0;
fail_class:
bus_unregister(&fsi_bus_type);
fail_bus:
unregister_chrdev_region(fsi_base_dev, FSI_CHAR_MAX_DEVICES);
return rc;
@ -1360,6 +1390,7 @@ postcore_initcall(fsi_init);
static void fsi_exit(void)
{
class_unregister(&fsi_master_class);
bus_unregister(&fsi_bus_type);
unregister_chrdev_region(fsi_base_dev, FSI_CHAR_MAX_DEVICES);
ida_destroy(&fsi_minor_ida);

View File

@ -0,0 +1,544 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (C) IBM Corporation 2018
// FSI master driver for AST2600
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/fsi.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/iopoll.h>
#include "fsi-master.h"
struct fsi_master_aspeed {
struct fsi_master master;
struct device *dev;
void __iomem *base;
struct clk *clk;
};
#define to_fsi_master_aspeed(m) \
container_of(m, struct fsi_master_aspeed, master)
/* Control register (size 0x400) */
static const u32 ctrl_base = 0x80000000;
static const u32 fsi_base = 0xa0000000;
#define OPB_FSI_VER 0x00
#define OPB_TRIGGER 0x04
#define OPB_CTRL_BASE 0x08
#define OPB_FSI_BASE 0x0c
#define OPB_CLK_SYNC 0x3c
#define OPB_IRQ_CLEAR 0x40
#define OPB_IRQ_MASK 0x44
#define OPB_IRQ_STATUS 0x48
#define OPB0_SELECT 0x10
#define OPB0_RW 0x14
#define OPB0_XFER_SIZE 0x18
#define OPB0_FSI_ADDR 0x1c
#define OPB0_FSI_DATA_W 0x20
#define OPB0_STATUS 0x80
#define OPB0_FSI_DATA_R 0x84
#define OPB0_WRITE_ORDER1 0x4c
#define OPB0_WRITE_ORDER2 0x50
#define OPB1_WRITE_ORDER1 0x54
#define OPB1_WRITE_ORDER2 0x58
#define OPB0_READ_ORDER1 0x5c
#define OPB1_READ_ORDER2 0x60
#define OPB_RETRY_COUNTER 0x64
/* OPBn_STATUS */
#define STATUS_HALFWORD_ACK BIT(0)
#define STATUS_FULLWORD_ACK BIT(1)
#define STATUS_ERR_ACK BIT(2)
#define STATUS_RETRY BIT(3)
#define STATUS_TIMEOUT BIT(4)
/* OPB_IRQ_MASK */
#define OPB1_XFER_ACK_EN BIT(17)
#define OPB0_XFER_ACK_EN BIT(16)
/* OPB_RW */
#define CMD_READ BIT(0)
#define CMD_WRITE 0
/* OPBx_XFER_SIZE */
#define XFER_FULLWORD (BIT(1) | BIT(0))
#define XFER_HALFWORD (BIT(0))
#define XFER_BYTE (0)
#define CREATE_TRACE_POINTS
#include <trace/events/fsi_master_aspeed.h>
#define FSI_LINK_ENABLE_SETUP_TIME 10 /* in mS */
#define DEFAULT_DIVISOR 14
#define OPB_POLL_TIMEOUT 10000
static int __opb_write(struct fsi_master_aspeed *aspeed, u32 addr,
u32 val, u32 transfer_size)
{
void __iomem *base = aspeed->base;
u32 reg, status;
int ret;
writel(CMD_WRITE, base + OPB0_RW);
writel(transfer_size, base + OPB0_XFER_SIZE);
writel(addr, base + OPB0_FSI_ADDR);
writel(val, base + OPB0_FSI_DATA_W);
writel(0x1, base + OPB_IRQ_CLEAR);
writel(0x1, base + OPB_TRIGGER);
ret = readl_poll_timeout(base + OPB_IRQ_STATUS, reg,
(reg & OPB0_XFER_ACK_EN) != 0,
0, OPB_POLL_TIMEOUT);
status = readl(base + OPB0_STATUS);
trace_fsi_master_aspeed_opb_write(addr, val, transfer_size, status, reg);
/* Return error when poll timed out */
if (ret)
return ret;
/* Command failed, master will reset */
if (status & STATUS_ERR_ACK)
return -EIO;
return 0;
}
static int opb_writeb(struct fsi_master_aspeed *aspeed, u32 addr, u8 val)
{
return __opb_write(aspeed, addr, val, XFER_BYTE);
}
static int opb_writew(struct fsi_master_aspeed *aspeed, u32 addr, __be16 val)
{
return __opb_write(aspeed, addr, (__force u16)val, XFER_HALFWORD);
}
static int opb_writel(struct fsi_master_aspeed *aspeed, u32 addr, __be32 val)
{
return __opb_write(aspeed, addr, (__force u32)val, XFER_FULLWORD);
}
static int __opb_read(struct fsi_master_aspeed *aspeed, uint32_t addr,
u32 transfer_size, void *out)
{
void __iomem *base = aspeed->base;
u32 result, reg;
int status, ret;
writel(CMD_READ, base + OPB0_RW);
writel(transfer_size, base + OPB0_XFER_SIZE);
writel(addr, base + OPB0_FSI_ADDR);
writel(0x1, base + OPB_IRQ_CLEAR);
writel(0x1, base + OPB_TRIGGER);
ret = readl_poll_timeout(base + OPB_IRQ_STATUS, reg,
(reg & OPB0_XFER_ACK_EN) != 0,
0, OPB_POLL_TIMEOUT);
status = readl(base + OPB0_STATUS);
result = readl(base + OPB0_FSI_DATA_R);
trace_fsi_master_aspeed_opb_read(addr, transfer_size, result,
readl(base + OPB0_STATUS),
reg);
/* Return error when poll timed out */
if (ret)
return ret;
/* Command failed, master will reset */
if (status & STATUS_ERR_ACK)
return -EIO;
if (out) {
switch (transfer_size) {
case XFER_BYTE:
*(u8 *)out = result;
break;
case XFER_HALFWORD:
*(u16 *)out = result;
break;
case XFER_FULLWORD:
*(u32 *)out = result;
break;
default:
return -EINVAL;
}
}
return 0;
}
static int opb_readl(struct fsi_master_aspeed *aspeed, uint32_t addr, __be32 *out)
{
return __opb_read(aspeed, addr, XFER_FULLWORD, out);
}
static int opb_readw(struct fsi_master_aspeed *aspeed, uint32_t addr, __be16 *out)
{
return __opb_read(aspeed, addr, XFER_HALFWORD, (void *)out);
}
static int opb_readb(struct fsi_master_aspeed *aspeed, uint32_t addr, u8 *out)
{
return __opb_read(aspeed, addr, XFER_BYTE, (void *)out);
}
static int check_errors(struct fsi_master_aspeed *aspeed, int err)
{
int ret;
if (trace_fsi_master_aspeed_opb_error_enabled()) {
__be32 mresp0, mstap0, mesrb0;
opb_readl(aspeed, ctrl_base + FSI_MRESP0, &mresp0);
opb_readl(aspeed, ctrl_base + FSI_MSTAP0, &mstap0);
opb_readl(aspeed, ctrl_base + FSI_MESRB0, &mesrb0);
trace_fsi_master_aspeed_opb_error(
be32_to_cpu(mresp0),
be32_to_cpu(mstap0),
be32_to_cpu(mesrb0));
}
if (err == -EIO) {
/* Check MAEB (0x70) ? */
/* Then clear errors in master */
ret = opb_writel(aspeed, ctrl_base + FSI_MRESP0,
cpu_to_be32(FSI_MRESP_RST_ALL_MASTER));
if (ret) {
/* TODO: log? return different code? */
return ret;
}
/* TODO: confirm that 0x70 was okay */
}
/* This will pass through timeout errors */
return err;
}
static int aspeed_master_read(struct fsi_master *master, int link,
uint8_t id, uint32_t addr, void *val, size_t size)
{
struct fsi_master_aspeed *aspeed = to_fsi_master_aspeed(master);
int ret;
if (id != 0)
return -EINVAL;
addr += link * FSI_HUB_LINK_SIZE;
switch (size) {
case 1:
ret = opb_readb(aspeed, fsi_base + addr, val);
break;
case 2:
ret = opb_readw(aspeed, fsi_base + addr, val);
break;
case 4:
ret = opb_readl(aspeed, fsi_base + addr, val);
break;
default:
return -EINVAL;
}
ret = check_errors(aspeed, ret);
if (ret)
return ret;
return 0;
}
static int aspeed_master_write(struct fsi_master *master, int link,
uint8_t id, uint32_t addr, const void *val, size_t size)
{
struct fsi_master_aspeed *aspeed = to_fsi_master_aspeed(master);
int ret;
if (id != 0)
return -EINVAL;
addr += link * FSI_HUB_LINK_SIZE;
switch (size) {
case 1:
ret = opb_writeb(aspeed, fsi_base + addr, *(u8 *)val);
break;
case 2:
ret = opb_writew(aspeed, fsi_base + addr, *(__be16 *)val);
break;
case 4:
ret = opb_writel(aspeed, fsi_base + addr, *(__be32 *)val);
break;
default:
return -EINVAL;
}
ret = check_errors(aspeed, ret);
if (ret)
return ret;
return 0;
}
static int aspeed_master_link_enable(struct fsi_master *master, int link)
{
struct fsi_master_aspeed *aspeed = to_fsi_master_aspeed(master);
int idx, bit, ret;
__be32 reg, result;
idx = link / 32;
bit = link % 32;
reg = cpu_to_be32(0x80000000 >> bit);
ret = opb_writel(aspeed, ctrl_base + FSI_MSENP0 + (4 * idx), reg);
if (ret)
return ret;
mdelay(FSI_LINK_ENABLE_SETUP_TIME);
ret = opb_readl(aspeed, ctrl_base + FSI_MENP0 + (4 * idx), &result);
if (ret)
return ret;
if (result != reg) {
dev_err(aspeed->dev, "%s failed: %08x\n", __func__, result);
return -EIO;
}
return 0;
}
static int aspeed_master_term(struct fsi_master *master, int link, uint8_t id)
{
uint32_t addr;
__be32 cmd;
addr = 0x4;
cmd = cpu_to_be32(0xecc00000);
return aspeed_master_write(master, link, id, addr, &cmd, 4);
}
static int aspeed_master_break(struct fsi_master *master, int link)
{
uint32_t addr;
__be32 cmd;
addr = 0x0;
cmd = cpu_to_be32(0xc0de0000);
return aspeed_master_write(master, link, 0, addr, &cmd, 4);
}
static void aspeed_master_release(struct device *dev)
{
struct fsi_master_aspeed *aspeed =
to_fsi_master_aspeed(dev_to_fsi_master(dev));
kfree(aspeed);
}
/* mmode encoders */
static inline u32 fsi_mmode_crs0(u32 x)
{
return (x & FSI_MMODE_CRS0MASK) << FSI_MMODE_CRS0SHFT;
}
static inline u32 fsi_mmode_crs1(u32 x)
{
return (x & FSI_MMODE_CRS1MASK) << FSI_MMODE_CRS1SHFT;
}
static int aspeed_master_init(struct fsi_master_aspeed *aspeed)
{
__be32 reg;
reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
| FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
opb_writel(aspeed, ctrl_base + FSI_MRESP0, reg);
/* Initialize the MFSI (hub master) engine */
reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
| FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
opb_writel(aspeed, ctrl_base + FSI_MRESP0, reg);
reg = cpu_to_be32(FSI_MECTRL_EOAE | FSI_MECTRL_P8_AUTO_TERM);
opb_writel(aspeed, ctrl_base + FSI_MECTRL, reg);
reg = cpu_to_be32(FSI_MMODE_ECRC | FSI_MMODE_EPC | FSI_MMODE_RELA
| fsi_mmode_crs0(DEFAULT_DIVISOR)
| fsi_mmode_crs1(DEFAULT_DIVISOR)
| FSI_MMODE_P8_TO_LSB);
opb_writel(aspeed, ctrl_base + FSI_MMODE, reg);
reg = cpu_to_be32(0xffff0000);
opb_writel(aspeed, ctrl_base + FSI_MDLYR, reg);
reg = cpu_to_be32(~0);
opb_writel(aspeed, ctrl_base + FSI_MSENP0, reg);
/* Leave enabled long enough for master logic to set up */
mdelay(FSI_LINK_ENABLE_SETUP_TIME);
opb_writel(aspeed, ctrl_base + FSI_MCENP0, reg);
opb_readl(aspeed, ctrl_base + FSI_MAEB, NULL);
reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK);
opb_writel(aspeed, ctrl_base + FSI_MRESP0, reg);
opb_readl(aspeed, ctrl_base + FSI_MLEVP0, NULL);
/* Reset the master bridge */
reg = cpu_to_be32(FSI_MRESB_RST_GEN);
opb_writel(aspeed, ctrl_base + FSI_MRESB0, reg);
reg = cpu_to_be32(FSI_MRESB_RST_ERR);
opb_writel(aspeed, ctrl_base + FSI_MRESB0, reg);
return 0;
}
static int fsi_master_aspeed_probe(struct platform_device *pdev)
{
struct fsi_master_aspeed *aspeed;
struct resource *res;
int rc, links, reg;
__be32 raw;
aspeed = devm_kzalloc(&pdev->dev, sizeof(*aspeed), GFP_KERNEL);
if (!aspeed)
return -ENOMEM;
aspeed->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
aspeed->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(aspeed->base))
return PTR_ERR(aspeed->base);
aspeed->clk = devm_clk_get(aspeed->dev, NULL);
if (IS_ERR(aspeed->clk)) {
dev_err(aspeed->dev, "couldn't get clock\n");
return PTR_ERR(aspeed->clk);
}
rc = clk_prepare_enable(aspeed->clk);
if (rc) {
dev_err(aspeed->dev, "couldn't enable clock\n");
return rc;
}
writel(0x1, aspeed->base + OPB_CLK_SYNC);
writel(OPB1_XFER_ACK_EN | OPB0_XFER_ACK_EN,
aspeed->base + OPB_IRQ_MASK);
/* TODO: determine an appropriate value */
writel(0x10, aspeed->base + OPB_RETRY_COUNTER);
writel(ctrl_base, aspeed->base + OPB_CTRL_BASE);
writel(fsi_base, aspeed->base + OPB_FSI_BASE);
/* Set read data order */
writel(0x00030b1b, aspeed->base + OPB0_READ_ORDER1);
/* Set write data order */
writel(0x0011101b, aspeed->base + OPB0_WRITE_ORDER1);
writel(0x0c330f3f, aspeed->base + OPB0_WRITE_ORDER2);
/*
* Select OPB0 for all operations.
* Will need to be reworked when enabling DMA or anything that uses
* OPB1.
*/
writel(0x1, aspeed->base + OPB0_SELECT);
rc = opb_readl(aspeed, ctrl_base + FSI_MVER, &raw);
if (rc) {
dev_err(&pdev->dev, "failed to read hub version\n");
return rc;
}
reg = be32_to_cpu(raw);
links = (reg >> 8) & 0xff;
dev_info(&pdev->dev, "hub version %08x (%d links)\n", reg, links);
aspeed->master.dev.parent = &pdev->dev;
aspeed->master.dev.release = aspeed_master_release;
aspeed->master.dev.of_node = of_node_get(dev_of_node(&pdev->dev));
aspeed->master.n_links = links;
aspeed->master.read = aspeed_master_read;
aspeed->master.write = aspeed_master_write;
aspeed->master.send_break = aspeed_master_break;
aspeed->master.term = aspeed_master_term;
aspeed->master.link_enable = aspeed_master_link_enable;
dev_set_drvdata(&pdev->dev, aspeed);
aspeed_master_init(aspeed);
rc = fsi_master_register(&aspeed->master);
if (rc)
goto err_release;
/* At this point, fsi_master_register performs the device_initialize(),
* and holds the sole reference on master.dev. This means the device
* will be freed (via ->release) during any subsequent call to
* fsi_master_unregister. We add our own reference to it here, so we
* can perform cleanup (in _remove()) without it being freed before
* we're ready.
*/
get_device(&aspeed->master.dev);
return 0;
err_release:
clk_disable_unprepare(aspeed->clk);
return rc;
}
static int fsi_master_aspeed_remove(struct platform_device *pdev)
{
struct fsi_master_aspeed *aspeed = platform_get_drvdata(pdev);
fsi_master_unregister(&aspeed->master);
clk_disable_unprepare(aspeed->clk);
return 0;
}
static const struct of_device_id fsi_master_aspeed_match[] = {
{ .compatible = "aspeed,ast2600-fsi-master" },
{ },
};
static struct platform_driver fsi_master_aspeed_driver = {
.driver = {
.name = "fsi-master-aspeed",
.of_match_table = fsi_master_aspeed_match,
},
.probe = fsi_master_aspeed_probe,
.remove = fsi_master_aspeed_remove,
};
module_platform_driver(fsi_master_aspeed_driver);
MODULE_LICENSE("GPL");

View File

@ -13,53 +13,7 @@
#include "fsi-master.h"
/* Control Registers */
#define FSI_MMODE 0x0 /* R/W: mode */
#define FSI_MDLYR 0x4 /* R/W: delay */
#define FSI_MCRSP 0x8 /* R/W: clock rate */
#define FSI_MENP0 0x10 /* R/W: enable */
#define FSI_MLEVP0 0x18 /* R: plug detect */
#define FSI_MSENP0 0x18 /* S: Set enable */
#define FSI_MCENP0 0x20 /* C: Clear enable */
#define FSI_MAEB 0x70 /* R: Error address */
#define FSI_MVER 0x74 /* R: master version/type */
#define FSI_MRESP0 0xd0 /* W: Port reset */
#define FSI_MESRB0 0x1d0 /* R: Master error status */
#define FSI_MRESB0 0x1d0 /* W: Reset bridge */
#define FSI_MECTRL 0x2e0 /* W: Error control */
/* MMODE: Mode control */
#define FSI_MMODE_EIP 0x80000000 /* Enable interrupt polling */
#define FSI_MMODE_ECRC 0x40000000 /* Enable error recovery */
#define FSI_MMODE_EPC 0x10000000 /* Enable parity checking */
#define FSI_MMODE_P8_TO_LSB 0x00000010 /* Timeout value LSB */
/* MSB=1, LSB=0 is 0.8 ms */
/* MSB=0, LSB=1 is 0.9 ms */
#define FSI_MMODE_CRS0SHFT 18 /* Clk rate selection 0 shift */
#define FSI_MMODE_CRS0MASK 0x3ff /* Clk rate selection 0 mask */
#define FSI_MMODE_CRS1SHFT 8 /* Clk rate selection 1 shift */
#define FSI_MMODE_CRS1MASK 0x3ff /* Clk rate selection 1 mask */
/* MRESB: Reset brindge */
#define FSI_MRESB_RST_GEN 0x80000000 /* General reset */
#define FSI_MRESB_RST_ERR 0x40000000 /* Error Reset */
/* MRESB: Reset port */
#define FSI_MRESP_RST_ALL_MASTER 0x20000000 /* Reset all FSI masters */
#define FSI_MRESP_RST_ALL_LINK 0x10000000 /* Reset all FSI port contr. */
#define FSI_MRESP_RST_MCR 0x08000000 /* Reset FSI master reg. */
#define FSI_MRESP_RST_PYE 0x04000000 /* Reset FSI parity error */
#define FSI_MRESP_RST_ALL 0xfc000000 /* Reset any error */
/* MECTRL: Error control */
#define FSI_MECTRL_EOAE 0x8000 /* Enable machine check when */
/* master 0 in error */
#define FSI_MECTRL_P8_AUTO_TERM 0x4000 /* Auto terminate */
#define FSI_ENGID_HUB_MASTER 0x1c
#define FSI_HUB_LINK_OFFSET 0x80000
#define FSI_HUB_LINK_SIZE 0x80000
#define FSI_HUB_MASTER_MAX_LINKS 8
#define FSI_LINK_ENABLE_SETUP_TIME 10 /* in mS */

View File

@ -12,6 +12,71 @@
#include <linux/device.h>
#include <linux/mutex.h>
/*
* Master registers
*
* These are used by hardware masters, such as the one in the FSP2, AST2600 and
* the hub master in POWER processors.
*/
/* Control Registers */
#define FSI_MMODE 0x0 /* R/W: mode */
#define FSI_MDLYR 0x4 /* R/W: delay */
#define FSI_MCRSP 0x8 /* R/W: clock rate */
#define FSI_MENP0 0x10 /* R/W: enable */
#define FSI_MLEVP0 0x18 /* R: plug detect */
#define FSI_MSENP0 0x18 /* S: Set enable */
#define FSI_MCENP0 0x20 /* C: Clear enable */
#define FSI_MAEB 0x70 /* R: Error address */
#define FSI_MVER 0x74 /* R: master version/type */
#define FSI_MSTAP0 0xd0 /* R: Port status */
#define FSI_MRESP0 0xd0 /* W: Port reset */
#define FSI_MESRB0 0x1d0 /* R: Master error status */
#define FSI_MRESB0 0x1d0 /* W: Reset bridge */
#define FSI_MSCSB0 0x1d4 /* R: Master sub command stack */
#define FSI_MATRB0 0x1d8 /* R: Master address trace */
#define FSI_MDTRB0 0x1dc /* R: Master data trace */
#define FSI_MECTRL 0x2e0 /* W: Error control */
/* MMODE: Mode control */
#define FSI_MMODE_EIP 0x80000000 /* Enable interrupt polling */
#define FSI_MMODE_ECRC 0x40000000 /* Enable error recovery */
#define FSI_MMODE_RELA 0x20000000 /* Enable relative address commands */
#define FSI_MMODE_EPC 0x10000000 /* Enable parity checking */
#define FSI_MMODE_P8_TO_LSB 0x00000010 /* Timeout value LSB */
/* MSB=1, LSB=0 is 0.8 ms */
/* MSB=0, LSB=1 is 0.9 ms */
#define FSI_MMODE_CRS0SHFT 18 /* Clk rate selection 0 shift */
#define FSI_MMODE_CRS0MASK 0x3ff /* Clk rate selection 0 mask */
#define FSI_MMODE_CRS1SHFT 8 /* Clk rate selection 1 shift */
#define FSI_MMODE_CRS1MASK 0x3ff /* Clk rate selection 1 mask */
/* MRESB: Reset brindge */
#define FSI_MRESB_RST_GEN 0x80000000 /* General reset */
#define FSI_MRESB_RST_ERR 0x40000000 /* Error Reset */
/* MRESP: Reset port */
#define FSI_MRESP_RST_ALL_MASTER 0x20000000 /* Reset all FSI masters */
#define FSI_MRESP_RST_ALL_LINK 0x10000000 /* Reset all FSI port contr. */
#define FSI_MRESP_RST_MCR 0x08000000 /* Reset FSI master reg. */
#define FSI_MRESP_RST_PYE 0x04000000 /* Reset FSI parity error */
#define FSI_MRESP_RST_ALL 0xfc000000 /* Reset any error */
/* MECTRL: Error control */
#define FSI_MECTRL_EOAE 0x8000 /* Enable machine check when */
/* master 0 in error */
#define FSI_MECTRL_P8_AUTO_TERM 0x4000 /* Auto terminate */
#define FSI_HUB_LINK_OFFSET 0x80000
#define FSI_HUB_LINK_SIZE 0x80000
#define FSI_HUB_MASTER_MAX_LINKS 8
/*
* Protocol definitions
*
* These are used by low level masters that bit-bang out the protocol
*/
/* Various protocol delays */
#define FSI_ECHO_DELAY_CLOCKS 16 /* Number clocks for echo delay */
#define FSI_SEND_DELAY_CLOCKS 16 /* Number clocks for send delay */
@ -47,6 +112,12 @@
/* fsi-master definition and flags */
#define FSI_MASTER_FLAG_SWCLOCK 0x1
/*
* Structures and function prototypes
*
* These are common to all masters
*/
struct fsi_master {
struct device dev;
int idx;

View File

@ -211,3 +211,4 @@ MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
MODULE_DESCRIPTION("MEN 16z127 GPIO Controller");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("mcb:16z127");
MODULE_IMPORT_NS(MCB);

View File

@ -361,9 +361,6 @@ static int gb_connection_hd_cport_quiesce(struct gb_connection *connection)
if (connection->mode_switch)
peer_space += sizeof(struct gb_operation_msg_hdr);
if (!hd->driver->cport_quiesce)
return 0;
ret = hd->driver->cport_quiesce(hd, connection->hd_cport_id,
peer_space,
GB_CONNECTION_CPORT_QUIESCE_TIMEOUT);

View File

@ -4,6 +4,7 @@
#
menuconfig CORESIGHT
bool "CoreSight Tracing Support"
depends on ARM || ARM64
depends on OF || ACPI
select ARM_AMBA
select PERF_EVENTS

View File

@ -217,6 +217,7 @@ static ssize_t reset_store(struct device *dev,
/* No start-stop filtering for ViewInst */
config->vissctlr = 0x0;
config->vipcssctlr = 0x0;
/* Disable seq events */
for (i = 0; i < drvdata->nrseqstate-1; i++)
@ -238,6 +239,7 @@ static ssize_t reset_store(struct device *dev,
for (i = 0; i < drvdata->nr_resource; i++)
config->res_ctrl[i] = 0x0;
config->ss_idx = 0x0;
for (i = 0; i < drvdata->nr_ss_cmp; i++) {
config->ss_ctrl[i] = 0x0;
config->ss_pe_cmp[i] = 0x0;
@ -296,8 +298,6 @@ static ssize_t mode_store(struct device *dev,
spin_lock(&drvdata->spinlock);
config->mode = val & ETMv4_MODE_ALL;
etm4_set_mode_exclude(drvdata,
config->mode & ETM_MODE_EXCLUDE ? true : false);
if (drvdata->instrp0 == true) {
/* start by clearing instruction P0 field */
@ -652,10 +652,13 @@ static ssize_t cyc_threshold_store(struct device *dev,
if (kstrtoul(buf, 16, &val))
return -EINVAL;
/* mask off max threshold before checking min value */
val &= ETM_CYC_THRESHOLD_MASK;
if (val < drvdata->ccitmin)
return -EINVAL;
config->ccctlr = val & ETM_CYC_THRESHOLD_MASK;
config->ccctlr = val;
return size;
}
static DEVICE_ATTR_RW(cyc_threshold);
@ -686,14 +689,16 @@ static ssize_t bb_ctrl_store(struct device *dev,
return -EINVAL;
if (!drvdata->nr_addr_cmp)
return -EINVAL;
/*
* Bit[7:0] selects which address range comparator is used for
* branch broadcast control.
* Bit[8] controls include(1) / exclude(0), bits[0-7] select
* individual range comparators. If include then at least 1
* range must be selected.
*/
if (BMVAL(val, 0, 7) > drvdata->nr_addr_cmp)
if ((val & BIT(8)) && (BMVAL(val, 0, 7) == 0))
return -EINVAL;
config->bb_ctrl = val;
config->bb_ctrl = val & GENMASK(8, 0);
return size;
}
static DEVICE_ATTR_RW(bb_ctrl);
@ -738,7 +743,7 @@ static ssize_t s_exlevel_vinst_show(struct device *dev,
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
val = BMVAL(config->vinst_ctrl, 16, 19);
val = (config->vinst_ctrl & ETM_EXLEVEL_S_VICTLR_MASK) >> 16;
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
@ -754,8 +759,8 @@ static ssize_t s_exlevel_vinst_store(struct device *dev,
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* clear all EXLEVEL_S bits (bit[18] is never implemented) */
config->vinst_ctrl &= ~(BIT(16) | BIT(17) | BIT(19));
/* clear all EXLEVEL_S bits */
config->vinst_ctrl &= ~(ETM_EXLEVEL_S_VICTLR_MASK);
/* enable instruction tracing for corresponding exception level */
val &= drvdata->s_ex_level;
config->vinst_ctrl |= (val << 16);
@ -773,7 +778,7 @@ static ssize_t ns_exlevel_vinst_show(struct device *dev,
struct etmv4_config *config = &drvdata->config;
/* EXLEVEL_NS, bits[23:20] */
val = BMVAL(config->vinst_ctrl, 20, 23);
val = (config->vinst_ctrl & ETM_EXLEVEL_NS_VICTLR_MASK) >> 20;
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
@ -789,8 +794,8 @@ static ssize_t ns_exlevel_vinst_store(struct device *dev,
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* clear EXLEVEL_NS bits (bit[23] is never implemented */
config->vinst_ctrl &= ~(BIT(20) | BIT(21) | BIT(22));
/* clear EXLEVEL_NS bits */
config->vinst_ctrl &= ~(ETM_EXLEVEL_NS_VICTLR_MASK);
/* enable instruction tracing for corresponding exception level */
val &= drvdata->ns_ex_level;
config->vinst_ctrl |= (val << 20);
@ -966,8 +971,12 @@ static ssize_t addr_range_store(struct device *dev,
unsigned long val1, val2;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
int elements, exclude;
if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
elements = sscanf(buf, "%lx %lx %x", &val1, &val2, &exclude);
/* exclude is optional, but need at least two parameter */
if (elements < 2)
return -EINVAL;
/* lower address comparator cannot have a higher address value */
if (val1 > val2)
@ -995,9 +1004,11 @@ static ssize_t addr_range_store(struct device *dev,
/*
* Program include or exclude control bits for vinst or vdata
* whenever we change addr comparators to ETM_ADDR_TYPE_RANGE
* use supplied value, or default to bit set in 'mode'
*/
etm4_set_mode_exclude(drvdata,
config->mode & ETM_MODE_EXCLUDE ? true : false);
if (elements != 3)
exclude = config->mode & ETM_MODE_EXCLUDE;
etm4_set_mode_exclude(drvdata, exclude ? true : false);
spin_unlock(&drvdata->spinlock);
return size;
@ -1054,8 +1065,6 @@ static ssize_t addr_start_store(struct device *dev,
config->addr_val[idx] = (u64)val;
config->addr_type[idx] = ETM_ADDR_TYPE_START;
config->vissctlr |= BIT(idx);
/* SSSTATUS, bit[9] - turn on start/stop logic */
config->vinst_ctrl |= BIT(9);
spin_unlock(&drvdata->spinlock);
return size;
}
@ -1111,8 +1120,6 @@ static ssize_t addr_stop_store(struct device *dev,
config->addr_val[idx] = (u64)val;
config->addr_type[idx] = ETM_ADDR_TYPE_STOP;
config->vissctlr |= BIT(idx + 16);
/* SSSTATUS, bit[9] - turn on start/stop logic */
config->vinst_ctrl |= BIT(9);
spin_unlock(&drvdata->spinlock);
return size;
}
@ -1228,6 +1235,131 @@ static ssize_t addr_context_store(struct device *dev,
}
static DEVICE_ATTR_RW(addr_context);
static ssize_t addr_exlevel_s_ns_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
u8 idx;
unsigned long val;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
idx = config->addr_idx;
val = BMVAL(config->addr_acc[idx], 8, 14);
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static ssize_t addr_exlevel_s_ns_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
u8 idx;
unsigned long val;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
if (kstrtoul(buf, 0, &val))
return -EINVAL;
if (val & ~((GENMASK(14, 8) >> 8)))
return -EINVAL;
spin_lock(&drvdata->spinlock);
idx = config->addr_idx;
/* clear Exlevel_ns & Exlevel_s bits[14:12, 11:8], bit[15] is res0 */
config->addr_acc[idx] &= ~(GENMASK(14, 8));
config->addr_acc[idx] |= (val << 8);
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(addr_exlevel_s_ns);
static const char * const addr_type_names[] = {
"unused",
"single",
"range",
"start",
"stop"
};
static ssize_t addr_cmp_view_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
u8 idx, addr_type;
unsigned long addr_v, addr_v2, addr_ctrl;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
int size = 0;
bool exclude = false;
spin_lock(&drvdata->spinlock);
idx = config->addr_idx;
addr_v = config->addr_val[idx];
addr_ctrl = config->addr_acc[idx];
addr_type = config->addr_type[idx];
if (addr_type == ETM_ADDR_TYPE_RANGE) {
if (idx & 0x1) {
idx -= 1;
addr_v2 = addr_v;
addr_v = config->addr_val[idx];
} else {
addr_v2 = config->addr_val[idx + 1];
}
exclude = config->viiectlr & BIT(idx / 2 + 16);
}
spin_unlock(&drvdata->spinlock);
if (addr_type) {
size = scnprintf(buf, PAGE_SIZE, "addr_cmp[%i] %s %#lx", idx,
addr_type_names[addr_type], addr_v);
if (addr_type == ETM_ADDR_TYPE_RANGE) {
size += scnprintf(buf + size, PAGE_SIZE - size,
" %#lx %s", addr_v2,
exclude ? "exclude" : "include");
}
size += scnprintf(buf + size, PAGE_SIZE - size,
" ctrl(%#lx)\n", addr_ctrl);
} else {
size = scnprintf(buf, PAGE_SIZE, "addr_cmp[%i] unused\n", idx);
}
return size;
}
static DEVICE_ATTR_RO(addr_cmp_view);
static ssize_t vinst_pe_cmp_start_stop_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
unsigned long val;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
if (!drvdata->nr_pe_cmp)
return -EINVAL;
val = config->vipcssctlr;
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static ssize_t vinst_pe_cmp_start_stop_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
unsigned long val;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
if (!drvdata->nr_pe_cmp)
return -EINVAL;
spin_lock(&drvdata->spinlock);
config->vipcssctlr = val;
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(vinst_pe_cmp_start_stop);
static ssize_t seq_idx_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@ -1324,8 +1456,8 @@ static ssize_t seq_event_store(struct device *dev,
spin_lock(&drvdata->spinlock);
idx = config->seq_idx;
/* RST, bits[7:0] */
config->seq_ctrl[idx] = val & 0xFF;
/* Seq control has two masks B[15:8] F[7:0] */
config->seq_ctrl[idx] = val & 0xFFFF;
spin_unlock(&drvdata->spinlock);
return size;
}
@ -1580,12 +1712,129 @@ static ssize_t res_ctrl_store(struct device *dev,
if (idx % 2 != 0)
/* PAIRINV, bit[21] */
val &= ~BIT(21);
config->res_ctrl[idx] = val;
config->res_ctrl[idx] = val & GENMASK(21, 0);
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(res_ctrl);
static ssize_t sshot_idx_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned long val;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
val = config->ss_idx;
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static ssize_t sshot_idx_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
unsigned long val;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
if (val >= drvdata->nr_ss_cmp)
return -EINVAL;
spin_lock(&drvdata->spinlock);
config->ss_idx = val;
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(sshot_idx);
static ssize_t sshot_ctrl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
unsigned long val;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
val = config->ss_ctrl[config->ss_idx];
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static ssize_t sshot_ctrl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
u8 idx;
unsigned long val;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
idx = config->ss_idx;
config->ss_ctrl[idx] = val & GENMASK(24, 0);
/* must clear bit 31 in related status register on programming */
config->ss_status[idx] &= ~BIT(31);
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(sshot_ctrl);
static ssize_t sshot_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned long val;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
val = config->ss_status[config->ss_idx];
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static DEVICE_ATTR_RO(sshot_status);
static ssize_t sshot_pe_ctrl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
unsigned long val;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
val = config->ss_pe_cmp[config->ss_idx];
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static ssize_t sshot_pe_ctrl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
u8 idx;
unsigned long val;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
idx = config->ss_idx;
config->ss_pe_cmp[idx] = val & GENMASK(7, 0);
/* must clear bit 31 in related status register on programming */
config->ss_status[idx] &= ~BIT(31);
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(sshot_pe_ctrl);
static ssize_t ctxid_idx_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@ -1714,6 +1963,7 @@ static ssize_t ctxid_masks_store(struct device *dev,
unsigned long val1, val2, mask;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
int nr_inputs;
/*
* Don't use contextID tracing if coming from a PID namespace. See
@ -1729,7 +1979,9 @@ static ssize_t ctxid_masks_store(struct device *dev,
*/
if (!drvdata->ctxid_size || !drvdata->numcidc)
return -EINVAL;
if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
/* one mask if <= 4 comparators, two for up to 8 */
nr_inputs = sscanf(buf, "%lx %lx", &val1, &val2);
if ((drvdata->numcidc > 4) && (nr_inputs != 2))
return -EINVAL;
spin_lock(&drvdata->spinlock);
@ -1903,6 +2155,7 @@ static ssize_t vmid_masks_store(struct device *dev,
unsigned long val1, val2, mask;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
int nr_inputs;
/*
* only implemented when vmid tracing is enabled, i.e. at least one
@ -1910,7 +2163,9 @@ static ssize_t vmid_masks_store(struct device *dev,
*/
if (!drvdata->vmid_size || !drvdata->numvmidc)
return -EINVAL;
if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
/* one mask if <= 4 comparators, two for up to 8 */
nr_inputs = sscanf(buf, "%lx %lx", &val1, &val2);
if ((drvdata->numvmidc > 4) && (nr_inputs != 2))
return -EINVAL;
spin_lock(&drvdata->spinlock);
@ -2033,6 +2288,13 @@ static struct attribute *coresight_etmv4_attrs[] = {
&dev_attr_addr_stop.attr,
&dev_attr_addr_ctxtype.attr,
&dev_attr_addr_context.attr,
&dev_attr_addr_exlevel_s_ns.attr,
&dev_attr_addr_cmp_view.attr,
&dev_attr_vinst_pe_cmp_start_stop.attr,
&dev_attr_sshot_idx.attr,
&dev_attr_sshot_ctrl.attr,
&dev_attr_sshot_pe_ctrl.attr,
&dev_attr_sshot_status.attr,
&dev_attr_seq_idx.attr,
&dev_attr_seq_state.attr,
&dev_attr_seq_event.attr,

View File

@ -18,6 +18,7 @@
#include <linux/stat.h>
#include <linux/clk.h>
#include <linux/cpu.h>
#include <linux/cpu_pm.h>
#include <linux/coresight.h>
#include <linux/coresight-pmu.h>
#include <linux/pm_wakeup.h>
@ -26,6 +27,7 @@
#include <linux/uaccess.h>
#include <linux/perf_event.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <asm/sections.h>
#include <asm/local.h>
#include <asm/virt.h>
@ -37,6 +39,15 @@ static int boot_enable;
module_param(boot_enable, int, 0444);
MODULE_PARM_DESC(boot_enable, "Enable tracing on boot");
#define PARAM_PM_SAVE_FIRMWARE 0 /* save self-hosted state as per firmware */
#define PARAM_PM_SAVE_NEVER 1 /* never save any state */
#define PARAM_PM_SAVE_SELF_HOSTED 2 /* save self-hosted state only */
static int pm_save_enable = PARAM_PM_SAVE_FIRMWARE;
module_param(pm_save_enable, int, 0444);
MODULE_PARM_DESC(pm_save_enable,
"Save/restore state on power down: 1 = never, 2 = self-hosted");
/* The number of ETMv4 currently registered */
static int etm4_count;
static struct etmv4_drvdata *etmdrvdata[NR_CPUS];
@ -54,6 +65,14 @@ static void etm4_os_unlock(struct etmv4_drvdata *drvdata)
isb();
}
static void etm4_os_lock(struct etmv4_drvdata *drvdata)
{
/* Writing 0x1 to TRCOSLAR locks the trace registers */
writel_relaxed(0x1, drvdata->base + TRCOSLAR);
drvdata->os_unlock = false;
isb();
}
static bool etm4_arch_supported(u8 arch)
{
/* Mask out the minor version number */
@ -149,6 +168,9 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata)
drvdata->base + TRCRSCTLRn(i));
for (i = 0; i < drvdata->nr_ss_cmp; i++) {
/* always clear status bit on restart if using single-shot */
if (config->ss_ctrl[i] || config->ss_pe_cmp[i])
config->ss_status[i] &= ~BIT(31);
writel_relaxed(config->ss_ctrl[i],
drvdata->base + TRCSSCCRn(i));
writel_relaxed(config->ss_status[i],
@ -448,6 +470,9 @@ static void etm4_disable_hw(void *info)
{
u32 control;
struct etmv4_drvdata *drvdata = info;
struct etmv4_config *config = &drvdata->config;
struct device *etm_dev = &drvdata->csdev->dev;
int i;
CS_UNLOCK(drvdata->base);
@ -470,6 +495,18 @@ static void etm4_disable_hw(void *info)
isb();
writel_relaxed(control, drvdata->base + TRCPRGCTLR);
/* wait for TRCSTATR.PMSTABLE to go to '1' */
if (coresight_timeout(drvdata->base, TRCSTATR,
TRCSTATR_PMSTABLE_BIT, 1))
dev_err(etm_dev,
"timeout while waiting for PM stable Trace Status\n");
/* read the status of the single shot comparators */
for (i = 0; i < drvdata->nr_ss_cmp; i++) {
config->ss_status[i] =
readl_relaxed(drvdata->base + TRCSSCSRn(i));
}
coresight_disclaim_device_unlocked(drvdata->base);
CS_LOCK(drvdata->base);
@ -576,6 +613,7 @@ static void etm4_init_arch_data(void *info)
u32 etmidr4;
u32 etmidr5;
struct etmv4_drvdata *drvdata = info;
int i;
/* Make sure all registers are accessible */
etm4_os_unlock(drvdata);
@ -629,6 +667,7 @@ static void etm4_init_arch_data(void *info)
* TRCARCHMAJ, bits[11:8] architecture major versin number
*/
drvdata->arch = BMVAL(etmidr1, 4, 11);
drvdata->config.arch = drvdata->arch;
/* maximum size of resources */
etmidr2 = readl_relaxed(drvdata->base + TRCIDR2);
@ -698,9 +737,14 @@ static void etm4_init_arch_data(void *info)
drvdata->nr_resource = BMVAL(etmidr4, 16, 19) + 1;
/*
* NUMSSCC, bits[23:20] the number of single-shot
* comparator control for tracing
* comparator control for tracing. Read any status regs as these
* also contain RO capability data.
*/
drvdata->nr_ss_cmp = BMVAL(etmidr4, 20, 23);
for (i = 0; i < drvdata->nr_ss_cmp; i++) {
drvdata->config.ss_status[i] =
readl_relaxed(drvdata->base + TRCSSCSRn(i));
}
/* NUMCIDC, bits[27:24] number of Context ID comparators for tracing */
drvdata->numcidc = BMVAL(etmidr4, 24, 27);
/* NUMVMIDC, bits[31:28] number of VMID comparators for tracing */
@ -780,6 +824,7 @@ static u64 etm4_get_ns_access_type(struct etmv4_config *config)
static u64 etm4_get_access_type(struct etmv4_config *config)
{
u64 access_type = etm4_get_ns_access_type(config);
u64 s_hyp = (config->arch & 0x0f) >= 0x4 ? ETM_EXLEVEL_S_HYP : 0;
/*
* EXLEVEL_S, bits[11:8], don't trace anything happening
@ -787,7 +832,8 @@ static u64 etm4_get_access_type(struct etmv4_config *config)
*/
access_type |= (ETM_EXLEVEL_S_APP |
ETM_EXLEVEL_S_OS |
ETM_EXLEVEL_S_HYP);
s_hyp |
ETM_EXLEVEL_S_MON);
return access_type;
}
@ -865,6 +911,7 @@ static void etm4_set_default_filter(struct etmv4_config *config)
* in the started state
*/
config->vinst_ctrl |= BIT(9);
config->mode |= ETM_MODE_VIEWINST_STARTSTOP;
/* No start-stop filtering for ViewInst */
config->vissctlr = 0x0;
@ -1085,6 +1132,288 @@ static void etm4_init_trace_id(struct etmv4_drvdata *drvdata)
drvdata->trcid = coresight_get_trace_id(drvdata->cpu);
}
#ifdef CONFIG_CPU_PM
static int etm4_cpu_save(struct etmv4_drvdata *drvdata)
{
int i, ret = 0;
struct etmv4_save_state *state;
struct device *etm_dev = &drvdata->csdev->dev;
/*
* As recommended by 3.4.1 ("The procedure when powering down the PE")
* of ARM IHI 0064D
*/
dsb(sy);
isb();
CS_UNLOCK(drvdata->base);
/* Lock the OS lock to disable trace and external debugger access */
etm4_os_lock(drvdata);
/* wait for TRCSTATR.PMSTABLE to go up */
if (coresight_timeout(drvdata->base, TRCSTATR,
TRCSTATR_PMSTABLE_BIT, 1)) {
dev_err(etm_dev,
"timeout while waiting for PM Stable Status\n");
etm4_os_unlock(drvdata);
ret = -EBUSY;
goto out;
}
state = drvdata->save_state;
state->trcprgctlr = readl(drvdata->base + TRCPRGCTLR);
state->trcprocselr = readl(drvdata->base + TRCPROCSELR);
state->trcconfigr = readl(drvdata->base + TRCCONFIGR);
state->trcauxctlr = readl(drvdata->base + TRCAUXCTLR);
state->trceventctl0r = readl(drvdata->base + TRCEVENTCTL0R);
state->trceventctl1r = readl(drvdata->base + TRCEVENTCTL1R);
state->trcstallctlr = readl(drvdata->base + TRCSTALLCTLR);
state->trctsctlr = readl(drvdata->base + TRCTSCTLR);
state->trcsyncpr = readl(drvdata->base + TRCSYNCPR);
state->trcccctlr = readl(drvdata->base + TRCCCCTLR);
state->trcbbctlr = readl(drvdata->base + TRCBBCTLR);
state->trctraceidr = readl(drvdata->base + TRCTRACEIDR);
state->trcqctlr = readl(drvdata->base + TRCQCTLR);
state->trcvictlr = readl(drvdata->base + TRCVICTLR);
state->trcviiectlr = readl(drvdata->base + TRCVIIECTLR);
state->trcvissctlr = readl(drvdata->base + TRCVISSCTLR);
state->trcvipcssctlr = readl(drvdata->base + TRCVIPCSSCTLR);
state->trcvdctlr = readl(drvdata->base + TRCVDCTLR);
state->trcvdsacctlr = readl(drvdata->base + TRCVDSACCTLR);
state->trcvdarcctlr = readl(drvdata->base + TRCVDARCCTLR);
for (i = 0; i < drvdata->nrseqstate; i++)
state->trcseqevr[i] = readl(drvdata->base + TRCSEQEVRn(i));
state->trcseqrstevr = readl(drvdata->base + TRCSEQRSTEVR);
state->trcseqstr = readl(drvdata->base + TRCSEQSTR);
state->trcextinselr = readl(drvdata->base + TRCEXTINSELR);
for (i = 0; i < drvdata->nr_cntr; i++) {
state->trccntrldvr[i] = readl(drvdata->base + TRCCNTRLDVRn(i));
state->trccntctlr[i] = readl(drvdata->base + TRCCNTCTLRn(i));
state->trccntvr[i] = readl(drvdata->base + TRCCNTVRn(i));
}
for (i = 0; i < drvdata->nr_resource * 2; i++)
state->trcrsctlr[i] = readl(drvdata->base + TRCRSCTLRn(i));
for (i = 0; i < drvdata->nr_ss_cmp; i++) {
state->trcssccr[i] = readl(drvdata->base + TRCSSCCRn(i));
state->trcsscsr[i] = readl(drvdata->base + TRCSSCSRn(i));
state->trcsspcicr[i] = readl(drvdata->base + TRCSSPCICRn(i));
}
for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) {
state->trcacvr[i] = readl(drvdata->base + TRCACVRn(i));
state->trcacatr[i] = readl(drvdata->base + TRCACATRn(i));
}
/*
* Data trace stream is architecturally prohibited for A profile cores
* so we don't save (or later restore) trcdvcvr and trcdvcmr - As per
* section 1.3.4 ("Possible functional configurations of an ETMv4 trace
* unit") of ARM IHI 0064D.
*/
for (i = 0; i < drvdata->numcidc; i++)
state->trccidcvr[i] = readl(drvdata->base + TRCCIDCVRn(i));
for (i = 0; i < drvdata->numvmidc; i++)
state->trcvmidcvr[i] = readl(drvdata->base + TRCVMIDCVRn(i));
state->trccidcctlr0 = readl(drvdata->base + TRCCIDCCTLR0);
state->trccidcctlr1 = readl(drvdata->base + TRCCIDCCTLR1);
state->trcvmidcctlr0 = readl(drvdata->base + TRCVMIDCCTLR0);
state->trcvmidcctlr0 = readl(drvdata->base + TRCVMIDCCTLR1);
state->trcclaimset = readl(drvdata->base + TRCCLAIMCLR);
state->trcpdcr = readl(drvdata->base + TRCPDCR);
/* wait for TRCSTATR.IDLE to go up */
if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 1)) {
dev_err(etm_dev,
"timeout while waiting for Idle Trace Status\n");
etm4_os_unlock(drvdata);
ret = -EBUSY;
goto out;
}
drvdata->state_needs_restore = true;
/*
* Power can be removed from the trace unit now. We do this to
* potentially save power on systems that respect the TRCPDCR_PU
* despite requesting software to save/restore state.
*/
writel_relaxed((state->trcpdcr & ~TRCPDCR_PU),
drvdata->base + TRCPDCR);
out:
CS_LOCK(drvdata->base);
return ret;
}
static void etm4_cpu_restore(struct etmv4_drvdata *drvdata)
{
int i;
struct etmv4_save_state *state = drvdata->save_state;
CS_UNLOCK(drvdata->base);
writel_relaxed(state->trcclaimset, drvdata->base + TRCCLAIMSET);
writel_relaxed(state->trcprgctlr, drvdata->base + TRCPRGCTLR);
writel_relaxed(state->trcprocselr, drvdata->base + TRCPROCSELR);
writel_relaxed(state->trcconfigr, drvdata->base + TRCCONFIGR);
writel_relaxed(state->trcauxctlr, drvdata->base + TRCAUXCTLR);
writel_relaxed(state->trceventctl0r, drvdata->base + TRCEVENTCTL0R);
writel_relaxed(state->trceventctl1r, drvdata->base + TRCEVENTCTL1R);
writel_relaxed(state->trcstallctlr, drvdata->base + TRCSTALLCTLR);
writel_relaxed(state->trctsctlr, drvdata->base + TRCTSCTLR);
writel_relaxed(state->trcsyncpr, drvdata->base + TRCSYNCPR);
writel_relaxed(state->trcccctlr, drvdata->base + TRCCCCTLR);
writel_relaxed(state->trcbbctlr, drvdata->base + TRCBBCTLR);
writel_relaxed(state->trctraceidr, drvdata->base + TRCTRACEIDR);
writel_relaxed(state->trcqctlr, drvdata->base + TRCQCTLR);
writel_relaxed(state->trcvictlr, drvdata->base + TRCVICTLR);
writel_relaxed(state->trcviiectlr, drvdata->base + TRCVIIECTLR);
writel_relaxed(state->trcvissctlr, drvdata->base + TRCVISSCTLR);
writel_relaxed(state->trcvipcssctlr, drvdata->base + TRCVIPCSSCTLR);
writel_relaxed(state->trcvdctlr, drvdata->base + TRCVDCTLR);
writel_relaxed(state->trcvdsacctlr, drvdata->base + TRCVDSACCTLR);
writel_relaxed(state->trcvdarcctlr, drvdata->base + TRCVDARCCTLR);
for (i = 0; i < drvdata->nrseqstate; i++)
writel_relaxed(state->trcseqevr[i],
drvdata->base + TRCSEQEVRn(i));
writel_relaxed(state->trcseqrstevr, drvdata->base + TRCSEQRSTEVR);
writel_relaxed(state->trcseqstr, drvdata->base + TRCSEQSTR);
writel_relaxed(state->trcextinselr, drvdata->base + TRCEXTINSELR);
for (i = 0; i < drvdata->nr_cntr; i++) {
writel_relaxed(state->trccntrldvr[i],
drvdata->base + TRCCNTRLDVRn(i));
writel_relaxed(state->trccntctlr[i],
drvdata->base + TRCCNTCTLRn(i));
writel_relaxed(state->trccntvr[i],
drvdata->base + TRCCNTVRn(i));
}
for (i = 0; i < drvdata->nr_resource * 2; i++)
writel_relaxed(state->trcrsctlr[i],
drvdata->base + TRCRSCTLRn(i));
for (i = 0; i < drvdata->nr_ss_cmp; i++) {
writel_relaxed(state->trcssccr[i],
drvdata->base + TRCSSCCRn(i));
writel_relaxed(state->trcsscsr[i],
drvdata->base + TRCSSCSRn(i));
writel_relaxed(state->trcsspcicr[i],
drvdata->base + TRCSSPCICRn(i));
}
for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) {
writel_relaxed(state->trcacvr[i],
drvdata->base + TRCACVRn(i));
writel_relaxed(state->trcacatr[i],
drvdata->base + TRCACATRn(i));
}
for (i = 0; i < drvdata->numcidc; i++)
writel_relaxed(state->trccidcvr[i],
drvdata->base + TRCCIDCVRn(i));
for (i = 0; i < drvdata->numvmidc; i++)
writel_relaxed(state->trcvmidcvr[i],
drvdata->base + TRCVMIDCVRn(i));
writel_relaxed(state->trccidcctlr0, drvdata->base + TRCCIDCCTLR0);
writel_relaxed(state->trccidcctlr1, drvdata->base + TRCCIDCCTLR1);
writel_relaxed(state->trcvmidcctlr0, drvdata->base + TRCVMIDCCTLR0);
writel_relaxed(state->trcvmidcctlr0, drvdata->base + TRCVMIDCCTLR1);
writel_relaxed(state->trcclaimset, drvdata->base + TRCCLAIMSET);
writel_relaxed(state->trcpdcr, drvdata->base + TRCPDCR);
drvdata->state_needs_restore = false;
/*
* As recommended by section 4.3.7 ("Synchronization when using the
* memory-mapped interface") of ARM IHI 0064D
*/
dsb(sy);
isb();
/* Unlock the OS lock to re-enable trace and external debug access */
etm4_os_unlock(drvdata);
CS_LOCK(drvdata->base);
}
static int etm4_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
void *v)
{
struct etmv4_drvdata *drvdata;
unsigned int cpu = smp_processor_id();
if (!etmdrvdata[cpu])
return NOTIFY_OK;
drvdata = etmdrvdata[cpu];
if (!drvdata->save_state)
return NOTIFY_OK;
if (WARN_ON_ONCE(drvdata->cpu != cpu))
return NOTIFY_BAD;
switch (cmd) {
case CPU_PM_ENTER:
/* save the state if self-hosted coresight is in use */
if (local_read(&drvdata->mode))
if (etm4_cpu_save(drvdata))
return NOTIFY_BAD;
break;
case CPU_PM_EXIT:
/* fallthrough */
case CPU_PM_ENTER_FAILED:
if (drvdata->state_needs_restore)
etm4_cpu_restore(drvdata);
break;
default:
return NOTIFY_DONE;
}
return NOTIFY_OK;
}
static struct notifier_block etm4_cpu_pm_nb = {
.notifier_call = etm4_cpu_pm_notify,
};
static int etm4_cpu_pm_register(void)
{
return cpu_pm_register_notifier(&etm4_cpu_pm_nb);
}
static void etm4_cpu_pm_unregister(void)
{
cpu_pm_unregister_notifier(&etm4_cpu_pm_nb);
}
#else
static int etm4_cpu_pm_register(void) { return 0; }
static void etm4_cpu_pm_unregister(void) { }
#endif
static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret;
@ -1101,6 +1430,17 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
dev_set_drvdata(dev, drvdata);
if (pm_save_enable == PARAM_PM_SAVE_FIRMWARE)
pm_save_enable = coresight_loses_context_with_cpu(dev) ?
PARAM_PM_SAVE_SELF_HOSTED : PARAM_PM_SAVE_NEVER;
if (pm_save_enable != PARAM_PM_SAVE_NEVER) {
drvdata->save_state = devm_kmalloc(dev,
sizeof(struct etmv4_save_state), GFP_KERNEL);
if (!drvdata->save_state)
return -ENOMEM;
}
/* Validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
@ -1135,6 +1475,10 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
if (ret < 0)
goto err_arch_supported;
hp_online = ret;
ret = etm4_cpu_pm_register();
if (ret)
goto err_arch_supported;
}
cpus_read_unlock();
@ -1185,6 +1529,8 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
err_arch_supported:
if (--etm4_count == 0) {
etm4_cpu_pm_unregister();
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
if (hp_online)
cpuhp_remove_state_nocalls(hp_online);
@ -1211,6 +1557,7 @@ static const struct amba_id etm4_ids[] = {
CS_AMBA_UCI_ID(0x000f0211, uci_id_etm4),/* Qualcomm Kryo */
CS_AMBA_ID(0x000bb802), /* Qualcomm Kryo 385 Cortex-A55 */
CS_AMBA_ID(0x000bb803), /* Qualcomm Kryo 385 Cortex-A75 */
CS_AMBA_UCI_ID(0x000cc0af, uci_id_etm4),/* Marvell ThunderX2 */
{},
};

View File

@ -175,22 +175,28 @@
ETM_MODE_EXCL_USER)
#define TRCSTATR_IDLE_BIT 0
#define TRCSTATR_PMSTABLE_BIT 1
#define ETM_DEFAULT_ADDR_COMP 0
/* PowerDown Control Register bits */
#define TRCPDCR_PU BIT(3)
/* secure state access levels */
/* secure state access levels - TRCACATRn */
#define ETM_EXLEVEL_S_APP BIT(8)
#define ETM_EXLEVEL_S_OS BIT(9)
#define ETM_EXLEVEL_S_NA BIT(10)
#define ETM_EXLEVEL_S_HYP BIT(11)
/* non-secure state access levels */
#define ETM_EXLEVEL_S_HYP BIT(10)
#define ETM_EXLEVEL_S_MON BIT(11)
/* non-secure state access levels - TRCACATRn */
#define ETM_EXLEVEL_NS_APP BIT(12)
#define ETM_EXLEVEL_NS_OS BIT(13)
#define ETM_EXLEVEL_NS_HYP BIT(14)
#define ETM_EXLEVEL_NS_NA BIT(15)
/* secure / non secure masks - TRCVICTLR, IDR3 */
#define ETM_EXLEVEL_S_VICTLR_MASK GENMASK(19, 16)
/* NS MON (EL3) mode never implemented */
#define ETM_EXLEVEL_NS_VICTLR_MASK GENMASK(22, 20)
/**
* struct etmv4_config - configuration information related to an ETMv4
* @mode: Controls various modes supported by this ETM.
@ -221,6 +227,7 @@
* @cntr_val: Sets or returns the value for a counter.
* @res_idx: Resource index selector.
* @res_ctrl: Controls the selection of the resources in the trace unit.
* @ss_idx: Single-shot index selector.
* @ss_ctrl: Controls the corresponding single-shot comparator resource.
* @ss_status: The status of the corresponding single-shot comparator.
* @ss_pe_cmp: Selects the PE comparator inputs for Single-shot control.
@ -237,6 +244,7 @@
* @vmid_mask0: VM ID comparator mask for comparator 0-3.
* @vmid_mask1: VM ID comparator mask for comparator 4-7.
* @ext_inp: External input selection.
* @arch: ETM architecture version (for arch dependent config).
*/
struct etmv4_config {
u32 mode;
@ -263,6 +271,7 @@ struct etmv4_config {
u32 cntr_val[ETMv4_MAX_CNTR];
u8 res_idx;
u32 res_ctrl[ETM_MAX_RES_SEL];
u8 ss_idx;
u32 ss_ctrl[ETM_MAX_SS_CMP];
u32 ss_status[ETM_MAX_SS_CMP];
u32 ss_pe_cmp[ETM_MAX_SS_CMP];
@ -279,6 +288,66 @@ struct etmv4_config {
u32 vmid_mask0;
u32 vmid_mask1;
u32 ext_inp;
u8 arch;
};
/**
* struct etm4_save_state - state to be preserved when ETM is without power
*/
struct etmv4_save_state {
u32 trcprgctlr;
u32 trcprocselr;
u32 trcconfigr;
u32 trcauxctlr;
u32 trceventctl0r;
u32 trceventctl1r;
u32 trcstallctlr;
u32 trctsctlr;
u32 trcsyncpr;
u32 trcccctlr;
u32 trcbbctlr;
u32 trctraceidr;
u32 trcqctlr;
u32 trcvictlr;
u32 trcviiectlr;
u32 trcvissctlr;
u32 trcvipcssctlr;
u32 trcvdctlr;
u32 trcvdsacctlr;
u32 trcvdarcctlr;
u32 trcseqevr[ETM_MAX_SEQ_STATES];
u32 trcseqrstevr;
u32 trcseqstr;
u32 trcextinselr;
u32 trccntrldvr[ETMv4_MAX_CNTR];
u32 trccntctlr[ETMv4_MAX_CNTR];
u32 trccntvr[ETMv4_MAX_CNTR];
u32 trcrsctlr[ETM_MAX_RES_SEL * 2];
u32 trcssccr[ETM_MAX_SS_CMP];
u32 trcsscsr[ETM_MAX_SS_CMP];
u32 trcsspcicr[ETM_MAX_SS_CMP];
u64 trcacvr[ETM_MAX_SINGLE_ADDR_CMP];
u64 trcacatr[ETM_MAX_SINGLE_ADDR_CMP];
u64 trccidcvr[ETMv4_MAX_CTXID_CMP];
u32 trcvmidcvr[ETM_MAX_VMID_CMP];
u32 trccidcctlr0;
u32 trccidcctlr1;
u32 trcvmidcctlr0;
u32 trcvmidcctlr1;
u32 trcclaimset;
u32 cntr_val[ETMv4_MAX_CNTR];
u32 seq_state;
u32 vinst_ctrl;
u32 ss_status[ETM_MAX_SS_CMP];
u32 trcpdcr;
};
/**
@ -336,6 +405,8 @@ struct etmv4_config {
* @atbtrig: If the implementation can support ATB triggers
* @lpoverride: If the implementation can support low-power state over.
* @config: structure holding configuration parameters.
* @save_state: State to be preserved across power loss
* @state_needs_restore: True when there is context to restore after PM exit
*/
struct etmv4_drvdata {
void __iomem *base;
@ -381,6 +452,8 @@ struct etmv4_drvdata {
bool atbtrig;
bool lpoverride;
struct etmv4_config config;
struct etmv4_save_state *save_state;
bool state_needs_restore;
};
/* Address comparator access types */

View File

@ -38,12 +38,14 @@ DEFINE_CORESIGHT_DEVLIST(funnel_devs, "funnel");
* @atclk: optional clock for the core parts of the funnel.
* @csdev: component vitals needed by the framework.
* @priority: port selection order.
* @spinlock: serialize enable/disable operations.
*/
struct funnel_drvdata {
void __iomem *base;
struct clk *atclk;
struct coresight_device *csdev;
unsigned long priority;
spinlock_t spinlock;
};
static int dynamic_funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
@ -76,11 +78,21 @@ static int funnel_enable(struct coresight_device *csdev, int inport,
{
int rc = 0;
struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
unsigned long flags;
bool first_enable = false;
if (drvdata->base)
rc = dynamic_funnel_enable_hw(drvdata, inport);
spin_lock_irqsave(&drvdata->spinlock, flags);
if (atomic_read(&csdev->refcnt[inport]) == 0) {
if (drvdata->base)
rc = dynamic_funnel_enable_hw(drvdata, inport);
if (!rc)
first_enable = true;
}
if (!rc)
atomic_inc(&csdev->refcnt[inport]);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
if (first_enable)
dev_dbg(&csdev->dev, "FUNNEL inport %d enabled\n", inport);
return rc;
}
@ -107,11 +119,19 @@ static void funnel_disable(struct coresight_device *csdev, int inport,
int outport)
{
struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
unsigned long flags;
bool last_disable = false;
if (drvdata->base)
dynamic_funnel_disable_hw(drvdata, inport);
spin_lock_irqsave(&drvdata->spinlock, flags);
if (atomic_dec_return(&csdev->refcnt[inport]) == 0) {
if (drvdata->base)
dynamic_funnel_disable_hw(drvdata, inport);
last_disable = true;
}
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_dbg(&csdev->dev, "FUNNEL inport %d disabled\n", inport);
if (last_disable)
dev_dbg(&csdev->dev, "FUNNEL inport %d disabled\n", inport);
}
static const struct coresight_ops_link funnel_link_ops = {
@ -233,6 +253,7 @@ static int funnel_probe(struct device *dev, struct resource *res)
}
dev->platform_data = pdata;
spin_lock_init(&drvdata->spinlock);
desc.type = CORESIGHT_DEV_TYPE_LINK;
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
desc.ops = &funnel_cs_ops;

View File

@ -31,11 +31,13 @@ DEFINE_CORESIGHT_DEVLIST(replicator_devs, "replicator");
* whether this one is programmable or not.
* @atclk: optional clock for the core parts of the replicator.
* @csdev: component vitals needed by the framework
* @spinlock: serialize enable/disable operations.
*/
struct replicator_drvdata {
void __iomem *base;
struct clk *atclk;
struct coresight_device *csdev;
spinlock_t spinlock;
};
static void dynamic_replicator_reset(struct replicator_drvdata *drvdata)
@ -97,10 +99,22 @@ static int replicator_enable(struct coresight_device *csdev, int inport,
{
int rc = 0;
struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
unsigned long flags;
bool first_enable = false;
if (drvdata->base)
rc = dynamic_replicator_enable(drvdata, inport, outport);
spin_lock_irqsave(&drvdata->spinlock, flags);
if (atomic_read(&csdev->refcnt[outport]) == 0) {
if (drvdata->base)
rc = dynamic_replicator_enable(drvdata, inport,
outport);
if (!rc)
first_enable = true;
}
if (!rc)
atomic_inc(&csdev->refcnt[outport]);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
if (first_enable)
dev_dbg(&csdev->dev, "REPLICATOR enabled\n");
return rc;
}
@ -137,10 +151,19 @@ static void replicator_disable(struct coresight_device *csdev, int inport,
int outport)
{
struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
unsigned long flags;
bool last_disable = false;
if (drvdata->base)
dynamic_replicator_disable(drvdata, inport, outport);
dev_dbg(&csdev->dev, "REPLICATOR disabled\n");
spin_lock_irqsave(&drvdata->spinlock, flags);
if (atomic_dec_return(&csdev->refcnt[outport]) == 0) {
if (drvdata->base)
dynamic_replicator_disable(drvdata, inport, outport);
last_disable = true;
}
spin_unlock_irqrestore(&drvdata->spinlock, flags);
if (last_disable)
dev_dbg(&csdev->dev, "REPLICATOR disabled\n");
}
static const struct coresight_ops_link replicator_link_ops = {
@ -225,6 +248,7 @@ static int replicator_probe(struct device *dev, struct resource *res)
}
dev->platform_data = pdata;
spin_lock_init(&drvdata->spinlock);
desc.type = CORESIGHT_DEV_TYPE_LINK;
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
desc.ops = &replicator_cs_ops;

View File

@ -334,9 +334,10 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev)
static int tmc_enable_etf_link(struct coresight_device *csdev,
int inport, int outport)
{
int ret;
int ret = 0;
unsigned long flags;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
bool first_enable = false;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
@ -344,12 +345,18 @@ static int tmc_enable_etf_link(struct coresight_device *csdev,
return -EBUSY;
}
ret = tmc_etf_enable_hw(drvdata);
if (atomic_read(&csdev->refcnt[0]) == 0) {
ret = tmc_etf_enable_hw(drvdata);
if (!ret) {
drvdata->mode = CS_MODE_SYSFS;
first_enable = true;
}
}
if (!ret)
drvdata->mode = CS_MODE_SYSFS;
atomic_inc(&csdev->refcnt[0]);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
if (!ret)
if (first_enable)
dev_dbg(&csdev->dev, "TMC-ETF enabled\n");
return ret;
}
@ -359,6 +366,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
{
unsigned long flags;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
bool last_disable = false;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
@ -366,11 +374,15 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
return;
}
tmc_etf_disable_hw(drvdata);
drvdata->mode = CS_MODE_DISABLED;
if (atomic_dec_return(&csdev->refcnt[0]) == 0) {
tmc_etf_disable_hw(drvdata);
drvdata->mode = CS_MODE_DISABLED;
last_disable = true;
}
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_dbg(&csdev->dev, "TMC-ETF disabled\n");
if (last_disable)
dev_dbg(&csdev->dev, "TMC-ETF disabled\n");
}
static void *tmc_alloc_etf_buffer(struct coresight_device *csdev,

View File

@ -253,9 +253,9 @@ static int coresight_enable_link(struct coresight_device *csdev,
struct coresight_device *parent,
struct coresight_device *child)
{
int ret;
int ret = 0;
int link_subtype;
int refport, inport, outport;
int inport, outport;
if (!parent || !child)
return -EINVAL;
@ -264,29 +264,17 @@ static int coresight_enable_link(struct coresight_device *csdev,
outport = coresight_find_link_outport(csdev, child);
link_subtype = csdev->subtype.link_subtype;
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG)
refport = inport;
else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT)
refport = outport;
else
refport = 0;
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG && inport < 0)
return inport;
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT && outport < 0)
return outport;
if (refport < 0)
return refport;
if (link_ops(csdev)->enable)
ret = link_ops(csdev)->enable(csdev, inport, outport);
if (!ret)
csdev->enable = true;
if (atomic_inc_return(&csdev->refcnt[refport]) == 1) {
if (link_ops(csdev)->enable) {
ret = link_ops(csdev)->enable(csdev, inport, outport);
if (ret) {
atomic_dec(&csdev->refcnt[refport]);
return ret;
}
}
}
csdev->enable = true;
return 0;
return ret;
}
static void coresight_disable_link(struct coresight_device *csdev,
@ -295,7 +283,7 @@ static void coresight_disable_link(struct coresight_device *csdev,
{
int i, nr_conns;
int link_subtype;
int refport, inport, outport;
int inport, outport;
if (!parent || !child)
return;
@ -305,20 +293,15 @@ static void coresight_disable_link(struct coresight_device *csdev,
link_subtype = csdev->subtype.link_subtype;
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) {
refport = inport;
nr_conns = csdev->pdata->nr_inport;
} else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT) {
refport = outport;
nr_conns = csdev->pdata->nr_outport;
} else {
refport = 0;
nr_conns = 1;
}
if (atomic_dec_return(&csdev->refcnt[refport]) == 0) {
if (link_ops(csdev)->disable)
link_ops(csdev)->disable(csdev, inport, outport);
}
if (link_ops(csdev)->disable)
link_ops(csdev)->disable(csdev, inport, outport);
for (i = 0; i < nr_conns; i++)
if (atomic_read(&csdev->refcnt[i]) != 0)
@ -1308,6 +1291,12 @@ static inline int coresight_search_device_idx(struct coresight_dev_list *dict,
return -ENOENT;
}
bool coresight_loses_context_with_cpu(struct device *dev)
{
return fwnode_property_present(dev_fwnode(dev),
"arm,coresight-loses-context-with-cpu");
}
/*
* coresight_alloc_device_name - Get an index for a given device in the
* device index list specific to a driver. An index is allocated for a

View File

@ -649,10 +649,8 @@ intel_th_subdevice_alloc(struct intel_th *th,
}
err = intel_th_device_add_resources(thdev, res, subdev->nres);
if (err) {
put_device(&thdev->dev);
if (err)
goto fail_put_device;
}
if (subdev->type == INTEL_TH_OUTPUT) {
if (subdev->mknode)
@ -667,10 +665,8 @@ intel_th_subdevice_alloc(struct intel_th *th,
}
err = device_add(&thdev->dev);
if (err) {
put_device(&thdev->dev);
if (err)
goto fail_free_res;
}
/* need switch driver to be loaded to enumerate the rest */
if (subdev->type == INTEL_TH_SWITCH && !req) {

View File

@ -209,6 +209,16 @@ static const struct pci_device_id intel_th_pci_id_table[] = {
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x45c5),
.driver_data = (kernel_ulong_t)&intel_th_2x,
},
{
/* Ice Lake CPU */
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8a29),
.driver_data = (kernel_ulong_t)&intel_th_2x,
},
{
/* Tiger Lake CPU */
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9a33),
.driver_data = (kernel_ulong_t)&intel_th_2x,
},
{
/* Tiger Lake PCH */
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa0a6),

View File

@ -345,7 +345,11 @@ void stp_policy_unbind(struct stp_policy *policy)
stm->policy = NULL;
policy->stm = NULL;
/*
* Drop the reference on the protocol driver and lose the link.
*/
stm_put_protocol(stm->pdrv);
stm->pdrv = NULL;
stm_put_device(stm);
}

View File

@ -167,3 +167,4 @@ MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("IIO ADC driver for MEN 16z188 ADC Core");
MODULE_ALIAS("mcb:16z188");
MODULE_IMPORT_NS(MCB);

View File

@ -5,6 +5,15 @@ config INTERCONNECT_QCOM
help
Support for Qualcomm's Network-on-Chip interconnect hardware.
config INTERCONNECT_QCOM_MSM8974
tristate "Qualcomm MSM8974 interconnect driver"
depends on INTERCONNECT_QCOM
depends on QCOM_SMD_RPM
select INTERCONNECT_QCOM_SMD_RPM
help
This is a driver for the Qualcomm Network-on-Chip on msm8974-based
platforms.
config INTERCONNECT_QCOM_QCS404
tristate "Qualcomm QCS404 interconnect driver"
depends on INTERCONNECT_QCOM

View File

@ -1,9 +1,11 @@
# SPDX-License-Identifier: GPL-2.0
qnoc-msm8974-objs := msm8974.o
qnoc-qcs404-objs := qcs404.o
qnoc-sdm845-objs := sdm845.o
icc-smd-rpm-objs := smd-rpm.o
obj-$(CONFIG_INTERCONNECT_QCOM_MSM8974) += qnoc-msm8974.o
obj-$(CONFIG_INTERCONNECT_QCOM_QCS404) += qnoc-qcs404.o
obj-$(CONFIG_INTERCONNECT_QCOM_SDM845) += qnoc-sdm845.o
obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM) += icc-smd-rpm.o

View File

@ -0,0 +1,784 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Brian Masney <masneyb@onstation.org>
*
* Based on MSM bus code from downstream MSM kernel sources.
* Copyright (c) 2012-2013 The Linux Foundation. All rights reserved.
*
* Based on qcs404.c
* Copyright (C) 2019 Linaro Ltd
*
* Here's a rough representation that shows the various buses that form the
* Network On Chip (NOC) for the msm8974:
*
* Multimedia Subsystem (MMSS)
* |----------+-----------------------------------+-----------|
* | |
* | |
* Config | Bus Interface | Memory Controller
* |------------+-+-----------| |------------+-+-----------|
* | |
* | |
* | System |
* |--------------+-+---------------------------------+-+-------------|
* | |
* | |
* Peripheral | On Chip | Memory (OCMEM)
* |------------+-------------| |------------+-------------|
*/
#include <dt-bindings/interconnect/qcom,msm8974.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/interconnect-provider.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "smd-rpm.h"
enum {
MSM8974_BIMC_MAS_AMPSS_M0 = 1,
MSM8974_BIMC_MAS_AMPSS_M1,
MSM8974_BIMC_MAS_MSS_PROC,
MSM8974_BIMC_TO_MNOC,
MSM8974_BIMC_TO_SNOC,
MSM8974_BIMC_SLV_EBI_CH0,
MSM8974_BIMC_SLV_AMPSS_L2,
MSM8974_CNOC_MAS_RPM_INST,
MSM8974_CNOC_MAS_RPM_DATA,
MSM8974_CNOC_MAS_RPM_SYS,
MSM8974_CNOC_MAS_DEHR,
MSM8974_CNOC_MAS_QDSS_DAP,
MSM8974_CNOC_MAS_SPDM,
MSM8974_CNOC_MAS_TIC,
MSM8974_CNOC_SLV_CLK_CTL,
MSM8974_CNOC_SLV_CNOC_MSS,
MSM8974_CNOC_SLV_SECURITY,
MSM8974_CNOC_SLV_TCSR,
MSM8974_CNOC_SLV_TLMM,
MSM8974_CNOC_SLV_CRYPTO_0_CFG,
MSM8974_CNOC_SLV_CRYPTO_1_CFG,
MSM8974_CNOC_SLV_IMEM_CFG,
MSM8974_CNOC_SLV_MESSAGE_RAM,
MSM8974_CNOC_SLV_BIMC_CFG,
MSM8974_CNOC_SLV_BOOT_ROM,
MSM8974_CNOC_SLV_PMIC_ARB,
MSM8974_CNOC_SLV_SPDM_WRAPPER,
MSM8974_CNOC_SLV_DEHR_CFG,
MSM8974_CNOC_SLV_MPM,
MSM8974_CNOC_SLV_QDSS_CFG,
MSM8974_CNOC_SLV_RBCPR_CFG,
MSM8974_CNOC_SLV_RBCPR_QDSS_APU_CFG,
MSM8974_CNOC_TO_SNOC,
MSM8974_CNOC_SLV_CNOC_ONOC_CFG,
MSM8974_CNOC_SLV_CNOC_MNOC_MMSS_CFG,
MSM8974_CNOC_SLV_CNOC_MNOC_CFG,
MSM8974_CNOC_SLV_PNOC_CFG,
MSM8974_CNOC_SLV_SNOC_MPU_CFG,
MSM8974_CNOC_SLV_SNOC_CFG,
MSM8974_CNOC_SLV_EBI1_DLL_CFG,
MSM8974_CNOC_SLV_PHY_APU_CFG,
MSM8974_CNOC_SLV_EBI1_PHY_CFG,
MSM8974_CNOC_SLV_RPM,
MSM8974_CNOC_SLV_SERVICE_CNOC,
MSM8974_MNOC_MAS_GRAPHICS_3D,
MSM8974_MNOC_MAS_JPEG,
MSM8974_MNOC_MAS_MDP_PORT0,
MSM8974_MNOC_MAS_VIDEO_P0,
MSM8974_MNOC_MAS_VIDEO_P1,
MSM8974_MNOC_MAS_VFE,
MSM8974_MNOC_TO_CNOC,
MSM8974_MNOC_TO_BIMC,
MSM8974_MNOC_SLV_CAMERA_CFG,
MSM8974_MNOC_SLV_DISPLAY_CFG,
MSM8974_MNOC_SLV_OCMEM_CFG,
MSM8974_MNOC_SLV_CPR_CFG,
MSM8974_MNOC_SLV_CPR_XPU_CFG,
MSM8974_MNOC_SLV_MISC_CFG,
MSM8974_MNOC_SLV_MISC_XPU_CFG,
MSM8974_MNOC_SLV_VENUS_CFG,
MSM8974_MNOC_SLV_GRAPHICS_3D_CFG,
MSM8974_MNOC_SLV_MMSS_CLK_CFG,
MSM8974_MNOC_SLV_MMSS_CLK_XPU_CFG,
MSM8974_MNOC_SLV_MNOC_MPU_CFG,
MSM8974_MNOC_SLV_ONOC_MPU_CFG,
MSM8974_MNOC_SLV_SERVICE_MNOC,
MSM8974_OCMEM_NOC_TO_OCMEM_VNOC,
MSM8974_OCMEM_MAS_JPEG_OCMEM,
MSM8974_OCMEM_MAS_MDP_OCMEM,
MSM8974_OCMEM_MAS_VIDEO_P0_OCMEM,
MSM8974_OCMEM_MAS_VIDEO_P1_OCMEM,
MSM8974_OCMEM_MAS_VFE_OCMEM,
MSM8974_OCMEM_MAS_CNOC_ONOC_CFG,
MSM8974_OCMEM_SLV_SERVICE_ONOC,
MSM8974_OCMEM_VNOC_TO_SNOC,
MSM8974_OCMEM_VNOC_TO_OCMEM_NOC,
MSM8974_OCMEM_VNOC_MAS_GFX3D,
MSM8974_OCMEM_SLV_OCMEM,
MSM8974_PNOC_MAS_PNOC_CFG,
MSM8974_PNOC_MAS_SDCC_1,
MSM8974_PNOC_MAS_SDCC_3,
MSM8974_PNOC_MAS_SDCC_4,
MSM8974_PNOC_MAS_SDCC_2,
MSM8974_PNOC_MAS_TSIF,
MSM8974_PNOC_MAS_BAM_DMA,
MSM8974_PNOC_MAS_BLSP_2,
MSM8974_PNOC_MAS_USB_HSIC,
MSM8974_PNOC_MAS_BLSP_1,
MSM8974_PNOC_MAS_USB_HS,
MSM8974_PNOC_TO_SNOC,
MSM8974_PNOC_SLV_SDCC_1,
MSM8974_PNOC_SLV_SDCC_3,
MSM8974_PNOC_SLV_SDCC_2,
MSM8974_PNOC_SLV_SDCC_4,
MSM8974_PNOC_SLV_TSIF,
MSM8974_PNOC_SLV_BAM_DMA,
MSM8974_PNOC_SLV_BLSP_2,
MSM8974_PNOC_SLV_USB_HSIC,
MSM8974_PNOC_SLV_BLSP_1,
MSM8974_PNOC_SLV_USB_HS,
MSM8974_PNOC_SLV_PDM,
MSM8974_PNOC_SLV_PERIPH_APU_CFG,
MSM8974_PNOC_SLV_PNOC_MPU_CFG,
MSM8974_PNOC_SLV_PRNG,
MSM8974_PNOC_SLV_SERVICE_PNOC,
MSM8974_SNOC_MAS_LPASS_AHB,
MSM8974_SNOC_MAS_QDSS_BAM,
MSM8974_SNOC_MAS_SNOC_CFG,
MSM8974_SNOC_TO_BIMC,
MSM8974_SNOC_TO_CNOC,
MSM8974_SNOC_TO_PNOC,
MSM8974_SNOC_TO_OCMEM_VNOC,
MSM8974_SNOC_MAS_CRYPTO_CORE0,
MSM8974_SNOC_MAS_CRYPTO_CORE1,
MSM8974_SNOC_MAS_LPASS_PROC,
MSM8974_SNOC_MAS_MSS,
MSM8974_SNOC_MAS_MSS_NAV,
MSM8974_SNOC_MAS_OCMEM_DMA,
MSM8974_SNOC_MAS_WCSS,
MSM8974_SNOC_MAS_QDSS_ETR,
MSM8974_SNOC_MAS_USB3,
MSM8974_SNOC_SLV_AMPSS,
MSM8974_SNOC_SLV_LPASS,
MSM8974_SNOC_SLV_USB3,
MSM8974_SNOC_SLV_WCSS,
MSM8974_SNOC_SLV_OCIMEM,
MSM8974_SNOC_SLV_SNOC_OCMEM,
MSM8974_SNOC_SLV_SERVICE_SNOC,
MSM8974_SNOC_SLV_QDSS_STM,
};
#define RPM_BUS_MASTER_REQ 0x73616d62
#define RPM_BUS_SLAVE_REQ 0x766c7362
#define to_msm8974_icc_provider(_provider) \
container_of(_provider, struct msm8974_icc_provider, provider)
static const struct clk_bulk_data msm8974_icc_bus_clocks[] = {
{ .id = "bus" },
{ .id = "bus_a" },
};
/**
* struct msm8974_icc_provider - Qualcomm specific interconnect provider
* @provider: generic interconnect provider
* @bus_clks: the clk_bulk_data table of bus clocks
* @num_clks: the total number of clk_bulk_data entries
*/
struct msm8974_icc_provider {
struct icc_provider provider;
struct clk_bulk_data *bus_clks;
int num_clks;
};
#define MSM8974_ICC_MAX_LINKS 3
/**
* struct msm8974_icc_node - Qualcomm specific interconnect nodes
* @name: the node name used in debugfs
* @id: a unique node identifier
* @links: an array of nodes where we can go next while traversing
* @num_links: the total number of @links
* @buswidth: width of the interconnect between a node and the bus (bytes)
* @mas_rpm_id: RPM ID for devices that are bus masters
* @slv_rpm_id: RPM ID for devices that are bus slaves
* @rate: current bus clock rate in Hz
*/
struct msm8974_icc_node {
unsigned char *name;
u16 id;
u16 links[MSM8974_ICC_MAX_LINKS];
u16 num_links;
u16 buswidth;
int mas_rpm_id;
int slv_rpm_id;
u64 rate;
};
struct msm8974_icc_desc {
struct msm8974_icc_node **nodes;
size_t num_nodes;
};
#define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \
...) \
static struct msm8974_icc_node _name = { \
.name = #_name, \
.id = _id, \
.buswidth = _buswidth, \
.mas_rpm_id = _mas_rpm_id, \
.slv_rpm_id = _slv_rpm_id, \
.num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })), \
.links = { __VA_ARGS__ }, \
}
DEFINE_QNODE(mas_ampss_m0, MSM8974_BIMC_MAS_AMPSS_M0, 8, 0, -1);
DEFINE_QNODE(mas_ampss_m1, MSM8974_BIMC_MAS_AMPSS_M1, 8, 0, -1);
DEFINE_QNODE(mas_mss_proc, MSM8974_BIMC_MAS_MSS_PROC, 8, 1, -1);
DEFINE_QNODE(bimc_to_mnoc, MSM8974_BIMC_TO_MNOC, 8, 2, -1, MSM8974_BIMC_SLV_EBI_CH0);
DEFINE_QNODE(bimc_to_snoc, MSM8974_BIMC_TO_SNOC, 8, 3, 2, MSM8974_SNOC_TO_BIMC, MSM8974_BIMC_SLV_EBI_CH0, MSM8974_BIMC_MAS_AMPSS_M0);
DEFINE_QNODE(slv_ebi_ch0, MSM8974_BIMC_SLV_EBI_CH0, 8, -1, 0);
DEFINE_QNODE(slv_ampss_l2, MSM8974_BIMC_SLV_AMPSS_L2, 8, -1, 1);
static struct msm8974_icc_node *msm8974_bimc_nodes[] = {
[BIMC_MAS_AMPSS_M0] = &mas_ampss_m0,
[BIMC_MAS_AMPSS_M1] = &mas_ampss_m1,
[BIMC_MAS_MSS_PROC] = &mas_mss_proc,
[BIMC_TO_MNOC] = &bimc_to_mnoc,
[BIMC_TO_SNOC] = &bimc_to_snoc,
[BIMC_SLV_EBI_CH0] = &slv_ebi_ch0,
[BIMC_SLV_AMPSS_L2] = &slv_ampss_l2,
};
static struct msm8974_icc_desc msm8974_bimc = {
.nodes = msm8974_bimc_nodes,
.num_nodes = ARRAY_SIZE(msm8974_bimc_nodes),
};
DEFINE_QNODE(mas_rpm_inst, MSM8974_CNOC_MAS_RPM_INST, 8, 45, -1);
DEFINE_QNODE(mas_rpm_data, MSM8974_CNOC_MAS_RPM_DATA, 8, 46, -1);
DEFINE_QNODE(mas_rpm_sys, MSM8974_CNOC_MAS_RPM_SYS, 8, 47, -1);
DEFINE_QNODE(mas_dehr, MSM8974_CNOC_MAS_DEHR, 8, 48, -1);
DEFINE_QNODE(mas_qdss_dap, MSM8974_CNOC_MAS_QDSS_DAP, 8, 49, -1);
DEFINE_QNODE(mas_spdm, MSM8974_CNOC_MAS_SPDM, 8, 50, -1);
DEFINE_QNODE(mas_tic, MSM8974_CNOC_MAS_TIC, 8, 51, -1);
DEFINE_QNODE(slv_clk_ctl, MSM8974_CNOC_SLV_CLK_CTL, 8, -1, 47);
DEFINE_QNODE(slv_cnoc_mss, MSM8974_CNOC_SLV_CNOC_MSS, 8, -1, 48);
DEFINE_QNODE(slv_security, MSM8974_CNOC_SLV_SECURITY, 8, -1, 49);
DEFINE_QNODE(slv_tcsr, MSM8974_CNOC_SLV_TCSR, 8, -1, 50);
DEFINE_QNODE(slv_tlmm, MSM8974_CNOC_SLV_TLMM, 8, -1, 51);
DEFINE_QNODE(slv_crypto_0_cfg, MSM8974_CNOC_SLV_CRYPTO_0_CFG, 8, -1, 52);
DEFINE_QNODE(slv_crypto_1_cfg, MSM8974_CNOC_SLV_CRYPTO_1_CFG, 8, -1, 53);
DEFINE_QNODE(slv_imem_cfg, MSM8974_CNOC_SLV_IMEM_CFG, 8, -1, 54);
DEFINE_QNODE(slv_message_ram, MSM8974_CNOC_SLV_MESSAGE_RAM, 8, -1, 55);
DEFINE_QNODE(slv_bimc_cfg, MSM8974_CNOC_SLV_BIMC_CFG, 8, -1, 56);
DEFINE_QNODE(slv_boot_rom, MSM8974_CNOC_SLV_BOOT_ROM, 8, -1, 57);
DEFINE_QNODE(slv_pmic_arb, MSM8974_CNOC_SLV_PMIC_ARB, 8, -1, 59);
DEFINE_QNODE(slv_spdm_wrapper, MSM8974_CNOC_SLV_SPDM_WRAPPER, 8, -1, 60);
DEFINE_QNODE(slv_dehr_cfg, MSM8974_CNOC_SLV_DEHR_CFG, 8, -1, 61);
DEFINE_QNODE(slv_mpm, MSM8974_CNOC_SLV_MPM, 8, -1, 62);
DEFINE_QNODE(slv_qdss_cfg, MSM8974_CNOC_SLV_QDSS_CFG, 8, -1, 63);
DEFINE_QNODE(slv_rbcpr_cfg, MSM8974_CNOC_SLV_RBCPR_CFG, 8, -1, 64);
DEFINE_QNODE(slv_rbcpr_qdss_apu_cfg, MSM8974_CNOC_SLV_RBCPR_QDSS_APU_CFG, 8, -1, 65);
DEFINE_QNODE(cnoc_to_snoc, MSM8974_CNOC_TO_SNOC, 8, 52, 75);
DEFINE_QNODE(slv_cnoc_onoc_cfg, MSM8974_CNOC_SLV_CNOC_ONOC_CFG, 8, -1, 68);
DEFINE_QNODE(slv_cnoc_mnoc_mmss_cfg, MSM8974_CNOC_SLV_CNOC_MNOC_MMSS_CFG, 8, -1, 58);
DEFINE_QNODE(slv_cnoc_mnoc_cfg, MSM8974_CNOC_SLV_CNOC_MNOC_CFG, 8, -1, 66);
DEFINE_QNODE(slv_pnoc_cfg, MSM8974_CNOC_SLV_PNOC_CFG, 8, -1, 69);
DEFINE_QNODE(slv_snoc_mpu_cfg, MSM8974_CNOC_SLV_SNOC_MPU_CFG, 8, -1, 67);
DEFINE_QNODE(slv_snoc_cfg, MSM8974_CNOC_SLV_SNOC_CFG, 8, -1, 70);
DEFINE_QNODE(slv_ebi1_dll_cfg, MSM8974_CNOC_SLV_EBI1_DLL_CFG, 8, -1, 71);
DEFINE_QNODE(slv_phy_apu_cfg, MSM8974_CNOC_SLV_PHY_APU_CFG, 8, -1, 72);
DEFINE_QNODE(slv_ebi1_phy_cfg, MSM8974_CNOC_SLV_EBI1_PHY_CFG, 8, -1, 73);
DEFINE_QNODE(slv_rpm, MSM8974_CNOC_SLV_RPM, 8, -1, 74);
DEFINE_QNODE(slv_service_cnoc, MSM8974_CNOC_SLV_SERVICE_CNOC, 8, -1, 76);
static struct msm8974_icc_node *msm8974_cnoc_nodes[] = {
[CNOC_MAS_RPM_INST] = &mas_rpm_inst,
[CNOC_MAS_RPM_DATA] = &mas_rpm_data,
[CNOC_MAS_RPM_SYS] = &mas_rpm_sys,
[CNOC_MAS_DEHR] = &mas_dehr,
[CNOC_MAS_QDSS_DAP] = &mas_qdss_dap,
[CNOC_MAS_SPDM] = &mas_spdm,
[CNOC_MAS_TIC] = &mas_tic,
[CNOC_SLV_CLK_CTL] = &slv_clk_ctl,
[CNOC_SLV_CNOC_MSS] = &slv_cnoc_mss,
[CNOC_SLV_SECURITY] = &slv_security,
[CNOC_SLV_TCSR] = &slv_tcsr,
[CNOC_SLV_TLMM] = &slv_tlmm,
[CNOC_SLV_CRYPTO_0_CFG] = &slv_crypto_0_cfg,
[CNOC_SLV_CRYPTO_1_CFG] = &slv_crypto_1_cfg,
[CNOC_SLV_IMEM_CFG] = &slv_imem_cfg,
[CNOC_SLV_MESSAGE_RAM] = &slv_message_ram,
[CNOC_SLV_BIMC_CFG] = &slv_bimc_cfg,
[CNOC_SLV_BOOT_ROM] = &slv_boot_rom,
[CNOC_SLV_PMIC_ARB] = &slv_pmic_arb,
[CNOC_SLV_SPDM_WRAPPER] = &slv_spdm_wrapper,
[CNOC_SLV_DEHR_CFG] = &slv_dehr_cfg,
[CNOC_SLV_MPM] = &slv_mpm,
[CNOC_SLV_QDSS_CFG] = &slv_qdss_cfg,
[CNOC_SLV_RBCPR_CFG] = &slv_rbcpr_cfg,
[CNOC_SLV_RBCPR_QDSS_APU_CFG] = &slv_rbcpr_qdss_apu_cfg,
[CNOC_TO_SNOC] = &cnoc_to_snoc,
[CNOC_SLV_CNOC_ONOC_CFG] = &slv_cnoc_onoc_cfg,
[CNOC_SLV_CNOC_MNOC_MMSS_CFG] = &slv_cnoc_mnoc_mmss_cfg,
[CNOC_SLV_CNOC_MNOC_CFG] = &slv_cnoc_mnoc_cfg,
[CNOC_SLV_PNOC_CFG] = &slv_pnoc_cfg,
[CNOC_SLV_SNOC_MPU_CFG] = &slv_snoc_mpu_cfg,
[CNOC_SLV_SNOC_CFG] = &slv_snoc_cfg,
[CNOC_SLV_EBI1_DLL_CFG] = &slv_ebi1_dll_cfg,
[CNOC_SLV_PHY_APU_CFG] = &slv_phy_apu_cfg,
[CNOC_SLV_EBI1_PHY_CFG] = &slv_ebi1_phy_cfg,
[CNOC_SLV_RPM] = &slv_rpm,
[CNOC_SLV_SERVICE_CNOC] = &slv_service_cnoc,
};
static struct msm8974_icc_desc msm8974_cnoc = {
.nodes = msm8974_cnoc_nodes,
.num_nodes = ARRAY_SIZE(msm8974_cnoc_nodes),
};
DEFINE_QNODE(mas_graphics_3d, MSM8974_MNOC_MAS_GRAPHICS_3D, 16, 6, -1, MSM8974_MNOC_TO_BIMC);
DEFINE_QNODE(mas_jpeg, MSM8974_MNOC_MAS_JPEG, 16, 7, -1, MSM8974_MNOC_TO_BIMC);
DEFINE_QNODE(mas_mdp_port0, MSM8974_MNOC_MAS_MDP_PORT0, 16, 8, -1, MSM8974_MNOC_TO_BIMC);
DEFINE_QNODE(mas_video_p0, MSM8974_MNOC_MAS_VIDEO_P0, 16, 9, -1);
DEFINE_QNODE(mas_video_p1, MSM8974_MNOC_MAS_VIDEO_P1, 16, 10, -1);
DEFINE_QNODE(mas_vfe, MSM8974_MNOC_MAS_VFE, 16, 11, -1, MSM8974_MNOC_TO_BIMC);
DEFINE_QNODE(mnoc_to_cnoc, MSM8974_MNOC_TO_CNOC, 16, 4, -1);
DEFINE_QNODE(mnoc_to_bimc, MSM8974_MNOC_TO_BIMC, 16, -1, 16, MSM8974_BIMC_TO_MNOC);
DEFINE_QNODE(slv_camera_cfg, MSM8974_MNOC_SLV_CAMERA_CFG, 16, -1, 3);
DEFINE_QNODE(slv_display_cfg, MSM8974_MNOC_SLV_DISPLAY_CFG, 16, -1, 4);
DEFINE_QNODE(slv_ocmem_cfg, MSM8974_MNOC_SLV_OCMEM_CFG, 16, -1, 5);
DEFINE_QNODE(slv_cpr_cfg, MSM8974_MNOC_SLV_CPR_CFG, 16, -1, 6);
DEFINE_QNODE(slv_cpr_xpu_cfg, MSM8974_MNOC_SLV_CPR_XPU_CFG, 16, -1, 7);
DEFINE_QNODE(slv_misc_cfg, MSM8974_MNOC_SLV_MISC_CFG, 16, -1, 8);
DEFINE_QNODE(slv_misc_xpu_cfg, MSM8974_MNOC_SLV_MISC_XPU_CFG, 16, -1, 9);
DEFINE_QNODE(slv_venus_cfg, MSM8974_MNOC_SLV_VENUS_CFG, 16, -1, 10);
DEFINE_QNODE(slv_graphics_3d_cfg, MSM8974_MNOC_SLV_GRAPHICS_3D_CFG, 16, -1, 11);
DEFINE_QNODE(slv_mmss_clk_cfg, MSM8974_MNOC_SLV_MMSS_CLK_CFG, 16, -1, 12);
DEFINE_QNODE(slv_mmss_clk_xpu_cfg, MSM8974_MNOC_SLV_MMSS_CLK_XPU_CFG, 16, -1, 13);
DEFINE_QNODE(slv_mnoc_mpu_cfg, MSM8974_MNOC_SLV_MNOC_MPU_CFG, 16, -1, 14);
DEFINE_QNODE(slv_onoc_mpu_cfg, MSM8974_MNOC_SLV_ONOC_MPU_CFG, 16, -1, 15);
DEFINE_QNODE(slv_service_mnoc, MSM8974_MNOC_SLV_SERVICE_MNOC, 16, -1, 17);
static struct msm8974_icc_node *msm8974_mnoc_nodes[] = {
[MNOC_MAS_GRAPHICS_3D] = &mas_graphics_3d,
[MNOC_MAS_JPEG] = &mas_jpeg,
[MNOC_MAS_MDP_PORT0] = &mas_mdp_port0,
[MNOC_MAS_VIDEO_P0] = &mas_video_p0,
[MNOC_MAS_VIDEO_P1] = &mas_video_p1,
[MNOC_MAS_VFE] = &mas_vfe,
[MNOC_TO_CNOC] = &mnoc_to_cnoc,
[MNOC_TO_BIMC] = &mnoc_to_bimc,
[MNOC_SLV_CAMERA_CFG] = &slv_camera_cfg,
[MNOC_SLV_DISPLAY_CFG] = &slv_display_cfg,
[MNOC_SLV_OCMEM_CFG] = &slv_ocmem_cfg,
[MNOC_SLV_CPR_CFG] = &slv_cpr_cfg,
[MNOC_SLV_CPR_XPU_CFG] = &slv_cpr_xpu_cfg,
[MNOC_SLV_MISC_CFG] = &slv_misc_cfg,
[MNOC_SLV_MISC_XPU_CFG] = &slv_misc_xpu_cfg,
[MNOC_SLV_VENUS_CFG] = &slv_venus_cfg,
[MNOC_SLV_GRAPHICS_3D_CFG] = &slv_graphics_3d_cfg,
[MNOC_SLV_MMSS_CLK_CFG] = &slv_mmss_clk_cfg,
[MNOC_SLV_MMSS_CLK_XPU_CFG] = &slv_mmss_clk_xpu_cfg,
[MNOC_SLV_MNOC_MPU_CFG] = &slv_mnoc_mpu_cfg,
[MNOC_SLV_ONOC_MPU_CFG] = &slv_onoc_mpu_cfg,
[MNOC_SLV_SERVICE_MNOC] = &slv_service_mnoc,
};
static struct msm8974_icc_desc msm8974_mnoc = {
.nodes = msm8974_mnoc_nodes,
.num_nodes = ARRAY_SIZE(msm8974_mnoc_nodes),
};
DEFINE_QNODE(ocmem_noc_to_ocmem_vnoc, MSM8974_OCMEM_NOC_TO_OCMEM_VNOC, 16, 54, 78, MSM8974_OCMEM_SLV_OCMEM);
DEFINE_QNODE(mas_jpeg_ocmem, MSM8974_OCMEM_MAS_JPEG_OCMEM, 16, 13, -1);
DEFINE_QNODE(mas_mdp_ocmem, MSM8974_OCMEM_MAS_MDP_OCMEM, 16, 14, -1);
DEFINE_QNODE(mas_video_p0_ocmem, MSM8974_OCMEM_MAS_VIDEO_P0_OCMEM, 16, 15, -1);
DEFINE_QNODE(mas_video_p1_ocmem, MSM8974_OCMEM_MAS_VIDEO_P1_OCMEM, 16, 16, -1);
DEFINE_QNODE(mas_vfe_ocmem, MSM8974_OCMEM_MAS_VFE_OCMEM, 16, 17, -1);
DEFINE_QNODE(mas_cnoc_onoc_cfg, MSM8974_OCMEM_MAS_CNOC_ONOC_CFG, 16, 12, -1);
DEFINE_QNODE(slv_service_onoc, MSM8974_OCMEM_SLV_SERVICE_ONOC, 16, -1, 19);
DEFINE_QNODE(slv_ocmem, MSM8974_OCMEM_SLV_OCMEM, 16, -1, 18);
/* Virtual NoC is needed for connection to OCMEM */
DEFINE_QNODE(ocmem_vnoc_to_onoc, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC, 16, 56, 79, MSM8974_OCMEM_NOC_TO_OCMEM_VNOC);
DEFINE_QNODE(ocmem_vnoc_to_snoc, MSM8974_OCMEM_VNOC_TO_SNOC, 8, 57, 80);
DEFINE_QNODE(mas_v_ocmem_gfx3d, MSM8974_OCMEM_VNOC_MAS_GFX3D, 8, 55, -1, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC);
static struct msm8974_icc_node *msm8974_onoc_nodes[] = {
[OCMEM_NOC_TO_OCMEM_VNOC] = &ocmem_noc_to_ocmem_vnoc,
[OCMEM_MAS_JPEG_OCMEM] = &mas_jpeg_ocmem,
[OCMEM_MAS_MDP_OCMEM] = &mas_mdp_ocmem,
[OCMEM_MAS_VIDEO_P0_OCMEM] = &mas_video_p0_ocmem,
[OCMEM_MAS_VIDEO_P1_OCMEM] = &mas_video_p1_ocmem,
[OCMEM_MAS_VFE_OCMEM] = &mas_vfe_ocmem,
[OCMEM_MAS_CNOC_ONOC_CFG] = &mas_cnoc_onoc_cfg,
[OCMEM_SLV_SERVICE_ONOC] = &slv_service_onoc,
[OCMEM_VNOC_TO_SNOC] = &ocmem_vnoc_to_snoc,
[OCMEM_VNOC_TO_OCMEM_NOC] = &ocmem_vnoc_to_onoc,
[OCMEM_VNOC_MAS_GFX3D] = &mas_v_ocmem_gfx3d,
[OCMEM_SLV_OCMEM] = &slv_ocmem,
};
static struct msm8974_icc_desc msm8974_onoc = {
.nodes = msm8974_onoc_nodes,
.num_nodes = ARRAY_SIZE(msm8974_onoc_nodes),
};
DEFINE_QNODE(mas_pnoc_cfg, MSM8974_PNOC_MAS_PNOC_CFG, 8, 43, -1);
DEFINE_QNODE(mas_sdcc_1, MSM8974_PNOC_MAS_SDCC_1, 8, 33, -1, MSM8974_PNOC_TO_SNOC);
DEFINE_QNODE(mas_sdcc_3, MSM8974_PNOC_MAS_SDCC_3, 8, 34, -1, MSM8974_PNOC_TO_SNOC);
DEFINE_QNODE(mas_sdcc_4, MSM8974_PNOC_MAS_SDCC_4, 8, 36, -1, MSM8974_PNOC_TO_SNOC);
DEFINE_QNODE(mas_sdcc_2, MSM8974_PNOC_MAS_SDCC_2, 8, 35, -1, MSM8974_PNOC_TO_SNOC);
DEFINE_QNODE(mas_tsif, MSM8974_PNOC_MAS_TSIF, 8, 37, -1, MSM8974_PNOC_TO_SNOC);
DEFINE_QNODE(mas_bam_dma, MSM8974_PNOC_MAS_BAM_DMA, 8, 38, -1);
DEFINE_QNODE(mas_blsp_2, MSM8974_PNOC_MAS_BLSP_2, 8, 39, -1, MSM8974_PNOC_TO_SNOC);
DEFINE_QNODE(mas_usb_hsic, MSM8974_PNOC_MAS_USB_HSIC, 8, 40, -1, MSM8974_PNOC_TO_SNOC);
DEFINE_QNODE(mas_blsp_1, MSM8974_PNOC_MAS_BLSP_1, 8, 41, -1, MSM8974_PNOC_TO_SNOC);
DEFINE_QNODE(mas_usb_hs, MSM8974_PNOC_MAS_USB_HS, 8, 42, -1, MSM8974_PNOC_TO_SNOC);
DEFINE_QNODE(pnoc_to_snoc, MSM8974_PNOC_TO_SNOC, 8, 44, 45, MSM8974_SNOC_TO_PNOC, MSM8974_PNOC_SLV_PRNG);
DEFINE_QNODE(slv_sdcc_1, MSM8974_PNOC_SLV_SDCC_1, 8, -1, 31);
DEFINE_QNODE(slv_sdcc_3, MSM8974_PNOC_SLV_SDCC_3, 8, -1, 32);
DEFINE_QNODE(slv_sdcc_2, MSM8974_PNOC_SLV_SDCC_2, 8, -1, 33);
DEFINE_QNODE(slv_sdcc_4, MSM8974_PNOC_SLV_SDCC_4, 8, -1, 34);
DEFINE_QNODE(slv_tsif, MSM8974_PNOC_SLV_TSIF, 8, -1, 35);
DEFINE_QNODE(slv_bam_dma, MSM8974_PNOC_SLV_BAM_DMA, 8, -1, 36);
DEFINE_QNODE(slv_blsp_2, MSM8974_PNOC_SLV_BLSP_2, 8, -1, 37);
DEFINE_QNODE(slv_usb_hsic, MSM8974_PNOC_SLV_USB_HSIC, 8, -1, 38);
DEFINE_QNODE(slv_blsp_1, MSM8974_PNOC_SLV_BLSP_1, 8, -1, 39);
DEFINE_QNODE(slv_usb_hs, MSM8974_PNOC_SLV_USB_HS, 8, -1, 40);
DEFINE_QNODE(slv_pdm, MSM8974_PNOC_SLV_PDM, 8, -1, 41);
DEFINE_QNODE(slv_periph_apu_cfg, MSM8974_PNOC_SLV_PERIPH_APU_CFG, 8, -1, 42);
DEFINE_QNODE(slv_pnoc_mpu_cfg, MSM8974_PNOC_SLV_PNOC_MPU_CFG, 8, -1, 43);
DEFINE_QNODE(slv_prng, MSM8974_PNOC_SLV_PRNG, 8, -1, 44, MSM8974_PNOC_TO_SNOC);
DEFINE_QNODE(slv_service_pnoc, MSM8974_PNOC_SLV_SERVICE_PNOC, 8, -1, 46);
static struct msm8974_icc_node *msm8974_pnoc_nodes[] = {
[PNOC_MAS_PNOC_CFG] = &mas_pnoc_cfg,
[PNOC_MAS_SDCC_1] = &mas_sdcc_1,
[PNOC_MAS_SDCC_3] = &mas_sdcc_3,
[PNOC_MAS_SDCC_4] = &mas_sdcc_4,
[PNOC_MAS_SDCC_2] = &mas_sdcc_2,
[PNOC_MAS_TSIF] = &mas_tsif,
[PNOC_MAS_BAM_DMA] = &mas_bam_dma,
[PNOC_MAS_BLSP_2] = &mas_blsp_2,
[PNOC_MAS_USB_HSIC] = &mas_usb_hsic,
[PNOC_MAS_BLSP_1] = &mas_blsp_1,
[PNOC_MAS_USB_HS] = &mas_usb_hs,
[PNOC_TO_SNOC] = &pnoc_to_snoc,
[PNOC_SLV_SDCC_1] = &slv_sdcc_1,
[PNOC_SLV_SDCC_3] = &slv_sdcc_3,
[PNOC_SLV_SDCC_2] = &slv_sdcc_2,
[PNOC_SLV_SDCC_4] = &slv_sdcc_4,
[PNOC_SLV_TSIF] = &slv_tsif,
[PNOC_SLV_BAM_DMA] = &slv_bam_dma,
[PNOC_SLV_BLSP_2] = &slv_blsp_2,
[PNOC_SLV_USB_HSIC] = &slv_usb_hsic,
[PNOC_SLV_BLSP_1] = &slv_blsp_1,
[PNOC_SLV_USB_HS] = &slv_usb_hs,
[PNOC_SLV_PDM] = &slv_pdm,
[PNOC_SLV_PERIPH_APU_CFG] = &slv_periph_apu_cfg,
[PNOC_SLV_PNOC_MPU_CFG] = &slv_pnoc_mpu_cfg,
[PNOC_SLV_PRNG] = &slv_prng,
[PNOC_SLV_SERVICE_PNOC] = &slv_service_pnoc,
};
static struct msm8974_icc_desc msm8974_pnoc = {
.nodes = msm8974_pnoc_nodes,
.num_nodes = ARRAY_SIZE(msm8974_pnoc_nodes),
};
DEFINE_QNODE(mas_lpass_ahb, MSM8974_SNOC_MAS_LPASS_AHB, 8, 18, -1);
DEFINE_QNODE(mas_qdss_bam, MSM8974_SNOC_MAS_QDSS_BAM, 8, 19, -1);
DEFINE_QNODE(mas_snoc_cfg, MSM8974_SNOC_MAS_SNOC_CFG, 8, 20, -1);
DEFINE_QNODE(snoc_to_bimc, MSM8974_SNOC_TO_BIMC, 8, 21, 24, MSM8974_BIMC_TO_SNOC);
DEFINE_QNODE(snoc_to_cnoc, MSM8974_SNOC_TO_CNOC, 8, 22, 25);
DEFINE_QNODE(snoc_to_pnoc, MSM8974_SNOC_TO_PNOC, 8, 29, 28, MSM8974_PNOC_TO_SNOC);
DEFINE_QNODE(snoc_to_ocmem_vnoc, MSM8974_SNOC_TO_OCMEM_VNOC, 8, 53, 77, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC);
DEFINE_QNODE(mas_crypto_core0, MSM8974_SNOC_MAS_CRYPTO_CORE0, 8, 23, -1, MSM8974_SNOC_TO_BIMC);
DEFINE_QNODE(mas_crypto_core1, MSM8974_SNOC_MAS_CRYPTO_CORE1, 8, 24, -1);
DEFINE_QNODE(mas_lpass_proc, MSM8974_SNOC_MAS_LPASS_PROC, 8, 25, -1, MSM8974_SNOC_TO_OCMEM_VNOC);
DEFINE_QNODE(mas_mss, MSM8974_SNOC_MAS_MSS, 8, 26, -1);
DEFINE_QNODE(mas_mss_nav, MSM8974_SNOC_MAS_MSS_NAV, 8, 27, -1);
DEFINE_QNODE(mas_ocmem_dma, MSM8974_SNOC_MAS_OCMEM_DMA, 8, 28, -1);
DEFINE_QNODE(mas_wcss, MSM8974_SNOC_MAS_WCSS, 8, 30, -1);
DEFINE_QNODE(mas_qdss_etr, MSM8974_SNOC_MAS_QDSS_ETR, 8, 31, -1);
DEFINE_QNODE(mas_usb3, MSM8974_SNOC_MAS_USB3, 8, 32, -1, MSM8974_SNOC_TO_BIMC);
DEFINE_QNODE(slv_ampss, MSM8974_SNOC_SLV_AMPSS, 8, -1, 20);
DEFINE_QNODE(slv_lpass, MSM8974_SNOC_SLV_LPASS, 8, -1, 21);
DEFINE_QNODE(slv_usb3, MSM8974_SNOC_SLV_USB3, 8, -1, 22);
DEFINE_QNODE(slv_wcss, MSM8974_SNOC_SLV_WCSS, 8, -1, 23);
DEFINE_QNODE(slv_ocimem, MSM8974_SNOC_SLV_OCIMEM, 8, -1, 26);
DEFINE_QNODE(slv_snoc_ocmem, MSM8974_SNOC_SLV_SNOC_OCMEM, 8, -1, 27);
DEFINE_QNODE(slv_service_snoc, MSM8974_SNOC_SLV_SERVICE_SNOC, 8, -1, 29);
DEFINE_QNODE(slv_qdss_stm, MSM8974_SNOC_SLV_QDSS_STM, 8, -1, 30);
static struct msm8974_icc_node *msm8974_snoc_nodes[] = {
[SNOC_MAS_LPASS_AHB] = &mas_lpass_ahb,
[SNOC_MAS_QDSS_BAM] = &mas_qdss_bam,
[SNOC_MAS_SNOC_CFG] = &mas_snoc_cfg,
[SNOC_TO_BIMC] = &snoc_to_bimc,
[SNOC_TO_CNOC] = &snoc_to_cnoc,
[SNOC_TO_PNOC] = &snoc_to_pnoc,
[SNOC_TO_OCMEM_VNOC] = &snoc_to_ocmem_vnoc,
[SNOC_MAS_CRYPTO_CORE0] = &mas_crypto_core0,
[SNOC_MAS_CRYPTO_CORE1] = &mas_crypto_core1,
[SNOC_MAS_LPASS_PROC] = &mas_lpass_proc,
[SNOC_MAS_MSS] = &mas_mss,
[SNOC_MAS_MSS_NAV] = &mas_mss_nav,
[SNOC_MAS_OCMEM_DMA] = &mas_ocmem_dma,
[SNOC_MAS_WCSS] = &mas_wcss,
[SNOC_MAS_QDSS_ETR] = &mas_qdss_etr,
[SNOC_MAS_USB3] = &mas_usb3,
[SNOC_SLV_AMPSS] = &slv_ampss,
[SNOC_SLV_LPASS] = &slv_lpass,
[SNOC_SLV_USB3] = &slv_usb3,
[SNOC_SLV_WCSS] = &slv_wcss,
[SNOC_SLV_OCIMEM] = &slv_ocimem,
[SNOC_SLV_SNOC_OCMEM] = &slv_snoc_ocmem,
[SNOC_SLV_SERVICE_SNOC] = &slv_service_snoc,
[SNOC_SLV_QDSS_STM] = &slv_qdss_stm,
};
static struct msm8974_icc_desc msm8974_snoc = {
.nodes = msm8974_snoc_nodes,
.num_nodes = ARRAY_SIZE(msm8974_snoc_nodes),
};
static int msm8974_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
{
*agg_avg += avg_bw;
*agg_peak = max(*agg_peak, peak_bw);
return 0;
}
static void msm8974_icc_rpm_smd_send(struct device *dev, int rsc_type,
char *name, int id, u64 val)
{
int ret;
if (id == -1)
return;
/*
* Setting the bandwidth requests for some nodes fails and this same
* behavior occurs on the downstream MSM 3.4 kernel sources based on
* errors like this in that kernel:
*
* msm_rpm_get_error_from_ack(): RPM NACK Unsupported resource
* AXI: msm_bus_rpm_req(): RPM: Ack failed
* AXI: msm_bus_rpm_commit_arb(): RPM: Req fail: mas:32, bw:240000000
*
* Since there's no publicly available documentation for this hardware,
* and the bandwidth for some nodes in the path can be set properly,
* let's not return an error.
*/
ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, rsc_type, id,
val);
if (ret)
dev_dbg(dev, "Cannot set bandwidth for node %s (%d): %d\n",
name, id, ret);
}
static int msm8974_icc_set(struct icc_node *src, struct icc_node *dst)
{
struct msm8974_icc_node *src_qn, *dst_qn;
struct msm8974_icc_provider *qp;
u64 sum_bw, max_peak_bw, rate;
u32 agg_avg = 0, agg_peak = 0;
struct icc_provider *provider;
struct icc_node *n;
int ret, i;
src_qn = src->data;
dst_qn = dst->data;
provider = src->provider;
qp = to_msm8974_icc_provider(provider);
list_for_each_entry(n, &provider->nodes, node_list)
msm8974_icc_aggregate(n, 0, n->avg_bw, n->peak_bw,
&agg_avg, &agg_peak);
sum_bw = icc_units_to_bps(agg_avg);
max_peak_bw = icc_units_to_bps(agg_peak);
/* Set bandwidth on source node */
msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_MASTER_REQ,
src_qn->name, src_qn->mas_rpm_id, sum_bw);
msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_SLAVE_REQ,
src_qn->name, src_qn->slv_rpm_id, sum_bw);
/* Set bandwidth on destination node */
msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_MASTER_REQ,
dst_qn->name, dst_qn->mas_rpm_id, sum_bw);
msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_SLAVE_REQ,
dst_qn->name, dst_qn->slv_rpm_id, sum_bw);
rate = max(sum_bw, max_peak_bw);
do_div(rate, src_qn->buswidth);
if (src_qn->rate == rate)
return 0;
for (i = 0; i < qp->num_clks; i++) {
ret = clk_set_rate(qp->bus_clks[i].clk, rate);
if (ret) {
dev_err(provider->dev, "%s clk_set_rate error: %d\n",
qp->bus_clks[i].id, ret);
ret = 0;
}
}
src_qn->rate = rate;
return 0;
}
static int msm8974_icc_probe(struct platform_device *pdev)
{
const struct msm8974_icc_desc *desc;
struct msm8974_icc_node **qnodes;
struct msm8974_icc_provider *qp;
struct device *dev = &pdev->dev;
struct icc_onecell_data *data;
struct icc_provider *provider;
struct icc_node *node;
size_t num_nodes, i;
int ret;
/* wait for the RPM proxy */
if (!qcom_icc_rpm_smd_available())
return -EPROBE_DEFER;
desc = of_device_get_match_data(dev);
if (!desc)
return -EINVAL;
qnodes = desc->nodes;
num_nodes = desc->num_nodes;
qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL);
if (!qp)
return -ENOMEM;
data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes),
GFP_KERNEL);
if (!data)
return -ENOMEM;
qp->bus_clks = devm_kmemdup(dev, msm8974_icc_bus_clocks,
sizeof(msm8974_icc_bus_clocks), GFP_KERNEL);
if (!qp->bus_clks)
return -ENOMEM;
qp->num_clks = ARRAY_SIZE(msm8974_icc_bus_clocks);
ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks);
if (ret)
return ret;
ret = clk_bulk_prepare_enable(qp->num_clks, qp->bus_clks);
if (ret)
return ret;
provider = &qp->provider;
INIT_LIST_HEAD(&provider->nodes);
provider->dev = dev;
provider->set = msm8974_icc_set;
provider->aggregate = msm8974_icc_aggregate;
provider->xlate = of_icc_xlate_onecell;
provider->data = data;
ret = icc_provider_add(provider);
if (ret) {
dev_err(dev, "error adding interconnect provider: %d\n", ret);
goto err_disable_clks;
}
for (i = 0; i < num_nodes; i++) {
size_t j;
node = icc_node_create(qnodes[i]->id);
if (IS_ERR(node)) {
ret = PTR_ERR(node);
goto err_del_icc;
}
node->name = qnodes[i]->name;
node->data = qnodes[i];
icc_node_add(node, provider);
dev_dbg(dev, "registered node %s\n", node->name);
/* populate links */
for (j = 0; j < qnodes[i]->num_links; j++)
icc_link_create(node, qnodes[i]->links[j]);
data->nodes[i] = node;
}
data->num_nodes = num_nodes;
platform_set_drvdata(pdev, qp);
return 0;
err_del_icc:
list_for_each_entry(node, &provider->nodes, node_list) {
icc_node_del(node);
icc_node_destroy(node->id);
}
icc_provider_del(provider);
err_disable_clks:
clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
return ret;
}
static int msm8974_icc_remove(struct platform_device *pdev)
{
struct msm8974_icc_provider *qp = platform_get_drvdata(pdev);
struct icc_provider *provider = &qp->provider;
struct icc_node *n;
list_for_each_entry(n, &provider->nodes, node_list) {
icc_node_del(n);
icc_node_destroy(n->id);
}
clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
return icc_provider_del(provider);
}
static const struct of_device_id msm8974_noc_of_match[] = {
{ .compatible = "qcom,msm8974-bimc", .data = &msm8974_bimc},
{ .compatible = "qcom,msm8974-cnoc", .data = &msm8974_cnoc},
{ .compatible = "qcom,msm8974-mmssnoc", .data = &msm8974_mnoc},
{ .compatible = "qcom,msm8974-ocmemnoc", .data = &msm8974_onoc},
{ .compatible = "qcom,msm8974-pnoc", .data = &msm8974_pnoc},
{ .compatible = "qcom,msm8974-snoc", .data = &msm8974_snoc},
{ },
};
MODULE_DEVICE_TABLE(of, msm8974_noc_of_match);
static struct platform_driver msm8974_noc_driver = {
.probe = msm8974_icc_probe,
.remove = msm8974_icc_remove,
.driver = {
.name = "qnoc-msm8974",
.of_match_table = msm8974_noc_of_match,
},
};
module_platform_driver(msm8974_noc_driver);
MODULE_DESCRIPTION("Qualcomm MSM8974 NoC driver");
MODULE_AUTHOR("Brian Masney <masneyb@onstation.org>");
MODULE_LICENSE("GPL v2");

View File

@ -191,7 +191,7 @@ int __mcb_register_driver(struct mcb_driver *drv, struct module *owner,
return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(__mcb_register_driver);
EXPORT_SYMBOL_NS_GPL(__mcb_register_driver, MCB);
/**
* mcb_unregister_driver() - Unregister a @mcb_driver from the system
@ -203,7 +203,7 @@ void mcb_unregister_driver(struct mcb_driver *drv)
{
driver_unregister(&drv->driver);
}
EXPORT_SYMBOL_GPL(mcb_unregister_driver);
EXPORT_SYMBOL_NS_GPL(mcb_unregister_driver, MCB);
static void mcb_release_dev(struct device *dev)
{
@ -249,7 +249,7 @@ out:
return ret;
}
EXPORT_SYMBOL_GPL(mcb_device_register);
EXPORT_SYMBOL_NS_GPL(mcb_device_register, MCB);
static void mcb_free_bus(struct device *dev)
{
@ -301,7 +301,7 @@ err_free:
kfree(bus);
return ERR_PTR(rc);
}
EXPORT_SYMBOL_GPL(mcb_alloc_bus);
EXPORT_SYMBOL_NS_GPL(mcb_alloc_bus, MCB);
static int __mcb_devices_unregister(struct device *dev, void *data)
{
@ -323,7 +323,7 @@ void mcb_release_bus(struct mcb_bus *bus)
{
mcb_devices_unregister(bus);
}
EXPORT_SYMBOL_GPL(mcb_release_bus);
EXPORT_SYMBOL_NS_GPL(mcb_release_bus, MCB);
/**
* mcb_bus_put() - Increment refcnt
@ -338,7 +338,7 @@ struct mcb_bus *mcb_bus_get(struct mcb_bus *bus)
return bus;
}
EXPORT_SYMBOL_GPL(mcb_bus_get);
EXPORT_SYMBOL_NS_GPL(mcb_bus_get, MCB);
/**
* mcb_bus_put() - Decrement refcnt
@ -351,7 +351,7 @@ void mcb_bus_put(struct mcb_bus *bus)
if (bus)
put_device(&bus->dev);
}
EXPORT_SYMBOL_GPL(mcb_bus_put);
EXPORT_SYMBOL_NS_GPL(mcb_bus_put, MCB);
/**
* mcb_alloc_dev() - Allocate a device
@ -371,7 +371,7 @@ struct mcb_device *mcb_alloc_dev(struct mcb_bus *bus)
return dev;
}
EXPORT_SYMBOL_GPL(mcb_alloc_dev);
EXPORT_SYMBOL_NS_GPL(mcb_alloc_dev, MCB);
/**
* mcb_free_dev() - Free @mcb_device
@ -383,7 +383,7 @@ void mcb_free_dev(struct mcb_device *dev)
{
kfree(dev);
}
EXPORT_SYMBOL_GPL(mcb_free_dev);
EXPORT_SYMBOL_NS_GPL(mcb_free_dev, MCB);
static int __mcb_bus_add_devices(struct device *dev, void *data)
{
@ -412,7 +412,7 @@ void mcb_bus_add_devices(const struct mcb_bus *bus)
{
bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_bus_add_devices);
}
EXPORT_SYMBOL_GPL(mcb_bus_add_devices);
EXPORT_SYMBOL_NS_GPL(mcb_bus_add_devices, MCB);
/**
* mcb_get_resource() - get a resource for a mcb device
@ -428,7 +428,7 @@ struct resource *mcb_get_resource(struct mcb_device *dev, unsigned int type)
else
return NULL;
}
EXPORT_SYMBOL_GPL(mcb_get_resource);
EXPORT_SYMBOL_NS_GPL(mcb_get_resource, MCB);
/**
* mcb_request_mem() - Request memory
@ -454,7 +454,7 @@ struct resource *mcb_request_mem(struct mcb_device *dev, const char *name)
return mem;
}
EXPORT_SYMBOL_GPL(mcb_request_mem);
EXPORT_SYMBOL_NS_GPL(mcb_request_mem, MCB);
/**
* mcb_release_mem() - Release memory requested by device
@ -469,7 +469,7 @@ void mcb_release_mem(struct resource *mem)
size = resource_size(mem);
release_mem_region(mem->start, size);
}
EXPORT_SYMBOL_GPL(mcb_release_mem);
EXPORT_SYMBOL_NS_GPL(mcb_release_mem, MCB);
static int __mcb_get_irq(struct mcb_device *dev)
{
@ -495,7 +495,7 @@ int mcb_get_irq(struct mcb_device *dev)
return __mcb_get_irq(dev);
}
EXPORT_SYMBOL_GPL(mcb_get_irq);
EXPORT_SYMBOL_NS_GPL(mcb_get_irq, MCB);
static int mcb_init(void)
{

View File

@ -168,3 +168,4 @@ module_exit(mcb_lpc_exit);
MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MCB over LPC support");
MODULE_IMPORT_NS(MCB);

View File

@ -253,4 +253,4 @@ free_header:
return ret;
}
EXPORT_SYMBOL_GPL(chameleon_parse_cells);
EXPORT_SYMBOL_NS_GPL(chameleon_parse_cells, MCB);

View File

@ -131,3 +131,4 @@ module_pci_driver(mcb_pci_driver);
MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MCB over PCI support");
MODULE_IMPORT_NS(MCB);

View File

@ -8,7 +8,6 @@ menu "Misc devices"
config SENSORS_LIS3LV02D
tristate
depends on INPUT
select INPUT_POLLDEV
config AD525X_DPOT
tristate "Analog Devices Digital Potentiometers"
@ -326,14 +325,14 @@ config SENSORS_TSL2550
will be called tsl2550.
config SENSORS_BH1770
tristate "BH1770GLC / SFH7770 combined ALS - Proximity sensor"
depends on I2C
---help---
Say Y here if you want to build a driver for BH1770GLC (ROHM) or
tristate "BH1770GLC / SFH7770 combined ALS - Proximity sensor"
depends on I2C
---help---
Say Y here if you want to build a driver for BH1770GLC (ROHM) or
SFH7770 (Osram) combined ambient light and proximity sensor chip.
To compile this driver as a module, choose M here: the
module will be called bh1770glc. If unsure, say N here.
To compile this driver as a module, choose M here: the
module will be called bh1770glc. If unsure, say N here.
config SENSORS_APDS990X
tristate "APDS990X combined als and proximity sensors"
@ -438,8 +437,8 @@ config PCI_ENDPOINT_TEST
select CRC32
tristate "PCI Endpoint Test driver"
---help---
Enable this configuration option to enable the host side test driver
for PCI Endpoint.
Enable this configuration option to enable the host side test driver
for PCI Endpoint.
config XILINX_SDFEC
tristate "Xilinx SDFEC 16"

View File

@ -109,7 +109,6 @@ static int __init tc_probe(struct platform_device *pdev)
struct atmel_tc *tc;
struct clk *clk;
int irq;
struct resource *r;
unsigned int i;
if (of_get_child_count(pdev->dev.of_node))
@ -133,8 +132,7 @@ static int __init tc_probe(struct platform_device *pdev)
if (IS_ERR(tc->slow_clk))
return PTR_ERR(tc->slow_clk);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
tc->regs = devm_ioremap_resource(&pdev->dev, r);
tc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(tc->regs))
return PTR_ERR(tc->regs);

View File

@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_MISC_ALCOR_PCI) += alcor_pci.o
obj-$(CONFIG_MISC_RTSX_PCI) += rtsx_pci.o
rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o rts5260.o
rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o rts5260.o rts5261.o
obj-$(CONFIG_MISC_RTSX_USB) += rtsx_usb.o

View File

@ -191,7 +191,6 @@ static int sd_set_sample_push_timing_sd30(struct rtsx_pcr *pcr)
static int rts5260_card_power_on(struct rtsx_pcr *pcr, int card)
{
int err = 0;
struct rtsx_cr_option *option = &pcr->option;
if (option->ocp_en)
@ -231,7 +230,7 @@ static int rts5260_card_power_on(struct rtsx_pcr *pcr, int card)
rtsx_pci_write_register(pcr, REG_PRE_RW_MODE, EN_INFINITE_MODE, 0);
return err;
return 0;
}
static int rts5260_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)

View File

@ -0,0 +1,792 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* Driver for Realtek PCI-Express card reader
*
* Copyright(c) 2018-2019 Realtek Semiconductor Corp. All rights reserved.
*
* Author:
* Rui FENG <rui_feng@realsil.com.cn>
* Wei WANG <wei_wang@realsil.com.cn>
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/rtsx_pci.h>
#include "rts5261.h"
#include "rtsx_pcr.h"
static u8 rts5261_get_ic_version(struct rtsx_pcr *pcr)
{
u8 val;
rtsx_pci_read_register(pcr, DUMMY_REG_RESET_0, &val);
return val & IC_VERSION_MASK;
}
static void rts5261_fill_driving(struct rtsx_pcr *pcr, u8 voltage)
{
u8 driving_3v3[4][3] = {
{0x13, 0x13, 0x13},
{0x96, 0x96, 0x96},
{0x7F, 0x7F, 0x7F},
{0x96, 0x96, 0x96},
};
u8 driving_1v8[4][3] = {
{0x99, 0x99, 0x99},
{0x3A, 0x3A, 0x3A},
{0xE6, 0xE6, 0xE6},
{0xB3, 0xB3, 0xB3},
};
u8 (*driving)[3], drive_sel;
if (voltage == OUTPUT_3V3) {
driving = driving_3v3;
drive_sel = pcr->sd30_drive_sel_3v3;
} else {
driving = driving_1v8;
drive_sel = pcr->sd30_drive_sel_1v8;
}
rtsx_pci_write_register(pcr, SD30_CLK_DRIVE_SEL,
0xFF, driving[drive_sel][0]);
rtsx_pci_write_register(pcr, SD30_CMD_DRIVE_SEL,
0xFF, driving[drive_sel][1]);
rtsx_pci_write_register(pcr, SD30_DAT_DRIVE_SEL,
0xFF, driving[drive_sel][2]);
}
static void rtsx5261_fetch_vendor_settings(struct rtsx_pcr *pcr)
{
u32 reg;
/* 0x814~0x817 */
rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, &reg);
pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg);
if (!rts5261_vendor_setting_valid(reg)) {
pcr_dbg(pcr, "skip fetch vendor setting\n");
return;
}
pcr->card_drive_sel &= 0x3F;
pcr->card_drive_sel |= rts5261_reg_to_card_drive_sel(reg);
if (rts5261_reg_check_reverse_socket(reg))
pcr->flags |= PCR_REVERSE_SOCKET;
/* 0x724~0x727 */
rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, &reg);
pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg);
pcr->aspm_en = rts5261_reg_to_aspm(reg);
pcr->sd30_drive_sel_1v8 = rts5261_reg_to_sd30_drive_sel_1v8(reg);
pcr->sd30_drive_sel_3v3 = rts5261_reg_to_sd30_drive_sel_3v3(reg);
}
static void rts5261_force_power_down(struct rtsx_pcr *pcr, u8 pm_state)
{
/* Set relink_time to 0 */
rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, MASK_8_BIT_DEF, 0);
rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, MASK_8_BIT_DEF, 0);
rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3,
RELINK_TIME_MASK, 0);
if (pm_state == HOST_ENTER_S3)
rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3,
D3_DELINK_MODE_EN, D3_DELINK_MODE_EN);
rtsx_pci_write_register(pcr, RTS5261_REG_FPDCTL,
SSC_POWER_DOWN, SSC_POWER_DOWN);
}
static int rts5261_enable_auto_blink(struct rtsx_pcr *pcr)
{
return rtsx_pci_write_register(pcr, OLT_LED_CTL,
LED_SHINE_MASK, LED_SHINE_EN);
}
static int rts5261_disable_auto_blink(struct rtsx_pcr *pcr)
{
return rtsx_pci_write_register(pcr, OLT_LED_CTL,
LED_SHINE_MASK, LED_SHINE_DISABLE);
}
static int rts5261_turn_on_led(struct rtsx_pcr *pcr)
{
return rtsx_pci_write_register(pcr, GPIO_CTL,
0x02, 0x02);
}
static int rts5261_turn_off_led(struct rtsx_pcr *pcr)
{
return rtsx_pci_write_register(pcr, GPIO_CTL,
0x02, 0x00);
}
/* SD Pull Control Enable:
* SD_DAT[3:0] ==> pull up
* SD_CD ==> pull up
* SD_WP ==> pull up
* SD_CMD ==> pull up
* SD_CLK ==> pull down
*/
static const u32 rts5261_sd_pull_ctl_enable_tbl[] = {
RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9),
0,
};
/* SD Pull Control Disable:
* SD_DAT[3:0] ==> pull down
* SD_CD ==> pull up
* SD_WP ==> pull down
* SD_CMD ==> pull down
* SD_CLK ==> pull down
*/
static const u32 rts5261_sd_pull_ctl_disable_tbl[] = {
RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5),
0,
};
static int rts5261_sd_set_sample_push_timing_sd30(struct rtsx_pcr *pcr)
{
rtsx_pci_write_register(pcr, SD_CFG1, SD_MODE_SELECT_MASK
| SD_ASYNC_FIFO_NOT_RST, SD_30_MODE | SD_ASYNC_FIFO_NOT_RST);
rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, CLK_LOW_FREQ);
rtsx_pci_write_register(pcr, CARD_CLK_SOURCE, 0xFF,
CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1);
rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, 0);
return 0;
}
static int rts5261_card_power_on(struct rtsx_pcr *pcr, int card)
{
struct rtsx_cr_option *option = &pcr->option;
if (option->ocp_en)
rtsx_pci_enable_ocp(pcr);
rtsx_pci_write_register(pcr, RTS5261_LDO1_CFG1,
RTS5261_LDO1_TUNE_MASK, RTS5261_LDO1_33);
rtsx_pci_write_register(pcr, RTS5261_LDO1233318_POW_CTL,
RTS5261_LDO1_POWERON, RTS5261_LDO1_POWERON);
rtsx_pci_write_register(pcr, RTS5261_LDO1233318_POW_CTL,
RTS5261_LDO3318_POWERON, RTS5261_LDO3318_POWERON);
msleep(20);
rtsx_pci_write_register(pcr, CARD_OE, SD_OUTPUT_EN, SD_OUTPUT_EN);
/* Initialize SD_CFG1 register */
rtsx_pci_write_register(pcr, SD_CFG1, 0xFF,
SD_CLK_DIVIDE_128 | SD_20_MODE | SD_BUS_WIDTH_1BIT);
rtsx_pci_write_register(pcr, SD_SAMPLE_POINT_CTL,
0xFF, SD20_RX_POS_EDGE);
rtsx_pci_write_register(pcr, SD_PUSH_POINT_CTL, 0xFF, 0);
rtsx_pci_write_register(pcr, CARD_STOP, SD_STOP | SD_CLR_ERR,
SD_STOP | SD_CLR_ERR);
/* Reset SD_CFG3 register */
rtsx_pci_write_register(pcr, SD_CFG3, SD30_CLK_END_EN, 0);
rtsx_pci_write_register(pcr, REG_SD_STOP_SDCLK_CFG,
SD30_CLK_STOP_CFG_EN | SD30_CLK_STOP_CFG1 |
SD30_CLK_STOP_CFG0, 0);
if (pcr->extra_caps & EXTRA_CAPS_SD_SDR50 ||
pcr->extra_caps & EXTRA_CAPS_SD_SDR104)
rts5261_sd_set_sample_push_timing_sd30(pcr);
return 0;
}
static int rts5261_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
{
int err;
u16 val = 0;
rtsx_pci_write_register(pcr, RTS5261_CARD_PWR_CTL,
RTS5261_PUPDC, RTS5261_PUPDC);
switch (voltage) {
case OUTPUT_3V3:
rtsx_pci_read_phy_register(pcr, PHY_TUNE, &val);
val |= PHY_TUNE_SDBUS_33;
err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, val);
if (err < 0)
return err;
rtsx_pci_write_register(pcr, RTS5261_DV3318_CFG,
RTS5261_DV3318_TUNE_MASK, RTS5261_DV3318_33);
rtsx_pci_write_register(pcr, SD_PAD_CTL,
SD_IO_USING_1V8, 0);
break;
case OUTPUT_1V8:
rtsx_pci_read_phy_register(pcr, PHY_TUNE, &val);
val &= ~PHY_TUNE_SDBUS_33;
err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, val);
if (err < 0)
return err;
rtsx_pci_write_register(pcr, RTS5261_DV3318_CFG,
RTS5261_DV3318_TUNE_MASK, RTS5261_DV3318_18);
rtsx_pci_write_register(pcr, SD_PAD_CTL,
SD_IO_USING_1V8, SD_IO_USING_1V8);
break;
default:
return -EINVAL;
}
/* set pad drive */
rts5261_fill_driving(pcr, voltage);
return 0;
}
static void rts5261_stop_cmd(struct rtsx_pcr *pcr)
{
rtsx_pci_writel(pcr, RTSX_HCBCTLR, STOP_CMD);
rtsx_pci_writel(pcr, RTSX_HDBCTLR, STOP_DMA);
rtsx_pci_write_register(pcr, RTS5260_DMA_RST_CTL_0,
RTS5260_DMA_RST | RTS5260_ADMA3_RST,
RTS5260_DMA_RST | RTS5260_ADMA3_RST);
rtsx_pci_write_register(pcr, RBCTL, RB_FLUSH, RB_FLUSH);
}
static void rts5261_card_before_power_off(struct rtsx_pcr *pcr)
{
rts5261_stop_cmd(pcr);
rts5261_switch_output_voltage(pcr, OUTPUT_3V3);
}
static void rts5261_enable_ocp(struct rtsx_pcr *pcr)
{
u8 val = 0;
val = SD_OCP_INT_EN | SD_DETECT_EN;
rtsx_pci_write_register(pcr, REG_OCPCTL, 0xFF, val);
}
static void rts5261_disable_ocp(struct rtsx_pcr *pcr)
{
u8 mask = 0;
mask = SD_OCP_INT_EN | SD_DETECT_EN;
rtsx_pci_write_register(pcr, REG_OCPCTL, mask, 0);
rtsx_pci_write_register(pcr, RTS5261_LDO1_CFG0,
RTS5261_LDO1_OCP_EN | RTS5261_LDO1_OCP_LMT_EN, 0);
}
static int rts5261_card_power_off(struct rtsx_pcr *pcr, int card)
{
int err = 0;
rts5261_card_before_power_off(pcr);
err = rtsx_pci_write_register(pcr, RTS5261_LDO1233318_POW_CTL,
RTS5261_LDO_POWERON_MASK, 0);
if (pcr->option.ocp_en)
rtsx_pci_disable_ocp(pcr);
return err;
}
static void rts5261_init_ocp(struct rtsx_pcr *pcr)
{
struct rtsx_cr_option *option = &pcr->option;
if (option->ocp_en) {
u8 mask, val;
rtsx_pci_write_register(pcr, RTS5261_LDO1_CFG0,
RTS5261_LDO1_OCP_EN | RTS5261_LDO1_OCP_LMT_EN,
RTS5261_LDO1_OCP_EN | RTS5261_LDO1_OCP_LMT_EN);
rtsx_pci_write_register(pcr, RTS5261_LDO1_CFG0,
RTS5261_LDO1_OCP_THD_MASK, option->sd_800mA_ocp_thd);
rtsx_pci_write_register(pcr, RTS5261_LDO1_CFG0,
RTS5261_LDO1_OCP_LMT_THD_MASK,
RTS5261_LDO1_LMT_THD_2000);
mask = SD_OCP_GLITCH_MASK;
val = pcr->hw_param.ocp_glitch;
rtsx_pci_write_register(pcr, REG_OCPGLITCH, mask, val);
rts5261_enable_ocp(pcr);
} else {
rtsx_pci_write_register(pcr, RTS5261_LDO1_CFG0,
RTS5261_LDO1_OCP_EN | RTS5261_LDO1_OCP_LMT_EN, 0);
}
}
static void rts5261_clear_ocpstat(struct rtsx_pcr *pcr)
{
u8 mask = 0;
u8 val = 0;
mask = SD_OCP_INT_CLR | SD_OC_CLR;
val = SD_OCP_INT_CLR | SD_OC_CLR;
rtsx_pci_write_register(pcr, REG_OCPCTL, mask, val);
udelay(10);
rtsx_pci_write_register(pcr, REG_OCPCTL, mask, 0);
}
static void rts5261_process_ocp(struct rtsx_pcr *pcr)
{
if (!pcr->option.ocp_en)
return;
rtsx_pci_get_ocpstat(pcr, &pcr->ocp_stat);
if (pcr->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) {
rts5261_card_power_off(pcr, RTSX_SD_CARD);
rtsx_pci_write_register(pcr, CARD_OE, SD_OUTPUT_EN, 0);
rts5261_clear_ocpstat(pcr);
pcr->ocp_stat = 0;
}
}
static int rts5261_init_from_hw(struct rtsx_pcr *pcr)
{
int retval;
u32 lval, i;
u8 valid, efuse_valid, tmp;
rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL,
REG_EFUSE_POR | REG_EFUSE_POWER_MASK,
REG_EFUSE_POR | REG_EFUSE_POWERON);
udelay(1);
rtsx_pci_write_register(pcr, RTS5261_EFUSE_ADDR,
RTS5261_EFUSE_ADDR_MASK, 0x00);
rtsx_pci_write_register(pcr, RTS5261_EFUSE_CTL,
RTS5261_EFUSE_ENABLE | RTS5261_EFUSE_MODE_MASK,
RTS5261_EFUSE_ENABLE);
/* Wait transfer end */
for (i = 0; i < MAX_RW_REG_CNT; i++) {
rtsx_pci_read_register(pcr, RTS5261_EFUSE_CTL, &tmp);
if ((tmp & 0x80) == 0)
break;
}
rtsx_pci_read_register(pcr, RTS5261_EFUSE_READ_DATA, &tmp);
efuse_valid = ((tmp & 0x0C) >> 2);
pcr_dbg(pcr, "Load efuse valid: 0x%x\n", efuse_valid);
if (efuse_valid == 0) {
retval = rtsx_pci_read_config_dword(pcr,
PCR_SETTING_REG2, &lval);
if (retval != 0)
pcr_dbg(pcr, "read 0x814 DW fail\n");
pcr_dbg(pcr, "DW from 0x814: 0x%x\n", lval);
/* 0x816 */
valid = (u8)((lval >> 16) & 0x03);
pcr_dbg(pcr, "0x816: %d\n", valid);
}
rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL,
REG_EFUSE_POR, 0);
pcr_dbg(pcr, "Disable efuse por!\n");
rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, &lval);
lval = lval & 0x00FFFFFF;
retval = rtsx_pci_write_config_dword(pcr, PCR_SETTING_REG2, lval);
if (retval != 0)
pcr_dbg(pcr, "write config fail\n");
return retval;
}
static void rts5261_init_from_cfg(struct rtsx_pcr *pcr)
{
u32 lval;
struct rtsx_cr_option *option = &pcr->option;
rtsx_pci_read_config_dword(pcr, PCR_ASPM_SETTING_REG1, &lval);
if (lval & ASPM_L1_1_EN_MASK)
rtsx_set_dev_flag(pcr, ASPM_L1_1_EN);
else
rtsx_clear_dev_flag(pcr, ASPM_L1_1_EN);
if (lval & ASPM_L1_2_EN_MASK)
rtsx_set_dev_flag(pcr, ASPM_L1_2_EN);
else
rtsx_clear_dev_flag(pcr, ASPM_L1_2_EN);
if (lval & PM_L1_1_EN_MASK)
rtsx_set_dev_flag(pcr, PM_L1_1_EN);
else
rtsx_clear_dev_flag(pcr, PM_L1_1_EN);
if (lval & PM_L1_2_EN_MASK)
rtsx_set_dev_flag(pcr, PM_L1_2_EN);
else
rtsx_clear_dev_flag(pcr, PM_L1_2_EN);
rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, 0xFF, 0);
if (option->ltr_en) {
u16 val;
pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &val);
if (val & PCI_EXP_DEVCTL2_LTR_EN) {
option->ltr_enabled = true;
option->ltr_active = true;
rtsx_set_ltr_latency(pcr, option->ltr_active_latency);
} else {
option->ltr_enabled = false;
}
}
if (rtsx_check_dev_flag(pcr, ASPM_L1_1_EN | ASPM_L1_2_EN
| PM_L1_1_EN | PM_L1_2_EN))
option->force_clkreq_0 = false;
else
option->force_clkreq_0 = true;
}
static int rts5261_extra_init_hw(struct rtsx_pcr *pcr)
{
struct rtsx_cr_option *option = &pcr->option;
rtsx_pci_write_register(pcr, RTS5261_AUTOLOAD_CFG1,
CD_RESUME_EN_MASK, CD_RESUME_EN_MASK);
rts5261_init_from_cfg(pcr);
rts5261_init_from_hw(pcr);
/* power off efuse */
rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL,
REG_EFUSE_POWER_MASK, REG_EFUSE_POWEROFF);
rtsx_pci_write_register(pcr, L1SUB_CONFIG1,
AUX_CLK_ACTIVE_SEL_MASK, MAC_CKSW_DONE);
rtsx_pci_write_register(pcr, L1SUB_CONFIG3, 0xFF, 0);
rtsx_pci_write_register(pcr, RTS5261_AUTOLOAD_CFG4,
RTS5261_AUX_CLK_16M_EN, 0);
/* Release PRSNT# */
rtsx_pci_write_register(pcr, RTS5261_AUTOLOAD_CFG4,
RTS5261_FORCE_PRSNT_LOW, 0);
rtsx_pci_write_register(pcr, FUNC_FORCE_CTL,
FUNC_FORCE_UPME_XMT_DBG, FUNC_FORCE_UPME_XMT_DBG);
rtsx_pci_write_register(pcr, PCLK_CTL,
PCLK_MODE_SEL, PCLK_MODE_SEL);
rtsx_pci_write_register(pcr, PM_EVENT_DEBUG, PME_DEBUG_0, PME_DEBUG_0);
rtsx_pci_write_register(pcr, PM_CLK_FORCE_CTL, CLK_PM_EN, CLK_PM_EN);
/* LED shine disabled, set initial shine cycle period */
rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x0F, 0x02);
/* Configure driving */
rts5261_fill_driving(pcr, OUTPUT_3V3);
/*
* If u_force_clkreq_0 is enabled, CLKREQ# PIN will be forced
* to drive low, and we forcibly request clock.
*/
if (option->force_clkreq_0)
rtsx_pci_write_register(pcr, PETXCFG,
FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW);
else
rtsx_pci_write_register(pcr, PETXCFG,
FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH);
rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x10, 0x00);
rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL,
FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL);
/* Clear Enter RTD3_cold Information*/
rtsx_pci_write_register(pcr, RTS5261_FW_CTL,
RTS5261_INFORM_RTD3_COLD, 0);
return 0;
}
static void rts5261_enable_aspm(struct rtsx_pcr *pcr, bool enable)
{
struct rtsx_cr_option *option = &pcr->option;
u8 val = 0;
if (pcr->aspm_enabled == enable)
return;
if (option->dev_aspm_mode == DEV_ASPM_DYNAMIC) {
val = pcr->aspm_en;
rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL,
ASPM_MASK_NEG, val);
} else if (option->dev_aspm_mode == DEV_ASPM_BACKDOOR) {
u8 mask = FORCE_ASPM_VAL_MASK | FORCE_ASPM_CTL0;
val = FORCE_ASPM_CTL0;
val |= (pcr->aspm_en & 0x02);
rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, mask, val);
val = pcr->aspm_en;
rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL,
ASPM_MASK_NEG, val);
}
pcr->aspm_enabled = enable;
}
static void rts5261_disable_aspm(struct rtsx_pcr *pcr, bool enable)
{
struct rtsx_cr_option *option = &pcr->option;
u8 val = 0;
if (pcr->aspm_enabled == enable)
return;
if (option->dev_aspm_mode == DEV_ASPM_DYNAMIC) {
val = 0;
rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL,
ASPM_MASK_NEG, val);
} else if (option->dev_aspm_mode == DEV_ASPM_BACKDOOR) {
u8 mask = FORCE_ASPM_VAL_MASK | FORCE_ASPM_CTL0;
val = 0;
rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL,
ASPM_MASK_NEG, val);
val = FORCE_ASPM_CTL0;
rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, mask, val);
}
rtsx_pci_write_register(pcr, SD_CFG1, SD_ASYNC_FIFO_NOT_RST, 0);
udelay(10);
pcr->aspm_enabled = enable;
}
static void rts5261_set_aspm(struct rtsx_pcr *pcr, bool enable)
{
if (enable)
rts5261_enable_aspm(pcr, true);
else
rts5261_disable_aspm(pcr, false);
}
static void rts5261_set_l1off_cfg_sub_d0(struct rtsx_pcr *pcr, int active)
{
struct rtsx_cr_option *option = &pcr->option;
int aspm_L1_1, aspm_L1_2;
u8 val = 0;
aspm_L1_1 = rtsx_check_dev_flag(pcr, ASPM_L1_1_EN);
aspm_L1_2 = rtsx_check_dev_flag(pcr, ASPM_L1_2_EN);
if (active) {
/* run, latency: 60us */
if (aspm_L1_1)
val = option->ltr_l1off_snooze_sspwrgate;
} else {
/* l1off, latency: 300us */
if (aspm_L1_2)
val = option->ltr_l1off_sspwrgate;
}
rtsx_set_l1off_sub(pcr, val);
}
static const struct pcr_ops rts5261_pcr_ops = {
.fetch_vendor_settings = rtsx5261_fetch_vendor_settings,
.turn_on_led = rts5261_turn_on_led,
.turn_off_led = rts5261_turn_off_led,
.extra_init_hw = rts5261_extra_init_hw,
.enable_auto_blink = rts5261_enable_auto_blink,
.disable_auto_blink = rts5261_disable_auto_blink,
.card_power_on = rts5261_card_power_on,
.card_power_off = rts5261_card_power_off,
.switch_output_voltage = rts5261_switch_output_voltage,
.force_power_down = rts5261_force_power_down,
.stop_cmd = rts5261_stop_cmd,
.set_aspm = rts5261_set_aspm,
.set_l1off_cfg_sub_d0 = rts5261_set_l1off_cfg_sub_d0,
.enable_ocp = rts5261_enable_ocp,
.disable_ocp = rts5261_disable_ocp,
.init_ocp = rts5261_init_ocp,
.process_ocp = rts5261_process_ocp,
.clear_ocpstat = rts5261_clear_ocpstat,
};
static inline u8 double_ssc_depth(u8 depth)
{
return ((depth > 1) ? (depth - 1) : depth);
}
int rts5261_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk)
{
int err, clk;
u8 n, clk_divider, mcu_cnt, div;
static const u8 depth[] = {
[RTSX_SSC_DEPTH_4M] = RTS5261_SSC_DEPTH_4M,
[RTSX_SSC_DEPTH_2M] = RTS5261_SSC_DEPTH_2M,
[RTSX_SSC_DEPTH_1M] = RTS5261_SSC_DEPTH_1M,
[RTSX_SSC_DEPTH_500K] = RTS5261_SSC_DEPTH_512K,
};
if (initial_mode) {
/* We use 250k(around) here, in initial stage */
clk_divider = SD_CLK_DIVIDE_128;
card_clock = 30000000;
} else {
clk_divider = SD_CLK_DIVIDE_0;
}
err = rtsx_pci_write_register(pcr, SD_CFG1,
SD_CLK_DIVIDE_MASK, clk_divider);
if (err < 0)
return err;
card_clock /= 1000000;
pcr_dbg(pcr, "Switch card clock to %dMHz\n", card_clock);
clk = card_clock;
if (!initial_mode && double_clk)
clk = card_clock * 2;
pcr_dbg(pcr, "Internal SSC clock: %dMHz (cur_clock = %d)\n",
clk, pcr->cur_clock);
if (clk == pcr->cur_clock)
return 0;
if (pcr->ops->conv_clk_and_div_n)
n = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N);
else
n = (u8)(clk - 4);
if ((clk <= 4) || (n > 396))
return -EINVAL;
mcu_cnt = (u8)(125/clk + 3);
if (mcu_cnt > 15)
mcu_cnt = 15;
div = CLK_DIV_1;
while ((n < MIN_DIV_N_PCR - 4) && (div < CLK_DIV_8)) {
if (pcr->ops->conv_clk_and_div_n) {
int dbl_clk = pcr->ops->conv_clk_and_div_n(n,
DIV_N_TO_CLK) * 2;
n = (u8)pcr->ops->conv_clk_and_div_n(dbl_clk,
CLK_TO_DIV_N);
} else {
n = (n + 4) * 2 - 4;
}
div++;
}
n = (n / 2);
pcr_dbg(pcr, "n = %d, div = %d\n", n, div);
ssc_depth = depth[ssc_depth];
if (double_clk)
ssc_depth = double_ssc_depth(ssc_depth);
if (ssc_depth) {
if (div == CLK_DIV_2) {
if (ssc_depth > 1)
ssc_depth -= 1;
else
ssc_depth = RTS5261_SSC_DEPTH_8M;
} else if (div == CLK_DIV_4) {
if (ssc_depth > 2)
ssc_depth -= 2;
else
ssc_depth = RTS5261_SSC_DEPTH_8M;
} else if (div == CLK_DIV_8) {
if (ssc_depth > 3)
ssc_depth -= 3;
else
ssc_depth = RTS5261_SSC_DEPTH_8M;
}
} else {
ssc_depth = 0;
}
pcr_dbg(pcr, "ssc_depth = %d\n", ssc_depth);
rtsx_pci_init_cmd(pcr);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL,
CLK_LOW_FREQ, CLK_LOW_FREQ);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_DIV,
0xFF, (div << 4) | mcu_cnt);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2,
SSC_DEPTH_MASK, ssc_depth);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB);
if (vpclk) {
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL,
PHASE_NOT_RESET, 0);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK1_CTL,
PHASE_NOT_RESET, 0);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL,
PHASE_NOT_RESET, PHASE_NOT_RESET);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK1_CTL,
PHASE_NOT_RESET, PHASE_NOT_RESET);
}
err = rtsx_pci_send_cmd(pcr, 2000);
if (err < 0)
return err;
/* Wait SSC clock stable */
udelay(SSC_CLOCK_STABLE_WAIT);
err = rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, 0);
if (err < 0)
return err;
pcr->cur_clock = clk;
return 0;
}
void rts5261_init_params(struct rtsx_pcr *pcr)
{
struct rtsx_cr_option *option = &pcr->option;
struct rtsx_hw_param *hw_param = &pcr->hw_param;
pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104;
pcr->num_slots = 1;
pcr->ops = &rts5261_pcr_ops;
pcr->flags = 0;
pcr->card_drive_sel = RTSX_CARD_DRIVE_DEFAULT;
pcr->sd30_drive_sel_1v8 = CFG_DRIVER_TYPE_B;
pcr->sd30_drive_sel_3v3 = CFG_DRIVER_TYPE_B;
pcr->aspm_en = ASPM_L1_EN;
pcr->tx_initial_phase = SET_CLOCK_PHASE(20, 27, 16);
pcr->rx_initial_phase = SET_CLOCK_PHASE(24, 6, 5);
pcr->ic_version = rts5261_get_ic_version(pcr);
pcr->sd_pull_ctl_enable_tbl = rts5261_sd_pull_ctl_enable_tbl;
pcr->sd_pull_ctl_disable_tbl = rts5261_sd_pull_ctl_disable_tbl;
pcr->reg_pm_ctrl3 = RTS5261_AUTOLOAD_CFG3;
option->dev_flags = (LTR_L1SS_PWR_GATE_CHECK_CARD_EN
| LTR_L1SS_PWR_GATE_EN);
option->ltr_en = true;
/* init latency of active, idle, L1OFF to 60us, 300us, 3ms */
option->ltr_active_latency = LTR_ACTIVE_LATENCY_DEF;
option->ltr_idle_latency = LTR_IDLE_LATENCY_DEF;
option->ltr_l1off_latency = LTR_L1OFF_LATENCY_DEF;
option->l1_snooze_delay = L1_SNOOZE_DELAY_DEF;
option->ltr_l1off_sspwrgate = 0x7F;
option->ltr_l1off_snooze_sspwrgate = 0x78;
option->dev_aspm_mode = DEV_ASPM_DYNAMIC;
option->ocp_en = 1;
hw_param->interrupt_en |= SD_OC_INT_EN;
hw_param->ocp_glitch = SD_OCP_GLITCH_800U;
option->sd_800mA_ocp_thd = RTS5261_LDO1_OCP_THD_1040;
}

View File

@ -0,0 +1,233 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Driver for Realtek PCI-Express card reader
*
* Copyright(c) 2018-2019 Realtek Semiconductor Corp. All rights reserved.
*
* Author:
* Rui FENG <rui_feng@realsil.com.cn>
* Wei WANG <wei_wang@realsil.com.cn>
*/
#ifndef RTS5261_H
#define RTS5261_H
/*New add*/
#define rts5261_vendor_setting_valid(reg) ((reg) & 0x010000)
#define rts5261_reg_to_aspm(reg) (((reg) >> 28) ^ 0x03)
#define rts5261_reg_check_reverse_socket(reg) ((reg) & 0x04)
#define rts5261_reg_to_card_drive_sel(reg) ((((reg) >> 6) & 0x01) << 6)
#define rts5261_reg_to_sd30_drive_sel_1v8(reg) (((reg) >> 22) ^ 0x03)
#define rts5261_reg_to_sd30_drive_sel_3v3(reg) (((reg) >> 16) ^ 0x03)
#define RTS5261_AUTOLOAD_CFG0 0xFF7B
#define RTS5261_AUTOLOAD_CFG1 0xFF7C
#define RTS5261_AUTOLOAD_CFG2 0xFF7D
#define RTS5261_AUTOLOAD_CFG3 0xFF7E
#define RTS5261_AUTOLOAD_CFG4 0xFF7F
#define RTS5261_FORCE_PRSNT_LOW (1 << 6)
#define RTS5261_AUX_CLK_16M_EN (1 << 5)
#define RTS5261_REG_VREF 0xFE97
#define RTS5261_PWD_SUSPND_EN (1 << 4)
#define RTS5261_PAD_H3L1 0xFF79
#define PAD_GPIO_H3L1 (1 << 3)
/* SSC_CTL2 0xFC12 */
#define RTS5261_SSC_DEPTH_MASK 0x07
#define RTS5261_SSC_DEPTH_DISALBE 0x00
#define RTS5261_SSC_DEPTH_8M 0x01
#define RTS5261_SSC_DEPTH_4M 0x02
#define RTS5261_SSC_DEPTH_2M 0x03
#define RTS5261_SSC_DEPTH_1M 0x04
#define RTS5261_SSC_DEPTH_512K 0x05
#define RTS5261_SSC_DEPTH_256K 0x06
#define RTS5261_SSC_DEPTH_128K 0x07
/* efuse control register*/
#define RTS5261_EFUSE_CTL 0xFC30
#define RTS5261_EFUSE_ENABLE 0x80
/* EFUSE_MODE: 0=READ 1=PROGRAM */
#define RTS5261_EFUSE_MODE_MASK 0x40
#define RTS5261_EFUSE_PROGRAM 0x40
#define RTS5261_EFUSE_ADDR 0xFC31
#define RTS5261_EFUSE_ADDR_MASK 0x3F
#define RTS5261_EFUSE_WRITE_DATA 0xFC32
#define RTS5261_EFUSE_READ_DATA 0xFC34
/* DMACTL 0xFE2C */
#define RTS5261_DMA_PACK_SIZE_MASK 0xF0
/* FW config info register */
#define RTS5261_FW_CFG_INFO0 0xFF50
#define RTS5261_FW_EXPRESS_TEST_MASK (0x01<<0)
#define RTS5261_FW_EA_MODE_MASK (0x01<<5)
/* FW config register */
#define RTS5261_FW_CFG0 0xFF54
#define RTS5261_FW_ENTER_EXPRESS (0x01<<0)
#define RTS5261_FW_CFG1 0xFF55
#define RTS5261_SYS_CLK_SEL_MCU_CLK (0x01<<7)
#define RTS5261_CRC_CLK_SEL_MCU_CLK (0x01<<6)
#define RTS5261_FAKE_MCU_CLOCK_GATING (0x01<<5)
/*MCU_bus_mode_sel: 0=real 8051 1=fake mcu*/
#define RTS5261_MCU_BUS_SEL_MASK (0x01<<4)
/*MCU_clock_sel:VerA 00=aux16M 01=aux400K 1x=REFCLK100M*/
/*MCU_clock_sel:VerB 00=aux400K 01=aux16M 10=REFCLK100M*/
#define RTS5261_MCU_CLOCK_SEL_MASK (0x03<<2)
#define RTS5261_MCU_CLOCK_SEL_16M (0x01<<2)
#define RTS5261_MCU_CLOCK_GATING (0x01<<1)
#define RTS5261_DRIVER_ENABLE_FW (0x01<<0)
/* FW status register */
#define RTS5261_FW_STATUS 0xFF56
#define RTS5261_EXPRESS_LINK_FAIL_MASK (0x01<<7)
/* FW control register */
#define RTS5261_FW_CTL 0xFF5F
#define RTS5261_INFORM_RTD3_COLD (0x01<<5)
#define RTS5261_REG_FPDCTL 0xFF60
#define RTS5261_REG_LDO12_CFG 0xFF6E
#define RTS5261_LDO12_VO_TUNE_MASK (0x07<<1)
#define RTS5261_LDO12_115 (0x03<<1)
#define RTS5261_LDO12_120 (0x04<<1)
#define RTS5261_LDO12_125 (0x05<<1)
#define RTS5261_LDO12_130 (0x06<<1)
#define RTS5261_LDO12_135 (0x07<<1)
/* LDO control register */
#define RTS5261_CARD_PWR_CTL 0xFD50
#define RTS5261_SD_CLK_ISO (0x01<<7)
#define RTS5261_PAD_SD_DAT_FW_CTRL (0x01<<6)
#define RTS5261_PUPDC (0x01<<5)
#define RTS5261_SD_CMD_ISO (0x01<<4)
#define RTS5261_SD_DAT_ISO_MASK (0x0F<<0)
#define RTS5261_LDO1233318_POW_CTL 0xFF70
#define RTS5261_LDO3318_POWERON (0x01<<3)
#define RTS5261_LDO3_POWERON (0x01<<2)
#define RTS5261_LDO2_POWERON (0x01<<1)
#define RTS5261_LDO1_POWERON (0x01<<0)
#define RTS5261_LDO_POWERON_MASK (0x0F<<0)
#define RTS5261_DV3318_CFG 0xFF71
#define RTS5261_DV3318_TUNE_MASK (0x07<<4)
#define RTS5261_DV3318_18 (0x02<<4)
#define RTS5261_DV3318_19 (0x04<<4)
#define RTS5261_DV3318_33 (0x07<<4)
#define RTS5261_LDO1_CFG0 0xFF72
#define RTS5261_LDO1_OCP_THD_MASK (0x07<<5)
#define RTS5261_LDO1_OCP_EN (0x01<<4)
#define RTS5261_LDO1_OCP_LMT_THD_MASK (0x03<<2)
#define RTS5261_LDO1_OCP_LMT_EN (0x01<<1)
/* CRD6603-433 190319 request changed */
#define RTS5261_LDO1_OCP_THD_740 (0x00<<5)
#define RTS5261_LDO1_OCP_THD_800 (0x01<<5)
#define RTS5261_LDO1_OCP_THD_860 (0x02<<5)
#define RTS5261_LDO1_OCP_THD_920 (0x03<<5)
#define RTS5261_LDO1_OCP_THD_980 (0x04<<5)
#define RTS5261_LDO1_OCP_THD_1040 (0x05<<5)
#define RTS5261_LDO1_OCP_THD_1100 (0x06<<5)
#define RTS5261_LDO1_OCP_THD_1160 (0x07<<5)
#define RTS5261_LDO1_LMT_THD_450 (0x00<<2)
#define RTS5261_LDO1_LMT_THD_1000 (0x01<<2)
#define RTS5261_LDO1_LMT_THD_1500 (0x02<<2)
#define RTS5261_LDO1_LMT_THD_2000 (0x03<<2)
#define RTS5261_LDO1_CFG1 0xFF73
#define RTS5261_LDO1_TUNE_MASK (0x07<<1)
#define RTS5261_LDO1_18 (0x05<<1)
#define RTS5261_LDO1_33 (0x07<<1)
#define RTS5261_LDO1_PWD_MASK (0x01<<0)
#define RTS5261_LDO2_CFG0 0xFF74
#define RTS5261_LDO2_OCP_THD_MASK (0x07<<5)
#define RTS5261_LDO2_OCP_EN (0x01<<4)
#define RTS5261_LDO2_OCP_LMT_THD_MASK (0x03<<2)
#define RTS5261_LDO2_OCP_LMT_EN (0x01<<1)
#define RTS5261_LDO2_OCP_THD_620 (0x00<<5)
#define RTS5261_LDO2_OCP_THD_650 (0x01<<5)
#define RTS5261_LDO2_OCP_THD_680 (0x02<<5)
#define RTS5261_LDO2_OCP_THD_720 (0x03<<5)
#define RTS5261_LDO2_OCP_THD_750 (0x04<<5)
#define RTS5261_LDO2_OCP_THD_780 (0x05<<5)
#define RTS5261_LDO2_OCP_THD_810 (0x06<<5)
#define RTS5261_LDO2_OCP_THD_840 (0x07<<5)
#define RTS5261_LDO2_CFG1 0xFF75
#define RTS5261_LDO2_TUNE_MASK (0x07<<1)
#define RTS5261_LDO2_18 (0x05<<1)
#define RTS5261_LDO2_33 (0x07<<1)
#define RTS5261_LDO2_PWD_MASK (0x01<<0)
#define RTS5261_LDO3_CFG0 0xFF76
#define RTS5261_LDO3_OCP_THD_MASK (0x07<<5)
#define RTS5261_LDO3_OCP_EN (0x01<<4)
#define RTS5261_LDO3_OCP_LMT_THD_MASK (0x03<<2)
#define RTS5261_LDO3_OCP_LMT_EN (0x01<<1)
#define RTS5261_LDO3_OCP_THD_620 (0x00<<5)
#define RTS5261_LDO3_OCP_THD_650 (0x01<<5)
#define RTS5261_LDO3_OCP_THD_680 (0x02<<5)
#define RTS5261_LDO3_OCP_THD_720 (0x03<<5)
#define RTS5261_LDO3_OCP_THD_750 (0x04<<5)
#define RTS5261_LDO3_OCP_THD_780 (0x05<<5)
#define RTS5261_LDO3_OCP_THD_810 (0x06<<5)
#define RTS5261_LDO3_OCP_THD_840 (0x07<<5)
#define RTS5261_LDO3_CFG1 0xFF77
#define RTS5261_LDO3_TUNE_MASK (0x07<<1)
#define RTS5261_LDO3_18 (0x05<<1)
#define RTS5261_LDO3_33 (0x07<<1)
#define RTS5261_LDO3_PWD_MASK (0x01<<0)
#define RTS5261_REG_PME_FORCE_CTL 0xFF78
#define FORCE_PM_CONTROL 0x20
#define FORCE_PM_VALUE 0x10
#define REG_EFUSE_BYPASS 0x08
#define REG_EFUSE_POR 0x04
#define REG_EFUSE_POWER_MASK 0x03
#define REG_EFUSE_POWERON 0x03
#define REG_EFUSE_POWEROFF 0x00
/* Single LUN, support SD/SD EXPRESS */
#define DEFAULT_SINGLE 0
#define SD_LUN 1
#define SD_EXPRESS_LUN 2
/* For Change_FPGA_SSCClock Function */
#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
int rts5261_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk);
#endif /* RTS5261_H */

View File

@ -22,6 +22,7 @@
#include <asm/unaligned.h>
#include "rtsx_pcr.h"
#include "rts5261.h"
static bool msi_en = true;
module_param(msi_en, bool, S_IRUGO | S_IWUSR);
@ -34,9 +35,6 @@ static struct mfd_cell rtsx_pcr_cells[] = {
[RTSX_SD_CARD] = {
.name = DRV_NAME_RTSX_PCI_SDMMC,
},
[RTSX_MS_CARD] = {
.name = DRV_NAME_RTSX_PCI_MS,
},
};
static const struct pci_device_id rtsx_pci_ids[] = {
@ -51,6 +49,7 @@ static const struct pci_device_id rtsx_pci_ids[] = {
{ PCI_DEVICE(0x10EC, 0x524A), PCI_CLASS_OTHERS << 16, 0xFF0000 },
{ PCI_DEVICE(0x10EC, 0x525A), PCI_CLASS_OTHERS << 16, 0xFF0000 },
{ PCI_DEVICE(0x10EC, 0x5260), PCI_CLASS_OTHERS << 16, 0xFF0000 },
{ PCI_DEVICE(0x10EC, 0x5261), PCI_CLASS_OTHERS << 16, 0xFF0000 },
{ 0, }
};
@ -438,8 +437,16 @@ static void rtsx_pci_add_sg_tbl(struct rtsx_pcr *pcr,
if (end)
option |= RTSX_SG_END;
val = ((u64)addr << 32) | ((u64)len << 12) | option;
if (PCI_PID(pcr) == PID_5261) {
if (len > 0xFFFF)
val = ((u64)addr << 32) | (((u64)len & 0xFFFF) << 16)
| (((u64)len >> 16) << 6) | option;
else
val = ((u64)addr << 32) | ((u64)len << 16) | option;
} else {
val = ((u64)addr << 32) | ((u64)len << 12) | option;
}
put_unaligned_le64(val, ptr);
pcr->sgi++;
}
@ -684,7 +691,6 @@ int rtsx_pci_card_pull_ctl_disable(struct rtsx_pcr *pcr, int card)
else
return -EINVAL;
return rtsx_pci_set_pull_ctl(pcr, tbl);
}
EXPORT_SYMBOL_GPL(rtsx_pci_card_pull_ctl_disable);
@ -735,6 +741,10 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
[RTSX_SSC_DEPTH_250K] = SSC_DEPTH_250K,
};
if (PCI_PID(pcr) == PID_5261)
return rts5261_pci_switch_clock(pcr, card_clock,
ssc_depth, initial_mode, double_clk, vpclk);
if (initial_mode) {
/* We use 250k(around) here, in initial stage */
clk_divider = SD_CLK_DIVIDE_128;
@ -1253,7 +1263,15 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr)
rtsx_pci_enable_bus_int(pcr);
/* Power on SSC */
err = rtsx_pci_write_register(pcr, FPDCTL, SSC_POWER_DOWN, 0);
if (PCI_PID(pcr) == PID_5261) {
/* Gating real mcu clock */
err = rtsx_pci_write_register(pcr, RTS5261_FW_CFG1,
RTS5261_MCU_CLOCK_GATING, 0);
err = rtsx_pci_write_register(pcr, RTS5261_REG_FPDCTL,
SSC_POWER_DOWN, 0);
} else {
err = rtsx_pci_write_register(pcr, FPDCTL, SSC_POWER_DOWN, 0);
}
if (err < 0)
return err;
@ -1283,7 +1301,12 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr)
/* Enable SSC Clock */
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1,
0xFF, SSC_8X_EN | SSC_SEL_4M);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, 0x12);
if (PCI_PID(pcr) == PID_5261)
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF,
RTS5261_SSC_DEPTH_2M);
else
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, 0x12);
/* Disable cd_pwr_save */
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CHANGE_LINK_STATE, 0x16, 0x10);
/* Clear Link Ready Interrupt */
@ -1314,6 +1337,7 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr)
case PID_524A:
case PID_525A:
case PID_5260:
case PID_5261:
rtsx_pci_write_register(pcr, PM_CLK_FORCE_CTL, 1, 1);
break;
default:
@ -1393,9 +1417,14 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr)
case 0x5286:
rtl8402_init_params(pcr);
break;
case 0x5260:
rts5260_init_params(pcr);
break;
case 0x5261:
rts5261_init_params(pcr);
break;
}
pcr_dbg(pcr, "PID: 0x%04x, IC version: 0x%02x\n",

View File

@ -53,6 +53,7 @@ void rts524a_init_params(struct rtsx_pcr *pcr);
void rts525a_init_params(struct rtsx_pcr *pcr);
void rtl8411b_init_params(struct rtsx_pcr *pcr);
void rts5260_init_params(struct rtsx_pcr *pcr);
void rts5261_init_params(struct rtsx_pcr *pcr);
static inline u8 map_sd_drive(int idx)
{

View File

@ -175,6 +175,10 @@ static int eeprom_probe(struct i2c_client *client,
}
}
/* Let the users know they are using deprecated driver */
dev_notice(&client->dev,
"eeprom driver is deprecated, please use at24 instead\n");
/* create the sysfs eeprom file */
return sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr);
}

View File

@ -32,8 +32,9 @@
#define FASTRPC_CTX_MAX (256)
#define FASTRPC_INIT_HANDLE 1
#define FASTRPC_CTXID_MASK (0xFF0)
#define INIT_FILELEN_MAX (64 * 1024 * 1024)
#define INIT_FILELEN_MAX (2 * 1024 * 1024)
#define FASTRPC_DEVICE_NAME "fastrpc"
#define ADSP_MMAP_ADD_PAGES 0x1000
/* Retrives number of input buffers from the scalars parameter */
#define REMOTE_SCALARS_INBUFS(sc) (((sc) >> 16) & 0x0ff)
@ -66,6 +67,8 @@
/* Remote Method id table */
#define FASTRPC_RMID_INIT_ATTACH 0
#define FASTRPC_RMID_INIT_RELEASE 1
#define FASTRPC_RMID_INIT_MMAP 4
#define FASTRPC_RMID_INIT_MUNMAP 5
#define FASTRPC_RMID_INIT_CREATE 6
#define FASTRPC_RMID_INIT_CREATE_ATTR 7
#define FASTRPC_RMID_INIT_CREATE_STATIC 8
@ -89,6 +92,23 @@ struct fastrpc_remote_arg {
u64 len;
};
struct fastrpc_mmap_rsp_msg {
u64 vaddr;
};
struct fastrpc_mmap_req_msg {
s32 pgid;
u32 flags;
u64 vaddr;
s32 num;
};
struct fastrpc_munmap_req_msg {
s32 pgid;
u64 vaddr;
u64 size;
};
struct fastrpc_msg {
int pid; /* process group id */
int tid; /* thread id */
@ -123,6 +143,9 @@ struct fastrpc_buf {
/* Lock for dma buf attachments */
struct mutex lock;
struct list_head attachments;
/* mmap support */
struct list_head node; /* list of user requested mmaps */
uintptr_t raddr;
};
struct fastrpc_dma_buf_attachment {
@ -192,6 +215,7 @@ struct fastrpc_user {
struct list_head user;
struct list_head maps;
struct list_head pending;
struct list_head mmaps;
struct fastrpc_channel_ctx *cctx;
struct fastrpc_session_ctx *sctx;
@ -269,6 +293,7 @@ static int fastrpc_buf_alloc(struct fastrpc_user *fl, struct device *dev,
return -ENOMEM;
INIT_LIST_HEAD(&buf->attachments);
INIT_LIST_HEAD(&buf->node);
mutex_init(&buf->lock);
buf->fl = fl;
@ -276,6 +301,7 @@ static int fastrpc_buf_alloc(struct fastrpc_user *fl, struct device *dev,
buf->phys = 0;
buf->size = size;
buf->dev = dev;
buf->raddr = 0;
buf->virt = dma_alloc_coherent(dev, buf->size, (dma_addr_t *)&buf->phys,
GFP_KERNEL);
@ -934,8 +960,13 @@ static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel,
if (err)
goto bail;
/* Wait for remote dsp to respond or time out */
err = wait_for_completion_interruptible(&ctx->work);
if (kernel) {
if (!wait_for_completion_timeout(&ctx->work, 10 * HZ))
err = -ETIMEDOUT;
} else {
err = wait_for_completion_interruptible(&ctx->work);
}
if (err)
goto bail;
@ -954,12 +985,13 @@ static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel,
}
bail:
/* We are done with this compute context, remove it from pending list */
spin_lock(&fl->lock);
list_del(&ctx->node);
spin_unlock(&fl->lock);
fastrpc_context_put(ctx);
if (err != -ERESTARTSYS && err != -ETIMEDOUT) {
/* We are done with this compute context */
spin_lock(&fl->lock);
list_del(&ctx->node);
spin_unlock(&fl->lock);
fastrpc_context_put(ctx);
}
if (err)
dev_dbg(fl->sctx->dev, "Error: Invoke Failed %d\n", err);
@ -1131,6 +1163,7 @@ static int fastrpc_device_release(struct inode *inode, struct file *file)
struct fastrpc_channel_ctx *cctx = fl->cctx;
struct fastrpc_invoke_ctx *ctx, *n;
struct fastrpc_map *map, *m;
struct fastrpc_buf *buf, *b;
unsigned long flags;
fastrpc_release_current_dsp_process(fl);
@ -1152,6 +1185,11 @@ static int fastrpc_device_release(struct inode *inode, struct file *file)
fastrpc_map_put(map);
}
list_for_each_entry_safe(buf, b, &fl->mmaps, node) {
list_del(&buf->node);
fastrpc_buf_free(buf);
}
fastrpc_session_free(cctx, fl->sctx);
fastrpc_channel_ctx_put(cctx);
@ -1180,6 +1218,7 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp)
mutex_init(&fl->mutex);
INIT_LIST_HEAD(&fl->pending);
INIT_LIST_HEAD(&fl->maps);
INIT_LIST_HEAD(&fl->mmaps);
INIT_LIST_HEAD(&fl->user);
fl->tgid = current->tgid;
fl->cctx = cctx;
@ -1285,6 +1324,148 @@ static int fastrpc_invoke(struct fastrpc_user *fl, char __user *argp)
return err;
}
static int fastrpc_req_munmap_impl(struct fastrpc_user *fl,
struct fastrpc_req_munmap *req)
{
struct fastrpc_invoke_args args[1] = { [0] = { 0 } };
struct fastrpc_buf *buf, *b;
struct fastrpc_munmap_req_msg req_msg;
struct device *dev = fl->sctx->dev;
int err;
u32 sc;
spin_lock(&fl->lock);
list_for_each_entry_safe(buf, b, &fl->mmaps, node) {
if ((buf->raddr == req->vaddrout) && (buf->size == req->size))
break;
buf = NULL;
}
spin_unlock(&fl->lock);
if (!buf) {
dev_err(dev, "mmap not in list\n");
return -EINVAL;
}
req_msg.pgid = fl->tgid;
req_msg.size = buf->size;
req_msg.vaddr = buf->raddr;
args[0].ptr = (u64) (uintptr_t) &req_msg;
args[0].length = sizeof(req_msg);
sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_MUNMAP, 1, 0);
err = fastrpc_internal_invoke(fl, true, FASTRPC_INIT_HANDLE, sc,
&args[0]);
if (!err) {
dev_dbg(dev, "unmmap\tpt 0x%09lx OK\n", buf->raddr);
spin_lock(&fl->lock);
list_del(&buf->node);
spin_unlock(&fl->lock);
fastrpc_buf_free(buf);
} else {
dev_err(dev, "unmmap\tpt 0x%09lx ERROR\n", buf->raddr);
}
return err;
}
static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp)
{
struct fastrpc_req_munmap req;
if (copy_from_user(&req, argp, sizeof(req)))
return -EFAULT;
return fastrpc_req_munmap_impl(fl, &req);
}
static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp)
{
struct fastrpc_invoke_args args[3] = { [0 ... 2] = { 0 } };
struct fastrpc_buf *buf = NULL;
struct fastrpc_mmap_req_msg req_msg;
struct fastrpc_mmap_rsp_msg rsp_msg;
struct fastrpc_req_munmap req_unmap;
struct fastrpc_phy_page pages;
struct fastrpc_req_mmap req;
struct device *dev = fl->sctx->dev;
int err;
u32 sc;
if (copy_from_user(&req, argp, sizeof(req)))
return -EFAULT;
if (req.flags != ADSP_MMAP_ADD_PAGES) {
dev_err(dev, "flag not supported 0x%x\n", req.flags);
return -EINVAL;
}
if (req.vaddrin) {
dev_err(dev, "adding user allocated pages is not supported\n");
return -EINVAL;
}
err = fastrpc_buf_alloc(fl, fl->sctx->dev, req.size, &buf);
if (err) {
dev_err(dev, "failed to allocate buffer\n");
return err;
}
req_msg.pgid = fl->tgid;
req_msg.flags = req.flags;
req_msg.vaddr = req.vaddrin;
req_msg.num = sizeof(pages);
args[0].ptr = (u64) (uintptr_t) &req_msg;
args[0].length = sizeof(req_msg);
pages.addr = buf->phys;
pages.size = buf->size;
args[1].ptr = (u64) (uintptr_t) &pages;
args[1].length = sizeof(pages);
args[2].ptr = (u64) (uintptr_t) &rsp_msg;
args[2].length = sizeof(rsp_msg);
sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_MMAP, 2, 1);
err = fastrpc_internal_invoke(fl, true, FASTRPC_INIT_HANDLE, sc,
&args[0]);
if (err) {
dev_err(dev, "mmap error (len 0x%08llx)\n", buf->size);
goto err_invoke;
}
/* update the buffer to be able to deallocate the memory on the DSP */
buf->raddr = (uintptr_t) rsp_msg.vaddr;
/* let the client know the address to use */
req.vaddrout = rsp_msg.vaddr;
spin_lock(&fl->lock);
list_add_tail(&buf->node, &fl->mmaps);
spin_unlock(&fl->lock);
if (copy_to_user((void __user *)argp, &req, sizeof(req))) {
/* unmap the memory and release the buffer */
req_unmap.vaddrout = buf->raddr;
req_unmap.size = buf->size;
fastrpc_req_munmap_impl(fl, &req_unmap);
return -EFAULT;
}
dev_dbg(dev, "mmap\t\tpt 0x%09lx OK [len 0x%08llx]\n",
buf->raddr, buf->size);
return 0;
err_invoke:
fastrpc_buf_free(buf);
return err;
}
static long fastrpc_device_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
@ -1305,6 +1486,12 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int cmd,
case FASTRPC_IOCTL_ALLOC_DMA_BUFF:
err = fastrpc_dmabuf_alloc(fl, argp);
break;
case FASTRPC_IOCTL_MMAP:
err = fastrpc_req_mmap(fl, argp);
break;
case FASTRPC_IOCTL_MUNMAP:
err = fastrpc_req_munmap(fl, argp);
break;
default:
err = -ENOTTY;
break;
@ -1430,8 +1617,8 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
return -ENOMEM;
data->miscdev.minor = MISC_DYNAMIC_MINOR;
data->miscdev.name = kasprintf(GFP_KERNEL, "fastrpc-%s",
domains[domain_id]);
data->miscdev.name = devm_kasprintf(rdev, GFP_KERNEL, "fastrpc-%s",
domains[domain_id]);
data->miscdev.fops = &fastrpc_fops;
err = misc_register(&data->miscdev);
if (err)

View File

@ -65,6 +65,18 @@ static void cs_put(struct hl_cs *cs)
kref_put(&cs->refcount, cs_do_release);
}
static bool is_cb_patched(struct hl_device *hdev, struct hl_cs_job *job)
{
/*
* Patched CB is created for external queues jobs, and for H/W queues
* jobs if the user CB was allocated by driver and MMU is disabled.
*/
return (job->queue_type == QUEUE_TYPE_EXT ||
(job->queue_type == QUEUE_TYPE_HW &&
job->is_kernel_allocated_cb &&
!hdev->mmu_enable));
}
/*
* cs_parser - parse the user command submission
*
@ -91,11 +103,13 @@ static int cs_parser(struct hl_fpriv *hpriv, struct hl_cs_job *job)
parser.patched_cb = NULL;
parser.user_cb = job->user_cb;
parser.user_cb_size = job->user_cb_size;
parser.ext_queue = job->ext_queue;
parser.queue_type = job->queue_type;
parser.is_kernel_allocated_cb = job->is_kernel_allocated_cb;
job->patched_cb = NULL;
rc = hdev->asic_funcs->cs_parser(hdev, &parser);
if (job->ext_queue) {
if (is_cb_patched(hdev, job)) {
if (!rc) {
job->patched_cb = parser.patched_cb;
job->job_cb_size = parser.patched_cb_size;
@ -124,7 +138,7 @@ static void free_job(struct hl_device *hdev, struct hl_cs_job *job)
{
struct hl_cs *cs = job->cs;
if (job->ext_queue) {
if (is_cb_patched(hdev, job)) {
hl_userptr_delete_list(hdev, &job->userptr_list);
/*
@ -140,6 +154,19 @@ static void free_job(struct hl_device *hdev, struct hl_cs_job *job)
}
}
/* For H/W queue jobs, if a user CB was allocated by driver and MMU is
* enabled, the user CB isn't released in cs_parser() and thus should be
* released here.
*/
if (job->queue_type == QUEUE_TYPE_HW &&
job->is_kernel_allocated_cb && hdev->mmu_enable) {
spin_lock(&job->user_cb->lock);
job->user_cb->cs_cnt--;
spin_unlock(&job->user_cb->lock);
hl_cb_put(job->user_cb);
}
/*
* This is the only place where there can be multiple threads
* modifying the list at the same time
@ -150,7 +177,8 @@ static void free_job(struct hl_device *hdev, struct hl_cs_job *job)
hl_debugfs_remove_job(hdev, job);
if (job->ext_queue)
if (job->queue_type == QUEUE_TYPE_EXT ||
job->queue_type == QUEUE_TYPE_HW)
cs_put(cs);
kfree(job);
@ -387,18 +415,13 @@ static void job_wq_completion(struct work_struct *work)
free_job(hdev, job);
}
static struct hl_cb *validate_queue_index(struct hl_device *hdev,
struct hl_cb_mgr *cb_mgr,
struct hl_cs_chunk *chunk,
bool *ext_queue)
static int validate_queue_index(struct hl_device *hdev,
struct hl_cs_chunk *chunk,
enum hl_queue_type *queue_type,
bool *is_kernel_allocated_cb)
{
struct asic_fixed_properties *asic = &hdev->asic_prop;
struct hw_queue_properties *hw_queue_prop;
u32 cb_handle;
struct hl_cb *cb;
/* Assume external queue */
*ext_queue = true;
hw_queue_prop = &asic->hw_queues_props[chunk->queue_index];
@ -406,20 +429,29 @@ static struct hl_cb *validate_queue_index(struct hl_device *hdev,
(hw_queue_prop->type == QUEUE_TYPE_NA)) {
dev_err(hdev->dev, "Queue index %d is invalid\n",
chunk->queue_index);
return NULL;
return -EINVAL;
}
if (hw_queue_prop->driver_only) {
dev_err(hdev->dev,
"Queue index %d is restricted for the kernel driver\n",
chunk->queue_index);
return NULL;
} else if (hw_queue_prop->type == QUEUE_TYPE_INT) {
*ext_queue = false;
return (struct hl_cb *) (uintptr_t) chunk->cb_handle;
return -EINVAL;
}
/* Retrieve CB object */
*queue_type = hw_queue_prop->type;
*is_kernel_allocated_cb = !!hw_queue_prop->requires_kernel_cb;
return 0;
}
static struct hl_cb *get_cb_from_cs_chunk(struct hl_device *hdev,
struct hl_cb_mgr *cb_mgr,
struct hl_cs_chunk *chunk)
{
struct hl_cb *cb;
u32 cb_handle;
cb_handle = (u32) (chunk->cb_handle >> PAGE_SHIFT);
cb = hl_cb_get(hdev, cb_mgr, cb_handle);
@ -444,7 +476,8 @@ release_cb:
return NULL;
}
struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev, bool ext_queue)
struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev,
enum hl_queue_type queue_type, bool is_kernel_allocated_cb)
{
struct hl_cs_job *job;
@ -452,12 +485,14 @@ struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev, bool ext_queue)
if (!job)
return NULL;
job->ext_queue = ext_queue;
job->queue_type = queue_type;
job->is_kernel_allocated_cb = is_kernel_allocated_cb;
if (job->ext_queue) {
if (is_cb_patched(hdev, job))
INIT_LIST_HEAD(&job->userptr_list);
if (job->queue_type == QUEUE_TYPE_EXT)
INIT_WORK(&job->finish_work, job_wq_completion);
}
return job;
}
@ -470,7 +505,7 @@ static int _hl_cs_ioctl(struct hl_fpriv *hpriv, void __user *chunks,
struct hl_cs_job *job;
struct hl_cs *cs;
struct hl_cb *cb;
bool ext_queue_present = false;
bool int_queues_only = true;
u32 size_to_copy;
int rc, i, parse_cnt;
@ -514,23 +549,33 @@ static int _hl_cs_ioctl(struct hl_fpriv *hpriv, void __user *chunks,
/* Validate ALL the CS chunks before submitting the CS */
for (i = 0, parse_cnt = 0 ; i < num_chunks ; i++, parse_cnt++) {
struct hl_cs_chunk *chunk = &cs_chunk_array[i];
bool ext_queue;
enum hl_queue_type queue_type;
bool is_kernel_allocated_cb;
cb = validate_queue_index(hdev, &hpriv->cb_mgr, chunk,
&ext_queue);
if (ext_queue) {
ext_queue_present = true;
rc = validate_queue_index(hdev, chunk, &queue_type,
&is_kernel_allocated_cb);
if (rc)
goto free_cs_object;
if (is_kernel_allocated_cb) {
cb = get_cb_from_cs_chunk(hdev, &hpriv->cb_mgr, chunk);
if (!cb) {
rc = -EINVAL;
goto free_cs_object;
}
} else {
cb = (struct hl_cb *) (uintptr_t) chunk->cb_handle;
}
job = hl_cs_allocate_job(hdev, ext_queue);
if (queue_type == QUEUE_TYPE_EXT || queue_type == QUEUE_TYPE_HW)
int_queues_only = false;
job = hl_cs_allocate_job(hdev, queue_type,
is_kernel_allocated_cb);
if (!job) {
dev_err(hdev->dev, "Failed to allocate a new job\n");
rc = -ENOMEM;
if (ext_queue)
if (is_kernel_allocated_cb)
goto release_cb;
else
goto free_cs_object;
@ -540,7 +585,7 @@ static int _hl_cs_ioctl(struct hl_fpriv *hpriv, void __user *chunks,
job->cs = cs;
job->user_cb = cb;
job->user_cb_size = chunk->cb_size;
if (job->ext_queue)
if (is_kernel_allocated_cb)
job->job_cb_size = cb->size;
else
job->job_cb_size = chunk->cb_size;
@ -553,10 +598,11 @@ static int _hl_cs_ioctl(struct hl_fpriv *hpriv, void __user *chunks,
/*
* Increment CS reference. When CS reference is 0, CS is
* done and can be signaled to user and free all its resources
* Only increment for JOB on external queues, because only
* for those JOBs we get completion
* Only increment for JOB on external or H/W queues, because
* only for those JOBs we get completion
*/
if (job->ext_queue)
if (job->queue_type == QUEUE_TYPE_EXT ||
job->queue_type == QUEUE_TYPE_HW)
cs_get(cs);
hl_debugfs_add_job(hdev, job);
@ -570,9 +616,9 @@ static int _hl_cs_ioctl(struct hl_fpriv *hpriv, void __user *chunks,
}
}
if (!ext_queue_present) {
if (int_queues_only) {
dev_err(hdev->dev,
"Reject CS %d.%llu because no external queues jobs\n",
"Reject CS %d.%llu because only internal queues jobs are present\n",
cs->ctx->asid, cs->sequence);
rc = -EINVAL;
goto free_cs_object;
@ -580,9 +626,10 @@ static int _hl_cs_ioctl(struct hl_fpriv *hpriv, void __user *chunks,
rc = hl_hw_queue_schedule_cs(cs);
if (rc) {
dev_err(hdev->dev,
"Failed to submit CS %d.%llu to H/W queues, error %d\n",
cs->ctx->asid, cs->sequence, rc);
if (rc != -EAGAIN)
dev_err(hdev->dev,
"Failed to submit CS %d.%llu to H/W queues, error %d\n",
cs->ctx->asid, cs->sequence, rc);
goto free_cs_object;
}

View File

@ -307,45 +307,57 @@ static inline u64 get_hop0_addr(struct hl_ctx *ctx)
(ctx->asid * ctx->hdev->asic_prop.mmu_hop_table_size);
}
static inline u64 get_hop0_pte_addr(struct hl_ctx *ctx, u64 hop_addr,
u64 virt_addr)
static inline u64 get_hopN_pte_addr(struct hl_ctx *ctx, u64 hop_addr,
u64 virt_addr, u64 mask, u64 shift)
{
return hop_addr + ctx->hdev->asic_prop.mmu_pte_size *
((virt_addr & HOP0_MASK) >> HOP0_SHIFT);
((virt_addr & mask) >> shift);
}
static inline u64 get_hop1_pte_addr(struct hl_ctx *ctx, u64 hop_addr,
u64 virt_addr)
static inline u64 get_hop0_pte_addr(struct hl_ctx *ctx,
struct hl_mmu_properties *mmu_specs,
u64 hop_addr, u64 vaddr)
{
return hop_addr + ctx->hdev->asic_prop.mmu_pte_size *
((virt_addr & HOP1_MASK) >> HOP1_SHIFT);
return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop0_mask,
mmu_specs->hop0_shift);
}
static inline u64 get_hop2_pte_addr(struct hl_ctx *ctx, u64 hop_addr,
u64 virt_addr)
static inline u64 get_hop1_pte_addr(struct hl_ctx *ctx,
struct hl_mmu_properties *mmu_specs,
u64 hop_addr, u64 vaddr)
{
return hop_addr + ctx->hdev->asic_prop.mmu_pte_size *
((virt_addr & HOP2_MASK) >> HOP2_SHIFT);
return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop1_mask,
mmu_specs->hop1_shift);
}
static inline u64 get_hop3_pte_addr(struct hl_ctx *ctx, u64 hop_addr,
u64 virt_addr)
static inline u64 get_hop2_pte_addr(struct hl_ctx *ctx,
struct hl_mmu_properties *mmu_specs,
u64 hop_addr, u64 vaddr)
{
return hop_addr + ctx->hdev->asic_prop.mmu_pte_size *
((virt_addr & HOP3_MASK) >> HOP3_SHIFT);
return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop2_mask,
mmu_specs->hop2_shift);
}
static inline u64 get_hop4_pte_addr(struct hl_ctx *ctx, u64 hop_addr,
u64 virt_addr)
static inline u64 get_hop3_pte_addr(struct hl_ctx *ctx,
struct hl_mmu_properties *mmu_specs,
u64 hop_addr, u64 vaddr)
{
return hop_addr + ctx->hdev->asic_prop.mmu_pte_size *
((virt_addr & HOP4_MASK) >> HOP4_SHIFT);
return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop3_mask,
mmu_specs->hop3_shift);
}
static inline u64 get_hop4_pte_addr(struct hl_ctx *ctx,
struct hl_mmu_properties *mmu_specs,
u64 hop_addr, u64 vaddr)
{
return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop4_mask,
mmu_specs->hop4_shift);
}
static inline u64 get_next_hop_addr(u64 curr_pte)
{
if (curr_pte & PAGE_PRESENT_MASK)
return curr_pte & PHYS_ADDR_MASK;
return curr_pte & HOP_PHYS_ADDR_MASK;
else
return ULLONG_MAX;
}
@ -355,7 +367,10 @@ static int mmu_show(struct seq_file *s, void *data)
struct hl_debugfs_entry *entry = s->private;
struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
struct hl_device *hdev = dev_entry->hdev;
struct asic_fixed_properties *prop = &hdev->asic_prop;
struct hl_mmu_properties *mmu_prop;
struct hl_ctx *ctx;
bool is_dram_addr;
u64 hop0_addr = 0, hop0_pte_addr = 0, hop0_pte = 0,
hop1_addr = 0, hop1_pte_addr = 0, hop1_pte = 0,
@ -377,33 +392,39 @@ static int mmu_show(struct seq_file *s, void *data)
return 0;
}
is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size,
prop->va_space_dram_start_address,
prop->va_space_dram_end_address);
mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu;
mutex_lock(&ctx->mmu_lock);
/* the following lookup is copied from unmap() in mmu.c */
hop0_addr = get_hop0_addr(ctx);
hop0_pte_addr = get_hop0_pte_addr(ctx, hop0_addr, virt_addr);
hop0_pte_addr = get_hop0_pte_addr(ctx, mmu_prop, hop0_addr, virt_addr);
hop0_pte = hdev->asic_funcs->read_pte(hdev, hop0_pte_addr);
hop1_addr = get_next_hop_addr(hop0_pte);
if (hop1_addr == ULLONG_MAX)
goto not_mapped;
hop1_pte_addr = get_hop1_pte_addr(ctx, hop1_addr, virt_addr);
hop1_pte_addr = get_hop1_pte_addr(ctx, mmu_prop, hop1_addr, virt_addr);
hop1_pte = hdev->asic_funcs->read_pte(hdev, hop1_pte_addr);
hop2_addr = get_next_hop_addr(hop1_pte);
if (hop2_addr == ULLONG_MAX)
goto not_mapped;
hop2_pte_addr = get_hop2_pte_addr(ctx, hop2_addr, virt_addr);
hop2_pte_addr = get_hop2_pte_addr(ctx, mmu_prop, hop2_addr, virt_addr);
hop2_pte = hdev->asic_funcs->read_pte(hdev, hop2_pte_addr);
hop3_addr = get_next_hop_addr(hop2_pte);
if (hop3_addr == ULLONG_MAX)
goto not_mapped;
hop3_pte_addr = get_hop3_pte_addr(ctx, hop3_addr, virt_addr);
hop3_pte_addr = get_hop3_pte_addr(ctx, mmu_prop, hop3_addr, virt_addr);
hop3_pte = hdev->asic_funcs->read_pte(hdev, hop3_pte_addr);
if (!(hop3_pte & LAST_MASK)) {
@ -412,7 +433,8 @@ static int mmu_show(struct seq_file *s, void *data)
if (hop4_addr == ULLONG_MAX)
goto not_mapped;
hop4_pte_addr = get_hop4_pte_addr(ctx, hop4_addr, virt_addr);
hop4_pte_addr = get_hop4_pte_addr(ctx, mmu_prop, hop4_addr,
virt_addr);
hop4_pte = hdev->asic_funcs->read_pte(hdev, hop4_pte_addr);
if (!(hop4_pte & PAGE_PRESENT_MASK))
goto not_mapped;
@ -506,6 +528,12 @@ static int engines_show(struct seq_file *s, void *data)
struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
struct hl_device *hdev = dev_entry->hdev;
if (atomic_read(&hdev->in_reset)) {
dev_warn_ratelimited(hdev->dev,
"Can't check device idle during reset\n");
return 0;
}
hdev->asic_funcs->is_device_idle(hdev, NULL, s);
return 0;
@ -534,41 +562,50 @@ static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr,
u64 *phys_addr)
{
struct hl_ctx *ctx = hdev->compute_ctx;
struct asic_fixed_properties *prop = &hdev->asic_prop;
struct hl_mmu_properties *mmu_prop;
u64 hop_addr, hop_pte_addr, hop_pte;
u64 offset_mask = HOP4_MASK | OFFSET_MASK;
u64 offset_mask = HOP4_MASK | FLAGS_MASK;
int rc = 0;
bool is_dram_addr;
if (!ctx) {
dev_err(hdev->dev, "no ctx available\n");
return -EINVAL;
}
is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size,
prop->va_space_dram_start_address,
prop->va_space_dram_end_address);
mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu;
mutex_lock(&ctx->mmu_lock);
/* hop 0 */
hop_addr = get_hop0_addr(ctx);
hop_pte_addr = get_hop0_pte_addr(ctx, hop_addr, virt_addr);
hop_pte_addr = get_hop0_pte_addr(ctx, mmu_prop, hop_addr, virt_addr);
hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr);
/* hop 1 */
hop_addr = get_next_hop_addr(hop_pte);
if (hop_addr == ULLONG_MAX)
goto not_mapped;
hop_pte_addr = get_hop1_pte_addr(ctx, hop_addr, virt_addr);
hop_pte_addr = get_hop1_pte_addr(ctx, mmu_prop, hop_addr, virt_addr);
hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr);
/* hop 2 */
hop_addr = get_next_hop_addr(hop_pte);
if (hop_addr == ULLONG_MAX)
goto not_mapped;
hop_pte_addr = get_hop2_pte_addr(ctx, hop_addr, virt_addr);
hop_pte_addr = get_hop2_pte_addr(ctx, mmu_prop, hop_addr, virt_addr);
hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr);
/* hop 3 */
hop_addr = get_next_hop_addr(hop_pte);
if (hop_addr == ULLONG_MAX)
goto not_mapped;
hop_pte_addr = get_hop3_pte_addr(ctx, hop_addr, virt_addr);
hop_pte_addr = get_hop3_pte_addr(ctx, mmu_prop, hop_addr, virt_addr);
hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr);
if (!(hop_pte & LAST_MASK)) {
@ -576,10 +613,11 @@ static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr,
hop_addr = get_next_hop_addr(hop_pte);
if (hop_addr == ULLONG_MAX)
goto not_mapped;
hop_pte_addr = get_hop4_pte_addr(ctx, hop_addr, virt_addr);
hop_pte_addr = get_hop4_pte_addr(ctx, mmu_prop, hop_addr,
virt_addr);
hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr);
offset_mask = OFFSET_MASK;
offset_mask = FLAGS_MASK;
}
if (!(hop_pte & PAGE_PRESENT_MASK))
@ -608,6 +646,11 @@ static ssize_t hl_data_read32(struct file *f, char __user *buf,
u32 val;
ssize_t rc;
if (atomic_read(&hdev->in_reset)) {
dev_warn_ratelimited(hdev->dev, "Can't read during reset\n");
return 0;
}
if (*ppos)
return 0;
@ -637,6 +680,11 @@ static ssize_t hl_data_write32(struct file *f, const char __user *buf,
u32 value;
ssize_t rc;
if (atomic_read(&hdev->in_reset)) {
dev_warn_ratelimited(hdev->dev, "Can't write during reset\n");
return 0;
}
rc = kstrtouint_from_user(buf, count, 16, &value);
if (rc)
return rc;

View File

@ -42,12 +42,10 @@ static void hpriv_release(struct kref *ref)
{
struct hl_fpriv *hpriv;
struct hl_device *hdev;
struct hl_ctx *ctx;
hpriv = container_of(ref, struct hl_fpriv, refcount);
hdev = hpriv->hdev;
ctx = hpriv->ctx;
put_pid(hpriv->taskpid);
@ -889,13 +887,19 @@ again:
/* Go over all the queues, release all CS and their jobs */
hl_cs_rollback_all(hdev);
/* Kill processes here after CS rollback. This is because the process
* can't really exit until all its CSs are done, which is what we
* do in cs rollback
*/
if (from_hard_reset_thread)
if (hard_reset) {
/* Kill processes here after CS rollback. This is because the
* process can't really exit until all its CSs are done, which
* is what we do in cs rollback
*/
device_kill_open_processes(hdev);
/* Flush the Event queue workers to make sure no other thread is
* reading or writing to registers during the reset
*/
flush_workqueue(hdev->eq_wq);
}
/* Release kernel context */
if ((hard_reset) && (hl_ctx_put(hdev->kernel_ctx) == 1))
hdev->kernel_ctx = NULL;

View File

@ -143,10 +143,7 @@ int hl_fw_test_cpu_queue(struct hl_device *hdev)
sizeof(test_pkt), HL_DEVICE_TIMEOUT_USEC, &result);
if (!rc) {
if (result == ARMCP_PACKET_FENCE_VAL)
dev_info(hdev->dev,
"queue test on CPU queue succeeded\n");
else
if (result != ARMCP_PACKET_FENCE_VAL)
dev_err(hdev->dev,
"CPU queue test failed (0x%08lX)\n", result);
} else {

View File

@ -72,6 +72,9 @@
*
*/
#define GOYA_UBOOT_FW_FILE "habanalabs/goya/goya-u-boot.bin"
#define GOYA_LINUX_FW_FILE "habanalabs/goya/goya-fit.itb"
#define GOYA_MMU_REGS_NUM 63
#define GOYA_DMA_POOL_BLK_SIZE 0x100 /* 256 bytes */
@ -337,17 +340,20 @@ void goya_get_fixed_properties(struct hl_device *hdev)
for (i = 0 ; i < NUMBER_OF_EXT_HW_QUEUES ; i++) {
prop->hw_queues_props[i].type = QUEUE_TYPE_EXT;
prop->hw_queues_props[i].driver_only = 0;
prop->hw_queues_props[i].requires_kernel_cb = 1;
}
for (; i < NUMBER_OF_EXT_HW_QUEUES + NUMBER_OF_CPU_HW_QUEUES ; i++) {
prop->hw_queues_props[i].type = QUEUE_TYPE_CPU;
prop->hw_queues_props[i].driver_only = 1;
prop->hw_queues_props[i].requires_kernel_cb = 0;
}
for (; i < NUMBER_OF_EXT_HW_QUEUES + NUMBER_OF_CPU_HW_QUEUES +
NUMBER_OF_INT_HW_QUEUES; i++) {
prop->hw_queues_props[i].type = QUEUE_TYPE_INT;
prop->hw_queues_props[i].driver_only = 0;
prop->hw_queues_props[i].requires_kernel_cb = 0;
}
for (; i < HL_MAX_QUEUES; i++)
@ -377,6 +383,23 @@ void goya_get_fixed_properties(struct hl_device *hdev)
prop->mmu_hop0_tables_total_size = HOP0_TABLES_TOTAL_SIZE;
prop->dram_page_size = PAGE_SIZE_2MB;
prop->dmmu.hop0_shift = HOP0_SHIFT;
prop->dmmu.hop1_shift = HOP1_SHIFT;
prop->dmmu.hop2_shift = HOP2_SHIFT;
prop->dmmu.hop3_shift = HOP3_SHIFT;
prop->dmmu.hop4_shift = HOP4_SHIFT;
prop->dmmu.hop0_mask = HOP0_MASK;
prop->dmmu.hop1_mask = HOP1_MASK;
prop->dmmu.hop2_mask = HOP2_MASK;
prop->dmmu.hop3_mask = HOP3_MASK;
prop->dmmu.hop4_mask = HOP4_MASK;
prop->dmmu.huge_page_size = PAGE_SIZE_2MB;
/* No difference between PMMU and DMMU except of page size */
memcpy(&prop->pmmu, &prop->dmmu, sizeof(prop->dmmu));
prop->dmmu.page_size = PAGE_SIZE_2MB;
prop->pmmu.page_size = PAGE_SIZE_4KB;
prop->va_space_host_start_address = VA_HOST_SPACE_START;
prop->va_space_host_end_address = VA_HOST_SPACE_END;
prop->va_space_dram_start_address = VA_DDR_SPACE_START;
@ -393,6 +416,9 @@ void goya_get_fixed_properties(struct hl_device *hdev)
prop->tpc_enabled_mask = TPC_ENABLED_MASK;
prop->pcie_dbi_base_address = mmPCIE_DBI_BASE;
prop->pcie_aux_dbi_reg_addr = CFG_BASE + mmPCIE_AUX_DBI;
strncpy(prop->armcp_info.card_name, GOYA_DEFAULT_CARD_NAME,
CARD_NAME_MAX_LEN);
}
/*
@ -1454,6 +1480,9 @@ static void goya_init_golden_registers(struct hl_device *hdev)
1 << TPC0_NRTR_SCRAMB_EN_VAL_SHIFT);
WREG32(mmTPC0_NRTR_NON_LIN_SCRAMB + offset,
1 << TPC0_NRTR_NON_LIN_SCRAMB_EN_SHIFT);
WREG32_FIELD(TPC0_CFG_MSS_CONFIG, offset,
ICACHE_FETCH_LINE_NUM, 2);
}
WREG32(mmDMA_NRTR_SCRAMB_EN, 1 << DMA_NRTR_SCRAMB_EN_VAL_SHIFT);
@ -1533,7 +1562,6 @@ static void goya_init_mme_cmdq(struct hl_device *hdev)
u32 mtr_base_lo, mtr_base_hi;
u32 so_base_lo, so_base_hi;
u32 gic_base_lo, gic_base_hi;
u64 qman_base_addr;
mtr_base_lo = lower_32_bits(CFG_BASE + mmSYNC_MNGR_MON_PAY_ADDRL_0);
mtr_base_hi = upper_32_bits(CFG_BASE + mmSYNC_MNGR_MON_PAY_ADDRL_0);
@ -1545,9 +1573,6 @@ static void goya_init_mme_cmdq(struct hl_device *hdev)
gic_base_hi =
upper_32_bits(CFG_BASE + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR);
qman_base_addr = hdev->asic_prop.sram_base_address +
MME_QMAN_BASE_OFFSET;
WREG32(mmMME_CMDQ_CP_MSG_BASE0_ADDR_LO, mtr_base_lo);
WREG32(mmMME_CMDQ_CP_MSG_BASE0_ADDR_HI, mtr_base_hi);
WREG32(mmMME_CMDQ_CP_MSG_BASE1_ADDR_LO, so_base_lo);
@ -2141,13 +2166,11 @@ static void goya_halt_engines(struct hl_device *hdev, bool hard_reset)
*/
static int goya_push_uboot_to_device(struct hl_device *hdev)
{
char fw_name[200];
void __iomem *dst;
snprintf(fw_name, sizeof(fw_name), "habanalabs/goya/goya-u-boot.bin");
dst = hdev->pcie_bar[SRAM_CFG_BAR_ID] + UBOOT_FW_OFFSET;
return hl_fw_push_fw_to_device(hdev, fw_name, dst);
return hl_fw_push_fw_to_device(hdev, GOYA_UBOOT_FW_FILE, dst);
}
/*
@ -2160,13 +2183,11 @@ static int goya_push_uboot_to_device(struct hl_device *hdev)
*/
static int goya_push_linux_to_device(struct hl_device *hdev)
{
char fw_name[200];
void __iomem *dst;
snprintf(fw_name, sizeof(fw_name), "habanalabs/goya/goya-fit.itb");
dst = hdev->pcie_bar[DDR_BAR_ID] + LINUX_FW_OFFSET;
return hl_fw_push_fw_to_device(hdev, fw_name, dst);
return hl_fw_push_fw_to_device(hdev, GOYA_LINUX_FW_FILE, dst);
}
static int goya_pldm_init_cpu(struct hl_device *hdev)
@ -2291,6 +2312,10 @@ static int goya_init_cpu(struct hl_device *hdev, u32 cpu_timeout)
10000,
cpu_timeout);
/* Read U-Boot version now in case we will later fail */
goya_read_device_fw_version(hdev, FW_COMP_UBOOT);
goya_read_device_fw_version(hdev, FW_COMP_PREBOOT);
if (rc) {
dev_err(hdev->dev, "Error in ARM u-boot!");
switch (status) {
@ -2328,6 +2353,11 @@ static int goya_init_cpu(struct hl_device *hdev, u32 cpu_timeout)
"ARM status %d - u-boot stopped by user\n",
status);
break;
case CPU_BOOT_STATUS_TS_INIT_FAIL:
dev_err(hdev->dev,
"ARM status %d - Thermal Sensor initialization failed\n",
status);
break;
default:
dev_err(hdev->dev,
"ARM status %d - Invalid status code\n",
@ -2337,10 +2367,6 @@ static int goya_init_cpu(struct hl_device *hdev, u32 cpu_timeout)
return -EIO;
}
/* Read U-Boot version now in case we will later fail */
goya_read_device_fw_version(hdev, FW_COMP_UBOOT);
goya_read_device_fw_version(hdev, FW_COMP_PREBOOT);
if (!hdev->fw_loading) {
dev_info(hdev->dev, "Skip loading FW\n");
goto out;
@ -2453,7 +2479,8 @@ int goya_mmu_init(struct hl_device *hdev)
WREG32_AND(mmSTLB_STLB_FEATURE_EN,
(~STLB_STLB_FEATURE_EN_FOLLOWER_EN_MASK));
hdev->asic_funcs->mmu_invalidate_cache(hdev, true);
hdev->asic_funcs->mmu_invalidate_cache(hdev, true,
VM_TYPE_USERPTR | VM_TYPE_PHYS_PACK);
WREG32(mmMMU_MMU_ENABLE, 1);
WREG32(mmMMU_SPI_MASK, 0xF);
@ -2978,9 +3005,6 @@ int goya_test_queue(struct hl_device *hdev, u32 hw_queue_id)
"H/W queue %d test failed (scratch(0x%08llX) == 0x%08X)\n",
hw_queue_id, (unsigned long long) fence_dma_addr, tmp);
rc = -EIO;
} else {
dev_info(hdev->dev, "queue test on H/W queue %d succeeded\n",
hw_queue_id);
}
free_pkt:
@ -3925,7 +3949,7 @@ static int goya_parse_cb_no_ext_queue(struct hl_device *hdev,
return 0;
dev_err(hdev->dev,
"Internal CB address %px + 0x%x is not in SRAM nor in DRAM\n",
"Internal CB address 0x%px + 0x%x is not in SRAM nor in DRAM\n",
parser->user_cb, parser->user_cb_size);
return -EFAULT;
@ -3935,7 +3959,7 @@ int goya_cs_parser(struct hl_device *hdev, struct hl_cs_parser *parser)
{
struct goya_device *goya = hdev->asic_specific;
if (!parser->ext_queue)
if (parser->queue_type == QUEUE_TYPE_INT)
return goya_parse_cb_no_ext_queue(hdev, parser);
if (goya->hw_cap_initialized & HW_CAP_MMU)
@ -4606,7 +4630,7 @@ static int goya_memset_device_memory(struct hl_device *hdev, u64 addr, u64 size,
lin_dma_pkt++;
} while (--lin_dma_pkts_cnt);
job = hl_cs_allocate_job(hdev, true);
job = hl_cs_allocate_job(hdev, QUEUE_TYPE_EXT, true);
if (!job) {
dev_err(hdev->dev, "Failed to allocate a new job\n");
rc = -ENOMEM;
@ -4835,13 +4859,15 @@ static void goya_mmu_prepare(struct hl_device *hdev, u32 asid)
goya_mmu_prepare_reg(hdev, goya_mmu_regs[i], asid);
}
static void goya_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard)
static void goya_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard,
u32 flags)
{
struct goya_device *goya = hdev->asic_specific;
u32 status, timeout_usec;
int rc;
if (!(goya->hw_cap_initialized & HW_CAP_MMU))
if (!(goya->hw_cap_initialized & HW_CAP_MMU) ||
hdev->hard_reset_pending)
return;
/* no need in L1 only invalidation in Goya */
@ -4880,7 +4906,8 @@ static void goya_mmu_invalidate_cache_range(struct hl_device *hdev,
u32 status, timeout_usec, inv_data, pi;
int rc;
if (!(goya->hw_cap_initialized & HW_CAP_MMU))
if (!(goya->hw_cap_initialized & HW_CAP_MMU) ||
hdev->hard_reset_pending)
return;
/* no need in L1 only invalidation in Goya */
@ -5137,7 +5164,8 @@ static const struct hl_asic_funcs goya_funcs = {
.init_iatu = goya_init_iatu,
.rreg = hl_rreg,
.wreg = hl_wreg,
.halt_coresight = goya_halt_coresight
.halt_coresight = goya_halt_coresight,
.get_clk_rate = goya_get_clk_rate
};
/*

View File

@ -233,4 +233,6 @@ void goya_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size,
void *vaddr);
void goya_mmu_remove_device_cpu_mappings(struct hl_device *hdev);
int goya_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk);
#endif /* GOYAP_H_ */

View File

@ -8,6 +8,7 @@
#include "goyaP.h"
#include "include/goya/goya_coresight.h"
#include "include/goya/asic_reg/goya_regs.h"
#include "include/goya/asic_reg/goya_masks.h"
#include <uapi/misc/habanalabs.h>
@ -377,33 +378,32 @@ static int goya_config_etr(struct hl_device *hdev,
struct hl_debug_params *params)
{
struct hl_debug_params_etr *input;
u64 base_reg = mmPSOC_ETR_BASE - CFG_BASE;
u32 val;
int rc;
WREG32(base_reg + 0xFB0, CORESIGHT_UNLOCK);
WREG32(mmPSOC_ETR_LAR, CORESIGHT_UNLOCK);
val = RREG32(base_reg + 0x304);
val = RREG32(mmPSOC_ETR_FFCR);
val |= 0x1000;
WREG32(base_reg + 0x304, val);
WREG32(mmPSOC_ETR_FFCR, val);
val |= 0x40;
WREG32(base_reg + 0x304, val);
WREG32(mmPSOC_ETR_FFCR, val);
rc = goya_coresight_timeout(hdev, base_reg + 0x304, 6, false);
rc = goya_coresight_timeout(hdev, mmPSOC_ETR_FFCR, 6, false);
if (rc) {
dev_err(hdev->dev, "Failed to %s ETR on timeout, error %d\n",
params->enable ? "enable" : "disable", rc);
return rc;
}
rc = goya_coresight_timeout(hdev, base_reg + 0xC, 2, true);
rc = goya_coresight_timeout(hdev, mmPSOC_ETR_STS, 2, true);
if (rc) {
dev_err(hdev->dev, "Failed to %s ETR on timeout, error %d\n",
params->enable ? "enable" : "disable", rc);
return rc;
}
WREG32(base_reg + 0x20, 0);
WREG32(mmPSOC_ETR_CTL, 0);
if (params->enable) {
input = params->input;
@ -423,25 +423,26 @@ static int goya_config_etr(struct hl_device *hdev,
return -EINVAL;
}
WREG32(base_reg + 0x34, 0x3FFC);
WREG32(base_reg + 0x4, input->buffer_size);
WREG32(base_reg + 0x28, input->sink_mode);
WREG32(base_reg + 0x110, 0x700);
WREG32(base_reg + 0x118,
WREG32(mmPSOC_ETR_BUFWM, 0x3FFC);
WREG32(mmPSOC_ETR_RSZ, input->buffer_size);
WREG32(mmPSOC_ETR_MODE, input->sink_mode);
WREG32(mmPSOC_ETR_AXICTL,
0x700 | PSOC_ETR_AXICTL_PROTCTRLBIT1_SHIFT);
WREG32(mmPSOC_ETR_DBALO,
lower_32_bits(input->buffer_address));
WREG32(base_reg + 0x11C,
WREG32(mmPSOC_ETR_DBAHI,
upper_32_bits(input->buffer_address));
WREG32(base_reg + 0x304, 3);
WREG32(base_reg + 0x308, 0xA);
WREG32(base_reg + 0x20, 1);
WREG32(mmPSOC_ETR_FFCR, 3);
WREG32(mmPSOC_ETR_PSCR, 0xA);
WREG32(mmPSOC_ETR_CTL, 1);
} else {
WREG32(base_reg + 0x34, 0);
WREG32(base_reg + 0x4, 0x400);
WREG32(base_reg + 0x118, 0);
WREG32(base_reg + 0x11C, 0);
WREG32(base_reg + 0x308, 0);
WREG32(base_reg + 0x28, 0);
WREG32(base_reg + 0x304, 0);
WREG32(mmPSOC_ETR_BUFWM, 0);
WREG32(mmPSOC_ETR_RSZ, 0x400);
WREG32(mmPSOC_ETR_DBALO, 0);
WREG32(mmPSOC_ETR_DBAHI, 0);
WREG32(mmPSOC_ETR_PSCR, 0);
WREG32(mmPSOC_ETR_MODE, 0);
WREG32(mmPSOC_ETR_FFCR, 0);
if (params->output_size >= sizeof(u64)) {
u32 rwp, rwphi;
@ -451,8 +452,8 @@ static int goya_config_etr(struct hl_device *hdev,
* the buffer is set in the RWP register (lower 32
* bits), and in the RWPHI register (upper 8 bits).
*/
rwp = RREG32(base_reg + 0x18);
rwphi = RREG32(base_reg + 0x3c) & 0xff;
rwp = RREG32(mmPSOC_ETR_RWP);
rwphi = RREG32(mmPSOC_ETR_RWPHI) & 0xff;
*(u64 *) params->output = ((u64) rwphi << 32) | rwp;
}
}

View File

@ -32,6 +32,37 @@ void goya_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq)
}
}
int goya_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk)
{
long value;
if (hl_device_disabled_or_in_reset(hdev))
return -ENODEV;
value = hl_get_frequency(hdev, MME_PLL, false);
if (value < 0) {
dev_err(hdev->dev, "Failed to retrieve device max clock %ld\n",
value);
return value;
}
*max_clk = (value / 1000 / 1000);
value = hl_get_frequency(hdev, MME_PLL, true);
if (value < 0) {
dev_err(hdev->dev,
"Failed to retrieve device current clock %ld\n",
value);
return value;
}
*cur_clk = (value / 1000 / 1000);
return 0;
}
static ssize_t mme_clk_show(struct device *dev, struct device_attribute *attr,
char *buf)
{

View File

@ -40,8 +40,6 @@
#define HL_MAX_QUEUES 128
#define HL_MAX_JOBS_PER_CS 64
/* MUST BE POWER OF 2 and larger than 1 */
#define HL_MAX_PENDING_CS 64
@ -85,12 +83,15 @@ struct hl_fpriv;
* @QUEUE_TYPE_INT: internal queue that performs DMA inside the device's
* memories and/or operates the compute engines.
* @QUEUE_TYPE_CPU: S/W queue for communication with the device's CPU.
* @QUEUE_TYPE_HW: queue of DMA and compute engines jobs, for which completion
* notifications are sent by H/W.
*/
enum hl_queue_type {
QUEUE_TYPE_NA,
QUEUE_TYPE_EXT,
QUEUE_TYPE_INT,
QUEUE_TYPE_CPU
QUEUE_TYPE_CPU,
QUEUE_TYPE_HW
};
/**
@ -98,10 +99,13 @@ enum hl_queue_type {
* @type: queue type.
* @driver_only: true if only the driver is allowed to send a job to this queue,
* false otherwise.
* @requires_kernel_cb: true if a CB handle must be provided for jobs on this
* queue, false otherwise (a CB address must be provided).
*/
struct hw_queue_properties {
enum hl_queue_type type;
u8 driver_only;
u8 requires_kernel_cb;
};
/**
@ -110,8 +114,8 @@ struct hw_queue_properties {
* @VM_TYPE_PHYS_PACK: mapping of DRAM memory to device virtual address.
*/
enum vm_type_t {
VM_TYPE_USERPTR,
VM_TYPE_PHYS_PACK
VM_TYPE_USERPTR = 0x1,
VM_TYPE_PHYS_PACK = 0x2
};
/**
@ -126,6 +130,36 @@ enum hl_device_hw_state {
HL_DEVICE_HW_STATE_DIRTY
};
/**
* struct hl_mmu_properties - ASIC specific MMU address translation properties.
* @hop0_shift: shift of hop 0 mask.
* @hop1_shift: shift of hop 1 mask.
* @hop2_shift: shift of hop 2 mask.
* @hop3_shift: shift of hop 3 mask.
* @hop4_shift: shift of hop 4 mask.
* @hop0_mask: mask to get the PTE address in hop 0.
* @hop1_mask: mask to get the PTE address in hop 1.
* @hop2_mask: mask to get the PTE address in hop 2.
* @hop3_mask: mask to get the PTE address in hop 3.
* @hop4_mask: mask to get the PTE address in hop 4.
* @page_size: default page size used to allocate memory.
* @huge_page_size: page size used to allocate memory with huge pages.
*/
struct hl_mmu_properties {
u64 hop0_shift;
u64 hop1_shift;
u64 hop2_shift;
u64 hop3_shift;
u64 hop4_shift;
u64 hop0_mask;
u64 hop1_mask;
u64 hop2_mask;
u64 hop3_mask;
u64 hop4_mask;
u32 page_size;
u32 huge_page_size;
};
/**
* struct asic_fixed_properties - ASIC specific immutable properties.
* @hw_queues_props: H/W queues properties.
@ -133,6 +167,8 @@ enum hl_device_hw_state {
* available sensors.
* @uboot_ver: F/W U-boot version.
* @preboot_ver: F/W Preboot version.
* @dmmu: DRAM MMU address translation properties.
* @pmmu: PCI (host) MMU address translation properties.
* @sram_base_address: SRAM physical start address.
* @sram_end_address: SRAM physical end address.
* @sram_user_base_address - SRAM physical start address for user access.
@ -169,53 +205,55 @@ enum hl_device_hw_state {
* @psoc_pci_pll_nf: PCI PLL NF value.
* @psoc_pci_pll_od: PCI PLL OD value.
* @psoc_pci_pll_div_factor: PCI PLL DIV FACTOR 1 value.
* @completion_queues_count: number of completion queues.
* @high_pll: high PLL frequency used by the device.
* @cb_pool_cb_cnt: number of CBs in the CB pool.
* @cb_pool_cb_size: size of each CB in the CB pool.
* @tpc_enabled_mask: which TPCs are enabled.
* @completion_queues_count: number of completion queues.
*/
struct asic_fixed_properties {
struct hw_queue_properties hw_queues_props[HL_MAX_QUEUES];
struct armcp_info armcp_info;
char uboot_ver[VERSION_MAX_LEN];
char preboot_ver[VERSION_MAX_LEN];
u64 sram_base_address;
u64 sram_end_address;
u64 sram_user_base_address;
u64 dram_base_address;
u64 dram_end_address;
u64 dram_user_base_address;
u64 dram_size;
u64 dram_pci_bar_size;
u64 max_power_default;
u64 va_space_host_start_address;
u64 va_space_host_end_address;
u64 va_space_dram_start_address;
u64 va_space_dram_end_address;
u64 dram_size_for_default_page_mapping;
u64 pcie_dbi_base_address;
u64 pcie_aux_dbi_reg_addr;
u64 mmu_pgt_addr;
u64 mmu_dram_default_page_addr;
u32 mmu_pgt_size;
u32 mmu_pte_size;
u32 mmu_hop_table_size;
u32 mmu_hop0_tables_total_size;
u32 dram_page_size;
u32 cfg_size;
u32 sram_size;
u32 max_asid;
u32 num_of_events;
u32 psoc_pci_pll_nr;
u32 psoc_pci_pll_nf;
u32 psoc_pci_pll_od;
u32 psoc_pci_pll_div_factor;
u32 high_pll;
u32 cb_pool_cb_cnt;
u32 cb_pool_cb_size;
u8 completion_queues_count;
u8 tpc_enabled_mask;
struct armcp_info armcp_info;
char uboot_ver[VERSION_MAX_LEN];
char preboot_ver[VERSION_MAX_LEN];
struct hl_mmu_properties dmmu;
struct hl_mmu_properties pmmu;
u64 sram_base_address;
u64 sram_end_address;
u64 sram_user_base_address;
u64 dram_base_address;
u64 dram_end_address;
u64 dram_user_base_address;
u64 dram_size;
u64 dram_pci_bar_size;
u64 max_power_default;
u64 va_space_host_start_address;
u64 va_space_host_end_address;
u64 va_space_dram_start_address;
u64 va_space_dram_end_address;
u64 dram_size_for_default_page_mapping;
u64 pcie_dbi_base_address;
u64 pcie_aux_dbi_reg_addr;
u64 mmu_pgt_addr;
u64 mmu_dram_default_page_addr;
u32 mmu_pgt_size;
u32 mmu_pte_size;
u32 mmu_hop_table_size;
u32 mmu_hop0_tables_total_size;
u32 dram_page_size;
u32 cfg_size;
u32 sram_size;
u32 max_asid;
u32 num_of_events;
u32 psoc_pci_pll_nr;
u32 psoc_pci_pll_nf;
u32 psoc_pci_pll_od;
u32 psoc_pci_pll_div_factor;
u32 high_pll;
u32 cb_pool_cb_cnt;
u32 cb_pool_cb_size;
u8 tpc_enabled_mask;
u8 completion_queues_count;
};
/**
@ -236,8 +274,6 @@ struct hl_dma_fence {
* Command Buffers
*/
#define HL_MAX_CB_SIZE 0x200000 /* 2MB */
/**
* struct hl_cb_mgr - describes a Command Buffer Manager.
* @cb_lock: protects cb_handles.
@ -481,8 +517,8 @@ enum hl_pll_frequency {
* @get_events_stat: retrieve event queue entries histogram.
* @read_pte: read MMU page table entry from DRAM.
* @write_pte: write MMU page table entry to DRAM.
* @mmu_invalidate_cache: flush MMU STLB cache, either with soft (L1 only) or
* hard (L0 & L1) flush.
* @mmu_invalidate_cache: flush MMU STLB host/DRAM cache, either with soft
* (L1 only) or hard (L0 & L1) flush.
* @mmu_invalidate_cache_range: flush specific MMU STLB cache lines with
* ASID-VA-size mask.
* @send_heartbeat: send is-alive packet to ArmCP and verify response.
@ -502,6 +538,7 @@ enum hl_pll_frequency {
* @rreg: Read a register. Needed for simulator support.
* @wreg: Write a register. Needed for simulator support.
* @halt_coresight: stop the ETF and ETR traces.
* @get_clk_rate: Retrieve the ASIC current and maximum clock rate in MHz
*/
struct hl_asic_funcs {
int (*early_init)(struct hl_device *hdev);
@ -562,7 +599,8 @@ struct hl_asic_funcs {
u32 *size);
u64 (*read_pte)(struct hl_device *hdev, u64 addr);
void (*write_pte)(struct hl_device *hdev, u64 addr, u64 val);
void (*mmu_invalidate_cache)(struct hl_device *hdev, bool is_hard);
void (*mmu_invalidate_cache)(struct hl_device *hdev, bool is_hard,
u32 flags);
void (*mmu_invalidate_cache_range)(struct hl_device *hdev, bool is_hard,
u32 asid, u64 va, u64 size);
int (*send_heartbeat)(struct hl_device *hdev);
@ -584,6 +622,7 @@ struct hl_asic_funcs {
u32 (*rreg)(struct hl_device *hdev, u32 reg);
void (*wreg)(struct hl_device *hdev, u32 reg, u32 val);
void (*halt_coresight)(struct hl_device *hdev);
int (*get_clk_rate)(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk);
};
@ -688,7 +727,7 @@ struct hl_ctx_mgr {
* @sgt: pointer to the scatter-gather table that holds the pages.
* @dir: for DMA unmapping, the direction must be supplied, so save it.
* @debugfs_list: node in debugfs list of command submissions.
* @addr: user-space virtual pointer to the start of the memory area.
* @addr: user-space virtual address of the start of the memory area.
* @size: size of the memory area to pin & map.
* @dma_mapped: true if the SG was mapped to DMA addresses, false otherwise.
*/
@ -752,11 +791,14 @@ struct hl_cs {
* @userptr_list: linked-list of userptr mappings that belong to this job and
* wait for completion.
* @debugfs_list: node in debugfs list of command submission jobs.
* @queue_type: the type of the H/W queue this job is submitted to.
* @id: the id of this job inside a CS.
* @hw_queue_id: the id of the H/W queue this job is submitted to.
* @user_cb_size: the actual size of the CB we got from the user.
* @job_cb_size: the actual size of the CB that we put on the queue.
* @ext_queue: whether the job is for external queue or internal queue.
* @is_kernel_allocated_cb: true if the CB handle we got from the user holds a
* handle to a kernel-allocated CB object, false
* otherwise (SRAM/DRAM/host address).
*/
struct hl_cs_job {
struct list_head cs_node;
@ -766,39 +808,44 @@ struct hl_cs_job {
struct work_struct finish_work;
struct list_head userptr_list;
struct list_head debugfs_list;
enum hl_queue_type queue_type;
u32 id;
u32 hw_queue_id;
u32 user_cb_size;
u32 job_cb_size;
u8 ext_queue;
u8 is_kernel_allocated_cb;
};
/**
* struct hl_cs_parser - command submission paerser properties.
* struct hl_cs_parser - command submission parser properties.
* @user_cb: the CB we got from the user.
* @patched_cb: in case of patching, this is internal CB which is submitted on
* the queue instead of the CB we got from the IOCTL.
* @job_userptr_list: linked-list of userptr mappings that belong to the related
* job and wait for completion.
* @cs_sequence: the sequence number of the related CS.
* @queue_type: the type of the H/W queue this job is submitted to.
* @ctx_id: the ID of the context the related CS belongs to.
* @hw_queue_id: the id of the H/W queue this job is submitted to.
* @user_cb_size: the actual size of the CB we got from the user.
* @patched_cb_size: the size of the CB after parsing.
* @ext_queue: whether the job is for external queue or internal queue.
* @job_id: the id of the related job inside the related CS.
* @is_kernel_allocated_cb: true if the CB handle we got from the user holds a
* handle to a kernel-allocated CB object, false
* otherwise (SRAM/DRAM/host address).
*/
struct hl_cs_parser {
struct hl_cb *user_cb;
struct hl_cb *patched_cb;
struct list_head *job_userptr_list;
u64 cs_sequence;
enum hl_queue_type queue_type;
u32 ctx_id;
u32 hw_queue_id;
u32 user_cb_size;
u32 patched_cb_size;
u8 ext_queue;
u8 job_id;
u8 is_kernel_allocated_cb;
};
@ -1048,9 +1095,10 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val);
#define REG_FIELD_SHIFT(reg, field) reg##_##field##_SHIFT
#define REG_FIELD_MASK(reg, field) reg##_##field##_MASK
#define WREG32_FIELD(reg, field, val) \
WREG32(mm##reg, (RREG32(mm##reg) & ~REG_FIELD_MASK(reg, field)) | \
(val) << REG_FIELD_SHIFT(reg, field))
#define WREG32_FIELD(reg, offset, field, val) \
WREG32(mm##reg + offset, (RREG32(mm##reg + offset) & \
~REG_FIELD_MASK(reg, field)) | \
(val) << REG_FIELD_SHIFT(reg, field))
/* Timeout should be longer when working with simulator but cap the
* increased timeout to some maximum
@ -1501,7 +1549,8 @@ int hl_cb_pool_init(struct hl_device *hdev);
int hl_cb_pool_fini(struct hl_device *hdev);
void hl_cs_rollback_all(struct hl_device *hdev);
struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev, bool ext_queue);
struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev,
enum hl_queue_type queue_type, bool is_kernel_allocated_cb);
void goya_set_asic_funcs(struct hl_device *hdev);
@ -1513,7 +1562,7 @@ void hl_vm_fini(struct hl_device *hdev);
int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size,
struct hl_userptr *userptr);
int hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr);
void hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr);
void hl_userptr_delete_list(struct hl_device *hdev,
struct list_head *userptr_list);
bool hl_userptr_is_pinned(struct hl_device *hdev, u64 addr, u32 size,

View File

@ -60,11 +60,16 @@ static int hw_ip_info(struct hl_device *hdev, struct hl_info_args *args)
hw_ip.tpc_enabled_mask = prop->tpc_enabled_mask;
hw_ip.sram_size = prop->sram_size - sram_kmd_size;
hw_ip.dram_size = prop->dram_size - dram_kmd_size;
if (hw_ip.dram_size > 0)
if (hw_ip.dram_size > PAGE_SIZE)
hw_ip.dram_enabled = 1;
hw_ip.num_of_events = prop->num_of_events;
memcpy(hw_ip.armcp_version,
prop->armcp_info.armcp_version, VERSION_MAX_LEN);
memcpy(hw_ip.armcp_version, prop->armcp_info.armcp_version,
min(VERSION_MAX_LEN, HL_INFO_VERSION_MAX_LEN));
memcpy(hw_ip.card_name, prop->armcp_info.card_name,
min(CARD_NAME_MAX_LEN, HL_INFO_CARD_NAME_MAX_LEN));
hw_ip.armcp_cpld_version = le32_to_cpu(prop->armcp_info.cpld_version);
hw_ip.psoc_pci_pll_nr = prop->psoc_pci_pll_nr;
hw_ip.psoc_pci_pll_nf = prop->psoc_pci_pll_nf;
@ -179,17 +184,14 @@ static int debug_coresight(struct hl_device *hdev, struct hl_debug_args *args)
goto out;
}
if (output) {
if (copy_to_user((void __user *) (uintptr_t) args->output_ptr,
output,
args->output_size)) {
dev_err(hdev->dev,
"copy to user failed in debug ioctl\n");
rc = -EFAULT;
goto out;
}
if (output && copy_to_user((void __user *) (uintptr_t) args->output_ptr,
output, args->output_size)) {
dev_err(hdev->dev, "copy to user failed in debug ioctl\n");
rc = -EFAULT;
goto out;
}
out:
kfree(params);
kfree(output);
@ -221,6 +223,41 @@ static int device_utilization(struct hl_device *hdev, struct hl_info_args *args)
min((size_t) max_size, sizeof(device_util))) ? -EFAULT : 0;
}
static int get_clk_rate(struct hl_device *hdev, struct hl_info_args *args)
{
struct hl_info_clk_rate clk_rate = {0};
u32 max_size = args->return_size;
void __user *out = (void __user *) (uintptr_t) args->return_pointer;
int rc;
if ((!max_size) || (!out))
return -EINVAL;
rc = hdev->asic_funcs->get_clk_rate(hdev, &clk_rate.cur_clk_rate_mhz,
&clk_rate.max_clk_rate_mhz);
if (rc)
return rc;
return copy_to_user(out, &clk_rate,
min((size_t) max_size, sizeof(clk_rate))) ? -EFAULT : 0;
}
static int get_reset_count(struct hl_device *hdev, struct hl_info_args *args)
{
struct hl_info_reset_count reset_count = {0};
u32 max_size = args->return_size;
void __user *out = (void __user *) (uintptr_t) args->return_pointer;
if ((!max_size) || (!out))
return -EINVAL;
reset_count.hard_reset_cnt = hdev->hard_reset_cnt;
reset_count.soft_reset_cnt = hdev->soft_reset_cnt;
return copy_to_user(out, &reset_count,
min((size_t) max_size, sizeof(reset_count))) ? -EFAULT : 0;
}
static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data,
struct device *dev)
{
@ -239,6 +276,9 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data,
case HL_INFO_DEVICE_STATUS:
return device_status_info(hdev, args);
case HL_INFO_RESET_COUNT:
return get_reset_count(hdev, args);
default:
break;
}
@ -271,6 +311,10 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data,
rc = hw_events_info(hdev, true, args);
break;
case HL_INFO_CLK_RATE:
rc = get_clk_rate(hdev, args);
break;
default:
dev_err(dev, "Invalid request %d\n", args->op);
rc = -ENOTTY;
@ -406,9 +450,8 @@ static long _hl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg,
retcode = func(hpriv, kdata);
if (cmd & IOC_OUT)
if (copy_to_user((void __user *)arg, kdata, usize))
retcode = -EFAULT;
if ((cmd & IOC_OUT) && copy_to_user((void __user *)arg, kdata, usize))
retcode = -EFAULT;
out_err:
if (retcode)

View File

@ -58,8 +58,8 @@ out:
}
/*
* ext_queue_submit_bd - Submit a buffer descriptor to an external queue
*
* ext_and_hw_queue_submit_bd() - Submit a buffer descriptor to an external or a
* H/W queue.
* @hdev: pointer to habanalabs device structure
* @q: pointer to habanalabs queue structure
* @ctl: BD's control word
@ -73,8 +73,8 @@ out:
* This function must be called when the scheduler mutex is taken
*
*/
static void ext_queue_submit_bd(struct hl_device *hdev, struct hl_hw_queue *q,
u32 ctl, u32 len, u64 ptr)
static void ext_and_hw_queue_submit_bd(struct hl_device *hdev,
struct hl_hw_queue *q, u32 ctl, u32 len, u64 ptr)
{
struct hl_bd *bd;
@ -173,6 +173,45 @@ static int int_queue_sanity_checks(struct hl_device *hdev,
return 0;
}
/*
* hw_queue_sanity_checks() - Perform some sanity checks on a H/W queue.
* @hdev: Pointer to hl_device structure.
* @q: Pointer to hl_hw_queue structure.
* @num_of_entries: How many entries to check for space.
*
* Perform the following:
* - Make sure we have enough space in the completion queue.
* This check also ensures that there is enough space in the h/w queue, as
* both queues are of the same size.
* - Reserve space in the completion queue (needs to be reversed if there
* is a failure down the road before the actual submission of work).
*
* Both operations are done using the "free_slots_cnt" field of the completion
* queue. The CI counters of the queue and the completion queue are not
* needed/used for the H/W queue type.
*/
static int hw_queue_sanity_checks(struct hl_device *hdev, struct hl_hw_queue *q,
int num_of_entries)
{
atomic_t *free_slots =
&hdev->completion_queue[q->hw_queue_id].free_slots_cnt;
/*
* Check we have enough space in the completion queue.
* Add -1 to counter (decrement) unless counter was already 0.
* In that case, CQ is full so we can't submit a new CB.
* atomic_add_unless will return 0 if counter was already 0.
*/
if (atomic_add_negative(num_of_entries * -1, free_slots)) {
dev_dbg(hdev->dev, "No space for %d entries on CQ %d\n",
num_of_entries, q->hw_queue_id);
atomic_add(num_of_entries, free_slots);
return -EAGAIN;
}
return 0;
}
/*
* hl_hw_queue_send_cb_no_cmpl - send a single CB (not a JOB) without completion
*
@ -188,7 +227,7 @@ int hl_hw_queue_send_cb_no_cmpl(struct hl_device *hdev, u32 hw_queue_id,
u32 cb_size, u64 cb_ptr)
{
struct hl_hw_queue *q = &hdev->kernel_queues[hw_queue_id];
int rc;
int rc = 0;
/*
* The CPU queue is a synchronous queue with an effective depth of
@ -206,11 +245,18 @@ int hl_hw_queue_send_cb_no_cmpl(struct hl_device *hdev, u32 hw_queue_id,
goto out;
}
rc = ext_queue_sanity_checks(hdev, q, 1, false);
if (rc)
goto out;
/*
* hl_hw_queue_send_cb_no_cmpl() is called for queues of a H/W queue
* type only on init phase, when the queues are empty and being tested,
* so there is no need for sanity checks.
*/
if (q->queue_type != QUEUE_TYPE_HW) {
rc = ext_queue_sanity_checks(hdev, q, 1, false);
if (rc)
goto out;
}
ext_queue_submit_bd(hdev, q, 0, cb_size, cb_ptr);
ext_and_hw_queue_submit_bd(hdev, q, 0, cb_size, cb_ptr);
out:
if (q->queue_type != QUEUE_TYPE_CPU)
@ -220,14 +266,14 @@ out:
}
/*
* ext_hw_queue_schedule_job - submit an JOB to an external queue
* ext_queue_schedule_job - submit a JOB to an external queue
*
* @job: pointer to the job that needs to be submitted to the queue
*
* This function must be called when the scheduler mutex is taken
*
*/
static void ext_hw_queue_schedule_job(struct hl_cs_job *job)
static void ext_queue_schedule_job(struct hl_cs_job *job)
{
struct hl_device *hdev = job->cs->ctx->hdev;
struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id];
@ -260,7 +306,7 @@ static void ext_hw_queue_schedule_job(struct hl_cs_job *job)
* H/W queues is done under the scheduler mutex
*
* No need to check if CQ is full because it was already
* checked in hl_queue_sanity_checks
* checked in ext_queue_sanity_checks
*/
cq = &hdev->completion_queue[q->hw_queue_id];
cq_addr = cq->bus_address + cq->pi * sizeof(struct hl_cq_entry);
@ -274,18 +320,18 @@ static void ext_hw_queue_schedule_job(struct hl_cs_job *job)
cq->pi = hl_cq_inc_ptr(cq->pi);
ext_queue_submit_bd(hdev, q, ctl, len, ptr);
ext_and_hw_queue_submit_bd(hdev, q, ctl, len, ptr);
}
/*
* int_hw_queue_schedule_job - submit an JOB to an internal queue
* int_queue_schedule_job - submit a JOB to an internal queue
*
* @job: pointer to the job that needs to be submitted to the queue
*
* This function must be called when the scheduler mutex is taken
*
*/
static void int_hw_queue_schedule_job(struct hl_cs_job *job)
static void int_queue_schedule_job(struct hl_cs_job *job)
{
struct hl_device *hdev = job->cs->ctx->hdev;
struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id];
@ -307,6 +353,60 @@ static void int_hw_queue_schedule_job(struct hl_cs_job *job)
hdev->asic_funcs->ring_doorbell(hdev, q->hw_queue_id, q->pi);
}
/*
* hw_queue_schedule_job - submit a JOB to a H/W queue
*
* @job: pointer to the job that needs to be submitted to the queue
*
* This function must be called when the scheduler mutex is taken
*
*/
static void hw_queue_schedule_job(struct hl_cs_job *job)
{
struct hl_device *hdev = job->cs->ctx->hdev;
struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id];
struct hl_cq *cq;
u64 ptr;
u32 offset, ctl, len;
/*
* Upon PQE completion, COMP_DATA is used as the write data to the
* completion queue (QMAN HBW message), and COMP_OFFSET is used as the
* write address offset in the SM block (QMAN LBW message).
* The write address offset is calculated as "COMP_OFFSET << 2".
*/
offset = job->cs->sequence & (HL_MAX_PENDING_CS - 1);
ctl = ((offset << BD_CTL_COMP_OFFSET_SHIFT) & BD_CTL_COMP_OFFSET_MASK) |
((q->pi << BD_CTL_COMP_DATA_SHIFT) & BD_CTL_COMP_DATA_MASK);
len = job->job_cb_size;
/*
* A patched CB is created only if a user CB was allocated by driver and
* MMU is disabled. If MMU is enabled, the user CB should be used
* instead. If the user CB wasn't allocated by driver, assume that it
* holds an address.
*/
if (job->patched_cb)
ptr = job->patched_cb->bus_address;
else if (job->is_kernel_allocated_cb)
ptr = job->user_cb->bus_address;
else
ptr = (u64) (uintptr_t) job->user_cb;
/*
* No need to protect pi_offset because scheduling to the
* H/W queues is done under the scheduler mutex
*
* No need to check if CQ is full because it was already
* checked in hw_queue_sanity_checks
*/
cq = &hdev->completion_queue[q->hw_queue_id];
cq->pi = hl_cq_inc_ptr(cq->pi);
ext_and_hw_queue_submit_bd(hdev, q, ctl, len, ptr);
}
/*
* hl_hw_queue_schedule_cs - schedule a command submission
*
@ -330,23 +430,34 @@ int hl_hw_queue_schedule_cs(struct hl_cs *cs)
}
q = &hdev->kernel_queues[0];
/* This loop assumes all external queues are consecutive */
for (i = 0, cq_cnt = 0 ; i < HL_MAX_QUEUES ; i++, q++) {
if (q->queue_type == QUEUE_TYPE_EXT) {
if (cs->jobs_in_queue_cnt[i]) {
if (cs->jobs_in_queue_cnt[i]) {
switch (q->queue_type) {
case QUEUE_TYPE_EXT:
rc = ext_queue_sanity_checks(hdev, q,
cs->jobs_in_queue_cnt[i], true);
if (rc)
goto unroll_cq_resv;
cq_cnt++;
}
} else if (q->queue_type == QUEUE_TYPE_INT) {
if (cs->jobs_in_queue_cnt[i]) {
cs->jobs_in_queue_cnt[i], true);
break;
case QUEUE_TYPE_INT:
rc = int_queue_sanity_checks(hdev, q,
cs->jobs_in_queue_cnt[i]);
if (rc)
goto unroll_cq_resv;
cs->jobs_in_queue_cnt[i]);
break;
case QUEUE_TYPE_HW:
rc = hw_queue_sanity_checks(hdev, q,
cs->jobs_in_queue_cnt[i]);
break;
default:
dev_err(hdev->dev, "Queue type %d is invalid\n",
q->queue_type);
rc = -EINVAL;
break;
}
if (rc)
goto unroll_cq_resv;
if (q->queue_type == QUEUE_TYPE_EXT ||
q->queue_type == QUEUE_TYPE_HW)
cq_cnt++;
}
}
@ -373,21 +484,30 @@ int hl_hw_queue_schedule_cs(struct hl_cs *cs)
}
list_for_each_entry_safe(job, tmp, &cs->job_list, cs_node)
if (job->ext_queue)
ext_hw_queue_schedule_job(job);
else
int_hw_queue_schedule_job(job);
switch (job->queue_type) {
case QUEUE_TYPE_EXT:
ext_queue_schedule_job(job);
break;
case QUEUE_TYPE_INT:
int_queue_schedule_job(job);
break;
case QUEUE_TYPE_HW:
hw_queue_schedule_job(job);
break;
default:
break;
}
cs->submitted = true;
goto out;
unroll_cq_resv:
/* This loop assumes all external queues are consecutive */
q = &hdev->kernel_queues[0];
for (i = 0 ; (i < HL_MAX_QUEUES) && (cq_cnt > 0) ; i++, q++) {
if ((q->queue_type == QUEUE_TYPE_EXT) &&
(cs->jobs_in_queue_cnt[i])) {
if ((q->queue_type == QUEUE_TYPE_EXT ||
q->queue_type == QUEUE_TYPE_HW) &&
cs->jobs_in_queue_cnt[i]) {
atomic_t *free_slots =
&hdev->completion_queue[i].free_slots_cnt;
atomic_add(cs->jobs_in_queue_cnt[i], free_slots);
@ -414,8 +534,8 @@ void hl_hw_queue_inc_ci_kernel(struct hl_device *hdev, u32 hw_queue_id)
q->ci = hl_queue_inc_ptr(q->ci);
}
static int ext_and_cpu_hw_queue_init(struct hl_device *hdev,
struct hl_hw_queue *q, bool is_cpu_queue)
static int ext_and_cpu_queue_init(struct hl_device *hdev, struct hl_hw_queue *q,
bool is_cpu_queue)
{
void *p;
int rc;
@ -465,7 +585,7 @@ free_queue:
return rc;
}
static int int_hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q)
static int int_queue_init(struct hl_device *hdev, struct hl_hw_queue *q)
{
void *p;
@ -485,18 +605,38 @@ static int int_hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q)
return 0;
}
static int cpu_hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q)
static int cpu_queue_init(struct hl_device *hdev, struct hl_hw_queue *q)
{
return ext_and_cpu_hw_queue_init(hdev, q, true);
return ext_and_cpu_queue_init(hdev, q, true);
}
static int ext_hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q)
static int ext_queue_init(struct hl_device *hdev, struct hl_hw_queue *q)
{
return ext_and_cpu_hw_queue_init(hdev, q, false);
return ext_and_cpu_queue_init(hdev, q, false);
}
static int hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q)
{
void *p;
p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev,
HL_QUEUE_SIZE_IN_BYTES,
&q->bus_address,
GFP_KERNEL | __GFP_ZERO);
if (!p)
return -ENOMEM;
q->kernel_address = (u64) (uintptr_t) p;
/* Make sure read/write pointers are initialized to start of queue */
q->ci = 0;
q->pi = 0;
return 0;
}
/*
* hw_queue_init - main initialization function for H/W queue object
* queue_init - main initialization function for H/W queue object
*
* @hdev: pointer to hl_device device structure
* @q: pointer to hl_hw_queue queue structure
@ -505,7 +645,7 @@ static int ext_hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q)
* Allocate dma-able memory for the queue and initialize fields
* Returns 0 on success
*/
static int hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q,
static int queue_init(struct hl_device *hdev, struct hl_hw_queue *q,
u32 hw_queue_id)
{
int rc;
@ -516,21 +656,20 @@ static int hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q,
switch (q->queue_type) {
case QUEUE_TYPE_EXT:
rc = ext_hw_queue_init(hdev, q);
rc = ext_queue_init(hdev, q);
break;
case QUEUE_TYPE_INT:
rc = int_hw_queue_init(hdev, q);
rc = int_queue_init(hdev, q);
break;
case QUEUE_TYPE_CPU:
rc = cpu_hw_queue_init(hdev, q);
rc = cpu_queue_init(hdev, q);
break;
case QUEUE_TYPE_HW:
rc = hw_queue_init(hdev, q);
break;
case QUEUE_TYPE_NA:
q->valid = 0;
return 0;
default:
dev_crit(hdev->dev, "wrong queue type %d during init\n",
q->queue_type);
@ -554,7 +693,7 @@ static int hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q,
*
* Free the queue memory
*/
static void hw_queue_fini(struct hl_device *hdev, struct hl_hw_queue *q)
static void queue_fini(struct hl_device *hdev, struct hl_hw_queue *q)
{
if (!q->valid)
return;
@ -612,7 +751,7 @@ int hl_hw_queues_create(struct hl_device *hdev)
i < HL_MAX_QUEUES ; i++, q_ready_cnt++, q++) {
q->queue_type = asic->hw_queues_props[i].type;
rc = hw_queue_init(hdev, q, i);
rc = queue_init(hdev, q, i);
if (rc) {
dev_err(hdev->dev,
"failed to initialize queue %d\n", i);
@ -624,7 +763,7 @@ int hl_hw_queues_create(struct hl_device *hdev)
release_queues:
for (i = 0, q = hdev->kernel_queues ; i < q_ready_cnt ; i++, q++)
hw_queue_fini(hdev, q);
queue_fini(hdev, q);
kfree(hdev->kernel_queues);
@ -637,7 +776,7 @@ void hl_hw_queues_destroy(struct hl_device *hdev)
int i;
for (i = 0, q = hdev->kernel_queues ; i < HL_MAX_QUEUES ; i++, q++)
hw_queue_fini(hdev, q);
queue_fini(hdev, q);
kfree(hdev->kernel_queues);
}

View File

@ -260,4 +260,6 @@
#define DMA_QM_3_GLBL_CFG1_DMA_STOP_SHIFT DMA_QM_0_GLBL_CFG1_DMA_STOP_SHIFT
#define DMA_QM_4_GLBL_CFG1_DMA_STOP_SHIFT DMA_QM_0_GLBL_CFG1_DMA_STOP_SHIFT
#define PSOC_ETR_AXICTL_PROTCTRLBIT1_SHIFT 1
#endif /* ASIC_REG_GOYA_MASKS_H_ */

View File

@ -84,6 +84,7 @@
#include "tpc6_rtr_regs.h"
#include "tpc7_nrtr_regs.h"
#include "tpc0_eml_cfg_regs.h"
#include "psoc_etr_regs.h"
#include "psoc_global_conf_masks.h"
#include "dma_macro_masks.h"

View File

@ -0,0 +1,114 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright 2016-2018 HabanaLabs, Ltd.
* All Rights Reserved.
*
*/
/************************************
** This is an auto-generated file **
** DO NOT EDIT BELOW **
************************************/
#ifndef ASIC_REG_PSOC_ETR_REGS_H_
#define ASIC_REG_PSOC_ETR_REGS_H_
/*
*****************************************
* PSOC_ETR (Prototype: ETR)
*****************************************
*/
#define mmPSOC_ETR_RSZ 0x2C43004
#define mmPSOC_ETR_STS 0x2C4300C
#define mmPSOC_ETR_RRD 0x2C43010
#define mmPSOC_ETR_RRP 0x2C43014
#define mmPSOC_ETR_RWP 0x2C43018
#define mmPSOC_ETR_TRG 0x2C4301C
#define mmPSOC_ETR_CTL 0x2C43020
#define mmPSOC_ETR_RWD 0x2C43024
#define mmPSOC_ETR_MODE 0x2C43028
#define mmPSOC_ETR_LBUFLEVEL 0x2C4302C
#define mmPSOC_ETR_CBUFLEVEL 0x2C43030
#define mmPSOC_ETR_BUFWM 0x2C43034
#define mmPSOC_ETR_RRPHI 0x2C43038
#define mmPSOC_ETR_RWPHI 0x2C4303C
#define mmPSOC_ETR_AXICTL 0x2C43110
#define mmPSOC_ETR_DBALO 0x2C43118
#define mmPSOC_ETR_DBAHI 0x2C4311C
#define mmPSOC_ETR_FFSR 0x2C43300
#define mmPSOC_ETR_FFCR 0x2C43304
#define mmPSOC_ETR_PSCR 0x2C43308
#define mmPSOC_ETR_ITMISCOP0 0x2C43EE0
#define mmPSOC_ETR_ITTRFLIN 0x2C43EE8
#define mmPSOC_ETR_ITATBDATA0 0x2C43EEC
#define mmPSOC_ETR_ITATBCTR2 0x2C43EF0
#define mmPSOC_ETR_ITATBCTR1 0x2C43EF4
#define mmPSOC_ETR_ITATBCTR0 0x2C43EF8
#define mmPSOC_ETR_ITCTRL 0x2C43F00
#define mmPSOC_ETR_CLAIMSET 0x2C43FA0
#define mmPSOC_ETR_CLAIMCLR 0x2C43FA4
#define mmPSOC_ETR_LAR 0x2C43FB0
#define mmPSOC_ETR_LSR 0x2C43FB4
#define mmPSOC_ETR_AUTHSTATUS 0x2C43FB8
#define mmPSOC_ETR_DEVID 0x2C43FC8
#define mmPSOC_ETR_DEVTYPE 0x2C43FCC
#define mmPSOC_ETR_PERIPHID4 0x2C43FD0
#define mmPSOC_ETR_PERIPHID5 0x2C43FD4
#define mmPSOC_ETR_PERIPHID6 0x2C43FD8
#define mmPSOC_ETR_PERIPHID7 0x2C43FDC
#define mmPSOC_ETR_PERIPHID0 0x2C43FE0
#define mmPSOC_ETR_PERIPHID1 0x2C43FE4
#define mmPSOC_ETR_PERIPHID2 0x2C43FE8
#define mmPSOC_ETR_PERIPHID3 0x2C43FEC
#define mmPSOC_ETR_COMPID0 0x2C43FF0
#define mmPSOC_ETR_COMPID1 0x2C43FF4
#define mmPSOC_ETR_COMPID2 0x2C43FF8
#define mmPSOC_ETR_COMPID3 0x2C43FFC
#endif /* ASIC_REG_PSOC_ETR_REGS_H_ */

View File

@ -20,6 +20,8 @@ enum cpu_boot_status {
CPU_BOOT_STATUS_DRAM_INIT_FAIL,
CPU_BOOT_STATUS_FIT_CORRUPTED,
CPU_BOOT_STATUS_UBOOT_NOT_READY,
CPU_BOOT_STATUS_RESERVED,
CPU_BOOT_STATUS_TS_INIT_FAIL,
};
enum kmd_msg {

View File

@ -12,18 +12,16 @@
#define PAGE_SHIFT_2MB 21
#define PAGE_SIZE_2MB (_AC(1, UL) << PAGE_SHIFT_2MB)
#define PAGE_SIZE_4KB (_AC(1, UL) << PAGE_SHIFT_4KB)
#define PAGE_MASK_2MB (~(PAGE_SIZE_2MB - 1))
#define PAGE_PRESENT_MASK 0x0000000000001ull
#define SWAP_OUT_MASK 0x0000000000004ull
#define LAST_MASK 0x0000000000800ull
#define PHYS_ADDR_MASK 0xFFFFFFFFFFFFF000ull
#define HOP0_MASK 0x3000000000000ull
#define HOP1_MASK 0x0FF8000000000ull
#define HOP2_MASK 0x0007FC0000000ull
#define HOP3_MASK 0x000003FE00000ull
#define HOP4_MASK 0x00000001FF000ull
#define OFFSET_MASK 0x0000000000FFFull
#define FLAGS_MASK 0x0000000000FFFull
#define HOP0_SHIFT 48
#define HOP1_SHIFT 39
@ -31,8 +29,7 @@
#define HOP3_SHIFT 21
#define HOP4_SHIFT 12
#define PTE_PHYS_ADDR_SHIFT 12
#define PTE_PHYS_ADDR_MASK ~OFFSET_MASK
#define HOP_PHYS_ADDR_MASK (~FLAGS_MASK)
#define HL_PTE_SIZE sizeof(u64)
#define HOP_TABLE_SIZE PAGE_SIZE_4KB

View File

@ -23,6 +23,8 @@ struct hl_bd {
#define HL_BD_SIZE sizeof(struct hl_bd)
/*
* S/W CTL FIELDS.
*
* BD_CTL_REPEAT_VALID tells the CP whether the repeat field in the BD CTL is
* valid. 1 means the repeat field is valid, 0 means not-valid,
* i.e. repeat == 1
@ -33,6 +35,16 @@ struct hl_bd {
#define BD_CTL_SHADOW_INDEX_SHIFT 0
#define BD_CTL_SHADOW_INDEX_MASK 0x00000FFF
/*
* H/W CTL FIELDS
*/
#define BD_CTL_COMP_OFFSET_SHIFT 16
#define BD_CTL_COMP_OFFSET_MASK 0x00FF0000
#define BD_CTL_COMP_DATA_SHIFT 0
#define BD_CTL_COMP_DATA_MASK 0x0000FFFF
/*
* COMPLETION QUEUE
*/

View File

@ -13,7 +13,6 @@
#include <linux/slab.h>
#include <linux/genalloc.h>
#define PGS_IN_2MB_PAGE (PAGE_SIZE_2MB >> PAGE_SHIFT)
#define HL_MMU_DEBUG 0
/*
@ -159,20 +158,19 @@ pages_pack_err:
}
/*
* get_userptr_from_host_va - initialize userptr structure from given host
* virtual address
*
* @hdev : habanalabs device structure
* @args : parameters containing the virtual address and size
* @p_userptr : pointer to result userptr structure
* dma_map_host_va - DMA mapping of the given host virtual address.
* @hdev: habanalabs device structure
* @addr: the host virtual address of the memory area
* @size: the size of the memory area
* @p_userptr: pointer to result userptr structure
*
* This function does the following:
* - Allocate userptr structure
* - Pin the given host memory using the userptr structure
* - Perform DMA mapping to have the DMA addresses of the pages
*/
static int get_userptr_from_host_va(struct hl_device *hdev,
struct hl_mem_in *args, struct hl_userptr **p_userptr)
static int dma_map_host_va(struct hl_device *hdev, u64 addr, u64 size,
struct hl_userptr **p_userptr)
{
struct hl_userptr *userptr;
int rc;
@ -183,8 +181,7 @@ static int get_userptr_from_host_va(struct hl_device *hdev,
goto userptr_err;
}
rc = hl_pin_host_memory(hdev, args->map_host.host_virt_addr,
args->map_host.mem_size, userptr);
rc = hl_pin_host_memory(hdev, addr, size, userptr);
if (rc) {
dev_err(hdev->dev, "Failed to pin host memory\n");
goto pin_err;
@ -215,16 +212,16 @@ userptr_err:
}
/*
* free_userptr - free userptr structure
*
* @hdev : habanalabs device structure
* @userptr : userptr to free
* dma_unmap_host_va - DMA unmapping of the given host virtual address.
* @hdev: habanalabs device structure
* @userptr: userptr to free
*
* This function does the following:
* - Unpins the physical pages
* - Frees the userptr structure
*/
static void free_userptr(struct hl_device *hdev, struct hl_userptr *userptr)
static void dma_unmap_host_va(struct hl_device *hdev,
struct hl_userptr *userptr)
{
hl_unpin_host_memory(hdev, userptr);
kfree(userptr);
@ -253,10 +250,9 @@ static void dram_pg_pool_do_release(struct kref *ref)
}
/*
* free_phys_pg_pack - free physical page pack
*
* @hdev : habanalabs device structure
* @phys_pg_pack : physical page pack to free
* free_phys_pg_pack - free physical page pack
* @hdev: habanalabs device structure
* @phys_pg_pack: physical page pack to free
*
* This function does the following:
* - For DRAM memory only, iterate over the pack and free each physical block
@ -264,7 +260,7 @@ static void dram_pg_pool_do_release(struct kref *ref)
* - Free the hl_vm_phys_pg_pack structure
*/
static void free_phys_pg_pack(struct hl_device *hdev,
struct hl_vm_phys_pg_pack *phys_pg_pack)
struct hl_vm_phys_pg_pack *phys_pg_pack)
{
struct hl_vm *vm = &hdev->vm;
u64 i;
@ -519,8 +515,8 @@ static inline int add_va_block(struct hl_device *hdev,
* - Return the start address of the virtual block
*/
static u64 get_va_block(struct hl_device *hdev,
struct hl_va_range *va_range, u64 size, u64 hint_addr,
bool is_userptr)
struct hl_va_range *va_range, u64 size, u64 hint_addr,
bool is_userptr)
{
struct hl_vm_va_block *va_block, *new_va_block = NULL;
u64 valid_start, valid_size, prev_start, prev_end, page_mask,
@ -528,18 +524,17 @@ static u64 get_va_block(struct hl_device *hdev,
u32 page_size;
bool add_prev = false;
if (is_userptr) {
if (is_userptr)
/*
* We cannot know if the user allocated memory with huge pages
* or not, hence we continue with the biggest possible
* granularity.
*/
page_size = PAGE_SIZE_2MB;
page_mask = PAGE_MASK_2MB;
} else {
page_size = hdev->asic_prop.dram_page_size;
page_mask = ~((u64)page_size - 1);
}
page_size = hdev->asic_prop.pmmu.huge_page_size;
else
page_size = hdev->asic_prop.dmmu.page_size;
page_mask = ~((u64)page_size - 1);
mutex_lock(&va_range->lock);
@ -549,7 +544,6 @@ static u64 get_va_block(struct hl_device *hdev,
/* calc the first possible aligned addr */
valid_start = va_block->start;
if (valid_start & (page_size - 1)) {
valid_start &= page_mask;
valid_start += page_size;
@ -561,7 +555,6 @@ static u64 get_va_block(struct hl_device *hdev,
if (valid_size >= size &&
(!new_va_block || valid_size < res_valid_size)) {
new_va_block = va_block;
res_valid_start = valid_start;
res_valid_size = valid_size;
@ -631,11 +624,10 @@ static u32 get_sg_info(struct scatterlist *sg, dma_addr_t *dma_addr)
/*
* init_phys_pg_pack_from_userptr - initialize physical page pack from host
* memory
*
* @ctx : current context
* @userptr : userptr to initialize from
* @pphys_pg_pack : res pointer
* memory
* @ctx: current context
* @userptr: userptr to initialize from
* @pphys_pg_pack: result pointer
*
* This function does the following:
* - Pin the physical pages related to the given virtual block
@ -643,16 +635,19 @@ static u32 get_sg_info(struct scatterlist *sg, dma_addr_t *dma_addr)
* virtual block
*/
static int init_phys_pg_pack_from_userptr(struct hl_ctx *ctx,
struct hl_userptr *userptr,
struct hl_vm_phys_pg_pack **pphys_pg_pack)
struct hl_userptr *userptr,
struct hl_vm_phys_pg_pack **pphys_pg_pack)
{
struct hl_mmu_properties *mmu_prop = &ctx->hdev->asic_prop.pmmu;
struct hl_vm_phys_pg_pack *phys_pg_pack;
struct scatterlist *sg;
dma_addr_t dma_addr;
u64 page_mask, total_npages;
u32 npages, page_size = PAGE_SIZE;
u32 npages, page_size = PAGE_SIZE,
huge_page_size = mmu_prop->huge_page_size;
bool first = true, is_huge_page_opt = true;
int rc, i, j;
u32 pgs_in_huge_page = huge_page_size >> __ffs(page_size);
phys_pg_pack = kzalloc(sizeof(*phys_pg_pack), GFP_KERNEL);
if (!phys_pg_pack)
@ -675,14 +670,14 @@ static int init_phys_pg_pack_from_userptr(struct hl_ctx *ctx,
total_npages += npages;
if ((npages % PGS_IN_2MB_PAGE) ||
(dma_addr & (PAGE_SIZE_2MB - 1)))
if ((npages % pgs_in_huge_page) ||
(dma_addr & (huge_page_size - 1)))
is_huge_page_opt = false;
}
if (is_huge_page_opt) {
page_size = PAGE_SIZE_2MB;
total_npages /= PGS_IN_2MB_PAGE;
page_size = huge_page_size;
do_div(total_npages, pgs_in_huge_page);
}
page_mask = ~(((u64) page_size) - 1);
@ -714,7 +709,7 @@ static int init_phys_pg_pack_from_userptr(struct hl_ctx *ctx,
dma_addr += page_size;
if (is_huge_page_opt)
npages -= PGS_IN_2MB_PAGE;
npages -= pgs_in_huge_page;
else
npages--;
}
@ -731,19 +726,18 @@ page_pack_arr_mem_err:
}
/*
* map_phys_page_pack - maps the physical page pack
*
* @ctx : current context
* @vaddr : start address of the virtual area to map from
* @phys_pg_pack : the pack of physical pages to map to
* map_phys_pg_pack - maps the physical page pack.
* @ctx: current context
* @vaddr: start address of the virtual area to map from
* @phys_pg_pack: the pack of physical pages to map to
*
* This function does the following:
* - Maps each chunk of virtual memory to matching physical chunk
* - Stores number of successful mappings in the given argument
* - Returns 0 on success, error code otherwise.
* - Returns 0 on success, error code otherwise
*/
static int map_phys_page_pack(struct hl_ctx *ctx, u64 vaddr,
struct hl_vm_phys_pg_pack *phys_pg_pack)
static int map_phys_pg_pack(struct hl_ctx *ctx, u64 vaddr,
struct hl_vm_phys_pg_pack *phys_pg_pack)
{
struct hl_device *hdev = ctx->hdev;
u64 next_vaddr = vaddr, paddr, mapped_pg_cnt = 0, i;
@ -783,6 +777,36 @@ err:
return rc;
}
/*
* unmap_phys_pg_pack - unmaps the physical page pack
* @ctx: current context
* @vaddr: start address of the virtual area to unmap
* @phys_pg_pack: the pack of physical pages to unmap
*/
static void unmap_phys_pg_pack(struct hl_ctx *ctx, u64 vaddr,
struct hl_vm_phys_pg_pack *phys_pg_pack)
{
struct hl_device *hdev = ctx->hdev;
u64 next_vaddr, i;
u32 page_size;
page_size = phys_pg_pack->page_size;
next_vaddr = vaddr;
for (i = 0 ; i < phys_pg_pack->npages ; i++, next_vaddr += page_size) {
if (hl_mmu_unmap(ctx, next_vaddr, page_size))
dev_warn_ratelimited(hdev->dev,
"unmap failed for vaddr: 0x%llx\n", next_vaddr);
/*
* unmapping on Palladium can be really long, so avoid a CPU
* soft lockup bug by sleeping a little between unmapping pages
*/
if (hdev->pldm)
usleep_range(500, 1000);
}
}
static int get_paddr_from_handle(struct hl_ctx *ctx, struct hl_mem_in *args,
u64 *paddr)
{
@ -839,7 +863,10 @@ static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args,
*device_addr = 0;
if (is_userptr) {
rc = get_userptr_from_host_va(hdev, args, &userptr);
u64 addr = args->map_host.host_virt_addr,
size = args->map_host.mem_size;
rc = dma_map_host_va(hdev, addr, size, &userptr);
if (rc) {
dev_err(hdev->dev, "failed to get userptr from va\n");
return rc;
@ -850,7 +877,7 @@ static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args,
if (rc) {
dev_err(hdev->dev,
"unable to init page pack for vaddr 0x%llx\n",
args->map_host.host_virt_addr);
addr);
goto init_page_pack_err;
}
@ -909,7 +936,7 @@ static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args,
mutex_lock(&ctx->mmu_lock);
rc = map_phys_page_pack(ctx, ret_vaddr, phys_pg_pack);
rc = map_phys_pg_pack(ctx, ret_vaddr, phys_pg_pack);
if (rc) {
mutex_unlock(&ctx->mmu_lock);
dev_err(hdev->dev, "mapping page pack failed for handle %u\n",
@ -917,7 +944,7 @@ static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args,
goto map_err;
}
hdev->asic_funcs->mmu_invalidate_cache(hdev, false);
hdev->asic_funcs->mmu_invalidate_cache(hdev, false, *vm_type);
mutex_unlock(&ctx->mmu_lock);
@ -955,7 +982,7 @@ shared_err:
free_phys_pg_pack(hdev, phys_pg_pack);
init_page_pack_err:
if (is_userptr)
free_userptr(hdev, userptr);
dma_unmap_host_va(hdev, userptr);
return rc;
}
@ -965,20 +992,20 @@ init_page_pack_err:
*
* @ctx : current context
* @vaddr : device virtual address to unmap
* @ctx_free : true if in context free flow, false otherwise.
*
* This function does the following:
* - Unmap the physical pages related to the given virtual address
* - return the device virtual block to the virtual block list
*/
static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr)
static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr, bool ctx_free)
{
struct hl_device *hdev = ctx->hdev;
struct hl_vm_phys_pg_pack *phys_pg_pack = NULL;
struct hl_vm_hash_node *hnode = NULL;
struct hl_userptr *userptr = NULL;
struct hl_va_range *va_range;
enum vm_type_t *vm_type;
u64 next_vaddr, i;
u32 page_size;
bool is_userptr;
int rc;
@ -1003,9 +1030,10 @@ static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr)
if (*vm_type == VM_TYPE_USERPTR) {
is_userptr = true;
va_range = &ctx->host_va_range;
userptr = hnode->ptr;
rc = init_phys_pg_pack_from_userptr(ctx, userptr,
&phys_pg_pack);
&phys_pg_pack);
if (rc) {
dev_err(hdev->dev,
"unable to init page pack for vaddr 0x%llx\n",
@ -1014,6 +1042,7 @@ static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr)
}
} else if (*vm_type == VM_TYPE_PHYS_PACK) {
is_userptr = false;
va_range = &ctx->dram_va_range;
phys_pg_pack = hnode->ptr;
} else {
dev_warn(hdev->dev,
@ -1029,42 +1058,41 @@ static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr)
goto mapping_cnt_err;
}
page_size = phys_pg_pack->page_size;
vaddr &= ~(((u64) page_size) - 1);
next_vaddr = vaddr;
vaddr &= ~(((u64) phys_pg_pack->page_size) - 1);
mutex_lock(&ctx->mmu_lock);
for (i = 0 ; i < phys_pg_pack->npages ; i++, next_vaddr += page_size) {
if (hl_mmu_unmap(ctx, next_vaddr, page_size))
dev_warn_ratelimited(hdev->dev,
"unmap failed for vaddr: 0x%llx\n", next_vaddr);
unmap_phys_pg_pack(ctx, vaddr, phys_pg_pack);
/* unmapping on Palladium can be really long, so avoid a CPU
* soft lockup bug by sleeping a little between unmapping pages
*/
if (hdev->pldm)
usleep_range(500, 1000);
}
hdev->asic_funcs->mmu_invalidate_cache(hdev, true);
/*
* During context free this function is called in a loop to clean all
* the context mappings. Hence the cache invalidation can be called once
* at the loop end rather than for each iteration
*/
if (!ctx_free)
hdev->asic_funcs->mmu_invalidate_cache(hdev, true, *vm_type);
mutex_unlock(&ctx->mmu_lock);
if (add_va_block(hdev,
is_userptr ? &ctx->host_va_range : &ctx->dram_va_range,
vaddr,
vaddr + phys_pg_pack->total_size - 1))
dev_warn(hdev->dev, "add va block failed for vaddr: 0x%llx\n",
vaddr);
/*
* No point in maintaining the free VA block list if the context is
* closing as the list will be freed anyway
*/
if (!ctx_free) {
rc = add_va_block(hdev, va_range, vaddr,
vaddr + phys_pg_pack->total_size - 1);
if (rc)
dev_warn(hdev->dev,
"add va block failed for vaddr: 0x%llx\n",
vaddr);
}
atomic_dec(&phys_pg_pack->mapping_cnt);
kfree(hnode);
if (is_userptr) {
free_phys_pg_pack(hdev, phys_pg_pack);
free_userptr(hdev, userptr);
dma_unmap_host_va(hdev, userptr);
}
return 0;
@ -1189,8 +1217,8 @@ int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data)
break;
case HL_MEM_OP_UNMAP:
rc = unmap_device_va(ctx,
args->in.unmap.device_virt_addr);
rc = unmap_device_va(ctx, args->in.unmap.device_virt_addr,
false);
break;
default:
@ -1203,57 +1231,17 @@ out:
return rc;
}
/*
* hl_pin_host_memory - pins a chunk of host memory
*
* @hdev : pointer to the habanalabs device structure
* @addr : the user-space virtual address of the memory area
* @size : the size of the memory area
* @userptr : pointer to hl_userptr structure
*
* This function does the following:
* - Pins the physical pages
* - Create a SG list from those pages
*/
int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size,
struct hl_userptr *userptr)
static int get_user_memory(struct hl_device *hdev, u64 addr, u64 size,
u32 npages, u64 start, u32 offset,
struct hl_userptr *userptr)
{
u64 start, end;
u32 npages, offset;
int rc;
if (!size) {
dev_err(hdev->dev, "size to pin is invalid - %llu\n", size);
return -EINVAL;
}
if (!access_ok((void __user *) (uintptr_t) addr, size)) {
dev_err(hdev->dev, "user pointer is invalid - 0x%llx\n", addr);
return -EFAULT;
}
/*
* If the combination of the address and size requested for this memory
* region causes an integer overflow, return error.
*/
if (((addr + size) < addr) ||
PAGE_ALIGN(addr + size) < (addr + size)) {
dev_err(hdev->dev,
"user pointer 0x%llx + %llu causes integer overflow\n",
addr, size);
return -EINVAL;
}
start = addr & PAGE_MASK;
offset = addr & ~PAGE_MASK;
end = PAGE_ALIGN(addr + size);
npages = (end - start) >> PAGE_SHIFT;
userptr->size = size;
userptr->addr = addr;
userptr->dma_mapped = false;
INIT_LIST_HEAD(&userptr->job_node);
userptr->vec = frame_vector_create(npages);
if (!userptr->vec) {
dev_err(hdev->dev, "Failed to create frame vector\n");
@ -1279,17 +1267,82 @@ int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size,
goto put_framevec;
}
userptr->sgt = kzalloc(sizeof(*userptr->sgt), GFP_ATOMIC);
if (!userptr->sgt) {
rc = -ENOMEM;
goto put_framevec;
}
rc = sg_alloc_table_from_pages(userptr->sgt,
frame_vector_pages(userptr->vec),
npages, offset, size, GFP_ATOMIC);
if (rc < 0) {
dev_err(hdev->dev, "failed to create SG table from pages\n");
goto put_framevec;
}
return 0;
put_framevec:
put_vaddr_frames(userptr->vec);
destroy_framevec:
frame_vector_destroy(userptr->vec);
return rc;
}
/*
* hl_pin_host_memory - pins a chunk of host memory.
* @hdev: pointer to the habanalabs device structure
* @addr: the host virtual address of the memory area
* @size: the size of the memory area
* @userptr: pointer to hl_userptr structure
*
* This function does the following:
* - Pins the physical pages
* - Create an SG list from those pages
*/
int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size,
struct hl_userptr *userptr)
{
u64 start, end;
u32 npages, offset;
int rc;
if (!size) {
dev_err(hdev->dev, "size to pin is invalid - %llu\n", size);
return -EINVAL;
}
/*
* If the combination of the address and size requested for this memory
* region causes an integer overflow, return error.
*/
if (((addr + size) < addr) ||
PAGE_ALIGN(addr + size) < (addr + size)) {
dev_err(hdev->dev,
"user pointer 0x%llx + %llu causes integer overflow\n",
addr, size);
return -EINVAL;
}
/*
* This function can be called also from data path, hence use atomic
* always as it is not a big allocation.
*/
userptr->sgt = kzalloc(sizeof(*userptr->sgt), GFP_ATOMIC);
if (!userptr->sgt)
return -ENOMEM;
start = addr & PAGE_MASK;
offset = addr & ~PAGE_MASK;
end = PAGE_ALIGN(addr + size);
npages = (end - start) >> PAGE_SHIFT;
userptr->size = size;
userptr->addr = addr;
userptr->dma_mapped = false;
INIT_LIST_HEAD(&userptr->job_node);
rc = get_user_memory(hdev, addr, size, npages, start, offset,
userptr);
if (rc) {
dev_err(hdev->dev,
"failed to get user memory for address 0x%llx\n",
addr);
goto free_sgt;
}
@ -1299,34 +1352,28 @@ int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size,
free_sgt:
kfree(userptr->sgt);
put_framevec:
put_vaddr_frames(userptr->vec);
destroy_framevec:
frame_vector_destroy(userptr->vec);
return rc;
}
/*
* hl_unpin_host_memory - unpins a chunk of host memory
*
* @hdev : pointer to the habanalabs device structure
* @userptr : pointer to hl_userptr structure
* hl_unpin_host_memory - unpins a chunk of host memory.
* @hdev: pointer to the habanalabs device structure
* @userptr: pointer to hl_userptr structure
*
* This function does the following:
* - Unpins the physical pages related to the host memory
* - Free the SG list
*/
int hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr)
void hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr)
{
struct page **pages;
hl_debugfs_remove_userptr(hdev, userptr);
if (userptr->dma_mapped)
hdev->asic_funcs->hl_dma_unmap_sg(hdev,
userptr->sgt->sgl,
userptr->sgt->nents,
userptr->dir);
hdev->asic_funcs->hl_dma_unmap_sg(hdev, userptr->sgt->sgl,
userptr->sgt->nents,
userptr->dir);
pages = frame_vector_pages(userptr->vec);
if (!IS_ERR(pages)) {
@ -1342,8 +1389,6 @@ int hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr)
sg_free_table(userptr->sgt);
kfree(userptr->sgt);
return 0;
}
/*
@ -1542,43 +1587,16 @@ int hl_vm_ctx_init(struct hl_ctx *ctx)
* @hdev : pointer to the habanalabs structure
* va_range : pointer to virtual addresses range
*
* This function initializes the following:
* - Checks that the given range contains the whole initial range
* This function does the following:
* - Frees the virtual addresses block list and its lock
*/
static void hl_va_range_fini(struct hl_device *hdev,
struct hl_va_range *va_range)
{
struct hl_vm_va_block *va_block;
if (list_empty(&va_range->list)) {
dev_warn(hdev->dev,
"va list should not be empty on cleanup!\n");
goto out;
}
if (!list_is_singular(&va_range->list)) {
dev_warn(hdev->dev,
"va list should not contain multiple blocks on cleanup!\n");
goto free_va_list;
}
va_block = list_first_entry(&va_range->list, typeof(*va_block), node);
if (va_block->start != va_range->start_addr ||
va_block->end != va_range->end_addr) {
dev_warn(hdev->dev,
"wrong va block on cleanup, from 0x%llx to 0x%llx\n",
va_block->start, va_block->end);
goto free_va_list;
}
free_va_list:
mutex_lock(&va_range->lock);
clear_va_list_locked(hdev, &va_range->list);
mutex_unlock(&va_range->lock);
out:
mutex_destroy(&va_range->lock);
}
@ -1613,21 +1631,31 @@ void hl_vm_ctx_fini(struct hl_ctx *ctx)
hl_debugfs_remove_ctx_mem_hash(hdev, ctx);
if (!hash_empty(ctx->mem_hash))
dev_notice(hdev->dev, "ctx is freed while it has va in use\n");
/*
* Clearly something went wrong on hard reset so no point in printing
* another side effect error
*/
if (!hdev->hard_reset_pending && !hash_empty(ctx->mem_hash))
dev_notice(hdev->dev,
"ctx %d is freed while it has va in use\n",
ctx->asid);
hash_for_each_safe(ctx->mem_hash, i, tmp_node, hnode, node) {
dev_dbg(hdev->dev,
"hl_mem_hash_node of vaddr 0x%llx of asid %d is still alive\n",
hnode->vaddr, ctx->asid);
unmap_device_va(ctx, hnode->vaddr);
unmap_device_va(ctx, hnode->vaddr, true);
}
/* invalidate the cache once after the unmapping loop */
hdev->asic_funcs->mmu_invalidate_cache(hdev, true, VM_TYPE_USERPTR);
hdev->asic_funcs->mmu_invalidate_cache(hdev, true, VM_TYPE_PHYS_PACK);
spin_lock(&vm->idr_lock);
idr_for_each_entry(&vm->phys_pg_pack_handles, phys_pg_list, i)
if (phys_pg_list->asid == ctx->asid) {
dev_dbg(hdev->dev,
"page list 0x%p of asid %d is still alive\n",
"page list 0x%px of asid %d is still alive\n",
phys_pg_list, ctx->asid);
atomic64_sub(phys_pg_list->total_size,
&hdev->dram_used_mem);

View File

@ -25,10 +25,9 @@ static struct pgt_info *get_pgt_info(struct hl_ctx *ctx, u64 hop_addr)
return pgt_info;
}
static void free_hop(struct hl_ctx *ctx, u64 hop_addr)
static void _free_hop(struct hl_ctx *ctx, struct pgt_info *pgt_info)
{
struct hl_device *hdev = ctx->hdev;
struct pgt_info *pgt_info = get_pgt_info(ctx, hop_addr);
gen_pool_free(hdev->mmu_pgt_pool, pgt_info->phys_addr,
hdev->asic_prop.mmu_hop_table_size);
@ -37,6 +36,13 @@ static void free_hop(struct hl_ctx *ctx, u64 hop_addr)
kfree(pgt_info);
}
static void free_hop(struct hl_ctx *ctx, u64 hop_addr)
{
struct pgt_info *pgt_info = get_pgt_info(ctx, hop_addr);
_free_hop(ctx, pgt_info);
}
static u64 alloc_hop(struct hl_ctx *ctx)
{
struct hl_device *hdev = ctx->hdev;
@ -105,8 +111,8 @@ static inline void write_pte(struct hl_ctx *ctx, u64 shadow_pte_addr, u64 val)
* clear the 12 LSBs and translate the shadow hop to its associated
* physical hop, and add back the original 12 LSBs.
*/
u64 phys_val = get_phys_addr(ctx, val & PTE_PHYS_ADDR_MASK) |
(val & OFFSET_MASK);
u64 phys_val = get_phys_addr(ctx, val & HOP_PHYS_ADDR_MASK) |
(val & FLAGS_MASK);
ctx->hdev->asic_funcs->write_pte(ctx->hdev,
get_phys_addr(ctx, shadow_pte_addr),
@ -159,7 +165,7 @@ static inline int put_pte(struct hl_ctx *ctx, u64 hop_addr)
*/
num_of_ptes_left = pgt_info->num_of_ptes;
if (!num_of_ptes_left)
free_hop(ctx, hop_addr);
_free_hop(ctx, pgt_info);
return num_of_ptes_left;
}
@ -171,35 +177,50 @@ static inline u64 get_hopN_pte_addr(struct hl_ctx *ctx, u64 hop_addr,
((virt_addr & mask) >> shift);
}
static inline u64 get_hop0_pte_addr(struct hl_ctx *ctx, u64 hop_addr, u64 vaddr)
static inline u64 get_hop0_pte_addr(struct hl_ctx *ctx,
struct hl_mmu_properties *mmu_prop,
u64 hop_addr, u64 vaddr)
{
return get_hopN_pte_addr(ctx, hop_addr, vaddr, HOP0_MASK, HOP0_SHIFT);
return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop0_mask,
mmu_prop->hop0_shift);
}
static inline u64 get_hop1_pte_addr(struct hl_ctx *ctx, u64 hop_addr, u64 vaddr)
static inline u64 get_hop1_pte_addr(struct hl_ctx *ctx,
struct hl_mmu_properties *mmu_prop,
u64 hop_addr, u64 vaddr)
{
return get_hopN_pte_addr(ctx, hop_addr, vaddr, HOP1_MASK, HOP1_SHIFT);
return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop1_mask,
mmu_prop->hop1_shift);
}
static inline u64 get_hop2_pte_addr(struct hl_ctx *ctx, u64 hop_addr, u64 vaddr)
static inline u64 get_hop2_pte_addr(struct hl_ctx *ctx,
struct hl_mmu_properties *mmu_prop,
u64 hop_addr, u64 vaddr)
{
return get_hopN_pte_addr(ctx, hop_addr, vaddr, HOP2_MASK, HOP2_SHIFT);
return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop2_mask,
mmu_prop->hop2_shift);
}
static inline u64 get_hop3_pte_addr(struct hl_ctx *ctx, u64 hop_addr, u64 vaddr)
static inline u64 get_hop3_pte_addr(struct hl_ctx *ctx,
struct hl_mmu_properties *mmu_prop,
u64 hop_addr, u64 vaddr)
{
return get_hopN_pte_addr(ctx, hop_addr, vaddr, HOP3_MASK, HOP3_SHIFT);
return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop3_mask,
mmu_prop->hop3_shift);
}
static inline u64 get_hop4_pte_addr(struct hl_ctx *ctx, u64 hop_addr, u64 vaddr)
static inline u64 get_hop4_pte_addr(struct hl_ctx *ctx,
struct hl_mmu_properties *mmu_prop,
u64 hop_addr, u64 vaddr)
{
return get_hopN_pte_addr(ctx, hop_addr, vaddr, HOP4_MASK, HOP4_SHIFT);
return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop4_mask,
mmu_prop->hop4_shift);
}
static inline u64 get_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte)
{
if (curr_pte & PAGE_PRESENT_MASK)
return curr_pte & PHYS_ADDR_MASK;
return curr_pte & HOP_PHYS_ADDR_MASK;
else
return ULLONG_MAX;
}
@ -288,23 +309,23 @@ static int dram_default_mapping_init(struct hl_ctx *ctx)
}
/* need only pte 0 in hops 0 and 1 */
pte_val = (hop1_addr & PTE_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
pte_val = (hop1_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
write_pte(ctx, hop0_addr, pte_val);
pte_val = (hop2_addr & PTE_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
pte_val = (hop2_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
write_pte(ctx, hop1_addr, pte_val);
get_pte(ctx, hop1_addr);
hop2_pte_addr = hop2_addr;
for (i = 0 ; i < num_of_hop3 ; i++) {
pte_val = (ctx->dram_default_hops[i] & PTE_PHYS_ADDR_MASK) |
pte_val = (ctx->dram_default_hops[i] & HOP_PHYS_ADDR_MASK) |
PAGE_PRESENT_MASK;
write_pte(ctx, hop2_pte_addr, pte_val);
get_pte(ctx, hop2_addr);
hop2_pte_addr += HL_PTE_SIZE;
}
pte_val = (prop->mmu_dram_default_page_addr & PTE_PHYS_ADDR_MASK) |
pte_val = (prop->mmu_dram_default_page_addr & HOP_PHYS_ADDR_MASK) |
LAST_MASK | PAGE_PRESENT_MASK;
for (i = 0 ; i < num_of_hop3 ; i++) {
@ -400,8 +421,6 @@ int hl_mmu_init(struct hl_device *hdev)
if (!hdev->mmu_enable)
return 0;
/* MMU H/W init was already done in device hw_init() */
hdev->mmu_pgt_pool =
gen_pool_create(__ffs(prop->mmu_hop_table_size), -1);
@ -427,6 +446,8 @@ int hl_mmu_init(struct hl_device *hdev)
goto err_pool_add;
}
/* MMU H/W init will be done in device hw_init() */
return 0;
err_pool_add:
@ -450,10 +471,10 @@ void hl_mmu_fini(struct hl_device *hdev)
if (!hdev->mmu_enable)
return;
/* MMU H/W fini was already done in device hw_fini() */
kvfree(hdev->mmu_shadow_hop0);
gen_pool_destroy(hdev->mmu_pgt_pool);
/* MMU H/W fini will be done in device hw_fini() */
}
/**
@ -501,36 +522,36 @@ void hl_mmu_ctx_fini(struct hl_ctx *ctx)
dram_default_mapping_fini(ctx);
if (!hash_empty(ctx->mmu_shadow_hash))
dev_err(hdev->dev, "ctx is freed while it has pgts in use\n");
dev_err(hdev->dev, "ctx %d is freed while it has pgts in use\n",
ctx->asid);
hash_for_each_safe(ctx->mmu_shadow_hash, i, tmp, pgt_info, node) {
dev_err(hdev->dev,
dev_err_ratelimited(hdev->dev,
"pgt_info of addr 0x%llx of asid %d was not destroyed, num_ptes: %d\n",
pgt_info->phys_addr, ctx->asid, pgt_info->num_of_ptes);
free_hop(ctx, pgt_info->shadow_addr);
_free_hop(ctx, pgt_info);
}
mutex_destroy(&ctx->mmu_lock);
}
static int _hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr)
static int _hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr, bool is_dram_addr)
{
struct hl_device *hdev = ctx->hdev;
struct asic_fixed_properties *prop = &hdev->asic_prop;
struct hl_mmu_properties *mmu_prop;
u64 hop0_addr = 0, hop0_pte_addr = 0,
hop1_addr = 0, hop1_pte_addr = 0,
hop2_addr = 0, hop2_pte_addr = 0,
hop3_addr = 0, hop3_pte_addr = 0,
hop4_addr = 0, hop4_pte_addr = 0,
curr_pte;
bool is_dram_addr, is_huge, clear_hop3 = true;
bool is_huge, clear_hop3 = true;
is_dram_addr = hl_mem_area_inside_range(virt_addr, PAGE_SIZE_2MB,
prop->va_space_dram_start_address,
prop->va_space_dram_end_address);
mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu;
hop0_addr = get_hop0_addr(ctx);
hop0_pte_addr = get_hop0_pte_addr(ctx, hop0_addr, virt_addr);
hop0_pte_addr = get_hop0_pte_addr(ctx, mmu_prop, hop0_addr, virt_addr);
curr_pte = *(u64 *) (uintptr_t) hop0_pte_addr;
@ -539,7 +560,7 @@ static int _hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr)
if (hop1_addr == ULLONG_MAX)
goto not_mapped;
hop1_pte_addr = get_hop1_pte_addr(ctx, hop1_addr, virt_addr);
hop1_pte_addr = get_hop1_pte_addr(ctx, mmu_prop, hop1_addr, virt_addr);
curr_pte = *(u64 *) (uintptr_t) hop1_pte_addr;
@ -548,7 +569,7 @@ static int _hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr)
if (hop2_addr == ULLONG_MAX)
goto not_mapped;
hop2_pte_addr = get_hop2_pte_addr(ctx, hop2_addr, virt_addr);
hop2_pte_addr = get_hop2_pte_addr(ctx, mmu_prop, hop2_addr, virt_addr);
curr_pte = *(u64 *) (uintptr_t) hop2_pte_addr;
@ -557,7 +578,7 @@ static int _hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr)
if (hop3_addr == ULLONG_MAX)
goto not_mapped;
hop3_pte_addr = get_hop3_pte_addr(ctx, hop3_addr, virt_addr);
hop3_pte_addr = get_hop3_pte_addr(ctx, mmu_prop, hop3_addr, virt_addr);
curr_pte = *(u64 *) (uintptr_t) hop3_pte_addr;
@ -575,7 +596,8 @@ static int _hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr)
if (hop4_addr == ULLONG_MAX)
goto not_mapped;
hop4_pte_addr = get_hop4_pte_addr(ctx, hop4_addr, virt_addr);
hop4_pte_addr = get_hop4_pte_addr(ctx, mmu_prop, hop4_addr,
virt_addr);
curr_pte = *(u64 *) (uintptr_t) hop4_pte_addr;
@ -584,7 +606,7 @@ static int _hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr)
if (hdev->dram_default_page_mapping && is_dram_addr) {
u64 default_pte = (prop->mmu_dram_default_page_addr &
PTE_PHYS_ADDR_MASK) | LAST_MASK |
HOP_PHYS_ADDR_MASK) | LAST_MASK |
PAGE_PRESENT_MASK;
if (curr_pte == default_pte) {
dev_err(hdev->dev,
@ -667,25 +689,36 @@ not_mapped:
int hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr, u32 page_size)
{
struct hl_device *hdev = ctx->hdev;
struct asic_fixed_properties *prop = &hdev->asic_prop;
struct hl_mmu_properties *mmu_prop;
u64 real_virt_addr;
u32 real_page_size, npages;
int i, rc;
bool is_dram_addr;
if (!hdev->mmu_enable)
return 0;
is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size,
prop->va_space_dram_start_address,
prop->va_space_dram_end_address);
mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu;
/*
* The H/W handles mapping of 4KB/2MB page. Hence if the host page size
* is bigger, we break it to sub-pages and unmap them separately.
* The H/W handles mapping of specific page sizes. Hence if the page
* size is bigger, we break it to sub-pages and unmap them separately.
*/
if ((page_size % PAGE_SIZE_2MB) == 0) {
real_page_size = PAGE_SIZE_2MB;
} else if ((page_size % PAGE_SIZE_4KB) == 0) {
real_page_size = PAGE_SIZE_4KB;
if ((page_size % mmu_prop->huge_page_size) == 0) {
real_page_size = mmu_prop->huge_page_size;
} else if ((page_size % mmu_prop->page_size) == 0) {
real_page_size = mmu_prop->page_size;
} else {
dev_err(hdev->dev,
"page size of %u is not 4KB nor 2MB aligned, can't unmap\n",
page_size);
"page size of %u is not %uKB nor %uMB aligned, can't unmap\n",
page_size,
mmu_prop->page_size >> 10,
mmu_prop->huge_page_size >> 20);
return -EFAULT;
}
@ -694,7 +727,7 @@ int hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr, u32 page_size)
real_virt_addr = virt_addr;
for (i = 0 ; i < npages ; i++) {
rc = _hl_mmu_unmap(ctx, real_virt_addr);
rc = _hl_mmu_unmap(ctx, real_virt_addr, is_dram_addr);
if (rc)
return rc;
@ -705,10 +738,11 @@ int hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr, u32 page_size)
}
static int _hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr,
u32 page_size)
u32 page_size, bool is_dram_addr)
{
struct hl_device *hdev = ctx->hdev;
struct asic_fixed_properties *prop = &hdev->asic_prop;
struct hl_mmu_properties *mmu_prop;
u64 hop0_addr = 0, hop0_pte_addr = 0,
hop1_addr = 0, hop1_pte_addr = 0,
hop2_addr = 0, hop2_pte_addr = 0,
@ -716,21 +750,19 @@ static int _hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr,
hop4_addr = 0, hop4_pte_addr = 0,
curr_pte = 0;
bool hop1_new = false, hop2_new = false, hop3_new = false,
hop4_new = false, is_huge, is_dram_addr;
hop4_new = false, is_huge;
int rc = -ENOMEM;
/*
* This mapping function can map a 4KB/2MB page. For 2MB page there are
* only 3 hops rather than 4. Currently the DRAM allocation uses 2MB
* pages only but user memory could have been allocated with one of the
* two page sizes. Since this is a common code for all the three cases,
* we need this hugs page check.
*/
is_huge = page_size == PAGE_SIZE_2MB;
mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu;
is_dram_addr = hl_mem_area_inside_range(virt_addr, page_size,
prop->va_space_dram_start_address,
prop->va_space_dram_end_address);
/*
* This mapping function can map a page or a huge page. For huge page
* there are only 3 hops rather than 4. Currently the DRAM allocation
* uses huge pages only but user memory could have been allocated with
* one of the two page sizes. Since this is a common code for all the
* three cases, we need this hugs page check.
*/
is_huge = page_size == mmu_prop->huge_page_size;
if (is_dram_addr && !is_huge) {
dev_err(hdev->dev, "DRAM mapping should use huge pages only\n");
@ -738,28 +770,28 @@ static int _hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr,
}
hop0_addr = get_hop0_addr(ctx);
hop0_pte_addr = get_hop0_pte_addr(ctx, hop0_addr, virt_addr);
hop0_pte_addr = get_hop0_pte_addr(ctx, mmu_prop, hop0_addr, virt_addr);
curr_pte = *(u64 *) (uintptr_t) hop0_pte_addr;
hop1_addr = get_alloc_next_hop_addr(ctx, curr_pte, &hop1_new);
if (hop1_addr == ULLONG_MAX)
goto err;
hop1_pte_addr = get_hop1_pte_addr(ctx, hop1_addr, virt_addr);
hop1_pte_addr = get_hop1_pte_addr(ctx, mmu_prop, hop1_addr, virt_addr);
curr_pte = *(u64 *) (uintptr_t) hop1_pte_addr;
hop2_addr = get_alloc_next_hop_addr(ctx, curr_pte, &hop2_new);
if (hop2_addr == ULLONG_MAX)
goto err;
hop2_pte_addr = get_hop2_pte_addr(ctx, hop2_addr, virt_addr);
hop2_pte_addr = get_hop2_pte_addr(ctx, mmu_prop, hop2_addr, virt_addr);
curr_pte = *(u64 *) (uintptr_t) hop2_pte_addr;
hop3_addr = get_alloc_next_hop_addr(ctx, curr_pte, &hop3_new);
if (hop3_addr == ULLONG_MAX)
goto err;
hop3_pte_addr = get_hop3_pte_addr(ctx, hop3_addr, virt_addr);
hop3_pte_addr = get_hop3_pte_addr(ctx, mmu_prop, hop3_addr, virt_addr);
curr_pte = *(u64 *) (uintptr_t) hop3_pte_addr;
if (!is_huge) {
@ -767,13 +799,14 @@ static int _hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr,
if (hop4_addr == ULLONG_MAX)
goto err;
hop4_pte_addr = get_hop4_pte_addr(ctx, hop4_addr, virt_addr);
hop4_pte_addr = get_hop4_pte_addr(ctx, mmu_prop, hop4_addr,
virt_addr);
curr_pte = *(u64 *) (uintptr_t) hop4_pte_addr;
}
if (hdev->dram_default_page_mapping && is_dram_addr) {
u64 default_pte = (prop->mmu_dram_default_page_addr &
PTE_PHYS_ADDR_MASK) | LAST_MASK |
HOP_PHYS_ADDR_MASK) | LAST_MASK |
PAGE_PRESENT_MASK;
if (curr_pte != default_pte) {
@ -813,7 +846,7 @@ static int _hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr,
goto err;
}
curr_pte = (phys_addr & PTE_PHYS_ADDR_MASK) | LAST_MASK
curr_pte = (phys_addr & HOP_PHYS_ADDR_MASK) | LAST_MASK
| PAGE_PRESENT_MASK;
if (is_huge)
@ -823,25 +856,25 @@ static int _hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr,
if (hop1_new) {
curr_pte =
(hop1_addr & PTE_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
(hop1_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
write_pte(ctx, hop0_pte_addr, curr_pte);
}
if (hop2_new) {
curr_pte =
(hop2_addr & PTE_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
(hop2_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
write_pte(ctx, hop1_pte_addr, curr_pte);
get_pte(ctx, hop1_addr);
}
if (hop3_new) {
curr_pte =
(hop3_addr & PTE_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
(hop3_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
write_pte(ctx, hop2_pte_addr, curr_pte);
get_pte(ctx, hop2_addr);
}
if (!is_huge) {
if (hop4_new) {
curr_pte = (hop4_addr & PTE_PHYS_ADDR_MASK) |
curr_pte = (hop4_addr & HOP_PHYS_ADDR_MASK) |
PAGE_PRESENT_MASK;
write_pte(ctx, hop3_pte_addr, curr_pte);
get_pte(ctx, hop3_addr);
@ -890,25 +923,36 @@ err:
int hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_size)
{
struct hl_device *hdev = ctx->hdev;
struct asic_fixed_properties *prop = &hdev->asic_prop;
struct hl_mmu_properties *mmu_prop;
u64 real_virt_addr, real_phys_addr;
u32 real_page_size, npages;
int i, rc, mapped_cnt = 0;
bool is_dram_addr;
if (!hdev->mmu_enable)
return 0;
is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size,
prop->va_space_dram_start_address,
prop->va_space_dram_end_address);
mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu;
/*
* The H/W handles mapping of 4KB/2MB page. Hence if the host page size
* is bigger, we break it to sub-pages and map them separately.
* The H/W handles mapping of specific page sizes. Hence if the page
* size is bigger, we break it to sub-pages and map them separately.
*/
if ((page_size % PAGE_SIZE_2MB) == 0) {
real_page_size = PAGE_SIZE_2MB;
} else if ((page_size % PAGE_SIZE_4KB) == 0) {
real_page_size = PAGE_SIZE_4KB;
if ((page_size % mmu_prop->huge_page_size) == 0) {
real_page_size = mmu_prop->huge_page_size;
} else if ((page_size % mmu_prop->page_size) == 0) {
real_page_size = mmu_prop->page_size;
} else {
dev_err(hdev->dev,
"page size of %u is not 4KB nor 2MB aligned, can't map\n",
page_size);
"page size of %u is not %dKB nor %dMB aligned, can't unmap\n",
page_size,
mmu_prop->page_size >> 10,
mmu_prop->huge_page_size >> 20);
return -EFAULT;
}
@ -923,7 +967,7 @@ int hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_size)
for (i = 0 ; i < npages ; i++) {
rc = _hl_mmu_map(ctx, real_virt_addr, real_phys_addr,
real_page_size);
real_page_size, is_dram_addr);
if (rc)
goto err;
@ -937,7 +981,7 @@ int hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_size)
err:
real_virt_addr = virt_addr;
for (i = 0 ; i < mapped_cnt ; i++) {
if (_hl_mmu_unmap(ctx, real_virt_addr))
if (_hl_mmu_unmap(ctx, real_virt_addr, is_dram_addr))
dev_warn_ratelimited(hdev->dev,
"failed to unmap va: 0x%llx\n", real_virt_addr);

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* linux/drivers/char/hpilo.h
*

View File

@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0+
*
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* linux/drivers/misc/ibmvmc.h
*
* IBM Power Systems Virtual Management Channel Support.

View File

@ -16,7 +16,7 @@
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/input-polldev.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/poll.h>
@ -434,23 +434,23 @@ int lis3lv02d_poweron(struct lis3lv02d *lis3)
EXPORT_SYMBOL_GPL(lis3lv02d_poweron);
static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev)
static void lis3lv02d_joystick_poll(struct input_dev *input)
{
struct lis3lv02d *lis3 = pidev->private;
struct lis3lv02d *lis3 = input_get_drvdata(input);
int x, y, z;
mutex_lock(&lis3->mutex);
lis3lv02d_get_xyz(lis3, &x, &y, &z);
input_report_abs(pidev->input, ABS_X, x);
input_report_abs(pidev->input, ABS_Y, y);
input_report_abs(pidev->input, ABS_Z, z);
input_sync(pidev->input);
input_report_abs(input, ABS_X, x);
input_report_abs(input, ABS_Y, y);
input_report_abs(input, ABS_Z, z);
input_sync(input);
mutex_unlock(&lis3->mutex);
}
static void lis3lv02d_joystick_open(struct input_polled_dev *pidev)
static int lis3lv02d_joystick_open(struct input_dev *input)
{
struct lis3lv02d *lis3 = pidev->private;
struct lis3lv02d *lis3 = input_get_drvdata(input);
if (lis3->pm_dev)
pm_runtime_get_sync(lis3->pm_dev);
@ -461,12 +461,14 @@ static void lis3lv02d_joystick_open(struct input_polled_dev *pidev)
* Update coordinates for the case where poll interval is 0 and
* the chip in running purely under interrupt control
*/
lis3lv02d_joystick_poll(pidev);
lis3lv02d_joystick_poll(input);
return 0;
}
static void lis3lv02d_joystick_close(struct input_polled_dev *pidev)
static void lis3lv02d_joystick_close(struct input_dev *input)
{
struct lis3lv02d *lis3 = pidev->private;
struct lis3lv02d *lis3 = input_get_drvdata(input);
atomic_set(&lis3->wake_thread, 0);
if (lis3->pm_dev)
@ -497,7 +499,7 @@ out:
static void lis302dl_interrupt_handle_click(struct lis3lv02d *lis3)
{
struct input_dev *dev = lis3->idev->input;
struct input_dev *dev = lis3->idev;
u8 click_src;
mutex_lock(&lis3->mutex);
@ -677,26 +679,19 @@ int lis3lv02d_joystick_enable(struct lis3lv02d *lis3)
if (lis3->idev)
return -EINVAL;
lis3->idev = input_allocate_polled_device();
if (!lis3->idev)
input_dev = input_allocate_device();
if (!input_dev)
return -ENOMEM;
lis3->idev->poll = lis3lv02d_joystick_poll;
lis3->idev->open = lis3lv02d_joystick_open;
lis3->idev->close = lis3lv02d_joystick_close;
lis3->idev->poll_interval = MDPS_POLL_INTERVAL;
lis3->idev->poll_interval_min = MDPS_POLL_MIN;
lis3->idev->poll_interval_max = MDPS_POLL_MAX;
lis3->idev->private = lis3;
input_dev = lis3->idev->input;
input_dev->name = "ST LIS3LV02DL Accelerometer";
input_dev->phys = DRIVER_NAME "/input0";
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0;
input_dev->dev.parent = &lis3->pdev->dev;
set_bit(EV_ABS, input_dev->evbit);
input_dev->open = lis3lv02d_joystick_open;
input_dev->close = lis3lv02d_joystick_close;
max_val = (lis3->mdps_max_val * lis3->scale) / LIS3_ACCURACY;
if (lis3->whoami == WAI_12B) {
fuzz = LIS3_DEFAULT_FUZZ_12B;
@ -712,17 +707,32 @@ int lis3lv02d_joystick_enable(struct lis3lv02d *lis3)
input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat);
input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat);
input_set_drvdata(input_dev, lis3);
lis3->idev = input_dev;
err = input_setup_polling(input_dev, lis3lv02d_joystick_poll);
if (err)
goto err_free_input;
input_set_poll_interval(input_dev, MDPS_POLL_INTERVAL);
input_set_min_poll_interval(input_dev, MDPS_POLL_MIN);
input_set_max_poll_interval(input_dev, MDPS_POLL_MAX);
lis3->mapped_btns[0] = lis3lv02d_get_axis(abs(lis3->ac.x), btns);
lis3->mapped_btns[1] = lis3lv02d_get_axis(abs(lis3->ac.y), btns);
lis3->mapped_btns[2] = lis3lv02d_get_axis(abs(lis3->ac.z), btns);
err = input_register_polled_device(lis3->idev);
if (err) {
input_free_polled_device(lis3->idev);
lis3->idev = NULL;
}
err = input_register_device(lis3->idev);
if (err)
goto err_free_input;
return 0;
err_free_input:
input_free_device(input_dev);
lis3->idev = NULL;
return err;
}
EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable);
@ -738,8 +748,7 @@ void lis3lv02d_joystick_disable(struct lis3lv02d *lis3)
if (lis3->irq)
misc_deregister(&lis3->miscdev);
input_unregister_polled_device(lis3->idev);
input_free_polled_device(lis3->idev);
input_unregister_device(lis3->idev);
lis3->idev = NULL;
}
EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable);
@ -895,10 +904,9 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *lis3,
(p->click_thresh_y << 4));
if (lis3->idev) {
struct input_dev *input_dev = lis3->idev->input;
input_set_capability(input_dev, EV_KEY, BTN_X);
input_set_capability(input_dev, EV_KEY, BTN_Y);
input_set_capability(input_dev, EV_KEY, BTN_Z);
input_set_capability(lis3->idev, EV_KEY, BTN_X);
input_set_capability(lis3->idev, EV_KEY, BTN_Y);
input_set_capability(lis3->idev, EV_KEY, BTN_Z);
}
}

View File

@ -6,7 +6,7 @@
* Copyright (C) 2008-2009 Eric Piel
*/
#include <linux/platform_device.h>
#include <linux/input-polldev.h>
#include <linux/input.h>
#include <linux/regulator/consumer.h>
#include <linux/miscdevice.h>
@ -281,7 +281,7 @@ struct lis3lv02d {
* (1/1000th of earth gravity)
*/
struct input_polled_dev *idev; /* input device */
struct input_dev *idev; /* input device */
struct platform_device *pdev; /* platform device */
struct regulator_bulk_data regulators[2];
atomic_t count; /* interrupt count after last read */

View File

@ -46,8 +46,6 @@ static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO;
*/
static void number_of_connections(struct mei_cl_device *cldev)
{
dev_dbg(&cldev->dev, "running hook %s\n", __func__);
if (cldev->me_cl->props.max_number_of_connections > 1)
cldev->do_match = 0;
}
@ -59,8 +57,6 @@ static void number_of_connections(struct mei_cl_device *cldev)
*/
static void blacklist(struct mei_cl_device *cldev)
{
dev_dbg(&cldev->dev, "running hook %s\n", __func__);
cldev->do_match = 0;
}
@ -71,8 +67,6 @@ static void blacklist(struct mei_cl_device *cldev)
*/
static void whitelist(struct mei_cl_device *cldev)
{
dev_dbg(&cldev->dev, "running hook %s\n", __func__);
cldev->do_match = 1;
}
@ -256,7 +250,6 @@ static void mei_wd(struct mei_cl_device *cldev)
{
struct pci_dev *pdev = to_pci_dev(cldev->dev.parent);
dev_dbg(&cldev->dev, "running hook %s\n", __func__);
if (pdev->device == MEI_DEV_ID_WPT_LP ||
pdev->device == MEI_DEV_ID_SPT ||
pdev->device == MEI_DEV_ID_SPT_H)
@ -410,8 +403,6 @@ static void mei_nfc(struct mei_cl_device *cldev)
bus = cldev->bus;
dev_dbg(&cldev->dev, "running hook %s\n", __func__);
mutex_lock(&bus->device_lock);
/* we need to connect to INFO GUID */
cl = mei_cl_alloc_linked(bus);

View File

@ -791,11 +791,44 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
}
static DEVICE_ATTR_RO(modalias);
static ssize_t max_conn_show(struct device *dev, struct device_attribute *a,
char *buf)
{
struct mei_cl_device *cldev = to_mei_cl_device(dev);
u8 maxconn = mei_me_cl_max_conn(cldev->me_cl);
return scnprintf(buf, PAGE_SIZE, "%d", maxconn);
}
static DEVICE_ATTR_RO(max_conn);
static ssize_t fixed_show(struct device *dev, struct device_attribute *a,
char *buf)
{
struct mei_cl_device *cldev = to_mei_cl_device(dev);
u8 fixed = mei_me_cl_fixed(cldev->me_cl);
return scnprintf(buf, PAGE_SIZE, "%d", fixed);
}
static DEVICE_ATTR_RO(fixed);
static ssize_t max_len_show(struct device *dev, struct device_attribute *a,
char *buf)
{
struct mei_cl_device *cldev = to_mei_cl_device(dev);
u32 maxlen = mei_me_cl_max_len(cldev->me_cl);
return scnprintf(buf, PAGE_SIZE, "%u", maxlen);
}
static DEVICE_ATTR_RO(max_len);
static struct attribute *mei_cldev_attrs[] = {
&dev_attr_name.attr,
&dev_attr_uuid.attr,
&dev_attr_version.attr,
&dev_attr_modalias.attr,
&dev_attr_max_conn.attr,
&dev_attr_fixed.attr,
&dev_attr_max_len.attr,
NULL,
};
ATTRIBUTE_GROUPS(mei_cldev);
@ -873,15 +906,16 @@ static const struct device_type mei_cl_device_type = {
/**
* mei_cl_bus_set_name - set device name for me client device
* <controller>-<client device>
* Example: 0000:00:16.0-55213584-9a29-4916-badf-0fb7ed682aeb
*
* @cldev: me client device
*/
static inline void mei_cl_bus_set_name(struct mei_cl_device *cldev)
{
dev_set_name(&cldev->dev, "mei:%s:%pUl:%02X",
cldev->name,
mei_me_cl_uuid(cldev->me_cl),
mei_me_cl_ver(cldev->me_cl));
dev_set_name(&cldev->dev, "%s-%pUl",
dev_name(cldev->bus->dev),
mei_me_cl_uuid(cldev->me_cl));
}
/**

View File

@ -69,6 +69,42 @@ static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl)
return me_cl->props.protocol_version;
}
/**
* mei_me_cl_max_conn - return me client max number of connections
*
* @me_cl: me client
*
* Return: me client max number of connections
*/
static inline u8 mei_me_cl_max_conn(const struct mei_me_client *me_cl)
{
return me_cl->props.max_number_of_connections;
}
/**
* mei_me_cl_fixed - return me client fixed address, if any
*
* @me_cl: me client
*
* Return: me client fixed address
*/
static inline u8 mei_me_cl_fixed(const struct mei_me_client *me_cl)
{
return me_cl->props.fixed_address;
}
/**
* mei_me_cl_max_len - return me client max msg length
*
* @me_cl: me client
*
* Return: me client max msg length
*/
static inline u32 mei_me_cl_max_len(const struct mei_me_client *me_cl)
{
return me_cl->props.max_msg_length;
}
/*
* MEI IO Functions
*/

View File

@ -81,6 +81,7 @@
#define MEI_DEV_ID_CMP_LP 0x02e0 /* Comet Point LP */
#define MEI_DEV_ID_CMP_LP_3 0x02e4 /* Comet Point LP 3 (iTouch) */
#define MEI_DEV_ID_CMP_V 0xA3BA /* Comet Point Lake V */
#define MEI_DEV_ID_ICP_LP 0x34E0 /* Ice Lake Point LP */
@ -162,7 +163,8 @@ access to ME_CBD */
#define ME_IS_HRA 0x00000002
/* ME Interrupt Enable HRA - host read only access to ME_IE */
#define ME_IE_HRA 0x00000001
/* TRC control shadow register */
#define ME_TRC 0x00000030
/* H_HPG_CSR register bits */
#define H_HPG_CSR_PGIHEXR 0x00000001

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2003-2018, Intel Corporation. All rights reserved.
* Copyright (c) 2003-2019, Intel Corporation. All rights reserved.
* Intel Management Engine Interface (Intel MEI) Linux driver
*/
@ -172,6 +172,27 @@ static inline void mei_me_d0i3c_write(struct mei_device *dev, u32 reg)
mei_me_reg_write(to_me_hw(dev), H_D0I3C, reg);
}
/**
* mei_me_trc_status - read trc status register
*
* @dev: mei device
* @trc: trc status register value
*
* Return: 0 on success, error otherwise
*/
static int mei_me_trc_status(struct mei_device *dev, u32 *trc)
{
struct mei_me_hw *hw = to_me_hw(dev);
if (!hw->cfg->hw_trc_supported)
return -EOPNOTSUPP;
*trc = mei_me_reg_read(hw, ME_TRC);
trace_mei_reg_read(dev->dev, "ME_TRC", ME_TRC, *trc);
return 0;
}
/**
* mei_me_fw_status - read fw status register from pci config space
*
@ -183,20 +204,19 @@ static inline void mei_me_d0i3c_write(struct mei_device *dev, u32 reg)
static int mei_me_fw_status(struct mei_device *dev,
struct mei_fw_status *fw_status)
{
struct pci_dev *pdev = to_pci_dev(dev->dev);
struct mei_me_hw *hw = to_me_hw(dev);
const struct mei_fw_status *fw_src = &hw->cfg->fw_status;
int ret;
int i;
if (!fw_status)
if (!fw_status || !hw->read_fws)
return -EINVAL;
fw_status->count = fw_src->count;
for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) {
ret = pci_read_config_dword(pdev, fw_src->status[i],
&fw_status->status[i]);
trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HSF_X",
ret = hw->read_fws(dev, fw_src->status[i],
&fw_status->status[i]);
trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_X",
fw_src->status[i],
fw_status->status[i]);
if (ret)
@ -210,19 +230,26 @@ static int mei_me_fw_status(struct mei_device *dev,
* mei_me_hw_config - configure hw dependent settings
*
* @dev: mei device
*
* Return:
* * -EINVAL when read_fws is not set
* * 0 on success
*
*/
static void mei_me_hw_config(struct mei_device *dev)
static int mei_me_hw_config(struct mei_device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev->dev);
struct mei_me_hw *hw = to_me_hw(dev);
u32 hcsr, reg;
if (WARN_ON(!hw->read_fws))
return -EINVAL;
/* Doesn't change in runtime */
hcsr = mei_hcsr_read(dev);
hw->hbuf_depth = (hcsr & H_CBD) >> 24;
reg = 0;
pci_read_config_dword(pdev, PCI_CFG_HFS_1, &reg);
hw->read_fws(dev, PCI_CFG_HFS_1, &reg);
trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg);
hw->d0i3_supported =
((reg & PCI_CFG_HFS_1_D0I3_MSK) == PCI_CFG_HFS_1_D0I3_MSK);
@ -233,6 +260,8 @@ static void mei_me_hw_config(struct mei_device *dev)
if (reg & H_D0I3C_I3)
hw->pg_state = MEI_PG_ON;
}
return 0;
}
/**
@ -269,7 +298,7 @@ static inline void me_intr_disable(struct mei_device *dev, u32 hcsr)
}
/**
* mei_me_intr_clear - clear and stop interrupts
* me_intr_clear - clear and stop interrupts
*
* @dev: the device structure
* @hcsr: supplied hcsr register value
@ -323,9 +352,9 @@ static void mei_me_intr_disable(struct mei_device *dev)
*/
static void mei_me_synchronize_irq(struct mei_device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev->dev);
struct mei_me_hw *hw = to_me_hw(dev);
synchronize_irq(pdev->irq);
synchronize_irq(hw->irq);
}
/**
@ -1294,6 +1323,7 @@ end:
static const struct mei_hw_ops mei_me_hw_ops = {
.trc_status = mei_me_trc_status,
.fw_status = mei_me_fw_status,
.pg_state = mei_me_pg_state,
@ -1384,6 +1414,9 @@ static bool mei_me_fw_type_sps(struct pci_dev *pdev)
.dma_size[DMA_DSCR_DEVICE] = SZ_128K, \
.dma_size[DMA_DSCR_CTRL] = PAGE_SIZE
#define MEI_CFG_TRC \
.hw_trc_supported = 1
/* ICH Legacy devices */
static const struct mei_cfg mei_me_ich_cfg = {
MEI_CFG_ICH_HFS,
@ -1432,6 +1465,14 @@ static const struct mei_cfg mei_me_pch12_cfg = {
MEI_CFG_DMA_128,
};
/* Tiger Lake and newer devices */
static const struct mei_cfg mei_me_pch15_cfg = {
MEI_CFG_PCH8_HFS,
MEI_CFG_FW_VER_SUPP,
MEI_CFG_DMA_128,
MEI_CFG_TRC,
};
/*
* mei_cfg_list - A list of platform platform specific configurations.
* Note: has to be synchronized with enum mei_cfg_idx.
@ -1446,6 +1487,7 @@ static const struct mei_cfg *const mei_cfg_list[] = {
[MEI_ME_PCH8_CFG] = &mei_me_pch8_cfg,
[MEI_ME_PCH8_SPS_CFG] = &mei_me_pch8_sps_cfg,
[MEI_ME_PCH12_CFG] = &mei_me_pch12_cfg,
[MEI_ME_PCH15_CFG] = &mei_me_pch15_cfg,
};
const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx)
@ -1461,19 +1503,19 @@ const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx)
/**
* mei_me_dev_init - allocates and initializes the mei device structure
*
* @pdev: The pci device structure
* @parent: device associated with physical device (pci/platform)
* @cfg: per device generation config
*
* Return: The mei_device pointer on success, NULL on failure.
*/
struct mei_device *mei_me_dev_init(struct pci_dev *pdev,
struct mei_device *mei_me_dev_init(struct device *parent,
const struct mei_cfg *cfg)
{
struct mei_device *dev;
struct mei_me_hw *hw;
int i;
dev = devm_kzalloc(&pdev->dev, sizeof(struct mei_device) +
dev = devm_kzalloc(parent, sizeof(struct mei_device) +
sizeof(struct mei_me_hw), GFP_KERNEL);
if (!dev)
return NULL;
@ -1483,7 +1525,7 @@ struct mei_device *mei_me_dev_init(struct pci_dev *pdev,
for (i = 0; i < DMA_DSCR_NUM; i++)
dev->dr_dscr[i].size = cfg->dma_size[i];
mei_device_init(dev, &pdev->dev, &mei_me_hw_ops);
mei_device_init(dev, parent, &mei_me_hw_ops);
hw->cfg = cfg;
dev->fw_f_fw_ver_supported = cfg->fw_ver_supported;

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2012-2018, Intel Corporation. All rights reserved.
* Copyright (c) 2012-2019, Intel Corporation. All rights reserved.
* Intel Management Engine Interface (Intel MEI) Linux driver
*/
@ -21,12 +21,14 @@
* @quirk_probe: device exclusion quirk
* @dma_size: device DMA buffers size
* @fw_ver_supported: is fw version retrievable from FW
* @hw_trc_supported: does the hw support trc register
*/
struct mei_cfg {
const struct mei_fw_status fw_status;
bool (*quirk_probe)(struct pci_dev *pdev);
size_t dma_size[DMA_DSCR_NUM];
u32 fw_ver_supported:1;
u32 hw_trc_supported:1;
};
@ -42,16 +44,20 @@ struct mei_cfg {
*
* @cfg: per device generation config and ops
* @mem_addr: io memory address
* @irq: irq number
* @pg_state: power gating state
* @d0i3_supported: di03 support
* @hbuf_depth: depth of hardware host/write buffer in slots
* @read_fws: read FW status register handler
*/
struct mei_me_hw {
const struct mei_cfg *cfg;
void __iomem *mem_addr;
int irq;
enum mei_pg_state pg_state;
bool d0i3_supported;
u8 hbuf_depth;
int (*read_fws)(const struct mei_device *dev, int where, u32 *val);
};
#define to_me_hw(dev) (struct mei_me_hw *)((dev)->hw)
@ -74,6 +80,7 @@ struct mei_me_hw {
* servers platforms with quirk for
* SPS firmware exclusion.
* @MEI_ME_PCH12_CFG: Platform Controller Hub Gen12 and newer
* @MEI_ME_PCH15_CFG: Platform Controller Hub Gen15 and newer
* @MEI_ME_NUM_CFG: Upper Sentinel.
*/
enum mei_cfg_idx {
@ -86,12 +93,13 @@ enum mei_cfg_idx {
MEI_ME_PCH8_CFG,
MEI_ME_PCH8_SPS_CFG,
MEI_ME_PCH12_CFG,
MEI_ME_PCH15_CFG,
MEI_ME_NUM_CFG,
};
const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx);
struct mei_device *mei_me_dev_init(struct pci_dev *pdev,
struct mei_device *mei_me_dev_init(struct device *parent,
const struct mei_cfg *cfg);
int mei_me_pg_enter_sync(struct mei_device *dev);

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2013-2014, Intel Corporation. All rights reserved.
* Copyright (c) 2013-2019, Intel Corporation. All rights reserved.
* Intel Management Engine Interface (Intel MEI) Linux driver
*/
@ -660,14 +660,16 @@ static int mei_txe_fw_status(struct mei_device *dev,
}
/**
* mei_txe_hw_config - configure hardware at the start of the devices
* mei_txe_hw_config - configure hardware at the start of the devices
*
* @dev: the device structure
*
* Configure hardware at the start of the device should be done only
* once at the device probe time
*
* Return: always 0
*/
static void mei_txe_hw_config(struct mei_device *dev)
static int mei_txe_hw_config(struct mei_device *dev)
{
struct mei_txe_hw *hw = to_txe_hw(dev);
@ -677,6 +679,8 @@ static void mei_txe_hw_config(struct mei_device *dev)
dev_dbg(dev->dev, "aliveness_resp = 0x%08x, readiness = 0x%08x.\n",
hw->aliveness, hw->readiness);
return 0;
}
/**

Some files were not shown because too many files have changed in this diff Show More