mbox series

[V13,0/7] soc: qcom: dcc: Add driver support for Data Capture and Compare unit(DCC)

Message ID cover.1663642051.git.quic_schowdhu@quicinc.com
Headers show
Series soc: qcom: dcc: Add driver support for Data Capture and Compare unit(DCC) | expand

Message

Souradeep Chowdhury Sept. 20, 2022, 3:56 a.m. UTC
DCC(Data Capture and Compare) is a DMA engine designed for debugging purposes.
In case of a system crash or manual software triggers by the user the DCC hardware
stores the value at the register addresses which can be used for debugging purposes.
The DCC driver provides the user with debugfs interface to configure the register
addresses. The options that the DCC hardware provides include reading from registers,
writing to registers, first reading and then writing to registers and looping
through the values of the same register.

In certain cases a register write needs to be executed for accessing the rest of the
registers, also the user might want to record the changing values of a register with
time for which he has the option to use the loop feature.

The options mentioned above are exposed to the user by debugfs files once the driver
is probed. The details and usage of this debugfs files are documented in
Documentation/ABI/testing/debugfs-driver-dcc.

As an example let us consider a couple of debug scenarios where DCC has been proved to be
effective for debugging purposes:-

i)TimeStamp Related Issue

On SC7180, there was a coresight timestamp issue where it would occasionally be all 0
instead of proper timestamp values.

Proper timestamp:
Idx:3373; ID:10; I_TIMESTAMP : Timestamp.; Updated val = 0x13004d8f5b7aa; CC=0x9e

Zero timestamp:
Idx:3387; ID:10; I_TIMESTAMP : Timestamp.; Updated val = 0x0; CC=0xa2

Now this is a non-fatal issue and doesn't need a system reset, but still needs
to be rootcaused and fixed for those who do care about coresight etm traces.
Since this is a timestamp issue, we would be looking for any timestamp related
clocks and such.

We get all the clk register details from IP documentation and configure it
via DCC config_read debugfs node. Before that we set the current linked list.

/* Program the linked list with the addresses */
echo R 0x10c004 > /sys/kernel/debug/dcc/../3/config
echo R 0x10c008 > /sys/kernel/debug/dcc/../3/config
echo R 0x10c00c > /sys/kernel/debug/dcc/../3/config
echo R 0x10c010 > /sys/kernel/debug/dcc/../3/config
..... and so on for other timestamp related clk registers

/* Other way of specifying is in "addr len" pair, in below case it
specifies to capture 4 words starting 0x10C004 */

echo R 0x10C004 4 > /sys/kernel/debug/dcc/../3/config_read

/* Enable DCC */
echo 1 > /sys/kernel/debug/dcc/../3/enable

/* Run the timestamp test for working case */

/* Send SW trigger */
echo 1 > /sys/kernel/debug/dcc/../trigger

/* Read SRAM */
cat /dev/dcc_sram > dcc_sram1.bin

/* Run the timestamp test for non-working case */

/* Send SW trigger */
echo 1 > /sys/kernel/debug/dcc/../trigger

/* Read SRAM */
cat /dev/dcc_sram > dcc_sram2.bin

Get the parser from [1] and checkout the latest branch.

/* Parse the SRAM bin */
python dcc_parser.py -s dcc_sram1.bin --v2 -o output/
python dcc_parser.py -s dcc_sram2.bin --v2 -o output/

Sample parsed output of dcc_sram1.bin:

<hwioDump version="1">
        <timestamp>03/14/21</timestamp>
            <generator>Linux DCC Parser</generator>
                <chip name="None" version="None">
                <register address="0x0010c004" value="0x80000000" />
                <register address="0x0010c008" value="0x00000008" />
                <register address="0x0010c00c" value="0x80004220" />
                <register address="0x0010c010" value="0x80000000" />
            </chip>
    <next_ll_offset>next_ll_offset : 0x1c </next_ll_offset>
</hwioDump>

ii)NOC register errors

A particular class of registers called NOC which are functional registers was reporting
errors while logging the values.To trace these errors the DCC has been used effectively.
The steps followed were similar to the ones mentioned above.
In addition to NOC registers a few other dependent registers were configured in DCC to
monitor it's values during a crash. A look at the dependent register values revealed that
the crash was happening due to a secured access to one of these dependent registers.
All these debugging activity and finding the root cause was achieved using DCC.

DCC parser is available at the following open source location

https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource/tools/tree/dcc_parser

Changes in V13

*Fixed the quic issue with v12 of the patch

Souradeep Chowdhury (7):
  dt-bindings: Added the yaml bindings for DCC
  soc: qcom: dcc: Add driver support for Data Capture and Compare
    unit(DCC)
  MAINTAINERS: Add the entry for DCC(Data Capture and Compare) driver
    support
  arm64: dts: qcom: sm8150: Add Data Capture and Compare(DCC) support
    node
  arm64: dts: qcom: sc7280: Add Data Capture and Compare(DCC) support
    node
  arm64: dts: qcom: sc7180: Add Data Capture and Compare(DCC) support
    node
  arm64: dts: qcom: sdm845: Add Data Capture and Compare(DCC) support
    node

 Documentation/ABI/testing/debugfs-driver-dcc       |   98 ++
 .../devicetree/bindings/arm/msm/qcom,dcc.yaml      |   44 +
 MAINTAINERS                                        |    8 +
 arch/arm64/boot/dts/qcom/sc7180.dtsi               |    6 +
 arch/arm64/boot/dts/qcom/sc7280.dtsi               |    6 +
 arch/arm64/boot/dts/qcom/sdm845.dtsi               |    6 +
 arch/arm64/boot/dts/qcom/sm8150.dtsi               |    6 +
 drivers/soc/qcom/Kconfig                           |    8 +
 drivers/soc/qcom/Makefile                          |    1 +
 drivers/soc/qcom/dcc.c                             | 1348 ++++++++++++++++++++
 10 files changed, 1531 insertions(+)
 create mode 100644 Documentation/ABI/testing/debugfs-driver-dcc
 create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,dcc.yaml
 create mode 100644 drivers/soc/qcom/dcc.c

Comments

Krzysztof Kozlowski Sept. 23, 2022, 7:42 p.m. UTC | #1
On 20/09/2022 05:56, Souradeep Chowdhury wrote:
> The DCC is a DMA Engine designed to capture and store data
> during system crash or software triggers. The DCC operates
> based on user inputs via the debugfs interface. The user gives
> addresses as inputs and these addresses are stored in the

(...)

> +
> +#define DCC_RD_MOD_WR_ADDR              0xC105E
> +
> +/*DCC debugfs directory*/
> +static struct dentry	*dcc_dbg;
> +
> +enum dcc_descriptor_type {
> +	DCC_READ_TYPE,
> +	DCC_LOOP_TYPE,
> +	DCC_READ_WRITE_TYPE,
> +	DCC_WRITE_TYPE
> +};
> +
> +struct dcc_config_entry {
> +	u32				base;
> +	u32				offset;
> +	u32				len;
> +	u32				loop_cnt;
> +	u32				write_val;
> +	u32				mask;
> +	bool				apb_bus;
> +	enum dcc_descriptor_type	desc_type;
> +	struct list_head		list;
> +};
> +
> +/**
> + * struct dcc_drvdata - configuration information related to a dcc device
> + * @base:	      Base Address of the dcc device
> + * @dev:	      The device attached to the driver data
> + * @mutex:	      Lock to protect access and manipulation of dcc_drvdata
> + * @ram_base:         Base address for the SRAM dedicated for the dcc device
> + * @ram_size:         Total size of the SRAM dedicated for the dcc device
> + * @ram_offset:       Offset to the SRAM dedicated for dcc device
> + * @mem_map_ver:      Memory map version of DCC hardware
> + * @ram_cfg:          Used for address limit calculation for dcc
> + * @ram_start:        Starting address of DCC SRAM
> + * @sram_dev:	      Micellaneous device equivalent of dcc SRAM
> + * @cfg_head:	      Points to the head of the linked list of addresses
> + * @dbg_dir:          The dcc debugfs directory under which all the debugfs files are placed
> + * @nr_link_list:     Total number of linkedlists supported by the DCC configuration
> + * @loopoff:          Loop offset bits range for the addresses

All these entres have messed up spacing.

> + * @enable:           This contains an array of linkedlist enable flags

No, this is not an array of linked lists... It's a pointer to bool. This
is not way to store linked lists.


> +
> +static int dcc_probe(struct platform_device *pdev)
> +{
> +	u32 val;
> +	int ret = 0, i, size;
> +	struct device *dev = &pdev->dev;
> +	struct dcc_drvdata *dcc;
> +	struct resource *res;
> +
> +	dcc = devm_kzalloc(dev, sizeof(*dcc), GFP_KERNEL);
> +	if (!dcc)
> +		return -ENOMEM;
> +
> +	dcc->dev = &pdev->dev;
> +	platform_set_drvdata(pdev, dcc);
> +
> +	dcc->base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(dcc->base))
> +		return PTR_ERR(dcc->base);
> +
> +	dcc->ram_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res);
> +	if (IS_ERR(dcc->ram_base))
> +		return PTR_ERR(dcc->ram_base);
> +
> +	dcc->ram_size = resource_size(res);
> +
> +	dcc->ram_offset = (size_t)of_device_get_match_data(&pdev->dev);
> +
> +	val = dcc_readl(dcc, DCC_HW_INFO);
> +
> +	if (FIELD_GET(DCC_VER_INFO_MASK, val)) {
> +		dcc->mem_map_ver = 3;
> +		dcc->nr_link_list = dcc_readl(dcc, DCC_LL_NUM_INFO);
> +		if (dcc->nr_link_list == 0)
> +			return	-EINVAL;
> +	} else if ((val & DCC_VER_MASK2) == DCC_VER_MASK2) {
> +		dcc->mem_map_ver = 2;
> +		dcc->nr_link_list = dcc_readl(dcc, DCC_LL_NUM_INFO);
> +		if (dcc->nr_link_list == 0)
> +			return	-EINVAL;
> +	} else {
> +		dcc->mem_map_ver = 1;
> +		dcc->nr_link_list = DCC_MAX_LINK_LIST;
> +	}
> +
> +	/* Either set the fixed loop offset or calculate it

Start with /*
(see coding style)

> +	 * from ram_size.Max consecutive addresses the
> +	 * dcc can loop is equivalent to the ram size
> +	 */
> +	if (val & DCC_LOOP_OFFSET_MASK)
> +		dcc->loopoff = DCC_FIX_LOOP_OFFSET;
> +	else
> +		dcc->loopoff = get_bitmask_order((dcc->ram_size +
> +				dcc->ram_offset) / 4 - 1);
> +
> +	mutex_init(&dcc->mutex);
> +	/* Allocate space for all entries at once */
> +	size = sizeof(*dcc->enable) + sizeof(*dcc->cfg_head);

This is quite confusing way of handling lists - some parts of drvdata
are list, some are not.

> +
> +	dcc->enable = devm_kcalloc(dev, dcc->nr_link_list, size, GFP_KERNEL);
> +	if (!dcc->enable)
> +		return -ENOMEM;
> +
> +	dcc->cfg_head = (struct list_head *)(dcc->enable + dcc->nr_link_list);

That's unusual way to iterate over list...

> +
> +	for (i = 0; i < dcc->nr_link_list; i++)
> +		INIT_LIST_HEAD(&dcc->cfg_head[i]);
> +
> +	ret = dcc_sram_dev_init(dcc);
> +	if (ret) {
> +		dev_err(dcc->dev, "DCC: sram node not registered.\n");
> +		return ret;
> +	}
> +
> +	ret = dcc_create_debug_dir(dcc);
> +	if (ret) {
> +		dev_err(dcc->dev, "DCC: debugfs files not created.\n");

debugfs failures are not reasons to fail probe. Also no need to print
errors.

> +		dcc_sram_dev_exit(dcc);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int dcc_remove(struct platform_device *pdev)
> +{
> +	struct dcc_drvdata *drvdata = platform_get_drvdata(pdev);
> +
> +	dcc_delete_debug_dir(drvdata);
> +
> +	dcc_sram_dev_exit(drvdata);
> +

No need for blank lines between each calls.

> +	dcc_config_reset(drvdata);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id dcc_match_table[] = {
> +	{ .compatible = "qcom,sm8150-dcc", .data = (void *)0x5000 },
> +	{ .compatible = "qcom,sc7280-dcc", .data = (void *)0x12000 },
> +	{ .compatible = "qcom,sc7180-dcc", .data = (void *)0x6000 },
> +	{ .compatible = "qcom,sdm845-dcc", .data = (void *)0x6000 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, dcc_match_table);
> +
> +static struct platform_driver dcc_driver = {
> +	.probe = dcc_probe,
> +	.remove	= dcc_remove,
> +	.driver	= {
> +		.name = "qcom-dcc",
> +		.of_match_table	= dcc_match_table,
> +	},
> +};
> +
> +module_platform_driver(dcc_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Qualcomm Technologies Inc. DCC driver");
> +

Best regards,
Krzysztof
Krzysztof Kozlowski Sept. 23, 2022, 7:50 p.m. UTC | #2
On 20/09/2022 05:56, Souradeep Chowdhury wrote:
> The DCC is a DMA Engine designed to capture and store data
> during system crash or software triggers. The DCC operates
> based on user inputs via the debugfs interface. The user gives
> addresses as inputs and these addresses are stored in the

Please wrap commit message according to Linux coding style / submission
process:
https://elixir.bootlin.com/linux/v5.18-rc4/source/Documentation/process/submitting-patches.rst#L586

Best regards,
Krzysztof
Krzysztof Kozlowski Sept. 23, 2022, 8:03 p.m. UTC | #3
On 20/09/2022 05:57, Souradeep Chowdhury wrote:
> Added the entries for all the files added as a part of driver support for
> DCC(Data Capture and Compare).
> 


> +F:	Documentation/devicetree/bindings/arm/msm/qcom,dcc.yaml

arm is only for top level nodes/properties. Please put your bindings in
respective subsystem.

Best regards,
Krzysztof
Souradeep Chowdhury Sept. 24, 2022, 11:44 a.m. UTC | #4
On 9/24/2022 1:12 AM, Krzysztof Kozlowski wrote:
> On 20/09/2022 05:56, Souradeep Chowdhury wrote:
>> The DCC is a DMA Engine designed to capture and store data
>> during system crash or software triggers. The DCC operates
>> based on user inputs via the debugfs interface. The user gives
>> addresses as inputs and these addresses are stored in the
> (...)
>
>> +
>> +#define DCC_RD_MOD_WR_ADDR              0xC105E
>> +
>> +/*DCC debugfs directory*/
>> +static struct dentry	*dcc_dbg;
>> +
>> +enum dcc_descriptor_type {
>> +	DCC_READ_TYPE,
>> +	DCC_LOOP_TYPE,
>> +	DCC_READ_WRITE_TYPE,
>> +	DCC_WRITE_TYPE
>> +};
>> +
>> +struct dcc_config_entry {
>> +	u32				base;
>> +	u32				offset;
>> +	u32				len;
>> +	u32				loop_cnt;
>> +	u32				write_val;
>> +	u32				mask;
>> +	bool				apb_bus;
>> +	enum dcc_descriptor_type	desc_type;
>> +	struct list_head		list;
>> +};
>> +
>> +/**
>> + * struct dcc_drvdata - configuration information related to a dcc device
>> + * @base:	      Base Address of the dcc device
>> + * @dev:	      The device attached to the driver data
>> + * @mutex:	      Lock to protect access and manipulation of dcc_drvdata
>> + * @ram_base:         Base address for the SRAM dedicated for the dcc device
>> + * @ram_size:         Total size of the SRAM dedicated for the dcc device
>> + * @ram_offset:       Offset to the SRAM dedicated for dcc device
>> + * @mem_map_ver:      Memory map version of DCC hardware
>> + * @ram_cfg:          Used for address limit calculation for dcc
>> + * @ram_start:        Starting address of DCC SRAM
>> + * @sram_dev:	      Micellaneous device equivalent of dcc SRAM
>> + * @cfg_head:	      Points to the head of the linked list of addresses
>> + * @dbg_dir:          The dcc debugfs directory under which all the debugfs files are placed
>> + * @nr_link_list:     Total number of linkedlists supported by the DCC configuration
>> + * @loopoff:          Loop offset bits range for the addresses
> All these entres have messed up spacing.
Ack
>
>> + * @enable:           This contains an array of linkedlist enable flags
> No, this is not an array of linked lists... It's a pointer to bool. This
> is not way to store linked lists.
Ack
>
>
>> +
>> +static int dcc_probe(struct platform_device *pdev)
>> +{
>> +	u32 val;
>> +	int ret = 0, i, size;
>> +	struct device *dev = &pdev->dev;
>> +	struct dcc_drvdata *dcc;
>> +	struct resource *res;
>> +
>> +	dcc = devm_kzalloc(dev, sizeof(*dcc), GFP_KERNEL);
>> +	if (!dcc)
>> +		return -ENOMEM;
>> +
>> +	dcc->dev = &pdev->dev;
>> +	platform_set_drvdata(pdev, dcc);
>> +
>> +	dcc->base = devm_platform_ioremap_resource(pdev, 0);
>> +	if (IS_ERR(dcc->base))
>> +		return PTR_ERR(dcc->base);
>> +
>> +	dcc->ram_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res);
>> +	if (IS_ERR(dcc->ram_base))
>> +		return PTR_ERR(dcc->ram_base);
>> +
>> +	dcc->ram_size = resource_size(res);
>> +
>> +	dcc->ram_offset = (size_t)of_device_get_match_data(&pdev->dev);
>> +
>> +	val = dcc_readl(dcc, DCC_HW_INFO);
>> +
>> +	if (FIELD_GET(DCC_VER_INFO_MASK, val)) {
>> +		dcc->mem_map_ver = 3;
>> +		dcc->nr_link_list = dcc_readl(dcc, DCC_LL_NUM_INFO);
>> +		if (dcc->nr_link_list == 0)
>> +			return	-EINVAL;
>> +	} else if ((val & DCC_VER_MASK2) == DCC_VER_MASK2) {
>> +		dcc->mem_map_ver = 2;
>> +		dcc->nr_link_list = dcc_readl(dcc, DCC_LL_NUM_INFO);
>> +		if (dcc->nr_link_list == 0)
>> +			return	-EINVAL;
>> +	} else {
>> +		dcc->mem_map_ver = 1;
>> +		dcc->nr_link_list = DCC_MAX_LINK_LIST;
>> +	}
>> +
>> +	/* Either set the fixed loop offset or calculate it
> Start with /*
> (see coding style)
Ack
>
>> +	 * from ram_size.Max consecutive addresses the
>> +	 * dcc can loop is equivalent to the ram size
>> +	 */
>> +	if (val & DCC_LOOP_OFFSET_MASK)
>> +		dcc->loopoff = DCC_FIX_LOOP_OFFSET;
>> +	else
>> +		dcc->loopoff = get_bitmask_order((dcc->ram_size +
>> +				dcc->ram_offset) / 4 - 1);
>> +
>> +	mutex_init(&dcc->mutex);
>> +	/* Allocate space for all entries at once */
>> +	size = sizeof(*dcc->enable) + sizeof(*dcc->cfg_head);
> This is quite confusing way of handling lists - some parts of drvdata
> are list, some are not.

We are using three things for lists here.  A cfg_head which points to 
the head of the

individual linkedlist of addresses. a nr_linked_list to store the total 
number of lists

supported by dcc and an array of boolean to store the enabled status of 
each individual lists

>> +
>> +	dcc->enable = devm_kcalloc(dev, dcc->nr_link_list, size, GFP_KERNEL);
>> +	if (!dcc->enable)
>> +		return -ENOMEM;
>> +
>> +	dcc->cfg_head = (struct list_head *)(dcc->enable + dcc->nr_link_list);
> That's unusual way to iterate over list...
Here we are instantiating the head of each individual linked lists in 
the array that stores the list.
>
>> +
>> +	for (i = 0; i < dcc->nr_link_list; i++)
>> +		INIT_LIST_HEAD(&dcc->cfg_head[i]);
>> +
>> +	ret = dcc_sram_dev_init(dcc);
>> +	if (ret) {
>> +		dev_err(dcc->dev, "DCC: sram node not registered.\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = dcc_create_debug_dir(dcc);
>> +	if (ret) {
>> +		dev_err(dcc->dev, "DCC: debugfs files not created.\n");
> debugfs failures are not reasons to fail probe. Also no need to print
> errors.

The total functionality of this driver is dependent on the debugfs 
files. That is why

the probe if failed with error message if it is not created. This is 
done as per Alex's

comment on version 8 of the patch.

>> +		dcc_sram_dev_exit(dcc);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int dcc_remove(struct platform_device *pdev)
>> +{
>> +	struct dcc_drvdata *drvdata = platform_get_drvdata(pdev);
>> +
>> +	dcc_delete_debug_dir(drvdata);
>> +
>> +	dcc_sram_dev_exit(drvdata);
>> +
> No need for blank lines between each calls.
Ack
>
>> +	dcc_config_reset(drvdata);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id dcc_match_table[] = {
>> +	{ .compatible = "qcom,sm8150-dcc", .data = (void *)0x5000 },
>> +	{ .compatible = "qcom,sc7280-dcc", .data = (void *)0x12000 },
>> +	{ .compatible = "qcom,sc7180-dcc", .data = (void *)0x6000 },
>> +	{ .compatible = "qcom,sdm845-dcc", .data = (void *)0x6000 },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(of, dcc_match_table);
>> +
>> +static struct platform_driver dcc_driver = {
>> +	.probe = dcc_probe,
>> +	.remove	= dcc_remove,
>> +	.driver	= {
>> +		.name = "qcom-dcc",
>> +		.of_match_table	= dcc_match_table,
>> +	},
>> +};
>> +
>> +module_platform_driver(dcc_driver);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_DESCRIPTION("Qualcomm Technologies Inc. DCC driver");
>> +
> Best regards,
> Krzysztof
>
Souradeep Chowdhury Sept. 24, 2022, 11:46 a.m. UTC | #5
On 9/24/2022 1:20 AM, Krzysztof Kozlowski wrote:
> On 20/09/2022 05:56, Souradeep Chowdhury wrote:
>> The DCC is a DMA Engine designed to capture and store data
>> during system crash or software triggers. The DCC operates
>> based on user inputs via the debugfs interface. The user gives
>> addresses as inputs and these addresses are stored in the
> Please wrap commit message according to Linux coding style / submission
> process:
> https://elixir.bootlin.com/linux/v5.18-rc4/source/Documentation/process/submitting-patches.rst#L586
Ack
> Best regards,
> Krzysztof
>
Souradeep Chowdhury Sept. 24, 2022, 11:47 a.m. UTC | #6
On 9/24/2022 1:33 AM, Krzysztof Kozlowski wrote:
> On 20/09/2022 05:57, Souradeep Chowdhury wrote:
>> Added the entries for all the files added as a part of driver support for
>> DCC(Data Capture and Compare).
>>
>
>> +F:	Documentation/devicetree/bindings/arm/msm/qcom,dcc.yaml
> arm is only for top level nodes/properties. Please put your bindings in
> respective subsystem.
Ack
>
> Best regards,
> Krzysztof
>