mirror of
https://github.com/torvalds/linux.git
synced 2026-04-30 04:22:32 -04:00
The PPE hardware counters maintain counters for packets handled by the various functional blocks of PPE. They help in tracing the packets passed through PPE and debugging any packet drops. The counters displayed by this debugfs file are ones that are common for all Ethernet ports, and they do not include the counters that are specific for a MAC port. Hence they cannot be displayed using ethtool. The per-MAC counters will be supported using "ethtool -S" along with the netdevice driver. The PPE hardware various type counters are made available through the debugfs files under directory "/sys/kernel/debug/ppe/". Signed-off-by: Luo Jie <quic_luoj@quicinc.com> Link: https://patch.msgid.link/20250818-qcom_ipq_ppe-v8-13-1d4ff641fce9@quicinc.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
240 lines
6.2 KiB
C
240 lines
6.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
|
*/
|
|
|
|
/* PPE platform device probe, DTSI parser and PPE clock initializations. */
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/interconnect.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/reset.h>
|
|
|
|
#include "ppe.h"
|
|
#include "ppe_config.h"
|
|
#include "ppe_debugfs.h"
|
|
|
|
#define PPE_PORT_MAX 8
|
|
#define PPE_CLK_RATE 353000000
|
|
|
|
/* ICC clocks for enabling PPE device. The avg_bw and peak_bw with value 0
|
|
* will be updated by the clock rate of PPE.
|
|
*/
|
|
static const struct icc_bulk_data ppe_icc_data[] = {
|
|
{
|
|
.name = "ppe",
|
|
.avg_bw = 0,
|
|
.peak_bw = 0,
|
|
},
|
|
{
|
|
.name = "ppe_cfg",
|
|
.avg_bw = 0,
|
|
.peak_bw = 0,
|
|
},
|
|
{
|
|
.name = "qos_gen",
|
|
.avg_bw = 6000,
|
|
.peak_bw = 6000,
|
|
},
|
|
{
|
|
.name = "timeout_ref",
|
|
.avg_bw = 6000,
|
|
.peak_bw = 6000,
|
|
},
|
|
{
|
|
.name = "nssnoc_memnoc",
|
|
.avg_bw = 533333,
|
|
.peak_bw = 533333,
|
|
},
|
|
{
|
|
.name = "memnoc_nssnoc",
|
|
.avg_bw = 533333,
|
|
.peak_bw = 533333,
|
|
},
|
|
{
|
|
.name = "memnoc_nssnoc_1",
|
|
.avg_bw = 533333,
|
|
.peak_bw = 533333,
|
|
},
|
|
};
|
|
|
|
static const struct regmap_range ppe_readable_ranges[] = {
|
|
regmap_reg_range(0x0, 0x1ff), /* Global */
|
|
regmap_reg_range(0x400, 0x5ff), /* LPI CSR */
|
|
regmap_reg_range(0x1000, 0x11ff), /* GMAC0 */
|
|
regmap_reg_range(0x1200, 0x13ff), /* GMAC1 */
|
|
regmap_reg_range(0x1400, 0x15ff), /* GMAC2 */
|
|
regmap_reg_range(0x1600, 0x17ff), /* GMAC3 */
|
|
regmap_reg_range(0x1800, 0x19ff), /* GMAC4 */
|
|
regmap_reg_range(0x1a00, 0x1bff), /* GMAC5 */
|
|
regmap_reg_range(0xb000, 0xefff), /* PRX CSR */
|
|
regmap_reg_range(0xf000, 0x1efff), /* IPE */
|
|
regmap_reg_range(0x20000, 0x5ffff), /* PTX CSR */
|
|
regmap_reg_range(0x60000, 0x9ffff), /* IPE L2 CSR */
|
|
regmap_reg_range(0xb0000, 0xeffff), /* IPO CSR */
|
|
regmap_reg_range(0x100000, 0x17ffff), /* IPE PC */
|
|
regmap_reg_range(0x180000, 0x1bffff), /* PRE IPO CSR */
|
|
regmap_reg_range(0x1d0000, 0x1dffff), /* Tunnel parser */
|
|
regmap_reg_range(0x1e0000, 0x1effff), /* Ingress parse */
|
|
regmap_reg_range(0x200000, 0x2fffff), /* IPE L3 */
|
|
regmap_reg_range(0x300000, 0x3fffff), /* IPE tunnel */
|
|
regmap_reg_range(0x400000, 0x4fffff), /* Scheduler */
|
|
regmap_reg_range(0x500000, 0x503fff), /* XGMAC0 */
|
|
regmap_reg_range(0x504000, 0x507fff), /* XGMAC1 */
|
|
regmap_reg_range(0x508000, 0x50bfff), /* XGMAC2 */
|
|
regmap_reg_range(0x50c000, 0x50ffff), /* XGMAC3 */
|
|
regmap_reg_range(0x510000, 0x513fff), /* XGMAC4 */
|
|
regmap_reg_range(0x514000, 0x517fff), /* XGMAC5 */
|
|
regmap_reg_range(0x600000, 0x6fffff), /* BM */
|
|
regmap_reg_range(0x800000, 0x9fffff), /* QM */
|
|
regmap_reg_range(0xb00000, 0xbef800), /* EDMA */
|
|
};
|
|
|
|
static const struct regmap_access_table ppe_reg_table = {
|
|
.yes_ranges = ppe_readable_ranges,
|
|
.n_yes_ranges = ARRAY_SIZE(ppe_readable_ranges),
|
|
};
|
|
|
|
static const struct regmap_config regmap_config_ipq9574 = {
|
|
.reg_bits = 32,
|
|
.reg_stride = 4,
|
|
.val_bits = 32,
|
|
.rd_table = &ppe_reg_table,
|
|
.wr_table = &ppe_reg_table,
|
|
.max_register = 0xbef800,
|
|
.fast_io = true,
|
|
};
|
|
|
|
static int ppe_clock_init_and_reset(struct ppe_device *ppe_dev)
|
|
{
|
|
unsigned long ppe_rate = ppe_dev->clk_rate;
|
|
struct device *dev = ppe_dev->dev;
|
|
struct reset_control *rstc;
|
|
struct clk_bulk_data *clks;
|
|
struct clk *clk;
|
|
int ret, i;
|
|
|
|
for (i = 0; i < ppe_dev->num_icc_paths; i++) {
|
|
ppe_dev->icc_paths[i].name = ppe_icc_data[i].name;
|
|
ppe_dev->icc_paths[i].avg_bw = ppe_icc_data[i].avg_bw ? :
|
|
Bps_to_icc(ppe_rate);
|
|
|
|
/* PPE does not have an explicit peak bandwidth requirement,
|
|
* so set the peak bandwidth to be equal to the average
|
|
* bandwidth.
|
|
*/
|
|
ppe_dev->icc_paths[i].peak_bw = ppe_icc_data[i].peak_bw ? :
|
|
Bps_to_icc(ppe_rate);
|
|
}
|
|
|
|
ret = devm_of_icc_bulk_get(dev, ppe_dev->num_icc_paths,
|
|
ppe_dev->icc_paths);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = icc_bulk_set_bw(ppe_dev->num_icc_paths, ppe_dev->icc_paths);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* The PPE clocks have a common parent clock. Setting the clock
|
|
* rate of "ppe" ensures the clock rate of all PPE clocks is
|
|
* configured to the same rate.
|
|
*/
|
|
clk = devm_clk_get(dev, "ppe");
|
|
if (IS_ERR(clk))
|
|
return PTR_ERR(clk);
|
|
|
|
ret = clk_set_rate(clk, ppe_rate);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = devm_clk_bulk_get_all_enabled(dev, &clks);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Reset the PPE. */
|
|
rstc = devm_reset_control_get_exclusive(dev, NULL);
|
|
if (IS_ERR(rstc))
|
|
return PTR_ERR(rstc);
|
|
|
|
ret = reset_control_assert(rstc);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* The delay 10 ms of assert is necessary for resetting PPE. */
|
|
usleep_range(10000, 11000);
|
|
|
|
return reset_control_deassert(rstc);
|
|
}
|
|
|
|
static int qcom_ppe_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct ppe_device *ppe_dev;
|
|
void __iomem *base;
|
|
int ret, num_icc;
|
|
|
|
num_icc = ARRAY_SIZE(ppe_icc_data);
|
|
ppe_dev = devm_kzalloc(dev, struct_size(ppe_dev, icc_paths, num_icc),
|
|
GFP_KERNEL);
|
|
if (!ppe_dev)
|
|
return -ENOMEM;
|
|
|
|
base = devm_platform_ioremap_resource(pdev, 0);
|
|
if (IS_ERR(base))
|
|
return dev_err_probe(dev, PTR_ERR(base), "PPE ioremap failed\n");
|
|
|
|
ppe_dev->regmap = devm_regmap_init_mmio(dev, base, ®map_config_ipq9574);
|
|
if (IS_ERR(ppe_dev->regmap))
|
|
return dev_err_probe(dev, PTR_ERR(ppe_dev->regmap),
|
|
"PPE initialize regmap failed\n");
|
|
ppe_dev->dev = dev;
|
|
ppe_dev->clk_rate = PPE_CLK_RATE;
|
|
ppe_dev->num_ports = PPE_PORT_MAX;
|
|
ppe_dev->num_icc_paths = num_icc;
|
|
|
|
ret = ppe_clock_init_and_reset(ppe_dev);
|
|
if (ret)
|
|
return dev_err_probe(dev, ret, "PPE clock config failed\n");
|
|
|
|
ret = ppe_hw_config(ppe_dev);
|
|
if (ret)
|
|
return dev_err_probe(dev, ret, "PPE HW config failed\n");
|
|
|
|
ppe_debugfs_setup(ppe_dev);
|
|
platform_set_drvdata(pdev, ppe_dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void qcom_ppe_remove(struct platform_device *pdev)
|
|
{
|
|
struct ppe_device *ppe_dev;
|
|
|
|
ppe_dev = platform_get_drvdata(pdev);
|
|
ppe_debugfs_teardown(ppe_dev);
|
|
}
|
|
|
|
static const struct of_device_id qcom_ppe_of_match[] = {
|
|
{ .compatible = "qcom,ipq9574-ppe" },
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(of, qcom_ppe_of_match);
|
|
|
|
static struct platform_driver qcom_ppe_driver = {
|
|
.driver = {
|
|
.name = "qcom_ppe",
|
|
.of_match_table = qcom_ppe_of_match,
|
|
},
|
|
.probe = qcom_ppe_probe,
|
|
.remove = qcom_ppe_remove,
|
|
};
|
|
module_platform_driver(qcom_ppe_driver);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. IPQ PPE driver");
|