提交 c9214f50 编写于 作者: D David S. Miller

Merge branch 'mvneta-hwbm'

Gregory CLEMENT says:

====================
API set for HW Buffer management

This is the sixth version of the API set for HW Buffer management (that was
initially submitted here:
http://thread.gmane.org/gmane.linux.kernel/2125152).

This version is just a rebasing onto the last net-next. I also added
the Tested-by flag from Sebastian Careba : "The patch set applies
successfully and it works well, no more Samba issues any longer".

For the record in the previous versions I made the following changes:
v4 -> v5:
- Add a field with the size of the buffer of the pool was added. It
  then allow to fix some misused size in the mvneta_bm code when using
  the new framework.

- Add a new patch from Marcin for sram allowing to require
  non-bufferable access to the memory. It was needed for the hardware
  buffer management of the mvneta.

- Fix the build issue notified by the 0-day builder when building the
  drivers as module.

v3 -> v4
- Fix build issue when HWBM is not selected

v2 -> v3
- Make a HWBM and a SWBM version of the mvneta_rx() function in order
  to reduce the the conditional code. Kept a condition inside the
  mvneta_poll because specializing this function would have means
  duplicating 95% of the code.

- Put back the register_netdev() call at the end of the mvneta_probe()
  function. In order to have a unique ID for each port, just used a
  global variable in the driver.

- Added a fix from Marcin in the "net: mvneta: bm: add support for
  hardware buffer management" patch: "when dropping packets, only
  buffer pointers passed from BM to descriptors have to be returned to
  the pool. In submitted version after closing the port and
  mvneta_rxq_deinit(), it was very likely that a lot of fake buffers
  are added to the pool, because all descriptors took part in
  iteration."

- Removed the select MVNETA_BM from the Kconfig, it will let the user
  the choice to use not use it if they want.

v1 -> v2
- The hardware buffer management helpers are no more built by default
  and now depend on a hidden config symbol which has to be selected
  by the driver if needed
- The hwbm_pool_refill() and hwbm_pool_add() now receive a gfp_t as
  argument allowing the caller to specify the flag it needs.
- buf_num is now tested to ensure there is no wrapping
- A spinlock has been added to protect the hwbm_pool_add() function in
  SMP or irq context.
- used pr_warn instead of pr_debug in case of errors.
- fixed the mvneta implementation by returning the buffer to the pool
  at various place instead of ignoring it.
- Squashed "bus: mvenus-mbus: Fix size test for
   mvebu_mbus_get_dram_win_info" into bus: mvebu-mbus: provide api for
   obtaining IO and DRAM window information.
- Added my signed-otf-by on all the patches as submitter of the series.
- Renamed the dts patches with the pattern "ARM: dts: platform:"
- Removed the patch "ARM: mvebu: enable SRAM support in
  mvebu_v7_defconfig" of this series and already applied it
- Modified the order of the patches.

In order to ease the test the branch mvneta-BM-framework-v6 is
available at git@github.com:MISL-EBU-System-SW/mainline-public.git.
====================
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
......@@ -18,15 +18,30 @@ Optional properties:
"core" for core clock and "bus" for the optional bus clock.
Optional properties (valid only for Armada XP/38x):
- buffer-manager: a phandle to a buffer manager node. Please refer to
Documentation/devicetree/bindings/net/marvell-neta-bm.txt
- bm,pool-long: ID of a pool, that will accept all packets of a size
higher than 'short' pool's threshold (if set) and up to MTU value.
Obligatory, when the port is supposed to use hardware
buffer management.
- bm,pool-short: ID of a pool, that will be used for accepting
packets of a size lower than given threshold. If not set, the port
will use a single 'long' pool for all packets, as defined above.
Example:
ethernet@d0070000 {
ethernet@70000 {
compatible = "marvell,armada-370-neta";
reg = <0xd0070000 0x2500>;
reg = <0x70000 0x2500>;
interrupts = <8>;
clocks = <&gate_clk 4>;
tx-csum-limit = <9800>
status = "okay";
phy = <&phy0>;
phy-mode = "rgmii-id";
buffer-manager = <&bm>;
bm,pool-long = <0>;
bm,pool-short = <1>;
};
* Marvell Armada 380/XP Buffer Manager driver (BM)
Required properties:
- compatible: should be "marvell,armada-380-neta-bm".
- reg: address and length of the register set for the device.
- clocks: a pointer to the reference clock for this device.
- internal-mem: a phandle to BM internal SRAM definition.
Optional properties (port):
- pool<0 : 3>,capacity: size of external buffer pointers' ring maintained
in DRAM. Can be set for each pool (id 0 : 3) separately. The value has
to be chosen between 128 and 16352 and it also has to be aligned to 32.
Otherwise the driver would adjust a given number or choose default if
not set.
- pool<0 : 3>,pkt-size: maximum size of a packet accepted by a given buffer
pointers' pool (id 0 : 3). It will be taken into consideration only when pool
type is 'short'. For 'long' ones it would be overridden by port's MTU.
If not set a driver will choose a default value.
In order to see how to hook the BM to a given ethernet port, please
refer to Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt.
Example:
- main node:
bm: bm@c8000 {
compatible = "marvell,armada-380-neta-bm";
reg = <0xc8000 0xac>;
clocks = <&gateclk 13>;
internal-mem = <&bm_bppi>;
status = "okay";
pool2,capacity = <4096>;
pool1,pkt-size = <512>;
};
- internal SRAM node:
bm_bppi: bm-bppi {
compatible = "mmio-sram";
reg = <MBUS_ID(0x0c, 0x04) 0 0x100000>;
ranges = <0 MBUS_ID(0x0c, 0x04) 0 0x100000>;
#address-cells = <1>;
#size-cells = <1>;
clocks = <&gateclk 13>;
status = "okay";
};
......@@ -25,6 +25,11 @@ Required properties in the sram node:
- ranges : standard definition, should translate from local addresses
within the sram to bus addresses
Optional properties in the sram node:
- no-memory-wc : the flag indicating, that SRAM memory region has not to
be remapped as write combining. WC is used by default.
Required properties in the area nodes:
- reg : iomem address range, relative to the SRAM range
......
......@@ -61,7 +61,8 @@
ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000
MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000
MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000>;
MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000
MBUS_ID(0x0c, 0x04) 0 0xf1200000 0x100000>;
internal-regs {
spi1: spi@10680 {
......@@ -138,12 +139,18 @@
status = "okay";
phy = <&phy2>;
phy-mode = "sgmii";
buffer-manager = <&bm>;
bm,pool-long = <1>;
bm,pool-short = <3>;
};
ethernet@34000 {
status = "okay";
phy = <&phy1>;
phy-mode = "sgmii";
buffer-manager = <&bm>;
bm,pool-long = <2>;
bm,pool-short = <3>;
};
ethernet@70000 {
......@@ -157,6 +164,13 @@
status = "okay";
phy = <&phy0>;
phy-mode = "rgmii-id";
buffer-manager = <&bm>;
bm,pool-long = <0>;
bm,pool-short = <3>;
};
bm@c8000 {
status = "okay";
};
nfc: flash@d0000 {
......@@ -178,6 +192,10 @@
};
};
bm-bppi {
status = "okay";
};
pcie-controller {
status = "okay";
......
......@@ -78,6 +78,9 @@
internal-regs {
ethernet@30000 {
phy-mode = "sgmii";
buffer-manager = <&bm>;
bm,pool-long = <2>;
bm,pool-short = <1>;
status = "okay";
fixed-link {
......@@ -88,6 +91,9 @@
ethernet@34000 {
phy-mode = "sgmii";
buffer-manager = <&bm>;
bm,pool-long = <3>;
bm,pool-short = <1>;
status = "okay";
fixed-link {
......
......@@ -66,7 +66,8 @@
ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000
MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000
MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000>;
MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000
MBUS_ID(0x0c, 0x04) 0 0xf1200000 0x100000>;
internal-regs {
spi@10600 {
......@@ -99,6 +100,9 @@
status = "okay";
phy = <&phy1>;
phy-mode = "rgmii-id";
buffer-manager = <&bm>;
bm,pool-long = <2>;
bm,pool-short = <3>;
};
usb@58000 {
......@@ -109,6 +113,9 @@
status = "okay";
phy = <&phy0>;
phy-mode = "rgmii-id";
buffer-manager = <&bm>;
bm,pool-long = <0>;
bm,pool-short = <1>;
};
mdio@72004 {
......@@ -129,6 +136,10 @@
status = "okay";
};
bm@c8000 {
status = "okay";
};
flash@d0000 {
status = "okay";
num-cs = <1>;
......@@ -169,6 +180,10 @@
};
};
bm-bppi {
status = "okay";
};
pcie-controller {
status = "okay";
/*
......
......@@ -60,7 +60,8 @@
ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000
MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000
MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000>;
MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000
MBUS_ID(0x0c, 0x04) 0 0xf1200000 0x100000>;
internal-regs {
spi@10600 {
......@@ -133,6 +134,9 @@
status = "okay";
phy = <&phy1>;
phy-mode = "rgmii-id";
buffer-manager = <&bm>;
bm,pool-long = <2>;
bm,pool-short = <3>;
};
/* CON4 */
......@@ -152,6 +156,9 @@
status = "okay";
phy = <&phy0>;
phy-mode = "rgmii-id";
buffer-manager = <&bm>;
bm,pool-long = <0>;
bm,pool-short = <1>;
};
......@@ -186,6 +193,10 @@
};
};
bm@c8000 {
status = "okay";
};
sata@e0000 {
pinctrl-names = "default";
pinctrl-0 = <&sata2_pins>, <&sata3_pins>;
......@@ -240,6 +251,10 @@
};
};
bm-bppi {
status = "okay";
};
pcie-controller {
status = "okay";
/*
......
......@@ -58,7 +58,8 @@
ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000
MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000
MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000>;
MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000
MBUS_ID(0x0c, 0x04) 0 0xf1200000 0x100000>;
internal-regs {
ethernet@70000 {
......@@ -66,6 +67,9 @@
pinctrl-names = "default";
phy = <&phy_dedicated>;
phy-mode = "rgmii-id";
buffer-manager = <&bm>;
bm,pool-long = <0>;
bm,pool-short = <1>;
status = "okay";
};
......@@ -110,6 +114,15 @@
pinctrl-names = "default";
status = "okay";
};
bm@c8000 {
status = "okay";
};
};
bm-bppi {
status = "okay";
};
};
};
......@@ -540,6 +540,14 @@
status = "disabled";
};
bm: bm@c8000 {
compatible = "marvell,armada-380-neta-bm";
reg = <0xc8000 0xac>;
clocks = <&gateclk 13>;
internal-mem = <&bm_bppi>;
status = "disabled";
};
sata@e0000 {
compatible = "marvell,armada-380-ahci";
reg = <0xe0000 0x2000>;
......@@ -618,6 +626,17 @@
#size-cells = <1>;
ranges = <0 MBUS_ID(0x09, 0x15) 0 0x800>;
};
bm_bppi: bm-bppi {
compatible = "mmio-sram";
reg = <MBUS_ID(0x0c, 0x04) 0 0x100000>;
ranges = <0 MBUS_ID(0x0c, 0x04) 0 0x100000>;
#address-cells = <1>;
#size-cells = <1>;
clocks = <&gateclk 13>;
no-memory-wc;
status = "disabled";
};
};
clocks {
......
......@@ -77,7 +77,8 @@
MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
MBUS_ID(0x01, 0x2f) 0 0 0xf0000000 0x1000000
MBUS_ID(0x09, 0x09) 0 0 0xf8100000 0x10000
MBUS_ID(0x09, 0x05) 0 0 0xf8110000 0x10000>;
MBUS_ID(0x09, 0x05) 0 0 0xf8110000 0x10000
MBUS_ID(0x0c, 0x04) 0 0 0xf1200000 0x100000>;
devbus-bootcs {
status = "okay";
......@@ -181,21 +182,33 @@
status = "okay";
phy = <&phy0>;
phy-mode = "rgmii-id";
buffer-manager = <&bm>;
bm,pool-long = <0>;
};
ethernet@74000 {
status = "okay";
phy = <&phy1>;
phy-mode = "rgmii-id";
buffer-manager = <&bm>;
bm,pool-long = <1>;
};
ethernet@30000 {
status = "okay";
phy = <&phy2>;
phy-mode = "sgmii";
buffer-manager = <&bm>;
bm,pool-long = <2>;
};
ethernet@34000 {
status = "okay";
phy = <&phy3>;
phy-mode = "sgmii";
buffer-manager = <&bm>;
bm,pool-long = <3>;
};
bm@c0000 {
status = "okay";
};
mvsdio@d4000 {
......@@ -230,5 +243,9 @@
};
};
};
bm-bppi {
status = "okay";
};
};
};
......@@ -96,7 +96,8 @@
MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
MBUS_ID(0x01, 0x2f) 0 0 0xf0000000 0x1000000
MBUS_ID(0x09, 0x09) 0 0 0xf8100000 0x10000
MBUS_ID(0x09, 0x05) 0 0 0xf8110000 0x10000>;
MBUS_ID(0x09, 0x05) 0 0 0xf8110000 0x10000
MBUS_ID(0x0c, 0x04) 0 0 0xf1200000 0x100000>;
devbus-bootcs {
status = "okay";
......@@ -196,21 +197,29 @@
status = "okay";
phy = <&phy0>;
phy-mode = "qsgmii";
buffer-manager = <&bm>;
bm,pool-long = <0>;
};
ethernet@74000 {
status = "okay";
phy = <&phy1>;
phy-mode = "qsgmii";
buffer-manager = <&bm>;
bm,pool-long = <1>;
};
ethernet@30000 {
status = "okay";
phy = <&phy2>;
phy-mode = "qsgmii";
buffer-manager = <&bm>;
bm,pool-long = <2>;
};
ethernet@34000 {
status = "okay";
phy = <&phy3>;
phy-mode = "qsgmii";
buffer-manager = <&bm>;
bm,pool-long = <3>;
};
/* Front-side USB slot */
......@@ -235,6 +244,10 @@
};
};
bm@c0000 {
status = "okay";
};
nand@d0000 {
status = "okay";
num-cs = <1>;
......@@ -243,5 +256,9 @@
nand-on-flash-bbt;
};
};
bm-bppi {
status = "okay";
};
};
};
......@@ -67,7 +67,8 @@
MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
MBUS_ID(0x01, 0x2f) 0 0 0xf0000000 0x8000000
MBUS_ID(0x09, 0x09) 0 0 0xf8100000 0x10000
MBUS_ID(0x09, 0x05) 0 0 0xf8110000 0x10000>;
MBUS_ID(0x09, 0x05) 0 0 0xf8110000 0x10000
MBUS_ID(0x0c, 0x04) 0 0 0xd1200000 0x100000>;
devbus-bootcs {
status = "okay";
......@@ -176,21 +177,29 @@
status = "okay";
phy = <&phy0>;
phy-mode = "sgmii";
buffer-manager = <&bm>;
bm,pool-long = <0>;
};
ethernet@74000 {
status = "okay";
phy = <&phy1>;
phy-mode = "sgmii";
buffer-manager = <&bm>;
bm,pool-long = <1>;
};
ethernet@30000 {
status = "okay";
phy = <&phy2>;
phy-mode = "sgmii";
buffer-manager = <&bm>;
bm,pool-long = <2>;
};
ethernet@34000 {
status = "okay";
phy = <&phy3>;
phy-mode = "sgmii";
buffer-manager = <&bm>;
bm,pool-long = <3>;
};
i2c@11000 {
status = "okay";
......@@ -219,6 +228,14 @@
usb@51000 {
status = "okay";
};
bm@c0000 {
status = "okay";
};
};
bm-bppi {
status = "okay";
};
};
};
......
......@@ -253,6 +253,14 @@
marvell,crypto-sram-size = <0x800>;
};
bm: bm@c0000 {
compatible = "marvell,armada-380-neta-bm";
reg = <0xc0000 0xac>;
clocks = <&gateclk 13>;
internal-mem = <&bm_bppi>;
status = "disabled";
};
xor@f0900 {
compatible = "marvell,orion-xor";
reg = <0xF0900 0x100
......@@ -291,6 +299,17 @@
#size-cells = <1>;
ranges = <0 MBUS_ID(0x09, 0x05) 0 0x800>;
};
bm_bppi: bm-bppi {
compatible = "mmio-sram";
reg = <MBUS_ID(0x0c, 0x04) 0 0x100000>;
ranges = <0 MBUS_ID(0x0c, 0x04) 0 0x100000>;
#address-cells = <1>;
#size-cells = <1>;
clocks = <&gateclk 13>;
no-memory-wc;
status = "disabled";
};
};
clocks {
......
......@@ -948,6 +948,58 @@ void mvebu_mbus_get_pcie_io_aperture(struct resource *res)
*res = mbus_state.pcie_io_aperture;
}
int mvebu_mbus_get_dram_win_info(phys_addr_t phyaddr, u8 *target, u8 *attr)
{
const struct mbus_dram_target_info *dram;
int i;
/* Get dram info */
dram = mv_mbus_dram_info();
if (!dram) {
pr_err("missing DRAM information\n");
return -ENODEV;
}
/* Try to find matching DRAM window for phyaddr */
for (i = 0; i < dram->num_cs; i++) {
const struct mbus_dram_window *cs = dram->cs + i;
if (cs->base <= phyaddr &&
phyaddr <= (cs->base + cs->size - 1)) {
*target = dram->mbus_dram_target_id;
*attr = cs->mbus_attr;
return 0;
}
}
pr_err("invalid dram address 0x%x\n", phyaddr);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(mvebu_mbus_get_dram_win_info);
int mvebu_mbus_get_io_win_info(phys_addr_t phyaddr, u32 *size, u8 *target,
u8 *attr)
{
int win;
for (win = 0; win < mbus_state.soc->num_wins; win++) {
u64 wbase;
int enabled;
mvebu_mbus_read_window(&mbus_state, win, &enabled, &wbase,
size, target, attr, NULL);
if (!enabled)
continue;
if (wbase <= phyaddr && phyaddr <= wbase + *size)
return win;
}
return -EINVAL;
}
EXPORT_SYMBOL_GPL(mvebu_mbus_get_io_win_info);
static __init int mvebu_mbus_debugfs_init(void)
{
struct mvebu_mbus_state *s = &mbus_state;
......
......@@ -360,7 +360,10 @@ static int sram_probe(struct platform_device *pdev)
return -EBUSY;
}
sram->virt_base = devm_ioremap_wc(sram->dev, res->start, size);
if (of_property_read_bool(pdev->dev.of_node, "no-memory-wc"))
sram->virt_base = devm_ioremap(sram->dev, res->start, size);
else
sram->virt_base = devm_ioremap_wc(sram->dev, res->start, size);
if (IS_ERR(sram->virt_base))
return PTR_ERR(sram->virt_base);
......
......@@ -40,6 +40,20 @@ config MVMDIO
This driver is used by the MV643XX_ETH and MVNETA drivers.
config MVNETA_BM
tristate "Marvell Armada 38x/XP network interface BM support"
depends on MVNETA
select HWBM
---help---
This driver supports auxiliary block of the network
interface units in the Marvell ARMADA XP and ARMADA 38x SoC
family, which is called buffer manager.
This driver, when enabled, strictly cooperates with mvneta
driver and is common for all network ports of the devices,
even for Armada 370 SoC, which doesn't support hardware
buffer management.
config MVNETA
tristate "Marvell Armada 370/38x/XP network interface support"
depends on PLAT_ORION
......
......@@ -4,6 +4,7 @@
obj-$(CONFIG_MVMDIO) += mvmdio.o
obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
obj-$(CONFIG_MVNETA_BM) += mvneta_bm.o
obj-$(CONFIG_MVNETA) += mvneta.o
obj-$(CONFIG_MVPP2) += mvpp2.o
obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
......
/*
* Driver for Marvell NETA network controller Buffer Manager.
*
* Copyright (C) 2015 Marvell
*
* Marcin Wojtas <mw@semihalf.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/clk.h>
#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mbus.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/skbuff.h>
#include <net/hwbm.h>
#include "mvneta_bm.h"
#define MVNETA_BM_DRIVER_NAME "mvneta_bm"
#define MVNETA_BM_DRIVER_VERSION "1.0"
static void mvneta_bm_write(struct mvneta_bm *priv, u32 offset, u32 data)
{
writel(data, priv->reg_base + offset);
}
static u32 mvneta_bm_read(struct mvneta_bm *priv, u32 offset)
{
return readl(priv->reg_base + offset);
}
static void mvneta_bm_pool_enable(struct mvneta_bm *priv, int pool_id)
{
u32 val;
val = mvneta_bm_read(priv, MVNETA_BM_POOL_BASE_REG(pool_id));
val |= MVNETA_BM_POOL_ENABLE_MASK;
mvneta_bm_write(priv, MVNETA_BM_POOL_BASE_REG(pool_id), val);
/* Clear BM cause register */
mvneta_bm_write(priv, MVNETA_BM_INTR_CAUSE_REG, 0);
}
static void mvneta_bm_pool_disable(struct mvneta_bm *priv, int pool_id)
{
u32 val;
val = mvneta_bm_read(priv, MVNETA_BM_POOL_BASE_REG(pool_id));
val &= ~MVNETA_BM_POOL_ENABLE_MASK;
mvneta_bm_write(priv, MVNETA_BM_POOL_BASE_REG(pool_id), val);
}
static inline void mvneta_bm_config_set(struct mvneta_bm *priv, u32 mask)
{
u32 val;
val = mvneta_bm_read(priv, MVNETA_BM_CONFIG_REG);
val |= mask;
mvneta_bm_write(priv, MVNETA_BM_CONFIG_REG, val);
}
static inline void mvneta_bm_config_clear(struct mvneta_bm *priv, u32 mask)
{
u32 val;
val = mvneta_bm_read(priv, MVNETA_BM_CONFIG_REG);
val &= ~mask;
mvneta_bm_write(priv, MVNETA_BM_CONFIG_REG, val);
}
static void mvneta_bm_pool_target_set(struct mvneta_bm *priv, int pool_id,
u8 target_id, u8 attr)
{
u32 val;
val = mvneta_bm_read(priv, MVNETA_BM_XBAR_POOL_REG(pool_id));
val &= ~MVNETA_BM_TARGET_ID_MASK(pool_id);
val &= ~MVNETA_BM_XBAR_ATTR_MASK(pool_id);
val |= MVNETA_BM_TARGET_ID_VAL(pool_id, target_id);
val |= MVNETA_BM_XBAR_ATTR_VAL(pool_id, attr);
mvneta_bm_write(priv, MVNETA_BM_XBAR_POOL_REG(pool_id), val);
}
int mvneta_bm_construct(struct hwbm_pool *hwbm_pool, void *buf)
{
struct mvneta_bm_pool *bm_pool =
(struct mvneta_bm_pool *)hwbm_pool->priv;
struct mvneta_bm *priv = bm_pool->priv;
dma_addr_t phys_addr;
/* In order to update buf_cookie field of RX descriptor properly,
* BM hardware expects buf virtual address to be placed in the
* first four bytes of mapped buffer.
*/
*(u32 *)buf = (u32)buf;
phys_addr = dma_map_single(&priv->pdev->dev, buf, bm_pool->buf_size,
DMA_FROM_DEVICE);
if (unlikely(dma_mapping_error(&priv->pdev->dev, phys_addr)))
return -ENOMEM;
mvneta_bm_pool_put_bp(priv, bm_pool, phys_addr);
return 0;
}
EXPORT_SYMBOL_GPL(mvneta_bm_construct);
/* Create pool */
static int mvneta_bm_pool_create(struct mvneta_bm *priv,
struct mvneta_bm_pool *bm_pool)
{
struct platform_device *pdev = priv->pdev;
u8 target_id, attr;
int size_bytes, err;
size_bytes = sizeof(u32) * bm_pool->hwbm_pool.size;
bm_pool->virt_addr = dma_alloc_coherent(&pdev->dev, size_bytes,
&bm_pool->phys_addr,
GFP_KERNEL);
if (!bm_pool->virt_addr)
return -ENOMEM;
if (!IS_ALIGNED((u32)bm_pool->virt_addr, MVNETA_BM_POOL_PTR_ALIGN)) {
dma_free_coherent(&pdev->dev, size_bytes, bm_pool->virt_addr,
bm_pool->phys_addr);
dev_err(&pdev->dev, "BM pool %d is not %d bytes aligned\n",
bm_pool->id, MVNETA_BM_POOL_PTR_ALIGN);
return -ENOMEM;
}
err = mvebu_mbus_get_dram_win_info(bm_pool->phys_addr, &target_id,
&attr);
if (err < 0) {
dma_free_coherent(&pdev->dev, size_bytes, bm_pool->virt_addr,
bm_pool->phys_addr);
return err;
}
/* Set pool address */
mvneta_bm_write(priv, MVNETA_BM_POOL_BASE_REG(bm_pool->id),
bm_pool->phys_addr);
mvneta_bm_pool_target_set(priv, bm_pool->id, target_id, attr);
mvneta_bm_pool_enable(priv, bm_pool->id);
return 0;
}
/* Notify the driver that BM pool is being used as specific type and return the
* pool pointer on success
*/
struct mvneta_bm_pool *mvneta_bm_pool_use(struct mvneta_bm *priv, u8 pool_id,
enum mvneta_bm_type type, u8 port_id,
int pkt_size)
{
struct mvneta_bm_pool *new_pool = &priv->bm_pools[pool_id];
int num, err;
if (new_pool->type == MVNETA_BM_LONG &&
new_pool->port_map != 1 << port_id) {
dev_err(&priv->pdev->dev,
"long pool cannot be shared by the ports\n");
return NULL;
}
if (new_pool->type == MVNETA_BM_SHORT && new_pool->type != type) {
dev_err(&priv->pdev->dev,
"mixing pools' types between the ports is forbidden\n");
return NULL;
}
if (new_pool->pkt_size == 0 || type != MVNETA_BM_SHORT)
new_pool->pkt_size = pkt_size;
/* Allocate buffers in case BM pool hasn't been used yet */
if (new_pool->type == MVNETA_BM_FREE) {
struct hwbm_pool *hwbm_pool = &new_pool->hwbm_pool;
new_pool->priv = priv;
new_pool->type = type;
new_pool->buf_size = MVNETA_RX_BUF_SIZE(new_pool->pkt_size);
hwbm_pool->frag_size =
SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(new_pool->pkt_size)) +
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
hwbm_pool->construct = mvneta_bm_construct;
hwbm_pool->priv = new_pool;
/* Create new pool */
err = mvneta_bm_pool_create(priv, new_pool);
if (err) {
dev_err(&priv->pdev->dev, "fail to create pool %d\n",
new_pool->id);
return NULL;
}
/* Allocate buffers for this pool */
num = hwbm_pool_add(hwbm_pool, hwbm_pool->size, GFP_ATOMIC);
if (num != hwbm_pool->size) {
WARN(1, "pool %d: %d of %d allocated\n",
new_pool->id, num, hwbm_pool->size);
return NULL;
}
}
return new_pool;
}
EXPORT_SYMBOL_GPL(mvneta_bm_pool_use);
/* Free all buffers from the pool */
void mvneta_bm_bufs_free(struct mvneta_bm *priv, struct mvneta_bm_pool *bm_pool,
u8 port_map)
{
int i;
bm_pool->port_map &= ~port_map;
if (bm_pool->port_map)
return;
mvneta_bm_config_set(priv, MVNETA_BM_EMPTY_LIMIT_MASK);
for (i = 0; i < bm_pool->hwbm_pool.buf_num; i++) {
dma_addr_t buf_phys_addr;
u32 *vaddr;
/* Get buffer physical address (indirect access) */
buf_phys_addr = mvneta_bm_pool_get_bp(priv, bm_pool);
/* Work-around to the problems when destroying the pool,
* when it occurs that a read access to BPPI returns 0.
*/
if (buf_phys_addr == 0)
continue;
vaddr = phys_to_virt(buf_phys_addr);
if (!vaddr)
break;
dma_unmap_single(&priv->pdev->dev, buf_phys_addr,
bm_pool->buf_size, DMA_FROM_DEVICE);
hwbm_buf_free(&bm_pool->hwbm_pool, vaddr);
}
mvneta_bm_config_clear(priv, MVNETA_BM_EMPTY_LIMIT_MASK);
/* Update BM driver with number of buffers removed from pool */
bm_pool->hwbm_pool.buf_num -= i;
}
EXPORT_SYMBOL_GPL(mvneta_bm_bufs_free);
/* Cleanup pool */
void mvneta_bm_pool_destroy(struct mvneta_bm *priv,
struct mvneta_bm_pool *bm_pool, u8 port_map)
{
struct hwbm_pool *hwbm_pool = &bm_pool->hwbm_pool;
bm_pool->port_map &= ~port_map;
if (bm_pool->port_map)
return;
bm_pool->type = MVNETA_BM_FREE;
mvneta_bm_bufs_free(priv, bm_pool, port_map);
if (hwbm_pool->buf_num)
WARN(1, "cannot free all buffers in pool %d\n", bm_pool->id);
if (bm_pool->virt_addr) {
dma_free_coherent(&priv->pdev->dev,
sizeof(u32) * hwbm_pool->size,
bm_pool->virt_addr, bm_pool->phys_addr);
bm_pool->virt_addr = NULL;
}
mvneta_bm_pool_disable(priv, bm_pool->id);
}
EXPORT_SYMBOL_GPL(mvneta_bm_pool_destroy);
static void mvneta_bm_pools_init(struct mvneta_bm *priv)
{
struct device_node *dn = priv->pdev->dev.of_node;
struct mvneta_bm_pool *bm_pool;
char prop[15];
u32 size;
int i;
/* Activate BM unit */
mvneta_bm_write(priv, MVNETA_BM_COMMAND_REG, MVNETA_BM_START_MASK);
/* Create all pools with maximum size */
for (i = 0; i < MVNETA_BM_POOLS_NUM; i++) {
bm_pool = &priv->bm_pools[i];
bm_pool->id = i;
bm_pool->type = MVNETA_BM_FREE;
/* Reset read pointer */
mvneta_bm_write(priv, MVNETA_BM_POOL_READ_PTR_REG(i), 0);
/* Reset write pointer */
mvneta_bm_write(priv, MVNETA_BM_POOL_WRITE_PTR_REG(i), 0);
/* Configure pool size according to DT or use default value */
sprintf(prop, "pool%d,capacity", i);
if (of_property_read_u32(dn, prop, &size)) {
size = MVNETA_BM_POOL_CAP_DEF;
} else if (size > MVNETA_BM_POOL_CAP_MAX) {
dev_warn(&priv->pdev->dev,
"Illegal pool %d capacity %d, set to %d\n",
i, size, MVNETA_BM_POOL_CAP_MAX);
size = MVNETA_BM_POOL_CAP_MAX;
} else if (size < MVNETA_BM_POOL_CAP_MIN) {
dev_warn(&priv->pdev->dev,
"Illegal pool %d capacity %d, set to %d\n",
i, size, MVNETA_BM_POOL_CAP_MIN);
size = MVNETA_BM_POOL_CAP_MIN;
} else if (!IS_ALIGNED(size, MVNETA_BM_POOL_CAP_ALIGN)) {
dev_warn(&priv->pdev->dev,
"Illegal pool %d capacity %d, round to %d\n",
i, size, ALIGN(size,
MVNETA_BM_POOL_CAP_ALIGN));
size = ALIGN(size, MVNETA_BM_POOL_CAP_ALIGN);
}
bm_pool->hwbm_pool.size = size;
mvneta_bm_write(priv, MVNETA_BM_POOL_SIZE_REG(i),
bm_pool->hwbm_pool.size);
/* Obtain custom pkt_size from DT */
sprintf(prop, "pool%d,pkt-size", i);
if (of_property_read_u32(dn, prop, &bm_pool->pkt_size))
bm_pool->pkt_size = 0;
}
}
static void mvneta_bm_default_set(struct mvneta_bm *priv)
{
u32 val;
/* Mask BM all interrupts */
mvneta_bm_write(priv, MVNETA_BM_INTR_MASK_REG, 0);
/* Clear BM cause register */
mvneta_bm_write(priv, MVNETA_BM_INTR_CAUSE_REG, 0);
/* Set BM configuration register */
val = mvneta_bm_read(priv, MVNETA_BM_CONFIG_REG);
/* Reduce MaxInBurstSize from 32 BPs to 16 BPs */
val &= ~MVNETA_BM_MAX_IN_BURST_SIZE_MASK;
val |= MVNETA_BM_MAX_IN_BURST_SIZE_16BP;
mvneta_bm_write(priv, MVNETA_BM_CONFIG_REG, val);
}
static int mvneta_bm_init(struct mvneta_bm *priv)
{
mvneta_bm_default_set(priv);
/* Allocate and initialize BM pools structures */
priv->bm_pools = devm_kcalloc(&priv->pdev->dev, MVNETA_BM_POOLS_NUM,
sizeof(struct mvneta_bm_pool),
GFP_KERNEL);
if (!priv->bm_pools)
return -ENOMEM;
mvneta_bm_pools_init(priv);
return 0;
}
static int mvneta_bm_get_sram(struct device_node *dn,
struct mvneta_bm *priv)
{
priv->bppi_pool = of_gen_pool_get(dn, "internal-mem", 0);
if (!priv->bppi_pool)
return -ENOMEM;
priv->bppi_virt_addr = gen_pool_dma_alloc(priv->bppi_pool,
MVNETA_BM_BPPI_SIZE,
&priv->bppi_phys_addr);
if (!priv->bppi_virt_addr)
return -ENOMEM;
return 0;
}
static void mvneta_bm_put_sram(struct mvneta_bm *priv)
{
gen_pool_free(priv->bppi_pool, priv->bppi_phys_addr,
MVNETA_BM_BPPI_SIZE);
}
static int mvneta_bm_probe(struct platform_device *pdev)
{
struct device_node *dn = pdev->dev.of_node;
struct mvneta_bm *priv;
struct resource *res;
int err;
priv = devm_kzalloc(&pdev->dev, sizeof(struct mvneta_bm), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->reg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->reg_base))
return PTR_ERR(priv->reg_base);
priv->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
err = clk_prepare_enable(priv->clk);
if (err < 0)
return err;
err = mvneta_bm_get_sram(dn, priv);
if (err < 0) {
dev_err(&pdev->dev, "failed to allocate internal memory\n");
goto err_clk;
}
priv->pdev = pdev;
/* Initialize buffer manager internals */
err = mvneta_bm_init(priv);
if (err < 0) {
dev_err(&pdev->dev, "failed to initialize controller\n");
goto err_sram;
}
dn->data = priv;
platform_set_drvdata(pdev, priv);
dev_info(&pdev->dev, "Buffer Manager for network controller enabled\n");
return 0;
err_sram:
mvneta_bm_put_sram(priv);
err_clk:
clk_disable_unprepare(priv->clk);
return err;
}
static int mvneta_bm_remove(struct platform_device *pdev)
{
struct mvneta_bm *priv = platform_get_drvdata(pdev);
u8 all_ports_map = 0xff;
int i = 0;
for (i = 0; i < MVNETA_BM_POOLS_NUM; i++) {
struct mvneta_bm_pool *bm_pool = &priv->bm_pools[i];
mvneta_bm_pool_destroy(priv, bm_pool, all_ports_map);
}
mvneta_bm_put_sram(priv);
/* Dectivate BM unit */
mvneta_bm_write(priv, MVNETA_BM_COMMAND_REG, MVNETA_BM_STOP_MASK);
clk_disable_unprepare(priv->clk);
return 0;
}
static const struct of_device_id mvneta_bm_match[] = {
{ .compatible = "marvell,armada-380-neta-bm" },
{ }
};
MODULE_DEVICE_TABLE(of, mvneta_bm_match);
static struct platform_driver mvneta_bm_driver = {
.probe = mvneta_bm_probe,
.remove = mvneta_bm_remove,
.driver = {
.name = MVNETA_BM_DRIVER_NAME,
.of_match_table = mvneta_bm_match,
},
};
module_platform_driver(mvneta_bm_driver);
MODULE_DESCRIPTION("Marvell NETA Buffer Manager Driver - www.marvell.com");
MODULE_AUTHOR("Marcin Wojtas <mw@semihalf.com>");
MODULE_LICENSE("GPL v2");
/*
* Driver for Marvell NETA network controller Buffer Manager.
*
* Copyright (C) 2015 Marvell
*
* Marcin Wojtas <mw@semihalf.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#ifndef _MVNETA_BM_H_
#define _MVNETA_BM_H_
/* BM Configuration Register */
#define MVNETA_BM_CONFIG_REG 0x0
#define MVNETA_BM_STATUS_MASK 0x30
#define MVNETA_BM_ACTIVE_MASK BIT(4)
#define MVNETA_BM_MAX_IN_BURST_SIZE_MASK 0x60000
#define MVNETA_BM_MAX_IN_BURST_SIZE_16BP BIT(18)
#define MVNETA_BM_EMPTY_LIMIT_MASK BIT(19)
/* BM Activation Register */
#define MVNETA_BM_COMMAND_REG 0x4
#define MVNETA_BM_START_MASK BIT(0)
#define MVNETA_BM_STOP_MASK BIT(1)
#define MVNETA_BM_PAUSE_MASK BIT(2)
/* BM Xbar interface Register */
#define MVNETA_BM_XBAR_01_REG 0x8
#define MVNETA_BM_XBAR_23_REG 0xc
#define MVNETA_BM_XBAR_POOL_REG(pool) \
(((pool) < 2) ? MVNETA_BM_XBAR_01_REG : MVNETA_BM_XBAR_23_REG)
#define MVNETA_BM_TARGET_ID_OFFS(pool) (((pool) & 1) ? 16 : 0)
#define MVNETA_BM_TARGET_ID_MASK(pool) \
(0xf << MVNETA_BM_TARGET_ID_OFFS(pool))
#define MVNETA_BM_TARGET_ID_VAL(pool, id) \
((id) << MVNETA_BM_TARGET_ID_OFFS(pool))
#define MVNETA_BM_XBAR_ATTR_OFFS(pool) (((pool) & 1) ? 20 : 4)
#define MVNETA_BM_XBAR_ATTR_MASK(pool) \
(0xff << MVNETA_BM_XBAR_ATTR_OFFS(pool))
#define MVNETA_BM_XBAR_ATTR_VAL(pool, attr) \
((attr) << MVNETA_BM_XBAR_ATTR_OFFS(pool))
/* Address of External Buffer Pointers Pool Register */
#define MVNETA_BM_POOL_BASE_REG(pool) (0x10 + ((pool) << 4))
#define MVNETA_BM_POOL_ENABLE_MASK BIT(0)
/* External Buffer Pointers Pool RD pointer Register */
#define MVNETA_BM_POOL_READ_PTR_REG(pool) (0x14 + ((pool) << 4))
#define MVNETA_BM_POOL_SET_READ_PTR_MASK 0xfffc
#define MVNETA_BM_POOL_GET_READ_PTR_OFFS 16
#define MVNETA_BM_POOL_GET_READ_PTR_MASK 0xfffc0000
/* External Buffer Pointers Pool WR pointer */
#define MVNETA_BM_POOL_WRITE_PTR_REG(pool) (0x18 + ((pool) << 4))
#define MVNETA_BM_POOL_SET_WRITE_PTR_OFFS 0
#define MVNETA_BM_POOL_SET_WRITE_PTR_MASK 0xfffc
#define MVNETA_BM_POOL_GET_WRITE_PTR_OFFS 16
#define MVNETA_BM_POOL_GET_WRITE_PTR_MASK 0xfffc0000
/* External Buffer Pointers Pool Size Register */
#define MVNETA_BM_POOL_SIZE_REG(pool) (0x1c + ((pool) << 4))
#define MVNETA_BM_POOL_SIZE_MASK 0x3fff
/* BM Interrupt Cause Register */
#define MVNETA_BM_INTR_CAUSE_REG (0x50)
/* BM interrupt Mask Register */
#define MVNETA_BM_INTR_MASK_REG (0x54)
/* Other definitions */
#define MVNETA_BM_SHORT_PKT_SIZE 256
#define MVNETA_BM_POOLS_NUM 4
#define MVNETA_BM_POOL_CAP_MIN 128
#define MVNETA_BM_POOL_CAP_DEF 2048
#define MVNETA_BM_POOL_CAP_MAX \
(16 * 1024 - MVNETA_BM_POOL_CAP_ALIGN)
#define MVNETA_BM_POOL_CAP_ALIGN 32
#define MVNETA_BM_POOL_PTR_ALIGN 32
#define MVNETA_BM_POOL_ACCESS_OFFS 8
#define MVNETA_BM_BPPI_SIZE 0x100000
#define MVNETA_RX_BUF_SIZE(pkt_size) ((pkt_size) + NET_SKB_PAD)
enum mvneta_bm_type {
MVNETA_BM_FREE,
MVNETA_BM_LONG,
MVNETA_BM_SHORT
};
struct mvneta_bm {
void __iomem *reg_base;
struct clk *clk;
struct platform_device *pdev;
struct gen_pool *bppi_pool;
/* BPPI virtual base address */
void __iomem *bppi_virt_addr;
/* BPPI physical base address */
dma_addr_t bppi_phys_addr;
/* BM pools */
struct mvneta_bm_pool *bm_pools;
};
struct mvneta_bm_pool {
struct hwbm_pool hwbm_pool;
/* Pool number in the range 0-3 */
u8 id;
enum mvneta_bm_type type;
/* Packet size */
int pkt_size;
/* Size of the buffer acces through DMA*/
u32 buf_size;
/* BPPE virtual base address */
u32 *virt_addr;
/* BPPE physical base address */
dma_addr_t phys_addr;
/* Ports using BM pool */
u8 port_map;
struct mvneta_bm *priv;
};
/* Declarations and definitions */
void *mvneta_frag_alloc(unsigned int frag_size);
void mvneta_frag_free(unsigned int frag_size, void *data);
#if defined(CONFIG_MVNETA_BM) || defined(CONFIG_MVNETA_BM_MODULE)
void mvneta_bm_pool_destroy(struct mvneta_bm *priv,
struct mvneta_bm_pool *bm_pool, u8 port_map);
void mvneta_bm_bufs_free(struct mvneta_bm *priv, struct mvneta_bm_pool *bm_pool,
u8 port_map);
int mvneta_bm_construct(struct hwbm_pool *hwbm_pool, void *buf);
int mvneta_bm_pool_refill(struct mvneta_bm *priv,
struct mvneta_bm_pool *bm_pool);
struct mvneta_bm_pool *mvneta_bm_pool_use(struct mvneta_bm *priv, u8 pool_id,
enum mvneta_bm_type type, u8 port_id,
int pkt_size);
static inline void mvneta_bm_pool_put_bp(struct mvneta_bm *priv,
struct mvneta_bm_pool *bm_pool,
dma_addr_t buf_phys_addr)
{
writel_relaxed(buf_phys_addr, priv->bppi_virt_addr +
(bm_pool->id << MVNETA_BM_POOL_ACCESS_OFFS));
}
static inline u32 mvneta_bm_pool_get_bp(struct mvneta_bm *priv,
struct mvneta_bm_pool *bm_pool)
{
return readl_relaxed(priv->bppi_virt_addr +
(bm_pool->id << MVNETA_BM_POOL_ACCESS_OFFS));
}
#else
void mvneta_bm_pool_destroy(struct mvneta_bm *priv,
struct mvneta_bm_pool *bm_pool, u8 port_map) {}
void mvneta_bm_bufs_free(struct mvneta_bm *priv, struct mvneta_bm_pool *bm_pool,
u8 port_map) {}
int mvneta_bm_construct(struct hwbm_pool *hwbm_pool, void *buf) { return 0; }
int mvneta_bm_pool_refill(struct mvneta_bm *priv,
struct mvneta_bm_pool *bm_pool) {return 0; }
struct mvneta_bm_pool *mvneta_bm_pool_use(struct mvneta_bm *priv, u8 pool_id,
enum mvneta_bm_type type, u8 port_id,
int pkt_size) { return NULL; }
static inline void mvneta_bm_pool_put_bp(struct mvneta_bm *priv,
struct mvneta_bm_pool *bm_pool,
dma_addr_t buf_phys_addr) {}
static inline u32 mvneta_bm_pool_get_bp(struct mvneta_bm *priv,
struct mvneta_bm_pool *bm_pool)
{ return 0; }
#endif /* CONFIG_MVNETA_BM */
#endif
......@@ -69,6 +69,9 @@ static inline const struct mbus_dram_target_info *mv_mbus_dram_info_nooverlap(vo
int mvebu_mbus_save_cpu_target(u32 *store_addr);
void mvebu_mbus_get_pcie_mem_aperture(struct resource *res);
void mvebu_mbus_get_pcie_io_aperture(struct resource *res);
int mvebu_mbus_get_dram_win_info(phys_addr_t phyaddr, u8 *target, u8 *attr);
int mvebu_mbus_get_io_win_info(phys_addr_t phyaddr, u32 *size, u8 *target,
u8 *attr);
int mvebu_mbus_add_window_remap_by_id(unsigned int target,
unsigned int attribute,
phys_addr_t base, size_t size,
......
#ifndef _HWBM_H
#define _HWBM_H
struct hwbm_pool {
/* Capacity of the pool */
int size;
/* Size of the buffers managed */
int frag_size;
/* Number of buffers currently used by this pool */
int buf_num;
/* constructor called during alocation */
int (*construct)(struct hwbm_pool *bm_pool, void *buf);
/* protect acces to the buffer counter*/
spinlock_t lock;
/* private data */
void *priv;
};
#ifdef CONFIG_HWBM
void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf);
int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp);
int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp);
#else
void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf) {}
int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp) { return 0; }
int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp)
{ return 0; }
#endif /* CONFIG_HWBM */
#endif /* _HWBM_H */
......@@ -253,6 +253,9 @@ config XPS
depends on SMP
default y
config HWBM
bool
config SOCK_CGROUP_DATA
bool
default n
......
......@@ -25,4 +25,5 @@ obj-$(CONFIG_CGROUP_NET_PRIO) += netprio_cgroup.o
obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o
obj-$(CONFIG_LWTUNNEL) += lwtunnel.o
obj-$(CONFIG_DST_CACHE) += dst_cache.o
obj-$(CONFIG_HWBM) += hwbm.o
obj-$(CONFIG_NET_DEVLINK) += devlink.o
/* Support for hardware buffer manager.
*
* Copyright (C) 2016 Marvell
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/skbuff.h>
#include <net/hwbm.h>
void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf)
{
if (likely(bm_pool->frag_size <= PAGE_SIZE))
skb_free_frag(buf);
else
kfree(buf);
}
EXPORT_SYMBOL_GPL(hwbm_buf_free);
/* Refill processing for HW buffer management */
int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp)
{
int frag_size = bm_pool->frag_size;
void *buf;
if (likely(frag_size <= PAGE_SIZE))
buf = netdev_alloc_frag(frag_size);
else
buf = kmalloc(frag_size, gfp);
if (!buf)
return -ENOMEM;
if (bm_pool->construct)
if (bm_pool->construct(bm_pool, buf)) {
hwbm_buf_free(bm_pool, buf);
return -ENOMEM;
}
return 0;
}
EXPORT_SYMBOL_GPL(hwbm_pool_refill);
int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp)
{
int err, i;
unsigned long flags;
spin_lock_irqsave(&bm_pool->lock, flags);
if (bm_pool->buf_num == bm_pool->size) {
pr_warn("pool already filled\n");
return bm_pool->buf_num;
}
if (buf_num + bm_pool->buf_num > bm_pool->size) {
pr_warn("cannot allocate %d buffers for pool\n",
buf_num);
return 0;
}
if ((buf_num + bm_pool->buf_num) < bm_pool->buf_num) {
pr_warn("Adding %d buffers to the %d current buffers will overflow\n",
buf_num, bm_pool->buf_num);
return 0;
}
for (i = 0; i < buf_num; i++) {
err = hwbm_pool_refill(bm_pool, gfp);
if (err < 0)
break;
}
/* Update BM driver with number of buffers added to pool */
bm_pool->buf_num += i;
pr_debug("hwpm pool: %d of %d buffers added\n", i, buf_num);
spin_unlock_irqrestore(&bm_pool->lock, flags);
return i;
}
EXPORT_SYMBOL_GPL(hwbm_pool_add);
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册