This patchset adds support for Ultrasoc Trace Module on Hip08 and Hip09 platform. It includes core layer framework, AXI Communicator(AXI-COM) and System Memory Buffer(SMB).
Qi Liu (4): Documentation: tracing: Documentation for ultrasoc framework and drivers ultrasoc: add ultrasoc core layer framework ultrasoc: Add ultrasoc AXI Communicator driver ultrasoc: Add System Memory Buffer driver
Documentation/trace/ultrasoc-trace.rst | 209 ++++++++ MAINTAINERS | 7 + drivers/Makefile | 1 + drivers/hwtracing/Kconfig | 2 + drivers/hwtracing/ultrasoc/Kconfig | 34 ++ drivers/hwtracing/ultrasoc/Makefile | 13 + drivers/hwtracing/ultrasoc/ultrasoc-axi-com.c | 334 +++++++++++++ drivers/hwtracing/ultrasoc/ultrasoc-axi-com.h | 66 +++ drivers/hwtracing/ultrasoc/ultrasoc-smb.c | 663 ++++++++++++++++++++++++++ drivers/hwtracing/ultrasoc/ultrasoc-smb.h | 182 +++++++ drivers/hwtracing/ultrasoc/ultrasoc.c | 518 ++++++++++++++++++++ drivers/hwtracing/ultrasoc/ultrasoc.h | 168 +++++++ 12 files changed, 2197 insertions(+) create mode 100644 Documentation/trace/ultrasoc-trace.rst create mode 100644 drivers/hwtracing/ultrasoc/Kconfig create mode 100644 drivers/hwtracing/ultrasoc/Makefile create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-axi-com.c create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-axi-com.h create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-smb.c create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-smb.h create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc.c create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc.h
Ultrasoc trace module is a system level solution for both core tracing and SoC tracing. This patch brings in a documentation for ultrasoc framework and drivers. It simply introduces function of ultrasoc, a typical Ultrasoc system, and a driver framework for ultrasoc.
Signed-off-by: Jonathan Zhou jonathan.zhouwen@huawei.com Signed-off-by: Qi Liu liuqi115@huawei.com --- Documentation/trace/ultrasoc-trace.rst | 209 +++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 Documentation/trace/ultrasoc-trace.rst
diff --git a/Documentation/trace/ultrasoc-trace.rst b/Documentation/trace/ultrasoc-trace.rst new file mode 100644 index 0000000..36d2df2 --- /dev/null +++ b/Documentation/trace/ultrasoc-trace.rst @@ -0,0 +1,209 @@ +======================================================= +Siemens Embedded Analytics - HW Assisted Tracing on SoC +======================================================= + :Author: Jonathan Zhou Jonathan.zhouwen@huawei.com + Qi Liu liuqi115@huawei.com + :Date: January 16th, 2021 + +Introduction +------------ + +The Siemens Embedded Analytics Framework is system level solution for tracing +of multiple type SoC, this document is concerned with trace module. This module +has two main components: AXI Bus Communicator and System Memory Buffer. + +The AXI Communicator has upstream and downstream channels, the upstream channel +is used to transmit user configuration, and downstream channel to carry response +and trace data to the users. + +The System Memory Buffer provides a way to buffer and store messages in system +memory. It provides a capability to store messages received on its input message +interface to an area of system memory. + +A typical Siemens trace system would look like the following diagram: + @@@@@@@@@@@@@ + @ CPU @ + @@@@@@@@@@@@@ + ############# + # Coresight # + # ETM # + ####### + ### + # + | + * + ******************************* + *** AMBA Advanced Trace Bus (ATB) *** + ***************^*************** + =============== | + === FUNNEL ==<--- | + ======= + | + * + @@@@@@@ + @ TRC @ + @@@@@ + @@@ + @ + | + * + ************************************** ******************* + ************************ Message BUS *************************** + ******************^************************^**************** + | | + @@@@@@@@@@@@@@@@@@ | @@@@@@@@@@@@ + @ Message Engine @ | @ JTAG COM @ + @@@@@@@@@@@@@@@@@@ | @@@@@@@@@@ + | * |---> @@@@@@@@ + | | @@@@@@ + @@@@@@@ | @@@@@@@@@@@ | + @ SMB @ | @ AXI COM @ JTAG + @@@@@ |--> @@@@@@@@@ + @@@--| @@@@@@@ + @ | @@@@@ + | | + | | + * * + *************************************************************** + **************************** AMBA AXI **************************** + ***************************************************************** + +Acronyms +--------------------------- + +Acronyms: + +AXI-COM: AXI Communicator +SMB: System Memory Buffer +TRC: Trace receiver + +Framework and implementation +------------------------------ + +Siemens Embedded Analytics Framework is implemented as a platform device. The +platform device provides a global point to configure the Embedded Analytics +subsystem, and also provides a ``struct ultrasoc_com`` to manage AXI-COM and +SMB. + +AXI-COM and SMB are implemented as platform devices, each SCCL has one AXI-COM +device and one SMB device. AXI-COM and SMB can use the following API to register +into Embedded Analytics framework: +.. c:function:: struct ultrasoc_com *ultrasoc_register_com(struct device *root_dev, struct ultrasoc_com_descp *com_descp) +.. c:function:: void ultrasoc_unregister_com(struct ultrasoc_com *com); + +As TRC receives data from coresight ETM device, SMB can use the following API +to register into coresight framework as a sink device: +.. c:function:: struct coresight_device *coresight_register(struct coresight_desc *desc); +.. c:function:: void coresight_unregister(struct coresight_device *csdev); + +Then users can get trace data by this path: ETM->funnel->SMB->System Memory. +More information about coresight framework can be found in +Documention/trace/coresight/coresight.rst. + +If everything goes well, the relationship of Embedded Analytics devices will be +described under the sysfs:: + + $# ls /sys/bus/platform/devices/ + <HID.Embedded Analytics>:00 <HID.axi-com>:00 <HID.smb>:00 + $# ls /sys/bus/platform/devices/<HID.Embedded Analytics>:00 + com_mux firmware_node power <HID.axi-com>:00 + driver message subsystem <HID.smb>:00 + driver_override modalias uevent + $# ls /sys/bus/coresight/devices/ + etm0 etm14 etm2 etm25 etm30 etm8 funnel4 + etm1 etm15 etm20 etm26 etm31 etm9 funnel5 + etm10 etm16 etm21 etm27 etm4 funnel0 funnel6 + etm11 etm17 etm22 etm28 etm5 funnel1 funnel7 + etm12 etm18 etm23 etm29 etm6 funnel2 sink_smb0 + etm13 etm19 etm24 etm3 etm7 funnel3 + $# ls -l /sys/bus/coresight/devices/funnel0/connections/ + <file details> in:0 -> ../../../../system/cpu/cpu0/ARMHC500:00/etm0 + <file details> in:1 -> ../../../../system/cpu/cpu1/ARMHC500:01/etm1 + <file details> in:2 -> ../../../../system/cpu/cpu2/ARMHC500:02/etm2 + <file details> in:3 -> ../../../../system/cpu/cpu3/ARMHC500:03/etm3 + <file details> nr_links + <file details> out:0 -> ../../../HISI0391:00/HISI03A1:00/sink_smb0 + $# ls -l /sys/bus/coresight/devices/sink_smb0/connections/ + <file details> in:101 -> ../../../../ARMHC9FE:05/funnel5 + <file details> in:114 -> ../../../../ARMHC9FE:07/funnel7 + <file details> in:121 -> ../../../../ARMHC9FE:03/funnel3 + <file details> in:39 -> ../../../../ARMHC9FE:00/funnel0 + <file details> in:51 -> ../../../../ARMHC9FE:04/funnel4 + <file details> in:61 -> ../../../../ARMHC9FE:06/funnel6 + <file details> in:68 -> ../../../../ARMHC9FE:02/funnel2 + <file details> in:89 -> ../../../../ARMHC9FE:01/funnel1 + <file details> nr_links + +How to use the Embedded Analytics trace module +----------------------------------------------- + +There are two ways to use the Embedded Analytics trace module: + +1. interacting directly with the devices using the sysFS interface. +2. using the perf cmd line tools. + +1) Using the sysFS interface: + +Before trace collection can start, a coresight sink needs to be identified. +There is no limit on the amount of sinks (nor sources) that can be enabled at +any given moment. As a generic operation, all device pertaining to the sink +class will have an "active" entry in sysfs:: + + $# ls /sys/bus/coresight/devices/ + etm0 etm14 etm2 etm25 etm30 etm8 funnel4 + etm1 etm15 etm20 etm26 etm31 etm9 funnel5 + etm10 etm16 etm21 etm27 etm4 funnel0 funnel6 + etm11 etm17 etm22 etm28 etm5 funnel1 funnel7 + etm12 etm18 etm23 etm29 etm6 funnel2 sink_smb0 + etm13 etm19 etm24 etm3 etm7 funnel3 + $# ls /sys/bus/coresight/devices/sink_smb0 + connections enable_sink firmware_node power subsystem uevent + $# echo 1 > /sys/bus/coresight/devices/sink_smb0/enable_sink + $# cat /sys/bus/coresight/devices/sink_smb0/enable_sink + 1 + +When start trace collection, etm devices corresponding to the enabled sink +should be selected:: + + $# echo 1 > /sys/bus/coresight/devices/etm0/enable_source + $# cat /sys/bus/coresight/devices/etm0/enable_source + 1 + $# cat /sys/bus/platform/devices/<HID.smb>:00/com_status + com-type : DOWN-ONLY + service status : stopped + interrupt status : 0x00000003 + write point : 0x5437f400 <----- The write pointer is moving + +Trace collection is stopped the same way:: + + $# echo 0 > /sys/bus/coresight/devices/etm0/enable_source + $# echo 0 > /sys/bus/coresight/devices/sink_smb0/enable_sink + +The content of the SMB buffer can be harvested directly from /dev:: + + $# dd if=/dev/sink_smb0 of=~/cstrace.bin + 5233+0 records in + 5233+0 records out + 2679296 bytes (2.7 MB) copied, 0.0131708 s, 203 MB/s + + root:/sys/bus/coresight/devices# + +The file cstrace.bin can be decompressed using "ptm2human". + +2) Using perf framework: + +As SMB device has been registered with coresight framework, perf tool can be +used to control Embedded Analytics trace collection, and the method is similar +to using perf to do coresight trace collection. + +The only thing to note is, list of cpus should be correspond to the specified +sink device. + +Example usage of perf:: + + $# ./perf list pmu + cs_etm// [Kernel PMU event] + $# ./perf record -e cs_etm/@sink_smb0/ -C 0 --per-thread sleep 2s + [ perf record: Woken up 2 times to write data ] + [ perf record: Captured and wrote 0.288 MB perf.data ] + $# ./perf report
Hi,
On Tue, Jun 15, 2021 at 05:34:41PM +0800, Qi Liu wrote:
Ultrasoc trace module is a system level solution for both core tracing and SoC tracing. This patch brings in a documentation for ultrasoc framework and drivers. It simply introduces function of ultrasoc, a typical Ultrasoc system, and a driver framework for ultrasoc.
Signed-off-by: Jonathan Zhou jonathan.zhouwen@huawei.com Signed-off-by: Qi Liu liuqi115@huawei.com
Documentation/trace/ultrasoc-trace.rst | 209 +++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 Documentation/trace/ultrasoc-trace.rst
diff --git a/Documentation/trace/ultrasoc-trace.rst b/Documentation/trace/ultrasoc-trace.rst new file mode 100644 index 0000000..36d2df2 --- /dev/null +++ b/Documentation/trace/ultrasoc-trace.rst @@ -0,0 +1,209 @@ +======================================================= +Siemens Embedded Analytics - HW Assisted Tracing on SoC +=======================================================
- :Author: Jonathan Zhou Jonathan.zhouwen@huawei.com
Qi Liu <liuqi115@huawei.com>
- :Date: January 16th, 2021
+Introduction +------------
+The Siemens Embedded Analytics Framework is system level solution for tracing +of multiple type SoC, this document is concerned with trace module. This module +has two main components: AXI Bus Communicator and System Memory Buffer.
+The AXI Communicator has upstream and downstream channels, the upstream channel +is used to transmit user configuration, and downstream channel to carry response +and trace data to the users.
+The System Memory Buffer provides a way to buffer and store messages in system +memory. It provides a capability to store messages received on its input message +interface to an area of system memory.
+A typical Siemens trace system would look like the following diagram:
@@@@@@@@@@@@@
@ CPU @
@@@@@@@@@@@@@
#############
# Coresight #
# ETM #
#######
###
#
|
*
*******************************
*** AMBA Advanced Trace Bus (ATB) ***
***************^***************
=============== |
=== FUNNEL ==<--- |
=======
|
*
@@@@@@@
@ TRC @
@@@@@
@@@
@
|
*
- ************************ Message BUS ***************************
- ******************^************************^****************
| |
@@@@@@@@@@@@@@@@@@ | @@@@@@@@@@@@
@ Message Engine @ | @ JTAG COM @
@@@@@@@@@@@@@@@@@@ | @@@@@@@@@@
| * |---> @@@@@@@@
| | @@@@@@
- @@@@@@@ | @@@@@@@@@@@ |
- @ SMB @ | @ AXI COM @ JTAG
@@@@@ |--> @@@@@@@@@
@@@--| @@@@@@@
@ | @@@@@
| |
| |
* *
- **************************** AMBA AXI ****************************
+Acronyms +---------------------------
+Acronyms:
+AXI-COM: AXI Communicator +SMB: System Memory Buffer +TRC: Trace receiver
+Framework and implementation +------------------------------
+Siemens Embedded Analytics Framework is implemented as a platform device. The +platform device provides a global point to configure the Embedded Analytics +subsystem, and also provides a ``struct ultrasoc_com`` to manage AXI-COM and +SMB.
+AXI-COM and SMB are implemented as platform devices, each SCCL has one AXI-COM +device and one SMB device. AXI-COM and SMB can use the following API to register +into Embedded Analytics framework: +.. c:function:: struct ultrasoc_com *ultrasoc_register_com(struct device *root_dev, struct ultrasoc_com_descp *com_descp) +.. c:function:: void ultrasoc_unregister_com(struct ultrasoc_com *com);
+As TRC receives data from coresight ETM device, SMB can use the following API +to register into coresight framework as a sink device: +.. c:function:: struct coresight_device *coresight_register(struct coresight_desc *desc); +.. c:function:: void coresight_unregister(struct coresight_device *csdev);
+Then users can get trace data by this path: ETM->funnel->SMB->System Memory. +More information about coresight framework can be found in +Documention/trace/coresight/coresight.rst.
+If everything goes well, the relationship of Embedded Analytics devices will be +described under the sysfs::
- $# ls /sys/bus/platform/devices/
- <HID.Embedded Analytics>:00 <HID.axi-com>:00 <HID.smb>:00
- $# ls /sys/bus/platform/devices/<HID.Embedded Analytics>:00
- com_mux firmware_node power <HID.axi-com>:00
- driver message subsystem <HID.smb>:00
- driver_override modalias uevent
- $# ls /sys/bus/coresight/devices/
- etm0 etm14 etm2 etm25 etm30 etm8 funnel4
- etm1 etm15 etm20 etm26 etm31 etm9 funnel5
- etm10 etm16 etm21 etm27 etm4 funnel0 funnel6
- etm11 etm17 etm22 etm28 etm5 funnel1 funnel7
- etm12 etm18 etm23 etm29 etm6 funnel2 sink_smb0
- etm13 etm19 etm24 etm3 etm7 funnel3
- $# ls -l /sys/bus/coresight/devices/funnel0/connections/
- <file details> in:0 -> ../../../../system/cpu/cpu0/ARMHC500:00/etm0
- <file details> in:1 -> ../../../../system/cpu/cpu1/ARMHC500:01/etm1
- <file details> in:2 -> ../../../../system/cpu/cpu2/ARMHC500:02/etm2
- <file details> in:3 -> ../../../../system/cpu/cpu3/ARMHC500:03/etm3
- <file details> nr_links
- <file details> out:0 -> ../../../HISI0391:00/HISI03A1:00/sink_smb0
- $# ls -l /sys/bus/coresight/devices/sink_smb0/connections/
- <file details> in:101 -> ../../../../ARMHC9FE:05/funnel5
- <file details> in:114 -> ../../../../ARMHC9FE:07/funnel7
- <file details> in:121 -> ../../../../ARMHC9FE:03/funnel3
- <file details> in:39 -> ../../../../ARMHC9FE:00/funnel0
- <file details> in:51 -> ../../../../ARMHC9FE:04/funnel4
- <file details> in:61 -> ../../../../ARMHC9FE:06/funnel6
- <file details> in:68 -> ../../../../ARMHC9FE:02/funnel2
- <file details> in:89 -> ../../../../ARMHC9FE:01/funnel1
- <file details> nr_links
+How to use the Embedded Analytics trace module +-----------------------------------------------
+There are two ways to use the Embedded Analytics trace module:
+1. interacting directly with the devices using the sysFS interface. +2. using the perf cmd line tools.
+1) Using the sysFS interface:
+Before trace collection can start, a coresight sink needs to be identified. +There is no limit on the amount of sinks (nor sources) that can be enabled at +any given moment. As a generic operation, all device pertaining to the sink +class will have an "active" entry in sysfs::
I haven't looked at the rest of the patchset but unless you have changed that, only one sink will be selected by the framework when operating from sysfs. Regardless of the number of sinks that were enabled, the framework will pick the first one it finds.
- $# ls /sys/bus/coresight/devices/
- etm0 etm14 etm2 etm25 etm30 etm8 funnel4
- etm1 etm15 etm20 etm26 etm31 etm9 funnel5
- etm10 etm16 etm21 etm27 etm4 funnel0 funnel6
- etm11 etm17 etm22 etm28 etm5 funnel1 funnel7
- etm12 etm18 etm23 etm29 etm6 funnel2 sink_smb0
- etm13 etm19 etm24 etm3 etm7 funnel3
- $# ls /sys/bus/coresight/devices/sink_smb0
- connections enable_sink firmware_node power subsystem uevent
- $# echo 1 > /sys/bus/coresight/devices/sink_smb0/enable_sink
- $# cat /sys/bus/coresight/devices/sink_smb0/enable_sink
- 1
+When start trace collection, etm devices corresponding to the enabled sink +should be selected::
- $# echo 1 > /sys/bus/coresight/devices/etm0/enable_source
- $# cat /sys/bus/coresight/devices/etm0/enable_source
- 1
- $# cat /sys/bus/platform/devices/<HID.smb>:00/com_status
- com-type : DOWN-ONLY
- service status : stopped
- interrupt status : 0x00000003
- write point : 0x5437f400 <----- The write pointer is moving
+Trace collection is stopped the same way::
- $# echo 0 > /sys/bus/coresight/devices/etm0/enable_source
- $# echo 0 > /sys/bus/coresight/devices/sink_smb0/enable_sink
+The content of the SMB buffer can be harvested directly from /dev::
- $# dd if=/dev/sink_smb0 of=~/cstrace.bin
- 5233+0 records in
- 5233+0 records out
- 2679296 bytes (2.7 MB) copied, 0.0131708 s, 203 MB/s
- root:/sys/bus/coresight/devices#
+The file cstrace.bin can be decompressed using "ptm2human".
+2) Using perf framework:
+As SMB device has been registered with coresight framework, perf tool can be +used to control Embedded Analytics trace collection, and the method is similar +to using perf to do coresight trace collection.
+The only thing to note is, list of cpus should be correspond to the specified +sink device.
+Example usage of perf::
$# ./perf list pmu
cs_etm// [Kernel PMU event]
- $# ./perf record -e cs_etm/@sink_smb0/ -C 0 --per-thread sleep 2s
- [ perf record: Woken up 2 times to write data ]
- [ perf record: Captured and wrote 0.288 MB perf.data ]
- $# ./perf report
After reading all this and without looking at the rest of the patchset it seems to me this work should go under drivers/hwtracing/coresight/.
There is a lot of code to review and as such it will take me a fair amount of time to go through it all. Comments will be scattered over several days (weeks) - I will set you know when I am done.
Thanks, Mathieu
-- 2.7.4
Hi Mathieu,
Thanks for reviewing this patch.
On 2021/6/24 6:51, Mathieu Poirier wrote:
Hi,
On Tue, Jun 15, 2021 at 05:34:41PM +0800, Qi Liu wrote:
Ultrasoc trace module is a system level solution for both core tracing and SoC tracing. This patch brings in a documentation for ultrasoc framework and drivers. It simply introduces function of ultrasoc, a typical Ultrasoc system, and a driver framework for ultrasoc.
Signed-off-by: Jonathan Zhou jonathan.zhouwen@huawei.com Signed-off-by: Qi Liu liuqi115@huawei.com
Documentation/trace/ultrasoc-trace.rst | 209 +++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 Documentation/trace/ultrasoc-trace.rst
diff --git a/Documentation/trace/ultrasoc-trace.rst b/Documentation/trace/ultrasoc-trace.rst new file mode 100644 index 0000000..36d2df2 --- /dev/null +++ b/Documentation/trace/ultrasoc-trace.rst @@ -0,0 +1,209 @@ +======================================================= +Siemens Embedded Analytics - HW Assisted Tracing on SoC +=======================================================
- :Author: Jonathan Zhou Jonathan.zhouwen@huawei.com
Qi Liu <liuqi115@huawei.com>
- :Date: January 16th, 2021
+Introduction +------------
+The Siemens Embedded Analytics Framework is system level solution for tracing +of multiple type SoC, this document is concerned with trace module. This module +has two main components: AXI Bus Communicator and System Memory Buffer.
+The AXI Communicator has upstream and downstream channels, the upstream channel +is used to transmit user configuration, and downstream channel to carry response +and trace data to the users.
+The System Memory Buffer provides a way to buffer and store messages in system +memory. It provides a capability to store messages received on its input message +interface to an area of system memory.
+A typical Siemens trace system would look like the following diagram:
@@@@@@@@@@@@@
@ CPU @
@@@@@@@@@@@@@
#############
# Coresight #
# ETM #
#######
###
#
|
*
*******************************
*** AMBA Advanced Trace Bus (ATB) ***
***************^***************
=============== |
=== FUNNEL ==<--- |
=======
|
*
@@@@@@@
@ TRC @
@@@@@
@@@
@
|
*
- ************************ Message BUS ***************************
- ******************^************************^****************
| |
@@@@@@@@@@@@@@@@@@ | @@@@@@@@@@@@
@ Message Engine @ | @ JTAG COM @
@@@@@@@@@@@@@@@@@@ | @@@@@@@@@@
| * |---> @@@@@@@@
| | @@@@@@
- @@@@@@@ | @@@@@@@@@@@ |
- @ SMB @ | @ AXI COM @ JTAG
@@@@@ |--> @@@@@@@@@
@@@--| @@@@@@@
@ | @@@@@
| |
| |
* *
- **************************** AMBA AXI ****************************
+Acronyms +---------------------------
+Acronyms:
+AXI-COM: AXI Communicator +SMB: System Memory Buffer +TRC: Trace receiver
+Framework and implementation +------------------------------
+Siemens Embedded Analytics Framework is implemented as a platform device. The +platform device provides a global point to configure the Embedded Analytics +subsystem, and also provides a ``struct ultrasoc_com`` to manage AXI-COM and +SMB.
+AXI-COM and SMB are implemented as platform devices, each SCCL has one AXI-COM +device and one SMB device. AXI-COM and SMB can use the following API to register +into Embedded Analytics framework: +.. c:function:: struct ultrasoc_com *ultrasoc_register_com(struct device *root_dev, struct ultrasoc_com_descp *com_descp) +.. c:function:: void ultrasoc_unregister_com(struct ultrasoc_com *com);
+As TRC receives data from coresight ETM device, SMB can use the following API +to register into coresight framework as a sink device: +.. c:function:: struct coresight_device *coresight_register(struct coresight_desc *desc); +.. c:function:: void coresight_unregister(struct coresight_device *csdev);
+Then users can get trace data by this path: ETM->funnel->SMB->System Memory. +More information about coresight framework can be found in +Documention/trace/coresight/coresight.rst.
+If everything goes well, the relationship of Embedded Analytics devices will be +described under the sysfs::
- $# ls /sys/bus/platform/devices/
- <HID.Embedded Analytics>:00 <HID.axi-com>:00 <HID.smb>:00
- $# ls /sys/bus/platform/devices/<HID.Embedded Analytics>:00
- com_mux firmware_node power <HID.axi-com>:00
- driver message subsystem <HID.smb>:00
- driver_override modalias uevent
- $# ls /sys/bus/coresight/devices/
- etm0 etm14 etm2 etm25 etm30 etm8 funnel4
- etm1 etm15 etm20 etm26 etm31 etm9 funnel5
- etm10 etm16 etm21 etm27 etm4 funnel0 funnel6
- etm11 etm17 etm22 etm28 etm5 funnel1 funnel7
- etm12 etm18 etm23 etm29 etm6 funnel2 sink_smb0
- etm13 etm19 etm24 etm3 etm7 funnel3
- $# ls -l /sys/bus/coresight/devices/funnel0/connections/
- <file details> in:0 -> ../../../../system/cpu/cpu0/ARMHC500:00/etm0
- <file details> in:1 -> ../../../../system/cpu/cpu1/ARMHC500:01/etm1
- <file details> in:2 -> ../../../../system/cpu/cpu2/ARMHC500:02/etm2
- <file details> in:3 -> ../../../../system/cpu/cpu3/ARMHC500:03/etm3
- <file details> nr_links
- <file details> out:0 -> ../../../HISI0391:00/HISI03A1:00/sink_smb0
- $# ls -l /sys/bus/coresight/devices/sink_smb0/connections/
- <file details> in:101 -> ../../../../ARMHC9FE:05/funnel5
- <file details> in:114 -> ../../../../ARMHC9FE:07/funnel7
- <file details> in:121 -> ../../../../ARMHC9FE:03/funnel3
- <file details> in:39 -> ../../../../ARMHC9FE:00/funnel0
- <file details> in:51 -> ../../../../ARMHC9FE:04/funnel4
- <file details> in:61 -> ../../../../ARMHC9FE:06/funnel6
- <file details> in:68 -> ../../../../ARMHC9FE:02/funnel2
- <file details> in:89 -> ../../../../ARMHC9FE:01/funnel1
- <file details> nr_links
+How to use the Embedded Analytics trace module +-----------------------------------------------
+There are two ways to use the Embedded Analytics trace module:
+1. interacting directly with the devices using the sysFS interface. +2. using the perf cmd line tools.
+1) Using the sysFS interface:
+Before trace collection can start, a coresight sink needs to be identified. +There is no limit on the amount of sinks (nor sources) that can be enabled at +any given moment. As a generic operation, all device pertaining to the sink +class will have an "active" entry in sysfs::
I haven't looked at the rest of the patchset but unless you have changed that, only one sink will be selected by the framework when operating from sysfs. Regardless of the number of sinks that were enabled, the framework will pick the first one it finds.
Yes, framework only choose the first sink device if there are several sinks. I'll change the description in document in next version, thanks.
- $# ls /sys/bus/coresight/devices/
- etm0 etm14 etm2 etm25 etm30 etm8 funnel4
- etm1 etm15 etm20 etm26 etm31 etm9 funnel5
- etm10 etm16 etm21 etm27 etm4 funnel0 funnel6
- etm11 etm17 etm22 etm28 etm5 funnel1 funnel7
- etm12 etm18 etm23 etm29 etm6 funnel2 sink_smb0
- etm13 etm19 etm24 etm3 etm7 funnel3
- $# ls /sys/bus/coresight/devices/sink_smb0
- connections enable_sink firmware_node power subsystem uevent
- $# echo 1 > /sys/bus/coresight/devices/sink_smb0/enable_sink
- $# cat /sys/bus/coresight/devices/sink_smb0/enable_sink
- 1
+When start trace collection, etm devices corresponding to the enabled sink +should be selected::
- $# echo 1 > /sys/bus/coresight/devices/etm0/enable_source
- $# cat /sys/bus/coresight/devices/etm0/enable_source
- 1
- $# cat /sys/bus/platform/devices/<HID.smb>:00/com_status
- com-type : DOWN-ONLY
- service status : stopped
- interrupt status : 0x00000003
- write point : 0x5437f400 <----- The write pointer is moving
+Trace collection is stopped the same way::
- $# echo 0 > /sys/bus/coresight/devices/etm0/enable_source
- $# echo 0 > /sys/bus/coresight/devices/sink_smb0/enable_sink
+The content of the SMB buffer can be harvested directly from /dev::
- $# dd if=/dev/sink_smb0 of=~/cstrace.bin
- 5233+0 records in
- 5233+0 records out
- 2679296 bytes (2.7 MB) copied, 0.0131708 s, 203 MB/s
- root:/sys/bus/coresight/devices#
+The file cstrace.bin can be decompressed using "ptm2human".
+2) Using perf framework:
+As SMB device has been registered with coresight framework, perf tool can be +used to control Embedded Analytics trace collection, and the method is similar +to using perf to do coresight trace collection.
+The only thing to note is, list of cpus should be correspond to the specified +sink device.
+Example usage of perf::
$# ./perf list pmu
cs_etm// [Kernel PMU event]
- $# ./perf record -e cs_etm/@sink_smb0/ -C 0 --per-thread sleep 2s
- [ perf record: Woken up 2 times to write data ]
- [ perf record: Captured and wrote 0.288 MB perf.data ]
- $# ./perf report
After reading all this and without looking at the rest of the patchset it seems to me this work should go under drivers/hwtracing/coresight/.
So how about drivers/hwtracing/coresight/ultrasoc?
There is a lot of code to review and as such it will take me a fair amount of time to go through it all. Comments will be scattered over several days (weeks)
- I will set you know when I am done.
Thanks, Mathieu
Ok, thanks for revewing this patchset
Qi
-- 2.7.4
.
+Example usage of perf::
$# ./perf list pmu
cs_etm// [Kernel PMU event]
- $# ./perf record -e cs_etm/@sink_smb0/ -C 0 --per-thread sleep 2s
- [ perf record: Woken up 2 times to write data ]
- [ perf record: Captured and wrote 0.288 MB perf.data ]
- $# ./perf report
After reading all this and without looking at the rest of the patchset it seems to me this work should go under drivers/hwtracing/coresight/.
So how about drivers/hwtracing/coresight/ultrasoc?
It's hard to say at this time - I'll advise further once I have reviewed the other patches.
There is a lot of code to review and as such it will take me a fair amount of time to go through it all. Comments will be scattered over several days (weeks)
- I will set you know when I am done.
Thanks, Mathieu
Ok, thanks for revewing this patchset
Qi
-- 2.7.4
.
This patch introduces a platform driver for the top device of Ultrasoc SubSystem. It also provides a framework to manage Ultrasoc communictors, and a set of standard attributes of communicators to access the service data and to configure the communictor drivers. Once a Ultrasoc Communictor driver register itself into the framework, these attributes will be added into communicator devices.
Signed-off-by: Jonathan Zhou jonathan.zhouwen@huawei.com Signed-off-by: Qi Liu liuqi115@huawei.com --- MAINTAINERS | 7 + drivers/Makefile | 1 + drivers/hwtracing/Kconfig | 2 + drivers/hwtracing/ultrasoc/Kconfig | 16 ++ drivers/hwtracing/ultrasoc/Makefile | 7 + drivers/hwtracing/ultrasoc/ultrasoc.c | 518 ++++++++++++++++++++++++++++++++++ drivers/hwtracing/ultrasoc/ultrasoc.h | 168 +++++++++++ 7 files changed, 719 insertions(+) create mode 100644 drivers/hwtracing/ultrasoc/Kconfig create mode 100644 drivers/hwtracing/ultrasoc/Makefile create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc.c create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc.h
diff --git a/MAINTAINERS b/MAINTAINERS index 88c2c4d..d799f6e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8324,6 +8324,13 @@ S: Maintained F: drivers/misc/hisi_hikey_usb.c F: Documentation/devicetree/bindings/misc/hisilicon-hikey-usb.yaml
+SIEMENS EMBEDDED ANALYTICS DRIVER +M: Jonathan Zhou Jonathan.zhouwen@huawei.com +M: Qi Liu liuqi115@huawei.com +S: Supported +F: Documentation/trace/ultrasoc-trace.rst +F: drivers/hwtracing/ultrasoc/ + HISILICON PMU DRIVER M: Shaokun Zhang zhangshaokun@hisilicon.com S: Supported diff --git a/drivers/Makefile b/drivers/Makefile index 5a6d613..4c132a7 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -176,6 +176,7 @@ obj-$(CONFIG_PERF_EVENTS) += perf/ obj-$(CONFIG_RAS) += ras/ obj-$(CONFIG_USB4) += thunderbolt/ obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/ +obj-y += hwtracing/ultrasoc/ obj-y += hwtracing/intel_th/ obj-$(CONFIG_STM) += hwtracing/stm/ obj-$(CONFIG_ANDROID) += android/ diff --git a/drivers/hwtracing/Kconfig b/drivers/hwtracing/Kconfig index 1308583..3829030 100644 --- a/drivers/hwtracing/Kconfig +++ b/drivers/hwtracing/Kconfig @@ -5,4 +5,6 @@ source "drivers/hwtracing/stm/Kconfig"
source "drivers/hwtracing/intel_th/Kconfig"
+source "drivers/hwtracing/ultrasoc/Kconfig" + endmenu diff --git a/drivers/hwtracing/ultrasoc/Kconfig b/drivers/hwtracing/ultrasoc/Kconfig new file mode 100644 index 0000000..90a3934 --- /dev/null +++ b/drivers/hwtracing/ultrasoc/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: MIT +# +# ultrasoc configuration +# + +menuconfig ULTRASOC + tristate "Ultrasoc Tracing Support" + select CORESIGHT + help + This framework provides a kernel interface for the Ultrasoc trace + drivers to register themselves with. It's intended to build + a topological view of the Ultrasoc components based on ACPI + specification and configure the right series of components when a + trace source gets enabled. + +endif diff --git a/drivers/hwtracing/ultrasoc/Makefile b/drivers/hwtracing/ultrasoc/Makefile new file mode 100644 index 0000000..a747171 --- /dev/null +++ b/drivers/hwtracing/ultrasoc/Makefile @@ -0,0 +1,7 @@ +# # SPDX-License-Identifier: MIT +# +# Makefile for ultrasoc drivers. +# + +obj-$(CONFIG_ULTRASOC) += ultrasoc-drv.o +ultrasoc-drv-objs := ultrasoc.o diff --git a/drivers/hwtracing/ultrasoc/ultrasoc.c b/drivers/hwtracing/ultrasoc/ultrasoc.c new file mode 100644 index 0000000..191c3ec --- /dev/null +++ b/drivers/hwtracing/ultrasoc/ultrasoc.c @@ -0,0 +1,518 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2021 Hisilicon Limited Permission is hereby granted, free of + * charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Code herein communicates with and accesses proprietary hardware which is + * licensed intellectual property (IP) belonging to Siemens Digital Industries + * Software Ltd. + * + * Siemens Digital Industries Software Ltd. asserts and reserves all rights to + * their intellectual property. This paragraph may not be removed or modified + * in any way without permission from Siemens Digital Industries Software Ltd. + */ +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kthread.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> + +#include "ultrasoc.h" + +static ssize_t com_mux_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct ultrasoc_drv_data *drvdata = dev_get_drvdata(dev); + long val; + int ret; + + ret = kstrtol(buf, 0, &val); + if (ret) + return -EINVAL; + + writel(val & 0xffffffff, drvdata->com_mux); + return size; +} + +static ssize_t com_mux_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ultrasoc_drv_data *drvdata = dev_get_drvdata(dev); + + return sysfs_emit(buf, "0x%x\n", readl(drvdata->com_mux)); +} +static DEVICE_ATTR_RW(com_mux); + +static umode_t ultrasoc_com_mux_is_visible(struct kobject *kobj, + struct attribute *attr, int unused) +{ + struct device *dev = kobj_to_dev(kobj); + struct ultrasoc_drv_data *drvdata = dev_get_drvdata(dev); + + if (IS_ERR(drvdata->com_mux)) + return 0; + + return attr->mode; +} + +static struct attribute *ultrasoc_com_mux_attr[] = { + &dev_attr_com_mux.attr, + NULL, +}; + +static const struct attribute_group ultrasoc_com_mux_group = { + .attrs = ultrasoc_com_mux_attr, + .is_visible = ultrasoc_com_mux_is_visible, +}; + +static const struct attribute_group *ultrasoc_global_groups[] = { + &ultrasoc_com_mux_group, + NULL, +}; + +static int ultrasoc_probe(struct platform_device *pdev) +{ + struct ultrasoc_drv_data *drvdata; + + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + drvdata->dev = &pdev->dev; + INIT_LIST_HEAD(&drvdata->ultrasoc_com_head); + + drvdata->com_mux = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(drvdata->com_mux)) { + dev_err(&pdev->dev, "Failed to ioremap for com_mux resource.\n"); + return PTR_ERR(drvdata->com_mux); + } + /* switch ultrasoc commucator mux for on-chip drivers. */ + writel(US_SELECT_ONCHIP, drvdata->com_mux); + platform_set_drvdata(pdev, drvdata); + + return 0; +} + +static int ultrasoc_remove(struct platform_device *pdev) +{ + struct ultrasoc_drv_data *pdata = platform_get_drvdata(pdev); + + /* switch back to external debuger users if necessary.*/ + if (!IS_ERR(pdata->com_mux)) + writel(0, pdata->com_mux); + + return 0; +} + +static struct acpi_device_id ultrasoc_acpi_match[] = { + {"HISI0391", }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, ultrasoc_acpi_match); + +static struct platform_driver ultrasoc_driver = { + .driver = { + .name = "ultrasoc", + .acpi_match_table = ultrasoc_acpi_match, + .dev_groups = ultrasoc_global_groups, + }, + .probe = ultrasoc_probe, + .remove = ultrasoc_remove, +}; +module_platform_driver(ultrasoc_driver); + +static const char * const ultrasoc_com_type_string[] = { + "UNKNOWN", + "UP-DOWN-BOTH", + "DOWN-ONLY", +}; + +static const char * const ultrasoc_com_service_status_string[] = { + "stopped", + "sleeping", + "running normal", +}; + +/* + * To avoid communicator buffer overflow, we create a service thread + * to do the communicator work. This is the service thread entry. + */ +static int ultrasoc_com_service(void *arg) +{ + unsigned int deep_sleep = 0; + struct ultrasoc_com *com; + int ud_flag = 0; + int core; + + core = smp_processor_id(); + com = (struct ultrasoc_com *)arg; + if (!com->com_work) { + dev_err(com->dev, + "This communicator do not have a work entry.\n"); + com->service_status = ULTRASOC_COM_SERVICE_STOPPED; + return -EINVAL; + } + dev_dbg(com->dev, "ultrasoc com service %s run on core %d.\n", + com->name, core); + + while (true) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock(&com->service_lock); + if (com->service_status == ULTRASOC_COM_SERVICE_SLEEPING) { + spin_unlock(&com->service_lock); + schedule(); + spin_lock(&com->service_lock); + } + + /* + * Since this thread service might be woken up with a status + * of STOP, we check the status again to avoid setting an error + * status + */ + if (com->service_status == ULTRASOC_COM_SERVICE_SLEEPING) { + com->service_status = + ULTRASOC_COM_SERVICE_RUNNING_NORMAL; + deep_sleep = 0; + } + spin_unlock(&com->service_lock); + __set_current_state(TASK_RUNNING); + + if (com->service_status == ULTRASOC_COM_SERVICE_STOPPED) + break; + + ud_flag = com->com_work(com); + if (!ud_flag) { + usleep_range(10, 100); + deep_sleep++; + } else { + deep_sleep = 0; + usleep_range(1, 4); + } + if (deep_sleep > com->timeout) + com->service_status = ULTRASOC_COM_SERVICE_SLEEPING; + if (kthread_should_stop()) + break; + } + com->service_status = ULTRASOC_COM_SERVICE_STOPPED; + + return 0; +} + +static void com_try_stop_service(struct ultrasoc_com *com) +{ + if (com->service_status != ULTRASOC_COM_SERVICE_STOPPED) { + spin_lock(&com->service_lock); + com->service_status = ULTRASOC_COM_SERVICE_STOPPED; + spin_unlock(&com->service_lock); + kthread_stop(com->service); + com->service = NULL; + } +} + +static void com_try_start_service(struct ultrasoc_com *com) +{ + if (com->service && + com->service_status != ULTRASOC_COM_SERVICE_STOPPED) { + dev_notice(com->dev, "Service is already running on %ld.\n", + com->core_bind); + wake_up_process(com->service); + return; + } + + dev_dbg(com->dev, "Starting service %s on core %ld.\n", com->name, + com->core_bind); + com->service = kthread_create(ultrasoc_com_service, com, "%s_service", + com->name); + if (IS_ERR(com->service)) { + spin_lock(&com->service_lock); + com->service_status = ULTRASOC_COM_SERVICE_STOPPED; + spin_unlock(&com->service_lock); + dev_err(com->dev, "Failed to start service.\n"); + } + + if (com->core_bind != -1) + kthread_bind(com->service, com->core_bind); + + spin_lock(&com->service_lock); + com->service_status = ULTRASOC_COM_SERVICE_RUNNING_NORMAL; + spin_unlock(&com->service_lock); + wake_up_process(com->service); +} + +static void com_service_restart(struct ultrasoc_com *com) +{ + com_try_stop_service(com); + com_try_start_service(com); +} + +static ssize_t ultrasoc_com_status(struct ultrasoc_com *com, char *buf) +{ + enum ultrasoc_com_service_status status = com->service_status; + enum ultrasoc_com_type type = com->com_type; + ssize_t wr_size; + + wr_size = sysfs_emit(buf, "%-20s: %s\n", "com-type", + ultrasoc_com_type_string[type]); + wr_size += sysfs_emit_at(buf, wr_size, "%-20s: %s\n", "service status", + ultrasoc_com_service_status_string[status]); + wr_size += uscom_ops_com_status(com, buf, wr_size); + + return wr_size; +} + +ULTRASOC_COM_ATTR_WO_OPS(start, com_try_start_service); +ULTRASOC_COM_ATTR_WO_OPS(stop, com_try_stop_service); +ULTRASOC_COM_ATTR_WO_OPS(restart, com_service_restart); +ULTRASOC_COM_ATTR_RO_OPS(com_status, ultrasoc_com_status); + +struct ultrasoc_com *ultrasoc_find_com_by_dev(struct device *com_dev) +{ + struct ultrasoc_drv_data *pdata = dev_get_drvdata(com_dev->parent); + struct list_head *com_head = &pdata->ultrasoc_com_head; + struct ultrasoc_com *com; + struct list_head *cur; + + list_for_each(cur, com_head) { + com = list_entry(cur, struct ultrasoc_com, node); + if (com->dev == com_dev) + return com; + } + + dev_err(com_dev, "Unable to find com associated with this device!\n"); + return NULL; +} + +static ssize_t core_bind_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + struct ultrasoc_com *com = ultrasoc_find_com_by_dev(dev); + long core_bind; + int ret; + + if (!com) + return 0; + + ret = kstrtol(buf, 0, &core_bind); + if (!ret) + com->core_bind = core_bind; + + return size; +} + +static ssize_t core_bind_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ultrasoc_com *com = ultrasoc_find_com_by_dev(dev); + + if (!com) + return 0; + + return sysfs_emit(buf, "%#lx", com->core_bind); +} +static DEVICE_ATTR_RW(core_bind); + +static ssize_t message_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct ultrasoc_com *com = ultrasoc_find_com_by_dev(dev); + u64 msg, msg_len; + int elements; + + elements = sscanf(buf, "%llx %llx", &msg, &msg_len); + if (elements < 2) + return -EINVAL; + + com->com_ops->put_raw_msg(com, msg_len, msg); + dev_dbg(dev, "Set message %#llx, length is %#llx.\n", msg, msg_len); + + return size; +} +static DEVICE_ATTR_WO(message); + +static umode_t ultrasoc_com_message_is_visible(struct kobject *kobj, + struct attribute *attr, int unused) +{ + struct device *dev = kobj_to_dev(kobj); + struct ultrasoc_com *com = ultrasoc_find_com_by_dev(dev); + + if (com->com_type != ULTRASOC_COM_TYPE_BOTH) + return 0; + + return attr->mode; +} + +static struct attribute *ultrasoc_com_global_attrs[] = { + &dev_attr_com_status.attr, + NULL, +}; + +static struct attribute *ultrasoc_com_service_attrs[] = { + &dev_attr_core_bind.attr, + &dev_attr_start.attr, + &dev_attr_stop.attr, + &dev_attr_restart.attr, + NULL, +}; + +static struct attribute *ultrasoc_com_message_attrs[] = { + &dev_attr_message.attr, + NULL, +}; + +static const struct attribute_group ultrasoc_com_global_group = { + .attrs = ultrasoc_com_global_attrs, +}; + +static const struct attribute_group ultrasoc_com_service_group = { + .attrs = ultrasoc_com_service_attrs, + .name = "service", +}; + +static const struct attribute_group ultrasoc_com_message_group = { + .attrs = ultrasoc_com_message_attrs, + .is_visible = ultrasoc_com_message_is_visible, +}; + +static const struct attribute_group *ultrasoc_com_attr[] = { + &ultrasoc_com_global_group, + &ultrasoc_com_service_group, + &ultrasoc_com_message_group, + NULL, +}; + +static int ultrasoc_validate_com_descp(struct ultrasoc_com_descp *com_descp) +{ + if (!com_descp->uscom_ops) + return -EINVAL; + + if (com_descp->com_type == ULTRASOC_COM_TYPE_BOTH) { + if (!com_descp->uscom_ops->put_raw_msg || + !com_descp->default_route_msg) + return -EINVAL; + } + + return 0; +} + +static int wait_com_service_stop(struct ultrasoc_com *com) +{ + u32 timeout = 0; + + if (com->service_status != ULTRASOC_COM_SERVICE_STOPPED) + com_try_stop_service(com); + while (com->service_status != ULTRASOC_COM_SERVICE_STOPPED) { + usleep_range(10, 100); + timeout++; + if (timeout > com->timeout) + return -ETIMEDOUT; + } + + return 0; +} + +/** + * ultrasoc_register_com - register a ultrasoc communicator for communication + * between usmsg bus devices and platform bus devices. + * + * @top_dev: the ultrasoc top platform device to manage all communicator. + * @com_descp: the communicator description to be registered. + * Return: the pointer to a new communicator if register ok, NULL if failure. + */ +struct ultrasoc_com *ultrasoc_register_com(struct device *top_dev, + struct ultrasoc_com_descp *com_descp) +{ + struct ultrasoc_drv_data *drv_data = dev_get_drvdata(top_dev); + struct ultrasoc_com *com; + int ret; + + if (!drv_data) + return ERR_PTR(-EBUSY); + + ret = ultrasoc_validate_com_descp(com_descp); + if (ret) + return ERR_PTR(-EINVAL); + + com = devm_kzalloc(top_dev, sizeof(*com), GFP_KERNEL); + if (!com) + return ERR_PTR(-ENOMEM); + + com->name = com_descp->name; + com->com_type = com_descp->com_type; + com->com_ops = com_descp->uscom_ops; + com->com_work = com_descp->com_work; + com->timeout = US_SERVICE_TIMEOUT; + com->core_bind = -1; + com->root = top_dev; + com->dev = com_descp->com_dev; + spin_lock_init(&com->service_lock); + + device_lock(top_dev); + list_add_tail(&com->node, &drv_data->ultrasoc_com_head); + device_unlock(top_dev); + + if (com->com_type == ULTRASOC_COM_TYPE_BOTH && !drv_data->def_up_com) { + /* + * There is one ULTRASOC_COM_TYPE_BOTH device per ultrasoc + * system, so race will not happen. + */ + drv_data->def_up_com = com; + /* start the default communicator service. */ + com_try_start_service(com); + /* set ultrasoc route all msgs to port 1 as default*/ + com->com_ops->put_raw_msg(com, US_ROUTE_LENGTH, + com_descp->default_route_msg); + } + + ret = device_add_groups(com->dev, ultrasoc_com_attr); + if (ret) + return ERR_PTR(ret); + + return com; +} +EXPORT_SYMBOL_GPL(ultrasoc_register_com); + +int ultrasoc_unregister_com(struct ultrasoc_com *com) +{ + struct ultrasoc_drv_data *pdata = dev_get_drvdata(com->root); + struct device *com_dev = com->dev; + struct device *dev = com->root; + + if (wait_com_service_stop(com)) { + dev_err(com_dev, "Com service is still running.\n"); + return -EBUSY; + } + + if (pdata->def_up_com == com) + pdata->def_up_com = NULL; + + device_lock(dev); + list_del(&com->node); + device_unlock(dev); + device_remove_groups(com_dev, ultrasoc_com_attr); + + return 0; +} +EXPORT_SYMBOL_GPL(ultrasoc_unregister_com); + +MODULE_DESCRIPTION("Ultrasoc driver"); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("Jonathan Zhou jonathan.zhouwen@huawei.com"); +MODULE_AUTHOR("Qi Liu liuqi115@huawei.com"); diff --git a/drivers/hwtracing/ultrasoc/ultrasoc.h b/drivers/hwtracing/ultrasoc/ultrasoc.h new file mode 100644 index 0000000..2831e14 --- /dev/null +++ b/drivers/hwtracing/ultrasoc/ultrasoc.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (C) 2021 Hisilicon Limited Permission is hereby granted, free of + * charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Code herein communicates with and accesses proprietary hardware which is + * licensed intellectual property (IP) belonging to Siemens Digital Industries + * Software Ltd. + * + * Siemens Digital Industries Software Ltd. asserts and reserves all rights to + * their intellectual property. This paragraph may not be removed or modified + * in any way without permission from Siemens Digital Industries Software Ltd. + */ + +#ifndef _LINUX_ULTRASOC_H +#define _LINUX_ULTRASOC_H + +#include <linux/device.h> +#include <linux/list.h> +#include <linux/slab.h> + +struct ultrasoc_drv_data { + struct device *dev; + void __iomem *com_mux; + struct list_head ultrasoc_com_head; + struct ultrasoc_com *def_up_com; + const char *dev_data_path; + spinlock_t spinlock; +}; + +enum ultrasoc_com_type { + ULTRASOC_COM_TYPE_BOTH, + ULTRASOC_COM_TYPE_DOWN, +}; + +struct ultrasoc_com_descp { + const char *name; + enum ultrasoc_com_type com_type; + struct device *com_dev; + struct uscom_ops *uscom_ops; + int (*com_work)(struct ultrasoc_com *com); + u64 default_route_msg; +}; + +enum ultrasoc_com_service_status { + ULTRASOC_COM_SERVICE_STOPPED, + ULTRASOC_COM_SERVICE_SLEEPING, + ULTRASOC_COM_SERVICE_RUNNING_NORMAL, +}; + +#define USMSG_MAX_IDX 9 +struct msg_descp { + unsigned int msg_len; + __le32 msg_buf[USMSG_MAX_IDX]; + struct list_head node; +}; + +static inline void usmsg_list_realse_all(struct list_head *msg_head) +{ + struct msg_descp *msgd, *next; + + list_for_each_entry_safe(msgd, next, msg_head, node) { + list_del(&msgd->node); + kfree(msgd); + } +} + +struct ultrasoc_com { + const char *name; + enum ultrasoc_com_type com_type; + struct device *root; + struct device *dev; + + long core_bind; + int (*com_work)(struct ultrasoc_com *com); + spinlock_t service_lock; + struct task_struct *service; + int service_status; + unsigned int timeout; + + char *data_path; + struct uscom_ops *com_ops; + + struct list_head node; +}; + +struct uscom_ops { + ssize_t (*com_status)(struct ultrasoc_com *com, char *buf, + ssize_t size); + void (*put_raw_msg)(struct ultrasoc_com *com, int msg_size, + unsigned long long msg); +}; + +#define uscom_ops_com_status(uscom, buf, size) \ + (((uscom)->com_ops && (uscom)->com_ops->com_status) ? \ + (uscom)->com_ops->com_status(uscom, buf, size) : 0) + +static inline void *ultrasoc_com_get_drvdata(struct ultrasoc_com *uscom) +{ + return dev_get_drvdata(uscom->dev); +} + +struct ultrasoc_com * +ultrasoc_register_com(struct device *root_dev, + struct ultrasoc_com_descp *com_descp); +int ultrasoc_unregister_com(struct ultrasoc_com *com); +int ultrasoc_com_del_usmsg_device(struct ultrasoc_com *com, int index); + +struct ultrasoc_com *ultrasoc_find_com_by_dev(struct device *com_dev); + +#define ULTRASOC_COM_ATTR_WO_OPS(attr_name, com_ops) \ + static ssize_t attr_name##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t size) \ + { \ + struct ultrasoc_com *com = ultrasoc_find_com_by_dev(dev); \ + long attr_name; \ + int ret; \ + if (!com) \ + return 0; \ + ret = kstrtol(buf, 0, &attr_name); \ + if (ret) { \ + return size; \ + } \ + if (attr_name == 1) { \ + com_ops(com); \ + } \ + return size; \ + } \ + static DEVICE_ATTR_WO(attr_name) + +#define ULTRASOC_COM_ATTR_RO_OPS(attr_name, com_ops) \ + static ssize_t attr_name##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ + { \ + struct ultrasoc_com *com = ultrasoc_find_com_by_dev(dev); \ + if (!com) \ + return 0; \ + return com_ops(com, buf); \ + } \ + static DEVICE_ATTR_RO(attr_name) + +/* 1000 * (10us ~ 100us) */ +#define US_SERVICE_TIMEOUT 1000 +/* communicator service work status */ +#define US_SERVICE_ONWORK 1 +#define US_SERVICE_IDLE 0 +#define US_ROUTE_LENGTH 11 +#define US_SELECT_ONCHIP 0x3 + +#endif
This patch adds driver for ultrasoc AXI Communicator. It includes a platform driver to probe AXI Communicator device, a set of operations to access the service data, and a service work entry which will be called by the standard communicator service.
Signed-off-by: Jonathan Zhou jonathan.zhouwen@huawei.com Signed-off-by: Qi Liu liuqi115@huawei.com --- drivers/hwtracing/ultrasoc/Kconfig | 9 + drivers/hwtracing/ultrasoc/Makefile | 3 + drivers/hwtracing/ultrasoc/ultrasoc-axi-com.c | 334 ++++++++++++++++++++++++++ drivers/hwtracing/ultrasoc/ultrasoc-axi-com.h | 66 +++++ 4 files changed, 412 insertions(+) create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-axi-com.c create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-axi-com.h
diff --git a/drivers/hwtracing/ultrasoc/Kconfig b/drivers/hwtracing/ultrasoc/Kconfig index 90a3934..77429f3 100644 --- a/drivers/hwtracing/ultrasoc/Kconfig +++ b/drivers/hwtracing/ultrasoc/Kconfig @@ -13,4 +13,13 @@ menuconfig ULTRASOC specification and configure the right series of components when a trace source gets enabled.
+if ULTRASOC +config ULTRASOC_AXI_COM + tristate "Ultrasoc AXI communicator drivers" + help + This config enables support for Ultrasoc AXI Bus Communicator + drivers. The AXI Communicator has upstream and downstream channels, + the upstream channel is used to transmit user configuration, and + downstream channel to carry response and trace data to the users. + endif diff --git a/drivers/hwtracing/ultrasoc/Makefile b/drivers/hwtracing/ultrasoc/Makefile index a747171..54711a7b 100644 --- a/drivers/hwtracing/ultrasoc/Makefile +++ b/drivers/hwtracing/ultrasoc/Makefile @@ -5,3 +5,6 @@
obj-$(CONFIG_ULTRASOC) += ultrasoc-drv.o ultrasoc-drv-objs := ultrasoc.o + +obj-$(CONFIG_ULTRASOC_AXI_COM) += ultrasoc-axi-com-drv.o +ultrasoc-axi-com-drv-objs := ultrasoc-axi-com.o diff --git a/drivers/hwtracing/ultrasoc/ultrasoc-axi-com.c b/drivers/hwtracing/ultrasoc/ultrasoc-axi-com.c new file mode 100644 index 0000000..af153dd --- /dev/null +++ b/drivers/hwtracing/ultrasoc/ultrasoc-axi-com.c @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2021 Hisilicon Limited Permission is hereby granted, free of + * charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Code herein communicates with and accesses proprietary hardware which is + * licensed intellectual property (IP) belonging to Siemens Digital Industries + * Software Ltd. + * + * Siemens Digital Industries Software Ltd. asserts and reserves all rights to + * their intellectual property. This paragraph may not be removed or modified + * in any way without permission from Siemens Digital Industries Software Ltd. + */ +#include <linux/acpi.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <asm/unaligned.h> + +#include "ultrasoc-axi-com.h" + +static void axi_com_enable_hw(struct axi_com_drv_data *drvdata) +{ + u32 val; + + val = readl(drvdata->base + AXIC_US_CTL); + val |= AXIC_US_CTL_EN; + writel(val, drvdata->base + AXIC_US_CTL); + + val = readl(drvdata->base + AXIC_DS_CTL); + val |= AXIC_DS_CTL_EN; + writel(val, drvdata->base + AXIC_DS_CTL); +} + +static void axi_com_disable_hw(struct axi_com_drv_data *drvdata) +{ + u32 val; + + val = readl(drvdata->base + AXIC_US_CTL); + val &= ~AXIC_US_CTL_EN; + writel(val, drvdata->base + AXIC_US_CTL); + + val = readl(drvdata->base + AXIC_DS_CTL); + val &= ~AXIC_DS_CTL_EN; + writel(val, drvdata->base + AXIC_DS_CTL); +} + +static inline bool axi_com_us_buf_full(struct axi_com_drv_data *drvdata) +{ + return readl(drvdata->base + AXIC_US_BUF_STS) & BIT(0); +} + +static inline bool axi_com_ds_buf_full(struct axi_com_drv_data *drvdata) +{ + return readl(drvdata->base + AXIC_DS_BUF_STS) & BIT(0); +} + +static int axi_com_try_send_msg(struct axi_com_drv_data *drvdata) +{ + struct msg_descp *msg; + struct list_head *node; + int index = 0; + int unsent; + u32 data; + + if (axi_com_us_buf_full(drvdata)) { + dev_err_once(drvdata->dev, "No room for upstream buffer.\n"); + return US_SERVICE_IDLE; + } + + spin_lock(&drvdata->us_msg_list_lock); + if (list_empty(&drvdata->us_msg_head)) { + spin_unlock(&drvdata->us_msg_list_lock); + return US_SERVICE_IDLE; + } + + node = drvdata->us_msg_head.next; + list_del(node); + drvdata->us_msg_cur--; + msg = container_of(node, struct msg_descp, node); + spin_unlock(&drvdata->us_msg_list_lock); + + unsent = msg->msg_len; + dev_dbg(drvdata->dev, "Length of send msg: %d.\n", msg->msg_len); + while (unsent > 0) { + data = get_unaligned_le32(&msg->msg_buf[index++]); + writel(data, drvdata->base + AXIC_US_DATA); + unsent -= AXIC_MSG_LEN_PER_SEND; + } + kfree(msg); + + return US_SERVICE_ONWORK; +} + +static int axi_com_try_recv_msg(struct axi_com_drv_data *drvdata) +{ + struct msg_descp tmp_msg = {0}; + struct msg_descp *msg; + bool lost = false; + u32 index = 0; + u32 status, entries, data; + + if (!axi_com_ds_buf_full(drvdata)) + return US_SERVICE_IDLE; + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) { + /* + * create local variable tmp_msg to read and clear + * the downstream message. + */ + msg = &tmp_msg; + lost = true; + } + + do { + if (index == USMSG_MAX_IDX) { + dev_warn(drvdata->dev, "Illegal message.\n"); + break; + } + data = readl(drvdata->base + AXIC_DS_DATA); + put_unaligned_le32(data, &msg->msg_buf[index++]); + status = readl(drvdata->base + AXIC_DS_RD_STS); + entries = status & GENMASK(7, 4); + msg->msg_len += AXIC_MSG_LEN_PER_REC; + } while (entries != 0); + + if (!lost) { + spin_lock(&drvdata->ds_msg_list_lock); + drvdata->ds_msg_cur++; + drvdata->ds_msg_counter++; + list_add_tail(&msg->node, &drvdata->ds_msg_head); + spin_unlock(&drvdata->ds_msg_list_lock); + } + + return US_SERVICE_ONWORK; +} + +static int axi_com_work(struct ultrasoc_com *uscom) +{ + struct axi_com_drv_data *drvdata = ultrasoc_com_get_drvdata(uscom); + int us_ds_flag; + + us_ds_flag = axi_com_try_recv_msg(drvdata); + us_ds_flag |= axi_com_try_send_msg(drvdata); + + return us_ds_flag; +} + +static ssize_t axi_com_show_status(struct ultrasoc_com *uscom, char *buf, + ssize_t wr_size) +{ + struct axi_com_drv_data *drvdata = ultrasoc_com_get_drvdata(uscom); + + wr_size += sysfs_emit_at(buf, wr_size, "%-20s: %d\n", + "ds msg list num", drvdata->ds_msg_cur); + wr_size += sysfs_emit_at(buf, wr_size, "%-20s: %d\n", + "us msg list num", drvdata->us_msg_cur); + + return wr_size; +} + +static void axi_com_put_raw_msg(struct ultrasoc_com *uscom, int msg_size, + unsigned long long msg_data) +{ + struct axi_com_drv_data *drvdata = ultrasoc_com_get_drvdata(uscom); + struct msg_descp *p_msg; + + p_msg = kmalloc(sizeof(*p_msg), GFP_KERNEL); + if (!p_msg) + return; + + p_msg->msg_len = msg_size; + put_unaligned_le64(msg_data, &p_msg->msg_buf[0]); + spin_lock(&drvdata->us_msg_list_lock); + list_add_tail(&p_msg->node, &drvdata->us_msg_head); + drvdata->us_msg_cur++; + spin_unlock(&drvdata->us_msg_list_lock); + + if (uscom->service_status != ULTRASOC_COM_SERVICE_STOPPED) + wake_up_process(uscom->service); + else + dev_warn(uscom->dev, "Com service is not running.\n"); +} + +static struct uscom_ops axi_com_ops = { + .com_status = axi_com_show_status, + .put_raw_msg = axi_com_put_raw_msg, +}; + +/* + * Config hardwares on the tracing path, using DSM calls to avoid exposing + * hardware message format. + */ +static int axi_com_config_inport(struct axi_com_drv_data *drvdata, bool enable) +{ + struct device *dev = drvdata->dev; + u32 flag = enable ? 1 : 0; + union acpi_object *obj; + guid_t guid; + + if (guid_parse("82ae1283-7f6a-4cbe-aa06-53e8fb24db18", &guid)) { + dev_err(dev, "Get GUID failed.\n"); + return -EINVAL; + } + + obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), &guid, 0, flag, NULL); + if (!obj) + dev_err(dev, "ACPI handle failed!\n"); + + ACPI_FREE(obj); + + return 0; +} + +static int axi_com_config_com_descp(struct platform_device *pdev, + struct axi_com_drv_data *drvdata) +{ + struct device *parent = pdev->dev.parent; + struct ultrasoc_com_descp com_descp = {0}; + struct device *dev = &pdev->dev; + struct ultrasoc_com *com; + + com_descp.name = pdev->name; + com_descp.com_type = ULTRASOC_COM_TYPE_BOTH; + com_descp.com_dev = dev; + com_descp.uscom_ops = &axi_com_ops; + com_descp.com_work = axi_com_work; + + if (device_property_read_u64(dev, "ultrasoc,default_route", + &com_descp.default_route_msg)) { + dev_err(dev, "Failed to read default_route!\n"); + return -EINVAL; + } + + com = ultrasoc_register_com(parent, &com_descp); + if (IS_ERR(com)) { + dev_err(dev, "Failed to register to ultrasoc.\n"); + return PTR_ERR(com); + } + + /* + * record the returned com point in drvdata, + * it will be used to unregister the com + * from ultrasoc. + */ + drvdata->com = com; + return 0; +} + +static int axi_com_probe(struct platform_device *pdev) +{ + struct axi_com_drv_data *drvdata; + int ret; + + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(drvdata->base)) { + dev_err(&pdev->dev, "Failed to ioremap resource.\n"); + return PTR_ERR(drvdata->base); + } + + drvdata->dev = &pdev->dev; + spin_lock_init(&drvdata->ds_msg_list_lock); + spin_lock_init(&drvdata->us_msg_list_lock); + INIT_LIST_HEAD(&drvdata->us_msg_head); + INIT_LIST_HEAD(&drvdata->ds_msg_head); + + axi_com_enable_hw(drvdata); + ret = axi_com_config_inport(drvdata, true); + if (ret) + return ret; + + platform_set_drvdata(pdev, drvdata); + return axi_com_config_com_descp(pdev, drvdata); +} + +static int axi_com_remove(struct platform_device *pdev) +{ + struct axi_com_drv_data *drvdata = platform_get_drvdata(pdev); + int ret; + + if (ultrasoc_unregister_com(drvdata->com) == -EBUSY) + return -EBUSY; + + ret = axi_com_config_inport(drvdata, false); + if (ret) + return ret; + + axi_com_disable_hw(drvdata); + usmsg_list_realse_all(&drvdata->ds_msg_head); + usmsg_list_realse_all(&drvdata->us_msg_head); + + return 0; +} + +static const struct acpi_device_id ultrasoc_axi_com_acpi_match[] = { + {"HISI03B1", }, + {}, +}; + +static struct platform_driver axi_com_driver = { + .driver = { + .name = "ultrasoc,axi-com", + .acpi_match_table = ultrasoc_axi_com_acpi_match, + }, + .probe = axi_com_probe, + .remove = axi_com_remove, +}; +module_platform_driver(axi_com_driver); + +MODULE_DESCRIPTION("Ultrasoc AXI COM driver"); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("Jonathan Zhou jonathan.zhouwen@huawei.com"); +MODULE_AUTHOR("Qi Liu liuqi115@huawei.com"); diff --git a/drivers/hwtracing/ultrasoc/ultrasoc-axi-com.h b/drivers/hwtracing/ultrasoc/ultrasoc-axi-com.h new file mode 100644 index 0000000..64bcf83 --- /dev/null +++ b/drivers/hwtracing/ultrasoc/ultrasoc-axi-com.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (C) 2021 Hisilicon Limited Permission is hereby granted, free of + * charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Code herein communicates with and accesses proprietary hardware which is + * licensed intellectual property (IP) belonging to Siemens Digital Industries + * Software Ltd. + * + * Siemens Digital Industries Software Ltd. asserts and reserves all rights to + * their intellectual property. This paragraph may not be removed or modified + * in any way without permission from Siemens Digital Industries Software Ltd. + */ +#ifndef ULTRASOC_AXI_COM_H +#define ULTRASOC_AXI_COM_H + +#include "ultrasoc.h" + +#define AXIC_US_CTL 0X0 /* Upstream general control */ +#define AXIC_US_DATA 0XC /* Upstream message data */ +#define AXIC_US_BUF_STS 0X10 /* Upstream buffer status */ + +#define AXIC_DS_CTL 0X80 /* Downstream general contral */ +#define AXIC_DS_DATA 0X8C /* Downstream message data */ +#define AXIC_DS_BUF_STS 0X90 /* Downstream buffer status */ +#define AXIC_DS_RD_STS 0X94 /* Downstream read status */ + +#define AXIC_MSG_LEN_PER_SEND 4 +#define AXIC_MSG_LEN_PER_REC 4 +#define AXIC_US_CTL_EN 0x1 +#define AXIC_DS_CTL_EN 0x1 + +struct axi_com_drv_data { + void __iomem *base; + + struct device *dev; + struct ultrasoc_com *com; + + u32 ds_msg_counter; + + u32 us_msg_cur; + spinlock_t us_msg_list_lock; + struct list_head us_msg_head; + + u32 ds_msg_cur; + spinlock_t ds_msg_list_lock; + struct list_head ds_msg_head; +}; + +#endif
On Tue, Jun 15, 2021 at 05:34:43PM +0800, Qi Liu wrote:
This patch adds driver for ultrasoc AXI Communicator. It includes a platform driver to probe AXI Communicator device, a set of operations to access the service data, and a service work entry which will be called by the standard communicator service.
Signed-off-by: Jonathan Zhou jonathan.zhouwen@huawei.com Signed-off-by: Qi Liu liuqi115@huawei.com
drivers/hwtracing/ultrasoc/Kconfig | 9 + drivers/hwtracing/ultrasoc/Makefile | 3 + drivers/hwtracing/ultrasoc/ultrasoc-axi-com.c | 334 ++++++++++++++++++++++++++ drivers/hwtracing/ultrasoc/ultrasoc-axi-com.h | 66 +++++ 4 files changed, 412 insertions(+) create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-axi-com.c create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-axi-com.h
diff --git a/drivers/hwtracing/ultrasoc/Kconfig b/drivers/hwtracing/ultrasoc/Kconfig index 90a3934..77429f3 100644 --- a/drivers/hwtracing/ultrasoc/Kconfig +++ b/drivers/hwtracing/ultrasoc/Kconfig @@ -13,4 +13,13 @@ menuconfig ULTRASOC specification and configure the right series of components when a trace source gets enabled. +if ULTRASOC +config ULTRASOC_AXI_COM
- tristate "Ultrasoc AXI communicator drivers"
- help
This config enables support for Ultrasoc AXI Bus Communicator
drivers. The AXI Communicator has upstream and downstream channels,
the upstream channel is used to transmit user configuration, and
downstream channel to carry response and trace data to the users.
endif diff --git a/drivers/hwtracing/ultrasoc/Makefile b/drivers/hwtracing/ultrasoc/Makefile index a747171..54711a7b 100644 --- a/drivers/hwtracing/ultrasoc/Makefile +++ b/drivers/hwtracing/ultrasoc/Makefile @@ -5,3 +5,6 @@ obj-$(CONFIG_ULTRASOC) += ultrasoc-drv.o ultrasoc-drv-objs := ultrasoc.o
+obj-$(CONFIG_ULTRASOC_AXI_COM) += ultrasoc-axi-com-drv.o +ultrasoc-axi-com-drv-objs := ultrasoc-axi-com.o diff --git a/drivers/hwtracing/ultrasoc/ultrasoc-axi-com.c b/drivers/hwtracing/ultrasoc/ultrasoc-axi-com.c new file mode 100644 index 0000000..af153dd --- /dev/null +++ b/drivers/hwtracing/ultrasoc/ultrasoc-axi-com.c @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: MIT +/*
- Copyright (C) 2021 Hisilicon Limited Permission is hereby granted, free of
- charge, to any person obtaining a copy of this software and associated
- documentation files (the "Software"), to deal in the Software without
- restriction, including without limitation the rights to use, copy, modify,
- merge, publish, distribute, sublicense, and/or sell copies of the Software,
- and to permit persons to whom the Software is furnished to do so, subject
- to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- IN THE SOFTWARE.
- Code herein communicates with and accesses proprietary hardware which is
- licensed intellectual property (IP) belonging to Siemens Digital Industries
- Software Ltd.
- Siemens Digital Industries Software Ltd. asserts and reserves all rights to
- their intellectual property. This paragraph may not be removed or modified
- in any way without permission from Siemens Digital Industries Software Ltd.
- */
+#include <linux/acpi.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <asm/unaligned.h>
+#include "ultrasoc-axi-com.h"
+static void axi_com_enable_hw(struct axi_com_drv_data *drvdata) +{
- u32 val;
- val = readl(drvdata->base + AXIC_US_CTL);
- val |= AXIC_US_CTL_EN;
- writel(val, drvdata->base + AXIC_US_CTL);
- val = readl(drvdata->base + AXIC_DS_CTL);
- val |= AXIC_DS_CTL_EN;
- writel(val, drvdata->base + AXIC_DS_CTL);
+}
+static void axi_com_disable_hw(struct axi_com_drv_data *drvdata) +{
- u32 val;
- val = readl(drvdata->base + AXIC_US_CTL);
- val &= ~AXIC_US_CTL_EN;
- writel(val, drvdata->base + AXIC_US_CTL);
- val = readl(drvdata->base + AXIC_DS_CTL);
- val &= ~AXIC_DS_CTL_EN;
- writel(val, drvdata->base + AXIC_DS_CTL);
+}
+static inline bool axi_com_us_buf_full(struct axi_com_drv_data *drvdata) +{
- return readl(drvdata->base + AXIC_US_BUF_STS) & BIT(0);
+}
+static inline bool axi_com_ds_buf_full(struct axi_com_drv_data *drvdata) +{
- return readl(drvdata->base + AXIC_DS_BUF_STS) & BIT(0);
+}
+static int axi_com_try_send_msg(struct axi_com_drv_data *drvdata) +{
- struct msg_descp *msg;
- struct list_head *node;
- int index = 0;
- int unsent;
- u32 data;
- if (axi_com_us_buf_full(drvdata)) {
dev_err_once(drvdata->dev, "No room for upstream buffer.\n");
return US_SERVICE_IDLE;
- }
- spin_lock(&drvdata->us_msg_list_lock);
- if (list_empty(&drvdata->us_msg_head)) {
spin_unlock(&drvdata->us_msg_list_lock);
return US_SERVICE_IDLE;
- }
- node = drvdata->us_msg_head.next;
- list_del(node);
- drvdata->us_msg_cur--;
- msg = container_of(node, struct msg_descp, node);
- spin_unlock(&drvdata->us_msg_list_lock);
- unsent = msg->msg_len;
- dev_dbg(drvdata->dev, "Length of send msg: %d.\n", msg->msg_len);
- while (unsent > 0) {
data = get_unaligned_le32(&msg->msg_buf[index++]);
writel(data, drvdata->base + AXIC_US_DATA);
Who reads the data that gets written here?
unsent -= AXIC_MSG_LEN_PER_SEND;
- }
- kfree(msg);
- return US_SERVICE_ONWORK;
+}
+static int axi_com_try_recv_msg(struct axi_com_drv_data *drvdata) +{
- struct msg_descp tmp_msg = {0};
- struct msg_descp *msg;
- bool lost = false;
- u32 index = 0;
- u32 status, entries, data;
- if (!axi_com_ds_buf_full(drvdata))
return US_SERVICE_IDLE;
- msg = kzalloc(sizeof(*msg), GFP_KERNEL);
- if (!msg) {
/*
* create local variable tmp_msg to read and clear
* the downstream message.
*/
msg = &tmp_msg;
lost = true;
- }
- do {
if (index == USMSG_MAX_IDX) {
dev_warn(drvdata->dev, "Illegal message.\n");
break;
}
data = readl(drvdata->base + AXIC_DS_DATA);
Same thing - who writes the data that gets read here?
put_unaligned_le32(data, &msg->msg_buf[index++]);
status = readl(drvdata->base + AXIC_DS_RD_STS);
entries = status & GENMASK(7, 4);
msg->msg_len += AXIC_MSG_LEN_PER_REC;
- } while (entries != 0);
- if (!lost) {
spin_lock(&drvdata->ds_msg_list_lock);
drvdata->ds_msg_cur++;
drvdata->ds_msg_counter++;
list_add_tail(&msg->node, &drvdata->ds_msg_head);
spin_unlock(&drvdata->ds_msg_list_lock);
- }
- return US_SERVICE_ONWORK;
+}
+static int axi_com_work(struct ultrasoc_com *uscom) +{
- struct axi_com_drv_data *drvdata = ultrasoc_com_get_drvdata(uscom);
- int us_ds_flag;
- us_ds_flag = axi_com_try_recv_msg(drvdata);
- us_ds_flag |= axi_com_try_send_msg(drvdata);
Why does this work has to be done in ultrasoc.c when (as far as I can see) it could be done as part of this driver using something like smp_call_function_single()?
- return us_ds_flag;
+}
+static ssize_t axi_com_show_status(struct ultrasoc_com *uscom, char *buf,
ssize_t wr_size)
+{
- struct axi_com_drv_data *drvdata = ultrasoc_com_get_drvdata(uscom);
- wr_size += sysfs_emit_at(buf, wr_size, "%-20s: %d\n",
"ds msg list num", drvdata->ds_msg_cur);
- wr_size += sysfs_emit_at(buf, wr_size, "%-20s: %d\n",
"us msg list num", drvdata->us_msg_cur);
Same comment as smb.
- return wr_size;
+}
+static void axi_com_put_raw_msg(struct ultrasoc_com *uscom, int msg_size,
unsigned long long msg_data)
+{
- struct axi_com_drv_data *drvdata = ultrasoc_com_get_drvdata(uscom);
- struct msg_descp *p_msg;
- p_msg = kmalloc(sizeof(*p_msg), GFP_KERNEL);
- if (!p_msg)
return;
- p_msg->msg_len = msg_size;
- put_unaligned_le64(msg_data, &p_msg->msg_buf[0]);
- spin_lock(&drvdata->us_msg_list_lock);
- list_add_tail(&p_msg->node, &drvdata->us_msg_head);
- drvdata->us_msg_cur++;
- spin_unlock(&drvdata->us_msg_list_lock);
- if (uscom->service_status != ULTRASOC_COM_SERVICE_STOPPED)
wake_up_process(uscom->service);
- else
dev_warn(uscom->dev, "Com service is not running.\n");
+}
+static struct uscom_ops axi_com_ops = {
- .com_status = axi_com_show_status,
- .put_raw_msg = axi_com_put_raw_msg,
+};
+/*
- Config hardwares on the tracing path, using DSM calls to avoid exposing
- hardware message format.
- */
+static int axi_com_config_inport(struct axi_com_drv_data *drvdata, bool enable) +{
- struct device *dev = drvdata->dev;
- u32 flag = enable ? 1 : 0;
- union acpi_object *obj;
- guid_t guid;
- if (guid_parse("82ae1283-7f6a-4cbe-aa06-53e8fb24db18", &guid)) {
dev_err(dev, "Get GUID failed.\n");
return -EINVAL;
- }
- obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), &guid, 0, flag, NULL);
- if (!obj)
dev_err(dev, "ACPI handle failed!\n");
- ACPI_FREE(obj);
- return 0;
+}
+static int axi_com_config_com_descp(struct platform_device *pdev,
struct axi_com_drv_data *drvdata)
+{
- struct device *parent = pdev->dev.parent;
- struct ultrasoc_com_descp com_descp = {0};
- struct device *dev = &pdev->dev;
- struct ultrasoc_com *com;
- com_descp.name = pdev->name;
- com_descp.com_type = ULTRASOC_COM_TYPE_BOTH;
Both what? From this I have to guess there is a dual purpose to this specific type but there is no documentation as to what that can be. This set is difficult to review because of the general lack of documention.
- com_descp.com_dev = dev;
- com_descp.uscom_ops = &axi_com_ops;
- com_descp.com_work = axi_com_work;
- if (device_property_read_u64(dev, "ultrasoc,default_route",
&com_descp.default_route_msg)) {
dev_err(dev, "Failed to read default_route!\n");
return -EINVAL;
- }
- com = ultrasoc_register_com(parent, &com_descp);
- if (IS_ERR(com)) {
dev_err(dev, "Failed to register to ultrasoc.\n");
return PTR_ERR(com);
- }
- /*
* record the returned com point in drvdata,
* it will be used to unregister the com
* from ultrasoc.
*/
- drvdata->com = com;
- return 0;
+}
+static int axi_com_probe(struct platform_device *pdev) +{
- struct axi_com_drv_data *drvdata;
- int ret;
- drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata)
return -ENOMEM;
- drvdata->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(drvdata->base)) {
dev_err(&pdev->dev, "Failed to ioremap resource.\n");
return PTR_ERR(drvdata->base);
- }
- drvdata->dev = &pdev->dev;
- spin_lock_init(&drvdata->ds_msg_list_lock);
- spin_lock_init(&drvdata->us_msg_list_lock);
- INIT_LIST_HEAD(&drvdata->us_msg_head);
- INIT_LIST_HEAD(&drvdata->ds_msg_head);
- axi_com_enable_hw(drvdata);
- ret = axi_com_config_inport(drvdata, true);
- if (ret)
return ret;
- platform_set_drvdata(pdev, drvdata);
- return axi_com_config_com_descp(pdev, drvdata);
+}
+static int axi_com_remove(struct platform_device *pdev) +{
- struct axi_com_drv_data *drvdata = platform_get_drvdata(pdev);
- int ret;
- if (ultrasoc_unregister_com(drvdata->com) == -EBUSY)
return -EBUSY;
- ret = axi_com_config_inport(drvdata, false);
- if (ret)
return ret;
- axi_com_disable_hw(drvdata);
- usmsg_list_realse_all(&drvdata->ds_msg_head);
- usmsg_list_realse_all(&drvdata->us_msg_head);
- return 0;
+}
+static const struct acpi_device_id ultrasoc_axi_com_acpi_match[] = {
- {"HISI03B1", },
- {},
+};
No need for MODULE_DEVICE_TABLE()?
I am very confused as to what this IP does... And I'm even more confused as to why ultrasoc.c is needed at all. As I pointed out in a previous comment there is a lot of work to do on this patchset but there is no point in writing more while questions about the current design choices are pending.
I am done reviewing this set.
Thanks, Mathieu
+static struct platform_driver axi_com_driver = {
- .driver = {
.name = "ultrasoc,axi-com",
.acpi_match_table = ultrasoc_axi_com_acpi_match,
- },
- .probe = axi_com_probe,
- .remove = axi_com_remove,
+}; +module_platform_driver(axi_com_driver);
+MODULE_DESCRIPTION("Ultrasoc AXI COM driver"); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("Jonathan Zhou jonathan.zhouwen@huawei.com"); +MODULE_AUTHOR("Qi Liu liuqi115@huawei.com"); diff --git a/drivers/hwtracing/ultrasoc/ultrasoc-axi-com.h b/drivers/hwtracing/ultrasoc/ultrasoc-axi-com.h new file mode 100644 index 0000000..64bcf83 --- /dev/null +++ b/drivers/hwtracing/ultrasoc/ultrasoc-axi-com.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: MIT */ +/*
- Copyright (C) 2021 Hisilicon Limited Permission is hereby granted, free of
- charge, to any person obtaining a copy of this software and associated
- documentation files (the "Software"), to deal in the Software without
- restriction, including without limitation the rights to use, copy, modify,
- merge, publish, distribute, sublicense, and/or sell copies of the Software,
- and to permit persons to whom the Software is furnished to do so, subject
- to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- IN THE SOFTWARE.
- Code herein communicates with and accesses proprietary hardware which is
- licensed intellectual property (IP) belonging to Siemens Digital Industries
- Software Ltd.
- Siemens Digital Industries Software Ltd. asserts and reserves all rights to
- their intellectual property. This paragraph may not be removed or modified
- in any way without permission from Siemens Digital Industries Software Ltd.
- */
+#ifndef ULTRASOC_AXI_COM_H +#define ULTRASOC_AXI_COM_H
+#include "ultrasoc.h"
+#define AXIC_US_CTL 0X0 /* Upstream general control */ +#define AXIC_US_DATA 0XC /* Upstream message data */ +#define AXIC_US_BUF_STS 0X10 /* Upstream buffer status */
+#define AXIC_DS_CTL 0X80 /* Downstream general contral */ +#define AXIC_DS_DATA 0X8C /* Downstream message data */ +#define AXIC_DS_BUF_STS 0X90 /* Downstream buffer status */ +#define AXIC_DS_RD_STS 0X94 /* Downstream read status */
+#define AXIC_MSG_LEN_PER_SEND 4 +#define AXIC_MSG_LEN_PER_REC 4 +#define AXIC_US_CTL_EN 0x1 +#define AXIC_DS_CTL_EN 0x1
+struct axi_com_drv_data {
- void __iomem *base;
- struct device *dev;
- struct ultrasoc_com *com;
- u32 ds_msg_counter;
- u32 us_msg_cur;
- spinlock_t us_msg_list_lock;
- struct list_head us_msg_head;
- u32 ds_msg_cur;
- spinlock_t ds_msg_list_lock;
- struct list_head ds_msg_head;
+};
+#endif
2.7.4
Hi Mathieu,
On 2021/6/30 5:22, Mathieu Poirier wrote:
On Tue, Jun 15, 2021 at 05:34:43PM +0800, Qi Liu wrote:
This patch adds driver for ultrasoc AXI Communicator. It includes a platform driver to probe AXI Communicator device, a set of operations to access the service data, and a service work entry which will be called by the standard communicator service.
Signed-off-by: Jonathan Zhou jonathan.zhouwen@huawei.com Signed-off-by: Qi Liu liuqi115@huawei.com
[...]
+static const struct acpi_device_id ultrasoc_axi_com_acpi_match[] = {
- {"HISI03B1", },
- {},
+};
No need for MODULE_DEVICE_TABLE()?
I am very confused as to what this IP does... And I'm even more confused as to why ultrasoc.c is needed at all. As I pointed out in a previous comment there is a lot of work to do on this patchset but there is no point in writing more while questions about the current design choices are pending. thanks for reviewing this patch.
This module is used on Hip08 platform, to store trace data from ETM, you can find the data path diagram in kernel document patch.
And this module is developed by Ultrasoc technology, which is acquired by Siemens, we still use "Ultrasoc" to name document and structures.
At the beginning we use the ultrasoc.c as a framework to adapt multiple hardware devices and support more capabilities. But after discussing with suppliers, we are only allowed to upstream the axi-com and smb driver.
So the software architecture seems unreasonable now, I'll refactor it in next version, thanks.
Qi
I am done reviewing this set.
Thanks, Mathieu
+static struct platform_driver axi_com_driver = {
- .driver = {
.name = "ultrasoc,axi-com",
.acpi_match_table = ultrasoc_axi_com_acpi_match,
- },
- .probe = axi_com_probe,
- .remove = axi_com_remove,
+}; +module_platform_driver(axi_com_driver);
+MODULE_DESCRIPTION("Ultrasoc AXI COM driver"); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("Jonathan Zhou jonathan.zhouwen@huawei.com"); +MODULE_AUTHOR("Qi Liu liuqi115@huawei.com"); diff --git a/drivers/hwtracing/ultrasoc/ultrasoc-axi-com.h b/drivers/hwtracing/ultrasoc/ultrasoc-axi-com.h new file mode 100644 index 0000000..64bcf83 --- /dev/null +++ b/drivers/hwtracing/ultrasoc/ultrasoc-axi-com.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: MIT */ +/*
- Copyright (C) 2021 Hisilicon Limited Permission is hereby granted, free of
- charge, to any person obtaining a copy of this software and associated
- documentation files (the "Software"), to deal in the Software without
- restriction, including without limitation the rights to use, copy, modify,
- merge, publish, distribute, sublicense, and/or sell copies of the Software,
- and to permit persons to whom the Software is furnished to do so, subject
- to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- IN THE SOFTWARE.
- Code herein communicates with and accesses proprietary hardware which is
- licensed intellectual property (IP) belonging to Siemens Digital Industries
- Software Ltd.
- Siemens Digital Industries Software Ltd. asserts and reserves all rights to
- their intellectual property. This paragraph may not be removed or modified
- in any way without permission from Siemens Digital Industries Software Ltd.
- */
+#ifndef ULTRASOC_AXI_COM_H +#define ULTRASOC_AXI_COM_H
+#include "ultrasoc.h"
+#define AXIC_US_CTL 0X0 /* Upstream general control */ +#define AXIC_US_DATA 0XC /* Upstream message data */ +#define AXIC_US_BUF_STS 0X10 /* Upstream buffer status */
+#define AXIC_DS_CTL 0X80 /* Downstream general contral */ +#define AXIC_DS_DATA 0X8C /* Downstream message data */ +#define AXIC_DS_BUF_STS 0X90 /* Downstream buffer status */ +#define AXIC_DS_RD_STS 0X94 /* Downstream read status */
+#define AXIC_MSG_LEN_PER_SEND 4 +#define AXIC_MSG_LEN_PER_REC 4 +#define AXIC_US_CTL_EN 0x1 +#define AXIC_DS_CTL_EN 0x1
+struct axi_com_drv_data {
- void __iomem *base;
- struct device *dev;
- struct ultrasoc_com *com;
- u32 ds_msg_counter;
- u32 us_msg_cur;
- spinlock_t us_msg_list_lock;
- struct list_head us_msg_head;
- u32 ds_msg_cur;
- spinlock_t ds_msg_list_lock;
- struct list_head ds_msg_head;
+};
+#endif
2.7.4
.
This patch adds driver for System Memory Buffer. It includes a platform driver for the SMB device.
Signed-off-by: Jonathan Zhou jonathan.zhouwen@huawei.com Signed-off-by: Qi Liu liuqi115@huawei.com --- drivers/hwtracing/ultrasoc/Kconfig | 9 + drivers/hwtracing/ultrasoc/Makefile | 3 + drivers/hwtracing/ultrasoc/ultrasoc-smb.c | 663 ++++++++++++++++++++++++++++++ drivers/hwtracing/ultrasoc/ultrasoc-smb.h | 182 ++++++++ 4 files changed, 857 insertions(+) create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-smb.c create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-smb.h
diff --git a/drivers/hwtracing/ultrasoc/Kconfig b/drivers/hwtracing/ultrasoc/Kconfig index 77429f3..8899949 100644 --- a/drivers/hwtracing/ultrasoc/Kconfig +++ b/drivers/hwtracing/ultrasoc/Kconfig @@ -22,4 +22,13 @@ config ULTRASOC_AXI_COM the upstream channel is used to transmit user configuration, and downstream channel to carry response and trace data to the users.
+config ULTRASOC_SMB + tristate "Ultrasoc System memory buffer drivers" + help + This config enables support for Ultrasoc System Memory Buffer + drivers. The System Memory Buffer provides a way to buffer and + store messages in system memory. It provides a capability to + store messages received on its input message interface to an + area of system memory. + endif diff --git a/drivers/hwtracing/ultrasoc/Makefile b/drivers/hwtracing/ultrasoc/Makefile index 54711a7b..b174ca8 100644 --- a/drivers/hwtracing/ultrasoc/Makefile +++ b/drivers/hwtracing/ultrasoc/Makefile @@ -8,3 +8,6 @@ ultrasoc-drv-objs := ultrasoc.o
obj-$(CONFIG_ULTRASOC_AXI_COM) += ultrasoc-axi-com-drv.o ultrasoc-axi-com-drv-objs := ultrasoc-axi-com.o + +obj-$(CONFIG_ULTRASOC_SMB) += ultrasoc-smb-drv.o +ultrasoc-smb-drv-objs := ultrasoc-smb.o diff --git a/drivers/hwtracing/ultrasoc/ultrasoc-smb.c b/drivers/hwtracing/ultrasoc/ultrasoc-smb.c new file mode 100644 index 0000000..ce03f5e --- /dev/null +++ b/drivers/hwtracing/ultrasoc/ultrasoc-smb.c @@ -0,0 +1,663 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2021 Hisilicon Limited Permission is hereby granted, free of + * charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Code herein communicates with and accesses proprietary hardware which is + * licensed intellectual property (IP) belonging to Siemens Digital Industries + * Software Ltd. + * + * Siemens Digital Industries Software Ltd. asserts and reserves all rights to + * their intellectual property. This paragraph may not be removed or modified + * in any way without permission from Siemens Digital Industries Software Ltd. + */ + +#include <linux/circ_buf.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> + +#include "ultrasoc-smb.h" + +static inline int smb_hw_buffer_empty(struct smb_drv_data *drvdata) +{ + u32 buf_status = readl(drvdata->base + SMB_LB_INT_STS); + + return buf_status & BIT(0) ? 0 : 1; +} + +static inline int smb_buffer_pointer_pos(struct smb_drv_data *drvdata) +{ + u32 wr_offset, rd_offset; + + wr_offset = readl(drvdata->base + SMB_LB_WR_ADDR); + rd_offset = readl(drvdata->base + SMB_LB_RD_ADDR); + return wr_offset == rd_offset; +} + +static inline int smb_hw_buffer_full(struct smb_drv_data *drvdata) +{ + return smb_buffer_pointer_pos(drvdata) && !smb_hw_buffer_empty(drvdata); +} + +static inline void smb_clear_buf_status(struct smb_drv_data *drvdata) +{ + struct smb_data_buffer *sdb = &drvdata->smb_db; + + if (smb_buffer_pointer_pos(drvdata) && !sdb->to_copy) + writel(0xf, drvdata->base + SMB_LB_INT_STS); +} + +static void smb_update_hw_write_size(struct smb_drv_data *drvdata) +{ + struct smb_data_buffer *sdb = &drvdata->smb_db; + u32 write_offset, write_base; + + sdb->lost = false; + writel(0x1, drvdata->base + SMB_LB_PURGE); + if (smb_hw_buffer_empty(drvdata)) { + sdb->to_copy = 0; + return; + } + + if (smb_hw_buffer_full(drvdata)) { + sdb->to_copy = sdb->buf_size; + return; + } + + write_base = sdb->buf_base_phys & SMB_BUF_WRITE_BASE; + write_offset = readl(drvdata->base + SMB_LB_WR_ADDR) - write_base; + sdb->to_copy = CIRC_CNT(write_offset, sdb->rd_offset, sdb->buf_size); +} + +static int smb_open(struct inode *inode, struct file *file) +{ + struct smb_drv_data *drvdata = container_of(file->private_data, + struct smb_drv_data, miscdev); + + if (local_cmpxchg(&drvdata->reading, 0, 1)) + return -EBUSY; + + smb_update_hw_write_size(drvdata); + return 0; +} + +static ssize_t smb_read(struct file *file, char __user *data, + size_t len, loff_t *ppos) +{ + struct smb_drv_data *drvdata = container_of(file->private_data, + struct smb_drv_data, miscdev); + struct smb_data_buffer *sdb = &drvdata->smb_db; + struct device *dev = &drvdata->csdev->dev; + unsigned long to_copy = sdb->to_copy; + + if (!to_copy) { + smb_update_hw_write_size(drvdata); + to_copy = sdb->to_copy; + if (!to_copy) + return to_copy; + } + + to_copy = min(to_copy, len); + /* + * if the read needs to cross the boundary of the data buffer, copy + * last datas of the buffer to user + */ + if (sdb->rd_offset + to_copy > sdb->buf_size) + to_copy = sdb->buf_size - sdb->rd_offset; + + if (copy_to_user(data, (void *)sdb->buf_base + sdb->rd_offset, to_copy)) { + dev_dbg(dev, "Failed to copy data to user.\n"); + return -EFAULT; + } + + *ppos += to_copy; + sdb->rd_offset += to_copy; + sdb->rd_offset %= sdb->buf_size; + sdb->to_copy -= to_copy; + + /* update the read point */ + writel(sdb->buf_base_phys + sdb->rd_offset, + drvdata->base + SMB_LB_RD_ADDR); + smb_clear_buf_status(drvdata); + dev_dbg(dev, "%lu bytes copied.\n", to_copy); + + return to_copy; +} + +static int smb_release(struct inode *inode, struct file *file) +{ + struct smb_drv_data *drvdata = container_of(file->private_data, + struct smb_drv_data, miscdev); + local_set(&drvdata->reading, 0); + return 0; +} + +static const struct file_operations smb_fops = { + .owner = THIS_MODULE, + .open = smb_open, + .read = smb_read, + .release = smb_release, + .llseek = no_llseek, +}; + +static ssize_t smb_show_status(struct ultrasoc_com *com, char *buf, + ssize_t wr_size) +{ + struct smb_drv_data *drvdata; + u32 value; + + drvdata = dev_get_drvdata(com->dev); + value = readl(drvdata->base + SMB_LB_INT_STS); + wr_size += sysfs_emit_at(buf, wr_size, "%-20s: 0x%08x\n", + "interrupt status", value); + value = readl(drvdata->base + SMB_LB_WR_ADDR); + wr_size += sysfs_emit_at(buf, wr_size, "%-20s: %#x\n", "write point", + value); + value = readl(drvdata->base + SMB_LB_RD_ADDR); + wr_size += sysfs_emit_at(buf, wr_size, "%-20s: %#x\n", "read point", + value); + + return wr_size; +} + +static int smb_init_data_buffer(struct platform_device *pdev, + struct smb_data_buffer *sdb) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (IS_ERR(res)) { + dev_err(&pdev->dev, "SMB device without data buffer.\n"); + return -EINVAL; + } + + sdb->buf_base_phys = res->start; + sdb->buf_size = resource_size(res); + if (sdb->buf_size == 0) + return -EINVAL; + + sdb->buf_base = ioremap_cache(sdb->buf_base_phys, sdb->buf_size); + if (sdb->buf_base == NULL) + return -ENOMEM; + + sdb->buf_cfg_mode = SMB_BUF_CFG_STREAMING; + return 0; +} + +static void smb_release_data_buffer(struct smb_drv_data *drvdata) +{ + struct smb_data_buffer *sdb = &drvdata->smb_db; + + if (sdb->buf_base) + iounmap(sdb->buf_base); +} + +static struct uscom_ops smb_ops = { + .com_status = smb_show_status, + .put_raw_msg = NULL, +}; + +static int smb_set_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle); + +static void smb_enable_hw(struct smb_drv_data *drvdata) +{ + writel(0x1, drvdata->base + SMB_GLOBAL_EN); +} + +static void smb_disable_hw(struct smb_drv_data *drvdata) +{ + writel(0x1, drvdata->base + SMB_LB_PURGE); + writel(0x0, drvdata->base + SMB_GLOBAL_EN); +} + +static int smb_enable_sysfs(struct coresight_device *csdev) +{ + struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); + unsigned long flags; + int ret = 0; + + WARN_ON_ONCE(drvdata == NULL); + + spin_lock_irqsave(&drvdata->spinlock, flags); + + /* Don't messup with perf sessions. */ + if (drvdata->mode == CS_MODE_PERF) { + ret = -EBUSY; + goto out; + } + + if (drvdata->mode == CS_MODE_DISABLED) { + smb_enable_hw(drvdata); + drvdata->mode = CS_MODE_SYSFS; + } + atomic_inc(csdev->refcnt); +out: + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return ret; +} + +static int smb_enable_perf(struct coresight_device *csdev, void *data) +{ + struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); + struct device *dev = &drvdata->csdev->dev; + struct perf_output_handle *handle = data; + unsigned long flags; + int ret = 0; + pid_t pid; + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->mode == CS_MODE_SYSFS) { + dev_err(dev, "Device is already in used by sysfs.\n"); + ret = -EBUSY; + goto out; + } + + /* Get a handle on the pid of the target process*/ + pid = task_pid_nr(handle->event->owner); + if (drvdata->pid != -1 && drvdata->pid != pid) { + ret = -EBUSY; + goto out; + } + + /* + * No HW configuration is needed if the sink is already in + * use for this session. + */ + if (drvdata->pid == pid) { + atomic_inc(csdev->refcnt); + goto out; + } + + /* + * We don't have an internal state to clean up if we fail to setup + * the perf buffer. So we can perform the step before we turn the + * ETB on and leave without cleaning up. + */ + ret = smb_set_buffer(csdev, handle); + if (ret) + goto out; + + smb_enable_hw(drvdata); + drvdata->pid = pid; + drvdata->mode = CS_MODE_PERF; + atomic_inc(csdev->refcnt); +out: + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return ret; +} + +static int smb_enable(struct coresight_device *csdev, u32 mode, void *data) +{ + int ret; + + switch (mode) { + case CS_MODE_SYSFS: + ret = smb_enable_sysfs(csdev); + break; + case CS_MODE_PERF: + ret = smb_enable_perf(csdev, data); + break; + default: + ret = -EINVAL; + break; + } + + if (ret) + return ret; + + dev_dbg(&csdev->dev, "Ultrasoc smb enabled.\n"); + + return 0; +} + +static int smb_disable(struct coresight_device *csdev) +{ + struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); + unsigned long flags; + + spin_lock_irqsave(&drvdata->spinlock, flags); + + if (atomic_dec_return(csdev->refcnt)) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return -EBUSY; + } + + /* Complain if we (somehow) got out of sync */ + WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED); + smb_disable_hw(drvdata); + + /* Dissociate from the target process. */ + drvdata->pid = -1; + drvdata->mode = CS_MODE_DISABLED; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + dev_dbg(&csdev->dev, "Ultrasoc smb disabled.\n"); + return 0; +} + +static void smb_set_default_hw(struct smb_drv_data *drvdata) +{ + struct smb_data_buffer *sdb = &drvdata->smb_db; + u32 value, base_hi, base_lo, limit_lo; + + /* first disable smb and clear the status of SMB buffer */ + smb_disable_hw(drvdata); + smb_clear_buf_status(drvdata); + + /* using smb in single-end mode, and set other configures default */ + value = sdb->buf_cfg_mode | SMB_BUF_SINGLE_END | SMB_BUF_ENABLE; + writel(value, drvdata->base + SMB_LB_CFG_LO); + value = SMB_MSG_FILTER(0x0, 0xff); + writel(value, drvdata->base + SMB_LB_CFG_HI); + + writel(HISI_SMB_GLOBAL_CFG, drvdata->base + SMB_GLOBAL_CFG); + writel(HISI_SMB_GLB_INT_CFG, drvdata->base + SMB_GLOBAL_INT); + writel(HISI_SMB_BUF_INT_CFG, drvdata->base + SMB_LB_INT_CTRL); + + /* config hardware registers according to physical base of SMB buffer */ + base_hi = sdb->buf_base_phys >> 32; + base_lo = sdb->buf_base_phys & SMB_BUF_WRITE_BASE; + limit_lo = base_lo + sdb->buf_size; + writel(base_lo, drvdata->base + SMB_LB_BASE_LO); + writel(base_hi, drvdata->base + SMB_LB_BASE_HI); + writel(limit_lo, drvdata->base + SMB_LB_LIMIT); + + /* initial hardware read-ptr address*/ + writel(base_lo, drvdata->base + SMB_LB_RD_ADDR); +} + +static void *smb_alloc_buffer(struct coresight_device *csdev, + struct perf_event *event, void **pages, + int nr_pages, bool overwrite) +{ + struct cs_buffers *buf; + int node; + + node = (event->cpu == -1) ? NUMA_NO_NODE : cpu_to_node(event->cpu); + buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node); + if (!buf) + return NULL; + + buf->snapshot = overwrite; + buf->nr_pages = nr_pages; + buf->data_pages = pages; + + return buf; +} + +static void smb_free_buffer(void *config) +{ + struct cs_buffers *buf = config; + + kfree(buf); +} + +static int smb_set_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle) +{ + struct cs_buffers *buf = etm_perf_sink_config(handle); + u32 head; + + if (!buf) + return -EINVAL; + + /* wrap head around to the amount of space we have */ + head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1); + + /* find the page to write to and offset within that page */ + buf->cur = head / PAGE_SIZE; + buf->offset = head % PAGE_SIZE; + + local_set(&buf->data_size, 0); + + return 0; +} + +static void smb_sync_perf_buffer(struct smb_drv_data *drvdata, + struct cs_buffers *buf, unsigned long to_copy) +{ + struct smb_data_buffer *sdb = &drvdata->smb_db; + char **dst_pages = (char **)buf->data_pages; + u32 buf_offset = buf->offset; + u32 cur = buf->cur; + u32 bytes; + + while (to_copy > 0) { + /* + * if the read needs to cross the boundary of the data buffer, + * copy last datas of the buffer to user + */ + if (sdb->rd_offset + PAGE_SIZE - buf_offset > sdb->buf_size) + bytes = sdb->buf_size - sdb->rd_offset; + else + bytes = min(to_copy, PAGE_SIZE - buf_offset); + + memcpy_fromio(dst_pages[cur] + buf_offset, + sdb->buf_base + sdb->rd_offset, bytes); + + buf_offset += bytes; + if (buf_offset >= PAGE_SIZE) { + buf_offset = 0; + cur++; + cur %= buf->nr_pages; + } + to_copy -= bytes; + /* ensure memcpy finished before update the read pointer */ + sdb->rd_offset += bytes; + sdb->rd_offset %= sdb->buf_size; + } + + writel(sdb->buf_base_phys + sdb->rd_offset, + drvdata->base + SMB_LB_RD_ADDR); + sdb->to_copy = to_copy; +} + +static unsigned long smb_update_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, void *sink_config) +{ + struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); + struct smb_data_buffer *sdb = &drvdata->smb_db; + struct cs_buffers *buf = sink_config; + u64 to_copy; + + if (!buf) + return 0; + + smb_update_hw_write_size(drvdata); + to_copy = sdb->to_copy; + if (to_copy > handle->size) { + sdb->rd_offset += (to_copy - handle->size); + sdb->rd_offset %= sdb->buf_size; + to_copy = handle->size; + sdb->lost = true; + } + + smb_sync_perf_buffer(drvdata, buf, to_copy); + smb_clear_buf_status(drvdata); + if (!buf->snapshot && sdb->lost) + perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + + return to_copy; +} + +static const struct coresight_ops_sink smb_cs_ops = { + .enable = smb_enable, + .disable = smb_disable, + .alloc_buffer = smb_alloc_buffer, + .free_buffer = smb_free_buffer, + .update_buffer = smb_update_buffer, +}; + +static const struct coresight_ops cs_ops = { + .sink_ops = &smb_cs_ops, +}; + +static int smb_init_res(struct platform_device *pdev, + struct smb_drv_data *drvdata) +{ + struct smb_data_buffer *sdb; + int ret; + + sdb = &drvdata->smb_db; + drvdata->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(drvdata->base)) { + dev_err(&pdev->dev, "Failed to ioremap resource.\n"); + return PTR_ERR(drvdata->base); + } + + ret = smb_init_data_buffer(pdev, sdb); + if (ret) + dev_err(&pdev->dev, "Failed to init buffer, ret = %d.\n", ret); + + return ret; +} + +DEFINE_CORESIGHT_DEVLIST(sink_devs, "sink_smb"); +static int smb_register_sink(struct platform_device *pdev, + struct smb_drv_data *drvdata) +{ + struct coresight_platform_data *pdata = NULL; + struct coresight_desc desc = { 0 }; + int ret; + + pdata = coresight_get_platform_data(&pdev->dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + + drvdata->dev->platform_data = pdata; + desc.type = CORESIGHT_DEV_TYPE_SINK; + desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; + desc.ops = &cs_ops; + desc.pdata = pdata; + desc.dev = &pdev->dev; + desc.name = coresight_alloc_device_name(&sink_devs, &pdev->dev); + if (!desc.name) { + dev_err(&pdev->dev, "Failed to alloc coresight device name."); + return -ENOMEM; + } + + drvdata->csdev = coresight_register(&desc); + if (IS_ERR(drvdata->csdev)) + return PTR_ERR(drvdata->csdev); + + drvdata->miscdev.name = desc.name; + drvdata->miscdev.minor = MISC_DYNAMIC_MINOR; + drvdata->miscdev.fops = &smb_fops; + ret = misc_register(&drvdata->miscdev); + if (ret) { + coresight_unregister(drvdata->csdev); + dev_err(&pdev->dev, "Failed to register misc, ret=%d\n", ret); + } + + return ret; +} + +static void smb_unregister_sink(struct smb_drv_data *drvdata) +{ + misc_deregister(&drvdata->miscdev); + coresight_unregister(drvdata->csdev); +} + +static int smb_config_com_descp(struct platform_device *pdev, + struct smb_drv_data *drvdata) +{ + struct device *parent = pdev->dev.parent; + struct ultrasoc_com_descp com_descp = {0}; + struct device *dev = &pdev->dev; + struct ultrasoc_com *com; + + com_descp.name = pdev->name; + com_descp.com_type = ULTRASOC_COM_TYPE_DOWN; + com_descp.com_dev = dev; + com_descp.uscom_ops = &smb_ops; + com = ultrasoc_register_com(parent, &com_descp); + if (IS_ERR(com)) { + dev_err(dev, "Failed to register smb com.\n"); + return PTR_ERR(com); + } + + drvdata->com = com; + return 0; +} + +static int smb_probe(struct platform_device *pdev) +{ + struct smb_drv_data *drvdata; + int ret; + + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + ret = smb_init_res(pdev, drvdata); + if (ret) + return ret; + + smb_set_default_hw(drvdata); + spin_lock_init(&drvdata->spinlock); + drvdata->dev = &pdev->dev; + drvdata->pid = -1; + + ret = smb_config_com_descp(pdev, drvdata); + if (ret) + return ret; + + ret = smb_register_sink(pdev, drvdata); + if (ret) { + dev_err(&pdev->dev, "failed to register smb sink.\n"); + ultrasoc_unregister_com(drvdata->com); + return ret; + } + + platform_set_drvdata(pdev, drvdata); + return 0; +} + +static int smb_remove(struct platform_device *pdev) +{ + struct smb_drv_data *drvdata = platform_get_drvdata(pdev); + + smb_unregister_sink(drvdata); + ultrasoc_unregister_com(drvdata->com); + smb_release_data_buffer(drvdata); + return 0; +} + +static const struct acpi_device_id ultrasoc_smb_acpi_match[] = { + {"HISI03A1", }, + {}, +}; + +static struct platform_driver smb_driver = { + .driver = { + .name = "ultrasoc,smb", + .acpi_match_table = ultrasoc_smb_acpi_match, + }, + .probe = smb_probe, + .remove = smb_remove, +}; +module_platform_driver(smb_driver); + +MODULE_DESCRIPTION("Ultrasoc smb driver"); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("Jonathan Zhou jonathan.zhouwen@huawei.com"); +MODULE_AUTHOR("Qi Liu liuqi115@huawei.com"); diff --git a/drivers/hwtracing/ultrasoc/ultrasoc-smb.h b/drivers/hwtracing/ultrasoc/ultrasoc-smb.h new file mode 100644 index 0000000..e37d510 --- /dev/null +++ b/drivers/hwtracing/ultrasoc/ultrasoc-smb.h @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (C) 2021 Hisilicon Limited Permission is hereby granted, free of + * charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Code herein communicates with and accesses proprietary hardware which is + * licensed intellectual property (IP) belonging to Siemens Digital Industries + * Software Ltd. + * + * Siemens Digital Industries Software Ltd. asserts and reserves all rights to + * their intellectual property. This paragraph may not be removed or modified + * in any way without permission from Siemens Digital Industries Software Ltd. + */ + +#ifndef _ULTRASOC_SMB_H +#define _ULTRASOC_SMB_H + +#include <linux/coresight.h> +#include <linux/list.h> +#include <linux/miscdevice.h> + +#include "ultrasoc.h" + +#define SMB_GLOBAL_CFG 0X0 +#define SMB_GLOBAL_EN 0X4 +#define SMB_GLOBAL_INT 0X8 +#define SMB_LB_CFG_LO 0X40 +#define SMB_LB_CFG_HI 0X44 +#define SMB_LB_INT_CTRL 0X48 +#define SMB_LB_INT_STS 0X4C +#define SMB_LB_BASE_LO 0X50 +#define SMB_LB_BASE_HI 0X54 +#define SMB_LB_LIMIT 0X58 +#define SMB_LB_RD_ADDR 0X5C +#define SMB_LB_WR_ADDR 0X60 +#define SMB_LB_PURGE 0X64 + +#define SMB_MSG_LC(lc) ((lc & 0x3) << 2) +#define SMB_BST_LEN(len) (((len - 1) & 0xff) << 4) +/* idle message injection timer period */ +#define SMB_IDLE_PRD(period) (((period - 216) & 0xf) << 12) +#define SMB_MEM_WR(credit, rate) (((credit & 0x3) << 16) | ((rate & 0xf) << 18)) +#define SMB_MEM_RD(credit, rate) (((credit & 0x3) << 22) | ((rate & 0xf) << 24)) +#define HISI_SMB_GLOBAL_CFG \ + (SMB_MSG_LC(0) | SMB_IDLE_PRD(231) | SMB_MEM_WR(0x3, 0x0) | \ + SMB_MEM_RD(0x3, 0x6) | SMB_BST_LEN(16)) + +#define SMB_INT_ENABLE BIT(0) +#define SMB_INT_TYPE_PULSE BIT(1) +#define SMB_INT_POLARITY_HIGH BIT(2) +#define HISI_SMB_GLB_INT_CFG (SMB_INT_ENABLE | SMB_INT_TYPE_PULSE | \ + SMB_INT_POLARITY_HIGH) + +/* logic buffer config register low 32b */ +#define SMB_BUF_ENABLE BIT(0) +#define SMB_BUF_SINGLE_END BIT(1) +#define SMB_BUF_INIT BIT(8) +#define SMB_BUF_CONTINUOUS BIT(11) +#define SMB_FLOW_MASK GENMASK(19, 16) +#define SMB_BUF_CFG_STREAMING \ + (SMB_BUF_INIT | SMB_BUF_CONTINUOUS | SMB_FLOW_MASK) +#define SMB_BUF_WRITE_BASE GENMASK(31, 0) + +/* logic buffer config register high 32b */ +#define SMB_MSG_FILTER(lower, upper) ((lower & 0xff) | ((upper & 0xff) << 8)) +#define SMB_BUF_INT_ENABLE BIT(0) +#define SMB_BUF_NOTE_NOT_EMPTY BIT(8) +#define SMB_BUF_NOTE_BLOCK_AVAIL BIT(9) +#define SMB_BUF_NOTE_TRIGGERED BIT(10) +#define SMB_BUF_NOTE_FULL BIT(11) +#define HISI_SMB_BUF_INT_CFG \ + (SMB_BUF_INT_ENABLE | SMB_BUF_NOTE_NOT_EMPTY | \ + SMB_BUF_NOTE_BLOCK_AVAIL | SMB_BUF_NOTE_TRIGGERED | \ + SMB_BUF_NOTE_FULL) + +struct smb_data_buffer { + /* memory buffer for hardware write */ + u32 buf_cfg_mode; + bool lost; + void __iomem *buf_base; + u64 buf_base_phys; + u64 buf_size; + u64 to_copy; + u32 rd_offset; +}; + +struct smb_drv_data { + void __iomem *base; + struct device *dev; + struct ultrasoc_com *com; + struct smb_data_buffer smb_db; + /* to register ultrasoc smb as a coresight sink device. */ + struct coresight_device *csdev; + spinlock_t spinlock; + local_t reading; + pid_t pid; + u32 mode; + struct miscdevice miscdev; +}; + +#define SMB_MSG_ALIGH_SIZE 0x400 + +static inline struct smb_data_buffer * + dev_get_smb_data_buffer(struct device *dev) +{ + struct smb_drv_data *drvdata = dev_get_drvdata(dev); + + if (drvdata) + return &drvdata->smb_db; + + return NULL; +} + +/* + * Coresight doesn't export the following + * structures(cs_mode,cs_buffers,etm_event_data), + * so we redefine a copy here. + */ +enum cs_mode { + CS_MODE_DISABLED, + CS_MODE_SYSFS, + CS_MODE_PERF, +}; + +struct cs_buffers { + unsigned int cur; + unsigned int nr_pages; + unsigned long offset; + local_t data_size; + bool snapshot; + void **data_pages; +}; + +struct etm_event_data { + struct work_struct work; + cpumask_t mask; + void *snk_config; + struct list_head * __percpu *path; +}; + +#if IS_ENABLED(CONFIG_CORESIGHT) +int etm_perf_symlink(struct coresight_device *csdev, bool link); +int etm_perf_add_symlink_sink(struct coresight_device *csdev); +void etm_perf_del_symlink_sink(struct coresight_device *csdev); +static inline void *etm_perf_sink_config(struct perf_output_handle *handle) +{ + struct etm_event_data *data = perf_get_aux(handle); + + if (data) + return data->snk_config; + return NULL; +} +#else +static inline int etm_perf_symlink(struct coresight_device *csdev, bool link) +{ return -EINVAL; } +int etm_perf_add_symlink_sink(struct coresight_device *csdev) +{ return -EINVAL; } +void etm_perf_del_symlink_sink(struct coresight_device *csdev) {} +static inline void *etm_perf_sink_config(struct perf_output_handle *handle) +{ + return NULL; +} + +#endif /* CONFIG_CORESIGHT */ + +#endif
Hi Qi
On 15/06/2021 10:34, Qi Liu wrote:
This patch adds driver for System Memory Buffer. It includes a platform driver for the SMB device.
Signed-off-by: Jonathan Zhou jonathan.zhouwen@huawei.com Signed-off-by: Qi Liu liuqi115@huawei.com
drivers/hwtracing/ultrasoc/Kconfig | 9 + drivers/hwtracing/ultrasoc/Makefile | 3 + drivers/hwtracing/ultrasoc/ultrasoc-smb.c | 663 ++++++++++++++++++++++++++++++ drivers/hwtracing/ultrasoc/ultrasoc-smb.h | 182 ++++++++ 4 files changed, 857 insertions(+) create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-smb.c create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-smb.h
+/*
- Coresight doesn't export the following
- structures(cs_mode,cs_buffers,etm_event_data),
- so we redefine a copy here.
- */
Please do not duplicate them. This indicates, either :
- You need to place your driver under coresight
OR
- Export the required definitions.
+enum cs_mode {
- CS_MODE_DISABLED,
- CS_MODE_SYSFS,
- CS_MODE_PERF,
+};
+struct cs_buffers {
- unsigned int cur;
- unsigned int nr_pages;
- unsigned long offset;
- local_t data_size;
- bool snapshot;
- void **data_pages;
+};
Why does this need to be replicated ?
+struct etm_event_data {
- struct work_struct work;
- cpumask_t mask;
- void *snk_config;
- struct list_head * __percpu *path;
+};
+#if IS_ENABLED(CONFIG_CORESIGHT) +int etm_perf_symlink(struct coresight_device *csdev, bool link); +int etm_perf_add_symlink_sink(struct coresight_device *csdev); +void etm_perf_del_symlink_sink(struct coresight_device *csdev); +static inline void *etm_perf_sink_config(struct perf_output_handle *handle) +{
- struct etm_event_data *data = perf_get_aux(handle);
- if (data)
return data->snk_config;
- return NULL;
+} +#else +static inline int etm_perf_symlink(struct coresight_device *csdev, bool link) +{ return -EINVAL; } +int etm_perf_add_symlink_sink(struct coresight_device *csdev) +{ return -EINVAL; } +void etm_perf_del_symlink_sink(struct coresight_device *csdev) {} +static inline void *etm_perf_sink_config(struct perf_output_handle *handle) +{
- return NULL;
+}
+#endif /* CONFIG_CORESIGHT */
+#endif
Suzuki
Hi Suzuki, On 2021/6/25 6:47, Suzuki K Poulose wrote:
Hi Qi
On 15/06/2021 10:34, Qi Liu wrote:
This patch adds driver for System Memory Buffer. It includes a platform driver for the SMB device.
Signed-off-by: Jonathan Zhou jonathan.zhouwen@huawei.com Signed-off-by: Qi Liu liuqi115@huawei.com
drivers/hwtracing/ultrasoc/Kconfig | 9 + drivers/hwtracing/ultrasoc/Makefile | 3 + drivers/hwtracing/ultrasoc/ultrasoc-smb.c | 663 ++++++++++++++++++++++++++++++ drivers/hwtracing/ultrasoc/ultrasoc-smb.h | 182 ++++++++ 4 files changed, 857 insertions(+) create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-smb.c create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-smb.h
+/*
- Coresight doesn't export the following
- structures(cs_mode,cs_buffers,etm_event_data),
- so we redefine a copy here.
- */
Please do not duplicate them. This indicates, either :
- You need to place your driver under coresight
OR
- Export the required definitions.
got it, I'll move this driver to coresight/ultrasoc, thanks.
Qi
+enum cs_mode { + CS_MODE_DISABLED, + CS_MODE_SYSFS, + CS_MODE_PERF, +};
+struct cs_buffers { + unsigned int cur; + unsigned int nr_pages; + unsigned long offset; + local_t data_size; + bool snapshot; + void **data_pages; +};
Why does this need to be replicated ?
+struct etm_event_data { + struct work_struct work; + cpumask_t mask; + void *snk_config; + struct list_head * __percpu *path; +};
+#if IS_ENABLED(CONFIG_CORESIGHT) +int etm_perf_symlink(struct coresight_device *csdev, bool link); +int etm_perf_add_symlink_sink(struct coresight_device *csdev); +void etm_perf_del_symlink_sink(struct coresight_device *csdev); +static inline void *etm_perf_sink_config(struct perf_output_handle *handle) +{ + struct etm_event_data *data = perf_get_aux(handle);
+ if (data) + return data->snk_config; + return NULL; +} +#else +static inline int etm_perf_symlink(struct coresight_device *csdev, bool link) +{ return -EINVAL; } +int etm_perf_add_symlink_sink(struct coresight_device *csdev) +{ return -EINVAL; } +void etm_perf_del_symlink_sink(struct coresight_device *csdev) {} +static inline void *etm_perf_sink_config(struct perf_output_handle *handle) +{ + return NULL; +}
+#endif /* CONFIG_CORESIGHT */
+#endif
Suzuki .
Hi Qi,
On Tue, Jun 15, 2021 at 05:34:44PM +0800, Qi Liu wrote:
This patch adds driver for System Memory Buffer. It includes a platform driver for the SMB device.
Signed-off-by: Jonathan Zhou jonathan.zhouwen@huawei.com Signed-off-by: Qi Liu liuqi115@huawei.com
drivers/hwtracing/ultrasoc/Kconfig | 9 + drivers/hwtracing/ultrasoc/Makefile | 3 + drivers/hwtracing/ultrasoc/ultrasoc-smb.c | 663 ++++++++++++++++++++++++++++++ drivers/hwtracing/ultrasoc/ultrasoc-smb.h | 182 ++++++++ 4 files changed, 857 insertions(+) create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-smb.c create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-smb.h
diff --git a/drivers/hwtracing/ultrasoc/Kconfig b/drivers/hwtracing/ultrasoc/Kconfig index 77429f3..8899949 100644 --- a/drivers/hwtracing/ultrasoc/Kconfig +++ b/drivers/hwtracing/ultrasoc/Kconfig @@ -22,4 +22,13 @@ config ULTRASOC_AXI_COM the upstream channel is used to transmit user configuration, and downstream channel to carry response and trace data to the users. +config ULTRASOC_SMB
- tristate "Ultrasoc System memory buffer drivers"
- help
This config enables support for Ultrasoc System Memory Buffer
drivers. The System Memory Buffer provides a way to buffer and
store messages in system memory. It provides a capability to
store messages received on its input message interface to an
area of system memory.
endif diff --git a/drivers/hwtracing/ultrasoc/Makefile b/drivers/hwtracing/ultrasoc/Makefile index 54711a7b..b174ca8 100644 --- a/drivers/hwtracing/ultrasoc/Makefile +++ b/drivers/hwtracing/ultrasoc/Makefile @@ -8,3 +8,6 @@ ultrasoc-drv-objs := ultrasoc.o obj-$(CONFIG_ULTRASOC_AXI_COM) += ultrasoc-axi-com-drv.o ultrasoc-axi-com-drv-objs := ultrasoc-axi-com.o
+obj-$(CONFIG_ULTRASOC_SMB) += ultrasoc-smb-drv.o +ultrasoc-smb-drv-objs := ultrasoc-smb.o diff --git a/drivers/hwtracing/ultrasoc/ultrasoc-smb.c b/drivers/hwtracing/ultrasoc/ultrasoc-smb.c new file mode 100644 index 0000000..ce03f5e --- /dev/null +++ b/drivers/hwtracing/ultrasoc/ultrasoc-smb.c @@ -0,0 +1,663 @@ +// SPDX-License-Identifier: MIT +/*
- Copyright (C) 2021 Hisilicon Limited Permission is hereby granted, free of
- charge, to any person obtaining a copy of this software and associated
- documentation files (the "Software"), to deal in the Software without
- restriction, including without limitation the rights to use, copy, modify,
- merge, publish, distribute, sublicense, and/or sell copies of the Software,
- and to permit persons to whom the Software is furnished to do so, subject
- to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- IN THE SOFTWARE.
- Code herein communicates with and accesses proprietary hardware which is
- licensed intellectual property (IP) belonging to Siemens Digital Industries
- Software Ltd.
- Siemens Digital Industries Software Ltd. asserts and reserves all rights to
- their intellectual property. This paragraph may not be removed or modified
- in any way without permission from Siemens Digital Industries Software Ltd.
- */
+#include <linux/circ_buf.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h>
+#include "ultrasoc-smb.h"
+static inline int smb_hw_buffer_empty(struct smb_drv_data *drvdata) +{
- u32 buf_status = readl(drvdata->base + SMB_LB_INT_STS);
- return buf_status & BIT(0) ? 0 : 1;
+}
+static inline int smb_buffer_pointer_pos(struct smb_drv_data *drvdata) +{
- u32 wr_offset, rd_offset;
- wr_offset = readl(drvdata->base + SMB_LB_WR_ADDR);
- rd_offset = readl(drvdata->base + SMB_LB_RD_ADDR);
- return wr_offset == rd_offset;
+}
+static inline int smb_hw_buffer_full(struct smb_drv_data *drvdata) +{
- return smb_buffer_pointer_pos(drvdata) && !smb_hw_buffer_empty(drvdata);
+}
+static inline void smb_clear_buf_status(struct smb_drv_data *drvdata) +{
- struct smb_data_buffer *sdb = &drvdata->smb_db;
- if (smb_buffer_pointer_pos(drvdata) && !sdb->to_copy)
writel(0xf, drvdata->base + SMB_LB_INT_STS);
+}
+static void smb_update_hw_write_size(struct smb_drv_data *drvdata) +{
- struct smb_data_buffer *sdb = &drvdata->smb_db;
- u32 write_offset, write_base;
- sdb->lost = false;
- writel(0x1, drvdata->base + SMB_LB_PURGE);
- if (smb_hw_buffer_empty(drvdata)) {
sdb->to_copy = 0;
return;
- }
- if (smb_hw_buffer_full(drvdata)) {
sdb->to_copy = sdb->buf_size;
return;
- }
- write_base = sdb->buf_base_phys & SMB_BUF_WRITE_BASE;
- write_offset = readl(drvdata->base + SMB_LB_WR_ADDR) - write_base;
- sdb->to_copy = CIRC_CNT(write_offset, sdb->rd_offset, sdb->buf_size);
+}
+static int smb_open(struct inode *inode, struct file *file) +{
- struct smb_drv_data *drvdata = container_of(file->private_data,
struct smb_drv_data, miscdev);
- if (local_cmpxchg(&drvdata->reading, 0, 1))
return -EBUSY;
- smb_update_hw_write_size(drvdata);
- return 0;
+}
+static ssize_t smb_read(struct file *file, char __user *data,
size_t len, loff_t *ppos)
+{
- struct smb_drv_data *drvdata = container_of(file->private_data,
struct smb_drv_data, miscdev);
- struct smb_data_buffer *sdb = &drvdata->smb_db;
- struct device *dev = &drvdata->csdev->dev;
- unsigned long to_copy = sdb->to_copy;
- if (!to_copy) {
smb_update_hw_write_size(drvdata);
to_copy = sdb->to_copy;
if (!to_copy)
return to_copy;
- }
- to_copy = min(to_copy, len);
- /*
* if the read needs to cross the boundary of the data buffer, copy
* last datas of the buffer to user
*/
- if (sdb->rd_offset + to_copy > sdb->buf_size)
to_copy = sdb->buf_size - sdb->rd_offset;
- if (copy_to_user(data, (void *)sdb->buf_base + sdb->rd_offset, to_copy)) {
dev_dbg(dev, "Failed to copy data to user.\n");
return -EFAULT;
- }
- *ppos += to_copy;
- sdb->rd_offset += to_copy;
- sdb->rd_offset %= sdb->buf_size;
- sdb->to_copy -= to_copy;
- /* update the read point */
- writel(sdb->buf_base_phys + sdb->rd_offset,
drvdata->base + SMB_LB_RD_ADDR);
- smb_clear_buf_status(drvdata);
- dev_dbg(dev, "%lu bytes copied.\n", to_copy);
- return to_copy;
+}
+static int smb_release(struct inode *inode, struct file *file) +{
- struct smb_drv_data *drvdata = container_of(file->private_data,
struct smb_drv_data, miscdev);
- local_set(&drvdata->reading, 0);
- return 0;
+}
+static const struct file_operations smb_fops = {
- .owner = THIS_MODULE,
- .open = smb_open,
- .read = smb_read,
- .release = smb_release,
- .llseek = no_llseek,
+};
+static ssize_t smb_show_status(struct ultrasoc_com *com, char *buf,
ssize_t wr_size)
+{
- struct smb_drv_data *drvdata;
- u32 value;
- drvdata = dev_get_drvdata(com->dev);
- value = readl(drvdata->base + SMB_LB_INT_STS);
- wr_size += sysfs_emit_at(buf, wr_size, "%-20s: 0x%08x\n",
"interrupt status", value);
- value = readl(drvdata->base + SMB_LB_WR_ADDR);
- wr_size += sysfs_emit_at(buf, wr_size, "%-20s: %#x\n", "write point",
value);
- value = readl(drvdata->base + SMB_LB_RD_ADDR);
- wr_size += sysfs_emit_at(buf, wr_size, "%-20s: %#x\n", "read point",
value);
This will not work. The sysfs interface requires one line per entry. Please look at what other coresight drivers do in that area.
- return wr_size;
+}
+static int smb_init_data_buffer(struct platform_device *pdev,
struct smb_data_buffer *sdb)
+{
- struct resource *res;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (IS_ERR(res)) {
dev_err(&pdev->dev, "SMB device without data buffer.\n");
return -EINVAL;
- }
- sdb->buf_base_phys = res->start;
- sdb->buf_size = resource_size(res);
- if (sdb->buf_size == 0)
return -EINVAL;
- sdb->buf_base = ioremap_cache(sdb->buf_base_phys, sdb->buf_size);
Why no using devm_ioremap_resource() ?
- if (sdb->buf_base == NULL)
return -ENOMEM;
- sdb->buf_cfg_mode = SMB_BUF_CFG_STREAMING;
As far as I can tell there is no point in keeping the value of SMB_BUF_CFG_STREAMING in the smb_data_buffer since it isn't used for anything else other than setting a HW register in smb_set_default_hw().
- return 0;
+}
+static void smb_release_data_buffer(struct smb_drv_data *drvdata) +{
- struct smb_data_buffer *sdb = &drvdata->smb_db;
- if (sdb->buf_base)
iounmap(sdb->buf_base);
+}
+static struct uscom_ops smb_ops = {
- .com_status = smb_show_status,
- .put_raw_msg = NULL,
+};
+static int smb_set_buffer(struct coresight_device *csdev,
struct perf_output_handle *handle);
+static void smb_enable_hw(struct smb_drv_data *drvdata) +{
- writel(0x1, drvdata->base + SMB_GLOBAL_EN);
+}
+static void smb_disable_hw(struct smb_drv_data *drvdata) +{
- writel(0x1, drvdata->base + SMB_LB_PURGE);
- writel(0x0, drvdata->base + SMB_GLOBAL_EN);
+}
+static int smb_enable_sysfs(struct coresight_device *csdev) +{
- struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent);
- unsigned long flags;
- int ret = 0;
- WARN_ON_ONCE(drvdata == NULL);
- spin_lock_irqsave(&drvdata->spinlock, flags);
- /* Don't messup with perf sessions. */
- if (drvdata->mode == CS_MODE_PERF) {
ret = -EBUSY;
goto out;
- }
- if (drvdata->mode == CS_MODE_DISABLED) {
smb_enable_hw(drvdata);
drvdata->mode = CS_MODE_SYSFS;
- }
- atomic_inc(csdev->refcnt);
+out:
- spin_unlock_irqrestore(&drvdata->spinlock, flags);
- return ret;
+}
+static int smb_enable_perf(struct coresight_device *csdev, void *data) +{
- struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent);
- struct device *dev = &drvdata->csdev->dev;
- struct perf_output_handle *handle = data;
- unsigned long flags;
- int ret = 0;
- pid_t pid;
- spin_lock_irqsave(&drvdata->spinlock, flags);
- if (drvdata->mode == CS_MODE_SYSFS) {
dev_err(dev, "Device is already in used by sysfs.\n");
ret = -EBUSY;
goto out;
- }
- /* Get a handle on the pid of the target process*/
- pid = task_pid_nr(handle->event->owner);
- if (drvdata->pid != -1 && drvdata->pid != pid) {
ret = -EBUSY;
goto out;
- }
- /*
* No HW configuration is needed if the sink is already in
* use for this session.
*/
- if (drvdata->pid == pid) {
atomic_inc(csdev->refcnt);
goto out;
- }
- /*
* We don't have an internal state to clean up if we fail to setup
* the perf buffer. So we can perform the step before we turn the
* ETB on and leave without cleaning up.
*/
- ret = smb_set_buffer(csdev, handle);
- if (ret)
goto out;
- smb_enable_hw(drvdata);
- drvdata->pid = pid;
- drvdata->mode = CS_MODE_PERF;
- atomic_inc(csdev->refcnt);
+out:
- spin_unlock_irqrestore(&drvdata->spinlock, flags);
- return ret;
+}
+static int smb_enable(struct coresight_device *csdev, u32 mode, void *data) +{
- int ret;
- switch (mode) {
- case CS_MODE_SYSFS:
ret = smb_enable_sysfs(csdev);
break;
- case CS_MODE_PERF:
ret = smb_enable_perf(csdev, data);
break;
- default:
ret = -EINVAL;
break;
- }
- if (ret)
return ret;
- dev_dbg(&csdev->dev, "Ultrasoc smb enabled.\n");
- return 0;
+}
+static int smb_disable(struct coresight_device *csdev) +{
- struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent);
- unsigned long flags;
- spin_lock_irqsave(&drvdata->spinlock, flags);
- if (atomic_dec_return(csdev->refcnt)) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return -EBUSY;
- }
- /* Complain if we (somehow) got out of sync */
- WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED);
- smb_disable_hw(drvdata);
- /* Dissociate from the target process. */
- drvdata->pid = -1;
- drvdata->mode = CS_MODE_DISABLED;
- spin_unlock_irqrestore(&drvdata->spinlock, flags);
- dev_dbg(&csdev->dev, "Ultrasoc smb disabled.\n");
- return 0;
+}
+static void smb_set_default_hw(struct smb_drv_data *drvdata) +{
- struct smb_data_buffer *sdb = &drvdata->smb_db;
- u32 value, base_hi, base_lo, limit_lo;
- /* first disable smb and clear the status of SMB buffer */
- smb_disable_hw(drvdata);
- smb_clear_buf_status(drvdata);
- /* using smb in single-end mode, and set other configures default */
- value = sdb->buf_cfg_mode | SMB_BUF_SINGLE_END | SMB_BUF_ENABLE;
- writel(value, drvdata->base + SMB_LB_CFG_LO);
- value = SMB_MSG_FILTER(0x0, 0xff);
- writel(value, drvdata->base + SMB_LB_CFG_HI);
- writel(HISI_SMB_GLOBAL_CFG, drvdata->base + SMB_GLOBAL_CFG);
- writel(HISI_SMB_GLB_INT_CFG, drvdata->base + SMB_GLOBAL_INT);
- writel(HISI_SMB_BUF_INT_CFG, drvdata->base + SMB_LB_INT_CTRL);
- /* config hardware registers according to physical base of SMB buffer */
- base_hi = sdb->buf_base_phys >> 32;
- base_lo = sdb->buf_base_phys & SMB_BUF_WRITE_BASE;
- limit_lo = base_lo + sdb->buf_size;
- writel(base_lo, drvdata->base + SMB_LB_BASE_LO);
- writel(base_hi, drvdata->base + SMB_LB_BASE_HI);
- writel(limit_lo, drvdata->base + SMB_LB_LIMIT);
- /* initial hardware read-ptr address*/
- writel(base_lo, drvdata->base + SMB_LB_RD_ADDR);
+}
+static void *smb_alloc_buffer(struct coresight_device *csdev,
struct perf_event *event, void **pages,
int nr_pages, bool overwrite)
+{
- struct cs_buffers *buf;
- int node;
- node = (event->cpu == -1) ? NUMA_NO_NODE : cpu_to_node(event->cpu);
- buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node);
- if (!buf)
return NULL;
- buf->snapshot = overwrite;
- buf->nr_pages = nr_pages;
- buf->data_pages = pages;
- return buf;
+}
+static void smb_free_buffer(void *config) +{
- struct cs_buffers *buf = config;
- kfree(buf);
+}
+static int smb_set_buffer(struct coresight_device *csdev,
struct perf_output_handle *handle)
+{
- struct cs_buffers *buf = etm_perf_sink_config(handle);
- u32 head;
- if (!buf)
return -EINVAL;
- /* wrap head around to the amount of space we have */
- head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1);
- /* find the page to write to and offset within that page */
- buf->cur = head / PAGE_SIZE;
- buf->offset = head % PAGE_SIZE;
- local_set(&buf->data_size, 0);
- return 0;
+}
+static void smb_sync_perf_buffer(struct smb_drv_data *drvdata,
struct cs_buffers *buf, unsigned long to_copy)
+{
- struct smb_data_buffer *sdb = &drvdata->smb_db;
- char **dst_pages = (char **)buf->data_pages;
- u32 buf_offset = buf->offset;
- u32 cur = buf->cur;
- u32 bytes;
- while (to_copy > 0) {
/*
* if the read needs to cross the boundary of the data buffer,
* copy last datas of the buffer to user
*/
if (sdb->rd_offset + PAGE_SIZE - buf_offset > sdb->buf_size)
bytes = sdb->buf_size - sdb->rd_offset;
else
bytes = min(to_copy, PAGE_SIZE - buf_offset);
memcpy_fromio(dst_pages[cur] + buf_offset,
sdb->buf_base + sdb->rd_offset, bytes);
buf_offset += bytes;
if (buf_offset >= PAGE_SIZE) {
buf_offset = 0;
cur++;
cur %= buf->nr_pages;
}
to_copy -= bytes;
/* ensure memcpy finished before update the read pointer */
sdb->rd_offset += bytes;
sdb->rd_offset %= sdb->buf_size;
- }
- writel(sdb->buf_base_phys + sdb->rd_offset,
drvdata->base + SMB_LB_RD_ADDR);
- sdb->to_copy = to_copy;
+}
+static unsigned long smb_update_buffer(struct coresight_device *csdev,
struct perf_output_handle *handle, void *sink_config)
+{
- struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent);
- struct smb_data_buffer *sdb = &drvdata->smb_db;
- struct cs_buffers *buf = sink_config;
- u64 to_copy;
- if (!buf)
return 0;
- smb_update_hw_write_size(drvdata);
- to_copy = sdb->to_copy;
- if (to_copy > handle->size) {
sdb->rd_offset += (to_copy - handle->size);
sdb->rd_offset %= sdb->buf_size;
to_copy = handle->size;
sdb->lost = true;
- }
- smb_sync_perf_buffer(drvdata, buf, to_copy);
- smb_clear_buf_status(drvdata);
- if (!buf->snapshot && sdb->lost)
perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED);
- return to_copy;
+}
+static const struct coresight_ops_sink smb_cs_ops = {
- .enable = smb_enable,
- .disable = smb_disable,
- .alloc_buffer = smb_alloc_buffer,
- .free_buffer = smb_free_buffer,
- .update_buffer = smb_update_buffer,
+};
+static const struct coresight_ops cs_ops = {
- .sink_ops = &smb_cs_ops,
+};
+static int smb_init_res(struct platform_device *pdev,
struct smb_drv_data *drvdata)
+{
- struct smb_data_buffer *sdb;
- int ret;
- sdb = &drvdata->smb_db;
- drvdata->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(drvdata->base)) {
dev_err(&pdev->dev, "Failed to ioremap resource.\n");
return PTR_ERR(drvdata->base);
- }
- ret = smb_init_data_buffer(pdev, sdb);
- if (ret)
dev_err(&pdev->dev, "Failed to init buffer, ret = %d.\n", ret);
- return ret;
+}
+DEFINE_CORESIGHT_DEVLIST(sink_devs, "sink_smb"); +static int smb_register_sink(struct platform_device *pdev,
struct smb_drv_data *drvdata)
+{
- struct coresight_platform_data *pdata = NULL;
- struct coresight_desc desc = { 0 };
- int ret;
- pdata = coresight_get_platform_data(&pdev->dev);
- if (IS_ERR(pdata))
return PTR_ERR(pdata);
- drvdata->dev->platform_data = pdata;
- desc.type = CORESIGHT_DEV_TYPE_SINK;
- desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
- desc.ops = &cs_ops;
- desc.pdata = pdata;
- desc.dev = &pdev->dev;
- desc.name = coresight_alloc_device_name(&sink_devs, &pdev->dev);
- if (!desc.name) {
dev_err(&pdev->dev, "Failed to alloc coresight device name.");
return -ENOMEM;
- }
- drvdata->csdev = coresight_register(&desc);
- if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
- drvdata->miscdev.name = desc.name;
- drvdata->miscdev.minor = MISC_DYNAMIC_MINOR;
- drvdata->miscdev.fops = &smb_fops;
- ret = misc_register(&drvdata->miscdev);
- if (ret) {
coresight_unregister(drvdata->csdev);
dev_err(&pdev->dev, "Failed to register misc, ret=%d\n", ret);
- }
- return ret;
+}
+static void smb_unregister_sink(struct smb_drv_data *drvdata) +{
- misc_deregister(&drvdata->miscdev);
- coresight_unregister(drvdata->csdev);
+}
+static int smb_config_com_descp(struct platform_device *pdev,
struct smb_drv_data *drvdata)
+{
- struct device *parent = pdev->dev.parent;
- struct ultrasoc_com_descp com_descp = {0};
- struct device *dev = &pdev->dev;
- struct ultrasoc_com *com;
- com_descp.name = pdev->name;
- com_descp.com_type = ULTRASOC_COM_TYPE_DOWN;
- com_descp.com_dev = dev;
- com_descp.uscom_ops = &smb_ops;
- com = ultrasoc_register_com(parent, &com_descp);
Why is this needed? As far as I can see this device does not need to register with the ultrasoc core.
To me the very first thing do to about this patchset is to move this in drivers/hwtracing/coresight/. That will dissociate this code completely from the ultrasoc core (more on that later) and avoid duplications as pointed out by Suzuki.
There are several things to address with this patch but there is no point in elaborating further until the above hasn't been done.
- if (IS_ERR(com)) {
dev_err(dev, "Failed to register smb com.\n");
return PTR_ERR(com);
- }
- drvdata->com = com;
- return 0;
+}
+static int smb_probe(struct platform_device *pdev) +{
- struct smb_drv_data *drvdata;
- int ret;
- drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata)
return -ENOMEM;
- ret = smb_init_res(pdev, drvdata);
- if (ret)
return ret;
- smb_set_default_hw(drvdata);
- spin_lock_init(&drvdata->spinlock);
- drvdata->dev = &pdev->dev;
- drvdata->pid = -1;
- ret = smb_config_com_descp(pdev, drvdata);
- if (ret)
return ret;
- ret = smb_register_sink(pdev, drvdata);
- if (ret) {
dev_err(&pdev->dev, "failed to register smb sink.\n");
ultrasoc_unregister_com(drvdata->com);
return ret;
- }
- platform_set_drvdata(pdev, drvdata);
- return 0;
+}
+static int smb_remove(struct platform_device *pdev) +{
- struct smb_drv_data *drvdata = platform_get_drvdata(pdev);
- smb_unregister_sink(drvdata);
- ultrasoc_unregister_com(drvdata->com);
- smb_release_data_buffer(drvdata);
- return 0;
+}
+static const struct acpi_device_id ultrasoc_smb_acpi_match[] = {
- {"HISI03A1", },
- {},
+};
+static struct platform_driver smb_driver = {
- .driver = {
.name = "ultrasoc,smb",
.acpi_match_table = ultrasoc_smb_acpi_match,
- },
- .probe = smb_probe,
- .remove = smb_remove,
+}; +module_platform_driver(smb_driver);
+MODULE_DESCRIPTION("Ultrasoc smb driver"); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("Jonathan Zhou jonathan.zhouwen@huawei.com"); +MODULE_AUTHOR("Qi Liu liuqi115@huawei.com"); diff --git a/drivers/hwtracing/ultrasoc/ultrasoc-smb.h b/drivers/hwtracing/ultrasoc/ultrasoc-smb.h new file mode 100644 index 0000000..e37d510 --- /dev/null +++ b/drivers/hwtracing/ultrasoc/ultrasoc-smb.h @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: MIT */ +/*
- Copyright (C) 2021 Hisilicon Limited Permission is hereby granted, free of
- charge, to any person obtaining a copy of this software and associated
- documentation files (the "Software"), to deal in the Software without
- restriction, including without limitation the rights to use, copy, modify,
- merge, publish, distribute, sublicense, and/or sell copies of the Software,
- and to permit persons to whom the Software is furnished to do so, subject
- to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- IN THE SOFTWARE.
- Code herein communicates with and accesses proprietary hardware which is
- licensed intellectual property (IP) belonging to Siemens Digital Industries
- Software Ltd.
- Siemens Digital Industries Software Ltd. asserts and reserves all rights to
- their intellectual property. This paragraph may not be removed or modified
- in any way without permission from Siemens Digital Industries Software Ltd.
- */
+#ifndef _ULTRASOC_SMB_H +#define _ULTRASOC_SMB_H
+#include <linux/coresight.h> +#include <linux/list.h> +#include <linux/miscdevice.h>
+#include "ultrasoc.h"
+#define SMB_GLOBAL_CFG 0X0 +#define SMB_GLOBAL_EN 0X4 +#define SMB_GLOBAL_INT 0X8 +#define SMB_LB_CFG_LO 0X40 +#define SMB_LB_CFG_HI 0X44 +#define SMB_LB_INT_CTRL 0X48 +#define SMB_LB_INT_STS 0X4C +#define SMB_LB_BASE_LO 0X50 +#define SMB_LB_BASE_HI 0X54 +#define SMB_LB_LIMIT 0X58 +#define SMB_LB_RD_ADDR 0X5C +#define SMB_LB_WR_ADDR 0X60 +#define SMB_LB_PURGE 0X64
+#define SMB_MSG_LC(lc) ((lc & 0x3) << 2) +#define SMB_BST_LEN(len) (((len - 1) & 0xff) << 4) +/* idle message injection timer period */ +#define SMB_IDLE_PRD(period) (((period - 216) & 0xf) << 12) +#define SMB_MEM_WR(credit, rate) (((credit & 0x3) << 16) | ((rate & 0xf) << 18)) +#define SMB_MEM_RD(credit, rate) (((credit & 0x3) << 22) | ((rate & 0xf) << 24)) +#define HISI_SMB_GLOBAL_CFG \
- (SMB_MSG_LC(0) | SMB_IDLE_PRD(231) | SMB_MEM_WR(0x3, 0x0) | \
SMB_MEM_RD(0x3, 0x6) | SMB_BST_LEN(16))
+#define SMB_INT_ENABLE BIT(0) +#define SMB_INT_TYPE_PULSE BIT(1) +#define SMB_INT_POLARITY_HIGH BIT(2) +#define HISI_SMB_GLB_INT_CFG (SMB_INT_ENABLE | SMB_INT_TYPE_PULSE | \
SMB_INT_POLARITY_HIGH)
+/* logic buffer config register low 32b */ +#define SMB_BUF_ENABLE BIT(0) +#define SMB_BUF_SINGLE_END BIT(1) +#define SMB_BUF_INIT BIT(8) +#define SMB_BUF_CONTINUOUS BIT(11) +#define SMB_FLOW_MASK GENMASK(19, 16) +#define SMB_BUF_CFG_STREAMING \
- (SMB_BUF_INIT | SMB_BUF_CONTINUOUS | SMB_FLOW_MASK)
+#define SMB_BUF_WRITE_BASE GENMASK(31, 0)
+/* logic buffer config register high 32b */ +#define SMB_MSG_FILTER(lower, upper) ((lower & 0xff) | ((upper & 0xff) << 8)) +#define SMB_BUF_INT_ENABLE BIT(0) +#define SMB_BUF_NOTE_NOT_EMPTY BIT(8) +#define SMB_BUF_NOTE_BLOCK_AVAIL BIT(9) +#define SMB_BUF_NOTE_TRIGGERED BIT(10) +#define SMB_BUF_NOTE_FULL BIT(11) +#define HISI_SMB_BUF_INT_CFG \
- (SMB_BUF_INT_ENABLE | SMB_BUF_NOTE_NOT_EMPTY | \
SMB_BUF_NOTE_BLOCK_AVAIL | SMB_BUF_NOTE_TRIGGERED | \
SMB_BUF_NOTE_FULL)
+struct smb_data_buffer {
- /* memory buffer for hardware write */
- u32 buf_cfg_mode;
- bool lost;
- void __iomem *buf_base;
- u64 buf_base_phys;
- u64 buf_size;
- u64 to_copy;
- u32 rd_offset;
+};
+struct smb_drv_data {
- void __iomem *base;
- struct device *dev;
- struct ultrasoc_com *com;
- struct smb_data_buffer smb_db;
- /* to register ultrasoc smb as a coresight sink device. */
- struct coresight_device *csdev;
- spinlock_t spinlock;
- local_t reading;
- pid_t pid;
- u32 mode;
- struct miscdevice miscdev;
+};
+#define SMB_MSG_ALIGH_SIZE 0x400
+static inline struct smb_data_buffer *
- dev_get_smb_data_buffer(struct device *dev)
+{
- struct smb_drv_data *drvdata = dev_get_drvdata(dev);
- if (drvdata)
return &drvdata->smb_db;
- return NULL;
+}
+/*
- Coresight doesn't export the following
- structures(cs_mode,cs_buffers,etm_event_data),
- so we redefine a copy here.
- */
+enum cs_mode {
- CS_MODE_DISABLED,
- CS_MODE_SYSFS,
- CS_MODE_PERF,
+};
+struct cs_buffers {
- unsigned int cur;
- unsigned int nr_pages;
- unsigned long offset;
- local_t data_size;
- bool snapshot;
- void **data_pages;
+};
+struct etm_event_data {
- struct work_struct work;
- cpumask_t mask;
- void *snk_config;
- struct list_head * __percpu *path;
+};
+#if IS_ENABLED(CONFIG_CORESIGHT) +int etm_perf_symlink(struct coresight_device *csdev, bool link); +int etm_perf_add_symlink_sink(struct coresight_device *csdev); +void etm_perf_del_symlink_sink(struct coresight_device *csdev); +static inline void *etm_perf_sink_config(struct perf_output_handle *handle) +{
- struct etm_event_data *data = perf_get_aux(handle);
- if (data)
return data->snk_config;
- return NULL;
+} +#else +static inline int etm_perf_symlink(struct coresight_device *csdev, bool link) +{ return -EINVAL; } +int etm_perf_add_symlink_sink(struct coresight_device *csdev) +{ return -EINVAL; } +void etm_perf_del_symlink_sink(struct coresight_device *csdev) {} +static inline void *etm_perf_sink_config(struct perf_output_handle *handle) +{
- return NULL;
+}
+#endif /* CONFIG_CORESIGHT */
+#endif
2.7.4
Hi Mathieu,
Thanks for reviewing this patch.
On 2021/6/30 4:50, Mathieu Poirier wrote:
Hi Qi,
On Tue, Jun 15, 2021 at 05:34:44PM +0800, Qi Liu wrote:
This patch adds driver for System Memory Buffer. It includes a platform driver for the SMB device.
Signed-off-by: Jonathan Zhou jonathan.zhouwen@huawei.com Signed-off-by: Qi Liu liuqi115@huawei.com
drivers/hwtracing/ultrasoc/Kconfig | 9 + drivers/hwtracing/ultrasoc/Makefile | 3 + drivers/hwtracing/ultrasoc/ultrasoc-smb.c | 663 ++++++++++++++++++++++++++++++ drivers/hwtracing/ultrasoc/ultrasoc-smb.h | 182 ++++++++ 4 files changed, 857 insertions(+) create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-smb.c create mode 100644 drivers/hwtracing/ultrasoc/ultrasoc-smb.h
diff --git a/drivers/hwtracing/ultrasoc/Kconfig b/drivers/hwtracing/ultrasoc/Kconfig index 77429f3..8899949 100644 --- a/drivers/hwtracing/ultrasoc/Kconfig +++ b/drivers/hwtracing/ultrasoc/Kconfig @@ -22,4 +22,13 @@ config ULTRASOC_AXI_COM the upstream channel is used to transmit user configuration, and downstream channel to carry response and trace data to the users. +config ULTRASOC_SMB
- tristate "Ultrasoc System memory buffer drivers"
- help
This config enables support for Ultrasoc System Memory Buffer
drivers. The System Memory Buffer provides a way to buffer and
store messages in system memory. It provides a capability to
store messages received on its input message interface to an
area of system memory.
- endif
diff --git a/drivers/hwtracing/ultrasoc/Makefile b/drivers/hwtracing/ultrasoc/Makefile index 54711a7b..b174ca8 100644 --- a/drivers/hwtracing/ultrasoc/Makefile +++ b/drivers/hwtracing/ultrasoc/Makefile @@ -8,3 +8,6 @@ ultrasoc-drv-objs := ultrasoc.o obj-$(CONFIG_ULTRASOC_AXI_COM) += ultrasoc-axi-com-drv.o ultrasoc-axi-com-drv-objs := ultrasoc-axi-com.o
[...]
+static ssize_t smb_show_status(struct ultrasoc_com *com, char *buf,
ssize_t wr_size)
+{
- struct smb_drv_data *drvdata;
- u32 value;
- drvdata = dev_get_drvdata(com->dev);
- value = readl(drvdata->base + SMB_LB_INT_STS);
- wr_size += sysfs_emit_at(buf, wr_size, "%-20s: 0x%08x\n",
"interrupt status", value);
- value = readl(drvdata->base + SMB_LB_WR_ADDR);
- wr_size += sysfs_emit_at(buf, wr_size, "%-20s: %#x\n", "write point",
value);
- value = readl(drvdata->base + SMB_LB_RD_ADDR);
- wr_size += sysfs_emit_at(buf, wr_size, "%-20s: %#x\n", "read point",
value);
This will not work. The sysfs interface requires one line per entry. Please look at what other coresight drivers do in that area.
got it, I'll use multi sysfs files to show these information, like this: static struct attribute *smb_sink_attrs[] = { &dev_attr_read_pos.attr, &dev_attr_write_pos.attr, &dev_attr_buf_status.attr, NULL, };
- return wr_size;
+}
+static int smb_init_data_buffer(struct platform_device *pdev,
struct smb_data_buffer *sdb)
+{
- struct resource *res;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (IS_ERR(res)) {
dev_err(&pdev->dev, "SMB device without data buffer.\n");
return -EINVAL;
- }
- sdb->buf_base_phys = res->start;
- sdb->buf_size = resource_size(res);
- if (sdb->buf_size == 0)
return -EINVAL;
- sdb->buf_base = ioremap_cache(sdb->buf_base_phys, sdb->buf_size);
Why no using devm_ioremap_resource() ?
will use this, thanks.
- if (sdb->buf_base == NULL)
return -ENOMEM;
- sdb->buf_cfg_mode = SMB_BUF_CFG_STREAMING;
As far as I can tell there is no point in keeping the value of SMB_BUF_CFG_STREAMING in the smb_data_buffer since it isn't used for anything else other than setting a HW register in smb_set_default_hw().
thanks, will remove this member in struct smb_data_buffer, thanks.
- return 0;
+}
[...]
+static int smb_config_com_descp(struct platform_device *pdev,
struct smb_drv_data *drvdata)
+{
- struct device *parent = pdev->dev.parent;
- struct ultrasoc_com_descp com_descp = {0};
- struct device *dev = &pdev->dev;
- struct ultrasoc_com *com;
- com_descp.name = pdev->name;
- com_descp.com_type = ULTRASOC_COM_TYPE_DOWN;
- com_descp.com_dev = dev;
- com_descp.uscom_ops = &smb_ops;
- com = ultrasoc_register_com(parent, &com_descp);
Why is this needed? As far as I can see this device does not need to register with the ultrasoc core.
yes, you are right.
At the beginning we use the ultrasoc core to adapt multiple hardware devices and support more capabilities. But after discussing with Siemens, we are allowed to only upstream the axi-com and smb driver.
So the software architecture seems unreasonable now, I'll refactor it in next version, thanks.
To me the very first thing do to about this patchset is to move this in drivers/hwtracing/coresight/. That will dissociate this code completely from the ultrasoc core (more on that later) and avoid duplications as pointed out by Suzuki.
There are several things to address with this patch but there is no point in elaborating further until the above hasn't been done.
Got it, will move the driver next time, thanks.
Qi
- if (IS_ERR(com)) {
dev_err(dev, "Failed to register smb com.\n");
return PTR_ERR(com);
- }
- drvdata->com = com;
- return 0;
+}
+static int smb_probe(struct platform_device *pdev) +{
- struct smb_drv_data *drvdata;
- int ret;
- drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata)
return -ENOMEM;
- ret = smb_init_res(pdev, drvdata);
- if (ret)
return ret;
- smb_set_default_hw(drvdata);
- spin_lock_init(&drvdata->spinlock);
- drvdata->dev = &pdev->dev;
- drvdata->pid = -1;
- ret = smb_config_com_descp(pdev, drvdata);
- if (ret)
return ret;
- ret = smb_register_sink(pdev, drvdata);
- if (ret) {
dev_err(&pdev->dev, "failed to register smb sink.\n");
ultrasoc_unregister_com(drvdata->com);
return ret;
- }
- platform_set_drvdata(pdev, drvdata);
- return 0;
+}
+static int smb_remove(struct platform_device *pdev) +{
- struct smb_drv_data *drvdata = platform_get_drvdata(pdev);
- smb_unregister_sink(drvdata);
- ultrasoc_unregister_com(drvdata->com);
- smb_release_data_buffer(drvdata);
- return 0;
+}
+static const struct acpi_device_id ultrasoc_smb_acpi_match[] = {
- {"HISI03A1", },
- {},
+};
+static struct platform_driver smb_driver = {
- .driver = {
.name = "ultrasoc,smb",
.acpi_match_table = ultrasoc_smb_acpi_match,
- },
- .probe = smb_probe,
- .remove = smb_remove,
+}; +module_platform_driver(smb_driver);
+MODULE_DESCRIPTION("Ultrasoc smb driver"); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("Jonathan Zhou jonathan.zhouwen@huawei.com"); +MODULE_AUTHOR("Qi Liu liuqi115@huawei.com"); diff --git a/drivers/hwtracing/ultrasoc/ultrasoc-smb.h b/drivers/hwtracing/ultrasoc/ultrasoc-smb.h new file mode 100644 index 0000000..e37d510 --- /dev/null +++ b/drivers/hwtracing/ultrasoc/ultrasoc-smb.h @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: MIT */ +/*
- Copyright (C) 2021 Hisilicon Limited Permission is hereby granted, free of
- charge, to any person obtaining a copy of this software and associated
- documentation files (the "Software"), to deal in the Software without
- restriction, including without limitation the rights to use, copy, modify,
- merge, publish, distribute, sublicense, and/or sell copies of the Software,
- and to permit persons to whom the Software is furnished to do so, subject
- to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- IN THE SOFTWARE.
- Code herein communicates with and accesses proprietary hardware which is
- licensed intellectual property (IP) belonging to Siemens Digital Industries
- Software Ltd.
- Siemens Digital Industries Software Ltd. asserts and reserves all rights to
- their intellectual property. This paragraph may not be removed or modified
- in any way without permission from Siemens Digital Industries Software Ltd.
- */
+#ifndef _ULTRASOC_SMB_H +#define _ULTRASOC_SMB_H
+#include <linux/coresight.h> +#include <linux/list.h> +#include <linux/miscdevice.h>
+#include "ultrasoc.h"
+#define SMB_GLOBAL_CFG 0X0 +#define SMB_GLOBAL_EN 0X4 +#define SMB_GLOBAL_INT 0X8 +#define SMB_LB_CFG_LO 0X40 +#define SMB_LB_CFG_HI 0X44 +#define SMB_LB_INT_CTRL 0X48 +#define SMB_LB_INT_STS 0X4C +#define SMB_LB_BASE_LO 0X50 +#define SMB_LB_BASE_HI 0X54 +#define SMB_LB_LIMIT 0X58 +#define SMB_LB_RD_ADDR 0X5C +#define SMB_LB_WR_ADDR 0X60 +#define SMB_LB_PURGE 0X64
+#define SMB_MSG_LC(lc) ((lc & 0x3) << 2) +#define SMB_BST_LEN(len) (((len - 1) & 0xff) << 4) +/* idle message injection timer period */ +#define SMB_IDLE_PRD(period) (((period - 216) & 0xf) << 12) +#define SMB_MEM_WR(credit, rate) (((credit & 0x3) << 16) | ((rate & 0xf) << 18)) +#define SMB_MEM_RD(credit, rate) (((credit & 0x3) << 22) | ((rate & 0xf) << 24)) +#define HISI_SMB_GLOBAL_CFG \
- (SMB_MSG_LC(0) | SMB_IDLE_PRD(231) | SMB_MEM_WR(0x3, 0x0) | \
SMB_MEM_RD(0x3, 0x6) | SMB_BST_LEN(16))
+#define SMB_INT_ENABLE BIT(0) +#define SMB_INT_TYPE_PULSE BIT(1) +#define SMB_INT_POLARITY_HIGH BIT(2) +#define HISI_SMB_GLB_INT_CFG (SMB_INT_ENABLE | SMB_INT_TYPE_PULSE | \
SMB_INT_POLARITY_HIGH)
+/* logic buffer config register low 32b */ +#define SMB_BUF_ENABLE BIT(0) +#define SMB_BUF_SINGLE_END BIT(1) +#define SMB_BUF_INIT BIT(8) +#define SMB_BUF_CONTINUOUS BIT(11) +#define SMB_FLOW_MASK GENMASK(19, 16) +#define SMB_BUF_CFG_STREAMING \
- (SMB_BUF_INIT | SMB_BUF_CONTINUOUS | SMB_FLOW_MASK)
+#define SMB_BUF_WRITE_BASE GENMASK(31, 0)
+/* logic buffer config register high 32b */ +#define SMB_MSG_FILTER(lower, upper) ((lower & 0xff) | ((upper & 0xff) << 8)) +#define SMB_BUF_INT_ENABLE BIT(0) +#define SMB_BUF_NOTE_NOT_EMPTY BIT(8) +#define SMB_BUF_NOTE_BLOCK_AVAIL BIT(9) +#define SMB_BUF_NOTE_TRIGGERED BIT(10) +#define SMB_BUF_NOTE_FULL BIT(11) +#define HISI_SMB_BUF_INT_CFG \
- (SMB_BUF_INT_ENABLE | SMB_BUF_NOTE_NOT_EMPTY | \
SMB_BUF_NOTE_BLOCK_AVAIL | SMB_BUF_NOTE_TRIGGERED | \
SMB_BUF_NOTE_FULL)
+struct smb_data_buffer {
- /* memory buffer for hardware write */
- u32 buf_cfg_mode;
- bool lost;
- void __iomem *buf_base;
- u64 buf_base_phys;
- u64 buf_size;
- u64 to_copy;
- u32 rd_offset;
+};
+struct smb_drv_data {
- void __iomem *base;
- struct device *dev;
- struct ultrasoc_com *com;
- struct smb_data_buffer smb_db;
- /* to register ultrasoc smb as a coresight sink device. */
- struct coresight_device *csdev;
- spinlock_t spinlock;
- local_t reading;
- pid_t pid;
- u32 mode;
- struct miscdevice miscdev;
+};
+#define SMB_MSG_ALIGH_SIZE 0x400
+static inline struct smb_data_buffer *
- dev_get_smb_data_buffer(struct device *dev)
+{
- struct smb_drv_data *drvdata = dev_get_drvdata(dev);
- if (drvdata)
return &drvdata->smb_db;
- return NULL;
+}
+/*
- Coresight doesn't export the following
- structures(cs_mode,cs_buffers,etm_event_data),
- so we redefine a copy here.
- */
+enum cs_mode {
- CS_MODE_DISABLED,
- CS_MODE_SYSFS,
- CS_MODE_PERF,
+};
+struct cs_buffers {
- unsigned int cur;
- unsigned int nr_pages;
- unsigned long offset;
- local_t data_size;
- bool snapshot;
- void **data_pages;
+};
+struct etm_event_data {
- struct work_struct work;
- cpumask_t mask;
- void *snk_config;
- struct list_head * __percpu *path;
+};
+#if IS_ENABLED(CONFIG_CORESIGHT) +int etm_perf_symlink(struct coresight_device *csdev, bool link); +int etm_perf_add_symlink_sink(struct coresight_device *csdev); +void etm_perf_del_symlink_sink(struct coresight_device *csdev); +static inline void *etm_perf_sink_config(struct perf_output_handle *handle) +{
- struct etm_event_data *data = perf_get_aux(handle);
- if (data)
return data->snk_config;
- return NULL;
+} +#else +static inline int etm_perf_symlink(struct coresight_device *csdev, bool link) +{ return -EINVAL; } +int etm_perf_add_symlink_sink(struct coresight_device *csdev) +{ return -EINVAL; } +void etm_perf_del_symlink_sink(struct coresight_device *csdev) {} +static inline void *etm_perf_sink_config(struct perf_output_handle *handle) +{
- return NULL;
+}
+#endif /* CONFIG_CORESIGHT */
+#endif
2.7.4
.