diff options
| -rw-r--r-- | drivers/net/ethernet/qualcomm/Kconfig | 15 | ||||
| -rw-r--r-- | drivers/net/ethernet/qualcomm/Makefile | 1 | ||||
| -rw-r--r-- | drivers/net/ethernet/qualcomm/ppe/Makefile | 7 | ||||
| -rw-r--r-- | drivers/net/ethernet/qualcomm/ppe/ppe.c | 223 | ||||
| -rw-r--r-- | drivers/net/ethernet/qualcomm/ppe/ppe.h | 36 |
5 files changed, 282 insertions, 0 deletions
diff --git a/drivers/net/ethernet/qualcomm/Kconfig b/drivers/net/ethernet/qualcomm/Kconfig index a4434eb38950..29e6d746ad31 100644 --- a/drivers/net/ethernet/qualcomm/Kconfig +++ b/drivers/net/ethernet/qualcomm/Kconfig @@ -60,6 +60,21 @@ config QCOM_EMAC low power, Receive-Side Scaling (RSS), and IEEE 1588-2008 Precision Clock Synchronization Protocol. +config QCOM_PPE + tristate "Qualcomm Technologies, Inc. PPE Ethernet support" + depends on HAS_IOMEM && OF + depends on COMMON_CLK + select REGMAP_MMIO + help + This driver supports the Qualcomm Technologies, Inc. packet + process engine (PPE) available with IPQ SoC. The PPE includes + the Ethernet MACs, Ethernet DMA (EDMA) and switch core that + supports L3 flow offload, L2 switch function, RSS and tunnel + offload. + + To compile this driver as a module, choose M here. The module + will be called qcom-ppe. + source "drivers/net/ethernet/qualcomm/rmnet/Kconfig" endif # NET_VENDOR_QUALCOMM diff --git a/drivers/net/ethernet/qualcomm/Makefile b/drivers/net/ethernet/qualcomm/Makefile index 9250976dd884..166a59aea363 100644 --- a/drivers/net/ethernet/qualcomm/Makefile +++ b/drivers/net/ethernet/qualcomm/Makefile @@ -11,4 +11,5 @@ qcauart-objs := qca_uart.o obj-y += emac/ +obj-$(CONFIG_QCOM_PPE) += ppe/ obj-$(CONFIG_RMNET) += rmnet/ diff --git a/drivers/net/ethernet/qualcomm/ppe/Makefile b/drivers/net/ethernet/qualcomm/ppe/Makefile new file mode 100644 index 000000000000..63d50d3b4f2e --- /dev/null +++ b/drivers/net/ethernet/qualcomm/ppe/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the device driver of PPE (Packet Process Engine) in IPQ SoC +# + +obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o +qcom-ppe-objs := ppe.o diff --git a/drivers/net/ethernet/qualcomm/ppe/ppe.c b/drivers/net/ethernet/qualcomm/ppe/ppe.c new file mode 100644 index 000000000000..3aacb8eddbae --- /dev/null +++ b/drivers/net/ethernet/qualcomm/ppe/ppe.c @@ -0,0 +1,223 @@ +// 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" + +#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"); + + platform_set_drvdata(pdev, ppe_dev); + + return 0; +} + +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, +}; +module_platform_driver(qcom_ppe_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. IPQ PPE driver"); diff --git a/drivers/net/ethernet/qualcomm/ppe/ppe.h b/drivers/net/ethernet/qualcomm/ppe/ppe.h new file mode 100644 index 000000000000..779f39c9f098 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/ppe/ppe.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __PPE_H__ +#define __PPE_H__ + +#include <linux/compiler.h> +#include <linux/interconnect.h> + +struct device; +struct regmap; + +/** + * struct ppe_device - PPE device private data. + * @dev: PPE device structure. + * @regmap: PPE register map. + * @clk_rate: PPE clock rate. + * @num_ports: Number of PPE ports. + * @num_icc_paths: Number of interconnect paths. + * @icc_paths: Interconnect path array. + * + * PPE device is the instance of PPE hardware, which is used to + * configure PPE packet process modules such as BM (buffer management), + * QM (queue management), and scheduler. + */ +struct ppe_device { + struct device *dev; + struct regmap *regmap; + unsigned long clk_rate; + unsigned int num_ports; + unsigned int num_icc_paths; + struct icc_bulk_data icc_paths[] __counted_by(num_icc_paths); +}; +#endif |
