* Re: [PATCH v5 2/5] net: ethernet: cpsw: add optional third memory region for CONTROL module
From: Sergei Shtylyov @ 2013-08-23 19:10 UTC (permalink / raw)
To: Daniel Mack
Cc: netdev, bcousson, nsekhar, davem, ujhelyi.m, mugunthanvnm,
vaibhav.bedia, d-gerlach, linux-arm-kernel, linux-omap,
devicetree
In-Reply-To: <1377283987-20040-3-git-send-email-zonque@gmail.com>
Hello.
On 08/23/2013 10:53 PM, Daniel Mack wrote:
> At least the AM33xx SoC has a control module register to configure
> details such as the hardware ethernet interface mode.
> I'm not sure whether all SoCs which feature the cpsw block have such a
> register, so that third memory region is considered optional for now.
> Signed-off-by: Daniel Mack <zonque@gmail.com>
> ---
> Documentation/devicetree/bindings/net/cpsw.txt | 5 ++++-
> drivers/net/ethernet/ti/cpsw.c | 5 +++++
> 2 files changed, 9 insertions(+), 1 deletion(-)
> diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
> index fc3263f..4feba2f 100644
> --- a/drivers/net/ethernet/ti/cpsw.c
> +++ b/drivers/net/ethernet/ti/cpsw.c
[...]
> @@ -1989,6 +1990,10 @@ static int cpsw_probe(struct platform_device *pdev)
> goto clean_runtime_disable_ret;
> }
>
> + /* Don't fail hard if the optional control memory region is missing */
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> + priv->gmii_sel_reg = devm_ioremap_resource(&pdev->dev, res);
Hm, but why now you don't fail if devm_ioremap_resource() fails?
WBR, Sergei
^ permalink raw reply
* [PATCH v5 0/5] cpsw: support for control module register
From: Daniel Mack @ 2013-08-23 18:53 UTC (permalink / raw)
To: netdev
Cc: bcousson, nsekhar, sergei.shtylyov, davem, ujhelyi.m,
mugunthanvnm, vaibhav.bedia, d-gerlach, linux-arm-kernel,
linux-omap, devicetree, Daniel Mack
Hi,
this is the 5th version of my patch set, the version history is below.
Note that for personal reasons, I won't be able to work on that patch
set for two weeks, starting from a few hours from now. If there are any
more objections or comments, I'll catch up after that period. Or if
anyone wants to make minor changes to my patches and resubmit them,
I'd also be fine with that :)
The compatible discussion is still ongoing, but I'd rather submit what
I got until now, and I'm also still convinced that having
ti,am3352-cpsw is still the best we can do.
Thanks,
Daniel
v4 -> v5:
* fix return value assignment in devm_ioremap_resource() error
paths (reported by Sergei Shtylyov)
* add more RGMII modes (reported by Mugunthan V N)
* check for IS_ERR(priv->gmii_sel_reg) in
cpsw_set_phy_interface_mode() (reported by Mugunthan V N)
* move SoC specific definitions to cpsw.h
* further simplify the probe logic, as devm_ioremap_resource()
will check for NULL resources (Sergei Shtylyov)
v3 -> v4:
* use IS_ERR() to check for failed devm_ioremap_resource()
calls (reported by Sergei Shtylyov)
v2 -> v3:
* swap "ti,am3352-cpsw" and "ti,cpsw" to work around a matching
bug (reported by Sekhar)
v1 -> v2:
* combine devm_request_mem_region() and devm_ioremap() and use
devm_ioremap_resource() (reported by Sergei Shtylyov)
* fix multi-line comment style (reported by Sergei Shtylyov)
* fix ti,rmii-clock-ext property name (reported by Sekhar)
* rebased to net-next (reported by Mugunthan V N, David Miller)
* add a new compatible type, and handle AM33xx specific
registers that way (reported by Sekhar)
* move gmii_sel_reg modifications to the open routine
(reported by Mugunthan V N)
Daniel Mack (5):
net: ethernet: cpsw: switch to devres allocations
net: ethernet: cpsw: add optional third memory region for CONTROL
module
net: ethernet: cpsw: introduce ti,am3352-cpsw compatible string
net: ethernet: cpsw: add support for hardware interface mode config
ARM: dts: am33xx: adopt to cpsw changes
Documentation/devicetree/bindings/net/cpsw.txt | 10 +-
arch/arm/boot/dts/am33xx.dtsi | 5 +-
drivers/net/ethernet/ti/cpsw.c | 248 +++++++++++++------------
drivers/net/ethernet/ti/cpsw.h | 9 +
4 files changed, 151 insertions(+), 121 deletions(-)
--
1.8.3.1
^ permalink raw reply
* [PATCH v5 1/5] net: ethernet: cpsw: switch to devres allocations
From: Daniel Mack @ 2013-08-23 18:53 UTC (permalink / raw)
To: netdev
Cc: bcousson, nsekhar, sergei.shtylyov, davem, ujhelyi.m,
mugunthanvnm, vaibhav.bedia, d-gerlach, linux-arm-kernel,
linux-omap, devicetree, Daniel Mack
In-Reply-To: <1377283987-20040-1-git-send-email-zonque@gmail.com>
This patch cleans up the allocation and error unwind paths, which
allows us to carry less information in struct cpsw_priv and reduce the
amount of jump labels in the probe functions.
Signed-off-by: Daniel Mack <zonque@gmail.com>
---
drivers/net/ethernet/ti/cpsw.c | 153 ++++++++++++-----------------------------
1 file changed, 43 insertions(+), 110 deletions(-)
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 79974e3..fc3263f 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -367,8 +367,6 @@ struct cpsw_priv {
spinlock_t lock;
struct platform_device *pdev;
struct net_device *ndev;
- struct resource *cpsw_res;
- struct resource *cpsw_wr_res;
struct napi_struct napi;
struct device *dev;
struct cpsw_platform_data data;
@@ -1712,62 +1710,55 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
if (of_property_read_u32(node, "active_slave", &prop)) {
pr_err("Missing active_slave property in the DT.\n");
- ret = -EINVAL;
- goto error_ret;
+ return -EINVAL;
}
data->active_slave = prop;
if (of_property_read_u32(node, "cpts_clock_mult", &prop)) {
pr_err("Missing cpts_clock_mult property in the DT.\n");
- ret = -EINVAL;
- goto error_ret;
+ return -EINVAL;
}
data->cpts_clock_mult = prop;
if (of_property_read_u32(node, "cpts_clock_shift", &prop)) {
pr_err("Missing cpts_clock_shift property in the DT.\n");
- ret = -EINVAL;
- goto error_ret;
+ return -EINVAL;
}
data->cpts_clock_shift = prop;
- data->slave_data = kcalloc(data->slaves, sizeof(struct cpsw_slave_data),
- GFP_KERNEL);
+ data->slave_data = devm_kzalloc(&pdev->dev, data->slaves
+ * sizeof(struct cpsw_slave_data),
+ GFP_KERNEL);
if (!data->slave_data)
- return -EINVAL;
+ return -ENOMEM;
if (of_property_read_u32(node, "cpdma_channels", &prop)) {
pr_err("Missing cpdma_channels property in the DT.\n");
- ret = -EINVAL;
- goto error_ret;
+ return -EINVAL;
}
data->channels = prop;
if (of_property_read_u32(node, "ale_entries", &prop)) {
pr_err("Missing ale_entries property in the DT.\n");
- ret = -EINVAL;
- goto error_ret;
+ return -EINVAL;
}
data->ale_entries = prop;
if (of_property_read_u32(node, "bd_ram_size", &prop)) {
pr_err("Missing bd_ram_size property in the DT.\n");
- ret = -EINVAL;
- goto error_ret;
+ return -EINVAL;
}
data->bd_ram_size = prop;
if (of_property_read_u32(node, "rx_descs", &prop)) {
pr_err("Missing rx_descs property in the DT.\n");
- ret = -EINVAL;
- goto error_ret;
+ return -EINVAL;
}
data->rx_descs = prop;
if (of_property_read_u32(node, "mac_control", &prop)) {
pr_err("Missing mac_control property in the DT.\n");
- ret = -EINVAL;
- goto error_ret;
+ return -EINVAL;
}
data->mac_control = prop;
@@ -1794,8 +1785,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
parp = of_get_property(slave_node, "phy_id", &lenp);
if ((parp == NULL) || (lenp != (sizeof(void *) * 2))) {
pr_err("Missing slave[%d] phy_id property\n", i);
- ret = -EINVAL;
- goto error_ret;
+ return -EINVAL;
}
mdio_node = of_find_node_by_phandle(be32_to_cpup(parp));
phyid = be32_to_cpup(parp+1);
@@ -1825,10 +1815,6 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
}
return 0;
-
-error_ret:
- kfree(data->slave_data);
- return ret;
}
static int cpsw_probe_dual_emac(struct platform_device *pdev,
@@ -1870,7 +1856,6 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
priv_sl2->coal_intvl = 0;
priv_sl2->bus_freq_mhz = priv->bus_freq_mhz;
- priv_sl2->cpsw_res = priv->cpsw_res;
priv_sl2->regs = priv->regs;
priv_sl2->host_port = priv->host_port;
priv_sl2->host_port_regs = priv->host_port_regs;
@@ -1914,8 +1899,8 @@ static int cpsw_probe(struct platform_device *pdev)
struct cpsw_priv *priv;
struct cpdma_params dma_params;
struct cpsw_ale_params ale_params;
- void __iomem *ss_regs, *wr_regs;
- struct resource *res;
+ void __iomem *ss_regs;
+ struct resource *res, *ss_res;
u32 slave_offset, sliver_offset, slave_size;
int ret = 0, i, k = 0;
@@ -1951,7 +1936,7 @@ static int cpsw_probe(struct platform_device *pdev)
if (cpsw_probe_dt(&priv->data, pdev)) {
pr_err("cpsw: platform data missing\n");
ret = -ENODEV;
- goto clean_ndev_ret;
+ goto clean_runtime_disable_ret;
}
data = &priv->data;
@@ -1965,11 +1950,12 @@ static int cpsw_probe(struct platform_device *pdev)
memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN);
- priv->slaves = kzalloc(sizeof(struct cpsw_slave) * data->slaves,
- GFP_KERNEL);
+ priv->slaves =
+ devm_kzalloc(&pdev->dev, sizeof(struct cpsw_slave) * data->slaves,
+ GFP_KERNEL);
if (!priv->slaves) {
- ret = -EBUSY;
- goto clean_ndev_ret;
+ ret = -ENOMEM;
+ goto clean_runtime_disable_ret;
}
for (i = 0; i < data->slaves; i++)
priv->slaves[i].slave_num = i;
@@ -1977,55 +1963,31 @@ static int cpsw_probe(struct platform_device *pdev)
priv->slaves[0].ndev = ndev;
priv->emac_port = 0;
- priv->clk = clk_get(&pdev->dev, "fck");
+ priv->clk = devm_clk_get(&pdev->dev, "fck");
if (IS_ERR(priv->clk)) {
- dev_err(&pdev->dev, "fck is not found\n");
+ dev_err(priv->dev, "fck is not found\n");
ret = -ENODEV;
- goto clean_slave_ret;
+ goto clean_runtime_disable_ret;
}
priv->coal_intvl = 0;
priv->bus_freq_mhz = clk_get_rate(priv->clk) / 1000000;
- priv->cpsw_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!priv->cpsw_res) {
- dev_err(priv->dev, "error getting i/o resource\n");
- ret = -ENOENT;
- goto clean_clk_ret;
- }
- if (!request_mem_region(priv->cpsw_res->start,
- resource_size(priv->cpsw_res), ndev->name)) {
- dev_err(priv->dev, "failed request i/o region\n");
- ret = -ENXIO;
- goto clean_clk_ret;
- }
- ss_regs = ioremap(priv->cpsw_res->start, resource_size(priv->cpsw_res));
- if (!ss_regs) {
- dev_err(priv->dev, "unable to map i/o region\n");
- goto clean_cpsw_iores_ret;
+ ss_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ss_regs = devm_ioremap_resource(&pdev->dev, ss_res);
+ if (IS_ERR(ss_regs)) {
+ ret = PTR_ERR(ss_regs);
+ goto clean_runtime_disable_ret;
}
priv->regs = ss_regs;
priv->version = __raw_readl(&priv->regs->id_ver);
priv->host_port = HOST_PORT_NUM;
- priv->cpsw_wr_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!priv->cpsw_wr_res) {
- dev_err(priv->dev, "error getting i/o resource\n");
- ret = -ENOENT;
- goto clean_iomap_ret;
- }
- if (!request_mem_region(priv->cpsw_wr_res->start,
- resource_size(priv->cpsw_wr_res), ndev->name)) {
- dev_err(priv->dev, "failed request i/o region\n");
- ret = -ENXIO;
- goto clean_iomap_ret;
- }
- wr_regs = ioremap(priv->cpsw_wr_res->start,
- resource_size(priv->cpsw_wr_res));
- if (!wr_regs) {
- dev_err(priv->dev, "unable to map i/o region\n");
- goto clean_cpsw_wr_iores_ret;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ priv->wr_regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->wr_regs)) {
+ ret = PTR_ERR(priv->wr_regs);
+ goto clean_runtime_disable_ret;
}
- priv->wr_regs = wr_regs;
memset(&dma_params, 0, sizeof(dma_params));
memset(&ale_params, 0, sizeof(ale_params));
@@ -2056,12 +2018,12 @@ static int cpsw_probe(struct platform_device *pdev)
slave_size = CPSW2_SLAVE_SIZE;
sliver_offset = CPSW2_SLIVER_OFFSET;
dma_params.desc_mem_phys =
- (u32 __force) priv->cpsw_res->start + CPSW2_BD_OFFSET;
+ (u32 __force) ss_res->start + CPSW2_BD_OFFSET;
break;
default:
dev_err(priv->dev, "unknown version 0x%08x\n", priv->version);
ret = -ENODEV;
- goto clean_cpsw_wr_iores_ret;
+ goto clean_runtime_disable_ret;
}
for (i = 0; i < priv->data.slaves; i++) {
struct cpsw_slave *slave = &priv->slaves[i];
@@ -2089,7 +2051,7 @@ static int cpsw_probe(struct platform_device *pdev)
if (!priv->dma) {
dev_err(priv->dev, "error initializing dma\n");
ret = -ENOMEM;
- goto clean_wr_iomap_ret;
+ goto clean_runtime_disable_ret;
}
priv->txch = cpdma_chan_create(priv->dma, tx_chan_num(0),
@@ -2124,8 +2086,8 @@ static int cpsw_probe(struct platform_device *pdev)
while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) {
for (i = res->start; i <= res->end; i++) {
- if (request_irq(i, cpsw_interrupt, 0,
- dev_name(&pdev->dev), priv)) {
+ if (devm_request_irq(&pdev->dev, i, cpsw_interrupt, 0,
+ dev_name(priv->dev), priv)) {
dev_err(priv->dev, "error attaching irq\n");
goto clean_ale_ret;
}
@@ -2147,7 +2109,7 @@ static int cpsw_probe(struct platform_device *pdev)
if (ret) {
dev_err(priv->dev, "error registering net device\n");
ret = -ENODEV;
- goto clean_irq_ret;
+ goto clean_ale_ret;
}
if (cpts_register(&pdev->dev, priv->cpts,
@@ -2155,44 +2117,27 @@ static int cpsw_probe(struct platform_device *pdev)
dev_err(priv->dev, "error registering cpts device\n");
cpsw_notice(priv, probe, "initialized device (regs %x, irq %d)\n",
- priv->cpsw_res->start, ndev->irq);
+ ss_res->start, ndev->irq);
if (priv->data.dual_emac) {
ret = cpsw_probe_dual_emac(pdev, priv);
if (ret) {
cpsw_err(priv, probe, "error probe slave 2 emac interface\n");
- goto clean_irq_ret;
+ goto clean_ale_ret;
}
}
return 0;
-clean_irq_ret:
- for (i = 0; i < priv->num_irqs; i++)
- free_irq(priv->irqs_table[i], priv);
clean_ale_ret:
cpsw_ale_destroy(priv->ale);
clean_dma_ret:
cpdma_chan_destroy(priv->txch);
cpdma_chan_destroy(priv->rxch);
cpdma_ctlr_destroy(priv->dma);
-clean_wr_iomap_ret:
- iounmap(priv->wr_regs);
-clean_cpsw_wr_iores_ret:
- release_mem_region(priv->cpsw_wr_res->start,
- resource_size(priv->cpsw_wr_res));
-clean_iomap_ret:
- iounmap(priv->regs);
-clean_cpsw_iores_ret:
- release_mem_region(priv->cpsw_res->start,
- resource_size(priv->cpsw_res));
-clean_clk_ret:
- clk_put(priv->clk);
-clean_slave_ret:
+clean_runtime_disable_ret:
pm_runtime_disable(&pdev->dev);
- kfree(priv->slaves);
clean_ndev_ret:
- kfree(priv->data.slave_data);
free_netdev(priv->ndev);
return ret;
}
@@ -2201,30 +2146,18 @@ static int cpsw_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
struct cpsw_priv *priv = netdev_priv(ndev);
- int i;
if (priv->data.dual_emac)
unregister_netdev(cpsw_get_slave_ndev(priv, 1));
unregister_netdev(ndev);
cpts_unregister(priv->cpts);
- for (i = 0; i < priv->num_irqs; i++)
- free_irq(priv->irqs_table[i], priv);
cpsw_ale_destroy(priv->ale);
cpdma_chan_destroy(priv->txch);
cpdma_chan_destroy(priv->rxch);
cpdma_ctlr_destroy(priv->dma);
- iounmap(priv->regs);
- release_mem_region(priv->cpsw_res->start,
- resource_size(priv->cpsw_res));
- iounmap(priv->wr_regs);
- release_mem_region(priv->cpsw_wr_res->start,
- resource_size(priv->cpsw_wr_res));
pm_runtime_disable(&pdev->dev);
- clk_put(priv->clk);
- kfree(priv->slaves);
- kfree(priv->data.slave_data);
if (priv->data.dual_emac)
free_netdev(cpsw_get_slave_ndev(priv, 1));
free_netdev(ndev);
--
1.8.3.1
^ permalink raw reply related
* [PATCH v5 5/5] ARM: dts: am33xx: adopt to cpsw changes
From: Daniel Mack @ 2013-08-23 18:53 UTC (permalink / raw)
To: netdev
Cc: bcousson, nsekhar, sergei.shtylyov, davem, ujhelyi.m,
mugunthanvnm, vaibhav.bedia, d-gerlach, linux-arm-kernel,
linux-omap, devicetree, Daniel Mack
In-Reply-To: <1377283987-20040-1-git-send-email-zonque@gmail.com>
This third memory region just denotes one single register in the CONTROL
module block. The driver uses that in order to set the correct physical
ethernet interface modes.
Also update the compatible string to make use of the am335x specific
features of the cpsw driver.
Signed-off-by: Daniel Mack <zonque@gmail.com>
---
arch/arm/boot/dts/am33xx.dtsi | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index a785b95..c7b41ae 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -446,7 +446,7 @@
};
mac: ethernet@4a100000 {
- compatible = "ti,cpsw";
+ compatible = "ti,am3352-cpsw", "ti,cpsw";
ti,hwmods = "cpgmac0";
cpdma_channels = <8>;
ale_entries = <1024>;
@@ -459,7 +459,8 @@
cpts_clock_mult = <0x80000000>;
cpts_clock_shift = <29>;
reg = <0x4a100000 0x800
- 0x4a101200 0x100>;
+ 0x4a101200 0x100
+ 0x44e10650 0x4>;
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;
--
1.8.3.1
^ permalink raw reply related
* [PATCH v5 4/5] net: ethernet: cpsw: add support for hardware interface mode config
From: Daniel Mack @ 2013-08-23 18:53 UTC (permalink / raw)
To: netdev
Cc: bcousson, nsekhar, sergei.shtylyov, davem, ujhelyi.m,
mugunthanvnm, vaibhav.bedia, d-gerlach, linux-arm-kernel,
linux-omap, devicetree, Daniel Mack
In-Reply-To: <1377283987-20040-1-git-send-email-zonque@gmail.com>
The cpsw currently lacks code to properly set up the hardware interface
mode on AM33xx. Other platforms might be equally affected.
Usually, the bootloader will configure the control module register, so
probably that's why such support wasn't needed in the past. In suspend
mode though, this register is modified, and so it needs reprogramming
after resume.
This patch adds code that makes use of the previously added and optional
support for passing the control mode register, and configures the
correct register bits when the slave is opened.
The AM33xx also has a bit for each slave to configure the RMII reference
clock direction. Setting it is now supported by a per-slave DT property.
This code path introducted by this patch is currently exclusive for
am33xx.
Signed-off-by: Daniel Mack <zonque@gmail.com>
---
Documentation/devicetree/bindings/net/cpsw.txt | 2 +
drivers/net/ethernet/ti/cpsw.c | 58 ++++++++++++++++++++++++++
drivers/net/ethernet/ti/cpsw.h | 8 ++++
3 files changed, 68 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt
index b717458..0895a51 100644
--- a/Documentation/devicetree/bindings/net/cpsw.txt
+++ b/Documentation/devicetree/bindings/net/cpsw.txt
@@ -34,6 +34,8 @@ Required properties:
- phy_id : Specifies slave phy id
- phy-mode : The interface between the SoC and the PHY (a string
that of_get_phy_mode() can understand)
+- ti,rmii-clock-ext : If present, the driver will configure the RMII
+ interface to external clock usage
- mac-address : Specifies slave MAC address
Optional properties:
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 87c2bab..64e2269 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -980,6 +980,60 @@ static inline void cpsw_add_dual_emac_def_ale_entries(
priv->host_port, ALE_VLAN, slave->port_vlan);
}
+static void cpsw_set_phy_interface_mode(struct cpsw_slave *slave,
+ struct cpsw_priv *priv)
+{
+ u32 reg;
+ u32 mask;
+ u32 mode = 0;
+
+ switch (priv->data.hw_type) {
+ case CPSW_TYPE_AM33XX:
+ if (IS_ERR(priv->gmii_sel_reg))
+ break;
+
+ reg = readl(priv->gmii_sel_reg);
+
+ if (slave->phy) {
+ switch (slave->phy->interface) {
+ case PHY_INTERFACE_MODE_MII:
+ default:
+ mode = AM33XX_GMII_SEL_MODE_MII;
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+ mode = AM33XX_GMII_SEL_MODE_RMII;
+ break;
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ mode = AM33XX_GMII_SEL_MODE_RGMII;
+ break;
+ };
+ }
+
+ mask = 0x3 << (slave->slave_num * 2) |
+ BIT(slave->slave_num + 6);
+ mode <<= slave->slave_num * 2;
+
+ if (slave->data->rmii_clock_external) {
+ if (slave->slave_num == 0)
+ mode |= AM33XX_GMII_SEL_RMII1_IO_CLK_EN;
+ else
+ mode |= AM33XX_GMII_SEL_RMII2_IO_CLK_EN;
+ }
+
+ reg &= ~mask;
+ reg |= mode;
+
+ writel(reg, priv->gmii_sel_reg);
+ break;
+
+ default:
+ break;
+ }
+}
+
static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
{
char name[32];
@@ -1028,6 +1082,8 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
slave->phy->phy_id);
phy_start(slave->phy);
}
+
+ cpsw_set_phy_interface_mode(slave, priv);
}
static inline void cpsw_add_default_vlan(struct cpsw_priv *priv)
@@ -1823,6 +1879,8 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
memcpy(slave_data->mac_addr, mac_addr, ETH_ALEN);
slave_data->phy_if = of_get_phy_mode(slave_node);
+ if (of_find_property(slave_node, "ti,rmii-clock-ext", NULL))
+ slave_data->rmii_clock_external = true;
if (data->dual_emac) {
if (of_property_read_u32(slave_node, "dual_emac_res_vlan",
diff --git a/drivers/net/ethernet/ti/cpsw.h b/drivers/net/ethernet/ti/cpsw.h
index 96c374a..0ee0fbe 100644
--- a/drivers/net/ethernet/ti/cpsw.h
+++ b/drivers/net/ethernet/ti/cpsw.h
@@ -19,6 +19,7 @@
struct cpsw_slave_data {
char phy_id[MII_BUS_ID_SIZE];
int phy_if;
+ bool rmii_clock_external;
u8 mac_addr[ETH_ALEN];
u16 dual_emac_res_vlan; /* Reserved VLAN for DualEMAC */
};
@@ -40,4 +41,11 @@ struct cpsw_platform_data {
u32 hw_type; /* hardware type as specified in 'compatible' */
};
+/* SoC specific definitions for the CONTROL port */
+#define AM33XX_GMII_SEL_MODE_MII (0)
+#define AM33XX_GMII_SEL_MODE_RMII (1)
+#define AM33XX_GMII_SEL_MODE_RGMII (2)
+
+#define AM33XX_GMII_SEL_RMII2_IO_CLK_EN BIT(7)
+#define AM33XX_GMII_SEL_RMII1_IO_CLK_EN BIT(6)
#endif /* __CPSW_H__ */
--
1.8.3.1
^ permalink raw reply related
* [PATCH v5 3/5] net: ethernet: cpsw: introduce ti,am3352-cpsw compatible string
From: Daniel Mack @ 2013-08-23 18:53 UTC (permalink / raw)
To: netdev
Cc: bcousson, nsekhar, sergei.shtylyov, davem, ujhelyi.m,
mugunthanvnm, vaibhav.bedia, d-gerlach, linux-arm-kernel,
linux-omap, devicetree, Daniel Mack
In-Reply-To: <1377283987-20040-1-git-send-email-zonque@gmail.com>
In order to support features that are specific to the AM335x IP, we have
to add hardware types and another compatible string.
Signed-off-by: Daniel Mack <zonque@gmail.com>
---
Documentation/devicetree/bindings/net/cpsw.txt | 3 ++-
drivers/net/ethernet/ti/cpsw.c | 32 ++++++++++++++++++++------
drivers/net/ethernet/ti/cpsw.h | 1 +
3 files changed, 28 insertions(+), 8 deletions(-)
diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt
index 4e5ca54..b717458 100644
--- a/Documentation/devicetree/bindings/net/cpsw.txt
+++ b/Documentation/devicetree/bindings/net/cpsw.txt
@@ -2,7 +2,8 @@ TI SoC Ethernet Switch Controller Device Tree Bindings
------------------------------------------------------
Required properties:
-- compatible : Should be "ti,cpsw"
+- compatible : Should be "ti,cpsw" for generic cpsw support, or
+ "ti,am3352-cpsw" for AM3352 SoCs
- reg : physical base address and size of the cpsw
registers map.
An optional third memory region can be supplied if
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 4feba2f..87c2bab 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -155,6 +155,11 @@ do { \
((priv->data.dual_emac) ? priv->emac_port : \
priv->data.active_slave)
+enum {
+ CPSW_TYPE_GENERIC,
+ CPSW_TYPE_AM33XX
+};
+
static int debug_level;
module_param(debug_level, int, 0);
MODULE_PARM_DESC(debug_level, "cpsw debug level (NETIF_MSG bits)");
@@ -1692,17 +1697,36 @@ static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv,
slave->port_vlan = data->dual_emac_res_vlan;
}
+static const struct of_device_id cpsw_of_mtable[] = {
+ {
+ .compatible = "ti,am3352-cpsw",
+ .data = (void *) CPSW_TYPE_AM33XX
+ }, {
+ .compatible = "ti,cpsw",
+ .data = (void *) CPSW_TYPE_GENERIC
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, cpsw_of_mtable);
+
static int cpsw_probe_dt(struct cpsw_platform_data *data,
struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
+ const struct of_device_id *match;
struct device_node *slave_node;
+ unsigned long match_data;
int i = 0, ret;
u32 prop;
- if (!node)
+ match = of_match_device(cpsw_of_mtable, &pdev->dev);
+
+ if (!node || !match)
return -EINVAL;
+ match_data = (unsigned long) match->data;
+ data->hw_type = match_data;
+
if (of_property_read_u32(node, "slaves", &prop)) {
pr_err("Missing slaves property in the DT.\n");
return -EINVAL;
@@ -2207,12 +2231,6 @@ static const struct dev_pm_ops cpsw_pm_ops = {
.resume = cpsw_resume,
};
-static const struct of_device_id cpsw_of_mtable[] = {
- { .compatible = "ti,cpsw", },
- { /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, cpsw_of_mtable);
-
static struct platform_driver cpsw_driver = {
.driver = {
.name = "cpsw",
diff --git a/drivers/net/ethernet/ti/cpsw.h b/drivers/net/ethernet/ti/cpsw.h
index eb3e101..96c374a 100644
--- a/drivers/net/ethernet/ti/cpsw.h
+++ b/drivers/net/ethernet/ti/cpsw.h
@@ -37,6 +37,7 @@ struct cpsw_platform_data {
u32 mac_control; /* Mac control register */
u16 default_vlan; /* Def VLAN for ALE lookup in VLAN aware mode*/
bool dual_emac; /* Enable Dual EMAC mode */
+ u32 hw_type; /* hardware type as specified in 'compatible' */
};
#endif /* __CPSW_H__ */
--
1.8.3.1
^ permalink raw reply related
* [PATCH v5 2/5] net: ethernet: cpsw: add optional third memory region for CONTROL module
From: Daniel Mack @ 2013-08-23 18:53 UTC (permalink / raw)
To: netdev
Cc: bcousson, nsekhar, sergei.shtylyov, davem, ujhelyi.m,
mugunthanvnm, vaibhav.bedia, d-gerlach, linux-arm-kernel,
linux-omap, devicetree, Daniel Mack
In-Reply-To: <1377283987-20040-1-git-send-email-zonque@gmail.com>
At least the AM33xx SoC has a control module register to configure
details such as the hardware ethernet interface mode.
I'm not sure whether all SoCs which feature the cpsw block have such a
register, so that third memory region is considered optional for now.
Signed-off-by: Daniel Mack <zonque@gmail.com>
---
Documentation/devicetree/bindings/net/cpsw.txt | 5 ++++-
drivers/net/ethernet/ti/cpsw.c | 5 +++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt
index 05d660e..4e5ca54 100644
--- a/Documentation/devicetree/bindings/net/cpsw.txt
+++ b/Documentation/devicetree/bindings/net/cpsw.txt
@@ -4,7 +4,10 @@ TI SoC Ethernet Switch Controller Device Tree Bindings
Required properties:
- compatible : Should be "ti,cpsw"
- reg : physical base address and size of the cpsw
- registers map
+ registers map.
+ An optional third memory region can be supplied if
+ the platform has a control module register to
+ configure phy interface details
- interrupts : property with a value describing the interrupt
number
- interrupt-parent : The parent interrupt controller
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index fc3263f..4feba2f 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -372,6 +372,7 @@ struct cpsw_priv {
struct cpsw_platform_data data;
struct cpsw_ss_regs __iomem *regs;
struct cpsw_wr_regs __iomem *wr_regs;
+ u32 __iomem *gmii_sel_reg;
u8 __iomem *hw_stats;
struct cpsw_host_regs __iomem *host_port_regs;
u32 msg_enable;
@@ -1989,6 +1990,10 @@ static int cpsw_probe(struct platform_device *pdev)
goto clean_runtime_disable_ret;
}
+ /* Don't fail hard if the optional control memory region is missing */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ priv->gmii_sel_reg = devm_ioremap_resource(&pdev->dev, res);
+
memset(&dma_params, 0, sizeof(dma_params));
memset(&ale_params, 0, sizeof(ale_params));
--
1.8.3.1
^ permalink raw reply related
* RE: [net-next v2 3/8] i40e: driver ethtool core
From: Nelson, Shannon @ 2013-08-23 18:40 UTC (permalink / raw)
To: Stefan Assmann, Kirsher, Jeffrey T
Cc: davem@davemloft.net, Brandeburg, Jesse, netdev@vger.kernel.org,
gospo@redhat.com, Waskiewicz Jr, Peter P,
e1000-devel@lists.sourceforge.net
In-Reply-To: <52179717.20301@kpanic.de>
> -----Original Message-----
> From: Stefan Assmann [mailto:sassmann@kpanic.de]
> Sent: Friday, August 23, 2013 10:09 AM
> To: Kirsher, Jeffrey T
> Cc: davem@davemloft.net; Brandeburg, Jesse; netdev@vger.kernel.org;
> gospo@redhat.com; Nelson, Shannon; Waskiewicz Jr, Peter P; e1000-
> devel@lists.sourceforge.net
> Subject: Re: [net-next v2 3/8] i40e: driver ethtool core
>
> On 23.08.2013 04:15, Jeff Kirsher wrote:
> > From: Jesse Brandeburg <jesse.brandeburg@intel.com>
> >
> > This patch contains the ethtool interface and implementation.
> >
> > The goal in this patch series is minimal functionality while not
> > including much in the way of "set support."
>
> [...]
>
> > diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
> b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
>
> [...]
>
> > +#define I40E_QUEUE_STATS_LEN(n) \
> > + ((((struct i40e_netdev_priv *)netdev_priv((n)))->vsi-
> >num_queue_pairs + \
> > + ((struct i40e_netdev_priv *)netdev_priv((n)))->vsi-
> >num_queue_pairs) * 2)
> > +#define I40E_GLOBAL_STATS_LEN ARRAY_SIZE(i40e_gstrings_stats)
> > +#define I40E_NETDEV_STATS_LEN ARRAY_SIZE(i40e_gstrings_net_stats)
> > +#define I40E_VSI_STATS_LEN(n) (I40E_NETDEV_STATS_LEN + \
>
> Please use tabs for spacing here.
Yep.
>
> > + I40E_QUEUE_STATS_LEN((n)))
> > +#define I40E_PFC_STATS_LEN ( \
> > + (FIELD_SIZEOF(struct i40e_pf, stats.priority_xoff_rx) + \
> > + FIELD_SIZEOF(struct i40e_pf, stats.priority_xon_rx) + \
> > + FIELD_SIZEOF(struct i40e_pf, stats.priority_xoff_tx) + \
> > + FIELD_SIZEOF(struct i40e_pf, stats.priority_xon_tx) + \
> > + FIELD_SIZEOF(struct i40e_pf, stats.priority_xon_2_xoff)) \
> > + / sizeof(u64))
> > +#define I40E_PF_STATS_LEN(n) (I40E_GLOBAL_STATS_LEN + \
>
> Here as well.
Yep.
>
> [...]
>
> > +static void i40e_get_regs(struct net_device *netdev, struct
> ethtool_regs *regs,
> > + void *p)
> > +{
> > + struct i40e_netdev_priv *np = netdev_priv(netdev);
> > + struct i40e_pf *pf = np->vsi->back;
> > + struct i40e_hw *hw = &pf->hw;
> > + u32 *reg_buf = p;
> > + int i, j, ri;
> > + u32 reg;
> > +
> > + /* Tell ethtool which driver-version-specific regs output we have.
> > + *
> > + * At some point, if we have ethtool doing special formatting of
> > + * this data, it will rely on this version number to know how to
> > + * interpret things. Hence, this needs to be updated if/when the
> > + * diags register table is changed.
> > + */
> > + regs->version = 1;
> > +
> > + /* loop through the diags reg table for what to print */
> > + ri = 0;
> > + for (i = 0; i40e_reg_list[i].offset != 0; i++) {
> > + for (j = 0; j < i40e_reg_list[i].elements; j++) {
> > + reg = i40e_reg_list[i].offset
> > + + (j * i40e_reg_list[i].stride);
> > + reg_buf[ri++] = rd32(hw, reg);
> > + }
> > + }
> > +
> > + return;
>
> void function, no return necessary.
Thanks
>
> > +}
>
> [...]
>
> > +static void i40e_get_ethtool_stats(struct net_device *netdev,
> > + struct ethtool_stats *stats, u64 *data)
> > +{
> > + struct i40e_netdev_priv *np = netdev_priv(netdev);
> > + struct i40e_vsi *vsi = np->vsi;
> > + struct i40e_pf *pf = vsi->back;
> > + struct rtnl_link_stats64 *net_stats =
> i40e_get_vsi_stats_struct(vsi);
> > + char *p;
> > + int i, j;
> > +
> > + i40e_update_stats(vsi);
> > +
> > + i = 0;
>
> This could be avoided by int i = 0 few lines above.
Sure.
>
> > + for (j = 0; j < I40E_NETDEV_STATS_LEN; j++) {
> > + p = (char *)net_stats +
> i40e_gstrings_net_stats[j].stat_offset;
> > + data[i++] = (i40e_gstrings_net_stats[j].sizeof_stat ==
> > + sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
> > + }
> > + for (j = 0; j < vsi->num_queue_pairs; j++) {
> > + data[i++] = vsi->tx_rings[j].tx_stats.packets;
> > + data[i++] = vsi->tx_rings[j].tx_stats.bytes;
> > + }
> > + for (j = 0; j < vsi->num_queue_pairs; j++) {
> > + data[i++] = vsi->rx_rings[j].rx_stats.packets;
> > + data[i++] = vsi->rx_rings[j].rx_stats.bytes;
> > + }
> > + if (vsi == pf->vsi[pf->lan_vsi]) {
> > + for (j = 0; j < I40E_GLOBAL_STATS_LEN; j++) {
> > + p = (char *)pf + i40e_gstrings_stats[j].stat_offset;
> > + data[i++] = (i40e_gstrings_stats[j].sizeof_stat ==
> > + sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
> > + }
> > + for (j = 0; j < I40E_MAX_USER_PRIORITY; j++) {
> > + data[i++] = pf->stats.priority_xon_tx[j];
> > + data[i++] = pf->stats.priority_xoff_tx[j];
> > + }
> > + for (j = 0; j < I40E_MAX_USER_PRIORITY; j++) {
> > + data[i++] = pf->stats.priority_xon_rx[j];
> > + data[i++] = pf->stats.priority_xoff_rx[j];
> > + }
> > + for (j = 0; j < I40E_MAX_USER_PRIORITY; j++)
> > + data[i++] = pf->stats.priority_xon_2_xoff[j];
> > + }
> > +
> > + return;
>
> Another void function.
Yep.
>
> [...]
>
> > +static struct ethtool_ops i40e_ethtool_ops = {
> > + .get_settings = i40e_get_settings,
> > + .get_drvinfo = i40e_get_drvinfo,
> > + .get_regs_len = i40e_get_regs_len,
> > + .get_regs = i40e_get_regs,
> > + .nway_reset = i40e_nway_reset,
> > + .get_link = ethtool_op_get_link,
> > + .get_wol = i40e_get_wol,
> > + .get_ringparam = i40e_get_ringparam,
> > + .set_ringparam = i40e_set_ringparam,
> > + .get_pauseparam = i40e_get_pauseparam,
> > + .get_msglevel = i40e_get_msglevel,
> > + .set_msglevel = i40e_set_msglevel,
> > + .get_rxnfc = i40e_get_rxnfc,
> > + .set_rxnfc = i40e_set_rxnfc,
> > + .self_test = i40e_diag_test,
> > + .get_strings = i40e_get_strings,
> > + .set_phys_id = i40e_set_phys_id,
> > + .get_sset_count = i40e_get_sset_count,
> > + .get_ethtool_stats = i40e_get_ethtool_stats,
> > + .get_coalesce = i40e_get_coalesce,
> > + .set_coalesce = i40e_set_coalesce,
> > + .get_ts_info = i40e_get_ts_info,
> > +};
>
> It would be nice if you could use tabs for spacing here.
Sure.
>
> Stefan
^ permalink raw reply
* RE: [net-next v2 2/8] i40e: transmit, receive, and napi
From: Nelson, Shannon @ 2013-08-23 18:37 UTC (permalink / raw)
To: Stefan Assmann, Kirsher, Jeffrey T
Cc: davem@davemloft.net, Brandeburg, Jesse, netdev@vger.kernel.org,
gospo@redhat.com, Waskiewicz Jr, Peter P,
e1000-devel@lists.sourceforge.net
In-Reply-To: <5217589F.6000602@kpanic.de>
> -----Original Message-----
> From: Stefan Assmann [mailto:sassmann@kpanic.de]
> Sent: Friday, August 23, 2013 5:42 AM
> To: Kirsher, Jeffrey T
> Cc: davem@davemloft.net; Brandeburg, Jesse; netdev@vger.kernel.org;
> gospo@redhat.com; Nelson, Shannon; Waskiewicz Jr, Peter P; e1000-
> devel@lists.sourceforge.net
> Subject: Re: [net-next v2 2/8] i40e: transmit, receive, and napi
>
> On 23.08.2013 04:15, Jeff Kirsher wrote:
> > From: Jesse Brandeburg <jesse.brandeburg@intel.com>
> >
> > This patch contains the transmit, receive, and napi routines, as well
> > as ancillary routines.
> >
> > This file is code that is (will be) shared between the VF and PF
> > drivers.
>
> Just some small nitpicks.
Thanks again.
>
> > diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> > new file mode 100644
> > index 0000000..ceafef0
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
>
> [...]
>
> > +static void i40e_receive_skb(struct i40e_ring *rx_ring,
> > + struct sk_buff *skb, u16 vlan_tag)
> > +{
> > + struct i40e_vsi *vsi = rx_ring->vsi;
> > + struct i40e_q_vector *q_vector = rx_ring->q_vector;
> > + u64 flags = vsi->back->flags;
> > +
> > + if (vlan_tag & VLAN_VID_MASK)
> > + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
>
> Suggesting __constant_htons instead of htons here.
We'll follow Dave's comment on this.
>
> [...]
>
> > +static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
> > + u32 tx_flags, __be16 protocol, u8 *hdr_len,
> > + u64 *cd_type_cmd_tso_mss, u32 *cd_tunneling)
> > +{
>
> [...]
>
> > + cd_cmd = I40E_TX_CTX_DESC_TSO;
> > + cd_tso_len = skb->len - *hdr_len;
> > + cd_mss = skb_shinfo(skb)->gso_size;
> > + *cd_type_cmd_tso_mss |= ((u64)cd_cmd <<
> I40E_TXD_CTX_QW1_CMD_SHIFT)
> > + | ((u64)cd_tso_len
> > + << I40E_TXD_CTX_QW1_TSO_LEN_SHIFT)
> > + | ((u64)cd_mss << I40E_TXD_CTX_QW1_MSS_SHIFT);
>
> Should use either tab or space after cd_cmd, cd_mss but please don't mix
> them.
Thanks, we'll tweak those.
>
> Stefan
^ permalink raw reply
* RE: [net-next v2 1/8] i40e: main driver core
From: Nelson, Shannon @ 2013-08-23 18:35 UTC (permalink / raw)
To: Stefan Assmann, Kirsher, Jeffrey T
Cc: davem@davemloft.net, Brandeburg, Jesse, netdev@vger.kernel.org,
gospo@redhat.com, Waskiewicz Jr, Peter P,
e1000-devel@lists.sourceforge.net
In-Reply-To: <52174988.2060708@kpanic.de>
> -----Original Message-----
> From: Stefan Assmann [mailto:sassmann@kpanic.de]
> Sent: Friday, August 23, 2013 4:38 AM
> To: Kirsher, Jeffrey T
> Cc: davem@davemloft.net; Brandeburg, Jesse; netdev@vger.kernel.org;
> gospo@redhat.com; Nelson, Shannon; Waskiewicz Jr, Peter P; e1000-
> devel@lists.sourceforge.net
> Subject: Re: [net-next v2 1/8] i40e: main driver core
Thanks, Stefan, for your time and comments on this and on the following postings. We appreciate the detailed feedback.
>
> On 23.08.2013 04:15, Jeff Kirsher wrote:
> > From: Jesse Brandeburg <jesse.brandeburg@intel.com>
> >
> > This is the driver for the Intel(R) Ethernet Controller XL710 Family.
> >
> > This driver is targeted at basic ethernet functionality only, and will
> be
> > improved upon further as time goes on.
> >
> > This patch mail contains the driver entry points but does not include
> transmit
> > and receive (see the next patch in the series) routines.
>
> [...]
>
> I see the term VSI a lot in the code, what exactly does it mean?
VSI is short for Virtual Station Interface - essentially a generic name for VMDq and VF type virtual interfaces into a network device. It's part of the nomenclature coming from the IEEE 802.1Qbg work. In our driver, a VSI is how we collect sets of Tx/Rx queues and mac address filters for HW offloading of network virtualization. Not every VSI will have a netdev associated to it, tho' it is a handy way to think about how they are used.
>
> > ---
> > drivers/net/ethernet/intel/i40e/i40e_main.c | 7520
> +++++++++++++++++++++++++++
> > 1 file changed, 7520 insertions(+)
> > create mode 100644 drivers/net/ethernet/intel/i40e/i40e_main.c
> >
> > diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c
> b/drivers/net/ethernet/intel/i40e/i40e_main.c
> > new file mode 100644
> > index 0000000..c2a79b5
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
>
> [...]
>
> > +/**
> > + * i40e_allocate_dma_mem_d - OS specific memory alloc for shared code
> > + * @hw: pointer to the HW structure
> > + * @mem: ptr to mem struct to fill out
> > + * @size: size of memory requested
> > + * @alignment: what to align the allocation to
> > + **/
> > +enum i40e_status_code i40e_allocate_dma_mem_d(struct i40e_hw *hw,
>
> If you want to use an enum I suggest you shorten the name to something
> like i40e_sc, otherwise the function declaration lines will become
> longer than needed and we'll have to split the arguments across multiple
> lines more than necessary.
This is where I whine about it being too late to change such a basic datatype in all the shared code of our other OS drivers that use it, and no one wants to hear it. You're right, of course, and we'll see what we can do about compressing it a little.
>
> > + struct i40e_dma_mem *mem,
> > + u64 size, u32 alignment)
> > +{
> > + struct i40e_pf *pf = (struct i40e_pf *)hw->back;
> > +
> > + if (!mem)
> > + return I40E_ERR_PARAM;
> > +
> > + mem->size = ALIGN(size, alignment);
> > + /* GFP_ZERO zeros the memory */
> > + mem->va = dma_alloc_coherent(&pf->pdev->dev, mem->size,
> > + &mem->pa, GFP_ATOMIC | __GFP_ZERO);
> > + if (mem->va)
> > + return I40E_SUCCESS;
> > + else
> > + return I40E_ERR_NO_MEMORY;
>
> Just wondering why you don't use the standard error codes like ENOMEM?
Some of these choices are driven by our use of code that is shared with other OSs.
>
> > +}
> > +
> > +/**
> > + * i40e_free_dma_mem_d - OS specific memory free for shared code
> > + * @hw: pointer to the HW structure
> > + * @mem: ptr to mem struct to free
> > + **/
> > +enum i40e_status_code i40e_free_dma_mem_d(struct i40e_hw *hw,
> > + struct i40e_dma_mem *mem)
> > +{
> > + struct i40e_pf *pf = (struct i40e_pf *)hw->back;
> > +
> > + if (!mem || !mem->va)
> > + return I40E_ERR_PARAM;
> > + dma_free_coherent(&pf->pdev->dev, mem->size, mem->va, mem->pa);
> > + mem->va = NULL;
> > + mem->pa = (dma_addr_t)NULL;
>
> Missing a blank line here.
Thanks.
>
> [...]
>
> > +/**
> > + * i40e_get_lump - find a lump of free generic resource
> > + * @pile: the pile of resource to search
> > + * @needed: the number of items needed
> > + * @id: an owner id to stick on the items assigned
> > + *
> > + * Returns the base item index of the lump, or negative for error
> > + *
> > + * The search_hint trick and lack of advanced fit-finding only work
> > + * because we're highly likely to have all the same size lump
> requests.
> > + * Linear search time and any fragmentation should be minimal.
> > + **/
> > +static int i40e_get_lump(struct i40e_lump_tracking *pile, u16 needed,
> u16 id)
> > +{
> > + int i = 0, j = 0;
> > + int ret = I40E_ERR_NO_MEMORY;
> > +
> > + if (pile == NULL || needed == 0 || id >= I40E_PILE_VALID_BIT) {
> > + pr_info("%s: param err: pile=%p needed=%d id=0x%04x\n",
> > + __func__, pile, needed, id);
>
> Shouldn't this be indented by another tab instead of the spaces? Maybe
> you could use netdev_info() instead of pr_info() so it's easier to
> understand which device is meant.
Arf, where'd those spaces come from? Will fix.
Yeah, Dave mentioned the dev_info() need as well.
>
> > + return I40E_ERR_PARAM;
> > + }
> > +
> > + /* start the linear search with an imperfect hint */
> > + i = pile->search_hint;
> > + while (i < pile->num_entries && ret < 0) {
> > + /* skip already allocated entries */
> > + if (pile->list[i] & I40E_PILE_VALID_BIT) {
> > + i++;
> > + continue;
> > + }
> > +
> > + /* do we have enough in this lump? */
> > + for (j = 0; (j < needed) && ((i+j) < pile->num_entries); j++)
> {
> > + if (pile->list[i+j] & I40E_PILE_VALID_BIT)
> > + break;
> > + }
> > +
> > + if (j == needed) {
> > + /* there was enough, so assign it to the requestor */
> > + for (j = 0; j < needed; j++)
> > + pile->list[i+j] = id | I40E_PILE_VALID_BIT;
> > + ret = i;
> > + pile->search_hint = i + j;
> > + } else {
> > + /* not enough, so skip over it and continue looking */
> > + i += j;
> > + }
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * i40e_put_lump - return a lump of generic resource
> > + * @pile: the pile of resource to search
> > + * @index: the base item index
> > + * @id: the owner id of the items assigned
> > + *
> > + * Returns the count of items in the lump
> > + **/
> > +static int i40e_put_lump(struct i40e_lump_tracking *pile, u16 index,
> u16 id)
> > +{
> > + int i = index;
> > + int count = 0;
> > +
> > + if (pile == NULL || index >= pile->num_entries)
> > + return I40E_ERR_PARAM;
> > +
> > + for (i = index;
> > + i < pile->num_entries && pile->list[i] ==
> (id|I40E_PILE_VALID_BIT);
>
> Missing spaces around |.
Got it.
>
> [...]
>
> > +static void i40e_tx_timeout(struct net_device *netdev)
> > +{
> > + struct i40e_netdev_priv *np = netdev_priv(netdev);
> > + struct i40e_vsi *vsi = np->vsi;
> > + struct i40e_pf *pf = vsi->back;
> > +
> > + pf->tx_timeout_count++;
> > +
> > + if (time_after(jiffies, (pf->tx_timeout_last_recovery + HZ*20)))
> > + pf->tx_timeout_recovery_level = 0;
> > + pf->tx_timeout_last_recovery = jiffies;
> > + netdev_info(netdev, "%s: recovery level %d\n",
> > + __func__, pf->tx_timeout_recovery_level);
> > +
> > + switch (pf->tx_timeout_recovery_level) {
> > + case 0:
> > + /* disable and re-enable queues for the VSI */
> > + if (in_interrupt()) {
> > + set_bit(__I40E_REINIT_REQUESTED, &pf->state);
> > + set_bit(__I40E_REINIT_REQUESTED, &vsi->state);
> > + } else {
> > + i40e_vsi_reinit_locked(vsi);
> > + }
> > + break;
> > + case 1:
> > + set_bit(__I40E_PF_RESET_REQUESTED, &pf->state);
> > + break;
> > + case 2:
> > + set_bit(__I40E_CORE_RESET_REQUESTED, &pf->state);
> > + break;
> > + case 3:
> > + set_bit(__I40E_GLOBAL_RESET_REQUESTED, &pf->state);
> > + break;
> > + default:
> > + netdev_err(netdev, "%s: recovery unsuccessful\n", __func__);
> > + i40e_down(vsi);
> > + break;
> > + }
> > + i40e_service_event_schedule(pf);
> > + pf->tx_timeout_recovery_level++;
> > +
> > + return;
>
> Function is void, no need for return.
Thanks.
>
> > +}
> > +
> > +/**
> > + * i40e_release_rx_desc - Store the new tail and head values
> > + * @rx_ring: ring to bump
> > + * @val: new head index
> > + **/
> > +static inline void i40e_release_rx_desc(struct i40e_ring *rx_ring,
> u32 val)
> > +{
> > + rx_ring->next_to_use = val;
> > + /* Force memory writes to complete before letting h/w
> > + * know there are new descriptors to fetch. (Only
> > + * applicable for weak-ordered memory model archs,
> > + * such as IA-64).
> > + */
> > + wmb();
> > + writel(val, rx_ring->tail);
> > +}
> > +
> > +/**
> > + * i40e_get_vsi_stats_struct - Get System Network Statistics
> > + * @vsi: the VSI we care about
> > + *
> > + * Returns the address of the device statistics structure.
> > + * The statistics are actually updated from the service task.
> > + **/
> > +struct rtnl_link_stats64 *i40e_get_vsi_stats_struct(struct i40e_vsi
> *vsi)
> > +{
> > + return &vsi->net_stats;
> > +}
> > +
> > +/**
> > + * i40e_get_netdev_stats_struct - Get statistics for netdev interface
> > + * @netdev: network interface device structure
> > + *
> > + * Returns the address of the device statistics structure.
> > + * The statistics are actually updated from the service task.
> > + **/
> > +static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct(
> > + struct net_device *netdev,
> > + struct rtnl_link_stats64 *storage)
> > +{
> > + memcpy(storage,
> > + i40e_get_vsi_stats_struct(
> > + ((struct i40e_netdev_priv *)netdev_priv(netdev))->vsi),
> > + sizeof(*storage));
>
> Missing a blank line.
Sure.
>
> > + return storage;
> > +}
> > +
> > +/**
> > + * i40e_vsi_reset_stats - Resets all stats of the given vsi
> > + * @vsi: the VSI to have its stats reset
> > + **/
> > +void i40e_vsi_reset_stats(struct i40e_vsi *vsi)
> > +{
> > + int i;
> > + struct rtnl_link_stats64 *ns;
> > +
> > + if (!vsi)
> > + return;
> > +
> > + ns = i40e_get_vsi_stats_struct(vsi);
> > + memset(ns, 0, sizeof(struct net_device_stats));
> > + memset(&vsi->net_stats_offsets, 0, sizeof(struct
> net_device_stats));
> > + memset(&vsi->eth_stats, 0, sizeof(struct i40e_eth_stats));
> > + memset(&vsi->eth_stats_offsets, 0, sizeof(struct i40e_eth_stats));
> > + if (vsi->rx_rings)
> > + for (i = 0; i < vsi->num_queue_pairs; i++) {
> > + memset(&vsi->rx_rings[i].rx_stats, 0 ,
> > + sizeof(struct i40e_rx_queue_stats));
> > + memset(&vsi->tx_rings[i].tx_stats, 0,
> > + sizeof(struct i40e_tx_queue_stats));
> > + }
> > + vsi->stat_offsets_loaded = false;
> > +}
> > +
> > +/**
> > + * i40e_pf_reset_stats - Reset all of the stats for the given pf
> > + * @pf: the PF to be reset
> > + **/
> > +void i40e_pf_reset_stats(struct i40e_pf *pf)
> > +{
> > + memset(&pf->stats, 0, sizeof(struct i40e_hw_port_stats));
> > + memset(&pf->stats_offsets, 0, sizeof(struct i40e_hw_port_stats));
> > + pf->stat_offsets_loaded = false;
> > +
>
> Remove blank line above.
Yep.
>
> > +}
> > +
> > +/**
> > + * i40e_stat_update48 - read and update a 48 bit stat from the chip
> > + * @hw: ptr to the hardware info
> > + * @hireg: the high 32 bit reg to read
> > + * @loreg: the low 32 bit reg to read
> > + * @offset_loaded: has the initial offset been loaded yet
> > + * @offset: ptr to current offset value
> > + * @stat: ptr to the stat
> > + *
> > + * Since the device stats are not reset at PFReset, they likely will
> not
> > + * be zeroed when the driver starts. We'll save the first values
> read
> > + * and use them as offsets to be subtracted from the raw values in
> order
> > + * to report stats that count from zero. In the process, we also
> manage
> > + * the potential roll-over.
> > + **/
> > +static void i40e_stat_update48(struct i40e_hw *hw, u32 hireg, u32
> loreg,
> > + bool offset_loaded, u64 *offset, u64 *stat)
> > +{
> > + u64 new_data;
>
> Missing a blank line.
Yep.
>
> > + if (hw->device_id == I40E_QEMU_DEVICE_ID) {
> > + new_data = rd32(hw, loreg);
> > + new_data |= ((u64)(rd32(hw, hireg) & 0xFFFF)) << 32;
> > + } else {
> > + new_data = rd64(hw, loreg);
> > + }
> > + if (!offset_loaded)
> > + *offset = new_data;
> > + if (likely(new_data >= *offset))
> > + *stat = new_data - *offset;
> > + else
> > + *stat = (new_data + ((u64)1 << 48)) - *offset;
> > + *stat &= 0xFFFFFFFFFFFFULL;
> > +}
> > +
> > +/**
> > + * i40e_stat_update32 - read and update a 32 bit stat from the chip
> > + * @hw: ptr to the hardware info
> > + * @reg: the hw reg to read
> > + * @offset_loaded: has the initial offset been loaded yet
> > + * @offset: ptr to current offset value
> > + * @stat: ptr to the stat
> > + **/
> > +static void i40e_stat_update32(struct i40e_hw *hw, u32 reg,
> > + bool offset_loaded, u64 *offset, u64 *stat)
> > +{
> > + u32 new_data;
>
> Missing a blank line.
Yep.
>
> > + new_data = rd32(hw, reg);
> > + if (!offset_loaded)
> > + *offset = new_data;
> > + if (likely(new_data >= *offset))
> > + *stat = (u32)(new_data - *offset);
> > + else
> > + *stat = (u32)((new_data + ((u64)1 << 32)) - *offset);
> > +}
>
> [...]
>
> > +/**
> > + * i40e_is_vsi_in_vlan - Check if VSI is in vlan mode
> > + * @vsi: the VSI to be searched
> > + *
> > + * Returns true if VSI is in vlan mode or false otherwise
> > + **/
> > +bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi)
> > +{
> > + struct i40e_mac_filter *f;
> > +
> > + /* Only -1 for all the filters denotes not in vlan mode
> > + * so we have to go through all the list in order to make sure
> > + */
> > + list_for_each_entry(f, &vsi->mac_filter_list, list) {
> > + if (f->vlan < 0)
> > + return false;
> > + }
> > +
> > + return true;
> > +}
> > +
> > +/**
> > + * i40e_put_mac_in_vlan - Goes through all the macvlan filters and
> adds a
> > + * macvlan filter for each unique vlan that already exists
>
> Superfluous space at the beginning.
Even better, that needs to be reworked to be a single line description.
>
> > + * @vsi: the VSI to be searched
> > + * @macaddr: the mac address to be filtered
> > + * @is_vf: true if it is a vf
> > + * @is_netdev: true if it is a netdev
> > + *
> > + * Returns I40E_SUCCESS on success or -ENOMEM if it could not add a
> filter
> > + **/
> > +enum i40e_status_code i40e_put_mac_in_vlan(struct i40e_vsi *vsi,
> > + u8 *macaddr,
> > + bool is_vf, bool is_netdev)
>
> This could be better indented, aligned with the struct i40e_vsi.
Yep.
>
> > +{
> > + struct i40e_mac_filter *f, *add_f;
> > +
> > + list_for_each_entry(f, &vsi->mac_filter_list, list) {
> > + if (!i40e_find_filter(vsi, macaddr, f->vlan,
> > + is_vf, is_netdev)) {
> > + add_f = i40e_add_filter(vsi, macaddr, f->vlan,
> > + is_vf, is_netdev);
> > +
> > + if (NULL == add_f) {
> > + dev_info(&vsi->back->pdev->dev, "%s: Could not add
> filter %d for %pM\n",
> > + __func__, f->vlan, f->macaddr);
> > + return -ENOMEM;
> > + }
> > + }
> > + }
> > + return I40E_SUCCESS;
> > +}
> > +
> > +/**
> > + * i40e_add_filter - Add a mac/vlan filter to the VSI
> > + * @vsi: the VSI to be searched
> > + * @macaddr: the MAC address
> > + * @vlan: the vlan
> > + * @is_vf: make sure its a vf filter, else doesn't matter
> > + * @is_netdev: make sure its a netdev filter, else doesn't matter
> > + *
> > + * Returns ptr to the filter object or NULL when no memory available.
> > + **/
> > +struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
> > + u8 *macaddr, s16 vlan,
> > + bool is_vf, bool is_netdev)
> > +{
> > + struct i40e_mac_filter *f;
>
> Just f seems to be a bad naming, can we come up with a more fitting
> name for this?
It seemed like a reasonable choice for an often-referenced object and the primary focus of the function. I understand your concern, but I think when used consistently it's not quite the problem that some other willy-nilly single letter variables can be.
>
> > +
> > + if (!vsi || !macaddr)
> > + return NULL;
> > +
> > + f = i40e_find_filter(vsi, macaddr, vlan, is_vf, is_netdev);
> > + if (NULL == f) {
> > + f = kzalloc(sizeof(*f), GFP_ATOMIC);
> > + if (NULL == f)
> > + goto add_filter_out;
> > +
> > + memcpy(f->macaddr, macaddr, ETH_ALEN);
> > + f->vlan = vlan;
> > + f->changed = true;
> > +
> > + INIT_LIST_HEAD(&f->list);
> > + list_add(&f->list, &vsi->mac_filter_list);
> > + }
> > +
> > + /* increment counter and add a new flag if needed */
> > + if (is_vf) {
> > + if (!f->is_vf) {
> > + f->is_vf = true;
> > + f->counter++;
> > + }
> > + } else if (is_netdev) {
> > + if (!f->is_netdev) {
> > + f->is_netdev = true;
> > + f->counter++;
> > + }
> > + } else {
> > + f->counter++;
> > + }
> > +
> > + /* changed tells sync_filters_subtask to
> > + * push the filter down to the firmware
> > + */
> > + if (f->changed) {
> > + vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
> > + vsi->back->flags |= I40E_FLAG_FILTER_SYNC;
> > + }
> > +
> > +add_filter_out:
> > + return f;
> > +}
> > +
> > +/**
> > + * i40e_del_filter - Remove a mac/vlan filter from the VSI
> > + * @vsi: the VSI to be searched
> > + * @macaddr: the MAC address
> > + * @vlan: the vlan
> > + * @is_vf: make sure it's a vf filter, else doesn't matter
> > + * @is_netdev: make sure it's a netdev filter, else doesn't matter
> > + **/
> > +void i40e_del_filter(struct i40e_vsi *vsi,
> > + u8 *macaddr, s16 vlan,
> > + bool is_vf, bool is_netdev)
>
> Again, please indent properly. I'm not going to make further comments
> about this.
Huh, odd, most of these are correct on my side - I'll have to look into how they got messed up on the way out.
>
> > +{
> > + struct i40e_mac_filter *f;
> > +
> > + if (!vsi || !macaddr)
> > + return;
> > +
> > + f = i40e_find_filter(vsi, macaddr, vlan, is_vf, is_netdev);
> > + if (NULL == f || f->counter == 0)
> > + goto del_filter_out;
> > +
> > + if (is_vf) {
> > + if (f->is_vf) {
> > + f->is_vf = false;
> > + f->counter--;
> > + }
> > + } else if (is_netdev) {
> > + if (f->is_netdev) {
> > + f->is_netdev = false;
> > + f->counter--;
> > + }
> > + } else {
> > + /* make sure we don't remove a filter in use by vf or netdev
> */
> > + int min_f = 0;
> > + min_f += (f->is_vf ? 1 : 0);
> > + min_f += (f->is_netdev ? 1 : 0);
> > +
> > + if (f->counter > min_f)
> > + f->counter--;
> > + }
> > +
> > + /* counter == 0 tells sync_filters_subtask to
> > + * remove the filter from the firmware's list
> > + */
> > + if (f->counter == 0) {
> > + f->changed = true;
> > + vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
> > + vsi->back->flags |= I40E_FLAG_FILTER_SYNC;
> > + }
> > +
> > +del_filter_out:
> > + return;
>
> [...]
>
> > +/**
> > + * i40e_sync_vsi_filters - Update the VSI filter list to the HW
> > + * @vsi: ptr to the VSI
> > + *
> > + * Push any outstanding VSI filter changes through the AdminQ.
> > + *
> > + * Returns I40E_SUCCESS or error value
> > + **/
> > +enum i40e_status_code i40e_sync_vsi_filters(struct i40e_vsi *vsi)
> > +{
> > + struct i40e_mac_filter *f, *ftmp;
> > + struct i40e_pf *pf;
> > + int num_add = 0;
> > + int num_del = 0;
> > + u32 changed_flags = 0;
> > + bool add_happened = false;
> > + bool promisc_forced_on = false;
> > + enum i40e_status_code ret = I40E_SUCCESS;
> > + u16 cmd_flags;
> > +
> > +#define FILTER_LIST_LEN 30
>
> Having the defines in one place at the top of the file would be nice,
> unless there's a special reason to have it here.
The reason is that it isn't needed/used anywhere else, it is simply a constant used in this function to set an arbitrary list length for processing the filters. However, since it is slightly related to the amount of data we can send down in the AQ request, perhaps we can compute a useful constant based on buffer size rather than using a random #define.
>
> > + /* empty array typed pointers, kcalloc later */
> > + struct i40e_aqc_add_macvlan_element_data *add_list;
> > + struct i40e_aqc_remove_macvlan_element_data *del_list;
> > +
> > + if (!vsi)
> > + return I40E_ERR_PARAM;
> > + while (test_and_set_bit(__I40E_CONFIG_BUSY, &vsi->state))
> > + usleep_range(1000, 2000);
> > + pf = vsi->back;
> > +
> > + if (vsi->netdev) {
> > + changed_flags = vsi->current_netdev_flags ^ vsi->netdev-
> >flags;
> > + vsi->current_netdev_flags = vsi->netdev->flags;
> > + }
> > +
> > + if (vsi->flags & I40E_VSI_FLAG_FILTER_CHANGED) {
> > + vsi->flags &= ~I40E_VSI_FLAG_FILTER_CHANGED;
> > +
> > + del_list = kcalloc(FILTER_LIST_LEN,
> > + sizeof(struct i40e_aqc_remove_macvlan_element_data),
> > + GFP_KERNEL);
> > + if (!del_list)
> > + return I40E_ERR_NO_MEMORY;
> > +
> > + list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list,
> list) {
> > + if (!f->changed)
> > + continue;
> > +
> > + if (f->counter != 0)
> > + continue;
> > + f->changed = false;
> > + cmd_flags = 0;
> > +
> > + /* add to delete list */
> > + memcpy(del_list[num_del].mac_addr,
> > + f->macaddr, ETH_ALEN);
> > + del_list[num_del].vlan_tag =
> > + cpu_to_le16((u16)(f->vlan ==
> > + I40E_VLAN_ANY ? 0 : f->vlan));
> > +
> > + /* vlan0 as wild card to allow packets from all vlans */
> > + if (f->vlan == I40E_VLAN_ANY ||
> > + (vsi->netdev && !(vsi->netdev->features &
> > + NETIF_F_HW_VLAN_CTAG_FILTER)))
> > + cmd_flags |= I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
> > + cmd_flags |= I40E_AQC_MACVLAN_DEL_PERFECT_MATCH;
> > + del_list[num_del].flags = cpu_to_le16(cmd_flags);
> > + num_del++;
> > +
> > + /* unlink from filter list */
> > + list_del(&f->list);
> > + kfree(f);
> > +
> > + /* flush a full buffer */
> > + if (num_del == FILTER_LIST_LEN) {
> > + ret = i40e_aq_remove_macvlan(&pf->hw,
> > + vsi->seid, del_list, num_del,
> > + NULL);
> > + num_del = 0;
> > + memset(del_list, 0, sizeof(*del_list));
> > +
> > + if (ret != I40E_SUCCESS)
> > + dev_info(&pf->pdev->dev,
>
> Maybe use netdev_info() instead of dev_info() in the whole driver? Would
> be nice if this was consistent.
We have to be careful about our use of netdev_info() because not always is there a netdev related to the operation. For example, if we're adding filters on behalf of one of our VFs, the netdev is in the VF context and we don't have access to it.
>
> [...]
>
> > +/**
> > + * i40e_change_mtu - NDO callback to change the Maximum Transfer Unit
> > + * @netdev: network interface device structure
> > + * @new_mtu: new value for maximum frame size
> > + *
> > + * Returns 0 on success, negative on failure
> > + **/
> > +static int i40e_change_mtu(struct net_device *netdev, int new_mtu)
> > +{
> > + struct i40e_netdev_priv *np = netdev_priv(netdev);
> > + struct i40e_vsi *vsi = np->vsi;
> > + int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
> > +
> > + /* MTU < 68 is an error and causes problems on some kernels */
> > + if ((new_mtu < 68) || (max_frame > I40E_MAX_RXBUFFER))
>
> Maybe add another netdev_info that MTU < 68 is not supported? Not sure
> if it makes sense to set it that low. Never mind if it's just a corner
> case.
Yes, this is a seldom used corner case, and we're using the same check as in our other drivers.
>
> > + return -EINVAL;
> > +
> > + netdev_info(netdev, "%s: changing MTU from %d to %d\n",
> > + __func__, netdev->mtu, new_mtu);
> > + netdev->mtu = new_mtu;
> > + if (netif_running(netdev))
> > + i40e_vsi_reinit_locked(vsi);
> > +
> > + return 0;
> > +}
>
> [...]
>
> > +/**
> > + * i40e_enable_misc_int_causes - enable the non-queue interrupts
> > + * @hw: ptr to the hardware info
> > + **/
> > +static void i40e_enable_misc_int_causes(struct i40e_hw *hw)
> > +{
> > + u32 val;
> > +
> > + /* clear things first */
> > + wr32(hw, I40E_PFINT_ICR0_ENA, 0); /* disable all */
> > + rd32(hw, I40E_PFINT_ICR0); /* read to clear */
> > +
> > + val = I40E_PFINT_ICR0_ENA_ECC_ERR_MASK |
> > + I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK |
> > + I40E_PFINT_ICR0_ENA_GRST_MASK |
> > + I40E_PFINT_ICR0_ENA_PCI_EXCEPTION_MASK |
> > + I40E_PFINT_ICR0_ENA_GPIO_MASK |
> > + I40E_PFINT_ICR0_ENA_STORM_DETECT_MASK |
> > + I40E_PFINT_ICR0_ENA_HMC_ERR_MASK |
> > + I40E_PFINT_ICR0_ENA_VFLR_MASK |
> > + I40E_PFINT_ICR0_ENA_ADMINQ_MASK;
>
> Inconsistent mix of tabs and whitespaces in the lines above. I guess you
> just missed the tab at the end of I40E_PFINT_ICR0_ENA_VFLR_MASK.
We'll tweak that.
>
> > +
> > + wr32(hw, I40E_PFINT_ICR0_ENA, val);
> > +
> > + /* SW_ITR_IDX = 0, but don't change INTENA */
> > + wr32(hw, I40E_PFINT_DYN_CTL0, I40E_PFINT_DYN_CTLN_SW_ITR_INDX_MASK
> |
> > + I40E_PFINT_DYN_CTLN_INTENA_MSK_MASK);
> > +
> > + /* OTHER_ITR_IDX = 0 */
> > + wr32(hw, I40E_PFINT_STAT_CTL0, 0);
> > +}
>
> [...]
>
> > +/**
> > + * i40e_vsi_configure_bw_alloc - Configure VSI BW allocation per TC
> > + * @vsi: the VSI being configured
> > + * @enabled_tc: TC bitmap
> > + * @bw_credits: BW shared credits per TC
> > + *
> > + * Returns 0 on success, negative value on failure
> > + **/
> > +static s32 i40e_vsi_configure_bw_alloc(struct i40e_vsi *vsi,
> > + u8 enabled_tc,
> > + u8 *bw_share)
> > +{
> > + int i, ret = 0;
> > + struct i40e_aqc_configure_vsi_tc_bw_data bw_data;
> > +
> > + bw_data.tc_valid_bits = enabled_tc;
> > + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
> > + bw_data.tc_bw_credits[i] = bw_share[i];
> > +
> > + ret = i40e_aq_config_vsi_tc_bw(&vsi->back->hw, vsi->seid,
> > + &bw_data, NULL);
> > + if (ret != I40E_SUCCESS) {
> > + dev_info(&vsi->back->pdev->dev,
> > + "%s: AQ command Config VSI BW allocation per TC failed
> = %d\n",
> > + __func__, vsi->back->hw.aq.asq_last_status);
> > + return ret;
> > + }
> > +
> > + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
> > + vsi->info.qs_handle[i] = bw_data.qs_handles[i];
> > +
> > + return ret;
> > +
>
> superfluous blank line.
Yep.
>
> > +}
>
> [...]
>
> > +/**
> > + * i40e_do_reset - Start a PF or Core Reset sequence
> > + * @pf: board private structure
> > + * @reset_flags: which reset is requested
> > + *
> > + * The essential difference in resets is that the PF Reset
> > + * doesn't clear the packet buffers, doesn't reset the PE
> > + * firmware, and doesn't bother the other PFs on the chip.
> > + **/
> > +void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags)
> > +{
> > + u32 val;
> > +
> > + WARN_ON(in_interrupt());
> > +
> > + /* do the biggest reset indicated */
> > + if (reset_flags & (1 << __I40E_GLOBAL_RESET_REQUESTED)) {
> > +
> > + /* Request a Global Reset
> > + *
> > + * This will start the chip's countdown to the actual full
> > + * chip reset event, and a warning interrupt to be sent
> > + * to all PFs, including the requestor. Our handler
> > + * for the warning interrupt will deal with the shutdown
> > + * and recovery of the switch setup.
> > + *
> > + * GlobR includes the MAC/PHY in the reset.
> > + */
> > + dev_info(&pf->pdev->dev, "%s: GlobalR requested\n",
> __func__);
> > + val = rd32(&pf->hw, I40E_GLGEN_RTRIG);
> > + val |= I40E_GLGEN_RTRIG_GLOBR_MASK;
> > + wr32(&pf->hw, I40E_GLGEN_RTRIG, val);
> > +
> > + } else if (reset_flags & (1 << __I40E_CORE_RESET_REQUESTED)) {
> > +
> > + /* Request a Core Reset
> > + *
> > + * This will start the chip's countdown to the actual full
> > + * chip reset event, and a warning interrupt to be sent
> > + * to all PFs, including the requestor. Our handler
> > + * for the warning interrupt will deal with the shutdown
> > + * and recovery of the switch setup.
> > + */
>
> Both comments for global and core reset are identical except for one
> more line. Could you change the core reset comment to something like:
> "Same as Global Reset excluding MAC/PHY" ?
Sure.
>
> > + dev_info(&pf->pdev->dev, "%s: CoreR requested\n", __func__);
> > + val = rd32(&pf->hw, I40E_GLGEN_RTRIG);
> > + val |= I40E_GLGEN_RTRIG_CORER_MASK;
> > + wr32(&pf->hw, I40E_GLGEN_RTRIG, val);
> > + flush(&pf->hw);
> > +
> > + } else if (reset_flags & (1 << __I40E_PF_RESET_REQUESTED)) {
> > +
> > + /* Request a PF Reset
> > + *
> > + * This goes directly to the tear-down and rebuild of
> > + * the switch, since we need to do the same recovery as
> > + * for the Core Reset.
> > + */
> > + dev_info(&pf->pdev->dev, "%s: PFR requested\n", __func__);
> > + i40e_handle_reset_warning(pf);
> > +
> > + } else if (reset_flags & (1 << __I40E_REINIT_REQUESTED)) {
> > + int v;
> > +
> > + /* Find the VSI(s) that requested a re-init */
> > + dev_info(&pf->pdev->dev,
> > + "%s: VSI reinit requested\n", __func__);
> > + for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
> > + struct i40e_vsi *vsi = pf->vsi[v];
> > + if (vsi != NULL &&
> > + test_bit(__I40E_REINIT_REQUESTED, &vsi->state)) {
> > + i40e_vsi_reinit_locked(pf->vsi[v]);
> > + clear_bit(__I40E_REINIT_REQUESTED, &vsi->state);
> > + }
> > + }
> > +
> > + /* no further action needed, so return now */
> > + return;
> > + } else {
> > + dev_info(&pf->pdev->dev,
> > + "%s: bad reset request 0x%08x\n",
> > + __func__, reset_flags);
> > + return;
> > + }
> > +
>
> superfluous blank line.
Got it.
>
> > +}
>
> [...]
>
> > +/**
> > + * i40e_link_event - Update netif_carrier status
> > + * @pf: board private structure
> > + **/
> > +static void i40e_link_event(struct i40e_pf *pf)
> > +{
> > + bool new_link, old_link;
> > +
> > + new_link = (pf->hw.phy.link_info.link_info & I40E_AQ_LINK_UP);
> > + old_link = (pf->hw.phy.link_info_old.link_info & I40E_AQ_LINK_UP);
> > +
> > + if (new_link == old_link)
> > + return;
> > +
> > + netdev_info(pf->vsi[pf->lan_vsi]->netdev,
> > + "%s: NIC Link is %s\n",
> > + __func__, (new_link ? "Up" : "Down"));
> > +
> > + /* Notify the base of the switch tree connected to
> > + * the link. Floating VEBs are not notified.
>
> What's a floating VEB?
VEBs usually have an uplink to the physical MAC port, and act as a physical bridge for offloading bridging and filtering duties. A floating VEB is essentially a bridge with no link to the outside, useful for offloading internal (private) bridging between VMs. With the lack of support in the kernel and userspace at this time for this offload concept, this comment may not be useful, and we can make it disappear for now.
>
> > + */
> > + if (pf->lan_veb != I40E_NO_VEB && pf->veb[pf->lan_veb])
> > + i40e_veb_link_event(pf->veb[pf->lan_veb], new_link);
> > + else
> > + i40e_vsi_link_event(pf->vsi[pf->lan_vsi], new_link);
> > +
> > + if (pf->vf)
> > + i40e_vc_notify_link_state(pf);
> > +}
>
> [...]
>
> > +/**
> > + * i40e_watchdog_subtask - Check and bring link up
> > + * @pf: board private structure
> > + **/
> > +static void i40e_watchdog_subtask(struct i40e_pf *pf)
> > +{
> > + int v;
>
> Why did you call the variable v and not i?
No good reason other than it is indexing the vsi array; we can change that name.
>
> > +
> > + /* if interface is down do nothing */
> > + if (test_bit(__I40E_DOWN, &pf->state) ||
> > + test_bit(__I40E_CONFIG_BUSY, &pf->state))
> > + return;
> > +
> > + /* Update the stats for active netdevs so the network stack
> > + * can look at updated numbers whenever it cares to
> > + */
> > + for (v = 0; v < pf->hw.func_caps.num_vsis; v++)
> > + if (pf->vsi[v] && pf->vsi[v]->netdev)
> > + i40e_update_stats(pf->vsi[v]);
> > +
> > + /* Update the stats for the active switching components */
> > + for (v = 0; v < I40E_MAX_VEB; v++)
> > + if (pf->veb[v])
> > + i40e_update_veb_stats(pf->veb[v]);
> > +}
>
> [...]
>
> > +/**
> > + * i40e_handle_reset_warning - prep for the core to reset
> > + * @pf: board private structure
> > + *
> > + * Close up the VFs and other things in prep for a Core Reset,
> > + * then get ready to rebuild the world.
> > + **/
> > +static void i40e_handle_reset_warning(struct i40e_pf *pf)
> > +{
>
> [...]
>
> > + /* reinit the misc interrupt */
> > + if (pf->flags & I40E_FLAG_MSIX_ENABLED)
>
> Did I understand this right, the misc interrupt is now handled by the
> legacy IRQ handler?
If MSIX setup failed, then we'll be using the misc handler for either MSI or Legacy IRQ. This is not intended to be the normal case. Usually the misc handler will be dealing with our 0-th MSIX vector.
>
> [...]
>
> > +/**
> > + * i40e_service_task - Run the driver's async subtasks
> > + * @work: pointer to work_struct containing our data
> > + **/
> > +static void i40e_service_task(struct work_struct *work)
> > +{
> > + struct i40e_pf *pf = container_of(work,
> > + struct i40e_pf,
> > + service_task);
> > + unsigned long start_time = jiffies;
> > +
> > + i40e_reset_subtask(pf);
> > + i40e_handle_mdd_event(pf);
> > + i40e_vc_process_vflr_event(pf);
> > + i40e_watchdog_subtask(pf);
> > + i40e_fdir_reinit_subtask(pf);
> > + i40e_check_hang_subtask(pf);
> > + i40e_sync_filters_subtask(pf);
> > + i40e_clean_adminq_subtask(pf);
> > +
>
> This blank line could probably removed as well or did you add it for a
> special reason?
It was added to separate the subtask operations from the "I'm done" routine.
>
> > + i40e_service_event_complete(pf);
> > +
> > + /* If the tasks have taken longer than one timer cycle or there
> > + * is more work to be done, reschedule the service task now
> > + * rather than wait for the timer to tick again.
> > + */
> > + if (time_after(jiffies, (start_time + pf->service_timer_period)) ||
> > + test_bit(__I40E_ADMINQ_EVENT_PENDING, &pf->state) ||
> > + test_bit(__I40E_MDD_EVENT_PENDING, &pf->state) ||
> > + test_bit(__I40E_VFLR_EVENT_PENDING, &pf->state))
> > + i40e_service_event_schedule(pf);
> > +}
>
> [...]
>
> > + /* prep for VF support */
> > + if ((pf->flags & I40E_FLAG_SRIOV_ENABLED) &&
> > + (pf->flags & I40E_FLAG_MSIX_ENABLED)) {
> > + u32 val;
> > +
> > + /* disable link interrupts for VFs */
> > + val = rd32(hw, I40E_PFGEN_PORTMDIO_NUM);
> > + val &= ~I40E_PFGEN_PORTMDIO_NUM_VFLINK_STAT_ENA_MASK;
> > + wr32(hw, I40E_PFGEN_PORTMDIO_NUM, val);
> > + flush(hw);
> > +
>
> superfluous blank line.
Thanks.
>
> > + }
>
> Stefan
^ permalink raw reply
* Re: [PATCH v4 1/5] net: ethernet: cpsw: switch to devres allocations
From: Daniel Mack @ 2013-08-23 18:34 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: mugunthanvnm, d-gerlach, netdev, nsekhar, vaibhav.bedia,
devicetree, bcousson, ujhelyi.m, linux-omap, davem,
linux-arm-kernel
In-Reply-To: <5217A5AE.6090704@cogentembedded.com>
On 23.08.2013 20:10, Sergei Shtylyov wrote:
> On 08/23/2013 06:16 PM, Daniel Mack wrote:
>> priv->coal_intvl = 0;
>> priv->bus_freq_mhz = clk_get_rate(priv->clk) / 1000000;
>>
>> - priv->cpsw_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> - if (!priv->cpsw_res) {
>> + ss_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!ss_res) {
>
> You don't need to check the result of platform_get_resource() if you call
> devm_ioremap_resource() right afterwards -- it will check resource ptr for
> NULL the first thing. :-)
Thanks! Will address that in v5.
Daniel
^ permalink raw reply
* Re: travelling...
From: Hannes Frederic Sowa @ 2013-08-23 18:29 UTC (permalink / raw)
To: David Miller; +Cc: netdev, linux-wireless, netfilter-devel
In-Reply-To: <20130823.111308.1720597810013400745.davem@davemloft.net>
On Fri, Aug 23, 2013 at 11:13:08AM -0700, David Miller wrote:
>
> I'm travelling over the weekend so I won't be as responsive as
> usual.
>
> I just pushed 'net' to Linus and he pulled it in. I plan to work
> on my next set of -stable submissions this coming Monday.
>
> If a merge of 'net' into 'net-next' would be useful for someone,
> now would be a really good time to tell me.
If it would not take you too much time, it would be nice. I depend on
a change in net to make another small patch. :)
But that is not too important..
Have a nice weekend!
^ permalink raw reply
* Re: [PATCH v4 3/5] net: ethernet: cpsw: introduce ti,am3352-cpsw compatible string
From: Mugunthan V N @ 2013-08-23 18:29 UTC (permalink / raw)
To: Santosh Shilimkar
Cc: devicetree, sergei.shtylyov, d-gerlach, netdev, Sekhar Nori,
vaibhav.bedia, Daniel Mack, bcousson, ujhelyi.m, linux-omap,
davem, linux-arm-kernel
In-Reply-To: <5217A59E.4090700@ti.com>
On Friday 23 August 2013 11:40 PM, Santosh Shilimkar wrote:
> On Friday 23 August 2013 01:39 PM, Sekhar Nori wrote:
>> On 8/23/2013 10:58 PM, Santosh Shilimkar wrote:
>>> On Friday 23 August 2013 01:24 PM, Daniel Mack wrote:
>>>> On 23.08.2013 19:19, Santosh Shilimkar wrote:
>>>>> On Friday 23 August 2013 01:09 PM, Sekhar Nori wrote:
>>>>>> On 8/23/2013 10:26 PM, Santosh Shilimkar wrote:
>>>>>>> So just stick the IP version or call it cpsw-v1... cpsw-v2 etc.
>>>>>> If this could be handled using IP version then the right way would be to
>>>>>> just read the IP version from hardware and use it. No need of DT property.
>>>>>>
>>>>> Thats fine as well but I thought the patch needed additional properties like
>>>>> CM reg-address come from DT and hence the separate compatible. If you can
>>>>> manage without that, thats even better.
>>>> We can't, that's the whole point :)
>>>>
>>> I saw that from the patch :)
>>>
>>>> Well, theoretically, we could for now, but that's not a clean solution.
>>>> Again: the problem here is that the control port is separated from the
>>>> cpsw core, and so we have to implement something specific for the AM3352
>>>> SoC. I know that's a violation of clean and generic driver ideas, but
>>>> there's no way we can assume that every cpsw v2 ip block has a control
>>>> port that is compatible to the one found on am335x chips.
>>>>
>>> But there is a possibility that other SOC will just use the same
>>> control module approach. So using a revision IP is just fine. BTW,
>> But this is misleading because it makes appear like the same compatible
>> can be used on on another SoC like DRA7 which probably has the same
>> version of IP but a different control module implementation, when in
>> practice it cannot.
>>
>> The fact is we are doing something SoC specific in the driver and we
>> cannot hide that behind IP versions. If really in practice there comes
>> another SoC with the same control module definition then it can always
>> use ti,am3352-cpsw compatible as well. The compatible name does not
>> preclude its usage.
>>
> My point was the CPSW needs a feature which is implemented using
> control module rather than within the IP itself. Its an implementation
> detail. As such the additional feature makes sense for that IP. O.w
> there was no need to do any monkeying with control module.
>
> E.g
> MMC card detect is a basic functionality, implemented by various types
> like control module, PMIC or MMC IP itself. As such the driver need
> that support and all the implementation details needs to still handled
> to make that part work.
>
>
CPSW core as such understands only GMII/MII signals, there is an
additional module which converts GMII/MII signals to RGMII/RMII signals
respectively which is called as CPRGMII/CPRMII as specified in the
AM335x TRM in Figure 14-1. Ethernet Switch Integration.
So to control this sub-module, the control register is used and this has
to be configured according to the EVM design like what mode of phy is
connected. CPRGMII and CPRMII is no way related to CPSW core.
Regards
Mugunthan V N
^ permalink raw reply
* Re: sun4i_handle_irq: WARNING at net/ipv4/tcp_input.c:2711 tcp_fastretrans_alert
From: Maxime Ripard @ 2013-08-23 18:21 UTC (permalink / raw)
To: Richard GENOUD; +Cc: Stefan Roese, linux-arm-kernel, linux-kernel, netdev
In-Reply-To: <20130821094432.GA28249@lnx-rg>
[-- Attachment #1: Type: text/plain, Size: 3732 bytes --]
Hi Richard,
On Wed, Aug 21, 2013 at 11:47:20AM +0200, Richard GENOUD wrote:
> Hi Maxime, Stephan
>
> I just realise that, *sometimes*, I have some warnings on my cubieboard
> (6 since the 22 of july, and the board is runnning 24/24).
Wow, I'm impressed it worked this fine actually :)
> It has happened also on 3.10 + emac patches.
> Here it is:
> [27224.060000] ------------[ cut here ]------------
> [27224.070000] WARNING: CPU: 0 PID: 0 at net/ipv4/tcp_input.c:2711 tcp_fastretrans_alert+0x717/0x734()
> [27224.080000] CPU: 0 PID: 0 Comm: swapper Not tainted 3.11.0-rc6-00010-g54f11ec #8
> [27224.080000] [<c000f231>] (unwind_backtrace+0x1/0x98) from [<c000e2b7>] (show_stack+0xb/0xc)
> [27224.090000] [<c000e2b7>] (show_stack+0xb/0xc) from [<c0013cd3>] (warn_slowpath_common+0x43/0x5c)
> [27224.100000] [<c0013cd3>] (warn_slowpath_common+0x43/0x5c) from [<c0013d6d>] (warn_slowpath_null+0x11/0x14)
> [27224.110000] [<c0013d6d>] (warn_slowpath_null+0x11/0x14) from [<c015eadf>] (tcp_fastretrans_alert+0x717/0x734)
> [27224.120000] [<c015eadf>] (tcp_fastretrans_alert+0x717/0x734) from [<c015f32b>] (tcp_ack+0x6af/0x984)
> [27224.130000] [<c015f32b>] (tcp_ack+0x6af/0x984) from [<c015fcab>] (tcp_rcv_established+0x93/0x3ec)
> [27224.140000] [<c015fcab>] (tcp_rcv_established+0x93/0x3ec) from [<c0165621>] (tcp_v4_do_rcv+0xa1/0x160)
> [27224.150000] [<c0165621>] (tcp_v4_do_rcv+0xa1/0x160) from [<c0167129>] (tcp_v4_rcv+0x4e9/0x500)
> [27224.160000] [<c0167129>] (tcp_v4_rcv+0x4e9/0x500) from [<c015025d>] (ip_local_deliver_finish+0x79/0x180)
> [27224.170000] [<c015025d>] (ip_local_deliver_finish+0x79/0x180) from [<c01500a1>] (ip_rcv_finish+0xc9/0x20c)
> [27224.180000] [<c01500a1>] (ip_rcv_finish+0xc9/0x20c) from [<c0129493>] (__netif_receive_skb_core+0x287/0x3dc)
> [27224.190000] [<c0129493>] (__netif_receive_skb_core+0x287/0x3dc) from [<c0129a8b>] (process_backlog+0x5b/0xd0)
> [27224.200000] [<c0129a8b>] (process_backlog+0x5b/0xd0) from [<c012bc6f>] (net_rx_action+0x5f/0xe8)
> [27224.200000] [<c012bc6f>] (net_rx_action+0x5f/0xe8) from [<c0015fc9>] (__do_softirq+0x91/0x134)
> [27224.210000] [<c0015fc9>] (__do_softirq+0x91/0x134) from [<c0016251>] (irq_exit+0x59/0x80)
> [27224.220000] [<c0016251>] (irq_exit+0x59/0x80) from [<c000d00d>] (handle_IRQ+0x21/0x54)
> [27224.230000] [<c000d00d>] (handle_IRQ+0x21/0x54) from [<c00083ef>] (sun4i_handle_irq+0x1f/0x30)
> [27224.240000] [<c00083ef>] (sun4i_handle_irq+0x1f/0x30) from [<c000ea1b>] (__irq_svc+0x3b/0x5c)
> [27224.250000] Exception stack(0xc0287f70 to 0xc0287fb8)
> [27224.250000] 7f60: 00000000 00000000 00000000 00000000
> [27224.260000] 7f80: c0286000 c02ad7fd 00000001 c02ad7fd c028e084 413fc082 00000000 00000000
> [27224.270000] 7fa0: 01000000 c0287fb8 c000d115 c000d116 40000033 ffffffff
> [27224.270000] [<c000ea1b>] (__irq_svc+0x3b/0x5c) from [<c000d116>] (arch_cpu_idle+0x16/0x1c)
> [27224.280000] [<c000d116>] (arch_cpu_idle+0x16/0x1c) from [<c002e69d>] (cpu_startup_entry+0x35/0xa0)
> [27224.290000] [<c002e69d>] (cpu_startup_entry+0x35/0xa0) from [<c026f75f>] (start_kernel+0x1af/0x1f0)
> [27224.300000] ---[ end trace 15641276e08fba8a ]---
Hmmm, that seems pretty far from the network/interrupt drivers, and it
looks like other users have seen this on !ARM machines:
http://forums.gentoo.org/viewtopic-p-7379928.html
https://bugzilla.redhat.com/show_bug.cgi?id=989251
There's a patch in the redhat's bugzilla, could you try to apply it and
see if it solves your problem?
Maybe the netdev guys will have other ideas as well.
Thanks,
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH] netfilter: avoid array overflow in nf_register_hook
From: Sergei Shtylyov @ 2013-08-23 18:14 UTC (permalink / raw)
To: Dong Fang
Cc: pablo, kaber, kadlec, davem, netfilter-devel, netfilter, coreteam,
netdev
In-Reply-To: <1377313651-16096-1-git-send-email-yp.fangdong@gmail.com>
Hello.
On 08/24/2013 07:07 AM, Dong Fang wrote:
> This patch fix the array overflow in nf_register_hook function
> Signed-off-by: Dong Fang <yp.fangdong@gmail.com>
> ---
> net/netfilter/core.c | 5 ++++-
> 1 files changed, 4 insertions(+), 1 deletions(-)
> diff --git a/net/netfilter/core.c b/net/netfilter/core.c
> index 2217363..819eee1 100644
> --- a/net/netfilter/core.c
> +++ b/net/netfilter/core.c
> @@ -68,8 +68,11 @@ static DEFINE_MUTEX(nf_hook_mutex);
> int nf_register_hook(struct nf_hook_ops *reg)
> {
> struct nf_hook_ops *elem;
> - int err;
> + int err = -EINVAL;
>
> + if (reg->pf >= NFPROTO_NUMPROTO || reg->hooknum >= NF_MAX_HOOKS)
> + return err;
Why not just return -EINVAL and avoid unneeded 'err' initialization?
> +
> err = mutex_lock_interruptible(&nf_hook_mutex);
> if (err < 0)
> return err;
WBR, Sergei
^ permalink raw reply
* travelling...
From: David Miller @ 2013-08-23 18:13 UTC (permalink / raw)
To: netdev; +Cc: linux-wireless, netfilter-devel
I'm travelling over the weekend so I won't be as responsive as
usual.
I just pushed 'net' to Linus and he pulled it in. I plan to work
on my next set of -stable submissions this coming Monday.
If a merge of 'net' into 'net-next' would be useful for someone,
now would be a really good time to tell me.
Thanks.
^ permalink raw reply
* Re: [PATCH v4 1/5] net: ethernet: cpsw: switch to devres allocations
From: Sergei Shtylyov @ 2013-08-23 18:10 UTC (permalink / raw)
To: Daniel Mack
Cc: netdev, bcousson, nsekhar, davem, ujhelyi.m, mugunthanvnm,
vaibhav.bedia, d-gerlach, linux-arm-kernel, linux-omap,
devicetree
In-Reply-To: <1377267365-24057-2-git-send-email-zonque@gmail.com>
On 08/23/2013 06:16 PM, Daniel Mack wrote:
> This patch cleans up the allocation and error unwind paths, which
> allows us to carry less information in struct cpsw_priv and reduce the
> amount of jump labels in the probe functions.
> Signed-off-by: Daniel Mack <zonque@gmail.com>
> ---
> drivers/net/ethernet/ti/cpsw.c | 147 +++++++++++++----------------------------
> 1 file changed, 45 insertions(+), 102 deletions(-)
> diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
> index 79974e3..849af52 100644
> --- a/drivers/net/ethernet/ti/cpsw.c
> +++ b/drivers/net/ethernet/ti/cpsw.c
[...]
> @@ -1977,55 +1963,41 @@ static int cpsw_probe(struct platform_device *pdev)
> priv->slaves[0].ndev = ndev;
> priv->emac_port = 0;
>
> - priv->clk = clk_get(&pdev->dev, "fck");
> + priv->clk = devm_clk_get(&pdev->dev, "fck");
> if (IS_ERR(priv->clk)) {
> - dev_err(&pdev->dev, "fck is not found\n");
> + dev_err(priv->dev, "fck is not found\n");
> ret = -ENODEV;
> - goto clean_slave_ret;
> + goto clean_runtime_disable_ret;
> }
> priv->coal_intvl = 0;
> priv->bus_freq_mhz = clk_get_rate(priv->clk) / 1000000;
>
> - priv->cpsw_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> - if (!priv->cpsw_res) {
> + ss_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!ss_res) {
You don't need to check the result of platform_get_resource() if you call
devm_ioremap_resource() right afterwards -- it will check resource ptr for
NULL the first thing. :-)
> dev_err(priv->dev, "error getting i/o resource\n");
> ret = -ENOENT;
> - goto clean_clk_ret;
> + goto clean_runtime_disable_ret;
> }
> - if (!request_mem_region(priv->cpsw_res->start,
> - resource_size(priv->cpsw_res), ndev->name)) {
> - dev_err(priv->dev, "failed request i/o region\n");
> - ret = -ENXIO;
> - goto clean_clk_ret;
> - }
> - ss_regs = ioremap(priv->cpsw_res->start, resource_size(priv->cpsw_res));
> - if (!ss_regs) {
> + ss_regs = devm_ioremap_resource(&pdev->dev, ss_res);
> + if (IS_ERR(ss_regs)) {
> dev_err(priv->dev, "unable to map i/o region\n");
Don't need the error message anymore -- devm_ioremap_resource() will print
it. And you missed:
ret = PTR_ERR(ss_regs);
> - goto clean_cpsw_iores_ret;
> + goto clean_runtime_disable_ret;
> }
> priv->regs = ss_regs;
> priv->version = __raw_readl(&priv->regs->id_ver);
> priv->host_port = HOST_PORT_NUM;
>
> - priv->cpsw_wr_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> - if (!priv->cpsw_wr_res) {
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + if (!res) {
Same comment about resource check here...
> dev_err(priv->dev, "error getting i/o resource\n");
> ret = -ENOENT;
> - goto clean_iomap_ret;
> - }
> - if (!request_mem_region(priv->cpsw_wr_res->start,
> - resource_size(priv->cpsw_wr_res), ndev->name)) {
> - dev_err(priv->dev, "failed request i/o region\n");
> - ret = -ENXIO;
> - goto clean_iomap_ret;
> + goto clean_runtime_disable_ret;
> }
> - wr_regs = ioremap(priv->cpsw_wr_res->start,
> - resource_size(priv->cpsw_wr_res));
> - if (!wr_regs) {
> + priv->wr_regs = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(priv->wr_regs)) {
> dev_err(priv->dev, "unable to map i/o region\n");
And same comments about the unneeded error message and missing 'ret'
assignment here...
> - goto clean_cpsw_wr_iores_ret;
> + goto clean_runtime_disable_ret;
> }
> - priv->wr_regs = wr_regs;
>
> memset(&dma_params, 0, sizeof(dma_params));
> memset(&ale_params, 0, sizeof(ale_params));
[...]
WBR, Sergei
^ permalink raw reply
* Re: [PATCH v4 3/5] net: ethernet: cpsw: introduce ti,am3352-cpsw compatible string
From: Santosh Shilimkar @ 2013-08-23 18:10 UTC (permalink / raw)
To: Sekhar Nori
Cc: mugunthanvnm, sergei.shtylyov, d-gerlach, netdev, vaibhav.bedia,
Daniel Mack, devicetree, bcousson, ujhelyi.m, linux-omap, davem,
linux-arm-kernel
In-Reply-To: <52179E44.5030202@ti.com>
On Friday 23 August 2013 01:39 PM, Sekhar Nori wrote:
> On 8/23/2013 10:58 PM, Santosh Shilimkar wrote:
>> On Friday 23 August 2013 01:24 PM, Daniel Mack wrote:
>>> On 23.08.2013 19:19, Santosh Shilimkar wrote:
>>>> On Friday 23 August 2013 01:09 PM, Sekhar Nori wrote:
>>>>> On 8/23/2013 10:26 PM, Santosh Shilimkar wrote:
>>>
>>>>>> So just stick the IP version or call it cpsw-v1... cpsw-v2 etc.
>>>>>
>>>>> If this could be handled using IP version then the right way would be to
>>>>> just read the IP version from hardware and use it. No need of DT property.
>>>>>
>>>> Thats fine as well but I thought the patch needed additional properties like
>>>> CM reg-address come from DT and hence the separate compatible. If you can
>>>> manage without that, thats even better.
>>>
>>> We can't, that's the whole point :)
>>>
>> I saw that from the patch :)
>>
>>> Well, theoretically, we could for now, but that's not a clean solution.
>>> Again: the problem here is that the control port is separated from the
>>> cpsw core, and so we have to implement something specific for the AM3352
>>> SoC. I know that's a violation of clean and generic driver ideas, but
>>> there's no way we can assume that every cpsw v2 ip block has a control
>>> port that is compatible to the one found on am335x chips.
>>>
>> But there is a possibility that other SOC will just use the same
>> control module approach. So using a revision IP is just fine. BTW,
>
> But this is misleading because it makes appear like the same compatible
> can be used on on another SoC like DRA7 which probably has the same
> version of IP but a different control module implementation, when in
> practice it cannot.
>
> The fact is we are doing something SoC specific in the driver and we
> cannot hide that behind IP versions. If really in practice there comes
> another SoC with the same control module definition then it can always
> use ti,am3352-cpsw compatible as well. The compatible name does not
> preclude its usage.
>
My point was the CPSW needs a feature which is implemented using
control module rather than within the IP itself. Its an implementation
detail. As such the additional feature makes sense for that IP. O.w
there was no need to do any monkeying with control module.
E.g
MMC card detect is a basic functionality, implemented by various types
like control module, PMIC or MMC IP itself. As such the driver need
that support and all the implementation details needs to still handled
to make that part work.
Regards,
Santosh
^ permalink raw reply
* [PATCH net-next 5/5] qlcnic: Update version to 5.3.49.
From: Sucheta Chakraborty @ 2013-08-23 17:38 UTC (permalink / raw)
To: davem; +Cc: netdev, Dept-HSGLinuxNICDev
In-Reply-To: <1377279509-18869-1-git-send-email-sucheta.chakraborty@qlogic.com>
Signed-off-by: Sucheta Chakraborty <sucheta.chakraborty@qlogic.com>
---
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index 3364924..156a78e 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -38,8 +38,8 @@
#define _QLCNIC_LINUX_MAJOR 5
#define _QLCNIC_LINUX_MINOR 3
-#define _QLCNIC_LINUX_SUBVERSION 48
-#define QLCNIC_LINUX_VERSIONID "5.3.48"
+#define _QLCNIC_LINUX_SUBVERSION 49
+#define QLCNIC_LINUX_VERSIONID "5.3.49"
#define QLCNIC_DRV_IDC_VER 0x01
#define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\
(_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION))
--
1.8.1.4
^ permalink raw reply related
* [PATCH net-next 4/5] qlcnic: dcb: Add support for CEE Netlink interface.
From: Sucheta Chakraborty @ 2013-08-23 17:38 UTC (permalink / raw)
To: davem; +Cc: netdev, Dept-HSGLinuxNICDev
In-Reply-To: <1377279509-18869-1-git-send-email-sucheta.chakraborty@qlogic.com>
o Adapter and driver supports only CEE dcbnl ops. Only GET callbacks
within dcbnl ops are supported currently.
Signed-off-by: Sucheta Chakraborty <sucheta.chakraborty@qlogic.com>
---
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 8 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c | 619 ++++++++++++++++++++++-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h | 1 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 2 +
4 files changed, 629 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index 1e868ee..3364924 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -2183,4 +2183,12 @@ static inline void qlcnic_dcb_handle_aen(struct qlcnic_adapter *adapter,
if (dcb && dcb->ops->handle_aen)
dcb->ops->handle_aen(adapter, msg);
}
+
+static inline void qlcnic_dcb_init_dcbnl_ops(struct qlcnic_adapter *adapter)
+{
+ struct qlcnic_dcb *dcb = adapter->dcb;
+
+ if (dcb && dcb->ops->init_dcbnl_ops)
+ dcb->ops->init_dcbnl_ops(adapter);
+}
#endif /* __QLCNIC_H_ */
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
index 3477818..2e10e79 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
@@ -9,11 +9,18 @@
#include "qlcnic.h"
#define QLC_DCB_NUM_PARAM 3
+#define QLC_DCB_LOCAL_IDX 0
+#define QLC_DCB_OPER_IDX 1
+#define QLC_DCB_PEER_IDX 2
+
+#define QLC_DCB_GET_MAP(V) (1 << V)
#define QLC_DCB_AEN_BIT 0x2
#define QLC_DCB_FW_VER 0x2
#define QLC_DCB_MAX_TC 0x8
#define QLC_DCB_MAX_APP 0x8
+#define QLC_DCB_MAX_PRIO QLC_DCB_MAX_TC
+#define QLC_DCB_MAX_PG QLC_DCB_MAX_TC
#define QLC_DCB_TSA_SUPPORT(V) (V & 0x1)
#define QLC_DCB_ETS_SUPPORT(V) ((V >> 1) & 0x1)
@@ -45,8 +52,12 @@
#define QLC_82XX_DCB_GET_PRIOMAP_APP(X) (1 << X)
#define QLC_82XX_DCB_PRIO_TC_MAP (0x76543210)
+static const struct dcbnl_rtnl_ops qlcnic_dcbnl_ops;
+
static void qlcnic_dcb_aen_work(struct work_struct *);
+static void qlcnic_dcb_data_cee_param_map(struct qlcnic_adapter *);
+static inline void __qlcnic_init_dcbnl_ops(struct qlcnic_adapter *);
static void __qlcnic_dcb_free(struct qlcnic_adapter *);
static int __qlcnic_dcb_attach(struct qlcnic_adapter *);
static int __qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *, char *);
@@ -94,12 +105,72 @@ struct qlcnic_82xx_dcb_param_mbx_le {
__le32 app[QLC_DCB_MAX_APP];
};
+enum qlcnic_dcb_selector {
+ QLC_SELECTOR_DEF = 0x0,
+ QLC_SELECTOR_ETHER,
+ QLC_SELECTOR_TCP,
+ QLC_SELECTOR_UDP,
+};
+
+enum qlcnic_dcb_prio_type {
+ QLC_PRIO_NONE = 0,
+ QLC_PRIO_GROUP,
+ QLC_PRIO_LINK,
+};
+
+enum qlcnic_dcb_pfc_type {
+ QLC_PFC_DISABLED = 0,
+ QLC_PFC_FULL,
+ QLC_PFC_TX,
+ QLC_PFC_RX
+};
+
+struct qlcnic_dcb_prio_cfg {
+ bool valid;
+ enum qlcnic_dcb_pfc_type pfc_type;
+};
+
+struct qlcnic_dcb_pg_cfg {
+ bool valid;
+ u8 total_bw_percent; /* of Link/ port BW */
+ u8 prio_count;
+ u8 tsa_type;
+};
+
+struct qlcnic_dcb_tc_cfg {
+ bool valid;
+ struct qlcnic_dcb_prio_cfg prio_cfg[QLC_DCB_MAX_PRIO];
+ enum qlcnic_dcb_prio_type prio_type; /* always prio_link */
+ u8 link_percent; /* % of link bandwidth */
+ u8 bwg_percent; /* % of BWG's bandwidth */
+ u8 up_tc_map;
+ u8 pgid;
+};
+
+struct qlcnic_dcb_app {
+ bool valid;
+ enum qlcnic_dcb_selector selector;
+ u16 protocol;
+ u8 priority;
+};
+
+struct qlcnic_dcb_cee {
+ struct qlcnic_dcb_tc_cfg tc_cfg[QLC_DCB_MAX_TC];
+ struct qlcnic_dcb_pg_cfg pg_cfg[QLC_DCB_MAX_PG];
+ struct qlcnic_dcb_app app[QLC_DCB_MAX_APP];
+ bool tc_param_valid;
+ bool pfc_mode_enable;
+};
+
struct qlcnic_dcb_cfg {
+ /* 0 - local, 1 - operational, 2 - remote */
+ struct qlcnic_dcb_cee type[QLC_DCB_NUM_PARAM];
struct qlcnic_dcb_capability capability;
u32 version;
};
static struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = {
+ .init_dcbnl_ops = __qlcnic_init_dcbnl_ops,
.free = __qlcnic_dcb_free,
.attach = __qlcnic_dcb_attach,
.query_hw_capability = __qlcnic_dcb_query_hw_capability,
@@ -113,6 +184,7 @@ static struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = {
};
static struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = {
+ .init_dcbnl_ops = __qlcnic_init_dcbnl_ops,
.free = __qlcnic_dcb_free,
.attach = __qlcnic_dcb_attach,
.query_hw_capability = __qlcnic_dcb_query_hw_capability,
@@ -132,6 +204,50 @@ static u8 qlcnic_dcb_get_num_app(struct qlcnic_adapter *adapter, u32 val)
return QLC_83XX_DCB_GET_NUMAPP(val);
}
+static inline u8 qlcnic_dcb_pfc_hdr_valid(struct qlcnic_adapter *adapter,
+ u32 val)
+{
+ if (qlcnic_82xx_check(adapter))
+ return QLC_82XX_DCB_PFC_VALID(val);
+ else
+ return QLC_83XX_DCB_PFC_VALID(val);
+}
+
+static inline u8 qlcnic_dcb_tsa_hdr_valid(struct qlcnic_adapter *adapter,
+ u32 val)
+{
+ if (qlcnic_82xx_check(adapter))
+ return QLC_82XX_DCB_TSA_VALID(val);
+ else
+ return QLC_83XX_DCB_TSA_VALID(val);
+}
+
+static inline u8 qlcnic_dcb_get_prio_map_app(struct qlcnic_adapter *adapter,
+ u32 val)
+{
+ if (qlcnic_82xx_check(adapter))
+ return QLC_82XX_DCB_GET_PRIOMAP_APP(val);
+ else
+ return QLC_83XX_DCB_GET_PRIOMAP_APP(val);
+}
+
+static int qlcnic_dcb_prio_count(u8 up_tc_map)
+{
+ int j;
+
+ for (j = 0; j < QLC_DCB_MAX_TC; j++)
+ if (up_tc_map & QLC_DCB_GET_MAP(j))
+ break;
+
+ return j;
+}
+
+static inline void __qlcnic_init_dcbnl_ops(struct qlcnic_adapter *adapter)
+{
+ if (test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+ adapter->netdev->dcbnl_ops = &qlcnic_dcbnl_ops;
+}
+
void qlcnic_set_dcb_ops(struct qlcnic_adapter *adapter)
{
if (qlcnic_82xx_check(adapter))
@@ -403,6 +519,8 @@ static int qlcnic_82xx_dcb_get_cee_cfg(struct qlcnic_adapter *adapter)
mbx->prio_tc_map = QLC_82XX_DCB_PRIO_TC_MAP;
+ qlcnic_dcb_data_cee_param_map(adapter);
+
return err;
}
@@ -517,8 +635,15 @@ out:
static int qlcnic_83xx_dcb_get_cee_cfg(struct qlcnic_adapter *adapter)
{
struct qlcnic_dcb *dcb = adapter->dcb;
+ int err;
- return qlcnic_dcb_query_cee_param(adapter, (char *)dcb->param, 0);
+ err = qlcnic_dcb_query_cee_param(adapter, (char *)dcb->param, 0);
+ if (err)
+ return err;
+
+ qlcnic_dcb_data_cee_param_map(adapter);
+
+ return err;
}
static int qlcnic_83xx_dcb_register_aen(struct qlcnic_adapter *adapter,
@@ -560,3 +685,495 @@ static void qlcnic_83xx_dcb_handle_aen(struct qlcnic_adapter *adapter,
queue_delayed_work(dcb->wq, &dcb->aen_work, 0);
}
+
+static void qlcnic_dcb_fill_cee_tc_params(struct qlcnic_dcb_mbx_params *mbx,
+ struct qlcnic_dcb_param *each,
+ struct qlcnic_dcb_cee *type)
+{
+ struct qlcnic_dcb_tc_cfg *tc_cfg;
+ u8 i, tc, pgid;
+
+ for (i = 0; i < QLC_DCB_MAX_PRIO; i++) {
+ tc = QLC_DCB_GET_TC_PRIO(mbx->prio_tc_map, i);
+ tc_cfg = &type->tc_cfg[tc];
+ tc_cfg->valid = true;
+ tc_cfg->up_tc_map |= QLC_DCB_GET_MAP(i);
+
+ if (QLC_DCB_GET_PFC_PRIO(each->hdr_prio_pfc_map[1], i) &&
+ type->pfc_mode_enable) {
+ tc_cfg->prio_cfg[i].valid = true;
+ tc_cfg->prio_cfg[i].pfc_type = QLC_PFC_FULL;
+ }
+
+ if (i < 4)
+ pgid = QLC_DCB_GET_PGID_PRIO(each->prio_pg_map[0], i);
+ else
+ pgid = QLC_DCB_GET_PGID_PRIO(each->prio_pg_map[1], i);
+
+ tc_cfg->pgid = pgid;
+
+ tc_cfg->prio_type = QLC_PRIO_LINK;
+ type->pg_cfg[tc_cfg->pgid].prio_count++;
+ }
+}
+
+static void qlcnic_dcb_fill_cee_pg_params(struct qlcnic_dcb_param *each,
+ struct qlcnic_dcb_cee *type)
+{
+ struct qlcnic_dcb_pg_cfg *pg_cfg;
+ u8 i, tsa, bw_per;
+
+ for (i = 0; i < QLC_DCB_MAX_PG; i++) {
+ pg_cfg = &type->pg_cfg[i];
+ pg_cfg->valid = true;
+
+ if (i < 4) {
+ bw_per = QLC_DCB_GET_BWPER_PG(each->pg_bw_map[0], i);
+ tsa = QLC_DCB_GET_TSA_PG(each->pg_tsa_map[0], i);
+ } else {
+ bw_per = QLC_DCB_GET_BWPER_PG(each->pg_bw_map[1], i);
+ tsa = QLC_DCB_GET_TSA_PG(each->pg_tsa_map[1], i);
+ }
+
+ pg_cfg->total_bw_percent = bw_per;
+ pg_cfg->tsa_type = tsa;
+ }
+}
+
+static void
+qlcnic_dcb_fill_cee_app_params(struct qlcnic_adapter *adapter, u8 idx,
+ struct qlcnic_dcb_param *each,
+ struct qlcnic_dcb_cee *type)
+{
+ struct qlcnic_dcb_app *app;
+ u8 i, num_app, map, cnt;
+ struct dcb_app new_app;
+
+ num_app = qlcnic_dcb_get_num_app(adapter, each->hdr_prio_pfc_map[0]);
+ for (i = 0; i < num_app; i++) {
+ app = &type->app[i];
+ app->valid = true;
+
+ /* Only for CEE (-1) */
+ app->selector = QLC_DCB_GET_SELECTOR_APP(each->app[i]) - 1;
+ new_app.selector = app->selector;
+ app->protocol = QLC_DCB_GET_PROTO_ID_APP(each->app[i]);
+ new_app.protocol = app->protocol;
+ map = qlcnic_dcb_get_prio_map_app(adapter, each->app[i]);
+ cnt = qlcnic_dcb_prio_count(map);
+
+ if (cnt >= QLC_DCB_MAX_TC)
+ cnt = 0;
+
+ app->priority = cnt;
+ new_app.priority = cnt;
+
+ if (idx == QLC_DCB_OPER_IDX && adapter->netdev->dcbnl_ops)
+ dcb_setapp(adapter->netdev, &new_app);
+ }
+}
+
+static void qlcnic_dcb_map_cee_params(struct qlcnic_adapter *adapter, u8 idx)
+{
+ struct qlcnic_dcb_mbx_params *mbx = adapter->dcb->param;
+ struct qlcnic_dcb_param *each = &mbx->type[idx];
+ struct qlcnic_dcb_cfg *cfg = adapter->dcb->cfg;
+ struct qlcnic_dcb_cee *type = &cfg->type[idx];
+
+ type->tc_param_valid = false;
+ type->pfc_mode_enable = false;
+ memset(type->tc_cfg, 0,
+ sizeof(struct qlcnic_dcb_tc_cfg) * QLC_DCB_MAX_TC);
+ memset(type->pg_cfg, 0,
+ sizeof(struct qlcnic_dcb_pg_cfg) * QLC_DCB_MAX_TC);
+
+ if (qlcnic_dcb_pfc_hdr_valid(adapter, each->hdr_prio_pfc_map[0]) &&
+ cfg->capability.max_pfc_tc)
+ type->pfc_mode_enable = true;
+
+ if (qlcnic_dcb_tsa_hdr_valid(adapter, each->hdr_prio_pfc_map[0]) &&
+ cfg->capability.max_ets_tc)
+ type->tc_param_valid = true;
+
+ qlcnic_dcb_fill_cee_tc_params(mbx, each, type);
+ qlcnic_dcb_fill_cee_pg_params(each, type);
+ qlcnic_dcb_fill_cee_app_params(adapter, idx, each, type);
+}
+
+static void qlcnic_dcb_data_cee_param_map(struct qlcnic_adapter *adapter)
+{
+ int i;
+
+ for (i = 0; i < QLC_DCB_NUM_PARAM; i++)
+ qlcnic_dcb_map_cee_params(adapter, i);
+
+ dcbnl_cee_notify(adapter->netdev, RTM_GETDCB, DCB_CMD_CEE_GET, 0, 0);
+}
+
+static u8 qlcnic_dcb_get_state(struct net_device *netdev)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+
+ return test_bit(__QLCNIC_DCB_STATE, &adapter->state);
+}
+
+static void qlcnic_dcb_get_perm_hw_addr(struct net_device *netdev, u8 *addr)
+{
+ memcpy(addr, netdev->dev_addr, netdev->addr_len);
+}
+
+static void
+qlcnic_dcb_get_pg_tc_cfg_tx(struct net_device *netdev, int tc, u8 *prio,
+ u8 *pgid, u8 *bw_per, u8 *up_tc_map)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct qlcnic_dcb_tc_cfg *tc_cfg, *temp;
+ struct qlcnic_dcb_cee *type;
+ u8 i, cnt, pg;
+
+ type = &adapter->dcb->cfg->type[QLC_DCB_OPER_IDX];
+ *prio = *pgid = *bw_per = *up_tc_map = 0;
+
+ if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state) ||
+ !type->tc_param_valid)
+ return;
+
+ if (tc < 0 || (tc > QLC_DCB_MAX_TC))
+ return;
+
+ tc_cfg = &type->tc_cfg[tc];
+ if (!tc_cfg->valid)
+ return;
+
+ *pgid = tc_cfg->pgid;
+ *prio = tc_cfg->prio_type;
+ *up_tc_map = tc_cfg->up_tc_map;
+ pg = *pgid;
+
+ for (i = 0, cnt = 0; i < QLC_DCB_MAX_TC; i++) {
+ temp = &type->tc_cfg[i];
+ if (temp->valid && (pg == temp->pgid))
+ cnt++;
+ }
+
+ tc_cfg->bwg_percent = (100 / cnt);
+ *bw_per = tc_cfg->bwg_percent;
+}
+
+static void qlcnic_dcb_get_pg_bwg_cfg_tx(struct net_device *netdev, int pgid,
+ u8 *bw_pct)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct qlcnic_dcb_pg_cfg *pgcfg;
+ struct qlcnic_dcb_cee *type;
+
+ *bw_pct = 0;
+ type = &adapter->dcb->cfg->type[QLC_DCB_OPER_IDX];
+
+ if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state) ||
+ !type->tc_param_valid)
+ return;
+
+ if (pgid < 0 || pgid > QLC_DCB_MAX_PG)
+ return;
+
+ pgcfg = &type->pg_cfg[pgid];
+ if (!pgcfg->valid)
+ return;
+
+ *bw_pct = pgcfg->total_bw_percent;
+}
+
+static void qlcnic_dcb_get_pfc_cfg(struct net_device *netdev, int prio,
+ u8 *setting)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct qlcnic_dcb_tc_cfg *tc_cfg;
+ u8 val = QLC_DCB_GET_MAP(prio);
+ struct qlcnic_dcb_cee *type;
+ u8 i;
+
+ *setting = 0;
+ type = &adapter->dcb->cfg->type[QLC_DCB_OPER_IDX];
+
+ if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state) ||
+ !type->pfc_mode_enable)
+ return;
+
+ for (i = 0; i < QLC_DCB_MAX_TC; i++) {
+ tc_cfg = &type->tc_cfg[i];
+ if (!tc_cfg->valid)
+ continue;
+
+ if ((val & tc_cfg->up_tc_map) && (tc_cfg->prio_cfg[prio].valid))
+ *setting = tc_cfg->prio_cfg[prio].pfc_type;
+ }
+}
+
+static u8 qlcnic_dcb_get_capability(struct net_device *netdev, int capid,
+ u8 *cap)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+
+ if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+ return 0;
+
+ switch (capid) {
+ case DCB_CAP_ATTR_PG:
+ case DCB_CAP_ATTR_UP2TC:
+ case DCB_CAP_ATTR_PFC:
+ case DCB_CAP_ATTR_GSP:
+ *cap = true;
+ break;
+ case DCB_CAP_ATTR_PG_TCS:
+ case DCB_CAP_ATTR_PFC_TCS:
+ *cap = 0x80; /* 8 priorities for PGs */
+ break;
+ case DCB_CAP_ATTR_DCBX:
+ *cap = adapter->dcb->cfg->capability.dcb_capability;
+ break;
+ default:
+ *cap = false;
+ }
+
+ return 0;
+}
+
+static int qlcnic_dcb_get_num_tcs(struct net_device *netdev, int attr, u8 *num)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct qlcnic_dcb_cfg *cfg = adapter->dcb->cfg;
+
+ if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+ return -EINVAL;
+
+ switch (attr) {
+ case DCB_NUMTCS_ATTR_PG:
+ *num = cfg->capability.max_ets_tc;
+ return 0;
+ case DCB_NUMTCS_ATTR_PFC:
+ *num = cfg->capability.max_pfc_tc;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static u8 qlcnic_dcb_get_app(struct net_device *netdev, u8 idtype, u16 id)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct dcb_app app = {
+ .selector = idtype,
+ .protocol = id,
+ };
+
+ if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+ return 0;
+
+ return dcb_getapp(netdev, &app);
+}
+
+static u8 qlcnic_dcb_get_pfc_state(struct net_device *netdev)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct qlcnic_dcb *dcb = adapter->dcb;
+
+ if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+ return 0;
+
+ return dcb->cfg->type[QLC_DCB_OPER_IDX].pfc_mode_enable;
+}
+
+static u8 qlcnic_dcb_get_dcbx(struct net_device *netdev)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct qlcnic_dcb_cfg *cfg = adapter->dcb->cfg;
+
+ if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+ return 0;
+
+ return cfg->capability.dcb_capability;
+}
+
+static u8 qlcnic_dcb_get_feat_cfg(struct net_device *netdev, int fid, u8 *flag)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct qlcnic_dcb_cee *type;
+
+ if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+ return 1;
+
+ type = &adapter->dcb->cfg->type[QLC_DCB_OPER_IDX];
+ *flag = 0;
+
+ switch (fid) {
+ case DCB_FEATCFG_ATTR_PG:
+ if (type->tc_param_valid)
+ *flag |= DCB_FEATCFG_ENABLE;
+ else
+ *flag |= DCB_FEATCFG_ERROR;
+ break;
+ case DCB_FEATCFG_ATTR_PFC:
+ if (type->pfc_mode_enable) {
+ if (type->tc_cfg[0].prio_cfg[0].pfc_type)
+ *flag |= DCB_FEATCFG_ENABLE;
+ } else {
+ *flag |= DCB_FEATCFG_ERROR;
+ }
+ break;
+ case DCB_FEATCFG_ATTR_APP:
+ *flag |= DCB_FEATCFG_ENABLE;
+ break;
+ default:
+ netdev_err(netdev, "Invalid Feature ID %d\n", fid);
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline void
+qlcnic_dcb_get_pg_tc_cfg_rx(struct net_device *netdev, int prio, u8 *prio_type,
+ u8 *pgid, u8 *bw_pct, u8 *up_map)
+{
+ *prio_type = *pgid = *bw_pct = *up_map = 0;
+}
+
+static inline void
+qlcnic_dcb_get_pg_bwg_cfg_rx(struct net_device *netdev, int pgid, u8 *bw_pct)
+{
+ *bw_pct = 0;
+}
+
+static int qlcnic_dcb_peer_app_info(struct net_device *netdev,
+ struct dcb_peer_app_info *info,
+ u16 *app_count)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct qlcnic_dcb_cee *peer;
+ int i;
+
+ *app_count = 0;
+
+ if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+ return 0;
+
+ peer = &adapter->dcb->cfg->type[QLC_DCB_PEER_IDX];
+
+ for (i = 0; i < QLC_DCB_MAX_APP; i++) {
+ if (peer->app[i].valid)
+ (*app_count)++;
+ }
+
+ return 0;
+}
+
+static int qlcnic_dcb_peer_app_table(struct net_device *netdev,
+ struct dcb_app *table)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct qlcnic_dcb_cee *peer;
+ struct qlcnic_dcb_app *app;
+ int i, j;
+
+ if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+ return 0;
+
+ peer = &adapter->dcb->cfg->type[QLC_DCB_PEER_IDX];
+
+ for (i = 0, j = 0; i < QLC_DCB_MAX_APP; i++) {
+ app = &peer->app[i];
+ if (!app->valid)
+ continue;
+
+ table[j].selector = app->selector;
+ table[j].priority = app->priority;
+ table[j++].protocol = app->protocol;
+ }
+
+ return 0;
+}
+
+static int qlcnic_dcb_cee_peer_get_pg(struct net_device *netdev,
+ struct cee_pg *pg)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct qlcnic_dcb_cee *peer;
+ u8 i, j, k, map;
+
+ if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+ return 0;
+
+ peer = &adapter->dcb->cfg->type[QLC_DCB_PEER_IDX];
+
+ for (i = 0, j = 0; i < QLC_DCB_MAX_PG; i++) {
+ if (!peer->pg_cfg[i].valid)
+ continue;
+
+ pg->pg_bw[j] = peer->pg_cfg[i].total_bw_percent;
+
+ for (k = 0; k < QLC_DCB_MAX_TC; k++) {
+ if (peer->tc_cfg[i].valid &&
+ (peer->tc_cfg[i].pgid == i)) {
+ map = peer->tc_cfg[i].up_tc_map;
+ pg->prio_pg[j++] = map;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int qlcnic_dcb_cee_peer_get_pfc(struct net_device *netdev,
+ struct cee_pfc *pfc)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct qlcnic_dcb_cfg *cfg = adapter->dcb->cfg;
+ struct qlcnic_dcb_tc_cfg *tc;
+ struct qlcnic_dcb_cee *peer;
+ u8 i, setting, prio;
+
+ pfc->pfc_en = 0;
+
+ if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+ return 0;
+
+ peer = &cfg->type[QLC_DCB_PEER_IDX];
+
+ for (i = 0; i < QLC_DCB_MAX_TC; i++) {
+ tc = &peer->tc_cfg[i];
+ prio = qlcnic_dcb_prio_count(tc->up_tc_map);
+
+ setting = 0;
+ qlcnic_dcb_get_pfc_cfg(netdev, prio, &setting);
+ if (setting)
+ pfc->pfc_en |= QLC_DCB_GET_MAP(i);
+ }
+
+ pfc->tcs_supported = cfg->capability.max_pfc_tc;
+
+ return 0;
+}
+
+static const struct dcbnl_rtnl_ops qlcnic_dcbnl_ops = {
+ .getstate = qlcnic_dcb_get_state,
+ .getpermhwaddr = qlcnic_dcb_get_perm_hw_addr,
+ .getpgtccfgtx = qlcnic_dcb_get_pg_tc_cfg_tx,
+ .getpgbwgcfgtx = qlcnic_dcb_get_pg_bwg_cfg_tx,
+ .getpfccfg = qlcnic_dcb_get_pfc_cfg,
+ .getcap = qlcnic_dcb_get_capability,
+ .getnumtcs = qlcnic_dcb_get_num_tcs,
+ .getapp = qlcnic_dcb_get_app,
+ .getpfcstate = qlcnic_dcb_get_pfc_state,
+ .getdcbx = qlcnic_dcb_get_dcbx,
+ .getfeatcfg = qlcnic_dcb_get_feat_cfg,
+
+ .getpgtccfgrx = qlcnic_dcb_get_pg_tc_cfg_rx,
+ .getpgbwgcfgrx = qlcnic_dcb_get_pg_bwg_cfg_rx,
+
+ .peer_getappinfo = qlcnic_dcb_peer_app_info,
+ .peer_getapptable = qlcnic_dcb_peer_app_table,
+ .cee_peer_getpg = qlcnic_dcb_cee_peer_get_pg,
+ .cee_peer_getpfc = qlcnic_dcb_cee_peer_get_pfc,
+};
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
index 6961dac..b87ce9f 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
@@ -18,6 +18,7 @@ static inline int __qlcnic_register_dcb(struct qlcnic_adapter *adapter)
#endif
struct qlcnic_dcb_ops {
+ void (*init_dcbnl_ops) (struct qlcnic_adapter *);
void (*free) (struct qlcnic_adapter *);
int (*attach) (struct qlcnic_adapter *);
int (*query_hw_capability) (struct qlcnic_adapter *, char *);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 343c6a0..df96f66 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -2028,6 +2028,8 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,
return err;
}
+ qlcnic_dcb_init_dcbnl_ops(adapter);
+
return 0;
}
--
1.8.1.4
^ permalink raw reply related
* [PATCH net-next 2/5] qlcnic: dcb: Get DCB parameters from the adapter.
From: Sucheta Chakraborty @ 2013-08-23 17:38 UTC (permalink / raw)
To: davem; +Cc: netdev, Dept-HSGLinuxNICDev
In-Reply-To: <1377279509-18869-1-git-send-email-sucheta.chakraborty@qlogic.com>
o Populate driver data structures with local, operational, and peer
DCB parameters.
Signed-off-by: Sucheta Chakraborty <sucheta.chakraborty@qlogic.com>
---
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 21 ++
.../net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 1 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c | 1 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c | 248 +++++++++++++++++++++
drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h | 7 +-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h | 1 +
.../net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c | 1 +
7 files changed, 278 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index 2196279..fc2972c 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -2142,4 +2142,25 @@ static inline void qlcnic_dcb_get_info(struct qlcnic_adapter *adapter)
if (dcb && dcb->ops->get_info)
dcb->ops->get_info(adapter);
}
+
+static inline int
+qlcnic_dcb_query_cee_param(struct qlcnic_adapter *adapter, char *buf, u8 type)
+{
+ struct qlcnic_dcb *dcb = adapter->dcb;
+
+ if (dcb && dcb->ops->query_cee_param)
+ return dcb->ops->query_cee_param(adapter, buf, type);
+
+ return 0;
+}
+
+static inline int qlcnic_dcb_get_cee_cfg(struct qlcnic_adapter *adapter)
+{
+ struct qlcnic_dcb *dcb = adapter->dcb;
+
+ if (dcb && dcb->ops->get_cee_cfg)
+ return dcb->ops->get_cee_cfg(adapter);
+
+ return 0;
+}
#endif /* __QLCNIC_H_ */
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
index d6d1b10..9b27ed8 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
@@ -68,6 +68,7 @@ static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = {
{QLCNIC_CMD_CONFIG_VPORT, 4, 4},
{QLCNIC_CMD_BC_EVENT_SETUP, 2, 1},
{QLCNIC_CMD_DCB_QUERY_CAP, 1, 2},
+ {QLCNIC_CMD_DCB_QUERY_PARAM, 2, 50},
};
const u32 qlcnic_83xx_ext_reg_tbl[] = {
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
index 4af3784..bf3b17e 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
@@ -40,6 +40,7 @@ static const struct qlcnic_mailbox_metadata qlcnic_mbx_tbl[] = {
{QLCNIC_CMD_GET_LED_STATUS, 4, 2},
{QLCNIC_CMD_MQ_TX_CONFIG_INTR, 2, 3},
{QLCNIC_CMD_DCB_QUERY_CAP, 1, 2},
+ {QLCNIC_CMD_DCB_QUERY_PARAM, 4, 1},
};
static inline u32 qlcnic_get_cmd_signature(struct qlcnic_hardware_context *ahw)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
index 121e492..e43866f 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
@@ -5,9 +5,14 @@
* See LICENSE.qlcnic for copyright and licensing details.
*/
+#include <linux/types.h>
#include "qlcnic.h"
+#define QLC_DCB_NUM_PARAM 3
+
+#define QLC_DCB_FW_VER 0x2
#define QLC_DCB_MAX_TC 0x8
+#define QLC_DCB_MAX_APP 0x8
#define QLC_DCB_TSA_SUPPORT(V) (V & 0x1)
#define QLC_DCB_ETS_SUPPORT(V) ((V >> 1) & 0x1)
@@ -15,6 +20,29 @@
#define QLC_DCB_MAX_NUM_TC(V) ((V >> 20) & 0xf)
#define QLC_DCB_MAX_NUM_ETS_TC(V) ((V >> 24) & 0xf)
#define QLC_DCB_MAX_NUM_PFC_TC(V) ((V >> 28) & 0xf)
+#define QLC_DCB_GET_TC_PRIO(X, P) ((X >> (P * 3)) & 0x7)
+#define QLC_DCB_GET_PGID_PRIO(X, P) ((X >> (P * 8)) & 0xff)
+#define QLC_DCB_GET_BWPER_PG(X, P) ((X >> (P * 8)) & 0xff)
+#define QLC_DCB_GET_TSA_PG(X, P) ((X >> (P * 8)) & 0xff)
+#define QLC_DCB_GET_PFC_PRIO(X, P) (((X >> 24) >> P) & 0x1)
+#define QLC_DCB_GET_PROTO_ID_APP(X) ((X >> 8) & 0xffff)
+#define QLC_DCB_GET_SELECTOR_APP(X) (X & 0xff)
+
+#define QLC_DCB_LOCAL_PARAM_FWID 0x3
+#define QLC_DCB_OPER_PARAM_FWID 0x1
+#define QLC_DCB_PEER_PARAM_FWID 0x2
+
+#define QLC_83XX_DCB_GET_NUMAPP(X) ((X >> 2) & 0xf)
+#define QLC_83XX_DCB_TSA_VALID(X) (X & 0x1)
+#define QLC_83XX_DCB_PFC_VALID(X) ((X >> 1) & 0x1)
+#define QLC_83XX_DCB_GET_PRIOMAP_APP(X) (X >> 24)
+
+#define QLC_82XX_DCB_GET_NUMAPP(X) ((X >> 12) & 0xf)
+#define QLC_82XX_DCB_TSA_VALID(X) ((X >> 4) & 0x1)
+#define QLC_82XX_DCB_PFC_VALID(X) ((X >> 5) & 0x1)
+#define QLC_82XX_DCB_GET_PRIOVAL_APP(X) ((X >> 24) & 0x7)
+#define QLC_82XX_DCB_GET_PRIOMAP_APP(X) (1 << X)
+#define QLC_82XX_DCB_PRIO_TC_MAP (0x76543210)
static void __qlcnic_dcb_free(struct qlcnic_adapter *);
static int __qlcnic_dcb_attach(struct qlcnic_adapter *);
@@ -22,8 +50,12 @@ static int __qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *, char *);
static void __qlcnic_dcb_get_info(struct qlcnic_adapter *);
static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_adapter *);
+static int qlcnic_82xx_dcb_query_cee_param(struct qlcnic_adapter *, char *, u8);
+static int qlcnic_82xx_dcb_get_cee_cfg(struct qlcnic_adapter *);
static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *);
+static int qlcnic_83xx_dcb_query_cee_param(struct qlcnic_adapter *, char *, u8);
+static int qlcnic_83xx_dcb_get_cee_cfg(struct qlcnic_adapter *);
struct qlcnic_dcb_capability {
bool tsa_capability;
@@ -34,6 +66,28 @@ struct qlcnic_dcb_capability {
u8 dcb_capability;
};
+struct qlcnic_dcb_param {
+ u32 hdr_prio_pfc_map[2];
+ u32 prio_pg_map[2];
+ u32 pg_bw_map[2];
+ u32 pg_tsa_map[2];
+ u32 app[QLC_DCB_MAX_APP];
+};
+
+struct qlcnic_dcb_mbx_params {
+ /* 1st local, 2nd operational 3rd remote */
+ struct qlcnic_dcb_param type[3];
+ u32 prio_tc_map;
+};
+
+struct qlcnic_82xx_dcb_param_mbx_le {
+ __le32 hdr_prio_pfc_map[2];
+ __le32 prio_pg_map[2];
+ __le32 pg_bw_map[2];
+ __le32 pg_tsa_map[2];
+ __le32 app[QLC_DCB_MAX_APP];
+};
+
struct qlcnic_dcb_cfg {
struct qlcnic_dcb_capability capability;
u32 version;
@@ -46,6 +100,8 @@ static struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = {
.get_info = __qlcnic_dcb_get_info,
.get_hw_capability = qlcnic_83xx_dcb_get_hw_capability,
+ .query_cee_param = qlcnic_83xx_dcb_query_cee_param,
+ .get_cee_cfg = qlcnic_83xx_dcb_get_cee_cfg,
};
static struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = {
@@ -55,8 +111,18 @@ static struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = {
.get_info = __qlcnic_dcb_get_info,
.get_hw_capability = qlcnic_82xx_dcb_get_hw_capability,
+ .query_cee_param = qlcnic_82xx_dcb_query_cee_param,
+ .get_cee_cfg = qlcnic_82xx_dcb_get_cee_cfg,
};
+static u8 qlcnic_dcb_get_num_app(struct qlcnic_adapter *adapter, u32 val)
+{
+ if (qlcnic_82xx_check(adapter))
+ return QLC_82XX_DCB_GET_NUMAPP(val);
+ else
+ return QLC_83XX_DCB_GET_NUMAPP(val);
+}
+
void qlcnic_set_dcb_ops(struct qlcnic_adapter *adapter)
{
if (qlcnic_82xx_check(adapter))
@@ -88,6 +154,8 @@ static void __qlcnic_dcb_free(struct qlcnic_adapter *adapter)
kfree(dcb->cfg);
dcb->cfg = NULL;
+ kfree(dcb->param);
+ dcb->param = NULL;
kfree(dcb);
adapter->dcb = NULL;
}
@@ -95,19 +163,32 @@ static void __qlcnic_dcb_free(struct qlcnic_adapter *adapter)
static void __qlcnic_dcb_get_info(struct qlcnic_adapter *adapter)
{
qlcnic_dcb_get_hw_capability(adapter);
+ qlcnic_dcb_get_cee_cfg(adapter);
}
static int __qlcnic_dcb_attach(struct qlcnic_adapter *adapter)
{
struct qlcnic_dcb *dcb = adapter->dcb;
+ int err = 0;
dcb->cfg = kzalloc(sizeof(struct qlcnic_dcb_cfg), GFP_ATOMIC);
if (!dcb->cfg)
return -ENOMEM;
+ dcb->param = kzalloc(sizeof(struct qlcnic_dcb_mbx_params), GFP_ATOMIC);
+ if (!dcb->param) {
+ err = -ENOMEM;
+ goto out_free_cfg;
+ }
+
qlcnic_dcb_get_info(adapter);
return 0;
+out_free_cfg:
+ kfree(dcb->cfg);
+ dcb->cfg = NULL;
+
+ return err;
}
static int __qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *adapter,
@@ -189,6 +270,104 @@ static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter)
return err;
}
+static int qlcnic_82xx_dcb_query_cee_param(struct qlcnic_adapter *adapter,
+ char *buf, u8 type)
+{
+ u16 size = sizeof(struct qlcnic_82xx_dcb_param_mbx_le);
+ struct qlcnic_82xx_dcb_param_mbx_le *prsp_le;
+ struct device *dev = &adapter->pdev->dev;
+ dma_addr_t cardrsp_phys_addr;
+ struct qlcnic_dcb_param rsp;
+ struct qlcnic_cmd_args cmd;
+ u64 phys_addr;
+ void *addr;
+ int err, i;
+
+ switch (type) {
+ case QLC_DCB_LOCAL_PARAM_FWID:
+ case QLC_DCB_OPER_PARAM_FWID:
+ case QLC_DCB_PEER_PARAM_FWID:
+ break;
+ default:
+ dev_err(dev, "Invalid parameter type %d\n", type);
+ return -EINVAL;
+ }
+
+ addr = dma_alloc_coherent(&adapter->pdev->dev, size, &cardrsp_phys_addr,
+ GFP_KERNEL);
+ if (addr == NULL)
+ return -ENOMEM;
+
+ prsp_le = addr;
+
+ err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DCB_QUERY_PARAM);
+ if (err)
+ goto out_free_rsp;
+
+ phys_addr = cardrsp_phys_addr;
+ cmd.req.arg[1] = size | (type << 16);
+ cmd.req.arg[2] = MSD(phys_addr);
+ cmd.req.arg[3] = LSD(phys_addr);
+
+ err = qlcnic_issue_cmd(adapter, &cmd);
+ if (err) {
+ dev_err(dev, "Failed to query DCBX parameter, err %d\n", err);
+ goto out;
+ }
+
+ memset(&rsp, 0, sizeof(struct qlcnic_dcb_param));
+ rsp.hdr_prio_pfc_map[0] = le32_to_cpu(prsp_le->hdr_prio_pfc_map[0]);
+ rsp.hdr_prio_pfc_map[1] = le32_to_cpu(prsp_le->hdr_prio_pfc_map[1]);
+ rsp.prio_pg_map[0] = le32_to_cpu(prsp_le->prio_pg_map[0]);
+ rsp.prio_pg_map[1] = le32_to_cpu(prsp_le->prio_pg_map[1]);
+ rsp.pg_bw_map[0] = le32_to_cpu(prsp_le->pg_bw_map[0]);
+ rsp.pg_bw_map[1] = le32_to_cpu(prsp_le->pg_bw_map[1]);
+ rsp.pg_tsa_map[0] = le32_to_cpu(prsp_le->pg_tsa_map[0]);
+ rsp.pg_tsa_map[1] = le32_to_cpu(prsp_le->pg_tsa_map[1]);
+
+ for (i = 0; i < QLC_DCB_MAX_APP; i++)
+ rsp.app[i] = le32_to_cpu(prsp_le->app[i]);
+
+ if (buf)
+ memcpy(buf, &rsp, size);
+out:
+ qlcnic_free_mbx_args(&cmd);
+
+out_free_rsp:
+ dma_free_coherent(&adapter->pdev->dev, size, addr, cardrsp_phys_addr);
+
+ return err;
+}
+
+static int qlcnic_82xx_dcb_get_cee_cfg(struct qlcnic_adapter *adapter)
+{
+ struct qlcnic_dcb_mbx_params *mbx;
+ int err;
+
+ mbx = adapter->dcb->param;
+ if (!mbx)
+ return 0;
+
+ err = qlcnic_dcb_query_cee_param(adapter, (char *)&mbx->type[0],
+ QLC_DCB_LOCAL_PARAM_FWID);
+ if (err)
+ return err;
+
+ err = qlcnic_dcb_query_cee_param(adapter, (char *)&mbx->type[1],
+ QLC_DCB_OPER_PARAM_FWID);
+ if (err)
+ return err;
+
+ err = qlcnic_dcb_query_cee_param(adapter, (char *)&mbx->type[2],
+ QLC_DCB_PEER_PARAM_FWID);
+ if (err)
+ return err;
+
+ mbx->prio_tc_map = QLC_82XX_DCB_PRIO_TC_MAP;
+
+ return err;
+}
+
static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter)
{
struct qlcnic_dcb_capability *cap = &adapter->dcb->cfg->capability;
@@ -211,3 +390,72 @@ static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter)
return err;
}
+
+static int qlcnic_83xx_dcb_query_cee_param(struct qlcnic_adapter *adapter,
+ char *buf, u8 idx)
+{
+ struct qlcnic_dcb_mbx_params mbx_out;
+ int err, i, j, k, max_app, size;
+ struct qlcnic_dcb_param *each;
+ struct qlcnic_cmd_args cmd;
+ u32 val;
+ char *p;
+
+ size = 0;
+ memset(&mbx_out, 0, sizeof(struct qlcnic_dcb_mbx_params));
+ memset(buf, 0, sizeof(struct qlcnic_dcb_mbx_params));
+
+ err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DCB_QUERY_PARAM);
+ if (err)
+ return err;
+
+ cmd.req.arg[0] |= QLC_DCB_FW_VER << 29;
+ err = qlcnic_issue_cmd(adapter, &cmd);
+ if (err) {
+ dev_err(&adapter->pdev->dev,
+ "Failed to query DCBX param, err %d\n", err);
+ goto out;
+ }
+
+ mbx_out.prio_tc_map = cmd.rsp.arg[1];
+ p = memcpy(buf, &mbx_out, sizeof(u32));
+ k = 2;
+ p += sizeof(u32);
+
+ for (j = 0; j < QLC_DCB_NUM_PARAM; j++) {
+ each = &mbx_out.type[j];
+
+ each->hdr_prio_pfc_map[0] = cmd.rsp.arg[k++];
+ each->hdr_prio_pfc_map[1] = cmd.rsp.arg[k++];
+ each->prio_pg_map[0] = cmd.rsp.arg[k++];
+ each->prio_pg_map[1] = cmd.rsp.arg[k++];
+ each->pg_bw_map[0] = cmd.rsp.arg[k++];
+ each->pg_bw_map[1] = cmd.rsp.arg[k++];
+ each->pg_tsa_map[0] = cmd.rsp.arg[k++];
+ each->pg_tsa_map[1] = cmd.rsp.arg[k++];
+ val = each->hdr_prio_pfc_map[0];
+
+ max_app = qlcnic_dcb_get_num_app(adapter, val);
+ for (i = 0; i < max_app; i++)
+ each->app[i] = cmd.rsp.arg[i + k];
+
+ size = 16 * sizeof(u32);
+ memcpy(p, &each->hdr_prio_pfc_map[0], size);
+ p += size;
+ if (j == 0)
+ k = 18;
+ else
+ k = 34;
+ }
+out:
+ qlcnic_free_mbx_args(&cmd);
+
+ return err;
+}
+
+static int qlcnic_83xx_dcb_get_cee_cfg(struct qlcnic_adapter *adapter)
+{
+ struct qlcnic_dcb *dcb = adapter->dcb;
+
+ return qlcnic_dcb_query_cee_param(adapter, (char *)dcb->param, 0);
+}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
index 45dc1fa..d1775d7 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
@@ -23,10 +23,13 @@ struct qlcnic_dcb_ops {
int (*query_hw_capability) (struct qlcnic_adapter *, char *);
int (*get_hw_capability) (struct qlcnic_adapter *);
void (*get_info) (struct qlcnic_adapter *);
+ int (*query_cee_param) (struct qlcnic_adapter *, char *, u8);
+ int (*get_cee_cfg) (struct qlcnic_adapter *);
};
struct qlcnic_dcb {
- struct qlcnic_dcb_ops *ops;
- struct qlcnic_dcb_cfg *cfg;
+ struct qlcnic_dcb_mbx_params *param;
+ struct qlcnic_dcb_ops *ops;
+ struct qlcnic_dcb_cfg *cfg;
};
#endif
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
index 243018b..d2276b8 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
@@ -86,6 +86,7 @@ enum qlcnic_regs {
#define QLCNIC_CMD_BC_EVENT_SETUP 0x31
#define QLCNIC_CMD_CONFIG_VPORT 0x32
#define QLCNIC_CMD_DCB_QUERY_CAP 0x34
+#define QLCNIC_CMD_DCB_QUERY_PARAM 0x35
#define QLCNIC_CMD_GET_MAC_STATS 0x37
#define QLCNIC_CMD_82XX_SET_DRV_VER 0x38
#define QLCNIC_CMD_MQ_TX_CONFIG_INTR 0x39
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
index b154048..3c0e02a 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
@@ -1285,6 +1285,7 @@ static const int qlcnic_pf_passthru_supp_cmds[] = {
QLCNIC_CMD_GET_PORT_CONFIG,
QLCNIC_CMD_GET_LINK_STATUS,
QLCNIC_CMD_DCB_QUERY_CAP,
+ QLCNIC_CMD_DCB_QUERY_PARAM,
};
static const struct qlcnic_sriov_cmd_handler qlcnic_pf_bc_cmd_hdlr[] = {
--
1.8.1.4
^ permalink raw reply related
* [PATCH net-next 1/5] qlcnic: dcb: Query adapter DCB capabilities.
From: Sucheta Chakraborty @ 2013-08-23 17:38 UTC (permalink / raw)
To: davem; +Cc: netdev, Dept-HSGLinuxNICDev
In-Reply-To: <1377279509-18869-1-git-send-email-sucheta.chakraborty@qlogic.com>
o Query adapter DCB capabilities and populate local data structures
with relevant information.
o Add QLCNIC_DCB to Kconfig for enabling/disabling DCB.
Signed-off-by: Sucheta Chakraborty <sucheta.chakraborty@qlogic.com>
---
drivers/net/ethernet/qlogic/Kconfig | 11 ++
drivers/net/ethernet/qlogic/qlcnic/Makefile | 2 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 50 +++++
.../net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 1 +
.../net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c | 5 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c | 1 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c | 213 +++++++++++++++++++++
drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h | 32 ++++
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h | 1 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 22 +++
.../ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | 6 +
.../net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c | 1 +
12 files changed, 345 insertions(+)
create mode 100644 drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
create mode 100644 drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
diff --git a/drivers/net/ethernet/qlogic/Kconfig b/drivers/net/ethernet/qlogic/Kconfig
index 0e17972..f59e6be 100644
--- a/drivers/net/ethernet/qlogic/Kconfig
+++ b/drivers/net/ethernet/qlogic/Kconfig
@@ -45,6 +45,17 @@ config QLCNIC_SRIOV
This allows for virtual function acceleration in virtualized
environments.
+config QLCNIC_DCB
+ bool "QLOGIC QLCNIC 82XX and 83XX family DCB Support"
+ depends on QLCNIC && DCB
+ default y
+ ---help---
+ This configuration parameter enables DCB support in QLE83XX
+ and QLE82XX Converged Ethernet devices. This allows for DCB
+ get operations support through rtNetlink interface. Only CEE
+ mode of DCB is supported. PG and PFC values are related only
+ to Tx.
+
config QLGE
tristate "QLogic QLGE 10Gb Ethernet Driver Support"
depends on PCI
diff --git a/drivers/net/ethernet/qlogic/qlcnic/Makefile b/drivers/net/ethernet/qlogic/qlcnic/Makefile
index 4b1fb3f..a848d29 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/Makefile
+++ b/drivers/net/ethernet/qlogic/qlcnic/Makefile
@@ -11,3 +11,5 @@ qlcnic-y := qlcnic_hw.o qlcnic_main.o qlcnic_init.o \
qlcnic_minidump.o qlcnic_sriov_common.o
qlcnic-$(CONFIG_QLCNIC_SRIOV) += qlcnic_sriov_pf.o
+
+qlcnic-$(CONFIG_QLCNIC_DCB) += qlcnic_dcb.o
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index 3f03856..2196279 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -34,6 +34,7 @@
#include "qlcnic_hdr.h"
#include "qlcnic_hw.h"
#include "qlcnic_83xx_hw.h"
+#include "qlcnic_dcb.h"
#define _QLCNIC_LINUX_MAJOR 5
#define _QLCNIC_LINUX_MINOR 3
@@ -959,6 +960,7 @@ struct qlcnic_ipaddr {
#define __QLCNIC_SRIOV_CAPABLE 11
#define __QLCNIC_MBX_POLL_ENABLE 12
#define __QLCNIC_DIAG_MODE 13
+#define __QLCNIC_DCB_STATE 14
#define QLCNIC_INTERRUPT_TEST 1
#define QLCNIC_LOOPBACK_TEST 2
@@ -1062,6 +1064,7 @@ struct qlcnic_adapter {
struct delayed_work fw_work;
struct delayed_work idc_aen_work;
struct delayed_work mbx_poll_work;
+ struct qlcnic_dcb *dcb;
struct qlcnic_filter_hash fhash;
struct qlcnic_filter_hash rx_fhash;
@@ -2092,4 +2095,51 @@ static inline bool qlcnic_sriov_vf_check(struct qlcnic_adapter *adapter)
return status;
}
+
+static inline int qlcnic_dcb_get_hw_capability(struct qlcnic_adapter *adapter)
+{
+ struct qlcnic_dcb *dcb = adapter->dcb;
+
+ if (dcb && dcb->ops->get_hw_capability)
+ return dcb->ops->get_hw_capability(adapter);
+
+ return 0;
+}
+
+static inline void qlcnic_dcb_free(struct qlcnic_adapter *adapter)
+{
+ struct qlcnic_dcb *dcb = adapter->dcb;
+
+ if (dcb && dcb->ops->free)
+ dcb->ops->free(adapter);
+}
+
+static inline int qlcnic_dcb_attach(struct qlcnic_adapter *adapter)
+{
+ struct qlcnic_dcb *dcb = adapter->dcb;
+
+ if (dcb && dcb->ops->attach)
+ return dcb->ops->attach(adapter);
+
+ return 0;
+}
+
+static inline int
+qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *adapter, char *buf)
+{
+ struct qlcnic_dcb *dcb = adapter->dcb;
+
+ if (dcb && dcb->ops->query_hw_capability)
+ return dcb->ops->query_hw_capability(adapter, buf);
+
+ return 0;
+}
+
+static inline void qlcnic_dcb_get_info(struct qlcnic_adapter *adapter)
+{
+ struct qlcnic_dcb *dcb = adapter->dcb;
+
+ if (dcb && dcb->ops->get_info)
+ dcb->ops->get_info(adapter);
+}
#endif /* __QLCNIC_H_ */
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
index 6c059f9..d6d1b10 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
@@ -67,6 +67,7 @@ static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = {
{QLCNIC_CMD_ADD_RCV_RINGS, 130, 26},
{QLCNIC_CMD_CONFIG_VPORT, 4, 4},
{QLCNIC_CMD_BC_EVENT_SETUP, 2, 1},
+ {QLCNIC_CMD_DCB_QUERY_CAP, 1, 2},
};
const u32 qlcnic_83xx_ext_reg_tbl[] = {
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
index fb0ef36..a969ac2 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
@@ -635,6 +635,8 @@ int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter)
if (adapter->portnum == 0)
qlcnic_set_drv_version(adapter);
+
+ qlcnic_dcb_get_info(adapter);
qlcnic_83xx_idc_attach_driver(adapter);
return 0;
@@ -2228,6 +2230,9 @@ int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac)
if (err)
goto disable_mbx_intr;
+ if (adapter->dcb && qlcnic_dcb_attach(adapter))
+ qlcnic_clear_dcb_ops(adapter);
+
/* Periodically monitor device status */
qlcnic_83xx_idc_poll_dev_state(&adapter->fw_work.work);
return 0;
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
index d4f0e95..4af3784 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
@@ -39,6 +39,7 @@ static const struct qlcnic_mailbox_metadata qlcnic_mbx_tbl[] = {
{QLCNIC_CMD_82XX_SET_DRV_VER, 4, 1},
{QLCNIC_CMD_GET_LED_STATUS, 4, 2},
{QLCNIC_CMD_MQ_TX_CONFIG_INTR, 2, 3},
+ {QLCNIC_CMD_DCB_QUERY_CAP, 1, 2},
};
static inline u32 qlcnic_get_cmd_signature(struct qlcnic_hardware_context *ahw)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
new file mode 100644
index 0000000..121e492
--- /dev/null
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
@@ -0,0 +1,213 @@
+/*
+ * QLogic qlcnic NIC Driver
+ * Copyright (c) 2009-2013 QLogic Corporation
+ *
+ * See LICENSE.qlcnic for copyright and licensing details.
+ */
+
+#include "qlcnic.h"
+
+#define QLC_DCB_MAX_TC 0x8
+
+#define QLC_DCB_TSA_SUPPORT(V) (V & 0x1)
+#define QLC_DCB_ETS_SUPPORT(V) ((V >> 1) & 0x1)
+#define QLC_DCB_VERSION_SUPPORT(V) ((V >> 2) & 0xf)
+#define QLC_DCB_MAX_NUM_TC(V) ((V >> 20) & 0xf)
+#define QLC_DCB_MAX_NUM_ETS_TC(V) ((V >> 24) & 0xf)
+#define QLC_DCB_MAX_NUM_PFC_TC(V) ((V >> 28) & 0xf)
+
+static void __qlcnic_dcb_free(struct qlcnic_adapter *);
+static int __qlcnic_dcb_attach(struct qlcnic_adapter *);
+static int __qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *, char *);
+static void __qlcnic_dcb_get_info(struct qlcnic_adapter *);
+
+static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_adapter *);
+
+static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *);
+
+struct qlcnic_dcb_capability {
+ bool tsa_capability;
+ bool ets_capability;
+ u8 max_num_tc;
+ u8 max_ets_tc;
+ u8 max_pfc_tc;
+ u8 dcb_capability;
+};
+
+struct qlcnic_dcb_cfg {
+ struct qlcnic_dcb_capability capability;
+ u32 version;
+};
+
+static struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = {
+ .free = __qlcnic_dcb_free,
+ .attach = __qlcnic_dcb_attach,
+ .query_hw_capability = __qlcnic_dcb_query_hw_capability,
+ .get_info = __qlcnic_dcb_get_info,
+
+ .get_hw_capability = qlcnic_83xx_dcb_get_hw_capability,
+};
+
+static struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = {
+ .free = __qlcnic_dcb_free,
+ .attach = __qlcnic_dcb_attach,
+ .query_hw_capability = __qlcnic_dcb_query_hw_capability,
+ .get_info = __qlcnic_dcb_get_info,
+
+ .get_hw_capability = qlcnic_82xx_dcb_get_hw_capability,
+};
+
+void qlcnic_set_dcb_ops(struct qlcnic_adapter *adapter)
+{
+ if (qlcnic_82xx_check(adapter))
+ adapter->dcb->ops = &qlcnic_82xx_dcb_ops;
+ else if (qlcnic_83xx_check(adapter))
+ adapter->dcb->ops = &qlcnic_83xx_dcb_ops;
+}
+
+int __qlcnic_register_dcb(struct qlcnic_adapter *adapter)
+{
+ struct qlcnic_dcb *dcb;
+
+ dcb = kzalloc(sizeof(struct qlcnic_dcb), GFP_ATOMIC);
+ if (!dcb)
+ return -ENOMEM;
+
+ adapter->dcb = dcb;
+ qlcnic_set_dcb_ops(adapter);
+
+ return 0;
+}
+
+static void __qlcnic_dcb_free(struct qlcnic_adapter *adapter)
+{
+ struct qlcnic_dcb *dcb = adapter->dcb;
+
+ if (!dcb)
+ return;
+
+ kfree(dcb->cfg);
+ dcb->cfg = NULL;
+ kfree(dcb);
+ adapter->dcb = NULL;
+}
+
+static void __qlcnic_dcb_get_info(struct qlcnic_adapter *adapter)
+{
+ qlcnic_dcb_get_hw_capability(adapter);
+}
+
+static int __qlcnic_dcb_attach(struct qlcnic_adapter *adapter)
+{
+ struct qlcnic_dcb *dcb = adapter->dcb;
+
+ dcb->cfg = kzalloc(sizeof(struct qlcnic_dcb_cfg), GFP_ATOMIC);
+ if (!dcb->cfg)
+ return -ENOMEM;
+
+ qlcnic_dcb_get_info(adapter);
+
+ return 0;
+}
+
+static int __qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *adapter,
+ char *buf)
+{
+ struct qlcnic_cmd_args cmd;
+ u32 mbx_out;
+ int err;
+
+ err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DCB_QUERY_CAP);
+ if (err)
+ return err;
+
+ err = qlcnic_issue_cmd(adapter, &cmd);
+ if (err) {
+ dev_err(&adapter->pdev->dev,
+ "Failed to query DCBX capability, err %d\n", err);
+ } else {
+ mbx_out = cmd.rsp.arg[1];
+ if (buf)
+ memcpy(buf, &mbx_out, sizeof(u32));
+ }
+
+ qlcnic_free_mbx_args(&cmd);
+
+ return err;
+}
+
+static int __qlcnic_dcb_get_capability(struct qlcnic_adapter *adapter, u32 *val)
+{
+ struct qlcnic_dcb_capability *cap = &adapter->dcb->cfg->capability;
+ u32 mbx_out;
+ int err;
+
+ memset(cap, 0, sizeof(struct qlcnic_dcb_capability));
+
+ err = qlcnic_dcb_query_hw_capability(adapter, (char *)val);
+ if (err)
+ return err;
+
+ mbx_out = *val;
+ if (QLC_DCB_TSA_SUPPORT(mbx_out))
+ cap->tsa_capability = true;
+
+ if (QLC_DCB_ETS_SUPPORT(mbx_out))
+ cap->ets_capability = true;
+
+ cap->max_num_tc = QLC_DCB_MAX_NUM_TC(mbx_out);
+ cap->max_ets_tc = QLC_DCB_MAX_NUM_ETS_TC(mbx_out);
+ cap->max_pfc_tc = QLC_DCB_MAX_NUM_PFC_TC(mbx_out);
+
+ if (cap->max_num_tc > QLC_DCB_MAX_TC ||
+ cap->max_ets_tc > cap->max_num_tc ||
+ cap->max_pfc_tc > cap->max_num_tc) {
+ dev_err(&adapter->pdev->dev, "Invalid DCB configuration\n");
+ return -EINVAL;
+ }
+
+ return err;
+}
+
+static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter)
+{
+ struct qlcnic_dcb_cfg *cfg = adapter->dcb->cfg;
+ struct qlcnic_dcb_capability *cap;
+ u32 mbx_out;
+ int err;
+
+ err = __qlcnic_dcb_get_capability(adapter, &mbx_out);
+ if (err)
+ return err;
+
+ cap = &cfg->capability;
+ cap->dcb_capability = DCB_CAP_DCBX_VER_CEE | DCB_CAP_DCBX_LLD_MANAGED;
+
+ if (cap->dcb_capability && cap->tsa_capability && cap->ets_capability)
+ set_bit(__QLCNIC_DCB_STATE, &adapter->state);
+
+ return err;
+}
+
+static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter)
+{
+ struct qlcnic_dcb_capability *cap = &adapter->dcb->cfg->capability;
+ u32 mbx_out;
+ int err;
+
+ err = __qlcnic_dcb_get_capability(adapter, &mbx_out);
+ if (err)
+ return err;
+
+ if (mbx_out & BIT_2)
+ cap->dcb_capability = DCB_CAP_DCBX_VER_CEE;
+ if (mbx_out & BIT_3)
+ cap->dcb_capability |= DCB_CAP_DCBX_VER_IEEE;
+ if (cap->dcb_capability)
+ cap->dcb_capability |= DCB_CAP_DCBX_LLD_MANAGED;
+
+ if (cap->dcb_capability && cap->tsa_capability && cap->ets_capability)
+ set_bit(__QLCNIC_DCB_STATE, &adapter->state);
+
+ return err;
+}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
new file mode 100644
index 0000000..45dc1fa
--- /dev/null
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
@@ -0,0 +1,32 @@
+/*
+ * QLogic qlcnic NIC Driver
+ * Copyright (c) 2009-2013 QLogic Corporation
+ *
+ * See LICENSE.qlcnic for copyright and licensing details.
+ */
+
+#ifndef __QLCNIC_DCBX_H
+#define __QLCNIC_DCBX_H
+
+void qlcnic_clear_dcb_ops(struct qlcnic_adapter *);
+
+#ifdef CONFIG_QLCNIC_DCB
+int __qlcnic_register_dcb(struct qlcnic_adapter *);
+#else
+static inline int __qlcnic_register_dcb(struct qlcnic_adapter *adapter)
+{ return 0; }
+#endif
+
+struct qlcnic_dcb_ops {
+ void (*free) (struct qlcnic_adapter *);
+ int (*attach) (struct qlcnic_adapter *);
+ int (*query_hw_capability) (struct qlcnic_adapter *, char *);
+ int (*get_hw_capability) (struct qlcnic_adapter *);
+ void (*get_info) (struct qlcnic_adapter *);
+};
+
+struct qlcnic_dcb {
+ struct qlcnic_dcb_ops *ops;
+ struct qlcnic_dcb_cfg *cfg;
+};
+#endif
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
index 786366c..243018b 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
@@ -85,6 +85,7 @@ enum qlcnic_regs {
#define QLCNIC_CMD_GET_TEMP_HDR 0x30
#define QLCNIC_CMD_BC_EVENT_SETUP 0x31
#define QLCNIC_CMD_CONFIG_VPORT 0x32
+#define QLCNIC_CMD_DCB_QUERY_CAP 0x34
#define QLCNIC_CMD_GET_MAC_STATS 0x37
#define QLCNIC_CMD_82XX_SET_DRV_VER 0x38
#define QLCNIC_CMD_MQ_TX_CONFIG_INTR 0x39
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 8321d1a..343c6a0 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -2121,6 +2121,17 @@ void qlcnic_set_drv_version(struct qlcnic_adapter *adapter)
qlcnic_fw_cmd_set_drv_version(adapter, fw_cmd);
}
+static int qlcnic_register_dcb(struct qlcnic_adapter *adapter)
+{
+ return __qlcnic_register_dcb(adapter);
+}
+
+void qlcnic_clear_dcb_ops(struct qlcnic_adapter *adapter)
+{
+ kfree(adapter->dcb);
+ adapter->dcb = NULL;
+}
+
static int
qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
@@ -2217,6 +2228,8 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
INIT_LIST_HEAD(&adapter->mac_list);
+ qlcnic_register_dcb(adapter);
+
if (qlcnic_82xx_check(adapter)) {
qlcnic_check_vf(adapter, ent);
adapter->portnum = adapter->ahw->pci_func;
@@ -2245,6 +2258,10 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_out_free_hw;
adapter->flags |= QLCNIC_NEED_FLR;
+
+ if (adapter->dcb && qlcnic_dcb_attach(adapter))
+ qlcnic_clear_dcb_ops(adapter);
+
} else if (qlcnic_83xx_check(adapter)) {
adapter->max_drv_tx_rings = 1;
qlcnic_83xx_check_vf(adapter, ent);
@@ -2369,6 +2386,8 @@ static void qlcnic_remove(struct pci_dev *pdev)
qlcnic_cancel_idc_work(adapter);
ahw = adapter->ahw;
+ qlcnic_dcb_free(adapter);
+
unregister_netdev(netdev);
qlcnic_sriov_cleanup(adapter);
@@ -2411,6 +2430,7 @@ static void qlcnic_remove(struct pci_dev *pdev)
destroy_workqueue(adapter->qlcnic_wq);
adapter->qlcnic_wq = NULL;
}
+
qlcnic_free_adapter_resources(adapter);
kfree(ahw);
free_netdev(netdev);
@@ -3228,6 +3248,8 @@ qlcnic_attach_work(struct work_struct *work)
return;
}
attach:
+ qlcnic_dcb_get_info(adapter);
+
if (netif_running(netdev)) {
if (qlcnic_up(adapter, netdev))
goto done;
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
index 2f79ec5..26f9aa6 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
@@ -538,6 +538,9 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter,
if (err)
goto err_out_send_channel_term;
+ if (adapter->dcb && qlcnic_dcb_attach(adapter))
+ qlcnic_clear_dcb_ops(adapter);
+
err = qlcnic_setup_netdev(adapter, adapter->netdev, pci_using_dac);
if (err)
goto err_out_send_channel_term;
@@ -545,6 +548,7 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter,
pci_set_drvdata(adapter->pdev, adapter);
dev_info(&adapter->pdev->dev, "%s: XGbE port initialized\n",
adapter->netdev->name);
+
qlcnic_schedule_work(adapter, qlcnic_sriov_vf_poll_dev_state,
adapter->ahw->idc.delay);
return 0;
@@ -1577,6 +1581,8 @@ static int qlcnic_sriov_vf_reinit_driver(struct qlcnic_adapter *adapter)
if (err)
goto err_out_term_channel;
+ qlcnic_dcb_get_info(adapter);
+
return 0;
err_out_term_channel:
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
index eb49cd6..b154048 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
@@ -1284,6 +1284,7 @@ static const int qlcnic_pf_passthru_supp_cmds[] = {
QLCNIC_CMD_GET_STATISTICS,
QLCNIC_CMD_GET_PORT_CONFIG,
QLCNIC_CMD_GET_LINK_STATUS,
+ QLCNIC_CMD_DCB_QUERY_CAP,
};
static const struct qlcnic_sriov_cmd_handler qlcnic_pf_bc_cmd_hdlr[] = {
--
1.8.1.4
^ permalink raw reply related
* [PATCH net-next 0/5] qlcnic: DCB support
From: Sucheta Chakraborty @ 2013-08-23 17:38 UTC (permalink / raw)
To: davem; +Cc: netdev, Dept-HSGLinuxNICDev
This patch series will enable Data Center Bridging (DCB) feature in
qlcnic driver. Currently the support is limited to CEE version of DCBX.
Only GET operations are supported through CEE rtnetlink interface.
Please apply to net-next.
Thanks,
Sucheta.
Sucheta Chakraborty (5):
qlcnic: dcb: Query adapter DCB capabilities.
qlcnic: dcb: Get DCB parameters from the adapter.
qlcnic: dcb: Register DCB AEN handler.
qlcnic: dcb: Add support for CEE Netlink interface.
qlcnic: Update version to 5.3.49.
drivers/net/ethernet/qlogic/Kconfig | 11 +
drivers/net/ethernet/qlogic/qlcnic/Makefile | 2 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 103 +-
.../net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 5 +
.../net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c | 5 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c | 2 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c | 1179 ++++++++++++++++++++
drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h | 41 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h | 3 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c | 3 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 24 +
.../ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | 6 +
.../net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c | 4 +
13 files changed, 1386 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
create mode 100644 drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
^ permalink raw reply
* [PATCH net-next 3/5] qlcnic: dcb: Register DCB AEN handler.
From: Sucheta Chakraborty @ 2013-08-23 17:38 UTC (permalink / raw)
To: davem; +Cc: netdev, Dept-HSGLinuxNICDev
In-Reply-To: <1377279509-18869-1-git-send-email-sucheta.chakraborty@qlogic.com>
o Adapter sends Asynchronous Event Notifications to the driver when
there are changes in the switch or adapter DCBX configuration.
AEN handler updates the driver DCBX parameters.
Signed-off-by: Sucheta Chakraborty <sucheta.chakraborty@qlogic.com>
---
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 20 ++++
.../net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 3 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c | 105 ++++++++++++++++++++-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h | 5 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h | 1 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c | 3 +
.../net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c | 2 +
7 files changed, 137 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index fc2972c..1e868ee 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -816,6 +816,7 @@ struct qlcnic_mac_list_s {
#define QLCNIC_C2H_OPCODE_CONFIG_LOOPBACK 0x8f
#define QLCNIC_C2H_OPCODE_GET_LINKEVENT_RESPONSE 0x8D
+#define QLCNIC_C2H_OPCODE_GET_DCB_AEN 0x90
#define VPORT_MISS_MODE_DROP 0 /* drop all unmatched */
#define VPORT_MISS_MODE_ACCEPT_ALL 1 /* accept all packets */
@@ -961,6 +962,7 @@ struct qlcnic_ipaddr {
#define __QLCNIC_MBX_POLL_ENABLE 12
#define __QLCNIC_DIAG_MODE 13
#define __QLCNIC_DCB_STATE 14
+#define __QLCNIC_DCB_IN_AEN 15
#define QLCNIC_INTERRUPT_TEST 1
#define QLCNIC_LOOPBACK_TEST 2
@@ -2163,4 +2165,22 @@ static inline int qlcnic_dcb_get_cee_cfg(struct qlcnic_adapter *adapter)
return 0;
}
+
+static inline void
+qlcnic_dcb_register_aen(struct qlcnic_adapter *adapter, u8 flag)
+{
+ struct qlcnic_dcb *dcb = adapter->dcb;
+
+ if (dcb && dcb->ops->register_aen)
+ dcb->ops->register_aen(adapter, flag);
+}
+
+static inline void qlcnic_dcb_handle_aen(struct qlcnic_adapter *adapter,
+ void *msg)
+{
+ struct qlcnic_dcb *dcb = adapter->dcb;
+
+ if (dcb && dcb->ops->handle_aen)
+ dcb->ops->handle_aen(adapter, msg);
+}
#endif /* __QLCNIC_H_ */
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
index 9b27ed8..8fce1d3 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
@@ -897,6 +897,9 @@ void __qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter)
dev_info(&adapter->pdev->dev, "SFP Removed AEN:0x%x.\n",
QLCNIC_MBX_RSP(event[0]));
break;
+ case QLCNIC_MBX_DCBX_CONFIG_CHANGE_EVENT:
+ qlcnic_dcb_handle_aen(adapter, (void *)&event[1]);
+ break;
default:
dev_dbg(&adapter->pdev->dev, "Unsupported AEN:0x%x.\n",
QLCNIC_MBX_RSP(event[0]));
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
index e43866f..3477818 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
@@ -10,6 +10,7 @@
#define QLC_DCB_NUM_PARAM 3
+#define QLC_DCB_AEN_BIT 0x2
#define QLC_DCB_FW_VER 0x2
#define QLC_DCB_MAX_TC 0x8
#define QLC_DCB_MAX_APP 0x8
@@ -44,6 +45,8 @@
#define QLC_82XX_DCB_GET_PRIOMAP_APP(X) (1 << X)
#define QLC_82XX_DCB_PRIO_TC_MAP (0x76543210)
+static void qlcnic_dcb_aen_work(struct work_struct *);
+
static void __qlcnic_dcb_free(struct qlcnic_adapter *);
static int __qlcnic_dcb_attach(struct qlcnic_adapter *);
static int __qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *, char *);
@@ -52,10 +55,13 @@ static void __qlcnic_dcb_get_info(struct qlcnic_adapter *);
static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_adapter *);
static int qlcnic_82xx_dcb_query_cee_param(struct qlcnic_adapter *, char *, u8);
static int qlcnic_82xx_dcb_get_cee_cfg(struct qlcnic_adapter *);
+static void qlcnic_82xx_dcb_handle_aen(struct qlcnic_adapter *, void *);
static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *);
static int qlcnic_83xx_dcb_query_cee_param(struct qlcnic_adapter *, char *, u8);
static int qlcnic_83xx_dcb_get_cee_cfg(struct qlcnic_adapter *);
+static int qlcnic_83xx_dcb_register_aen(struct qlcnic_adapter *, bool);
+static void qlcnic_83xx_dcb_handle_aen(struct qlcnic_adapter *, void *);
struct qlcnic_dcb_capability {
bool tsa_capability;
@@ -102,6 +108,8 @@ static struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = {
.get_hw_capability = qlcnic_83xx_dcb_get_hw_capability,
.query_cee_param = qlcnic_83xx_dcb_query_cee_param,
.get_cee_cfg = qlcnic_83xx_dcb_get_cee_cfg,
+ .register_aen = qlcnic_83xx_dcb_register_aen,
+ .handle_aen = qlcnic_83xx_dcb_handle_aen,
};
static struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = {
@@ -113,6 +121,7 @@ static struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = {
.get_hw_capability = qlcnic_82xx_dcb_get_hw_capability,
.query_cee_param = qlcnic_82xx_dcb_query_cee_param,
.get_cee_cfg = qlcnic_82xx_dcb_get_cee_cfg,
+ .handle_aen = qlcnic_82xx_dcb_handle_aen,
};
static u8 qlcnic_dcb_get_num_app(struct qlcnic_adapter *adapter, u32 val)
@@ -140,6 +149,7 @@ int __qlcnic_register_dcb(struct qlcnic_adapter *adapter)
return -ENOMEM;
adapter->dcb = dcb;
+ dcb->adapter = adapter;
qlcnic_set_dcb_ops(adapter);
return 0;
@@ -152,6 +162,18 @@ static void __qlcnic_dcb_free(struct qlcnic_adapter *adapter)
if (!dcb)
return;
+ qlcnic_dcb_register_aen(adapter, 0);
+
+ while (test_bit(__QLCNIC_DCB_IN_AEN, &adapter->state))
+ usleep_range(10000, 11000);
+
+ cancel_delayed_work_sync(&dcb->aen_work);
+
+ if (dcb->wq) {
+ destroy_workqueue(dcb->wq);
+ dcb->wq = NULL;
+ }
+
kfree(dcb->cfg);
dcb->cfg = NULL;
kfree(dcb->param);
@@ -164,6 +186,7 @@ static void __qlcnic_dcb_get_info(struct qlcnic_adapter *adapter)
{
qlcnic_dcb_get_hw_capability(adapter);
qlcnic_dcb_get_cee_cfg(adapter);
+ qlcnic_dcb_register_aen(adapter, 1);
}
static int __qlcnic_dcb_attach(struct qlcnic_adapter *adapter)
@@ -171,9 +194,20 @@ static int __qlcnic_dcb_attach(struct qlcnic_adapter *adapter)
struct qlcnic_dcb *dcb = adapter->dcb;
int err = 0;
+ INIT_DELAYED_WORK(&dcb->aen_work, qlcnic_dcb_aen_work);
+
+ dcb->wq = create_singlethread_workqueue("qlcnic-dcb");
+ if (!dcb->wq) {
+ dev_err(&adapter->pdev->dev,
+ "DCB workqueue allocation failed. DCB will be disabled\n");
+ return -1;
+ }
+
dcb->cfg = kzalloc(sizeof(struct qlcnic_dcb_cfg), GFP_ATOMIC);
- if (!dcb->cfg)
- return -ENOMEM;
+ if (!dcb->cfg) {
+ err = -ENOMEM;
+ goto out_free_wq;
+ }
dcb->param = kzalloc(sizeof(struct qlcnic_dcb_mbx_params), GFP_ATOMIC);
if (!dcb->param) {
@@ -188,6 +222,10 @@ out_free_cfg:
kfree(dcb->cfg);
dcb->cfg = NULL;
+out_free_wq:
+ destroy_workqueue(dcb->wq);
+ dcb->wq = NULL;
+
return err;
}
@@ -368,6 +406,29 @@ static int qlcnic_82xx_dcb_get_cee_cfg(struct qlcnic_adapter *adapter)
return err;
}
+static void qlcnic_dcb_aen_work(struct work_struct *work)
+{
+ struct qlcnic_adapter *adapter;
+ struct qlcnic_dcb *dcb;
+
+ dcb = container_of(work, struct qlcnic_dcb, aen_work.work);
+ adapter = dcb->adapter;
+
+ qlcnic_dcb_get_cee_cfg(adapter);
+ clear_bit(__QLCNIC_DCB_IN_AEN, &adapter->state);
+}
+
+static void qlcnic_82xx_dcb_handle_aen(struct qlcnic_adapter *adapter,
+ void *data)
+{
+ struct qlcnic_dcb *dcb = adapter->dcb;
+
+ if (test_and_set_bit(__QLCNIC_DCB_IN_AEN, &adapter->state))
+ return;
+
+ queue_delayed_work(dcb->wq, &dcb->aen_work, 0);
+}
+
static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter)
{
struct qlcnic_dcb_capability *cap = &adapter->dcb->cfg->capability;
@@ -459,3 +520,43 @@ static int qlcnic_83xx_dcb_get_cee_cfg(struct qlcnic_adapter *adapter)
return qlcnic_dcb_query_cee_param(adapter, (char *)dcb->param, 0);
}
+
+static int qlcnic_83xx_dcb_register_aen(struct qlcnic_adapter *adapter,
+ bool flag)
+{
+ u8 val = (flag ? QLCNIC_CMD_INIT_NIC_FUNC : QLCNIC_CMD_STOP_NIC_FUNC);
+ struct qlcnic_cmd_args cmd;
+ int err;
+
+ err = qlcnic_alloc_mbx_args(&cmd, adapter, val);
+ if (err)
+ return err;
+
+ cmd.req.arg[1] = QLC_DCB_AEN_BIT;
+
+ err = qlcnic_issue_cmd(adapter, &cmd);
+ if (err)
+ dev_err(&adapter->pdev->dev, "Failed to %s DCBX AEN, err %d\n",
+ (flag ? "register" : "unregister"), err);
+
+ qlcnic_free_mbx_args(&cmd);
+
+ return err;
+}
+
+static void qlcnic_83xx_dcb_handle_aen(struct qlcnic_adapter *adapter,
+ void *data)
+{
+ struct qlcnic_dcb *dcb = adapter->dcb;
+ u32 *val = data;
+
+ if (test_and_set_bit(__QLCNIC_DCB_IN_AEN, &adapter->state))
+ return;
+
+ if (*val & BIT_8)
+ set_bit(__QLCNIC_DCB_STATE, &adapter->state);
+ else
+ clear_bit(__QLCNIC_DCB_STATE, &adapter->state);
+
+ queue_delayed_work(dcb->wq, &dcb->aen_work, 0);
+}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
index d1775d7..6961dac 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
@@ -25,10 +25,15 @@ struct qlcnic_dcb_ops {
void (*get_info) (struct qlcnic_adapter *);
int (*query_cee_param) (struct qlcnic_adapter *, char *, u8);
int (*get_cee_cfg) (struct qlcnic_adapter *);
+ int (*register_aen) (struct qlcnic_adapter *, bool);
+ void (*handle_aen) (struct qlcnic_adapter *, void *);
};
struct qlcnic_dcb {
struct qlcnic_dcb_mbx_params *param;
+ struct qlcnic_adapter *adapter;
+ struct delayed_work aen_work;
+ struct workqueue_struct *wq;
struct qlcnic_dcb_ops *ops;
struct qlcnic_dcb_cfg *cfg;
};
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
index d2276b8..272c356 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
@@ -125,6 +125,7 @@ enum qlcnic_regs {
#define QLCNIC_MBX_COMP_EVENT 0x8100
#define QLCNIC_MBX_REQUEST_EVENT 0x8101
#define QLCNIC_MBX_TIME_EXTEND_EVENT 0x8102
+#define QLCNIC_MBX_DCBX_CONFIG_CHANGE_EVENT 0x8110
#define QLCNIC_MBX_SFP_INSERT_EVENT 0x8130
#define QLCNIC_MBX_SFP_REMOVE_EVENT 0x8131
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
index 89f6dff..8d06f88 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
@@ -1010,6 +1010,9 @@ static void qlcnic_handle_fw_message(int desc_cnt, int index,
break;
}
break;
+ case QLCNIC_C2H_OPCODE_GET_DCB_AEN:
+ qlcnic_dcb_handle_aen(adapter, (void *)&msg);
+ break;
default:
break;
}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
index 3c0e02a..2d6faf0 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
@@ -1286,6 +1286,8 @@ static const int qlcnic_pf_passthru_supp_cmds[] = {
QLCNIC_CMD_GET_LINK_STATUS,
QLCNIC_CMD_DCB_QUERY_CAP,
QLCNIC_CMD_DCB_QUERY_PARAM,
+ QLCNIC_CMD_INIT_NIC_FUNC,
+ QLCNIC_CMD_STOP_NIC_FUNC,
};
static const struct qlcnic_sriov_cmd_handler qlcnic_pf_bc_cmd_hdlr[] = {
--
1.8.1.4
^ permalink raw reply related
* Re: [net-next v2 2/8] i40e: transmit, receive, and napi
From: David Miller @ 2013-08-23 18:04 UTC (permalink / raw)
To: sassmann
Cc: jeffrey.t.kirsher, jesse.brandeburg, netdev, gospo,
shannon.nelson, peter.p.waskiewicz.jr, e1000-devel
In-Reply-To: <5217589F.6000602@kpanic.de>
From: Stefan Assmann <sassmann@kpanic.de>
Date: Fri, 23 Aug 2013 14:42:07 +0200
> On 23.08.2013 04:15, Jeff Kirsher wrote:
>> From: Jesse Brandeburg <jesse.brandeburg@intel.com>
>>
>> This patch contains the transmit, receive, and napi routines, as well
>> as ancillary routines.
>>
>> This file is code that is (will be) shared between the VF and PF
>> drivers.
>
> Just some small nitpicks.
>
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
>> new file mode 100644
>> index 0000000..ceafef0
>> --- /dev/null
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
>
> [...]
>
>> +static void i40e_receive_skb(struct i40e_ring *rx_ring,
>> + struct sk_buff *skb, u16 vlan_tag)
>> +{
>> + struct i40e_vsi *vsi = rx_ring->vsi;
>> + struct i40e_q_vector *q_vector = rx_ring->q_vector;
>> + u64 flags = vsi->back->flags;
>> +
>> + if (vlan_tag & VLAN_VID_MASK)
>> + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
>
> Suggesting __constant_htons instead of htons here.
We don't suggest that anymore, because it's completely unnecessary
with the way the macros are implemented.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox