[net-next] net: stmmac: qcom-ethqos: Add sysfs nodes for qcom ethqos
Commit Message
Add sysfs nodes to conifigure routing of specific vlan id to GVM queue.
GVM queue is not exposed to PVM stmmac, so TC ops can't configure routing.
Signed-off-by: Sneh Shah <quic_snehshah@quicinc.com>
---
.../stmicro/stmmac/dwmac-qcom-ethqos.c | 216 +++++++++++++++++-
1 file changed, 215 insertions(+), 1 deletion(-)
Comments
On Mon, Dec 04, 2023 at 02:18:54PM +0530, Sneh Shah wrote:
> Add sysfs nodes to conifigure routing of specific vlan id to GVM queue.
> GVM queue is not exposed to PVM stmmac, so TC ops can't configure routing.
>
Perhaps I'm just not familiar enough with the details of stmmac, but can
you please describe what PVM and GVM is?
Regards,
Bjorn
> Signed-off-by: Sneh Shah <quic_snehshah@quicinc.com>
> ---
> .../stmicro/stmmac/dwmac-qcom-ethqos.c | 216 +++++++++++++++++-
> 1 file changed, 215 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
> index d3bf42d0fceb..ea89045a90a1 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
> @@ -109,6 +109,8 @@ struct qcom_ethqos {
> unsigned int num_por;
> bool rgmii_config_loopback_en;
> bool has_emac_ge_3;
> + int gvm_vlan_prio;
> + int gvm_queue;
> };
>
> static int rgmii_readl(struct qcom_ethqos *ethqos, unsigned int offset)
> @@ -710,6 +712,214 @@ static void ethqos_ptp_clk_freq_config(struct stmmac_priv *priv)
> netdev_dbg(priv->dev, "PTP rate %d\n", plat_dat->clk_ptp_rate);
> }
>
> +static ssize_t gvm_vlan_routing_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *user_buf, size_t size)
> +{
> + struct net_device *netdev = to_net_dev(dev);
> + struct stmmac_priv *priv;
> + struct qcom_ethqos *ethqos;
> + u32 prio;
> + s8 input = 0;
> +
> + if (!netdev) {
> + pr_err("netdev is NULL\n");
> + return -EINVAL;
> + }
> +
> + priv = netdev_priv(netdev);
> + if (!priv) {
> + pr_err("priv is NULL\n");
> + return -EINVAL;
> + }
> +
> + ethqos = priv->plat->bsp_priv;
> + if (!ethqos) {
> + pr_err("ethqos is NULL\n");
> + return -EINVAL;
> + }
> +
> + if (kstrtos8(user_buf, 0, &input)) {
> + pr_err("Error in reading option from user\n");
> + return -EINVAL;
> + }
> +
> + if (input < 1 || input > 7) {
> + pr_err("Invalid option set by user\n");
> + return -EINVAL;
> + }
> +
> + if (input == ethqos->gvm_vlan_prio)
> + pr_err("No effect as duplicate input\n");
> +
> + ethqos->gvm_vlan_prio = input;
> + prio = 1 << input;
> +
> + stmmac_rx_queue_prio(priv, priv->hw, prio, ethqos->gvm_queue);
> +
> + return size;
> +}
> +
> +static ssize_t gvm_queue_mapping_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *user_buf, size_t size)
> +{
> + struct net_device *netdev = to_net_dev(dev);
> + struct stmmac_priv *priv;
> + struct qcom_ethqos *ethqos;
> + u32 prio;
> + s8 input = 0;
> +
> + if (!netdev) {
> + pr_err("netdev is NULL\n");
> + return -EINVAL;
> + }
> +
> + priv = netdev_priv(netdev);
> + if (!priv) {
> + pr_err("priv is NULL\n");
> + return -EINVAL;
> + }
> +
> + ethqos = priv->plat->bsp_priv;
> + if (!ethqos) {
> + pr_err("ethqos is NULL\n");
> + return -EINVAL;
> + }
> +
> + if (kstrtos8(user_buf, 0, &input)) {
> + pr_err("Error in reading option from user\n");
> + return -EINVAL;
> + }
> +
> + if (input == ethqos->gvm_queue)
> + pr_err("No effect as duplicate input\n");
> +
> + ethqos->gvm_queue = input;
> + prio = 1 << input;
> +
> + return size;
> +}
> +
> +static ssize_t gvm_queue_mapping_show(struct device *dev,
> + struct device_attribute *attr, char *user_buf)
> +{
> + struct net_device *netdev = to_net_dev(dev);
> + struct stmmac_priv *priv;
> + struct qcom_ethqos *ethqos;
> +
> + if (!netdev) {
> + pr_err("netdev is NULL\n");
> + return -EINVAL;
> + }
> +
> + priv = netdev_priv(netdev);
> + if (!priv) {
> + pr_err("priv is NULL\n");
> + return -EINVAL;
> + }
> +
> + ethqos = priv->plat->bsp_priv;
> + if (!ethqos) {
> + pr_err("ethqos is NULL\n");
> + return -EINVAL;
> + }
> +
> + return scnprintf(user_buf, 256, "%d\n", ethqos->gvm_queue);
> +}
> +
> +static ssize_t gvm_vlan_routing_show(struct device *dev,
> + struct device_attribute *attr, char *user_buf)
> +{
> + struct net_device *netdev = to_net_dev(dev);
> + struct stmmac_priv *priv;
> + struct qcom_ethqos *ethqos;
> +
> + if (!netdev) {
> + pr_err("netdev is NULL\n");
> + return -EINVAL;
> + }
> +
> + priv = netdev_priv(netdev);
> + if (!priv) {
> + pr_err("priv is NULL\n");
> + return -EINVAL;
> + }
> +
> + ethqos = priv->plat->bsp_priv;
> + if (!ethqos) {
> + pr_err("ethqos is NULL\n");
> + return -EINVAL;
> + }
> +
> + return scnprintf(user_buf, 256, "%d\n", ethqos->gvm_vlan_prio);
> +}
> +
> +static DEVICE_ATTR_RW(gvm_queue_mapping);
> +
> +static DEVICE_ATTR_RW(gvm_vlan_routing);
> +
> +static int ethqos_remove_sysfs(struct qcom_ethqos *ethqos)
> +{
> + struct net_device *net_dev;
> +
> + if (!ethqos) {
> + pr_err("ethqos is NULL\n");
> + return -EINVAL;
> + }
> +
> + net_dev = platform_get_drvdata(ethqos->pdev);
> + if (!net_dev) {
> + pr_err("netdev is NULL\n");
> + return -EINVAL;
> + }
> +
> + sysfs_remove_file(&net_dev->dev.kobj,
> + &dev_attr_gvm_queue_mapping.attr);
> + sysfs_remove_file(&net_dev->dev.kobj,
> + &dev_attr_gvm_vlan_routing.attr);
> +
> + return 0;
> +}
> +
> +static int ethqos_create_sysfs(struct qcom_ethqos *ethqos)
> +{
> + int ret;
> + struct net_device *net_dev;
> +
> + if (!ethqos) {
> + pr_err("ethqos is NULL\n");
> + return -EINVAL;
> + }
> +
> + net_dev = platform_get_drvdata(ethqos->pdev);
> + if (!net_dev) {
> + pr_err("netdev is NULL\n");
> + return -EINVAL;
> + }
> +
> + ret = sysfs_create_file(&net_dev->dev.kobj,
> + &dev_attr_gvm_queue_mapping.attr);
> + if (ret) {
> + pr_err("unable to create passthrough_en sysfs node\n");
> + goto fail;
> + }
> +
> + ret = sysfs_create_file(&net_dev->dev.kobj,
> + &dev_attr_gvm_vlan_routing.attr);
> + if (ret) {
> + pr_err("unable to create cv2x_priority sysfs node\n");
> + goto fail;
> + }
> +
> + return ret;
> +
> +fail:
> + ethqos_remove_sysfs(ethqos);
> +
> + return ret;
> +}
> +
> static int qcom_ethqos_probe(struct platform_device *pdev)
> {
> struct device_node *np = pdev->dev.of_node;
> @@ -812,7 +1022,11 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
> plat_dat->serdes_powerdown = qcom_ethqos_serdes_powerdown;
> }
>
> - return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
> + ret = devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
> + if (ret)
> + return ret;
> +
> + return ethqos_create_sysfs(ethqos);
> }
>
> static const struct of_device_id qcom_ethqos_match[] = {
> --
> 2.17.1
>
>
Hi Sneh,
kernel test robot noticed the following build warnings:
[auto build test WARNING on net-next/main]
url: https://github.com/intel-lab-lkp/linux/commits/Sneh-Shah/net-stmmac-qcom-ethqos-Add-sysfs-nodes-for-qcom-ethqos/20231204-165232
base: net-next/main
patch link: https://lore.kernel.org/r/20231204084854.31543-1-quic_snehshah%40quicinc.com
patch subject: [PATCH net-next] net: stmmac: qcom-ethqos: Add sysfs nodes for qcom ethqos
config: alpha-allyesconfig (https://download.01.org/0day-ci/archive/20231205/202312051347.L3L2pNLv-lkp@intel.com/config)
compiler: alpha-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231205/202312051347.L3L2pNLv-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202312051347.L3L2pNLv-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c: In function 'gvm_queue_mapping_store':
>> drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c:770:13: warning: variable 'prio' set but not used [-Wunused-but-set-variable]
770 | u32 prio;
| ^~~~
vim +/prio +770 drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
762
763 static ssize_t gvm_queue_mapping_store(struct device *dev,
764 struct device_attribute *attr,
765 const char *user_buf, size_t size)
766 {
767 struct net_device *netdev = to_net_dev(dev);
768 struct stmmac_priv *priv;
769 struct qcom_ethqos *ethqos;
> 770 u32 prio;
771 s8 input = 0;
772
773 if (!netdev) {
774 pr_err("netdev is NULL\n");
775 return -EINVAL;
776 }
777
778 priv = netdev_priv(netdev);
779 if (!priv) {
780 pr_err("priv is NULL\n");
781 return -EINVAL;
782 }
783
784 ethqos = priv->plat->bsp_priv;
785 if (!ethqos) {
786 pr_err("ethqos is NULL\n");
787 return -EINVAL;
788 }
789
790 if (kstrtos8(user_buf, 0, &input)) {
791 pr_err("Error in reading option from user\n");
792 return -EINVAL;
793 }
794
795 if (input == ethqos->gvm_queue)
796 pr_err("No effect as duplicate input\n");
797
798 ethqos->gvm_queue = input;
799 prio = 1 << input;
800
801 return size;
802 }
803
On Mon, Dec 04, 2023 at 02:18:54PM +0530, Sneh Shah wrote:
> Add sysfs nodes to conifigure routing of specific vlan id to GVM queue.
> GVM queue is not exposed to PVM stmmac, so TC ops can't configure routing.
Adding files in /sysfs has ~0 chance of being accepted.
As requested, please explain what all these different hardware blocks
are, and what you are trying to achieve. We can then recommend the
correct interface.
Andrew
---
pw-bot: cr
On 12/5/2023 8:38 PM, Andrew Lunn wrote:
> On Mon, Dec 04, 2023 at 02:18:54PM +0530, Sneh Shah wrote:
>> Add sysfs nodes to conifigure routing of specific vlan id to GVM queue.
>> GVM queue is not exposed to PVM stmmac, so TC ops can't configure routing.
>
> Adding files in /sysfs has ~0 chance of being accepted.
>
> As requested, please explain what all these different hardware blocks
> are, and what you are trying to achieve. We can then recommend the
> correct interface.
>
> Andrew
>
> ---
> pw-bot: cr
We have multiVM Architecture here. PVM will have stmmac running with 4 Rx Tx queues. stmmac in PVM is responsible to configure whole ethernet HW MAC/DMA/MTL ( including clocks, regulators and other core bsp elements).
In GVM we have thin Ethernet driver, which is responsible to configure and manage only 1 Rx/TX queue, i.e 5th Rx/Tx ethernet queue. GVM can't access any other resisters apart from this 5th queue specific MTL and DMA registers.
We need to route vlan traffic of a specific Priority to GVM Queue (Ethernet queue 5) via programming a MAC register. The MAC register is not accessible in GVM and has to be programmed from PVM. stmmac already has TC OPS to program this routing via vlan priority. However, as PVM has only 4 queues enabled, TC tool will not take 5th queue as input. Hence, these nodes were added to conifure the MAC register to route specific vlan packets to 5th queue in GVM.
Note: The queues mentioned above are HW MTL Queues and DMA Channels. The routing can be done in the HW itself based on vlan pcp before the packets reach to driver.
On Wed, Dec 06, 2023 at 05:17:25PM +0530, Sneh Shah wrote:
>
>
> On 12/5/2023 8:38 PM, Andrew Lunn wrote:
> > On Mon, Dec 04, 2023 at 02:18:54PM +0530, Sneh Shah wrote:
> >> Add sysfs nodes to conifigure routing of specific vlan id to GVM queue.
> >> GVM queue is not exposed to PVM stmmac, so TC ops can't configure routing.
> >
> > Adding files in /sysfs has ~0 chance of being accepted.
> >
> > As requested, please explain what all these different hardware blocks
> > are, and what you are trying to achieve. We can then recommend the
> > correct interface.
> >
> > Andrew
> >
> > ---
> > pw-bot: cr
>
> We have multiVM Architecture here. PVM will have stmmac running with
> 4 Rx Tx queues. stmmac in PVM is responsible to configure whole
> ethernet HW MAC/DMA/MTL ( including clocks, regulators and other
> core bsp elements).
Please remember that stmmac is mostly used in embedded systems. People
used to embedded systems generally don't know virtual machine
terminology. So please spell out what PBM, GVM, etc mean.
> In GVM we have thin Ethernet driver, which is responsible to
> configure and manage only 1 Rx/TX queue, i.e 5th Rx/Tx ethernet
> queue. GVM can't access any other resisters apart from this 5th
> queue specific MTL and DMA registers.
> We need to route vlan traffic of a specific Priority to GVM Queue
> (Ethernet queue 5) via programming a MAC register. The MAC register
> is not accessible in GVM and has to be programmed from PVM. stmmac
> already has TC OPS to program this routing via vlan
> priority. However, as PVM has only 4 queues enabled, TC tool will
> not take 5th queue as input. Hence, these nodes were added to
> conifure the MAC register to route specific vlan packets to 5th
> queue in GVM.
> Note: The queues mentioned above are HW MTL Queues and DMA
> Channels. The routing can be done in the HW itself based on vlan pcp
> before the packets reach to driver.
Is the normal way you would do this is like this:
tc qdisc add dev eth1 parent root handle 100 \
mqprio num_tc 4 \
map 0 1 2 3 0 0 0 0 0 0 0 0 0 0 0 0 \
queues 1@0 1@1 1@2 1@3 \
hw 1
But you are saying that you cannot extend this to 5 queues?
Andrew
On 12/6/2023 7:22 PM, Andrew Lunn wrote:
> On Wed, Dec 06, 2023 at 05:17:25PM +0530, Sneh Shah wrote:
>>
>>
>> On 12/5/2023 8:38 PM, Andrew Lunn wrote:
>>> On Mon, Dec 04, 2023 at 02:18:54PM +0530, Sneh Shah wrote:
>>>> Add sysfs nodes to conifigure routing of specific vlan id to GVM queue.
>>>> GVM queue is not exposed to PVM stmmac, so TC ops can't configure routing.
>>>
>>> Adding files in /sysfs has ~0 chance of being accepted.
>>>
>>> As requested, please explain what all these different hardware blocks
>>> are, and what you are trying to achieve. We can then recommend the
>>> correct interface.
>>>
>>> Andrew
>>>
>>> ---
>>> pw-bot: cr
>>
>
>> We have multiVM Architecture here. PVM will have stmmac running with
>> 4 Rx Tx queues. stmmac in PVM is responsible to configure whole
>> ethernet HW MAC/DMA/MTL ( including clocks, regulators and other
>> core bsp elements).
>
> Please remember that stmmac is mostly used in embedded systems. People
> used to embedded systems generally don't know virtual machine
> terminology. So please spell out what PBM, GVM, etc mean.
>
>> In GVM we have thin Ethernet driver, which is responsible to
>> configure and manage only 1 Rx/TX queue, i.e 5th Rx/Tx ethernet
>> queue. GVM can't access any other resisters apart from this 5th
>> queue specific MTL and DMA registers.
>
>> We need to route vlan traffic of a specific Priority to GVM Queue
>> (Ethernet queue 5) via programming a MAC register. The MAC register
>> is not accessible in GVM and has to be programmed from PVM. stmmac
>> already has TC OPS to program this routing via vlan
>> priority. However, as PVM has only 4 queues enabled, TC tool will
>> not take 5th queue as input. Hence, these nodes were added to
>> conifure the MAC register to route specific vlan packets to 5th
>> queue in GVM.
>
>> Note: The queues mentioned above are HW MTL Queues and DMA
>> Channels. The routing can be done in the HW itself based on vlan pcp
>> before the packets reach to driver.
>
> Is the normal way you would do this is like this:
>
> tc qdisc add dev eth1 parent root handle 100 \
> mqprio num_tc 4 \
> map 0 1 2 3 0 0 0 0 0 0 0 0 0 0 0 0 \
> queues 1@0 1@1 1@2 1@3 \
> hw 1
>
> But you are saying that you cannot extend this to 5 queues?
>
> Andrew
Yes this can't extend to 5 queues. Because, stmmac in primary virtual machine will only have 4 netdev queues. So TC won't take input for 5th queue.
> >> We need to route vlan traffic of a specific Priority to GVM Queue
> >> (Ethernet queue 5) via programming a MAC register. The MAC register
> >> is not accessible in GVM and has to be programmed from PVM. stmmac
> >> already has TC OPS to program this routing via vlan
> >> priority. However, as PVM has only 4 queues enabled, TC tool will
> >> not take 5th queue as input. Hence, these nodes were added to
> >> conifure the MAC register to route specific vlan packets to 5th
> >> queue in GVM.
> >
> >> Note: The queues mentioned above are HW MTL Queues and DMA
> >> Channels. The routing can be done in the HW itself based on vlan pcp
> >> before the packets reach to driver.
> >
> > Is the normal way you would do this is like this:
> >
> > tc qdisc add dev eth1 parent root handle 100 \
> > mqprio num_tc 4 \
> > map 0 1 2 3 0 0 0 0 0 0 0 0 0 0 0 0 \
> > queues 1@0 1@1 1@2 1@3 \
> > hw 1
> >
> > But you are saying that you cannot extend this to 5 queues?
> >
> > Andrew
>
> Yes this can't extend to 5 queues. Because, stmmac in primary
> virtual machine will only have 4 netdev queues. So TC won't take
> input for 5th queue.
I still don't understand your architecture. How can you have 5 queues
if the physical hardware only has 4?
Is there any documentation for all this? Any datasheet?
Andrew
@@ -109,6 +109,8 @@ struct qcom_ethqos {
unsigned int num_por;
bool rgmii_config_loopback_en;
bool has_emac_ge_3;
+ int gvm_vlan_prio;
+ int gvm_queue;
};
static int rgmii_readl(struct qcom_ethqos *ethqos, unsigned int offset)
@@ -710,6 +712,214 @@ static void ethqos_ptp_clk_freq_config(struct stmmac_priv *priv)
netdev_dbg(priv->dev, "PTP rate %d\n", plat_dat->clk_ptp_rate);
}
+static ssize_t gvm_vlan_routing_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *user_buf, size_t size)
+{
+ struct net_device *netdev = to_net_dev(dev);
+ struct stmmac_priv *priv;
+ struct qcom_ethqos *ethqos;
+ u32 prio;
+ s8 input = 0;
+
+ if (!netdev) {
+ pr_err("netdev is NULL\n");
+ return -EINVAL;
+ }
+
+ priv = netdev_priv(netdev);
+ if (!priv) {
+ pr_err("priv is NULL\n");
+ return -EINVAL;
+ }
+
+ ethqos = priv->plat->bsp_priv;
+ if (!ethqos) {
+ pr_err("ethqos is NULL\n");
+ return -EINVAL;
+ }
+
+ if (kstrtos8(user_buf, 0, &input)) {
+ pr_err("Error in reading option from user\n");
+ return -EINVAL;
+ }
+
+ if (input < 1 || input > 7) {
+ pr_err("Invalid option set by user\n");
+ return -EINVAL;
+ }
+
+ if (input == ethqos->gvm_vlan_prio)
+ pr_err("No effect as duplicate input\n");
+
+ ethqos->gvm_vlan_prio = input;
+ prio = 1 << input;
+
+ stmmac_rx_queue_prio(priv, priv->hw, prio, ethqos->gvm_queue);
+
+ return size;
+}
+
+static ssize_t gvm_queue_mapping_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *user_buf, size_t size)
+{
+ struct net_device *netdev = to_net_dev(dev);
+ struct stmmac_priv *priv;
+ struct qcom_ethqos *ethqos;
+ u32 prio;
+ s8 input = 0;
+
+ if (!netdev) {
+ pr_err("netdev is NULL\n");
+ return -EINVAL;
+ }
+
+ priv = netdev_priv(netdev);
+ if (!priv) {
+ pr_err("priv is NULL\n");
+ return -EINVAL;
+ }
+
+ ethqos = priv->plat->bsp_priv;
+ if (!ethqos) {
+ pr_err("ethqos is NULL\n");
+ return -EINVAL;
+ }
+
+ if (kstrtos8(user_buf, 0, &input)) {
+ pr_err("Error in reading option from user\n");
+ return -EINVAL;
+ }
+
+ if (input == ethqos->gvm_queue)
+ pr_err("No effect as duplicate input\n");
+
+ ethqos->gvm_queue = input;
+ prio = 1 << input;
+
+ return size;
+}
+
+static ssize_t gvm_queue_mapping_show(struct device *dev,
+ struct device_attribute *attr, char *user_buf)
+{
+ struct net_device *netdev = to_net_dev(dev);
+ struct stmmac_priv *priv;
+ struct qcom_ethqos *ethqos;
+
+ if (!netdev) {
+ pr_err("netdev is NULL\n");
+ return -EINVAL;
+ }
+
+ priv = netdev_priv(netdev);
+ if (!priv) {
+ pr_err("priv is NULL\n");
+ return -EINVAL;
+ }
+
+ ethqos = priv->plat->bsp_priv;
+ if (!ethqos) {
+ pr_err("ethqos is NULL\n");
+ return -EINVAL;
+ }
+
+ return scnprintf(user_buf, 256, "%d\n", ethqos->gvm_queue);
+}
+
+static ssize_t gvm_vlan_routing_show(struct device *dev,
+ struct device_attribute *attr, char *user_buf)
+{
+ struct net_device *netdev = to_net_dev(dev);
+ struct stmmac_priv *priv;
+ struct qcom_ethqos *ethqos;
+
+ if (!netdev) {
+ pr_err("netdev is NULL\n");
+ return -EINVAL;
+ }
+
+ priv = netdev_priv(netdev);
+ if (!priv) {
+ pr_err("priv is NULL\n");
+ return -EINVAL;
+ }
+
+ ethqos = priv->plat->bsp_priv;
+ if (!ethqos) {
+ pr_err("ethqos is NULL\n");
+ return -EINVAL;
+ }
+
+ return scnprintf(user_buf, 256, "%d\n", ethqos->gvm_vlan_prio);
+}
+
+static DEVICE_ATTR_RW(gvm_queue_mapping);
+
+static DEVICE_ATTR_RW(gvm_vlan_routing);
+
+static int ethqos_remove_sysfs(struct qcom_ethqos *ethqos)
+{
+ struct net_device *net_dev;
+
+ if (!ethqos) {
+ pr_err("ethqos is NULL\n");
+ return -EINVAL;
+ }
+
+ net_dev = platform_get_drvdata(ethqos->pdev);
+ if (!net_dev) {
+ pr_err("netdev is NULL\n");
+ return -EINVAL;
+ }
+
+ sysfs_remove_file(&net_dev->dev.kobj,
+ &dev_attr_gvm_queue_mapping.attr);
+ sysfs_remove_file(&net_dev->dev.kobj,
+ &dev_attr_gvm_vlan_routing.attr);
+
+ return 0;
+}
+
+static int ethqos_create_sysfs(struct qcom_ethqos *ethqos)
+{
+ int ret;
+ struct net_device *net_dev;
+
+ if (!ethqos) {
+ pr_err("ethqos is NULL\n");
+ return -EINVAL;
+ }
+
+ net_dev = platform_get_drvdata(ethqos->pdev);
+ if (!net_dev) {
+ pr_err("netdev is NULL\n");
+ return -EINVAL;
+ }
+
+ ret = sysfs_create_file(&net_dev->dev.kobj,
+ &dev_attr_gvm_queue_mapping.attr);
+ if (ret) {
+ pr_err("unable to create passthrough_en sysfs node\n");
+ goto fail;
+ }
+
+ ret = sysfs_create_file(&net_dev->dev.kobj,
+ &dev_attr_gvm_vlan_routing.attr);
+ if (ret) {
+ pr_err("unable to create cv2x_priority sysfs node\n");
+ goto fail;
+ }
+
+ return ret;
+
+fail:
+ ethqos_remove_sysfs(ethqos);
+
+ return ret;
+}
+
static int qcom_ethqos_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -812,7 +1022,11 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
plat_dat->serdes_powerdown = qcom_ethqos_serdes_powerdown;
}
- return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
+ ret = devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
+ if (ret)
+ return ret;
+
+ return ethqos_create_sysfs(ethqos);
}
static const struct of_device_id qcom_ethqos_match[] = {