* [PATCH net-next 0/8] ARM: Alpine: Ethernet support
@ 2017-02-03 18:12 Antoine Tenart
2017-02-03 18:12 ` [PATCH net-next 1/8] alpine: add I/O fabric interrupt controller (iofic) helpers Antoine Tenart
` (7 more replies)
0 siblings, 8 replies; 23+ messages in thread
From: Antoine Tenart @ 2017-02-03 18:12 UTC (permalink / raw)
To: linux-arm-kernel
Hello all,
This series intend to add Ethernet support for the Annapurna Labs Alpine
v1 (ARM) and v2 (ARM64). The series first adds helpers for the I/O
fabric interrupt controller that is found in many controllers on the
Alpine, then helpers for the so called "Universal DMA" (UDMA) are also
added and finally adds the Alpine Ethernet driver (split in a few
patches, per functionality).
Many units in the Alpine platform (as the Ethernet controller) are
connected to what is called an I/O fabric bus. All these units share a
common implementation of an interrupt controller (the IOFIC) and some
share a DMA controller implementation (the UDMA). These controller are
duplicated in each unit (and there can be more than one per unit) and
use the same common definitions and functions. This is why SoC-specific
helpers and headers are provided here, to abstract some code. As these
controllers are not fully independent ones (they just happen to expose
the same registers in the units register spaces), proper interrupt nor
DMA drivers were not implemented.
This Ethernet controller is connected over PCIe, uses MSIX interrupts,
handle multiple UDMA controllers (4 rx queues + 4 tx queues per UDMA),
and offloading helpers (TSO, checksum offloads...).
This was tested on an Alpine v2 (ARM64) platform, and applies on top
of net-next (8fe809a99263).
Thanks,
Antoine Tenart (8):
alpine: add I/O fabric interrupt controller (iofic) helpers
soc: alpine: add udma helpers
pci: add Annapurna Labs PCI id
net: ethernet: add the Alpine Ethernet driver
net: ethernet: annapurna: add statistics helper
net: ethernet: annapurna: add wol helpers to the Alpine driver
net: ethernet: annapurna: add eee helpers to the Alpine driver
net: ethernet: annapurna: add the coalesce helpers to the Alpine
driver
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/annapurna/Kconfig | 29 +
drivers/net/ethernet/annapurna/Makefile | 6 +
drivers/net/ethernet/annapurna/al_eth.c | 3267 +++++++++++++++++++
drivers/net/ethernet/annapurna/al_eth.h | 282 ++
drivers/net/ethernet/annapurna/al_hw_eth.h | 1301 ++++++++
drivers/net/ethernet/annapurna/al_hw_eth_ec_regs.h | 1099 +++++++
.../net/ethernet/annapurna/al_hw_eth_mac_regs.h | 738 +++++
drivers/net/ethernet/annapurna/al_hw_eth_main.c | 3367 ++++++++++++++++++++
.../ethernet/annapurna/al_hw_unit_adapter_regs.h | 24 +
drivers/soc/Kconfig | 1 +
drivers/soc/Makefile | 1 +
drivers/soc/alpine/Kconfig | 11 +
drivers/soc/alpine/Makefile | 1 +
drivers/soc/alpine/udma/Makefile | 1 +
drivers/soc/alpine/udma/al_udma_config.c | 140 +
drivers/soc/alpine/udma/al_udma_iofic.c | 110 +
drivers/soc/alpine/udma/al_udma_main.c | 245 ++
drivers/soc/alpine/udma/al_udma_queue.c | 232 ++
include/linux/pci_ids.h | 2 +
include/linux/soc/alpine/al_hw_udma.h | 499 +++
include/linux/soc/alpine/al_hw_udma_config.h | 75 +
include/linux/soc/alpine/al_hw_udma_iofic.h | 199 ++
include/linux/soc/alpine/al_hw_udma_regs.h | 134 +
include/linux/soc/alpine/al_hw_udma_regs_m2s.h | 413 +++
include/linux/soc/alpine/al_hw_udma_regs_s2m.h | 294 ++
include/linux/soc/alpine/iofic.h | 165 +
28 files changed, 12638 insertions(+)
create mode 100644 drivers/net/ethernet/annapurna/Kconfig
create mode 100644 drivers/net/ethernet/annapurna/Makefile
create mode 100644 drivers/net/ethernet/annapurna/al_eth.c
create mode 100644 drivers/net/ethernet/annapurna/al_eth.h
create mode 100644 drivers/net/ethernet/annapurna/al_hw_eth.h
create mode 100644 drivers/net/ethernet/annapurna/al_hw_eth_ec_regs.h
create mode 100644 drivers/net/ethernet/annapurna/al_hw_eth_mac_regs.h
create mode 100644 drivers/net/ethernet/annapurna/al_hw_eth_main.c
create mode 100644 drivers/net/ethernet/annapurna/al_hw_unit_adapter_regs.h
create mode 100644 drivers/soc/alpine/Kconfig
create mode 100644 drivers/soc/alpine/Makefile
create mode 100644 drivers/soc/alpine/udma/Makefile
create mode 100644 drivers/soc/alpine/udma/al_udma_config.c
create mode 100644 drivers/soc/alpine/udma/al_udma_iofic.c
create mode 100644 drivers/soc/alpine/udma/al_udma_main.c
create mode 100644 drivers/soc/alpine/udma/al_udma_queue.c
create mode 100644 include/linux/soc/alpine/al_hw_udma.h
create mode 100644 include/linux/soc/alpine/al_hw_udma_config.h
create mode 100644 include/linux/soc/alpine/al_hw_udma_iofic.h
create mode 100644 include/linux/soc/alpine/al_hw_udma_regs.h
create mode 100644 include/linux/soc/alpine/al_hw_udma_regs_m2s.h
create mode 100644 include/linux/soc/alpine/al_hw_udma_regs_s2m.h
create mode 100644 include/linux/soc/alpine/iofic.h
--
2.11.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH net-next 1/8] alpine: add I/O fabric interrupt controller (iofic) helpers
2017-02-03 18:12 [PATCH net-next 0/8] ARM: Alpine: Ethernet support Antoine Tenart
@ 2017-02-03 18:12 ` Antoine Tenart
2017-02-03 18:12 ` [PATCH net-next 2/8] soc: alpine: add udma helpers Antoine Tenart
` (6 subsequent siblings)
7 siblings, 0 replies; 23+ messages in thread
From: Antoine Tenart @ 2017-02-03 18:12 UTC (permalink / raw)
To: linux-arm-kernel
All units connected to the I/O fabric share a common interrupt
controller interface, with the same architecture. This means there are
multiple interrupt controllers exposed, one per unit connected to the
I/O fabric, with the exact same interface.
One particularity of the Alpine platform is that units conencted to the
I/O fabric and their controllers (iofic, udma) are exposed as PCIe
endpoints and their registers are mapped through BARs.
This patch adds helpers for drivers supporting one unit connected to the
Alpine I/O fabric, to configure and use the per-unit exposed iofic.
Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
---
include/linux/soc/alpine/iofic.h | 165 +++++++++++++++++++++++++++++++++++++++
1 file changed, 165 insertions(+)
create mode 100644 include/linux/soc/alpine/iofic.h
diff --git a/include/linux/soc/alpine/iofic.h b/include/linux/soc/alpine/iofic.h
new file mode 100644
index 000000000000..d65b99e865ff
--- /dev/null
+++ b/include/linux/soc/alpine/iofic.h
@@ -0,0 +1,165 @@
+/*
+ * Annapurna Labs IOFIC helpers
+ *
+ * Copyright (C) 2017, Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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 __AL_IOFIC_H__
+#define __AL_IOFIC_H__
+
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#define CTRL_GROUP(group) ((group) * 0x40)
+#define GROUP_INT_MODE(group, vector) (0x400 + (group) * 0x20 + (vector) * 0x8)
+
+#define INT_CAUSE_GROUP 0x0
+#define INT_MASK_GROUP 0x10
+#define INT_MASK_CLEAR_GROUP 0x18
+#define INT_CONTROL_GROUP 0x28
+#define INT_ABORT_MASK_GROUP 0x30
+
+#define INT_CONTROL_GRP_CLEAR_ON_READ BIT(0)
+#define INT_CONTROL_GRP_AUTO_MASK BIT(1)
+#define INT_CONTROL_GRP_AUTO_CLEAR BIT(2)
+#define INT_CONTROL_GRP_SET_ON_POSEDGE BIT(3)
+#define INT_CONTROL_GRP_MASK_MSI_X BIT(5)
+/* MSI-X AWID value, same ID for all cause bits */
+#define INT_CONTROL_GRP_MOD_RES_MASK 0xf000000
+#define INT_CONTROL_GRP_MOD_RES_SHIFT 0x18
+
+#define INT_MOD_INTV_MASK 0x000000ff
+#define INT_MOD_INTV_SHIFT 0x0
+
+
+/*
+ * Configure the interrupt controller registers, actual interrupts are still
+ * masked at this stage.
+ *
+ * @param regs_base regs pointer to interrupt controller registers
+ * @param group the interrupt group.
+ * @param flags flags of Interrupt Control Register
+ */
+
+static inline void al_iofic_config(void __iomem *base, int group, u32 flags)
+{
+ writel(flags, base + CTRL_GROUP(group) + INT_CONTROL_GROUP);
+}
+
+/*
+ * Configure the moderation timer resolution for a given group.
+ *
+ * Applies for both msix and legacy mode.
+ *
+ * @param regs_base pointer to unit registers
+ * @param group the interrupt group
+ * @param resolution resolution of the timer interval, the resolution determines
+ * the rate of decrementing the interval timer, setting value N means that the
+ * interval timer will be decremented each (N+1) * (0.68) micro seconds.
+ */
+static inline void al_iofic_moder_res_config(void __iomem *base, int group,
+ u8 resolution)
+{
+ u32 reg = readl(base + CTRL_GROUP(group) + INT_CONTROL_GROUP);
+
+ reg &= ~INT_CONTROL_GRP_MOD_RES_MASK;
+ reg |= resolution << INT_CONTROL_GRP_MOD_RES_SHIFT;
+
+ writel(reg, base + CTRL_GROUP(group) + INT_CONTROL_GROUP);
+}
+
+/*
+ * Configure the moderation timer interval for a given msix vector.
+ *
+ * @param regs_base pointer to unit registers
+ * @param group the interrupt group
+ * @param vector vector index
+ * @param interval interval between interrupts, 0 disable
+ */
+static inline void al_iofic_msix_moder_interval_config(void __iomem *base,
+ int group, u8 vector,
+ u8 interval)
+{
+ u32 reg = readl(base + GROUP_INT_MODE(group, vector));
+
+ reg &= ~INT_MOD_INTV_MASK;
+ reg |= interval << INT_MOD_INTV_SHIFT;
+
+ writel(reg, base + GROUP_INT_MODE(group, vector));
+}
+
+/*
+ * Unmask specific interrupts for a given group.
+ *
+ * This functions guarantees atomic operations, it is performance optimized as
+ * it will not require read-modify-write. The unmask done using the interrupt
+ * mask clear register, so it's safe to call it while the mask is changed by
+ * the HW (auto mask) or another core.
+ *
+ * @param regs_base pointer to unit registers
+ * @param group the interrupt group
+ * @param mask bitwise of interrupts to unmask, set bits will be unmasked.
+ */
+static inline void al_iofic_unmask(void __iomem *base, int group, u32 mask)
+{
+ /*
+ * use the mask clear register, no need to read the mask register
+ * itself. write 0 to unmask, 1 has no effect
+ */
+ writel(~mask, base + CTRL_GROUP(group) + INT_MASK_CLEAR_GROUP);
+}
+
+/*
+ * Mask specific interrupts for a given group.
+ *
+ * This functions modifies interrupt mask register, the callee must make sure
+ * the mask is not changed by another cpu.
+ *
+ * @param regs_base pointer to unit registers
+ * @param group the interrupt group
+ * @param mask bitwise of interrupts to mask, set bits will be masked.
+ */
+static inline void al_iofic_mask(void __iomem *base, int group, u32 mask)
+{
+ u32 reg = readl(base + CTRL_GROUP(group) + INT_MASK_GROUP);
+ reg |= mask;
+ writel(reg, base + CTRL_GROUP(group) + INT_MASK_GROUP);
+}
+
+/*
+ * Read interrupt cause register for a given group.
+ *
+ * This will clear the set bits if the clear on read mode is enabled.
+ *
+ * @param regs_base pointer to unit registers
+ * @param group the interrupt group
+ */
+static inline u32 al_iofic_read_cause(void __iomem *base, int group)
+{
+ return readl(base + CTRL_GROUP(group) + INT_CAUSE_GROUP);
+}
+
+/*
+ * Unmask specific interrupts from aborting the udma a given group.
+ *
+ * @param regs_base pointer to unit registers
+ * @param group the interrupt group
+ * @param mask bitwise of interrupts to mask
+ */
+static inline void al_iofic_abort_mask(void __iomem *base, int group, u32 mask)
+{
+ writel(mask, base + CTRL_GROUP(group) + INT_ABORT_MASK_GROUP);
+}
+
+/* return the offset of the unmask register for a given group */
+static inline u32 __iomem *al_iofic_unmask_offset_get(void __iomem *base,
+ int group)
+{
+ return base + CTRL_GROUP(group) + INT_MASK_CLEAR_GROUP;
+}
+
+#endif
--
2.11.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next 2/8] soc: alpine: add udma helpers
2017-02-03 18:12 [PATCH net-next 0/8] ARM: Alpine: Ethernet support Antoine Tenart
2017-02-03 18:12 ` [PATCH net-next 1/8] alpine: add I/O fabric interrupt controller (iofic) helpers Antoine Tenart
@ 2017-02-03 18:12 ` Antoine Tenart
2017-02-03 18:12 ` [PATCH net-next 3/8] pci: add Annapurna Labs PCI id Antoine Tenart
` (5 subsequent siblings)
7 siblings, 0 replies; 23+ messages in thread
From: Antoine Tenart @ 2017-02-03 18:12 UTC (permalink / raw)
To: linux-arm-kernel
One or more DMA controllers (called Universal DMA) are exposed as PCI
endpoints for some units connected to the I/O fabric. The DMA controller
registers are mapped in BARs.
These helpers help drivers supporting units connected to the Alpine I/O
fabric, to configure and use the exposed UDMA.
The Alpine UDMA is a full-duplex DMA consisting of a Tx (memory to
stream -m2s-) and an Rx (stream to memory -s2m-) DMA engines. Multiple
queues are available per DMA, with each a description and a completion
ring.
Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
---
drivers/soc/Kconfig | 1 +
drivers/soc/Makefile | 1 +
drivers/soc/alpine/Kconfig | 11 +
drivers/soc/alpine/Makefile | 1 +
drivers/soc/alpine/udma/Makefile | 1 +
drivers/soc/alpine/udma/al_udma_config.c | 140 +++++++
drivers/soc/alpine/udma/al_udma_iofic.c | 110 ++++++
drivers/soc/alpine/udma/al_udma_main.c | 245 ++++++++++++
drivers/soc/alpine/udma/al_udma_queue.c | 232 ++++++++++++
include/linux/soc/alpine/al_hw_udma.h | 499 +++++++++++++++++++++++++
include/linux/soc/alpine/al_hw_udma_config.h | 75 ++++
include/linux/soc/alpine/al_hw_udma_iofic.h | 199 ++++++++++
include/linux/soc/alpine/al_hw_udma_regs.h | 134 +++++++
include/linux/soc/alpine/al_hw_udma_regs_m2s.h | 413 ++++++++++++++++++++
include/linux/soc/alpine/al_hw_udma_regs_s2m.h | 294 +++++++++++++++
15 files changed, 2356 insertions(+)
create mode 100644 drivers/soc/alpine/Kconfig
create mode 100644 drivers/soc/alpine/Makefile
create mode 100644 drivers/soc/alpine/udma/Makefile
create mode 100644 drivers/soc/alpine/udma/al_udma_config.c
create mode 100644 drivers/soc/alpine/udma/al_udma_iofic.c
create mode 100644 drivers/soc/alpine/udma/al_udma_main.c
create mode 100644 drivers/soc/alpine/udma/al_udma_queue.c
create mode 100644 include/linux/soc/alpine/al_hw_udma.h
create mode 100644 include/linux/soc/alpine/al_hw_udma_config.h
create mode 100644 include/linux/soc/alpine/al_hw_udma_iofic.h
create mode 100644 include/linux/soc/alpine/al_hw_udma_regs.h
create mode 100644 include/linux/soc/alpine/al_hw_udma_regs_m2s.h
create mode 100644 include/linux/soc/alpine/al_hw_udma_regs_s2m.h
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index f31bceb69c0d..010b4c60cd1a 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -1,5 +1,6 @@
menu "SOC (System On Chip) specific Drivers"
+source "drivers/soc/alpine/Kconfig"
source "drivers/soc/bcm/Kconfig"
source "drivers/soc/fsl/Kconfig"
source "drivers/soc/mediatek/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 50c23d0bd457..8385e0108630 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -2,6 +2,7 @@
# Makefile for the Linux Kernel SOC specific device drivers.
#
+obj-$(CONFIG_ARCH_ALPINE) += alpine/
obj-y += bcm/
obj-$(CONFIG_ARCH_DOVE) += dove/
obj-$(CONFIG_MACH_DOVE) += dove/
diff --git a/drivers/soc/alpine/Kconfig b/drivers/soc/alpine/Kconfig
new file mode 100644
index 000000000000..d09df30ff723
--- /dev/null
+++ b/drivers/soc/alpine/Kconfig
@@ -0,0 +1,11 @@
+if ARCH_ALPINE
+
+config ALPINE_UDMA
+ bool "Alpine UDMA engine"
+ default ARCH_ALPINE
+ help
+ Say y here to enable the Alpine Universal DMA support. This UDMA
+ interfaces with the i/o fabric through a PCI endpoint for each
+ i/o fabric bus.
+
+endif
diff --git a/drivers/soc/alpine/Makefile b/drivers/soc/alpine/Makefile
new file mode 100644
index 000000000000..0d3769b342a8
--- /dev/null
+++ b/drivers/soc/alpine/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_ALPINE_UDMA) += udma/
diff --git a/drivers/soc/alpine/udma/Makefile b/drivers/soc/alpine/udma/Makefile
new file mode 100644
index 000000000000..b5915454a3f3
--- /dev/null
+++ b/drivers/soc/alpine/udma/Makefile
@@ -0,0 +1 @@
+obj-y += al_udma_main.o al_udma_iofic.o al_udma_config.o al_udma_queue.o
diff --git a/drivers/soc/alpine/udma/al_udma_config.c b/drivers/soc/alpine/udma/al_udma_config.c
new file mode 100644
index 000000000000..2471195fd7bd
--- /dev/null
+++ b/drivers/soc/alpine/udma/al_udma_config.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2017, Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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/device.h>
+#include <linux/soc/alpine/al_hw_udma_config.h>
+#include <linux/soc/alpine/al_hw_udma_regs.h>
+
+/* M2S packet len configuration */
+int al_udma_m2s_packet_size_cfg_set(struct al_udma *udma,
+ struct al_udma_m2s_pkt_len_conf *conf)
+{
+ u32 reg = readl(&udma->udma_regs->m2s.m2s.cfg_len);
+ u32 max_supported_size = UDMA_M2S_CFG_LEN_MAX_PKT_SIZE_MASK;
+
+ WARN_ON(udma->type != UDMA_TX);
+
+ if (conf->encode_64k_as_zero)
+ max_supported_size += 1;
+
+ if (conf->max_pkt_size > max_supported_size) {
+ dev_err(udma->dev,
+ "udma [%s]: requested max_pkt_size (0x%x) exceeds the supported limit (0x%x)\n",
+ udma->name, conf->max_pkt_size, max_supported_size);
+ return -EINVAL;
+ }
+
+ reg &= ~UDMA_M2S_CFG_LEN_ENCODE_64K;
+ if (conf->encode_64k_as_zero)
+ reg |= UDMA_M2S_CFG_LEN_ENCODE_64K;
+ else
+ reg &= ~UDMA_M2S_CFG_LEN_ENCODE_64K;
+
+ reg &= ~UDMA_M2S_CFG_LEN_MAX_PKT_SIZE_MASK;
+ reg |= conf->max_pkt_size;
+
+ writel(reg, &udma->udma_regs->m2s.m2s.cfg_len);
+
+ return 0;
+}
+
+/* set max descriptors */
+void al_udma_m2s_max_descs_set(struct al_udma *udma, u8 max_descs)
+{
+ u32 pref_thr = max_descs, min_burst_above_thr = 4, tmp;
+
+ /*
+ * increase min_burst_above_thr so larger burst can be used to fetch
+ * descriptors
+ */
+ if (pref_thr >= 8)
+ min_burst_above_thr = 8;
+ /*
+ * don't set prefetch threshold too low so we can have the
+ * min_burst_above_thr >= 4
+ */
+ else
+ pref_thr = 4;
+
+ tmp = readl(&udma->udma_regs->m2s.m2s_rd.desc_pref_cfg_2);
+ tmp &= ~UDMA_M2S_RD_DESC_PREF_CFG_2_MAX_DESC_PER_PKT_MASK;
+ tmp |= max_descs << UDMA_M2S_RD_DESC_PREF_CFG_2_MAX_DESC_PER_PKT_SHIFT;
+ writel(tmp, &udma->udma_regs->m2s.m2s_rd.desc_pref_cfg_2);
+
+ tmp = readl(&udma->udma_regs->m2s.m2s_rd.desc_pref_cfg_3);
+ tmp &= ~(UDMA_M2S_RD_DESC_PREF_CFG_3_PREF_THR_MASK |
+ UDMA_M2S_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_MASK);
+ tmp |= pref_thr << UDMA_M2S_RD_DESC_PREF_CFG_3_PREF_THR_SHIFT;
+ tmp |= min_burst_above_thr << UDMA_M2S_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_SHIFT;
+ writel(tmp, &udma->udma_regs->m2s.m2s_rd.desc_pref_cfg_3);
+}
+
+/* set s2m max descriptors */
+void al_udma_s2m_max_descs_set(struct al_udma *udma, u8 max_descs)
+{
+ u32 pref_thr = max_descs, min_burst_above_thr = 4, tmp;
+
+ /*
+ * increase min_burst_above_thr so larger burst can be used to fetch
+ * descriptors
+ */
+ if (pref_thr >= 8)
+ min_burst_above_thr = 8;
+ /*
+ * don't set prefetch threshold too low so we can have the
+ * min_burst_above_thr >= 4
+ */
+ else
+ pref_thr = 4;
+
+ tmp = readl(&udma->udma_regs->s2m.s2m_rd.desc_pref_cfg_3);
+ tmp &= ~(UDMA_S2M_RD_DESC_PREF_CFG_3_PREF_THR_MASK |
+ UDMA_S2M_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_MASK);
+ tmp |= pref_thr << UDMA_S2M_RD_DESC_PREF_CFG_3_PREF_THR_SHIFT;
+ tmp |= min_burst_above_thr << UDMA_S2M_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_SHIFT;
+ writel(tmp, &udma->udma_regs->s2m.s2m_rd.desc_pref_cfg_3);
+}
+
+/* S2M UDMA configure a queue's completion descriptors coalescing */
+void al_udma_s2m_q_compl_coal_config(struct al_udma_q *udma_q, bool enable,
+ u32 coal_timeout)
+{
+ u32 reg = readl(&udma_q->q_regs->s2m_q.comp_cfg);
+
+ if (enable)
+ reg &= ~UDMA_S2M_Q_COMP_CFG_DIS_COMP_COAL;
+ else
+ reg |= UDMA_S2M_Q_COMP_CFG_DIS_COMP_COAL;
+
+ writel(reg, &udma_q->q_regs->s2m_q.comp_cfg);
+ writel(coal_timeout, &udma_q->q_regs->s2m_q.comp_cfg_2);
+}
+
+/* S2M UDMA configure completion descriptors write burst parameters */
+int al_udma_s2m_compl_desc_burst_config(struct al_udma *udma, u16 burst_size)
+{
+ u32 tmp;
+
+ if ((burst_size != 64) && (burst_size != 128) && (burst_size != 256)) {
+ dev_err(udma->dev, "invalid burst_size value (%d)\n",
+ burst_size);
+ return -EINVAL;
+ }
+
+ /* convert burst size from bytes to beats (16 byte) */
+ burst_size = burst_size / 16;
+
+ tmp = readl(&udma->udma_regs->s2m.axi_s2m.desc_wr_cfg_1);
+ tmp &= ~(UDMA_AXI_S2M_DESC_WR_CFG_1_MIN_AXI_BEATS_MASK |
+ UDMA_AXI_S2M_DESC_WR_CFG_1_MAX_AXI_BEATS_MASK);
+ tmp |= burst_size << UDMA_AXI_S2M_DESC_WR_CFG_1_MIN_AXI_BEATS_SHIFT;
+ tmp |= burst_size << UDMA_AXI_S2M_DESC_WR_CFG_1_MAX_AXI_BEATS_SHIFT;
+ writel(tmp, &udma->udma_regs->s2m.axi_s2m.desc_wr_cfg_1);
+
+ return 0;
+}
diff --git a/drivers/soc/alpine/udma/al_udma_iofic.c b/drivers/soc/alpine/udma/al_udma_iofic.c
new file mode 100644
index 000000000000..cf4f27d21b61
--- /dev/null
+++ b/drivers/soc/alpine/udma/al_udma_iofic.c
@@ -0,0 +1,110 @@
+/*
+ * Annapurna Labs UDMA-specific IOFIC helpers
+ *
+ * Copyright (C) 2017, Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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/soc/alpine/al_hw_udma_iofic.h>
+#include <linux/soc/alpine/al_hw_udma_regs.h>
+
+/* configure the interrupt registers, interrupts will are kept masked */
+static int al_udma_main_iofic_config(void __iomem *iofic,
+ enum al_iofic_mode mode)
+{
+ switch (mode) {
+ case AL_IOFIC_MODE_LEGACY:
+ al_iofic_config(iofic, AL_INT_GROUP_A,
+ INT_CONTROL_GRP_SET_ON_POSEDGE |
+ INT_CONTROL_GRP_MASK_MSI_X |
+ INT_CONTROL_GRP_CLEAR_ON_READ);
+ al_iofic_config(iofic, AL_INT_GROUP_B,
+ INT_CONTROL_GRP_CLEAR_ON_READ |
+ INT_CONTROL_GRP_MASK_MSI_X);
+ al_iofic_config(iofic, AL_INT_GROUP_C,
+ INT_CONTROL_GRP_CLEAR_ON_READ |
+ INT_CONTROL_GRP_MASK_MSI_X);
+ al_iofic_config(iofic, AL_INT_GROUP_D,
+ INT_CONTROL_GRP_SET_ON_POSEDGE |
+ INT_CONTROL_GRP_MASK_MSI_X |
+ INT_CONTROL_GRP_CLEAR_ON_READ);
+ break;
+ case AL_IOFIC_MODE_MSIX_PER_Q:
+ al_iofic_config(iofic, AL_INT_GROUP_A,
+ INT_CONTROL_GRP_SET_ON_POSEDGE |
+ INT_CONTROL_GRP_AUTO_MASK |
+ INT_CONTROL_GRP_AUTO_CLEAR);
+ al_iofic_config(iofic, AL_INT_GROUP_B,
+ INT_CONTROL_GRP_AUTO_CLEAR |
+ INT_CONTROL_GRP_AUTO_MASK |
+ INT_CONTROL_GRP_CLEAR_ON_READ);
+ al_iofic_config(iofic, AL_INT_GROUP_C,
+ INT_CONTROL_GRP_AUTO_CLEAR |
+ INT_CONTROL_GRP_AUTO_MASK |
+ INT_CONTROL_GRP_CLEAR_ON_READ);
+ al_iofic_config(iofic, AL_INT_GROUP_D,
+ INT_CONTROL_GRP_SET_ON_POSEDGE |
+ INT_CONTROL_GRP_CLEAR_ON_READ |
+ INT_CONTROL_GRP_MASK_MSI_X);
+ break;
+ case AL_IOFIC_MODE_MSIX_PER_GROUP:
+ al_iofic_config(iofic, AL_INT_GROUP_A,
+ INT_CONTROL_GRP_SET_ON_POSEDGE |
+ INT_CONTROL_GRP_AUTO_CLEAR |
+ INT_CONTROL_GRP_AUTO_MASK);
+ al_iofic_config(iofic, AL_INT_GROUP_B,
+ INT_CONTROL_GRP_CLEAR_ON_READ |
+ INT_CONTROL_GRP_MASK_MSI_X);
+ al_iofic_config(iofic, AL_INT_GROUP_C,
+ INT_CONTROL_GRP_CLEAR_ON_READ |
+ INT_CONTROL_GRP_MASK_MSI_X);
+ al_iofic_config(iofic, AL_INT_GROUP_D,
+ INT_CONTROL_GRP_SET_ON_POSEDGE |
+ INT_CONTROL_GRP_CLEAR_ON_READ |
+ INT_CONTROL_GRP_MASK_MSI_X);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* configure the UDMA interrupt registers, interrupts are kept masked */
+int al_udma_iofic_config(struct unit_regs __iomem *regs,
+ enum al_iofic_mode mode, u32 m2s_errors_disable,
+ u32 m2s_aborts_disable, u32 s2m_errors_disable,
+ u32 s2m_aborts_disable)
+{
+ int rc;
+
+ rc = al_udma_main_iofic_config(®s->gen.interrupt_regs.main_iofic,
+ mode);
+ if (rc)
+ return rc;
+
+ al_iofic_unmask(®s->gen.interrupt_regs.secondary_iofic_ctrl,
+ AL_INT_GROUP_A, ~m2s_errors_disable);
+ al_iofic_abort_mask(®s->gen.interrupt_regs.secondary_iofic_ctrl,
+ AL_INT_GROUP_A, m2s_aborts_disable);
+
+ al_iofic_unmask(®s->gen.interrupt_regs.secondary_iofic_ctrl,
+ AL_INT_GROUP_B, ~s2m_errors_disable);
+ al_iofic_abort_mask(®s->gen.interrupt_regs.secondary_iofic_ctrl,
+ AL_INT_GROUP_B, s2m_aborts_disable);
+
+ return 0;
+}
+
+/* returns the offset of the unmask register for a given group */
+u32 __iomem *al_udma_iofic_unmask_offset_get(struct unit_regs __iomem *regs,
+ enum al_udma_iofic_level level,
+ int group)
+{
+ WARN_ON(!al_udma_iofic_level_and_group_valid(level, group));
+ return al_iofic_unmask_offset_get(
+ al_udma_iofic_reg_base_get(regs, level), group);
+}
diff --git a/drivers/soc/alpine/udma/al_udma_main.c b/drivers/soc/alpine/udma/al_udma_main.c
new file mode 100644
index 000000000000..86a294d65495
--- /dev/null
+++ b/drivers/soc/alpine/udma/al_udma_main.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2017, Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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/delay.h>
+#include <linux/device.h>
+#include <linux/soc/alpine/al_hw_udma.h>
+#include <linux/soc/alpine/al_hw_udma_config.h>
+
+#define UDMA_STATE_IDLE 0x0
+#define UDMA_STATE_NORMAL 0x1
+#define UDMA_STATE_ABORT 0x2
+#define UDMA_STATE_RESERVED 0x3
+
+const char *const al_udma_states_name[] = {
+ "Idle",
+ "Normal",
+ "Abort",
+ "Reset"
+};
+
+#define AL_ADDR_LOW(x) ((u32)((dma_addr_t)(x)))
+#define AL_ADDR_HIGH(x) ((u32)((((dma_addr_t)(x)) >> 16) >> 16))
+
+static void al_udma_set_defaults(struct al_udma *udma)
+{
+ u8 rev_id = udma->rev_id;
+ u32 tmp;
+
+ if (udma->type == UDMA_TX) {
+ struct unit_regs *tmp_unit_regs =
+ (struct unit_regs *)udma->udma_regs;
+
+ /*
+ * Setting the data fifo depth to 4K (256 strips of 16B)
+ * This allows the UDMA to have 16 outstanding writes
+ */
+ if (rev_id >= AL_UDMA_REV_ID_2) {
+ tmp = readl(&tmp_unit_regs->m2s.m2s_rd.data_cfg);
+ tmp &= ~UDMA_M2S_RD_DATA_CFG_DATA_FIFO_DEPTH_MASK;
+ tmp |= 256 << UDMA_M2S_RD_DATA_CFG_DATA_FIFO_DEPTH_SHIFT;
+ writel(tmp, &tmp_unit_regs->m2s.m2s_rd.data_cfg);
+ }
+
+ /* set AXI timeout to 1M (~2.6 ms) */
+ writel(1000000, &tmp_unit_regs->gen.axi.cfg_1);
+
+ writel(0, &tmp_unit_regs->m2s.m2s_comp.cfg_application_ack);
+ }
+
+ if (udma->type == UDMA_RX)
+ writel(0, &udma->udma_regs->s2m.s2m_comp.cfg_application_ack);
+}
+
+static void al_udma_config_compl(struct al_udma *udma)
+{
+ u32 val;
+
+ if (udma->type != UDMA_RX)
+ return;
+
+ val = readl(&udma->udma_regs->s2m.s2m_comp.cfg_1c);
+ val &= ~UDMA_S2M_COMP_CFG_1C_DESC_SIZE_MASK;
+ val |= (udma->cdesc_size >> 2) & UDMA_S2M_COMP_CFG_1C_DESC_SIZE_MASK;
+ writel(val, &udma->udma_regs->s2m.s2m_comp.cfg_1c);
+}
+
+/* Initialize the udma engine */
+int al_udma_init(struct al_udma *udma, struct al_udma_params *udma_params)
+{
+ int i;
+
+ udma->dev = udma_params->dev;
+
+ if (udma_params->num_of_queues > DMA_MAX_Q) {
+ dev_err(udma->dev, "udma: invalid num_of_queues parameter\n");
+ return -EINVAL;
+ }
+
+ udma->type = udma_params->type;
+ udma->num_of_queues = udma_params->num_of_queues;
+ udma->cdesc_size = udma_params->cdesc_size;
+ udma->gen_regs = &udma_params->udma_regs_base->gen;
+
+ if (udma->type == UDMA_TX)
+ udma->udma_regs = (union udma_regs *)&udma_params->udma_regs_base->m2s;
+ else
+ udma->udma_regs = (union udma_regs *)&udma_params->udma_regs_base->s2m;
+
+ udma->rev_id = al_udma_get_revision(udma_params->udma_regs_base);
+
+ if (udma_params->name == NULL)
+ udma->name = "";
+ else
+ udma->name = udma_params->name;
+
+ udma->state = UDMA_DISABLE;
+ for (i = 0; i < DMA_MAX_Q; i++) {
+ udma->udma_q[i].status = AL_QUEUE_NOT_INITIALIZED;
+ }
+
+ /*
+ * the register expects it to be in words
+ * initialize configuration registers to correct values
+ */
+ al_udma_set_defaults(udma);
+
+ al_udma_config_compl(udma);
+
+ dev_dbg(udma->dev, "udma [%s] initialized. base %p\n", udma->name,
+ udma->udma_regs);
+
+ return 0;
+}
+
+/* Change the UDMA's state */
+void al_udma_state_set(struct al_udma *udma, enum al_udma_state state)
+{
+ u32 reg = 0;
+
+ if (state == udma->state)
+ dev_dbg(udma->dev,
+ "udma [%s]: requested state identical to current state (%d)\n",
+ udma->name, state);
+ else
+ dev_dbg(udma->dev, "udma [%s]: change state from (%s) to (%s)\n",
+ udma->name, al_udma_states_name[udma->state],
+ al_udma_states_name[state]);
+
+ switch (state) {
+ case UDMA_DISABLE:
+ reg |= UDMA_M2S_CHANGE_STATE_DIS;
+ break;
+ case UDMA_NORMAL:
+ reg |= UDMA_M2S_CHANGE_STATE_NORMAL;
+ break;
+ case UDMA_ABORT:
+ reg |= UDMA_M2S_CHANGE_STATE_ABORT;
+ break;
+ default:
+ dev_err(udma->dev, "udma: invalid state (%d)\n", state);
+ return;
+ }
+
+ if (udma->type == UDMA_TX)
+ writel(reg, &udma->udma_regs->m2s.m2s.change_state);
+ else
+ writel(reg, &udma->udma_regs->s2m.s2m.change_state);
+
+ udma->state = state;
+}
+
+/* returns the current UDMA hardware state */
+enum al_udma_state al_udma_state_get(struct al_udma *udma)
+{
+ u32 state_reg;
+ u32 comp_ctrl;
+ u32 stream_if;
+ u32 data_rd;
+ u32 desc_pref;
+
+ if (udma->type == UDMA_TX)
+ state_reg = readl(&udma->udma_regs->m2s.m2s.state);
+ else
+ state_reg = readl(&udma->udma_regs->s2m.s2m.state);
+
+ comp_ctrl = (state_reg & UDMA_M2S_STATE_COMP_CTRL_MASK) >>
+ UDMA_M2S_STATE_COMP_CTRL_SHIFT;
+ stream_if = (state_reg & UDMA_M2S_STATE_STREAM_IF_MASK) >>
+ UDMA_M2S_STATE_STREAM_IF_SHIFT;
+ data_rd = (state_reg & UDMA_M2S_STATE_DATA_RD_CTRL_MASK) >>
+ UDMA_M2S_STATE_DATA_RD_CTRL_SHIFT;
+ desc_pref = (state_reg & UDMA_M2S_STATE_DESC_PREF_MASK) >>
+ UDMA_M2S_STATE_DESC_PREF_SHIFT;
+
+ /* if any of the states is abort then return abort */
+ if ((comp_ctrl == UDMA_STATE_ABORT) || (stream_if == UDMA_STATE_ABORT)
+ || (data_rd == UDMA_STATE_ABORT)
+ || (desc_pref == UDMA_STATE_ABORT))
+ return UDMA_ABORT;
+
+ /* if any of the states is normal then return normal */
+ if ((comp_ctrl == UDMA_STATE_NORMAL)
+ || (stream_if == UDMA_STATE_NORMAL)
+ || (data_rd == UDMA_STATE_NORMAL)
+ || (desc_pref == UDMA_STATE_NORMAL))
+ return UDMA_NORMAL;
+
+ return UDMA_IDLE;
+}
+
+/* get next completed packet from completion ring of the queue */
+u32 al_udma_cdesc_packet_get(struct al_udma_q *udma_q,
+ volatile union al_udma_cdesc **cdesc)
+{
+ u32 count;
+ volatile union al_udma_cdesc *curr;
+ u32 comp_flags;
+
+ /* comp_head points to the last comp desc that was processed */
+ curr = udma_q->comp_head_ptr;
+ comp_flags = le32_to_cpu(curr->al_desc_comp_tx.ctrl_meta);
+
+ /* check if the completion descriptor is new */
+ if (unlikely(!al_udma_new_cdesc(udma_q, comp_flags)))
+ return 0;
+
+ count = udma_q->pkt_crnt_descs + 1;
+
+ /* if new desc found, increment the current packets descriptors */
+ while (!cdesc_is_last(comp_flags)) {
+ curr = al_cdesc_next_update(udma_q, curr);
+ comp_flags = le32_to_cpu(curr->al_desc_comp_tx.ctrl_meta);
+
+ if (unlikely(!al_udma_new_cdesc(udma_q, comp_flags))) {
+ /*
+ * The current packet here doesn't have all
+ * descriptors completed. log the current desc
+ * location and number of completed descriptors so
+ * far. Then return.
+ */
+ udma_q->pkt_crnt_descs = count;
+ udma_q->comp_head_ptr = curr;
+
+ return 0;
+ }
+ count++;
+ }
+
+ /* return back the first descriptor of the packet */
+ *cdesc = al_udma_cdesc_idx_to_ptr(udma_q, udma_q->next_cdesc_idx);
+ udma_q->pkt_crnt_descs = 0;
+ udma_q->comp_head_ptr = al_cdesc_next_update(udma_q, curr);
+
+ dev_dbg(udma_q->udma->dev,
+ "udma [%s %d]: packet completed. first desc %p (ixd 0x%x) descs %d\n",
+ udma_q->udma->name, udma_q->qid, *cdesc, udma_q->next_cdesc_idx,
+ count);
+
+ return count;
+}
diff --git a/drivers/soc/alpine/udma/al_udma_queue.c b/drivers/soc/alpine/udma/al_udma_queue.c
new file mode 100644
index 000000000000..b5ca082faac6
--- /dev/null
+++ b/drivers/soc/alpine/udma/al_udma_queue.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2017, Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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/delay.h>
+#include <linux/device.h>
+#include <linux/soc/alpine/al_hw_udma.h>
+#include <linux/soc/alpine/al_hw_udma_config.h>
+
+/* dma_q flags */
+#define AL_UDMA_Q_FLAGS_IGNORE_RING_ID BIT(0)
+#define AL_UDMA_Q_FLAGS_NO_COMP_UPDATE BIT(1)
+#define AL_UDMA_Q_FLAGS_EN_COMP_COAL BIT(2)
+
+#define AL_UDMA_INITIAL_RING_ID 1
+
+#define AL_ADDR_LOW(x) ((u32)((dma_addr_t)(x)))
+#define AL_ADDR_HIGH(x) ((u32)((((dma_addr_t)(x)) >> 16) >> 16))
+
+/*
+ * misc queue configurations
+ *
+ * @param udma_q udma queue data structure
+ */
+static void al_udma_q_config(struct al_udma_q *udma_q)
+{
+ u32 *reg_addr;
+ u32 val;
+
+ if (udma_q->udma->type == UDMA_TX) {
+ reg_addr = &udma_q->q_regs->m2s_q.rlimit.mask;
+
+ /* enable DMB */
+ val = readl(reg_addr);
+ val &= ~UDMA_M2S_Q_RATE_LIMIT_MASK_INTERNAL_PAUSE_DMB;
+ writel(val, reg_addr);
+ }
+}
+
+/*
+ * set the queue's completion configuration register
+ *
+ * @param udma_q udma queue data structure
+ */
+static void al_udma_q_config_compl(struct al_udma_q *udma_q)
+{
+ u32 *reg_addr;
+ u32 val;
+
+ if (udma_q->udma->type == UDMA_TX)
+ reg_addr = &udma_q->q_regs->m2s_q.comp_cfg;
+ else
+ reg_addr = &udma_q->q_regs->s2m_q.comp_cfg;
+
+ val = readl(reg_addr);
+
+ if (udma_q->flags & AL_UDMA_Q_FLAGS_NO_COMP_UPDATE)
+ val &= ~UDMA_M2S_Q_COMP_CFG_EN_COMP_RING_UPDATE;
+ else
+ val |= UDMA_M2S_Q_COMP_CFG_EN_COMP_RING_UPDATE;
+
+ if (udma_q->flags & AL_UDMA_Q_FLAGS_EN_COMP_COAL)
+ val &= ~UDMA_M2S_Q_COMP_CFG_DIS_COMP_COAL;
+ else
+ val |= UDMA_M2S_Q_COMP_CFG_DIS_COMP_COAL;
+
+ writel(val, reg_addr);
+}
+
+/*
+ * reset the queues pointers (Head, Tail, etc) and set the base addresses
+ *
+ * @param udma_q udma queue data structure
+ */
+static void al_udma_q_set_pointers(struct al_udma_q *udma_q)
+{
+ /*
+ * reset the descriptors ring pointers
+ * assert descriptor base address aligned.
+ */
+ WARN_ON((AL_ADDR_LOW(udma_q->desc_phy_base) &
+ ~UDMA_M2S_Q_TDRBP_LOW_ADDR_MASK) != 0);
+ writel(AL_ADDR_LOW(udma_q->desc_phy_base),
+ &udma_q->q_regs->rings.drbp_low);
+ writel(AL_ADDR_HIGH(udma_q->desc_phy_base),
+ &udma_q->q_regs->rings.drbp_high);
+
+ writel(udma_q->size, &udma_q->q_regs->rings.drl);
+
+ /* if completion ring update disabled */
+ if (udma_q->cdesc_base_ptr == NULL) {
+ udma_q->flags |= AL_UDMA_Q_FLAGS_NO_COMP_UPDATE;
+ } else {
+ /*
+ * reset the completion descriptors ring pointers
+ * assert completion base address aligned.
+ */
+ WARN_ON((AL_ADDR_LOW(udma_q->cdesc_phy_base) & ~UDMA_M2S_Q_TCRBP_LOW_ADDR_MASK) != 0);
+ writel(AL_ADDR_LOW(udma_q->cdesc_phy_base),
+ &udma_q->q_regs->rings.crbp_low);
+ writel(AL_ADDR_HIGH(udma_q->cdesc_phy_base),
+ &udma_q->q_regs->rings.crbp_high);
+ }
+ al_udma_q_config_compl(udma_q);
+}
+
+/*
+ * enable/disable udma queue
+ *
+ * @param udma_q udma queue data structure
+ * @param enable none zero value enables the queue, zero means disable
+ */
+static void al_udma_q_enable(struct al_udma_q *udma_q, int enable)
+{
+ u32 reg = readl(&udma_q->q_regs->rings.cfg);
+
+ if (enable) {
+ reg |= (UDMA_M2S_Q_CFG_EN_PREF | UDMA_M2S_Q_CFG_EN_SCHEDULING);
+ udma_q->status = AL_QUEUE_ENABLED;
+ } else {
+ reg &= ~(UDMA_M2S_Q_CFG_EN_PREF | UDMA_M2S_Q_CFG_EN_SCHEDULING);
+ udma_q->status = AL_QUEUE_DISABLED;
+ }
+
+ writel(reg, &udma_q->q_regs->rings.cfg);
+}
+
+/* Initialize the udma queue data structure */
+int al_udma_q_init(struct al_udma *udma, u32 qid,
+ struct al_udma_q_params *q_params)
+{
+ struct al_udma_q *udma_q;
+
+ if (qid >= udma->num_of_queues) {
+ dev_err(udma->dev, "udma: invalid queue id (%d)\n", qid);
+ return -EINVAL;
+ }
+
+ if (udma->udma_q[qid].status == AL_QUEUE_ENABLED) {
+ dev_err(udma->dev, "udma: queue (%d) already enabled!\n", qid);
+ return -EIO;
+ }
+
+ if (q_params->size < AL_UDMA_MIN_Q_SIZE) {
+ dev_err(udma->dev, "udma: queue (%d) size too small\n", qid);
+ return -EINVAL;
+ }
+
+ if (q_params->size > AL_UDMA_MAX_Q_SIZE) {
+ dev_err(udma->dev, "udma: queue (%d) size too large\n", qid);
+ return -EINVAL;
+ }
+
+ if (q_params->size & (q_params->size - 1)) {
+ dev_err(udma->dev,
+ "udma: queue (%d) size (%d) must be power of 2\n",
+ q_params->size, qid);
+ return -EINVAL;
+ }
+
+ udma_q = &udma->udma_q[qid];
+ /* set the queue's regs base address */
+ if (udma->type == UDMA_TX)
+ udma_q->q_regs =
+ (union udma_q_regs __iomem *)&udma->udma_regs->m2s.m2s_q[qid];
+ else
+ udma_q->q_regs =
+ (union udma_q_regs __iomem *)&udma->udma_regs->s2m.s2m_q[qid];
+
+ udma_q->adapter_rev_id = q_params->adapter_rev_id;
+ udma_q->size = q_params->size;
+ udma_q->size_mask = q_params->size - 1;
+ udma_q->desc_base_ptr = q_params->desc_base;
+ udma_q->desc_phy_base = q_params->desc_phy_base;
+ udma_q->cdesc_base_ptr = q_params->cdesc_base;
+ udma_q->cdesc_phy_base = q_params->cdesc_phy_base;
+
+ udma_q->next_desc_idx = 0;
+ udma_q->next_cdesc_idx = 0;
+ udma_q->end_cdesc_ptr = (u8 *) udma_q->cdesc_base_ptr +
+ (udma_q->size - 1) * udma->cdesc_size;
+ udma_q->comp_head_idx = 0;
+ udma_q->comp_head_ptr = (union al_udma_cdesc *)udma_q->cdesc_base_ptr;
+ udma_q->desc_ring_id = AL_UDMA_INITIAL_RING_ID;
+ udma_q->comp_ring_id = AL_UDMA_INITIAL_RING_ID;
+
+ udma_q->pkt_crnt_descs = 0;
+ udma_q->flags = 0;
+ udma_q->status = AL_QUEUE_DISABLED;
+ udma_q->udma = udma;
+ udma_q->qid = qid;
+
+ /* start hardware configuration: */
+ al_udma_q_config(udma_q);
+ /* reset the queue pointers */
+ al_udma_q_set_pointers(udma_q);
+
+ /* enable the q */
+ al_udma_q_enable(udma_q, 1);
+
+ dev_dbg(udma->dev,
+ "udma [%s %d]: %s q init. size 0x%x\n desc ring info: phys base 0x%llx virt base %p)",
+ udma_q->udma->name, udma_q->qid,
+ udma->type == UDMA_TX ? "Tx" : "Rx", q_params->size,
+ (unsigned long long)q_params->desc_phy_base,
+ q_params->desc_base);
+ dev_dbg(udma->dev,
+ " cdesc ring info: phys base 0x%llx virt base %p",
+ (unsigned long long)q_params->cdesc_phy_base,
+ q_params->cdesc_base);
+
+ return 0;
+}
+
+/* return (by reference) a pointer to a specific queue date structure. */
+int al_udma_q_handle_get(struct al_udma *udma, u32 qid,
+ struct al_udma_q **q_handle)
+{
+
+ if (unlikely(qid >= udma->num_of_queues)) {
+ dev_err(udma->dev, "udma [%s]: invalid queue id (%d)\n",
+ udma->name, qid);
+ return -EINVAL;
+ }
+
+ *q_handle = &udma->udma_q[qid];
+ return 0;
+}
diff --git a/include/linux/soc/alpine/al_hw_udma.h b/include/linux/soc/alpine/al_hw_udma.h
new file mode 100644
index 000000000000..3a428a8daedc
--- /dev/null
+++ b/include/linux/soc/alpine/al_hw_udma.h
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2017, Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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 __AL_HW_UDMA_H__
+#define __AL_HW_UDMA_H__
+
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/types.h>
+
+#include "al_hw_udma_regs.h"
+
+#define DMA_MAX_Q 4
+#define AL_UDMA_MIN_Q_SIZE 4
+#define AL_UDMA_MAX_Q_SIZE BIT(16) /* hw can do more, but we limit it */
+
+#define AL_UDMA_REV_ID_2 2
+
+#define DMA_RING_ID_MASK 0x3
+/* New registers ?? */
+/* Statistics - TBD */
+
+/* UDMA submission descriptor */
+union al_udma_desc {
+ /* TX */
+ struct {
+ u32 len_ctrl;
+ u32 meta_ctrl;
+ u64 buf_ptr;
+ } tx;
+ /* TX Meta, used by upper layer */
+ struct {
+ u32 len_ctrl;
+ u32 meta_ctrl;
+ u32 meta1;
+ u32 meta2;
+ } tx_meta;
+ /* RX */
+ struct {
+ u32 len_ctrl;
+ u32 buf2_ptr_lo;
+ u64 buf1_ptr;
+ } rx;
+} __attribute__((aligned(16)));
+
+/* TX desc length and control fields */
+
+#define AL_M2S_DESC_CONCAT BIT(31)
+#define AL_M2S_DESC_NO_SNOOP_H BIT(29)
+#define AL_M2S_DESC_INT_EN BIT(28)
+#define AL_M2S_DESC_LAST BIT(27)
+#define AL_M2S_DESC_FIRST BIT(26)
+#define AL_M2S_DESC_RING_ID_SHIFT 24
+#define AL_M2S_DESC_RING_ID_MASK (0x3 << AL_M2S_DESC_RING_ID_SHIFT)
+#define AL_M2S_DESC_META_DATA BIT(23)
+#define AL_M2S_DESC_LEN_SHIFT 0
+#define AL_M2S_DESC_LEN_MASK (0xfffff << AL_M2S_DESC_LEN_SHIFT)
+
+#define AL_S2M_DESC_DUAL_BUF BIT(31)
+#define AL_S2M_DESC_RING_ID_SHIFT 24
+#define AL_S2M_DESC_LEN_SHIFT 0
+#define AL_S2M_DESC_LEN_MASK (0xffff << AL_S2M_DESC_LEN_SHIFT)
+#define AL_S2M_DESC_LEN2_SHIFT 16
+#define AL_S2M_DESC_LEN2_MASK (0x3fff << AL_S2M_DESC_LEN2_SHIFT)
+#define AL_S2M_DESC_LEN2_GRANULARITY_SHIFT 6
+
+/* TX/RX descriptor Target-ID field (in the buffer address 64 bit field) */
+#define AL_UDMA_DESC_TGTID_SHIFT 48
+
+/* UDMA completion descriptor */
+union al_udma_cdesc {
+ /* TX completion */
+ struct {
+ u32 ctrl_meta;
+ } al_desc_comp_tx;
+ /* RX completion */
+ struct {
+ u32 ctrl_meta;
+ } al_desc_comp_rx;
+} __attribute__((aligned(4)));
+
+/* TX/RX common completion desc ctrl_meta feilds */
+#define AL_UDMA_CDESC_ERROR BIT(31)
+#define AL_UDMA_CDESC_LAST BIT(27)
+#define AL_UDMA_CDESC_BUF2_USED BIT(31)
+
+/* Basic Buffer structure */
+struct al_buf {
+ /* Buffer physical address */
+ dma_addr_t addr;
+ /* Buffer lenght in bytes */
+ u32 len;
+};
+
+/* UDMA type */
+enum al_udma_type {
+ UDMA_TX,
+ UDMA_RX
+};
+
+/* UDMA state */
+enum al_udma_state {
+ UDMA_DISABLE = 0,
+ UDMA_IDLE,
+ UDMA_NORMAL,
+ UDMA_ABORT,
+ UDMA_RESET
+};
+
+extern const char *const al_udma_states_name[];
+
+/* UDMA Q specific parameters from upper layer */
+struct al_udma_q_params {
+ /*
+ * ring size (in descriptors), submission and completion rings must have
+ * the same size
+ */
+ u32 size;
+ /* cpu address for submission ring descriptors */
+ union al_udma_desc *desc_base;
+ /* submission ring descriptors physical base address */
+ dma_addr_t desc_phy_base;
+ /* completion descriptors pointer, NULL means no completion update */
+ u8 *cdesc_base;
+ /* completion descriptors ring physical base address */
+ dma_addr_t cdesc_phy_base;
+
+ u8 adapter_rev_id;
+};
+
+/* UDMA parameters from upper layer */
+struct al_udma_params {
+ struct device *dev;
+ struct unit_regs __iomem *udma_regs_base;
+ enum al_udma_type type;
+ /* size (in bytes) of the udma completion ring descriptor */
+ u32 cdesc_size;
+ u8 num_of_queues;
+ const char *name;
+};
+
+/* Fordward decleration */
+struct al_udma;
+
+/* SW status of a queue */
+enum al_udma_queue_status {
+ AL_QUEUE_NOT_INITIALIZED = 0,
+ AL_QUEUE_DISABLED,
+ AL_QUEUE_ENABLED,
+ AL_QUEUE_ABORTED
+};
+
+/* UDMA Queue private data structure */
+struct al_udma_q {
+ /* mask used for pointers wrap around equals to size - 1 */
+ u16 size_mask;
+ /* pointer to the per queue UDMA registers */
+ union udma_q_regs __iomem *q_regs;
+ /* base address submission ring descriptors */
+ union al_udma_desc *desc_base_ptr;
+ /* index to the next available submission descriptor */
+ u16 next_desc_idx;
+ /* current submission ring id */
+ u32 desc_ring_id;
+ /* completion descriptors pointer, NULL means no completion */
+ u8 *cdesc_base_ptr;
+ /* index in descriptors for next completing ring descriptor */
+ u16 next_cdesc_idx;
+ /* used for wrap around detection */
+ u8 *end_cdesc_ptr;
+ /* completion ring head pointer register shadow */
+ u16 comp_head_idx;
+ /*
+ * when working in get_packet mode we maintain pointer instead of the
+ * above id
+ */
+ volatile union al_udma_cdesc *comp_head_ptr;
+
+ /* holds the number of processed descriptors of the current packet */
+ u32 pkt_crnt_descs;
+ /* current completion Ring Id */
+ u32 comp_ring_id;
+
+ dma_addr_t desc_phy_base; /* submission desc. physical base */
+ dma_addr_t cdesc_phy_base; /* completion desc. physical base */
+
+ u32 flags; /* flags used for completion modes */
+ u32 size; /* ring size in descriptors */
+ enum al_udma_queue_status status;
+ struct al_udma *udma; /* pointer to parent UDMA */
+ u32 qid; /* the index number of the queue */
+
+ /*
+ * The following fields are duplicated from the UDMA parent adapter
+ * due to performance considerations.
+ */
+ u8 adapter_rev_id;
+} ____cacheline_aligned;
+
+/* UDMA */
+struct al_udma {
+ const char *name;
+ struct device *dev;
+ enum al_udma_type type; /* Tx or Rx */
+ enum al_udma_state state;
+ /* size (in bytes) of the udma completion ring descriptor */
+ u32 cdesc_size;
+ u8 num_of_queues;
+ union udma_regs __iomem *udma_regs;
+ struct udma_gen_regs *gen_regs;
+ struct al_udma_q udma_q[DMA_MAX_Q];
+ unsigned int rev_id;
+};
+
+/*
+ * Initialize the udma engine
+ *
+ * @param udma udma data structure
+ * @param udma_params udma parameters from upper layer
+ *
+ * @return 0 on success. -EINVAL otherwise.
+ */
+int al_udma_init(struct al_udma *udma, struct al_udma_params *udma_params);
+
+/*
+ * Initialize the udma queue data structure
+ *
+ * @param udma
+ * @param qid
+ * @param q_params
+ *
+ * @return 0 if no error found.
+ * -EINVAL if the qid is out of range
+ * -EIO if queue was already initialized
+ */
+
+int al_udma_q_init(struct al_udma *udma, u32 qid,
+ struct al_udma_q_params *q_params);
+
+/*
+ * return (by reference) a pointer to a specific queue date structure.
+ * this pointer needed for calling functions (i.e. al_udma_desc_action_add) that
+ * require this pointer as input argument.
+ *
+ * @param udma udma data structure
+ * @param qid queue index
+ * @param q_handle pointer to the location where the queue structure pointer
+ * written to.
+ *
+ * @return 0 on success. -EINVAL otherwise.
+ */
+int al_udma_q_handle_get(struct al_udma *udma, u32 qid,
+ struct al_udma_q **q_handle);
+
+/*
+ * Change the UDMA's state
+ *
+ * @param udma udma data structure
+ * @param state the target state
+ */
+void al_udma_state_set(struct al_udma *udma, enum al_udma_state state);
+
+/*
+ * return the current UDMA hardware state
+ *
+ * @param udma udma handle
+ *
+ * @return the UDMA state as reported by the hardware.
+ */
+enum al_udma_state al_udma_state_get(struct al_udma *udma);
+
+/*
+ * Action handling
+ */
+
+/*
+ * get number of descriptors that can be submitted to the udma.
+ * keep one free descriptor to simplify full/empty management
+ * @param udma_q queue handle
+ *
+ * @return num of free descriptors.
+ */
+static inline u32 al_udma_available_get(struct al_udma_q *udma_q)
+{
+ u16 tmp = udma_q->next_cdesc_idx - (udma_q->next_desc_idx + 1);
+ tmp &= udma_q->size_mask;
+
+ return (u32) tmp;
+}
+
+/*
+ * get next available descriptor
+ * @param udma_q queue handle
+ *
+ * @return pointer to the next available descriptor
+ */
+static inline union al_udma_desc *al_udma_desc_get(struct al_udma_q *udma_q)
+{
+ union al_udma_desc *desc;
+ u16 next_desc_idx;
+
+ next_desc_idx = udma_q->next_desc_idx;
+ desc = udma_q->desc_base_ptr + next_desc_idx;
+
+ next_desc_idx++;
+
+ /* if reached end of queue, wrap around */
+ udma_q->next_desc_idx = next_desc_idx & udma_q->size_mask;
+
+ return desc;
+}
+
+/*
+ * get ring id for the last allocated descriptor
+ * @param udma_q
+ *
+ * @return ring id for the last allocated descriptor
+ * this function must be called each time a new descriptor is allocated
+ * by the al_udma_desc_get(), unless ring id is ignored.
+ */
+static inline u32 al_udma_ring_id_get(struct al_udma_q *udma_q)
+{
+ u32 ring_id;
+
+ ring_id = udma_q->desc_ring_id;
+
+ /* calculate the ring id of the next desc */
+ /* if next_desc points to first desc, then queue wrapped around */
+ if (unlikely(udma_q->next_desc_idx) == 0)
+ udma_q->desc_ring_id = (udma_q->desc_ring_id + 1) &
+ DMA_RING_ID_MASK;
+ return ring_id;
+}
+
+/* add DMA action - trigger the engine */
+/*
+ * add num descriptors to the submission queue.
+ *
+ * @param udma_q queue handle
+ * @param num number of descriptors to add to the queues ring.
+ */
+static inline void al_udma_desc_action_add(struct al_udma_q *udma_q, u32 num)
+{
+ u32 *addr;
+
+ addr = &udma_q->q_regs->rings.drtp_inc;
+ /*
+ * make sure data written to the descriptors will be visible by the
+ * DMA
+ */
+ wmb();
+
+ writel_relaxed(num, addr);
+}
+
+#define cdesc_is_last(flags) ((flags) & AL_UDMA_CDESC_LAST)
+
+/*
+ * return pointer to the cdesc + offset desciptors. wrap around when needed.
+ *
+ * @param udma_q queue handle
+ * @param cdesc pointer that set by this function
+ * @param offset offset desciptors
+ *
+ */
+static inline volatile union al_udma_cdesc *al_cdesc_next(
+ struct al_udma_q *udma_q,
+ volatile union al_udma_cdesc *cdesc,
+ u32 offset)
+{
+ volatile u8 *tmp = (volatile u8 *) cdesc + offset * udma_q->udma->cdesc_size;
+
+ /* if wrap around */
+ if (unlikely((tmp > udma_q->end_cdesc_ptr)))
+ return (union al_udma_cdesc *)
+ (udma_q->cdesc_base_ptr +
+ (tmp - udma_q->end_cdesc_ptr - udma_q->udma->cdesc_size));
+
+ return (volatile union al_udma_cdesc *) tmp;
+}
+
+/*
+ * check if the flags of the descriptor indicates that is new one
+ * the function uses the ring id from the descriptor flags to know whether it
+ * new one by comparing it with the curring ring id of the queue
+ *
+ * @param udma_q queue handle
+ * @param flags the flags of the completion descriptor
+ *
+ * @return true if the completion descriptor is new one.
+ * false if it old one.
+ */
+static inline bool al_udma_new_cdesc(struct al_udma_q *udma_q, u32 flags)
+{
+ if (((flags & AL_M2S_DESC_RING_ID_MASK) >> AL_M2S_DESC_RING_ID_SHIFT)
+ == udma_q->comp_ring_id)
+ return true;
+ return false;
+}
+
+/*
+ * get next completion descriptor
+ * this function will also increment the completion ring id when the ring wraps
+ * around
+ *
+ * @param udma_q queue handle
+ * @param cdesc current completion descriptor
+ *
+ * @return pointer to the completion descriptor that follows the one pointed by
+ * cdesc
+ */
+static inline volatile union al_udma_cdesc *al_cdesc_next_update(
+ struct al_udma_q *udma_q,
+ volatile union al_udma_cdesc *cdesc)
+{
+ /* if last desc, wrap around */
+ if (unlikely(((volatile u8 *) cdesc == udma_q->end_cdesc_ptr))) {
+ udma_q->comp_ring_id =
+ (udma_q->comp_ring_id + 1) & DMA_RING_ID_MASK;
+ return (union al_udma_cdesc *) udma_q->cdesc_base_ptr;
+ }
+ return (volatile union al_udma_cdesc *) ((volatile u8 *) cdesc + udma_q->udma->cdesc_size);
+}
+
+/*
+ * get next completed packet from completion ring of the queue
+ *
+ * @param udma_q udma queue handle
+ * @param desc pointer that set by this function to the first descriptor
+ * note: desc is valid only when return value is not zero
+ * @return number of descriptors that belong to the packet. 0 means no completed
+ * full packet was found.
+ * If the descriptors found in the completion queue don't form full packet (no
+ * desc with LAST flag), then this function will do the following:
+ * (1) save the number of processed descriptors.
+ * (2) save last processed descriptor, so next time it called, it will resume
+ * from there.
+ * (3) return 0.
+ * note: the descriptors that belong to the completed packet will still be
+ * considered as used, that means the upper layer is safe to access those
+ * descriptors when this function returns. the al_udma_cdesc_ack() should be
+ * called to inform the udma driver that those descriptors are freed.
+ */
+u32 al_udma_cdesc_packet_get(struct al_udma_q *udma_q,
+ volatile union al_udma_cdesc **desc);
+
+/* get completion descriptor pointer from its index */
+#define al_udma_cdesc_idx_to_ptr(udma_q, idx) \
+ ((volatile union al_udma_cdesc *) ((udma_q)->cdesc_base_ptr + \
+ (idx) * (udma_q)->udma->cdesc_size))
+
+
+/*
+ * return number of all completed descriptors in the completion ring
+ *
+ * @param udma_q udma queue handle
+ * @param cdesc pointer that set by this function to the first descriptor
+ * note: desc is valid only when return value is not zero
+ * note: pass NULL if not interested
+ * @return number of descriptors. 0 means no completed descriptors were found.
+ * note: the descriptors that belong to the completed packet will still be
+ * considered as used, that means the upper layer is safe to access those
+ * descriptors when this function returns. the al_udma_cdesc_ack() should be
+ * called to inform the udma driver that those descriptors are freed.
+ */
+static inline u32 al_udma_cdesc_get_all(struct al_udma_q *udma_q,
+ volatile union al_udma_cdesc **cdesc)
+{
+ u16 count = 0;
+
+ udma_q->comp_head_idx = readl(&udma_q->q_regs->rings.crhp) & 0xffff;
+ count = (udma_q->comp_head_idx - udma_q->next_cdesc_idx) &
+ udma_q->size_mask;
+
+ if (cdesc)
+ *cdesc = al_udma_cdesc_idx_to_ptr(udma_q, udma_q->next_cdesc_idx);
+
+ return count;
+}
+
+/*
+ * acknowledge the driver that the upper layer completed processing completion
+ * descriptors
+ *
+ * @param udma_q udma queue handle
+ * @param num number of descriptors to acknowledge
+ */
+static inline void al_udma_cdesc_ack(struct al_udma_q *udma_q, u32 num)
+{
+ udma_q->next_cdesc_idx += num;
+ udma_q->next_cdesc_idx &= udma_q->size_mask;
+}
+
+#endif /* __AL_HW_UDMA_H__ */
diff --git a/include/linux/soc/alpine/al_hw_udma_config.h b/include/linux/soc/alpine/al_hw_udma_config.h
new file mode 100644
index 000000000000..0245d2a51459
--- /dev/null
+++ b/include/linux/soc/alpine/al_hw_udma_config.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017, Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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 __AL_HW_UDMA_CONFIG_H__
+#define __AL_HW_UDMA_CONFIG_H__
+
+#include "al_hw_udma_regs.h"
+#include "al_hw_udma.h"
+
+/* M2S max packet size configuration */
+struct al_udma_m2s_pkt_len_conf {
+ u32 max_pkt_size;
+ bool encode_64k_as_zero;
+};
+
+/* M2S DMA Rate Limitation mode */
+struct al_udma_m2s_rlimit_mode {
+ bool pkt_mode_en;
+ u16 short_cycle_sz;
+ u32 token_init_val;
+};
+
+enum al_udma_m2s_rlimit_action {
+ AL_UDMA_STRM_RLIMIT_ENABLE,
+ AL_UDMA_STRM_RLIMIT_PAUSE,
+ AL_UDMA_STRM_RLIMIT_RESET
+};
+
+/* UDMA / UDMA Q rate limitation configuration */
+struct al_udma_m2s_rlimit {
+ struct al_udma_m2s_rlimit_mode rlimit_mode; /* rate limitation enablers */
+};
+
+/* Configure M2S packet len */
+int al_udma_m2s_packet_size_cfg_set(struct al_udma *udma,
+ struct al_udma_m2s_pkt_len_conf *conf);
+
+void al_udma_s2m_max_descs_set(struct al_udma *udma, u8 max_descs);
+void al_udma_m2s_max_descs_set(struct al_udma *udma, u8 max_descs);
+
+/* UDMA get revision */
+static inline unsigned int al_udma_get_revision(
+ struct unit_regs __iomem *unit_regs)
+{
+ return (readl(&unit_regs->gen.dma_misc.revision)
+ & UDMA_GEN_DMA_MISC_REVISION_PROGRAMMING_ID_MASK) >>
+ UDMA_GEN_DMA_MISC_REVISION_PROGRAMMING_ID_SHIFT;
+}
+
+/*
+ * S2M UDMA configure a queue's completion descriptors coalescing
+ *
+ * @param q_udma
+ * @param enable set to true to enable completion coalescing
+ * @param coal_timeout in South Bridge cycles.
+ */
+void al_udma_s2m_q_compl_coal_config(struct al_udma_q *udma_q, bool enable,
+ u32 coal_timeout);
+
+/*
+ * S2M UDMA configure completion descriptors write burst parameters
+ *
+ * @param udma
+ * @param burst_size completion descriptors write burst size in bytes.
+ *
+ * @return 0 if no error found.
+ */
+int al_udma_s2m_compl_desc_burst_config(struct al_udma *udma, u16 burst_size);
+
+#endif /* __AL_HW_UDMA_CONFIG_H__ */
diff --git a/include/linux/soc/alpine/al_hw_udma_iofic.h b/include/linux/soc/alpine/al_hw_udma_iofic.h
new file mode 100644
index 000000000000..f55c4b608857
--- /dev/null
+++ b/include/linux/soc/alpine/al_hw_udma_iofic.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2017, Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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 __AL_HW_UDMA_IOFIC_H__
+#define __AL_HW_UDMA_IOFIC_H__
+
+#include <linux/soc/alpine/iofic.h>
+
+#include "al_hw_udma_regs.h"
+
+/*
+ * Interrupt Mode
+ * This is the interrupt mode for the primary interrupt level The secondary
+ * interrupt level does not have mode and it is always a level sensitive
+ * interrupt that is reflected in group D of the primary.
+ */
+enum al_iofic_mode {
+ AL_IOFIC_MODE_LEGACY, /*< level-sensitive interrupt wire */
+ AL_IOFIC_MODE_MSIX_PER_Q, /*< per UDMA queue MSI-X interrupt */
+ AL_IOFIC_MODE_MSIX_PER_GROUP
+};
+
+/* interrupt controller level (primary/secondary) */
+enum al_udma_iofic_level {
+ AL_UDMA_IOFIC_LEVEL_PRIMARY,
+ AL_UDMA_IOFIC_LEVEL_SECONDARY
+};
+
+/*
+ * The next four groups represents the standard 4 groups in the primary
+ * interrupt controller of each bus-master unit in the I/O Fabric.
+ * The first two groups can be used when accessing the secondary interrupt
+ * controller as well.
+ */
+#define AL_INT_GROUP_A 0 /* summary of the below events */
+#define AL_INT_GROUP_B 1 /* RX completion queues */
+#define AL_INT_GROUP_C 2 /* TX completion queues */
+#define AL_INT_GROUP_D 3 /* Misc */
+
+/*
+ * Primary interrupt controller, group A bits
+ * Group A bits which are just summary bits of GROUP B, C and D
+ */
+#define AL_INT_GROUP_A_GROUP_B_SUM BIT(0)
+#define AL_INT_GROUP_A_GROUP_C_SUM BIT(1)
+#define AL_INT_GROUP_A_GROUP_D_SUM BIT(2)
+
+/*
+ * Configure the UDMA interrupt controller registers, interrupts will are kept
+ * masked.
+ *
+ * This is a static setting that should be called while initialized the
+ * interrupt controller within a given UDMA, and should not be modified during
+ * runtime unless the UDMA is completely disabled. The first argument sets the
+ * interrupt and MSIX modes. The m2s/s2m errors/abort are a set of bit-wise
+ * masks to define the behaviour of the UDMA once an error happens: The _abort
+ * will put the UDMA in abort state once an error happens The _error bitmask
+ * will indicate and error in the secondary cause register but will not abort.
+ * The bit-mask that the _errors_disable and _aborts_disable are described in
+ * 'AL_INT_2ND_GROUP_A_*' and 'AL_INT_2ND_GROUP_B_*'
+ *
+ * @param regs pointer to unit registers
+ * @param mode interrupt scheme mode (legacy, MSI-X..)
+ * @param m2s_errors_disable
+ * This is a bit-wise mask, to indicate which one of the error causes in
+ * secondary interrupt group_A should generate an interrupt. When a bit is
+ * set, the error cause is ignored.
+ * Recommended value: 0 (enable all errors).
+ * @param m2s_aborts_disable
+ * This is a bit-wise mask, to indicate which one of the error causes in
+ * secondary interrupt group_A should automatically put the UDMA in
+ * abort state. When a bit is set, the error cause does cause an abort.
+ * Recommended value: 0 (enable all aborts).
+ * @param s2m_errors_disable
+ * This is a bit-wise mask, to indicate which one of the error causes in
+ * secondary interrupt group_A should generate an interrupt. When a bit is
+ * set, the error cause is ignored.
+ * Recommended value: 0xE0 (disable hint errors).
+ * @param s2m_aborts_disable
+ * This is a bit-wise mask, to indicate which one of the error causes in
+ * secondary interrupt group_A should automatically put the UDMA in
+ * abort state. When a bit is set, the error cause does cause an abort.
+ * Recommended value: 0xE0 (disable hint aborts).
+ *
+ * @return 0 on success. -EINVAL otherwise.
+ */
+int al_udma_iofic_config(struct unit_regs __iomem *regs,
+ enum al_iofic_mode mode, u32 m2s_errors_disable,
+ u32 m2s_aborts_disable, u32 s2m_errors_disable,
+ u32 s2m_aborts_disable);
+/*
+ * return the offset of the unmask register for a given group.
+ * this function can be used when the upper layer wants to directly
+ * access the unmask regiter and bypass the al_udma_iofic_unmask() API.
+ *
+ * @param regs pointer to udma registers
+ * @param level the interrupt controller level (primary / secondary)
+ * @param group the interrupt group ('AL_INT_GROUP_*')
+ * @return the offset of the unmask register.
+ */
+u32 __iomem *al_udma_iofic_unmask_offset_get(struct unit_regs __iomem *regs,
+ enum al_udma_iofic_level level,
+ int group);
+
+/*
+ * Get the interrupt controller base address for either the primary or secondary
+ * interrupt controller
+ *
+ * @param regs pointer to udma unit registers
+ * @param level the interrupt controller level (primary / secondary)
+ *
+ * @returns The interrupt controller base address
+ */
+static inline void __iomem *al_udma_iofic_reg_base_get(
+ struct unit_regs __iomem *regs, enum al_udma_iofic_level level)
+{
+ void __iomem *iofic_regs = (level == AL_UDMA_IOFIC_LEVEL_PRIMARY) ?
+ (void __iomem *)®s->gen.interrupt_regs.main_iofic :
+ (void __iomem *)®s->gen.interrupt_regs.secondary_iofic_ctrl;
+
+ return iofic_regs;
+}
+
+/*
+ * Check the interrupt controller level/group validity
+ *
+ * @param level the interrupt controller level (primary / secondary)
+ * @param group the interrupt group ('AL_INT_GROUP_*')
+ *
+ * @returns 0 - invalid, 1 - valid
+ */
+static inline int al_udma_iofic_level_and_group_valid(
+ enum al_udma_iofic_level level, int group)
+{
+ if (((level == AL_UDMA_IOFIC_LEVEL_PRIMARY) && (group >= 0) && (group < 4)) ||
+ ((level == AL_UDMA_IOFIC_LEVEL_SECONDARY) && (group >= 0) && (group < 2)))
+ return 1;
+
+ return 0;
+}
+/*
+ * unmask specific interrupts for a given group
+ * this functions uses the interrupt mask clear register to guarantee atomicity
+ * it's safe to call it while the mask is changed by the HW (auto mask) or
+ * another cpu.
+ *
+ * @param regs pointer to udma unit registers
+ * @param level the interrupt controller level (primary / secondary)
+ * @param group the interrupt group ('AL_INT_GROUP_*')
+ * @param mask bitwise of interrupts to unmask, set bits will be unmasked.
+ */
+static inline void al_udma_iofic_unmask(struct unit_regs __iomem *regs,
+ enum al_udma_iofic_level level,
+ int group, u32 mask)
+{
+ BUG_ON(!al_udma_iofic_level_and_group_valid(level, group));
+ al_iofic_unmask(al_udma_iofic_reg_base_get(regs, level), group, mask);
+}
+
+/*
+ * mask specific interrupts for a given group
+ * this functions modifies interrupt mask register, the callee must make sure
+ * the mask is not changed by another cpu.
+ *
+ * @param regs pointer to udma unit registers
+ * @param level the interrupt controller level (primary / secondary)
+ * @param group the interrupt group ('AL_INT_GROUP_*')
+ * @param mask bitwise of interrupts to mask, set bits will be masked.
+ */
+static inline void al_udma_iofic_mask(struct unit_regs __iomem *regs,
+ enum al_udma_iofic_level level, int group,
+ u32 mask)
+{
+ BUG_ON(!al_udma_iofic_level_and_group_valid(level, group));
+ al_iofic_mask(al_udma_iofic_reg_base_get(regs, level), group, mask);
+}
+
+/*
+ * read interrupt cause register for a given group
+ * this will clear the set bits if the Clear on Read mode enabled.
+ * @param regs pointer to udma unit registers
+ * @param level the interrupt controller level (primary / secondary)
+ * @param group the interrupt group ('AL_INT_GROUP_*')
+ */
+static inline u32 al_udma_iofic_read_cause(struct unit_regs __iomem *regs,
+ enum al_udma_iofic_level level,
+ int group)
+{
+ BUG_ON(!al_udma_iofic_level_and_group_valid(level, group));
+ return al_iofic_read_cause(al_udma_iofic_reg_base_get(regs, level),
+ group);
+}
+
+#endif
diff --git a/include/linux/soc/alpine/al_hw_udma_regs.h b/include/linux/soc/alpine/al_hw_udma_regs.h
new file mode 100644
index 000000000000..8ae3ceaa3753
--- /dev/null
+++ b/include/linux/soc/alpine/al_hw_udma_regs.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017, Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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 __AL_HW_UDMA_REG_H
+#define __AL_HW_UDMA_REG_H
+
+#include "al_hw_udma_regs_m2s.h"
+#include "al_hw_udma_regs_s2m.h"
+
+
+/* Design programming interface revision ID */
+#define UDMA_GEN_DMA_MISC_REVISION_PROGRAMMING_ID_MASK 0xfff
+#define UDMA_GEN_DMA_MISC_REVISION_PROGRAMMING_ID_SHIFT 0x0
+
+struct al_iofic_grp_mod {
+ u32 grp_int_mod_reg;
+ u32 grp_int_tgtid_reg;
+};
+
+struct al_iofic_grp_ctrl {
+ u32 int_cause_grp;
+ u32 rsrvd1;
+ u32 int_cause_set_grp;
+ u32 rsrvd2;
+ u32 int_mask_grp;
+ u32 rsrvd3;
+ u32 int_mask_clear_grp;
+ u32 rsrvd4;
+ u32 int_status_grp;
+ u32 rsrvd5;
+ u32 int_control_grp;
+ u32 rsrvd6;
+ u32 int_abort_msk_grp;
+ u32 rsrvd7;
+ u32 int_log_msk_grp;
+ u32 rsrvd8;
+};
+
+struct al_iofic_regs {
+ struct al_iofic_grp_ctrl ctrl[0];
+ u32 rsrvd1[0x100];
+ struct al_iofic_grp_mod grp_int_mod[0][32];
+};
+
+struct udma_iofic_regs {
+ struct al_iofic_regs main_iofic;
+ u32 rsrvd1[0x700];
+ struct al_iofic_grp_ctrl secondary_iofic_ctrl[2];
+};
+
+struct udma_gen_dma_misc {
+ u32 int_cfg;
+ u32 revision;
+ u32 general_cfg_1;
+ u32 general_cfg_2;
+ u32 general_cfg_3;
+ u32 general_cfg_4;
+ u32 general_cfg_5;
+ u32 rsrvd[57];
+};
+
+/*
+ * Mailbox interrupt generator.
+ * Generates interrupt to neighbor DMA
+ */
+struct udma_gen_mailbox {
+ u32 interrupt;
+ u32 msg_out;
+ u32 msg_in;
+ u32 rsrvd[0xd];
+};
+
+struct udma_gen_axi {
+ u32 cfg_1;
+ u32 cfg_2;
+ u32 endian_cfg;
+ u32 rsrvd[0x3d];
+};
+
+struct udma_gen_regs {
+ struct udma_iofic_regs interrupt_regs;
+ struct udma_gen_dma_misc dma_misc;
+ struct udma_gen_mailbox mailbox[4];
+ struct udma_gen_axi axi;
+};
+
+/* UDMA registers, either m2s or s2m */
+union udma_regs {
+ struct udma_m2s_regs m2s;
+ struct udma_s2m_regs s2m;
+};
+
+struct unit_regs {
+ struct udma_m2s_regs m2s;
+ u32 rsrvd0[0x2c00];
+ struct udma_s2m_regs s2m;
+ u32 rsrvd1[0x1c00];
+ struct udma_gen_regs gen;
+};
+
+/*
+ * UDMA submission and completion registers, M2S and S2M UDMAs have same
+ * stucture
+ */
+struct udma_rings_regs {
+ u32 rsrvd0[8];
+ u32 cfg; /* Descriptor ring configuration */
+ u32 status; /* Descriptor ring status and information */
+ u32 drbp_low; /* Descriptor Ring Base Pointer [31:4] */
+ u32 drbp_high; /* Descriptor Ring Base Pointer [63:32] */
+ u32 drl; /* Descriptor Ring Length[23:2] */
+ u32 drhp; /* Descriptor Ring Head Pointer */
+ u32 drtp_inc; /* Descriptor Tail Pointer increment */
+ u32 drtp; /* Descriptor Tail Pointer */
+ u32 dcp; /* Descriptor Current Pointer */
+ u32 crbp_low; /* Completion Ring Base Pointer [31:4] */
+ u32 crbp_high; /* Completion Ring Base Pointer [63:32] */
+ u32 crhp; /* Completion Ring Head Pointer */
+ u32 crhp_internal; /* Completion Ring Head Pointer internal */
+};
+
+/* M2S and S2M generic structure of Q registers */
+union udma_q_regs {
+ struct udma_rings_regs rings;
+ struct udma_m2s_q m2s_q;
+ struct udma_s2m_q s2m_q;
+};
+
+#endif /* __AL_HW_UDMA_REG_H */
diff --git a/include/linux/soc/alpine/al_hw_udma_regs_m2s.h b/include/linux/soc/alpine/al_hw_udma_regs_m2s.h
new file mode 100644
index 000000000000..78a04de9df27
--- /dev/null
+++ b/include/linux/soc/alpine/al_hw_udma_regs_m2s.h
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2015, Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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 __AL_HW_UDMA_M2S_REG_H
+#define __AL_HW_UDMA_M2S_REG_H
+
+#include <linux/types.h>
+
+struct udma_axi_m2s {
+ /* Completion write master configuration */
+ u32 comp_wr_cfg_1;
+ /* Completion write master configuration */
+ u32 comp_wr_cfg_2;
+ /* Data read master configuration */
+ u32 data_rd_cfg_1;
+ /* Data read master configuration */
+ u32 data_rd_cfg_2;
+ /* Descriptor read master configuration */
+ u32 desc_rd_cfg_1;
+ /* Descriptor read master configuration */
+ u32 desc_rd_cfg_2;
+ /* Data read master configuration */
+ u32 data_rd_cfg;
+ /* Descriptors read master configuration */
+ u32 desc_rd_cfg_3;
+ /* Descriptors write master configuration (completion) */
+ u32 desc_wr_cfg_1;
+ /* AXI outstanding configuration */
+ u32 ostand_cfg;
+ u32 rsrvd[54];
+};
+struct udma_m2s {
+ /*
+ * DMA state.
+ * 00 - No pending tasks
+ * 01 ? Normal (active)
+ * 10 ? Abort (error condition)
+ * 11 ? Reserved
+ */
+ u32 state;
+ /* CPU request to change DMA state */
+ u32 change_state;
+ u32 rsrvd_0;
+ /*
+ * M2S DMA error log mask.
+ * Each error has an interrupt controller cause bit.
+ * This register determines if these errors cause the M2S DMA to log the
+ * error condition.
+ * 0 - Log is enabled.
+ * 1 - Log is masked.
+ */
+ u32 err_log_mask;
+ u32 rsrvd_1;
+ /*
+ * DMA header log.
+ * Sample the packet header that caused the error.
+ */
+ u32 log_0;
+ /*
+ * DMA header log.
+ * Sample the packet header that caused the error.
+ */
+ u32 log_1;
+ /*
+ * DMA header log.
+ * Sample the packet header that caused the error.
+ */
+ u32 log_2;
+ /*
+ * DMA header log.
+ * Sample the packet header that caused the error.
+ */
+ u32 log_3;
+ /* DMA clear error log */
+ u32 clear_err_log;
+ /* M2S data FIFO status */
+ u32 data_fifo_status;
+ /* M2S header FIFO status */
+ u32 header_fifo_status;
+ /* M2S unack FIFO status */
+ u32 unack_fifo_status;
+ /* Select queue for debug */
+ u32 indirect_ctrl;
+ /*
+ * M2S prefetch FIFO status.
+ * Status of the selected queue in M2S_indirect_ctrl
+ */
+ u32 sel_pref_fifo_status;
+ /*
+ * M2S completion FIFO status.
+ * Status of the selected queue in M2S_indirect_ctrl
+ */
+ u32 sel_comp_fifo_status;
+ /*
+ * M2S rate limit status.
+ * Status of the selected queue in M2S_indirect_ctrl
+ */
+ u32 sel_rate_limit_status;
+ /*
+ * M2S DWRR scheduler status.
+ * Status of the selected queue in M2S_indirect_ctrl
+ */
+ u32 sel_dwrr_status;
+ /* M2S state machine and FIFO clear control */
+ u32 clear_ctrl;
+ /* Misc Check enable */
+ u32 check_en;
+ /* M2S FIFO enable control, internal */
+ u32 fifo_en;
+ /* M2S packet length configuration */
+ u32 cfg_len;
+ /* Stream interface configuration */
+ u32 stream_cfg;
+ u32 rsrvd[41];
+};
+struct udma_m2s_rd {
+ /* M2S descriptor prefetch configuration */
+ u32 desc_pref_cfg_1;
+ /* M2S descriptor prefetch configuration */
+ u32 desc_pref_cfg_2;
+ /* M2S descriptor prefetch configuration */
+ u32 desc_pref_cfg_3;
+ u32 rsrvd_0;
+ /* Data burst read configuration */
+ u32 data_cfg;
+ u32 rsrvd[11];
+};
+struct udma_m2s_dwrr {
+ /* Tx DMA DWRR scheduler configuration */
+ u32 cfg_sched;
+ /* Token bucket rate limit control */
+ u32 ctrl_deficit_cnt;
+ u32 rsrvd[14];
+};
+struct udma_m2s_rate_limiter {
+ /* Token bucket rate limit configuration */
+ u32 gen_cfg;
+ /*
+ * Token bucket rate limit control.
+ * Controls the cycle counters.
+ */
+ u32 ctrl_cycle_cnt;
+ /*
+ * Token bucket rate limit control.
+ * Controls the token bucket counter.
+ */
+ u32 ctrl_token;
+ u32 rsrvd[13];
+};
+
+struct udma_rlimit_common {
+ /* Token bucket configuration */
+ u32 cfg_1s;
+ /* Token bucket rate limit configuration */
+ u32 cfg_cycle;
+ /* Token bucket rate limit configuration */
+ u32 cfg_token_size_1;
+ /* Token bucket rate limit configuration */
+ u32 cfg_token_size_2;
+ /* Token bucket rate limit configuration */
+ u32 sw_ctrl;
+ /*
+ * Mask the different types of rate limiter.
+ * 0 - Rate limit is active.
+ * 1 - Rate limit is masked.
+ */
+ u32 mask;
+};
+
+struct udma_m2s_stream_rate_limiter {
+ struct udma_rlimit_common rlimit;
+ u32 rsrvd[10];
+};
+struct udma_m2s_comp {
+ /* Completion controller configuration */
+ u32 cfg_1c;
+ /* Completion controller coalescing configuration */
+ u32 cfg_coal;
+ /* Completion controller application acknowledge configuration */
+ u32 cfg_application_ack;
+ u32 rsrvd[61];
+};
+struct udma_m2s_stat {
+ /* Statistics counters configuration */
+ u32 cfg_st;
+ /* Counting number of descriptors with First-bit set. */
+ u32 tx_pkt;
+ /*
+ * Counting the net length of the data buffers [64-bit]
+ * Should be read before tx_bytes_high
+ */
+ u32 tx_bytes_low;
+ /*
+ * Counting the net length of the data buffers [64-bit],
+ * Should be read after tx_bytes_low (value is sampled when reading
+ * Should be read before tx_bytes_low
+ */
+ u32 tx_bytes_high;
+ /* Total number of descriptors read from the host memory */
+ u32 prefed_desc;
+ /* Number of packets read from the unack FIFO */
+ u32 comp_pkt;
+ /* Number of descriptors written into the completion ring */
+ u32 comp_desc;
+ /*
+ * Number of acknowledged packets.
+ * (acknowledge received from the stream interface)
+ */
+ u32 ack_pkts;
+ u32 rsrvd[56];
+};
+struct udma_m2s_feature {
+ /*
+ * M2S Feature register.
+ * M2S instantiation parameters
+ */
+ u32 reg_1;
+ /* Reserved M2S feature register */
+ u32 reg_2;
+ /*
+ * M2S Feature register.
+ * M2S instantiation parameters
+ */
+ u32 reg_3;
+ /*
+ * M2S Feature register.
+ * M2S instantiation parameters
+ */
+ u32 reg_4;
+ /*
+ * M2S Feature register.
+ * M2S instantiation parameters
+ */
+ u32 reg_5;
+ u32 rsrvd[59];
+};
+struct udma_m2s_q {
+ u32 rsrvd_0[8];
+ /* M2S descriptor ring configuration */
+ u32 cfg;
+ /* M2S descriptor ring status and information */
+ u32 status;
+ /* TX Descriptor Ring Base Pointer [31:4] */
+ u32 tdrbp_low;
+ /* TX Descriptor Ring Base Pointer [63:32] */
+ u32 tdrbp_high;
+ /*
+ * TX Descriptor Ring Length[23:2]
+ */
+ u32 tdrl;
+ /* TX Descriptor Ring Head Pointer */
+ u32 tdrhp;
+ /* Tx Descriptor Tail Pointer increment */
+ u32 tdrtp_inc;
+ /* Tx Descriptor Tail Pointer */
+ u32 tdrtp;
+ /* TX Descriptor Current Pointer */
+ u32 tdcp;
+ /* Tx Completion Ring Base Pointer [31:4] */
+ u32 tcrbp_low;
+ /* TX Completion Ring Base Pointer [63:32] */
+ u32 tcrbp_high;
+ /* TX Completion Ring Head Pointer */
+ u32 tcrhp;
+ /*
+ * Tx Completion Ring Head Pointer internal (Before the
+ * coalescing FIFO)
+ */
+ u32 tcrhp_internal;
+ u32 rsrvd_1[3];
+ /* Rate limit configuration */
+ struct udma_rlimit_common rlimit;
+ u32 rsrvd_2[2];
+ /* DWRR scheduler configuration */
+ u32 dwrr_cfg_1;
+ /* DWRR scheduler configuration */
+ u32 dwrr_cfg_2;
+ /* DWRR scheduler configuration */
+ u32 dwrr_cfg_3;
+ /* DWRR scheduler software control */
+ u32 dwrr_sw_ctrl;
+ u32 rsrvd_3[4];
+ /* Completion controller configuration */
+ u32 comp_cfg;
+ u32 rsrvd_4[3];
+ /* SW control */
+ u32 q_sw_ctrl;
+ u32 rsrvd_5[3];
+ /* Number of M2S Tx packets after the scheduler */
+ u32 q_tx_pkt;
+ u32 rsrvd[975];
+};
+
+struct udma_m2s_regs {
+ u32 rsrvd_0[64];
+ struct udma_axi_m2s axi_m2s;
+ struct udma_m2s m2s;
+ struct udma_m2s_rd m2s_rd;
+ struct udma_m2s_dwrr m2s_dwrr;
+ struct udma_m2s_rate_limiter m2s_rate_limiter;
+ struct udma_m2s_stream_rate_limiter m2s_stream_rate_limiter;
+ struct udma_m2s_comp m2s_comp;
+ struct udma_m2s_stat m2s_stat;
+ struct udma_m2s_feature m2s_feature;
+ u32 rsrvd_1[576];
+ struct udma_m2s_q m2s_q[4];
+};
+
+
+/* Completion control */
+#define UDMA_M2S_STATE_COMP_CTRL_MASK 0x00000003
+#define UDMA_M2S_STATE_COMP_CTRL_SHIFT 0
+/* Stream interface */
+#define UDMA_M2S_STATE_STREAM_IF_MASK 0x00000030
+#define UDMA_M2S_STATE_STREAM_IF_SHIFT 4
+/* Data read control */
+#define UDMA_M2S_STATE_DATA_RD_CTRL_MASK 0x00000300
+#define UDMA_M2S_STATE_DATA_RD_CTRL_SHIFT 8
+/* Descriptor prefetch */
+#define UDMA_M2S_STATE_DESC_PREF_MASK 0x00003000
+#define UDMA_M2S_STATE_DESC_PREF_SHIFT 12
+
+/* Start normal operation */
+#define UDMA_M2S_CHANGE_STATE_NORMAL BIT(0)
+/* Stop normal operation */
+#define UDMA_M2S_CHANGE_STATE_DIS BIT(1)
+/*
+ * Stop all machines.
+ * (Prefetch, scheduling, completion and stream interface)
+ */
+#define UDMA_M2S_CHANGE_STATE_ABORT BIT(2)
+
+/* Maximum packet size for the M2S */
+#define UDMA_M2S_CFG_LEN_MAX_PKT_SIZE_MASK 0x000FFFFF
+/*
+ * Length encoding for 64K.
+ * 0 - length 0x0000 = 0
+ * 1 - length 0x0000 = 64k
+ */
+#define UDMA_M2S_CFG_LEN_ENCODE_64K BIT(24)
+
+/* Maximum number of descriptors per packet */
+#define UDMA_M2S_RD_DESC_PREF_CFG_2_MAX_DESC_PER_PKT_MASK 0x0000001F
+#define UDMA_M2S_RD_DESC_PREF_CFG_2_MAX_DESC_PER_PKT_SHIFT 0
+/*
+ * Minimum descriptor burst size when prefetch FIFO level is above the
+ * descriptor prefetch threshold
+ */
+#define UDMA_M2S_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_MASK 0x000000F0
+#define UDMA_M2S_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_SHIFT 4
+/*
+ * Descriptor fetch threshold.
+ * Used as a threshold to determine the allowed minimum descriptor burst size.
+ * (Must be at least max_desc_per_pkt)
+ */
+#define UDMA_M2S_RD_DESC_PREF_CFG_3_PREF_THR_MASK 0x0000FF00
+#define UDMA_M2S_RD_DESC_PREF_CFG_3_PREF_THR_SHIFT 8
+
+/*
+ * Maximum number of data beats in the data read FIFO.
+ * Defined based on data FIFO size
+ * (default FIFO size 2KB ? 128 beats)
+ */
+#define UDMA_M2S_RD_DATA_CFG_DATA_FIFO_DEPTH_MASK 0x000003FF
+#define UDMA_M2S_RD_DATA_CFG_DATA_FIFO_DEPTH_SHIFT 0
+
+/*
+ * Enable operation of this queue.
+ * Start prefetch.
+ */
+#define UDMA_M2S_Q_CFG_EN_PREF BIT(16)
+/*
+ * Enable operation of this queue.
+ * Start scheduling.
+ */
+#define UDMA_M2S_Q_CFG_EN_SCHEDULING BIT(17)
+
+/*
+ * M2S Descriptor Ring Base address [31:4].
+ * Value of the base address of the M2S descriptor ring
+ * [3:0] - 0 - 16B alignment is enforced
+ * ([11:4] should be 0 for 4KB alignment)
+ */
+#define UDMA_M2S_Q_TDRBP_LOW_ADDR_MASK 0xFFFFFFF0
+
+/*
+ * M2S Descriptor Ring Base address [31:4].
+ * Value of the base address of the M2S descriptor ring
+ * [3:0] - 0 - 16B alignment is enforced
+ * ([11:4] should be 0 for 4KB alignment)
+ * NOTE:
+ * Length of the descriptor ring (in descriptors) associated with the ring base
+ * address. Ends at maximum burst size alignment.
+ */
+#define UDMA_M2S_Q_TCRBP_LOW_ADDR_MASK 0xFFFFFFF0
+
+/*
+ * Mask the internal pause mechanism for DMB.
+ * (Data Memory Barrier).
+ */
+#define UDMA_M2S_Q_RATE_LIMIT_MASK_INTERNAL_PAUSE_DMB BIT(2)
+
+/* Enable writing to the completion ring */
+#define UDMA_M2S_Q_COMP_CFG_EN_COMP_RING_UPDATE BIT(0)
+/* Disable the completion coalescing function. */
+#define UDMA_M2S_Q_COMP_CFG_DIS_COMP_COAL BIT(1)
+
+#endif /* __AL_HW_UDMA_M2S_REG_H */
diff --git a/include/linux/soc/alpine/al_hw_udma_regs_s2m.h b/include/linux/soc/alpine/al_hw_udma_regs_s2m.h
new file mode 100644
index 000000000000..e23480718844
--- /dev/null
+++ b/include/linux/soc/alpine/al_hw_udma_regs_s2m.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2017, Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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 __AL_HW_UDMA_S2M_REG_H
+#define __AL_HW_UDMA_S2M_REG_H
+
+struct udma_axi_s2m {
+ /* Data write master configuration */
+ u32 data_wr_cfg_1;
+ /* Data write master configuration */
+ u32 data_wr_cfg_2;
+ /* Descriptor read master configuration */
+ u32 desc_rd_cfg_4;
+ /* Descriptor read master configuration */
+ u32 desc_rd_cfg_5;
+ /* Completion write master configuration */
+ u32 comp_wr_cfg_1;
+ /* Completion write master configuration */
+ u32 comp_wr_cfg_2;
+ /* Data write master configuration */
+ u32 data_wr_cfg;
+ /* Descriptors read master configuration */
+ u32 desc_rd_cfg_3;
+ /* Completion descriptors write master configuration */
+ u32 desc_wr_cfg_1;
+ /* AXI outstanding read configuration */
+ u32 ostand_cfg_rd;
+ /* AXI outstanding write configuration */
+ u32 ostand_cfg_wr;
+ u32 rsrvd[53];
+};
+struct udma_s2m {
+ /*
+ * DMA state
+ * 00 - No pending tasks
+ * 01 ? Normal (active)
+ * 10 ? Abort (error condition)
+ * 11 ? Reserved
+ */
+ u32 state;
+ /* CPU request to change DMA state */
+ u32 change_state;
+ u32 rsrvd_0;
+ /*
+ * S2M DMA error log mask.
+ * Each error has an interrupt controller cause bit.
+ * This register determines if these errors cause the S2M DMA to log the
+ * error condition.
+ * 0 - Log is enable
+ * 1 - Log is masked.
+ */
+ u32 err_log_mask;
+ u32 rsrvd_1;
+ /*
+ * DMA header log
+ * Sample the packet header that caused the error
+ */
+ u32 log_0;
+ /*
+ * DMA header log
+ * Sample the packet header that caused the error.
+ */
+ u32 log_1;
+ /*
+ * DMA header log
+ * Sample the packet header that caused the error.
+ */
+ u32 log_2;
+ /*
+ * DMA header log
+ * Sample the packet header that caused the error
+ */
+ u32 log_3;
+ /* DMA clear error log */
+ u32 clear_err_log;
+ /* S2M stream data FIFO status */
+ u32 s_data_fifo_status;
+ /* S2M stream header FIFO status */
+ u32 s_header_fifo_status;
+ /* S2M AXI data FIFO status */
+ u32 axi_data_fifo_status;
+ /* S2M unack FIFO status */
+ u32 unack_fifo_status;
+ /* Select queue for debug */
+ u32 indirect_ctrl;
+ /*
+ * S2M prefetch FIFO status.
+ * Status of the selected queue in S2M_indirect_ctrl
+ */
+ u32 sel_pref_fifo_status;
+ /*
+ * S2M completion FIFO status.
+ * Status of the selected queue in S2M_indirect_ctrl
+ */
+ u32 sel_comp_fifo_status;
+ /* S2M state machine and FIFO clear control */
+ u32 clear_ctrl;
+ /* S2M Misc Check enable */
+ u32 check_en;
+ /* S2M FIFO enable control, internal */
+ u32 fifo_en;
+ /* Stream interface configuration */
+ u32 stream_cfg;
+ u32 rsrvd[43];
+};
+struct udma_s2m_rd {
+ /* S2M descriptor prefetch configuration */
+ u32 desc_pref_cfg_1;
+ /* S2M descriptor prefetch configuration */
+ u32 desc_pref_cfg_2;
+ /* S2M descriptor prefetch configuration */
+ u32 desc_pref_cfg_3;
+ /* S2M descriptor prefetch configuration */
+ u32 desc_pref_cfg_4;
+ u32 rsrvd[12];
+};
+struct udma_s2m_wr {
+ /* Stream data FIFO configuration */
+ u32 data_cfg_1;
+ /* Data write configuration */
+ u32 data_cfg_2;
+ u32 rsrvd[14];
+};
+struct udma_s2m_comp {
+ /* Completion controller configuration */
+ u32 cfg_1c;
+ /* Completion controller configuration */
+ u32 cfg_2c;
+ u32 rsrvd_0;
+ /* Completion controller application acknowledge configuration */
+ u32 cfg_application_ack;
+ u32 rsrvd[12];
+};
+struct udma_s2m_stat {
+ u32 rsrvd_0;
+ /* Number of dropped packets */
+ u32 drop_pkt;
+ /*
+ * Counting the net length of the data buffers [64-bit]
+ * Should be read before rx_bytes_high
+ */
+ u32 rx_bytes_low;
+ /*
+ * Counting the net length of the data buffers [64-bit]
+ * Should be read after tx_bytes_low (value is sampled when reading
+ * Should be read before rx_bytes_low
+ */
+ u32 rx_bytes_high;
+ /* Total number of descriptors read from the host memory */
+ u32 prefed_desc;
+ /* Number of packets written into the completion ring */
+ u32 comp_pkt;
+ /* Number of descriptors written into the completion ring */
+ u32 comp_desc;
+ /*
+ * Number of acknowledged packets.
+ * (acknowledge sent to the stream interface)
+ */
+ u32 ack_pkts;
+ u32 rsrvd[56];
+};
+struct udma_s2m_feature {
+ /*
+ * S2M Feature register
+ * S2M instantiation parameters
+ */
+ u32 reg_1;
+ /* Reserved S2M feature register */
+ u32 reg_2;
+ /*
+ * S2M Feature register
+ * S2M instantiation parameters
+ */
+ u32 reg_3;
+ /*
+ * S2M Feature register.
+ * S2M instantiation parameters.
+ */
+ u32 reg_4;
+ /*
+ * S2M Feature register.
+ * S2M instantiation parameters.
+ */
+ u32 reg_5;
+ /* S2M Feature register. S2M instantiation parameters. */
+ u32 reg_6;
+ u32 rsrvd[58];
+};
+struct udma_s2m_q {
+ u32 rsrvd_0[8];
+ /* S2M Descriptor ring configuration */
+ u32 cfg;
+ /* S2M Descriptor ring status and information */
+ u32 status;
+ /* Rx Descriptor Ring Base Pointer [31:4] */
+ u32 rdrbp_low;
+ /* Rx Descriptor Ring Base Pointer [63:32] */
+ u32 rdrbp_high;
+ /*
+ * Rx Descriptor Ring Length[23:2]
+ */
+ u32 rdrl;
+ /* RX Descriptor Ring Head Pointer */
+ u32 rdrhp;
+ /* Rx Descriptor Tail Pointer increment */
+ u32 rdrtp_inc;
+ /* Rx Descriptor Tail Pointer */
+ u32 rdrtp;
+ /* RX Descriptor Current Pointer */
+ u32 rdcp;
+ /* Rx Completion Ring Base Pointer [31:4] */
+ u32 rcrbp_low;
+ /* Rx Completion Ring Base Pointer [63:32] */
+ u32 rcrbp_high;
+ /* Rx Completion Ring Head Pointer */
+ u32 rcrhp;
+ /*
+ * RX Completion Ring Head Pointer internal.
+ * (Before the coalescing FIFO)
+ */
+ u32 rcrhp_internal;
+ /* Completion controller configuration for the queue */
+ u32 comp_cfg;
+ /* Completion controller configuration for the queue */
+ u32 comp_cfg_2;
+ /* Packet handler configuration */
+ u32 pkt_cfg;
+ /* Queue QoS configuration */
+ u32 qos_cfg;
+ /* DMB software control */
+ u32 q_sw_ctrl;
+ /* Number of S2M Rx packets after completion */
+ u32 q_rx_pkt;
+ u32 rsrvd[997];
+};
+
+struct udma_s2m_regs {
+ u32 rsrvd_0[64];
+ struct udma_axi_s2m axi_s2m;
+ struct udma_s2m s2m;
+ struct udma_s2m_rd s2m_rd;
+ struct udma_s2m_wr s2m_wr;
+ struct udma_s2m_comp s2m_comp;
+ u32 rsrvd_1[80];
+ struct udma_s2m_stat s2m_stat;
+ struct udma_s2m_feature s2m_feature;
+ u32 rsrvd_2[576];
+ struct udma_s2m_q s2m_q[4];
+};
+
+/*
+ * Defines the maximum number of AXI beats for a single AXI burst. This value is
+ * used for the burst split decision.
+ */
+#define UDMA_AXI_S2M_DESC_WR_CFG_1_MAX_AXI_BEATS_MASK 0x000000FF
+#define UDMA_AXI_S2M_DESC_WR_CFG_1_MAX_AXI_BEATS_SHIFT 0
+/*
+ * Minimum burst for writing completion descriptors.
+ * (AXI beats).
+ * Value must be aligned to cache lines (64 bytes).
+ * Default value is 2 cache lines, 8 beats.
+ */
+#define UDMA_AXI_S2M_DESC_WR_CFG_1_MIN_AXI_BEATS_MASK 0x00FF0000
+#define UDMA_AXI_S2M_DESC_WR_CFG_1_MIN_AXI_BEATS_SHIFT 16
+
+/*
+ * Minimum descriptor burst size when prefetch FIFO level is above the
+ * descriptor prefetch threshold
+ */
+#define UDMA_S2M_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_MASK 0x000000F0
+#define UDMA_S2M_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_SHIFT 4
+/*
+ * Descriptor fetch threshold.
+ * Used as a threshold to determine the allowed minimum descriptor burst size.
+ * (Must be at least "max_desc_per_pkt")
+ */
+#define UDMA_S2M_RD_DESC_PREF_CFG_3_PREF_THR_MASK 0x0000FF00
+#define UDMA_S2M_RD_DESC_PREF_CFG_3_PREF_THR_SHIFT 8
+
+/*
+ * Completion descriptor size.
+ * (words)
+ */
+#define UDMA_S2M_COMP_CFG_1C_DESC_SIZE_MASK 0x0000000F
+
+/* Disables the completion coalescing function. */
+#define UDMA_S2M_Q_COMP_CFG_DIS_COMP_COAL BIT(1)
+
+#endif /* __AL_HW_UDMA_S2M_REG_H */
--
2.11.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next 3/8] pci: add Annapurna Labs PCI id
2017-02-03 18:12 [PATCH net-next 0/8] ARM: Alpine: Ethernet support Antoine Tenart
2017-02-03 18:12 ` [PATCH net-next 1/8] alpine: add I/O fabric interrupt controller (iofic) helpers Antoine Tenart
2017-02-03 18:12 ` [PATCH net-next 2/8] soc: alpine: add udma helpers Antoine Tenart
@ 2017-02-03 18:12 ` Antoine Tenart
2017-02-03 18:12 ` [PATCH net-next 5/8] net: ethernet: annapurna: add statistics helper Antoine Tenart
` (4 subsequent siblings)
7 siblings, 0 replies; 23+ messages in thread
From: Antoine Tenart @ 2017-02-03 18:12 UTC (permalink / raw)
To: linux-arm-kernel
Adds the Annapurna Labs vendor id.
Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
---
include/linux/pci_ids.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 73dda0edcb97..8a11e2d8f35f 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -148,6 +148,8 @@
#define PCI_VENDOR_ID_DYNALINK 0x0675
#define PCI_DEVICE_ID_DYNALINK_IS64PH 0x1702
+#define PCI_VENDOR_ID_ANNAPURNA_LABS 0x1c36
+
#define PCI_VENDOR_ID_BERKOM 0x0871
#define PCI_DEVICE_ID_BERKOM_A1T 0xffa1
#define PCI_DEVICE_ID_BERKOM_T_CONCEPT 0xffa2
--
2.11.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next 5/8] net: ethernet: annapurna: add statistics helper
2017-02-03 18:12 [PATCH net-next 0/8] ARM: Alpine: Ethernet support Antoine Tenart
` (2 preceding siblings ...)
2017-02-03 18:12 ` [PATCH net-next 3/8] pci: add Annapurna Labs PCI id Antoine Tenart
@ 2017-02-03 18:12 ` Antoine Tenart
2017-02-03 19:34 ` Florian Fainelli
2017-02-03 21:24 ` kbuild test robot
2017-02-03 18:12 ` [PATCH net-next 6/8] net: ethernet: annapurna: add wol helpers to the Alpine driver Antoine Tenart
` (3 subsequent siblings)
7 siblings, 2 replies; 23+ messages in thread
From: Antoine Tenart @ 2017-02-03 18:12 UTC (permalink / raw)
To: linux-arm-kernel
Add the ndo_get_stats64() helper in the Annapurna Labs Alpine Ethernet
driver.
---
drivers/net/ethernet/annapurna/al_eth.c | 36 ++++
drivers/net/ethernet/annapurna/al_hw_eth.h | 9 +
drivers/net/ethernet/annapurna/al_hw_eth_main.c | 225 ++++++++++++++++++++++++
3 files changed, 270 insertions(+)
diff --git a/drivers/net/ethernet/annapurna/al_eth.c b/drivers/net/ethernet/annapurna/al_eth.c
index 779a885de014..8dd84f66b5d1 100644
--- a/drivers/net/ethernet/annapurna/al_eth.c
+++ b/drivers/net/ethernet/annapurna/al_eth.c
@@ -2388,6 +2388,41 @@ static void al_eth_set_msglevel(struct net_device *netdev, u32 value)
adapter->msg_enable = value;
}
+static void al_eth_get_stats64(struct net_device *netdev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct al_eth_adapter *adapter = netdev_priv(netdev);
+ struct al_eth_mac_stats *mac_stats = &adapter->mac_stats;
+
+ if (!adapter->up)
+ return NULL;
+
+ al_eth_mac_stats_get(&adapter->hw_adapter, mac_stats);
+
+ stats->rx_packets = mac_stats->aFramesReceivedOK; /* including pause frames */
+ stats->tx_packets = mac_stats->aFramesTransmittedOK; /* including pause frames */
+ stats->rx_bytes = mac_stats->aOctetsReceivedOK;
+ stats->tx_bytes = mac_stats->aOctetsTransmittedOK;
+ stats->rx_dropped = 0;
+ stats->multicast = mac_stats->ifInMulticastPkts;
+ stats->collisions = 0;
+
+ stats->rx_length_errors = (mac_stats->etherStatsUndersizePkts + /* good but short */
+ mac_stats->etherStatsFragments + /* short and bad*/
+ mac_stats->etherStatsJabbers + /* with crc errors */
+ mac_stats->etherStatsOversizePkts);
+ stats->rx_crc_errors = mac_stats->aFrameCheckSequenceErrors;
+ stats->rx_frame_errors = mac_stats->aAlignmentErrors;
+ stats->rx_fifo_errors = mac_stats->etherStatsDropEvents;
+ stats->rx_missed_errors = 0;
+ stats->tx_window_errors = 0;
+
+ stats->rx_errors = mac_stats->ifInErrors;
+ stats->tx_errors = mac_stats->ifOutErrors;
+
+ return stats;
+}
+
static void al_eth_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
@@ -2763,6 +2798,7 @@ static const struct net_device_ops al_eth_netdev_ops = {
.ndo_stop = al_eth_close,
.ndo_start_xmit = al_eth_start_xmit,
.ndo_select_queue = al_eth_select_queue,
+ .ndo_get_stats64 = al_eth_get_stats64,
.ndo_do_ioctl = al_eth_ioctl,
.ndo_tx_timeout = al_eth_tx_timeout,
.ndo_change_mtu = al_eth_change_mtu,
diff --git a/drivers/net/ethernet/annapurna/al_hw_eth.h b/drivers/net/ethernet/annapurna/al_hw_eth.h
index b2fc58793b3a..a44f3f200838 100644
--- a/drivers/net/ethernet/annapurna/al_hw_eth.h
+++ b/drivers/net/ethernet/annapurna/al_hw_eth.h
@@ -982,6 +982,15 @@ struct al_eth_mac_stats {
};
/*
+ * get mac statistics
+ * @param adapter pointer to the private structure.
+ * @param stats pointer to structure that will be filled with statistics.
+ *
+ * @return return 0 on success. otherwise on failure.
+ */
+int al_eth_mac_stats_get(struct al_hw_eth_adapter *adapter, struct al_eth_mac_stats *stats);
+
+/*
* perform Function Level Reset RMN
*
* Addressing RMN: 714
diff --git a/drivers/net/ethernet/annapurna/al_hw_eth_main.c b/drivers/net/ethernet/annapurna/al_hw_eth_main.c
index abb9ffd09fbf..dac0c1e2a941 100644
--- a/drivers/net/ethernet/annapurna/al_hw_eth_main.c
+++ b/drivers/net/ethernet/annapurna/al_hw_eth_main.c
@@ -2639,6 +2639,231 @@ int al_eth_flow_control_config(struct al_hw_eth_adapter *adapter,
return 0;
}
+/* get statistics */
+int al_eth_mac_stats_get(struct al_hw_eth_adapter *adapter, struct al_eth_mac_stats *stats)
+{
+ WARN_ON(!stats);
+
+ memset(stats, 0, sizeof(struct al_eth_mac_stats));
+
+ if (AL_ETH_IS_1G_MAC(adapter->mac_mode)) {
+ struct al_eth_mac_1g_stats __iomem *reg_stats =
+ &adapter->mac_regs_base->mac_1g.stats;
+
+ stats->ifInUcastPkts = readl(®_stats->ifInUcastPkts);
+ stats->ifInMulticastPkts = readl(®_stats->ifInMulticastPkts);
+ stats->ifInBroadcastPkts = readl(®_stats->ifInBroadcastPkts);
+ stats->etherStatsPkts = readl(®_stats->etherStatsPkts);
+ stats->ifOutUcastPkts = readl(®_stats->ifOutUcastPkts);
+ stats->ifOutMulticastPkts = readl(®_stats->ifOutMulticastPkts);
+ stats->ifOutBroadcastPkts = readl(®_stats->ifOutBroadcastPkts);
+ stats->ifInErrors = readl(®_stats->ifInErrors);
+ stats->ifOutErrors = readl(®_stats->ifOutErrors);
+ stats->aFramesReceivedOK = readl(®_stats->aFramesReceivedOK);
+ stats->aFramesTransmittedOK = readl(®_stats->aFramesTransmittedOK);
+ stats->aOctetsReceivedOK = readl(®_stats->aOctetsReceivedOK);
+ stats->aOctetsTransmittedOK = readl(®_stats->aOctetsTransmittedOK);
+ stats->etherStatsUndersizePkts = readl(®_stats->etherStatsUndersizePkts);
+ stats->etherStatsFragments = readl(®_stats->etherStatsFragments);
+ stats->etherStatsJabbers = readl(®_stats->etherStatsJabbers);
+ stats->etherStatsOversizePkts = readl(®_stats->etherStatsOversizePkts);
+ stats->aFrameCheckSequenceErrors =
+ readl(®_stats->aFrameCheckSequenceErrors);
+ stats->aAlignmentErrors = readl(®_stats->aAlignmentErrors);
+ stats->etherStatsDropEvents = readl(®_stats->etherStatsDropEvents);
+ stats->aPAUSEMACCtrlFramesTransmitted =
+ readl(®_stats->aPAUSEMACCtrlFramesTransmitted);
+ stats->aPAUSEMACCtrlFramesReceived =
+ readl(®_stats->aPAUSEMACCtrlFramesReceived);
+ stats->aFrameTooLongErrors = 0; /* N/A */
+ stats->aInRangeLengthErrors = 0; /* N/A */
+ stats->VLANTransmittedOK = 0; /* N/A */
+ stats->VLANReceivedOK = 0; /* N/A */
+ stats->etherStatsOctets = readl(®_stats->etherStatsOctets);
+ stats->etherStatsPkts64Octets = readl(®_stats->etherStatsPkts64Octets);
+ stats->etherStatsPkts65to127Octets =
+ readl(®_stats->etherStatsPkts65to127Octets);
+ stats->etherStatsPkts128to255Octets =
+ readl(®_stats->etherStatsPkts128to255Octets);
+ stats->etherStatsPkts256to511Octets =
+ readl(®_stats->etherStatsPkts256to511Octets);
+ stats->etherStatsPkts512to1023Octets =
+ readl(®_stats->etherStatsPkts512to1023Octets);
+ stats->etherStatsPkts1024to1518Octets =
+ readl(®_stats->etherStatsPkts1024to1518Octets);
+ stats->etherStatsPkts1519toX = readl(®_stats->etherStatsPkts1519toX);
+ } else if (AL_ETH_IS_10G_MAC(adapter->mac_mode) || AL_ETH_IS_25G_MAC(adapter->mac_mode)) {
+ if (adapter->rev_id < AL_ETH_REV_ID_3) {
+ struct al_eth_mac_10g_stats_v2 __iomem *reg_stats =
+ &adapter->mac_regs_base->mac_10g.stats.v2;
+ u64 octets;
+
+ stats->ifInUcastPkts = readl(®_stats->ifInUcastPkts);
+ stats->ifInMulticastPkts = readl(®_stats->ifInMulticastPkts);
+ stats->ifInBroadcastPkts = readl(®_stats->ifInBroadcastPkts);
+ stats->etherStatsPkts = readl(®_stats->etherStatsPkts);
+ stats->ifOutUcastPkts = readl(®_stats->ifOutUcastPkts);
+ stats->ifOutMulticastPkts = readl(®_stats->ifOutMulticastPkts);
+ stats->ifOutBroadcastPkts = readl(®_stats->ifOutBroadcastPkts);
+ stats->ifInErrors = readl(®_stats->ifInErrors);
+ stats->ifOutErrors = readl(®_stats->ifOutErrors);
+ stats->aFramesReceivedOK = readl(®_stats->aFramesReceivedOK);
+ stats->aFramesTransmittedOK = readl(®_stats->aFramesTransmittedOK);
+
+ /* aOctetsReceivedOK = ifInOctets - 18 * aFramesReceivedOK - 4 * VLANReceivedOK */
+ octets = readl(®_stats->ifInOctetsL);
+ octets |= (u64)(readl(®_stats->ifInOctetsH)) << 32;
+ octets -= 18 * stats->aFramesReceivedOK;
+ octets -= 4 * readl(®_stats->VLANReceivedOK);
+ stats->aOctetsReceivedOK = octets;
+
+ /* aOctetsTransmittedOK = ifOutOctets - 18 * aFramesTransmittedOK - 4 * VLANTransmittedOK */
+ octets = readl(®_stats->ifOutOctetsL);
+ octets |= (u64)(readl(®_stats->ifOutOctetsH)) << 32;
+ octets -= 18 * stats->aFramesTransmittedOK;
+ octets -= 4 * readl(®_stats->VLANTransmittedOK);
+ stats->aOctetsTransmittedOK = octets;
+
+ stats->etherStatsUndersizePkts = readl(®_stats->etherStatsUndersizePkts);
+ stats->etherStatsFragments = readl(®_stats->etherStatsFragments);
+ stats->etherStatsJabbers = readl(®_stats->etherStatsJabbers);
+ stats->etherStatsOversizePkts = readl(®_stats->etherStatsOversizePkts);
+ stats->aFrameCheckSequenceErrors = readl(®_stats->aFrameCheckSequenceErrors);
+ stats->aAlignmentErrors = readl(®_stats->aAlignmentErrors);
+ stats->etherStatsDropEvents = readl(®_stats->etherStatsDropEvents);
+ stats->aPAUSEMACCtrlFramesTransmitted = readl(®_stats->aPAUSEMACCtrlFramesTransmitted);
+ stats->aPAUSEMACCtrlFramesReceived = readl(®_stats->aPAUSEMACCtrlFramesReceived);
+ stats->aFrameTooLongErrors = readl(®_stats->aFrameTooLongErrors);
+ stats->aInRangeLengthErrors = readl(®_stats->aInRangeLengthErrors);
+ stats->VLANTransmittedOK = readl(®_stats->VLANTransmittedOK);
+ stats->VLANReceivedOK = readl(®_stats->VLANReceivedOK);
+ stats->etherStatsOctets = readl(®_stats->etherStatsOctets);
+ stats->etherStatsPkts64Octets = readl(®_stats->etherStatsPkts64Octets);
+ stats->etherStatsPkts65to127Octets = readl(®_stats->etherStatsPkts65to127Octets);
+ stats->etherStatsPkts128to255Octets = readl(®_stats->etherStatsPkts128to255Octets);
+ stats->etherStatsPkts256to511Octets = readl(®_stats->etherStatsPkts256to511Octets);
+ stats->etherStatsPkts512to1023Octets = readl(®_stats->etherStatsPkts512to1023Octets);
+ stats->etherStatsPkts1024to1518Octets = readl(®_stats->etherStatsPkts1024to1518Octets);
+ stats->etherStatsPkts1519toX = readl(®_stats->etherStatsPkts1519toX);
+ } else {
+ struct al_eth_mac_10g_stats_v3_rx __iomem *reg_rx_stats =
+ &adapter->mac_regs_base->mac_10g.stats.v3.rx;
+ struct al_eth_mac_10g_stats_v3_tx __iomem *reg_tx_stats =
+ &adapter->mac_regs_base->mac_10g.stats.v3.tx;
+ u64 octets;
+
+ stats->ifInUcastPkts = readl(®_rx_stats->ifInUcastPkts);
+ stats->ifInMulticastPkts = readl(®_rx_stats->ifInMulticastPkts);
+ stats->ifInBroadcastPkts = readl(®_rx_stats->ifInBroadcastPkts);
+ stats->etherStatsPkts = readl(®_rx_stats->etherStatsPkts);
+ stats->ifOutUcastPkts = readl(®_tx_stats->ifUcastPkts);
+ stats->ifOutMulticastPkts = readl(®_tx_stats->ifMulticastPkts);
+ stats->ifOutBroadcastPkts = readl(®_tx_stats->ifBroadcastPkts);
+ stats->ifInErrors = readl(®_rx_stats->ifInErrors);
+ stats->ifOutErrors = readl(®_tx_stats->ifOutErrors);
+ stats->aFramesReceivedOK = readl(®_rx_stats->FramesOK);
+ stats->aFramesTransmittedOK = readl(®_tx_stats->FramesOK);
+
+ /* aOctetsReceivedOK = ifInOctets - 18 * aFramesReceivedOK - 4 * VLANReceivedOK */
+ octets = readl(®_rx_stats->ifOctetsL);
+ octets |= (u64)(readl(®_rx_stats->ifOctetsH)) << 32;
+ octets -= 18 * stats->aFramesReceivedOK;
+ octets -= 4 * readl(®_rx_stats->VLANOK);
+ stats->aOctetsReceivedOK = octets;
+
+ /* aOctetsTransmittedOK = ifOutOctets - 18 * aFramesTransmittedOK - 4 * VLANTransmittedOK */
+ octets = readl(®_tx_stats->ifOctetsL);
+ octets |= (u64)(readl(®_tx_stats->ifOctetsH)) << 32;
+ octets -= 18 * stats->aFramesTransmittedOK;
+ octets -= 4 * readl(®_tx_stats->VLANOK);
+ stats->aOctetsTransmittedOK = octets;
+
+ stats->etherStatsUndersizePkts = readl(®_rx_stats->etherStatsUndersizePkts);
+ stats->etherStatsFragments = readl(®_rx_stats->etherStatsFragments);
+ stats->etherStatsJabbers = readl(®_rx_stats->etherStatsJabbers);
+ stats->etherStatsOversizePkts = readl(®_rx_stats->etherStatsOversizePkts);
+ stats->aFrameCheckSequenceErrors = readl(®_rx_stats->CRCErrors);
+ stats->aAlignmentErrors = readl(®_rx_stats->aAlignmentErrors);
+ stats->etherStatsDropEvents = readl(®_rx_stats->etherStatsDropEvents);
+ stats->aPAUSEMACCtrlFramesTransmitted = readl(®_tx_stats->aPAUSEMACCtrlFrames);
+ stats->aPAUSEMACCtrlFramesReceived = readl(®_rx_stats->aPAUSEMACCtrlFrames);
+ stats->aFrameTooLongErrors = readl(®_rx_stats->aFrameTooLong);
+ stats->aInRangeLengthErrors = readl(®_rx_stats->aInRangeLengthErrors);
+ stats->VLANTransmittedOK = readl(®_tx_stats->VLANOK);
+ stats->VLANReceivedOK = readl(®_rx_stats->VLANOK);
+ stats->etherStatsOctets = readl(®_rx_stats->etherStatsOctets);
+ stats->etherStatsPkts64Octets = readl(®_rx_stats->etherStatsPkts64Octets);
+ stats->etherStatsPkts65to127Octets = readl(®_rx_stats->etherStatsPkts65to127Octets);
+ stats->etherStatsPkts128to255Octets = readl(®_rx_stats->etherStatsPkts128to255Octets);
+ stats->etherStatsPkts256to511Octets = readl(®_rx_stats->etherStatsPkts256to511Octets);
+ stats->etherStatsPkts512to1023Octets = readl(®_rx_stats->etherStatsPkts512to1023Octets);
+ stats->etherStatsPkts1024to1518Octets = readl(®_rx_stats->etherStatsPkts1024to1518Octets);
+ stats->etherStatsPkts1519toX = readl(®_rx_stats->etherStatsPkts1519toMax);
+ }
+ } else {
+ struct al_eth_mac_10g_stats_v3_rx __iomem *reg_rx_stats =
+ &adapter->mac_regs_base->mac_10g.stats.v3.rx;
+ struct al_eth_mac_10g_stats_v3_tx __iomem *reg_tx_stats =
+ &adapter->mac_regs_base->mac_10g.stats.v3.tx;
+ u64 octets;
+
+ /* 40G MAC statistics registers are the same, only read indirectly */
+ #define _40g_mac_reg_read32(field) al_eth_40g_mac_reg_read(adapter, \
+ ((u8 *)(field)) - ((u8 *)&adapter->mac_regs_base->mac_10g))
+
+ stats->ifInUcastPkts = _40g_mac_reg_read32(®_rx_stats->ifInUcastPkts);
+ stats->ifInMulticastPkts = _40g_mac_reg_read32(®_rx_stats->ifInMulticastPkts);
+ stats->ifInBroadcastPkts = _40g_mac_reg_read32(®_rx_stats->ifInBroadcastPkts);
+ stats->etherStatsPkts = _40g_mac_reg_read32(®_rx_stats->etherStatsPkts);
+ stats->ifOutUcastPkts = _40g_mac_reg_read32(®_tx_stats->ifUcastPkts);
+ stats->ifOutMulticastPkts = _40g_mac_reg_read32(®_tx_stats->ifMulticastPkts);
+ stats->ifOutBroadcastPkts = _40g_mac_reg_read32(®_tx_stats->ifBroadcastPkts);
+ stats->ifInErrors = _40g_mac_reg_read32(®_rx_stats->ifInErrors);
+ stats->ifOutErrors = _40g_mac_reg_read32(®_tx_stats->ifOutErrors);
+ stats->aFramesReceivedOK = _40g_mac_reg_read32(®_rx_stats->FramesOK);
+ stats->aFramesTransmittedOK = _40g_mac_reg_read32(®_tx_stats->FramesOK);
+
+ /* aOctetsReceivedOK = ifInOctets - 18 * aFramesReceivedOK - 4 * VLANReceivedOK */
+ octets = _40g_mac_reg_read32(®_rx_stats->ifOctetsL);
+ octets |= (u64)(_40g_mac_reg_read32(®_rx_stats->ifOctetsH)) << 32;
+ octets -= 18 * stats->aFramesReceivedOK;
+ octets -= 4 * _40g_mac_reg_read32(®_rx_stats->VLANOK);
+ stats->aOctetsReceivedOK = octets;
+
+ /* aOctetsTransmittedOK = ifOutOctets - 18 * aFramesTransmittedOK - 4 * VLANTransmittedOK */
+ octets = _40g_mac_reg_read32(®_tx_stats->ifOctetsL);
+ octets |= (u64)(_40g_mac_reg_read32(®_tx_stats->ifOctetsH)) << 32;
+ octets -= 18 * stats->aFramesTransmittedOK;
+ octets -= 4 * _40g_mac_reg_read32(®_tx_stats->VLANOK);
+ stats->aOctetsTransmittedOK = octets;
+
+ stats->etherStatsUndersizePkts = _40g_mac_reg_read32(®_rx_stats->etherStatsUndersizePkts);
+ stats->etherStatsFragments = _40g_mac_reg_read32(®_rx_stats->etherStatsFragments);
+ stats->etherStatsJabbers = _40g_mac_reg_read32(®_rx_stats->etherStatsJabbers);
+ stats->etherStatsOversizePkts = _40g_mac_reg_read32(®_rx_stats->etherStatsOversizePkts);
+ stats->aFrameCheckSequenceErrors = _40g_mac_reg_read32(®_rx_stats->CRCErrors);
+ stats->aAlignmentErrors = _40g_mac_reg_read32(®_rx_stats->aAlignmentErrors);
+ stats->etherStatsDropEvents = _40g_mac_reg_read32(®_rx_stats->etherStatsDropEvents);
+ stats->aPAUSEMACCtrlFramesTransmitted = _40g_mac_reg_read32(®_tx_stats->aPAUSEMACCtrlFrames);
+ stats->aPAUSEMACCtrlFramesReceived = _40g_mac_reg_read32(®_rx_stats->aPAUSEMACCtrlFrames);
+ stats->aFrameTooLongErrors = _40g_mac_reg_read32(®_rx_stats->aFrameTooLong);
+ stats->aInRangeLengthErrors = _40g_mac_reg_read32(®_rx_stats->aInRangeLengthErrors);
+ stats->VLANTransmittedOK = _40g_mac_reg_read32(®_tx_stats->VLANOK);
+ stats->VLANReceivedOK = _40g_mac_reg_read32(®_rx_stats->VLANOK);
+ stats->etherStatsOctets = _40g_mac_reg_read32(®_rx_stats->etherStatsOctets);
+ stats->etherStatsPkts64Octets = _40g_mac_reg_read32(®_rx_stats->etherStatsPkts64Octets);
+ stats->etherStatsPkts65to127Octets = _40g_mac_reg_read32(®_rx_stats->etherStatsPkts65to127Octets);
+ stats->etherStatsPkts128to255Octets = _40g_mac_reg_read32(®_rx_stats->etherStatsPkts128to255Octets);
+ stats->etherStatsPkts256to511Octets = _40g_mac_reg_read32(®_rx_stats->etherStatsPkts256to511Octets);
+ stats->etherStatsPkts512to1023Octets = _40g_mac_reg_read32(®_rx_stats->etherStatsPkts512to1023Octets);
+ stats->etherStatsPkts1024to1518Octets = _40g_mac_reg_read32(®_rx_stats->etherStatsPkts1024to1518Octets);
+ stats->etherStatsPkts1519toX = _40g_mac_reg_read32(®_rx_stats->etherStatsPkts1519toMax);
+ }
+
+/* stats->etherStatsPkts = 1; */
+ return 0;
+}
+
/* Traffic control */
int al_eth_flr_rmn(int (*pci_read_config_u32)(void *handle, int where, u32 *val),
--
2.11.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next 6/8] net: ethernet: annapurna: add wol helpers to the Alpine driver
2017-02-03 18:12 [PATCH net-next 0/8] ARM: Alpine: Ethernet support Antoine Tenart
` (3 preceding siblings ...)
2017-02-03 18:12 ` [PATCH net-next 5/8] net: ethernet: annapurna: add statistics helper Antoine Tenart
@ 2017-02-03 18:12 ` Antoine Tenart
2017-02-03 18:21 ` Sergei Shtylyov
2017-02-03 18:12 ` [PATCH net-next 7/8] net: ethernet: annapurna: add eee " Antoine Tenart
` (2 subsequent siblings)
7 siblings, 1 reply; 23+ messages in thread
From: Antoine Tenart @ 2017-02-03 18:12 UTC (permalink / raw)
To: linux-arm-kernel
Implement the get_wol() and set_wol() helpers in the Annapurna Labs
Alpine Ethernet driver.
---
drivers/net/ethernet/annapurna/al_eth.c | 44 +++++++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/drivers/net/ethernet/annapurna/al_eth.c b/drivers/net/ethernet/annapurna/al_eth.c
index 8dd84f66b5d1..d06a75a49ce5 100644
--- a/drivers/net/ethernet/annapurna/al_eth.c
+++ b/drivers/net/ethernet/annapurna/al_eth.c
@@ -2519,10 +2519,54 @@ static u32 al_eth_get_rxfh_indir_size(struct net_device *netdev)
return AL_ETH_RX_RSS_TABLE_SIZE;
}
+static void al_eth_get_wol(struct net_device *netdev,
+ struct ethtool_wolinfo *wol)
+{
+ struct al_eth_adapter *adapter = netdev_priv(netdev);
+ struct phy_device *phydev;
+
+ wol->wolopts = adapter->wol;
+
+ if ((adapter) && (adapter->phy_exist) && (adapter->mdio_bus)) {
+ phydev = mdiobus_get_phy(adapter->mdio_bus, adapter->phy_addr);
+ if (phydev) {
+ phy_ethtool_get_wol(phydev, wol);
+ wol->supported |= WAKE_PHY;
+ return;
+ }
+ }
+
+ wol->supported |= WAKE_UCAST | WAKE_MCAST | WAKE_BCAST;
+}
+
+static int al_eth_set_wol(struct net_device *netdev,
+ struct ethtool_wolinfo *wol)
+{
+ struct al_eth_adapter *adapter = netdev_priv(netdev);
+ struct phy_device *phydev;
+
+ if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE))
+ return -EOPNOTSUPP;
+
+ adapter->wol = wol->wolopts;
+
+ if ((adapter) && (adapter->phy_exist) && (adapter->mdio_bus)) {
+ phydev = mdiobus_get_phy(adapter->mdio_bus, adapter->phy_addr);
+ if (phydev)
+ return phy_ethtool_set_wol(phydev, wol);
+ }
+
+ device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
+
+ return 0;
+}
+
static const struct ethtool_ops al_eth_ethtool_ops = {
.get_settings = al_eth_get_settings,
.set_settings = al_eth_set_settings,
.get_drvinfo = al_eth_get_drvinfo,
+ .get_wol = al_eth_get_wol,
+ .set_wol = al_eth_set_wol,
.get_msglevel = al_eth_get_msglevel,
.set_msglevel = al_eth_set_msglevel,
--
2.11.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next 7/8] net: ethernet: annapurna: add eee helpers to the Alpine driver
2017-02-03 18:12 [PATCH net-next 0/8] ARM: Alpine: Ethernet support Antoine Tenart
` (4 preceding siblings ...)
2017-02-03 18:12 ` [PATCH net-next 6/8] net: ethernet: annapurna: add wol helpers to the Alpine driver Antoine Tenart
@ 2017-02-03 18:12 ` Antoine Tenart
2017-02-03 19:01 ` Florian Fainelli
2017-02-03 18:12 ` [PATCH net-next 8/8] net: ethernet: annapurna: add the coalesce " Antoine Tenart
[not found] ` <20170203181216.30214-5-antoine.tenart@free-electrons.com>
7 siblings, 1 reply; 23+ messages in thread
From: Antoine Tenart @ 2017-02-03 18:12 UTC (permalink / raw)
To: linux-arm-kernel
Add the get_eee() and set_eee() helpers to support the Energy-Efficient
(EEE) feature in the Annapurna Labs Alpine driver.
---
drivers/net/ethernet/annapurna/al_eth.c | 44 +++++++++++
drivers/net/ethernet/annapurna/al_hw_eth.h | 28 +++++++
drivers/net/ethernet/annapurna/al_hw_eth_ec_regs.h | 11 +++
.../net/ethernet/annapurna/al_hw_eth_mac_regs.h | 11 +++
drivers/net/ethernet/annapurna/al_hw_eth_main.c | 92 ++++++++++++++++++++++
5 files changed, 186 insertions(+)
diff --git a/drivers/net/ethernet/annapurna/al_eth.c b/drivers/net/ethernet/annapurna/al_eth.c
index d06a75a49ce5..674dafdb638a 100644
--- a/drivers/net/ethernet/annapurna/al_eth.c
+++ b/drivers/net/ethernet/annapurna/al_eth.c
@@ -2519,6 +2519,47 @@ static u32 al_eth_get_rxfh_indir_size(struct net_device *netdev)
return AL_ETH_RX_RSS_TABLE_SIZE;
}
+static int al_eth_get_eee(struct net_device *netdev,
+ struct ethtool_eee *edata)
+{
+ struct al_eth_adapter *adapter = netdev_priv(netdev);
+ struct al_eth_eee_params params;
+
+ if (!adapter->phy_exist)
+ return -EOPNOTSUPP;
+
+ al_eth_eee_get(&adapter->hw_adapter, ¶ms);
+
+ edata->eee_enabled = params.enable;
+ edata->tx_lpi_timer = params.tx_eee_timer;
+
+ return phy_ethtool_get_eee(adapter->phydev, edata);
+}
+
+static int al_eth_set_eee(struct net_device *netdev,
+ struct ethtool_eee *edata)
+{
+ struct al_eth_adapter *adapter = netdev_priv(netdev);
+ struct al_eth_eee_params params;
+
+ struct phy_device *phydev;
+
+ if (!adapter->phy_exist)
+ return -EOPNOTSUPP;
+
+ phydev = mdiobus_get_phy(adapter->mdio_bus, adapter->phy_addr);
+
+ phy_init_eee(phydev, 1);
+
+ params.enable = edata->eee_enabled;
+ params.tx_eee_timer = edata->tx_lpi_timer;
+ params.min_interval = 10;
+
+ al_eth_eee_config(&adapter->hw_adapter, ¶ms);
+
+ return phy_ethtool_set_eee(phydev, edata);
+}
+
static void al_eth_get_wol(struct net_device *netdev,
struct ethtool_wolinfo *wol)
{
@@ -2576,6 +2617,9 @@ static const struct ethtool_ops al_eth_ethtool_ops = {
.set_pauseparam = al_eth_set_pauseparam,
.get_rxnfc = al_eth_get_rxnfc,
.get_rxfh_indir_size = al_eth_get_rxfh_indir_size,
+
+ .get_eee = al_eth_get_eee,
+ .set_eee = al_eth_set_eee,
};
static void al_eth_tx_csum(struct al_eth_ring *tx_ring,
diff --git a/drivers/net/ethernet/annapurna/al_hw_eth.h b/drivers/net/ethernet/annapurna/al_hw_eth.h
index a44f3f200838..2ef11de6b1db 100644
--- a/drivers/net/ethernet/annapurna/al_hw_eth.h
+++ b/drivers/net/ethernet/annapurna/al_hw_eth.h
@@ -884,6 +884,34 @@ int al_eth_filter_config(struct al_hw_eth_adapter *adapter, struct al_eth_filter
int al_eth_flow_control_config(struct al_hw_eth_adapter *adapter, struct al_eth_flow_control_params *params);
+struct al_eth_eee_params {
+ u8 enable;
+ u32 tx_eee_timer; /* time in cycles the interface delays prior to entering eee state */
+ u32 min_interval; /* minimum interval in cycles between two eee states */
+ u32 stop_cnt; /* time in cycles to stop Tx mac i/f after getting out of eee state */
+ bool fast_wake; /* fast_wake is only applicable to 40/50G, otherwise the mode is deep_sleep */
+};
+
+/*
+ * configure EEE mode
+ * @param adapter pointer to the private structure.
+ * @param params pointer to the eee input parameters.
+ *
+ * @return return 0 on success. otherwise on failure.
+ */
+int al_eth_eee_config(struct al_hw_eth_adapter *adapter,
+ struct al_eth_eee_params *params);
+
+/*
+ * get EEE configuration
+ * @param adapter pointer to the private structure.
+ * @param params pointer to the eee output parameters.
+ *
+ * @return return 0 on success. otherwise on failure.
+ */
+int al_eth_eee_get(struct al_hw_eth_adapter *adapter,
+ struct al_eth_eee_params *params);
+
/* enum for methods when updating systime using triggers */
enum al_eth_pth_update_method {
AL_ETH_PTH_UPDATE_METHOD_SET = 0, /* Set the time in int/ext update time */
diff --git a/drivers/net/ethernet/annapurna/al_hw_eth_ec_regs.h b/drivers/net/ethernet/annapurna/al_hw_eth_ec_regs.h
index c239ac1e8b6c..c9353012101e 100644
--- a/drivers/net/ethernet/annapurna/al_hw_eth_ec_regs.h
+++ b/drivers/net/ethernet/annapurna/al_hw_eth_ec_regs.h
@@ -1085,4 +1085,15 @@ struct al_ec_regs {
/* Threshold high */
#define EC_EFC_RX_FIFO_HYST_TH_HIGH_SHIFT 16
+/* Use Ethernet controller Tx FIFO empty status for EEE control */
+#define EC_EEE_CFG_E_USE_EC_TX_FIFO BIT(2)
+/* Use Ethernet controller Rx FIFO empty status for EEE control */
+#define EC_EEE_CFG_E_USE_EC_RX_FIFO BIT(3)
+/* Enable Low power signalling. */
+#define EC_EEE_CFG_E_ENABLE BIT(4)
+/* Mask output to MAC. */
+#define EC_EEE_CFG_E_MASK_MAC_EEE BIT(8)
+/* Mask output to stop MAC interface. */
+#define EC_EEE_CFG_E_MASK_EC_TMI_STOP BIT(9)
+
#endif /* __AL_HW_EC_REG_H */
diff --git a/drivers/net/ethernet/annapurna/al_hw_eth_mac_regs.h b/drivers/net/ethernet/annapurna/al_hw_eth_mac_regs.h
index b2b956b7e28f..4201f3c4b01f 100644
--- a/drivers/net/ethernet/annapurna/al_hw_eth_mac_regs.h
+++ b/drivers/net/ethernet/annapurna/al_hw_eth_mac_regs.h
@@ -692,6 +692,10 @@ struct al_eth_mac_regs {
/* LED default value */
#define ETH_MAC_GEN_LED_CFG_DEF BIT(4)
+/* EEE timer value */
+#define ETH_MAC_KR_PCS_CFG_EEE_TIMER_VAL_MASK 0x0000FF00
+#define ETH_MAC_KR_PCS_CFG_EEE_TIMER_VAL_SHIFT 8
+
#define ETH_MAC_SGMII_REG_ADDR_CTRL_REG 0x0
#define ETH_MAC_SGMII_REG_ADDR_IF_MODE_REG 0x14
@@ -703,6 +707,10 @@ struct al_eth_mac_regs {
#define ETH_MAC_SGMII_REG_DATA_IF_MODE_SGMII_SPEED_1000 0x2
#define ETH_MAC_SGMII_REG_DATA_IF_MODE_SGMII_DUPLEX BIT(4)
+/* Low power timer configuration */
+#define ETH_MAC_GEN_V3_PCS_40G_LL_EEE_CFG_TIMER_VAL_MASK 0x000000FF
+#define ETH_MAC_GEN_V3_PCS_40G_LL_EEE_CFG_TIMER_VAL_SHIFT 0
+
/* command config */
#define ETH_MAC_GEN_V3_MAC_40G_COMMAND_CONFIG_ADDR 0x00000008
#define ETH_MAC_GEN_V3_MAC_40G_COMMAND_CONFIG_TX_ENA BIT(0)
@@ -724,4 +732,7 @@ struct al_eth_mac_regs {
/* spare */
#define ETH_MAC_GEN_V3_SPARE_CHICKEN_DISABLE_TIMESTAMP_STRETCH BIT(0)
+/* 40g EEE control and capability */
+#define ETH_MAC_GEN_V3_PCS_40G_EEE_CONTROL_ADDR 0x00000028
+
#endif /* __AL_HW_ETH_MAC_REGS_H__ */
diff --git a/drivers/net/ethernet/annapurna/al_hw_eth_main.c b/drivers/net/ethernet/annapurna/al_hw_eth_main.c
index dac0c1e2a941..99ed601332da 100644
--- a/drivers/net/ethernet/annapurna/al_hw_eth_main.c
+++ b/drivers/net/ethernet/annapurna/al_hw_eth_main.c
@@ -20,6 +20,12 @@
#define AL_ADDR_LOW(x) ((u32)((dma_addr_t)(x)))
#define AL_ADDR_HIGH(x) ((u32)((((dma_addr_t)(x)) >> 16) >> 16))
+/* Number of xfi_txclk cycles that accumulate into 100ns */
+#define ETH_MAC_KR_10_PCS_CFG_EEE_TIMER_VAL 52
+#define ETH_MAC_KR_25_PCS_CFG_EEE_TIMER_VAL 80
+#define ETH_MAC_XLG_40_PCS_CFG_EEE_TIMER_VAL 63
+#define ETH_MAC_XLG_50_PCS_CFG_EEE_TIMER_VAL 85
+
#define AL_ETH_TX_PKT_UDMA_FLAGS (AL_ETH_TX_FLAGS_NO_SNOOP | \
AL_ETH_TX_FLAGS_INT)
@@ -2639,6 +2645,89 @@ int al_eth_flow_control_config(struct al_hw_eth_adapter *adapter,
return 0;
}
+int al_eth_eee_get(struct al_hw_eth_adapter *adapter,
+ struct al_eth_eee_params *params)
+{
+ u32 reg;
+
+ netdev_dbg(adapter->netdev, "[%s]: getting eee.\n", adapter->name);
+
+ reg = readl(&adapter->ec_regs_base->eee.cfg_e);
+ params->enable = (reg & EC_EEE_CFG_E_ENABLE) ? true : false;
+
+ params->tx_eee_timer = readl(&adapter->ec_regs_base->eee.pre_cnt);
+ params->min_interval = readl(&adapter->ec_regs_base->eee.post_cnt);
+ params->stop_cnt = readl(&adapter->ec_regs_base->eee.stop_cnt);
+
+ return 0;
+}
+
+int al_eth_eee_config(struct al_hw_eth_adapter *adapter,
+ struct al_eth_eee_params *params)
+{
+ u32 reg;
+
+ netdev_dbg(adapter->netdev, "[%s]: config eee.\n", adapter->name);
+
+ if (params->enable == 0) {
+ netdev_dbg(adapter->netdev, "[%s]: disable eee.\n",
+ adapter->name);
+ writel(0, &adapter->ec_regs_base->eee.cfg_e);
+ return 0;
+ }
+ if (AL_ETH_IS_10G_MAC(adapter->mac_mode) || AL_ETH_IS_25G_MAC(adapter->mac_mode)) {
+ reg = readl(&adapter->mac_regs_base->kr.pcs_cfg);
+ reg &= ~ETH_MAC_KR_PCS_CFG_EEE_TIMER_VAL_MASK;
+ reg |= (AL_ETH_IS_10G_MAC(adapter->mac_mode) ?
+ ETH_MAC_KR_10_PCS_CFG_EEE_TIMER_VAL :
+ ETH_MAC_KR_25_PCS_CFG_EEE_TIMER_VAL) <<
+ ETH_MAC_KR_PCS_CFG_EEE_TIMER_VAL_SHIFT,
+ writel(reg, &adapter->mac_regs_base->kr.pcs_cfg);
+ }
+ if ((adapter->mac_mode == AL_ETH_MAC_MODE_XLG_LL_40G) ||
+ (adapter->mac_mode == AL_ETH_MAC_MODE_XLG_LL_50G)) {
+ reg = readl(&adapter->mac_regs_base->gen_v3.pcs_40g_ll_eee_cfg);
+ reg &= ~ETH_MAC_GEN_V3_PCS_40G_LL_EEE_CFG_TIMER_VAL_MASK;
+ reg |= ((adapter->mac_mode == AL_ETH_MAC_MODE_XLG_LL_40G) ?
+ ETH_MAC_XLG_40_PCS_CFG_EEE_TIMER_VAL :
+ ETH_MAC_XLG_50_PCS_CFG_EEE_TIMER_VAL) <<
+ ETH_MAC_GEN_V3_PCS_40G_LL_EEE_CFG_TIMER_VAL_SHIFT;
+ writel(reg, &adapter->mac_regs_base->gen_v3.pcs_40g_ll_eee_cfg);
+
+ /* set Deep sleep mode as the LPI function (instead of Fast wake mode) */
+ al_eth_40g_pcs_reg_write(adapter, ETH_MAC_GEN_V3_PCS_40G_EEE_CONTROL_ADDR,
+ params->fast_wake ? 1 : 0);
+ }
+
+ writel(params->tx_eee_timer, &adapter->ec_regs_base->eee.pre_cnt);
+ writel(params->min_interval, &adapter->ec_regs_base->eee.post_cnt);
+ writel(params->stop_cnt, &adapter->ec_regs_base->eee.stop_cnt);
+
+ reg = EC_EEE_CFG_E_MASK_EC_TMI_STOP | EC_EEE_CFG_E_MASK_MAC_EEE |
+ EC_EEE_CFG_E_ENABLE | EC_EEE_CFG_E_USE_EC_TX_FIFO |
+ EC_EEE_CFG_E_USE_EC_RX_FIFO;
+
+ /*
+ * Addressing RMN: 3732
+ *
+ * RMN description:
+ * When the HW get into eee mode, it can't transmit any pause packet
+ * (when flow control policy is enabled).
+ * In such case, the HW has no way to handle extreme pushback from
+ * the Rx_path fifos.
+ *
+ * Software flow:
+ * Configure RX_FIFO empty as eee mode term.
+ * That way, nothing will prevent pause packet transmittion in
+ * case of extreme pushback from the Rx_path fifos.
+ *
+ */
+
+ writel(reg, &adapter->ec_regs_base->eee.cfg_e);
+
+ return 0;
+}
+
/* get statistics */
int al_eth_mac_stats_get(struct al_hw_eth_adapter *adapter, struct al_eth_mac_stats *stats)
{
@@ -2860,6 +2949,9 @@ int al_eth_mac_stats_get(struct al_hw_eth_adapter *adapter, struct al_eth_mac_st
stats->etherStatsPkts1519toX = _40g_mac_reg_read32(®_rx_stats->etherStatsPkts1519toMax);
}
+ stats->eee_in = readl(&adapter->mac_regs_base->stat.eee_in);
+ stats->eee_out = readl(&adapter->mac_regs_base->stat.eee_out);
+
/* stats->etherStatsPkts = 1; */
return 0;
}
--
2.11.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next 8/8] net: ethernet: annapurna: add the coalesce helpers to the Alpine driver
2017-02-03 18:12 [PATCH net-next 0/8] ARM: Alpine: Ethernet support Antoine Tenart
` (5 preceding siblings ...)
2017-02-03 18:12 ` [PATCH net-next 7/8] net: ethernet: annapurna: add eee " Antoine Tenart
@ 2017-02-03 18:12 ` Antoine Tenart
[not found] ` <20170203181216.30214-5-antoine.tenart@free-electrons.com>
7 siblings, 0 replies; 23+ messages in thread
From: Antoine Tenart @ 2017-02-03 18:12 UTC (permalink / raw)
To: linux-arm-kernel
Add the get_coalesce() and set_coalesce() helpers to the Annapurna Labs
Alpine Ethernet driver.
---
drivers/net/ethernet/annapurna/al_eth.c | 81 +++++++++++++++++++++++++++++++++
1 file changed, 81 insertions(+)
diff --git a/drivers/net/ethernet/annapurna/al_eth.c b/drivers/net/ethernet/annapurna/al_eth.c
index 674dafdb638a..369a5a7d5cc5 100644
--- a/drivers/net/ethernet/annapurna/al_eth.c
+++ b/drivers/net/ethernet/annapurna/al_eth.c
@@ -2132,6 +2132,9 @@ static void al_eth_config_rx_fwd(struct al_eth_adapter *adapter)
al_eth_fsm_table_init(adapter);
}
+static void al_eth_set_coalesce(struct al_eth_adapter *adapter,
+ unsigned int tx_usecs, unsigned int rx_usecs);
+
static void al_eth_restore_ethtool_params(struct al_eth_adapter *adapter)
{
int i;
@@ -2141,6 +2144,8 @@ static void al_eth_restore_ethtool_params(struct al_eth_adapter *adapter)
adapter->tx_usecs = 0;
adapter->rx_usecs = 0;
+ al_eth_set_coalesce(adapter, tx_usecs, rx_usecs);
+
for (i = 0; i < AL_ETH_RX_RSS_TABLE_SIZE; i++)
al_eth_thash_table_set(&adapter->hw_adapter, i, 0,
adapter->rss_ind_tbl[i]);
@@ -2364,6 +2369,80 @@ static int al_eth_set_settings(struct net_device *netdev,
return rc;
}
+static int al_eth_get_coalesce(struct net_device *net_dev,
+ struct ethtool_coalesce *coalesce)
+{
+ struct al_eth_adapter *adapter = netdev_priv(net_dev);
+
+ coalesce->tx_coalesce_usecs = adapter->tx_usecs;
+ coalesce->tx_coalesce_usecs_irq = adapter->tx_usecs;
+ coalesce->rx_coalesce_usecs = adapter->rx_usecs;
+ coalesce->rx_coalesce_usecs_irq = adapter->rx_usecs;
+ coalesce->use_adaptive_rx_coalesce = false;
+
+ return 0;
+}
+
+static void al_eth_set_coalesce(struct al_eth_adapter *adapter,
+ unsigned int tx_usecs, unsigned int rx_usecs)
+{
+ struct unit_regs *udma_base = (struct unit_regs *)(adapter->udma_base);
+ int qid;
+
+ if (adapter->tx_usecs != tx_usecs) {
+ uint interval = (tx_usecs + 15) / 16;
+
+ WARN_ON(interval > 255);
+
+ adapter->tx_usecs = interval * 16;
+ for (qid = 0; qid < adapter->num_tx_queues; qid++)
+ al_iofic_msix_moder_interval_config(
+ &udma_base->gen.interrupt_regs.main_iofic,
+ AL_INT_GROUP_C, qid, interval);
+ }
+ if (adapter->rx_usecs != rx_usecs) {
+ uint interval = (rx_usecs + 15) / 16;
+
+ WARN_ON(interval > 255);
+
+ adapter->rx_usecs = interval * 16;
+ for (qid = 0; qid < adapter->num_rx_queues; qid++)
+ al_iofic_msix_moder_interval_config(
+ &udma_base->gen.interrupt_regs.main_iofic,
+ AL_INT_GROUP_B, qid, interval);
+ }
+}
+
+static int al_eth_ethtool_set_coalesce(struct net_device *net_dev,
+ struct ethtool_coalesce *coalesce)
+{
+ struct al_eth_adapter *adapter = netdev_priv(net_dev);
+ unsigned int tx_usecs = adapter->tx_usecs;
+ unsigned int rx_usecs = adapter->rx_usecs;
+
+ if (coalesce->use_adaptive_tx_coalesce)
+ return -EINVAL;
+
+ if (coalesce->rx_coalesce_usecs != rx_usecs)
+ rx_usecs = coalesce->rx_coalesce_usecs;
+ else
+ rx_usecs = coalesce->rx_coalesce_usecs_irq;
+
+ if (coalesce->tx_coalesce_usecs != tx_usecs)
+ tx_usecs = coalesce->tx_coalesce_usecs;
+ else
+ tx_usecs = coalesce->tx_coalesce_usecs_irq;
+
+ if (tx_usecs > (255 * 16))
+ return -EINVAL;
+ if (rx_usecs > (255 * 16))
+ return -EINVAL;
+
+ al_eth_set_coalesce(adapter, tx_usecs, rx_usecs);
+
+ return 0;
+}
+
static int al_eth_nway_reset(struct net_device *netdev)
{
struct al_eth_adapter *adapter = netdev_priv(netdev);
@@ -2613,6 +2692,8 @@ static const struct ethtool_ops al_eth_ethtool_ops = {
.nway_reset = al_eth_nway_reset,
.get_link = ethtool_op_get_link,
+ .get_coalesce = al_eth_get_coalesce,
+ .set_coalesce = al_eth_ethtool_set_coalesce,
.get_pauseparam = al_eth_get_pauseparam,
.set_pauseparam = al_eth_set_pauseparam,
.get_rxnfc = al_eth_get_rxnfc,
--
2.11.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next 6/8] net: ethernet: annapurna: add wol helpers to the Alpine driver
2017-02-03 18:12 ` [PATCH net-next 6/8] net: ethernet: annapurna: add wol helpers to the Alpine driver Antoine Tenart
@ 2017-02-03 18:21 ` Sergei Shtylyov
2017-02-06 11:35 ` David Laight
0 siblings, 1 reply; 23+ messages in thread
From: Sergei Shtylyov @ 2017-02-03 18:21 UTC (permalink / raw)
To: linux-arm-kernel
Hello!
On 02/03/2017 09:12 PM, Antoine Tenart wrote:
> Implement the get_wol() and set_wol() helpers in the Annapurna Labs
> Alpine Ethernet driver.
> ---
> drivers/net/ethernet/annapurna/al_eth.c | 44 +++++++++++++++++++++++++++++++++
> 1 file changed, 44 insertions(+)
>
> diff --git a/drivers/net/ethernet/annapurna/al_eth.c b/drivers/net/ethernet/annapurna/al_eth.c
> index 8dd84f66b5d1..d06a75a49ce5 100644
> --- a/drivers/net/ethernet/annapurna/al_eth.c
> +++ b/drivers/net/ethernet/annapurna/al_eth.c
> @@ -2519,10 +2519,54 @@ static u32 al_eth_get_rxfh_indir_size(struct net_device *netdev)
> return AL_ETH_RX_RSS_TABLE_SIZE;
> }
>
> +static void al_eth_get_wol(struct net_device *netdev,
> + struct ethtool_wolinfo *wol)
> +{
> + struct al_eth_adapter *adapter = netdev_priv(netdev);
> + struct phy_device *phydev;
> +
> + wol->wolopts = adapter->wol;
> +
> + if ((adapter) && (adapter->phy_exist) && (adapter->mdio_bus)) {
Now that's somewhat stupid looking... does the whole driver use this "style"?
MBR, Sergei
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH net-next 7/8] net: ethernet: annapurna: add eee helpers to the Alpine driver
2017-02-03 18:12 ` [PATCH net-next 7/8] net: ethernet: annapurna: add eee " Antoine Tenart
@ 2017-02-03 19:01 ` Florian Fainelli
0 siblings, 0 replies; 23+ messages in thread
From: Florian Fainelli @ 2017-02-03 19:01 UTC (permalink / raw)
To: linux-arm-kernel
On 02/03/2017 10:12 AM, Antoine Tenart wrote:
> Add the get_eee() and set_eee() helpers to support the Energy-Efficient
> (EEE) feature in the Annapurna Labs Alpine driver.
Missing SoB.
> ---
> drivers/net/ethernet/annapurna/al_eth.c | 44 +++++++++++
> drivers/net/ethernet/annapurna/al_hw_eth.h | 28 +++++++
> drivers/net/ethernet/annapurna/al_hw_eth_ec_regs.h | 11 +++
> .../net/ethernet/annapurna/al_hw_eth_mac_regs.h | 11 +++
> drivers/net/ethernet/annapurna/al_hw_eth_main.c | 92 ++++++++++++++++++++++
> 5 files changed, 186 insertions(+)
>
> diff --git a/drivers/net/ethernet/annapurna/al_eth.c b/drivers/net/ethernet/annapurna/al_eth.c
> index d06a75a49ce5..674dafdb638a 100644
> --- a/drivers/net/ethernet/annapurna/al_eth.c
> +++ b/drivers/net/ethernet/annapurna/al_eth.c
> @@ -2519,6 +2519,47 @@ static u32 al_eth_get_rxfh_indir_size(struct net_device *netdev)
> return AL_ETH_RX_RSS_TABLE_SIZE;
> }
>
> +static int al_eth_get_eee(struct net_device *netdev,
> + struct ethtool_eee *edata)
> +{
> + struct al_eth_adapter *adapter = netdev_priv(netdev);
> + struct al_eth_eee_params params;
> +
> + if (!adapter->phy_exist)
> + return -EOPNOTSUPP;
Just check for adapter->phydev instead of doing that, please audit your
entire submission for similar uses.
> +
> + al_eth_eee_get(&adapter->hw_adapter, ¶ms);
> +
> + edata->eee_enabled = params.enable;
> + edata->tx_lpi_timer = params.tx_eee_timer;
> +
> + return phy_ethtool_get_eee(adapter->phydev, edata);
> +}
> +
> +static int al_eth_set_eee(struct net_device *netdev,
> + struct ethtool_eee *edata)
> +{
> + struct al_eth_adapter *adapter = netdev_priv(netdev);
> + struct al_eth_eee_params params;
> +
> + struct phy_device *phydev;
> +
> + if (!adapter->phy_exist)
> + return -EOPNOTSUPP;
> +
> + phydev = mdiobus_get_phy(adapter->mdio_bus, adapter->phy_addr);
That's a very bizarre way of getting a reference to a PHY device, you
should have gotten one from a prior call to phy_{connect,attach} and
remembered it in your drivers' private structure, of use the one
attached to the net_device. Why are you doing this?
> +
> + phy_init_eee(phydev, 1);
You are supposed to check the return value of phy_init_eee().
> +
> + params.enable = edata->eee_enabled;
> + params.tx_eee_timer = edata->tx_lpi_timer;
> + params.min_interval = 10;
> +
> + al_eth_eee_config(&adapter->hw_adapter, ¶ms);
> +
> + return phy_ethtool_set_eee(phydev, edata);
> +}
--
Florian
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH net-next 5/8] net: ethernet: annapurna: add statistics helper
2017-02-03 18:12 ` [PATCH net-next 5/8] net: ethernet: annapurna: add statistics helper Antoine Tenart
@ 2017-02-03 19:34 ` Florian Fainelli
2017-02-03 21:24 ` kbuild test robot
1 sibling, 0 replies; 23+ messages in thread
From: Florian Fainelli @ 2017-02-03 19:34 UTC (permalink / raw)
To: linux-arm-kernel
On 02/03/2017 10:12 AM, Antoine Tenart wrote:
> Add the ndo_get_stats64() helper in the Annapurna Labs Alpine Ethernet
> driver.
> ---
> drivers/net/ethernet/annapurna/al_eth.c | 36 ++++
> drivers/net/ethernet/annapurna/al_hw_eth.h | 9 +
> drivers/net/ethernet/annapurna/al_hw_eth_main.c | 225 ++++++++++++++++++++++++
> 3 files changed, 270 insertions(+)
>
> diff --git a/drivers/net/ethernet/annapurna/al_eth.c b/drivers/net/ethernet/annapurna/al_eth.c
> index 779a885de014..8dd84f66b5d1 100644
> --- a/drivers/net/ethernet/annapurna/al_eth.c
> +++ b/drivers/net/ethernet/annapurna/al_eth.c
> @@ -2388,6 +2388,41 @@ static void al_eth_set_msglevel(struct net_device *netdev, u32 value)
> adapter->msg_enable = value;
> }
>
> +static void al_eth_get_stats64(struct net_device *netdev,
> + struct rtnl_link_stats64 *stats)
> +{
> + struct al_eth_adapter *adapter = netdev_priv(netdev);
> + struct al_eth_mac_stats *mac_stats = &adapter->mac_stats;
> +
> + if (!adapter->up)
> + return NULL;
> +
> + al_eth_mac_stats_get(&adapter->hw_adapter, mac_stats);
Are not you missing a reader synchronization here? Something like what
drivers/net/ethernet/realtek/8139too.c does for instance, especially if
this driver runs on ARM where you won't get "atomic" 64-bit reads (you
could, but it's more complicated).
> +
> + stats->rx_packets = mac_stats->aFramesReceivedOK; /* including pause frames */
> + stats->tx_packets = mac_stats->aFramesTransmittedOK; /* including pause frames */
> + stats->rx_bytes = mac_stats->aOctetsReceivedOK;
> + stats->tx_bytes = mac_stats->aOctetsTransmittedOK;
> + stats->rx_dropped = 0;
> + stats->multicast = mac_stats->ifInMulticastPkts;
> + stats->collisions = 0;
Are these free running counters, or read on clear, or?
> +
> + stats->rx_length_errors = (mac_stats->etherStatsUndersizePkts + /* good but short */
> + mac_stats->etherStatsFragments + /* short and bad*/
> + mac_stats->etherStatsJabbers + /* with crc errors */
> + mac_stats->etherStatsOversizePkts);
> + stats->rx_crc_errors = mac_stats->aFrameCheckSequenceErrors;
> + stats->rx_frame_errors = mac_stats->aAlignmentErrors;
> + stats->rx_fifo_errors = mac_stats->etherStatsDropEvents;
> + stats->rx_missed_errors = 0;
> + stats->tx_window_errors = 0;
> +
> + stats->rx_errors = mac_stats->ifInErrors;
> + stats->tx_errors = mac_stats->ifOutErrors;
> +
> + return stats;
> +}
> +
> static void al_eth_get_drvinfo(struct net_device *dev,
> struct ethtool_drvinfo *info)
> {
> @@ -2763,6 +2798,7 @@ static const struct net_device_ops al_eth_netdev_ops = {
> .ndo_stop = al_eth_close,
> .ndo_start_xmit = al_eth_start_xmit,
> .ndo_select_queue = al_eth_select_queue,
> + .ndo_get_stats64 = al_eth_get_stats64,
> .ndo_do_ioctl = al_eth_ioctl,
> .ndo_tx_timeout = al_eth_tx_timeout,
> .ndo_change_mtu = al_eth_change_mtu,
> diff --git a/drivers/net/ethernet/annapurna/al_hw_eth.h b/drivers/net/ethernet/annapurna/al_hw_eth.h
> index b2fc58793b3a..a44f3f200838 100644
> --- a/drivers/net/ethernet/annapurna/al_hw_eth.h
> +++ b/drivers/net/ethernet/annapurna/al_hw_eth.h
> @@ -982,6 +982,15 @@ struct al_eth_mac_stats {
> };
>
> /*
> + * get mac statistics
> + * @param adapter pointer to the private structure.
> + * @param stats pointer to structure that will be filled with statistics.
> + *
> + * @return return 0 on success. otherwise on failure.
> + */
> +int al_eth_mac_stats_get(struct al_hw_eth_adapter *adapter, struct al_eth_mac_stats *stats);
> +
> +/*
> * perform Function Level Reset RMN
> *
> * Addressing RMN: 714
> diff --git a/drivers/net/ethernet/annapurna/al_hw_eth_main.c b/drivers/net/ethernet/annapurna/al_hw_eth_main.c
> index abb9ffd09fbf..dac0c1e2a941 100644
> --- a/drivers/net/ethernet/annapurna/al_hw_eth_main.c
> +++ b/drivers/net/ethernet/annapurna/al_hw_eth_main.c
> @@ -2639,6 +2639,231 @@ int al_eth_flow_control_config(struct al_hw_eth_adapter *adapter,
> return 0;
> }
>
> +/* get statistics */
> +int al_eth_mac_stats_get(struct al_hw_eth_adapter *adapter, struct al_eth_mac_stats *stats)
> +{
> + WARN_ON(!stats);
> +
> + memset(stats, 0, sizeof(struct al_eth_mac_stats));
> +
> + if (AL_ETH_IS_1G_MAC(adapter->mac_mode)) {
> + struct al_eth_mac_1g_stats __iomem *reg_stats =
> + &adapter->mac_regs_base->mac_1g.stats;
> +
> + stats->ifInUcastPkts = readl(®_stats->ifInUcastPkts);
> + stats->ifInMulticastPkts = readl(®_stats->ifInMulticastPkts);
> + stats->ifInBroadcastPkts = readl(®_stats->ifInBroadcastPkts);
> + stats->etherStatsPkts = readl(®_stats->etherStatsPkts);
> + stats->ifOutUcastPkts = readl(®_stats->ifOutUcastPkts);
> + stats->ifOutMulticastPkts = readl(®_stats->ifOutMulticastPkts);
> + stats->ifOutBroadcastPkts = readl(®_stats->ifOutBroadcastPkts);
> + stats->ifInErrors = readl(®_stats->ifInErrors);
> + stats->ifOutErrors = readl(®_stats->ifOutErrors);
> + stats->aFramesReceivedOK = readl(®_stats->aFramesReceivedOK);
> + stats->aFramesTransmittedOK = readl(®_stats->aFramesTransmittedOK);
> + stats->aOctetsReceivedOK = readl(®_stats->aOctetsReceivedOK);
> + stats->aOctetsTransmittedOK = readl(®_stats->aOctetsTransmittedOK);
> + stats->etherStatsUndersizePkts = readl(®_stats->etherStatsUndersizePkts);
> + stats->etherStatsFragments = readl(®_stats->etherStatsFragments);
> + stats->etherStatsJabbers = readl(®_stats->etherStatsJabbers);
> + stats->etherStatsOversizePkts = readl(®_stats->etherStatsOversizePkts);
> + stats->aFrameCheckSequenceErrors =
> + readl(®_stats->aFrameCheckSequenceErrors);
> + stats->aAlignmentErrors = readl(®_stats->aAlignmentErrors);
> + stats->etherStatsDropEvents = readl(®_stats->etherStatsDropEvents);
> + stats->aPAUSEMACCtrlFramesTransmitted =
> + readl(®_stats->aPAUSEMACCtrlFramesTransmitted);
> + stats->aPAUSEMACCtrlFramesReceived =
> + readl(®_stats->aPAUSEMACCtrlFramesReceived);
> + stats->aFrameTooLongErrors = 0; /* N/A */
> + stats->aInRangeLengthErrors = 0; /* N/A */
> + stats->VLANTransmittedOK = 0; /* N/A */
> + stats->VLANReceivedOK = 0; /* N/A */
> + stats->etherStatsOctets = readl(®_stats->etherStatsOctets);
> + stats->etherStatsPkts64Octets = readl(®_stats->etherStatsPkts64Octets);
> + stats->etherStatsPkts65to127Octets =
> + readl(®_stats->etherStatsPkts65to127Octets);
> + stats->etherStatsPkts128to255Octets =
> + readl(®_stats->etherStatsPkts128to255Octets);
> + stats->etherStatsPkts256to511Octets =
> + readl(®_stats->etherStatsPkts256to511Octets);
> + stats->etherStatsPkts512to1023Octets =
> + readl(®_stats->etherStatsPkts512to1023Octets);
> + stats->etherStatsPkts1024to1518Octets =
> + readl(®_stats->etherStatsPkts1024to1518Octets);
> + stats->etherStatsPkts1519toX = readl(®_stats->etherStatsPkts1519toX);
> + } else if (AL_ETH_IS_10G_MAC(adapter->mac_mode) || AL_ETH_IS_25G_MAC(adapter->mac_mode)) {
> + if (adapter->rev_id < AL_ETH_REV_ID_3) {
> + struct al_eth_mac_10g_stats_v2 __iomem *reg_stats =
> + &adapter->mac_regs_base->mac_10g.stats.v2;
> + u64 octets;
> +
Using a structure to describe HW register is really ugly here, can't you
either come up with separate functions per generation of the hardware,
or even indirection tables with register offsets? Most statistics read
seem to be identical between generations of the HW, but their location
might differ.
> + stats->ifInUcastPkts = readl(®_stats->ifInUcastPkts);
> + stats->ifInMulticastPkts = readl(®_stats->ifInMulticastPkts);
> + stats->ifInBroadcastPkts = readl(®_stats->ifInBroadcastPkts);
> + stats->etherStatsPkts = readl(®_stats->etherStatsPkts);
> + stats->ifOutUcastPkts = readl(®_stats->ifOutUcastPkts);
> + stats->ifOutMulticastPkts = readl(®_stats->ifOutMulticastPkts);
> + stats->ifOutBroadcastPkts = readl(®_stats->ifOutBroadcastPkts);
> + stats->ifInErrors = readl(®_stats->ifInErrors);
> + stats->ifOutErrors = readl(®_stats->ifOutErrors);
> + stats->aFramesReceivedOK = readl(®_stats->aFramesReceivedOK);
> + stats->aFramesTransmittedOK = readl(®_stats->aFramesTransmittedOK);
> +
> + /* aOctetsReceivedOK = ifInOctets - 18 * aFramesReceivedOK - 4 * VLANReceivedOK */
> + octets = readl(®_stats->ifInOctetsL);
> + octets |= (u64)(readl(®_stats->ifInOctetsH)) << 32;
> + octets -= 18 * stats->aFramesReceivedOK;
> + octets -= 4 * readl(®_stats->VLANReceivedOK);
> + stats->aOctetsReceivedOK = octets;
> +
> + /* aOctetsTransmittedOK = ifOutOctets - 18 * aFramesTransmittedOK - 4 * VLANTransmittedOK */
What's that 18 for? Ethernet header + VLAN header? If so, express it
with appropriate constants
> + octets = readl(®_stats->ifOutOctetsL);
> + octets |= (u64)(readl(®_stats->ifOutOctetsH)) << 32;
upper_32_bits()
> + octets -= 18 * stats->aFramesTransmittedOK;
What's that 18 for? Ethernet header + VLAN header? If so, express it
with appropriate constants
> + octets -= 4 * readl(®_stats->VLANTransmittedOK);
4 = ETH_VLAN_HLEN
> + stats->aOctetsTransmittedOK = octets;
> +
--
Florian
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH net-next 4/8] net: ethernet: add the Alpine Ethernet driver
[not found] ` <20170203181216.30214-5-antoine.tenart@free-electrons.com>
@ 2017-02-03 20:58 ` Andrew Lunn
2017-08-07 7:39 ` Chocron, Jonathan
2017-08-27 13:47 ` Chocron, Jonathan
0 siblings, 2 replies; 23+ messages in thread
From: Andrew Lunn @ 2017-02-03 20:58 UTC (permalink / raw)
To: linux-arm-kernel
> +/* MDIO */
> +#define AL_ETH_MDIO_C45_DEV_MASK 0x1f0000
> +#define AL_ETH_MDIO_C45_DEV_SHIFT 16
> +#define AL_ETH_MDIO_C45_REG_MASK 0xffff
> +
> +static int al_mdio_read(struct mii_bus *bp, int mii_id, int reg)
> +{
> + struct al_eth_adapter *adapter = bp->priv;
> + u16 value = 0;
> + int rc;
> + int timeout = MDIO_TIMEOUT_MSEC;
> +
> + while (timeout > 0) {
> + if (reg & MII_ADDR_C45) {
> + netdev_dbg(adapter->netdev, "[c45]: dev %x reg %x val %x\n",
> + ((reg & AL_ETH_MDIO_C45_DEV_MASK) >> AL_ETH_MDIO_C45_DEV_SHIFT),
> + (reg & AL_ETH_MDIO_C45_REG_MASK), value);
> + rc = al_eth_mdio_read(&adapter->hw_adapter, adapter->phy_addr,
> + ((reg & AL_ETH_MDIO_C45_DEV_MASK) >> AL_ETH_MDIO_C45_DEV_SHIFT),
> + (reg & AL_ETH_MDIO_C45_REG_MASK), &value);
> + } else {
> + rc = al_eth_mdio_read(&adapter->hw_adapter, adapter->phy_addr,
> + MDIO_DEVAD_NONE, reg, &value);
> + }
> +
> + if (rc == 0)
> + return value;
> +
> + netdev_dbg(adapter->netdev,
> + "mdio read failed. try again in 10 msec\n");
> +
> + timeout -= 10;
> + msleep(10);
> + }
This is rather unusual, retrying MDIO operations. Are you working
around a hardware bug? I suspect this also opens up race conditions,
in particular with PHY interrupts, which can be clear on read.
> +
> +static int al_eth_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
> +{
> + struct al_eth_adapter *adapter = netdev_priv(netdev);
> + struct mii_ioctl_data *mdio = if_mii(ifr);
> + struct phy_device *phydev;
> +
> + netdev_info(adapter->netdev, "ioctl: phy id 0x%x, reg 0x%x, val_in 0x%x\n",
> + mdio->phy_id, mdio->reg_num, mdio->val_in);
netdev_info() for an ioctl?
> +static int al_eth_flow_ctrl_config(struct al_eth_adapter *adapter);
> +static u8 al_eth_flow_ctrl_mutual_cap_get(struct al_eth_adapter *adapter);
> +static void al_eth_down(struct al_eth_adapter *adapter);
> +static int al_eth_up(struct al_eth_adapter *adapter);
Forward declarations are generally not liked. Can you move the code
around to remove them?
> +
> +static void al_eth_adjust_link(struct net_device *dev)
> +{
> + struct al_eth_adapter *adapter = netdev_priv(dev);
> + struct al_eth_link_config *link_config = &adapter->link_config;
> + struct phy_device *phydev = adapter->phydev;
> + enum al_eth_mac_mode mac_mode_needed = AL_ETH_MAC_MODE_RGMII;
> + int new_state = 0;
> + bool force_1000_base_x = false;
> +
> + if (phydev->link) {
> + if (phydev->duplex != link_config->active_duplex) {
> + new_state = 1;
> + link_config->active_duplex = phydev->duplex;
> + }
> +
> + if (phydev->speed != link_config->active_speed) {
> + new_state = 1;
> + switch (phydev->speed) {
> + case SPEED_1000:
> + case SPEED_100:
> + case SPEED_10:
> + mac_mode_needed = (adapter->mac_mode == AL_ETH_MAC_MODE_RGMII) ?
> + AL_ETH_MAC_MODE_RGMII : AL_ETH_MAC_MODE_SGMII;
> + break;
> + case SPEED_10000:
> + case SPEED_2500:
> + mac_mode_needed = AL_ETH_MAC_MODE_10GbE_Serial;
> + break;
> + default:
> + if (netif_msg_link(adapter))
> + netdev_warn(adapter->netdev,
> + "Ack! Speed (%d) is not 10/100/1000!",
Not particularly accurate, since 2.5G and 10G is supported.
> + phydev->speed);
> +static int al_eth_phy_init(struct al_eth_adapter *adapter)
> +{
> + struct phy_device *phydev = mdiobus_get_phy(adapter->mdio_bus, adapter->phy_addr);
> +
> + adapter->link_config.old_link = 0;
> + adapter->link_config.active_duplex = DUPLEX_UNKNOWN;
> + adapter->link_config.active_speed = SPEED_UNKNOWN;
> +
> + /* Attach the MAC to the PHY. */
> + phydev = phy_connect(adapter->netdev, dev_name(&phydev->mdio.dev), al_eth_adjust_link,
> + PHY_INTERFACE_MODE_RGMII);
> + if (IS_ERR(phydev)) {
> + netdev_err(adapter->netdev, "Could not attach to PHY\n");
> + return PTR_ERR(phydev);
> + }
> +
> + netdev_info(adapter->netdev, "phy[%d]: device %s, driver %s\n",
> + phydev->mdio.addr, dev_name(&phydev->mdio.dev),
> + phydev->drv ? phydev->drv->name : "unknown");
> +
phy_attached_info()?
> + /* Mask with MAC supported features. */
> + phydev->supported &= (PHY_GBIT_FEATURES |
> + SUPPORTED_Pause |
> + SUPPORTED_Asym_Pause);
> +
> + phydev->advertising = phydev->supported;
> +
> + netdev_info(adapter->netdev, "phy[%d]:supported %x adv %x\n",
> + phydev->mdio.addr, phydev->supported, phydev->advertising);
> +
More output?
> + adapter->phydev = phydev;
> + /* Bring the PHY up */
> + phy_start(adapter->phydev);
This is normally done in the open() call.
> +/* al_eth_mdiobus_setup - initialize mdiobus and register to kernel */
> +static int al_eth_mdiobus_setup(struct al_eth_adapter *adapter)
> +{
> + struct phy_device *phydev;
> + int i;
> + int ret = 0;
> +
> + adapter->mdio_bus = mdiobus_alloc();
> + if (!adapter->mdio_bus)
> + return -ENOMEM;
> +
> + adapter->mdio_bus->name = "al mdio bus";
> + snprintf(adapter->mdio_bus->id, MII_BUS_ID_SIZE, "%x",
> + (adapter->pdev->bus->number << 8) | adapter->pdev->devfn);
> + adapter->mdio_bus->priv = adapter;
> + adapter->mdio_bus->parent = &adapter->pdev->dev;
> + adapter->mdio_bus->read = &al_mdio_read;
> + adapter->mdio_bus->write = &al_mdio_write;
> + adapter->mdio_bus->phy_mask = ~BIT(adapter->phy_addr);
Why do this?
> + for (i = 0; i < PHY_MAX_ADDR; i++)
> + adapter->mdio_bus->irq[i] = PHY_POLL;
Not needed. The core will do this.
> +
> + if (adapter->phy_if != AL_ETH_BOARD_PHY_IF_XMDIO) {
> + i = mdiobus_register(adapter->mdio_bus);
> + if (i) {
> + netdev_warn(adapter->netdev,
> + "mdiobus_reg failed (0x%x)\n", i);
> + mdiobus_free(adapter->mdio_bus);
> + return i;
> + }
> +
> + phydev = mdiobus_get_phy(adapter->mdio_bus, adapter->phy_addr);
> + } else {
> + adapter->mdio_bus->phy_mask = 0xffffffff;
> + i = mdiobus_register(adapter->mdio_bus);
> + if (i) {
> + netdev_warn(adapter->netdev,
> + "mdiobus_reg failed (0x%x)\n", i);
> + mdiobus_free(adapter->mdio_bus);
> + return i;
> + }
> +
> + phydev = get_phy_device(adapter->mdio_bus, adapter->phy_addr,
> + true);
> + if (!phydev) {
> + netdev_err(adapter->netdev, "phy device get failed\n");
> + goto error;
> + }
> +
> + ret = phy_device_register(phydev);
> + if (ret) {
> + netdev_err(adapter->netdev,
> + "phy device register failed\n");
> + goto error;
> + }
> + }
It seems like this should be split up into two. One function to
register the MDIO bus, and a second to handle the PHY on the mdio bus.
> +
> + if (!phydev || !phydev->drv)
> + goto error;
> +
> + return 0;
> +
> +error:
> + netdev_warn(adapter->netdev, "No PHY devices\n");
Yet more warnings....
> + mdiobus_unregister(adapter->mdio_bus);
> + mdiobus_free(adapter->mdio_bus);
> + return -ENODEV;
> +}
> +
> +/* al_eth_mdiobus_teardown - mdiobus unregister */
> +static void al_eth_mdiobus_teardown(struct al_eth_adapter *adapter)
> +{
> + if (!adapter->mdio_bus)
> + return;
> +
> + mdiobus_unregister(adapter->mdio_bus);
> + mdiobus_free(adapter->mdio_bus);
> + phy_device_free(adapter->phydev);
Humm, you might want to think about the ordering here.
> +}
> +
> +static void al_eth_tx_timeout(struct net_device *dev)
> +{
> + struct al_eth_adapter *adapter = netdev_priv(dev);
> +
> + if (netif_msg_tx_err(adapter))
> + netdev_err(dev, "transmit timed out!!!!\n");
> +}
> +
> +static int al_eth_change_mtu(struct net_device *dev, int new_mtu)
> +{
> + struct al_eth_adapter *adapter = netdev_priv(dev);
> + int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
> +
> + if ((new_mtu < AL_ETH_MIN_FRAME_LEN) || (new_mtu > AL_ETH_MAX_MTU) ||
> + (max_frame > AL_ETH_MAX_FRAME_LEN)) {
> + netdev_err(dev, "Invalid MTU setting\n");
> + return -EINVAL;
> + }
The core will do this check for you, if you tell it to.
> + switch (params.speed) {
> + default:
> + dev_warn(&adapter->pdev->dev,
> + "invalid speed (%d)\n", params.speed);
It is a bit unusual having the default first. And that leads me to a C
question. Can it fall through into the next case statement if there is no break?
> +static int al_eth_nway_reset(struct net_device *netdev)
> +{
> + struct al_eth_adapter *adapter = netdev_priv(netdev);
> + struct phy_device *phydev = adapter->phydev;
> +
> + if (!phydev)
> + return -ENODEV;
> +
> + return phy_start_aneg(phydev);
> +}
phy_ethtool_nway_reset() should be used.
> +static int al_eth_set_mac_addr(struct net_device *dev, void *p)
> +{
> + struct al_eth_adapter *adapter = netdev_priv(dev);
> + struct sockaddr *addr = p;
> + int err = 0;
> +
> + if (!is_valid_ether_addr(addr->sa_data))
> + return -EADDRNOTAVAIL;
Seems like the core should be doing that for you. Not checked though.
If it does not, i suggest you do add it to the core.
> +static void al_eth_mdio_1g_mac_read(struct al_hw_eth_adapter *adapter,
> + u32 phy_addr, u32 reg, u16 *val)
> +{
> + *val = readl(&adapter->mac_regs_base->mac_1g.phy_regs_base + reg);
> +}
> +
> +static void al_eth_mdio_1g_mac_write(struct al_hw_eth_adapter *adapter,
> + u32 phy_addr, u32 reg, u16 val)
> +{
> + writel(val, &adapter->mac_regs_base->mac_1g.phy_regs_base + reg);
> +}
Are there range checks made on reg before these functions are called?
Just thinking about SIOCSMIIREG ioctl.
> +
> +static int al_eth_mdio_10g_mac_wait_busy(struct al_hw_eth_adapter *adapter)
> +{
> + int count = 0;
> + u32 mdio_cfg_status;
> +
> + do {
> + mdio_cfg_status = readl(&adapter->mac_regs_base->mac_10g.mdio_cfg_status);
> + if (mdio_cfg_status & BIT(0)) {
Would be nice to have a #define for this 0 bit, and it seems bit 1 is an error?
> + if (count > 0)
> + netdev_dbg(adapter->netdev,
> + "eth [%s] mdio: still busy!\n",
> + adapter->name);
> + } else {
> + return 0;
> + }
> + udelay(AL_ETH_MDIO_DELAY_PERIOD);
> + } while (count++ < AL_ETH_MDIO_DELAY_COUNT);
> +
> + return -ETIMEDOUT;
> +}
> +
> +static int al_eth_mdio_10g_mac_type22(struct al_hw_eth_adapter *adapter,
> + int read, u32 phy_addr, u32 reg, u16 *val)
> +{
> + int rc;
> + const char *op = (read == 1) ? "read" : "write";
> + u32 mdio_cfg_status;
> + u16 mdio_cmd;
> +
> + /* wait if the HW is busy */
> + rc = al_eth_mdio_10g_mac_wait_busy(adapter);
> + if (rc) {
> + netdev_err(adapter->netdev,
> + " eth [%s] mdio %s failed. HW is busy\n",
> + adapter->name, op);
How about moving this netdev_err() inside
al_eth_mdio_10g_mac_wait_busy() so you only need it once?
> + return rc;
> + }
> +
> + mdio_cmd = (u16)(0x1F & reg);
> + mdio_cmd |= (0x1F & phy_addr) << 5;
> +
> + if (read)
> + mdio_cmd |= BIT(15); /* READ command */
Another #define please.
> + * acquire mdio interface ownership
> + * when mdio interface shared between multiple eth controllers, this function waits until the ownership granted for this controller.
> + * this function does nothing when the mdio interface is used only by this controller.
> + *
> + * @param adapter
> + * @return 0 on success, -ETIMEDOUT on timeout.
> + */
> +static int al_eth_mdio_lock(struct al_hw_eth_adapter *adapter)
> +{
> + int count = 0;
> + u32 mdio_ctrl_1;
> +
> + if (!adapter->shared_mdio_if)
> + return 0; /* nothing to do when interface is not shared */
> +
> + do {
> + mdio_ctrl_1 = readl(&adapter->mac_regs_base->gen.mdio_ctrl_1);
> + if (mdio_ctrl_1 & BIT(0)) {
> + if (count > 0)
> + netdev_dbg(adapter->netdev,
> + "eth %s mdio interface still busy!\n",
> + adapter->name);
> + } else {
> + return 0;
> + }
> + udelay(AL_ETH_MDIO_DELAY_PERIOD);
> + } while (count++ < (AL_ETH_MDIO_DELAY_COUNT * 4));
This needs explaining. How can a read alone perform a lock? How is
this race free?
> + if (adapter->mdio_type == AL_ETH_MDIO_TYPE_CLAUSE_22)
> + rc = al_eth_mdio_10g_mac_type22(adapter, 1, phy_addr,
> + reg, val);
> + else
> + rc = al_eth_mdio_10g_mac_type45(adapter, 1, phy_addr,
> + device, reg, val);
This seems odd. My understanding is that the device on the MDIO bus,
the PHY, is either c22 or c45. The PHY driver will tell you this, not
the adaptor.
Andrew
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH net-next 5/8] net: ethernet: annapurna: add statistics helper
2017-02-03 18:12 ` [PATCH net-next 5/8] net: ethernet: annapurna: add statistics helper Antoine Tenart
2017-02-03 19:34 ` Florian Fainelli
@ 2017-02-03 21:24 ` kbuild test robot
1 sibling, 0 replies; 23+ messages in thread
From: kbuild test robot @ 2017-02-03 21:24 UTC (permalink / raw)
To: linux-arm-kernel
Hi Antoine,
[auto build test WARNING on net-next/master]
url: https://github.com/0day-ci/linux/commits/Antoine-Tenart/ARM-Alpine-Ethernet-support/20170204-022156
config: arm-allmodconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=arm
All warnings (new ones prefixed by >>):
drivers/net/ethernet/annapurna/al_eth.c: In function 'al_eth_restore_ethtool_params':
drivers/net/ethernet/annapurna/al_eth.c:2139:15: warning: unused variable 'rx_usecs' [-Wunused-variable]
unsigned int rx_usecs = adapter->rx_usecs;
^~~~~~~~
drivers/net/ethernet/annapurna/al_eth.c:2138:15: warning: unused variable 'tx_usecs' [-Wunused-variable]
unsigned int tx_usecs = adapter->tx_usecs;
^~~~~~~~
In file included from include/uapi/linux/posix_types.h:4:0,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/linux/list.h:4,
from include/linux/module.h:9,
from drivers/net/ethernet/annapurna/al_eth.c:9:
drivers/net/ethernet/annapurna/al_eth.c: In function 'al_eth_get_stats64':
>> include/linux/stddef.h:7:14: warning: 'return' with a value, in function returning void
#define NULL ((void *)0)
^
>> drivers/net/ethernet/annapurna/al_eth.c:2398:10: note: in expansion of macro 'NULL'
return NULL;
^~~~
drivers/net/ethernet/annapurna/al_eth.c:2391:13: note: declared here
static void al_eth_get_stats64(struct net_device *netdev,
^~~~~~~~~~~~~~~~~~
>> drivers/net/ethernet/annapurna/al_eth.c:2423:9: warning: 'return' with a value, in function returning void
return stats;
^~~~~
drivers/net/ethernet/annapurna/al_eth.c:2391:13: note: declared here
static void al_eth_get_stats64(struct net_device *netdev,
^~~~~~~~~~~~~~~~~~
vim +/NULL +2398 drivers/net/ethernet/annapurna/al_eth.c
2392 struct rtnl_link_stats64 *stats)
2393 {
2394 struct al_eth_adapter *adapter = netdev_priv(netdev);
2395 struct al_eth_mac_stats *mac_stats = &adapter->mac_stats;
2396
2397 if (!adapter->up)
> 2398 return NULL;
2399
2400 al_eth_mac_stats_get(&adapter->hw_adapter, mac_stats);
2401
2402 stats->rx_packets = mac_stats->aFramesReceivedOK; /* including pause frames */
2403 stats->tx_packets = mac_stats->aFramesTransmittedOK; /* including pause frames */
2404 stats->rx_bytes = mac_stats->aOctetsReceivedOK;
2405 stats->tx_bytes = mac_stats->aOctetsTransmittedOK;
2406 stats->rx_dropped = 0;
2407 stats->multicast = mac_stats->ifInMulticastPkts;
2408 stats->collisions = 0;
2409
2410 stats->rx_length_errors = (mac_stats->etherStatsUndersizePkts + /* good but short */
2411 mac_stats->etherStatsFragments + /* short and bad*/
2412 mac_stats->etherStatsJabbers + /* with crc errors */
2413 mac_stats->etherStatsOversizePkts);
2414 stats->rx_crc_errors = mac_stats->aFrameCheckSequenceErrors;
2415 stats->rx_frame_errors = mac_stats->aAlignmentErrors;
2416 stats->rx_fifo_errors = mac_stats->etherStatsDropEvents;
2417 stats->rx_missed_errors = 0;
2418 stats->tx_window_errors = 0;
2419
2420 stats->rx_errors = mac_stats->ifInErrors;
2421 stats->tx_errors = mac_stats->ifOutErrors;
2422
> 2423 return stats;
2424 }
2425
2426 static void al_eth_get_drvinfo(struct net_device *dev,
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/gzip
Size: 60463 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170204/90b7d779/attachment-0001.gz>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH net-next 6/8] net: ethernet: annapurna: add wol helpers to the Alpine driver
2017-02-03 18:21 ` Sergei Shtylyov
@ 2017-02-06 11:35 ` David Laight
2017-02-06 12:02 ` Sergei Shtylyov
2017-02-10 10:12 ` Antoine Tenart
0 siblings, 2 replies; 23+ messages in thread
From: David Laight @ 2017-02-06 11:35 UTC (permalink / raw)
To: linux-arm-kernel
From: netdev-owner@vger.kernel.org [mailto:netdev-owner at vger.kernel.org] On Behalf Of Sergei Shtylyov
> Sent: 03 February 2017 18:22
> On 02/03/2017 09:12 PM, Antoine Tenart wrote:
>
> > Implement the get_wol() and set_wol() helpers in the Annapurna Labs
> > Alpine Ethernet driver.
> > ---
> > drivers/net/ethernet/annapurna/al_eth.c | 44 +++++++++++++++++++++++++++++++++
> > 1 file changed, 44 insertions(+)
> >
> > diff --git a/drivers/net/ethernet/annapurna/al_eth.c b/drivers/net/ethernet/annapurna/al_eth.c
> > index 8dd84f66b5d1..d06a75a49ce5 100644
> > --- a/drivers/net/ethernet/annapurna/al_eth.c
> > +++ b/drivers/net/ethernet/annapurna/al_eth.c
> > @@ -2519,10 +2519,54 @@ static u32 al_eth_get_rxfh_indir_size(struct net_device *netdev)
> > return AL_ETH_RX_RSS_TABLE_SIZE;
> > }
> >
> > +static void al_eth_get_wol(struct net_device *netdev,
> > + struct ethtool_wolinfo *wol)
> > +{
> > + struct al_eth_adapter *adapter = netdev_priv(netdev);
> > + struct phy_device *phydev;
> > +
> > + wol->wolopts = adapter->wol;
> > +
> > + if ((adapter) && (adapter->phy_exist) && (adapter->mdio_bus)) {
>
> Now that's somewhat stupid looking... does the whole driver use this "style"?
Not only that, in one of the two functions it is followed by:
+ device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
Which assumes that 'adapter' is not NULL.
Some verifiers will detect that as a possible NULL pointer dereference.
Pointers should only be checked for NULL if there are valid reasons
why they can be NULL in that code path.
Getting there with a NULL pointer dues to some race condition isn't one of them.
David
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH net-next 6/8] net: ethernet: annapurna: add wol helpers to the Alpine driver
2017-02-06 11:35 ` David Laight
@ 2017-02-06 12:02 ` Sergei Shtylyov
2017-02-10 10:12 ` Antoine Tenart
1 sibling, 0 replies; 23+ messages in thread
From: Sergei Shtylyov @ 2017-02-06 12:02 UTC (permalink / raw)
To: linux-arm-kernel
On 02/06/2017 02:35 PM, David Laight wrote:
>>> Implement the get_wol() and set_wol() helpers in the Annapurna Labs
>>> Alpine Ethernet driver.
>>> ---
>>> drivers/net/ethernet/annapurna/al_eth.c | 44 +++++++++++++++++++++++++++++++++
>>> 1 file changed, 44 insertions(+)
>>>
>>> diff --git a/drivers/net/ethernet/annapurna/al_eth.c b/drivers/net/ethernet/annapurna/al_eth.c
>>> index 8dd84f66b5d1..d06a75a49ce5 100644
>>> --- a/drivers/net/ethernet/annapurna/al_eth.c
>>> +++ b/drivers/net/ethernet/annapurna/al_eth.c
>>> @@ -2519,10 +2519,54 @@ static u32 al_eth_get_rxfh_indir_size(struct net_device *netdev)
>>> return AL_ETH_RX_RSS_TABLE_SIZE;
>>> }
>>>
>>> +static void al_eth_get_wol(struct net_device *netdev,
>>> + struct ethtool_wolinfo *wol)
>>> +{
>>> + struct al_eth_adapter *adapter = netdev_priv(netdev);
>>> + struct phy_device *phydev;
>>> +
>>> + wol->wolopts = adapter->wol;
>>> +
>>> + if ((adapter) && (adapter->phy_exist) && (adapter->mdio_bus)) {
>>
>> Now that's somewhat stupid looking... does the whole driver use this "style"?
>
> Not only that, in one of the two functions it is followed by:
>
> + device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
> Which assumes that 'adapter' is not NULL.
> Some verifiers will detect that as a possible NULL pointer dereference.
I only meant unneeded parens. :-)
[...]
> David
MBR, Sergei
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH net-next 6/8] net: ethernet: annapurna: add wol helpers to the Alpine driver
2017-02-06 11:35 ` David Laight
2017-02-06 12:02 ` Sergei Shtylyov
@ 2017-02-10 10:12 ` Antoine Tenart
1 sibling, 0 replies; 23+ messages in thread
From: Antoine Tenart @ 2017-02-10 10:12 UTC (permalink / raw)
To: linux-arm-kernel
Hi!
On Mon, Feb 06, 2017 at 11:35:49AM +0000, David Laight wrote:
> From: netdev-owner at vger.kernel.org [mailto:netdev-owner at vger.kernel.org] On Behalf Of Sergei Shtylyov
> > Sent: 03 February 2017 18:22
> > On 02/03/2017 09:12 PM, Antoine Tenart wrote:
> >
> > > + if ((adapter) && (adapter->phy_exist) && (adapter->mdio_bus)) {
> >
> > Now that's somewhat stupid looking... does the whole driver use this "style"?
>
> Not only that, in one of the two functions it is followed by:
>
> + device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
>
> Which assumes that 'adapter' is not NULL.
> Some verifiers will detect that as a possible NULL pointer dereference.
>
> Pointers should only be checked for NULL if there are valid reasons
> why they can be NULL in that code path.
> Getting there with a NULL pointer dues to some race condition isn't one of them.
Totally agree, for the NULL checks and for the useless parenthesis. I'll
try to catch other examples of this in the driver.
Thanks!
Antoine
--
Antoine T?nart, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170210/dc32ea36/attachment.sig>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH net-next 4/8] net: ethernet: add the Alpine Ethernet driver
2017-02-03 20:58 ` [PATCH net-next 4/8] net: ethernet: add the Alpine Ethernet driver Andrew Lunn
@ 2017-08-07 7:39 ` Chocron, Jonathan
2017-08-27 13:47 ` Chocron, Jonathan
1 sibling, 0 replies; 23+ messages in thread
From: Chocron, Jonathan @ 2017-08-07 7:39 UTC (permalink / raw)
To: linux-arm-kernel
________________________________________
From: Andrew Lunn <andrew@lunn.ch>
Sent: Friday, February 3, 2017 22:58
To: Antoine Tenart
Cc: netdev at vger.kernel.org; davem at davemloft.net; linux-arm-kernel at lists.infradead.org; tsahee at annapurnalabs.com; rshitrit at annapurnalabs.com; saeed at annapurnalabs.com; barak at annapurnalabs.com; talz at annapurnalabs.com; thomas.petazzoni at free-electrons.com; arnd at arndb.de
Subject: Re: [PATCH net-next 4/8] net: ethernet: add the Alpine Ethernet driver
> +/* MDIO */
> +#define AL_ETH_MDIO_C45_DEV_MASK 0x1f0000
> +#define AL_ETH_MDIO_C45_DEV_SHIFT 16
> +#define AL_ETH_MDIO_C45_REG_MASK 0xffff
> +
> +static int al_mdio_read(struct mii_bus *bp, int mii_id, int reg)
> +{
> + struct al_eth_adapter *adapter = bp->priv;
> + u16 value = 0;
> + int rc;
> + int timeout = MDIO_TIMEOUT_MSEC;
> +
> + while (timeout > 0) {
> + if (reg & MII_ADDR_C45) {
> + netdev_dbg(adapter->netdev, "[c45]: dev %x reg %x val %x\n",
> + ((reg & AL_ETH_MDIO_C45_DEV_MASK) >> AL_ETH_MDIO_C45_DEV_SHIFT),
> + (reg & AL_ETH_MDIO_C45_REG_MASK), value);
> + rc = al_eth_mdio_read(&adapter->hw_adapter, adapter->phy_addr,
> + ((reg & AL_ETH_MDIO_C45_DEV_MASK) >> AL_ETH_MDIO_C45_DEV_SHIFT),
> + (reg & AL_ETH_MDIO_C45_REG_MASK), &value);
> + } else {
> + rc = al_eth_mdio_read(&adapter->hw_adapter, adapter->phy_addr,
> + MDIO_DEVAD_NONE, reg, &value);
> + }
> +
> + if (rc == 0)
> + return value;
> +
> + netdev_dbg(adapter->netdev,
> + "mdio read failed. try again in 10 msec\n");
> +
> + timeout -= 10;
> + msleep(10);
> + }
This is rather unusual, retrying MDIO operations. Are you working
around a hardware bug? I suspect this also opens up race conditions,
in particular with PHY interrupts, which can be clear on read.
The MDIO bus is shared between the ethernet units. There is a HW lock used to arbitrate between different interfaces trying to access the bus,
therefore there is a retry loop. The reg isn't accessed before obtaining the lock, so there shouldn't be any clear on read issues.
> +
> +static int al_eth_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
> +{
> + struct al_eth_adapter *adapter = netdev_priv(netdev);
> + struct mii_ioctl_data *mdio = if_mii(ifr);
> + struct phy_device *phydev;
> +
> + netdev_info(adapter->netdev, "ioctl: phy id 0x%x, reg 0x%x, val_in 0x%x\n",
> + mdio->phy_id, mdio->reg_num, mdio->val_in);
netdev_info() for an ioctl?
> +static int al_eth_flow_ctrl_config(struct al_eth_adapter *adapter);
> +static u8 al_eth_flow_ctrl_mutual_cap_get(struct al_eth_adapter *adapter);
> +static void al_eth_down(struct al_eth_adapter *adapter);
> +static int al_eth_up(struct al_eth_adapter *adapter);
Forward declarations are generally not liked. Can you move the code
around to remove them?
> +
> +static void al_eth_adjust_link(struct net_device *dev)
> +{
> + struct al_eth_adapter *adapter = netdev_priv(dev);
> + struct al_eth_link_config *link_config = &adapter->link_config;
> + struct phy_device *phydev = adapter->phydev;
> + enum al_eth_mac_mode mac_mode_needed = AL_ETH_MAC_MODE_RGMII;
> + int new_state = 0;
> + bool force_1000_base_x = false;
> +
> + if (phydev->link) {
> + if (phydev->duplex != link_config->active_duplex) {
> + new_state = 1;
> + link_config->active_duplex = phydev->duplex;
> + }
> +
> + if (phydev->speed != link_config->active_speed) {
> + new_state = 1;
> + switch (phydev->speed) {
> + case SPEED_1000:
> + case SPEED_100:
> + case SPEED_10:
> + mac_mode_needed = (adapter->mac_mode == AL_ETH_MAC_MODE_RGMII) ?
> + AL_ETH_MAC_MODE_RGMII : AL_ETH_MAC_MODE_SGMII;
> + break;
> + case SPEED_10000:
> + case SPEED_2500:
> + mac_mode_needed = AL_ETH_MAC_MODE_10GbE_Serial;
> + break;
> + default:
> + if (netif_msg_link(adapter))
> + netdev_warn(adapter->netdev,
> + "Ack! Speed (%d) is not 10/100/1000!",
Not particularly accurate, since 2.5G and 10G is supported.
> + phydev->speed);
> +static int al_eth_phy_init(struct al_eth_adapter *adapter)
> +{
> + struct phy_device *phydev = mdiobus_get_phy(adapter->mdio_bus, adapter->phy_addr);
> +
> + adapter->link_config.old_link = 0;
> + adapter->link_config.active_duplex = DUPLEX_UNKNOWN;
> + adapter->link_config.active_speed = SPEED_UNKNOWN;
> +
> + /* Attach the MAC to the PHY. */
> + phydev = phy_connect(adapter->netdev, dev_name(&phydev->mdio.dev), al_eth_adjust_link,
> + PHY_INTERFACE_MODE_RGMII);
> + if (IS_ERR(phydev)) {
> + netdev_err(adapter->netdev, "Could not attach to PHY\n");
> + return PTR_ERR(phydev);
> + }
> +
> + netdev_info(adapter->netdev, "phy[%d]: device %s, driver %s\n",
> + phydev->mdio.addr, dev_name(&phydev->mdio.dev),
> + phydev->drv ? phydev->drv->name : "unknown");
> +
phy_attached_info()?
> + /* Mask with MAC supported features. */
> + phydev->supported &= (PHY_GBIT_FEATURES |
> + SUPPORTED_Pause |
> + SUPPORTED_Asym_Pause);
> +
> + phydev->advertising = phydev->supported;
> +
> + netdev_info(adapter->netdev, "phy[%d]:supported %x adv %x\n",
> + phydev->mdio.addr, phydev->supported, phydev->advertising);
> +
More output?
> + adapter->phydev = phydev;
> + /* Bring the PHY up */
> + phy_start(adapter->phydev);
This is normally done in the open() call.
> +/* al_eth_mdiobus_setup - initialize mdiobus and register to kernel */
> +static int al_eth_mdiobus_setup(struct al_eth_adapter *adapter)
> +{
> + struct phy_device *phydev;
> + int i;
> + int ret = 0;
> +
> + adapter->mdio_bus = mdiobus_alloc();
> + if (!adapter->mdio_bus)
> + return -ENOMEM;
> +
> + adapter->mdio_bus->name = "al mdio bus";
> + snprintf(adapter->mdio_bus->id, MII_BUS_ID_SIZE, "%x",
> + (adapter->pdev->bus->number << 8) | adapter->pdev->devfn);
> + adapter->mdio_bus->priv = adapter;
> + adapter->mdio_bus->parent = &adapter->pdev->dev;
> + adapter->mdio_bus->read = &al_mdio_read;
> + adapter->mdio_bus->write = &al_mdio_write;
> + adapter->mdio_bus->phy_mask = ~BIT(adapter->phy_addr);
Why do this?
Since the MDIO bus is shared, we want each interface to probe only for the PHY associated with it.
> + for (i = 0; i < PHY_MAX_ADDR; i++)
> + adapter->mdio_bus->irq[i] = PHY_POLL;
Not needed. The core will do this.
> +
> + if (adapter->phy_if != AL_ETH_BOARD_PHY_IF_XMDIO) {
> + i = mdiobus_register(adapter->mdio_bus);
> + if (i) {
> + netdev_warn(adapter->netdev,
> + "mdiobus_reg failed (0x%x)\n", i);
> + mdiobus_free(adapter->mdio_bus);
> + return i;
> + }
> +
> + phydev = mdiobus_get_phy(adapter->mdio_bus, adapter->phy_addr);
> + } else {
> + adapter->mdio_bus->phy_mask = 0xffffffff;
> + i = mdiobus_register(adapter->mdio_bus);
> + if (i) {
> + netdev_warn(adapter->netdev,
> + "mdiobus_reg failed (0x%x)\n", i);
> + mdiobus_free(adapter->mdio_bus);
> + return i;
> + }
> +
> + phydev = get_phy_device(adapter->mdio_bus, adapter->phy_addr,
> + true);
> + if (!phydev) {
> + netdev_err(adapter->netdev, "phy device get failed\n");
> + goto error;
> + }
> +
> + ret = phy_device_register(phydev);
> + if (ret) {
> + netdev_err(adapter->netdev,
> + "phy device register failed\n");
> + goto error;
> + }
> + }
It seems like this should be split up into two. One function to
register the MDIO bus, and a second to handle the PHY on the mdio bus.
> +
> + if (!phydev || !phydev->drv)
> + goto error;
> +
> + return 0;
> +
> +error:
> + netdev_warn(adapter->netdev, "No PHY devices\n");
Yet more warnings....
> + mdiobus_unregister(adapter->mdio_bus);
> + mdiobus_free(adapter->mdio_bus);
> + return -ENODEV;
> +}
> +
> +/* al_eth_mdiobus_teardown - mdiobus unregister */
> +static void al_eth_mdiobus_teardown(struct al_eth_adapter *adapter)
> +{
> + if (!adapter->mdio_bus)
> + return;
> +
> + mdiobus_unregister(adapter->mdio_bus);
> + mdiobus_free(adapter->mdio_bus);
> + phy_device_free(adapter->phydev);
Humm, you might want to think about the ordering here.
> +}
> +
> +static void al_eth_tx_timeout(struct net_device *dev)
> +{
> + struct al_eth_adapter *adapter = netdev_priv(dev);
> +
> + if (netif_msg_tx_err(adapter))
> + netdev_err(dev, "transmit timed out!!!!\n");
> +}
> +
> +static int al_eth_change_mtu(struct net_device *dev, int new_mtu)
> +{
> + struct al_eth_adapter *adapter = netdev_priv(dev);
> + int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
> +
> + if ((new_mtu < AL_ETH_MIN_FRAME_LEN) || (new_mtu > AL_ETH_MAX_MTU) ||
> + (max_frame > AL_ETH_MAX_FRAME_LEN)) {
> + netdev_err(dev, "Invalid MTU setting\n");
> + return -EINVAL;
> + }
The core will do this check for you, if you tell it to.
> + switch (params.speed) {
> + default:
> + dev_warn(&adapter->pdev->dev,
> + "invalid speed (%d)\n", params.speed);
It is a bit unusual having the default first. And that leads me to a C
question. Can it fall through into the next case statement if there is no break?
> +static int al_eth_nway_reset(struct net_device *netdev)
> +{
> + struct al_eth_adapter *adapter = netdev_priv(netdev);
> + struct phy_device *phydev = adapter->phydev;
> +
> + if (!phydev)
> + return -ENODEV;
> +
> + return phy_start_aneg(phydev);
> +}
phy_ethtool_nway_reset() should be used.
> +static int al_eth_set_mac_addr(struct net_device *dev, void *p)
> +{
> + struct al_eth_adapter *adapter = netdev_priv(dev);
> + struct sockaddr *addr = p;
> + int err = 0;
> +
> + if (!is_valid_ether_addr(addr->sa_data))
> + return -EADDRNOTAVAIL;
Seems like the core should be doing that for you. Not checked though.
If it does not, i suggest you do add it to the core.
> +static void al_eth_mdio_1g_mac_read(struct al_hw_eth_adapter *adapter,
> + u32 phy_addr, u32 reg, u16 *val)
> +{
> + *val = readl(&adapter->mac_regs_base->mac_1g.phy_regs_base + reg);
> +}
> +
> +static void al_eth_mdio_1g_mac_write(struct al_hw_eth_adapter *adapter,
> + u32 phy_addr, u32 reg, u16 val)
> +{
> + writel(val, &adapter->mac_regs_base->mac_1g.phy_regs_base + reg);
> +}
Are there range checks made on reg before these functions are called?
Just thinking about SIOCSMIIREG ioctl.
> +
> +static int al_eth_mdio_10g_mac_wait_busy(struct al_hw_eth_adapter *adapter)
> +{
> + int count = 0;
> + u32 mdio_cfg_status;
> +
> + do {
> + mdio_cfg_status = readl(&adapter->mac_regs_base->mac_10g.mdio_cfg_status);
> + if (mdio_cfg_status & BIT(0)) {
Would be nice to have a #define for this 0 bit, and it seems bit 1 is an error?
> + if (count > 0)
> + netdev_dbg(adapter->netdev,
> + "eth [%s] mdio: still busy!\n",
> + adapter->name);
> + } else {
> + return 0;
> + }
> + udelay(AL_ETH_MDIO_DELAY_PERIOD);
> + } while (count++ < AL_ETH_MDIO_DELAY_COUNT);
> +
> + return -ETIMEDOUT;
> +}
> +
> +static int al_eth_mdio_10g_mac_type22(struct al_hw_eth_adapter *adapter,
> + int read, u32 phy_addr, u32 reg, u16 *val)
> +{
> + int rc;
> + const char *op = (read == 1) ? "read" : "write";
> + u32 mdio_cfg_status;
> + u16 mdio_cmd;
> +
> + /* wait if the HW is busy */
> + rc = al_eth_mdio_10g_mac_wait_busy(adapter);
> + if (rc) {
> + netdev_err(adapter->netdev,
> + " eth [%s] mdio %s failed. HW is busy\n",
> + adapter->name, op);
How about moving this netdev_err() inside
al_eth_mdio_10g_mac_wait_busy() so you only need it once?
> + return rc;
> + }
> +
> + mdio_cmd = (u16)(0x1F & reg);
> + mdio_cmd |= (0x1F & phy_addr) << 5;
> +
> + if (read)
> + mdio_cmd |= BIT(15); /* READ command */
Another #define please.
> + * acquire mdio interface ownership
> + * when mdio interface shared between multiple eth controllers, this function waits until the ownership granted for this controller.
> + * this function does nothing when the mdio interface is used only by this controller.
> + *
> + * @param adapter
> + * @return 0 on success, -ETIMEDOUT on timeout.
> + */
> +static int al_eth_mdio_lock(struct al_hw_eth_adapter *adapter)
> +{
> + int count = 0;
> + u32 mdio_ctrl_1;
> +
> + if (!adapter->shared_mdio_if)
> + return 0; /* nothing to do when interface is not shared */
> +
> + do {
> + mdio_ctrl_1 = readl(&adapter->mac_regs_base->gen.mdio_ctrl_1);
> + if (mdio_ctrl_1 & BIT(0)) {
> + if (count > 0)
> + netdev_dbg(adapter->netdev,
> + "eth %s mdio interface still busy!\n",
> + adapter->name);
> + } else {
> + return 0;
> + }
> + udelay(AL_ETH_MDIO_DELAY_PERIOD);
> + } while (count++ < (AL_ETH_MDIO_DELAY_COUNT * 4));
This needs explaining. How can a read alone perform a lock? How is
this race free?
This is how this HW lock works: when the bit is 0 this means the lock is free. When a read transaction arrives to the lock, it changes its value to 1 but sends 0 as the response,
basically taking ownership. When the owner is done, it writes a 0 which essentially "frees" the lock.
> + if (adapter->mdio_type == AL_ETH_MDIO_TYPE_CLAUSE_22)
> + rc = al_eth_mdio_10g_mac_type22(adapter, 1, phy_addr,
> + reg, val);
> + else
> + rc = al_eth_mdio_10g_mac_type45(adapter, 1, phy_addr,
> + device, reg, val);
This seems odd. My understanding is that the device on the MDIO bus,
the PHY, is either c22 or c45. The PHY driver will tell you this, not
the adaptor.
The current implementation sets mdio_type according to information which is originally deduced from the DeviceTree (the bootloader parses the ethernet node of the DeviceTree and saves this data to HW registers, which are then read by this driver).
How can this information be obtained by the PHY driver?
Andrew
Jonathan
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH net-next 4/8] net: ethernet: add the Alpine Ethernet driver
2017-02-03 20:58 ` [PATCH net-next 4/8] net: ethernet: add the Alpine Ethernet driver Andrew Lunn
2017-08-07 7:39 ` Chocron, Jonathan
@ 2017-08-27 13:47 ` Chocron, Jonathan
2017-08-28 18:09 ` Andrew Lunn
1 sibling, 1 reply; 23+ messages in thread
From: Chocron, Jonathan @ 2017-08-27 13:47 UTC (permalink / raw)
To: linux-arm-kernel
This is a fixed version of my previous response (using proper indentation and leaving only the specific questions responded to).
> > +/* MDIO */
> > +#define AL_ETH_MDIO_C45_DEV_MASK 0x1f0000
> > +#define AL_ETH_MDIO_C45_DEV_SHIFT 16
> > +#define AL_ETH_MDIO_C45_REG_MASK 0xffff
> > +
> > +static int al_mdio_read(struct mii_bus *bp, int mii_id, int reg)
> > +{
> > + struct al_eth_adapter *adapter = bp->priv;
> > + u16 value = 0;
> > + int rc;
> > + int timeout = MDIO_TIMEOUT_MSEC;
> > +
> > + while (timeout > 0) {
> > + if (reg & MII_ADDR_C45) {
> > + netdev_dbg(adapter->netdev, "[c45]: dev %x reg %x val %x\n",
> > + ((reg & AL_ETH_MDIO_C45_DEV_MASK) >> AL_ETH_MDIO_C45_DEV_SHIFT),
> > + (reg & AL_ETH_MDIO_C45_REG_MASK), value);
> > + rc = al_eth_mdio_read(&adapter->hw_adapter, adapter->phy_addr,
> > + ((reg & AL_ETH_MDIO_C45_DEV_MASK) >> AL_ETH_MDIO_C45_DEV_SHIFT),
> > + (reg & AL_ETH_MDIO_C45_REG_MASK), &value);
> > + } else {
> > + rc = al_eth_mdio_read(&adapter->hw_adapter, adapter->phy_addr,
> > + MDIO_DEVAD_NONE, reg, &value);
> > + }
> > +
> > + if (rc == 0)
> > + return value;
> > +
> > + netdev_dbg(adapter->netdev,
> > + "mdio read failed. try again in 10 msec\n");
> > +
> > + timeout -= 10;
> > + msleep(10);
> > + }
>
> This is rather unusual, retrying MDIO operations. Are you working
> around a hardware bug? I suspect this also opens up race conditions,
> in particular with PHY interrupts, which can be clear on read.
The MDIO bus is shared between the ethernet units. There is a HW lock used to arbitrate between different interfaces trying to access the bus,
therefore there is a retry loop. The reg isn't accessed before obtaining the lock, so there shouldn't be any clear on read issues.
> > +/* al_eth_mdiobus_setup - initialize mdiobus and register to kernel */
> > +static int al_eth_mdiobus_setup(struct al_eth_adapter *adapter)
> > +{
> > + struct phy_device *phydev;
> > + int i;
> > + int ret = 0;
> > +
> > + adapter->mdio_bus = mdiobus_alloc();
> > + if (!adapter->mdio_bus)
> > + return -ENOMEM;
> > +
> > + adapter->mdio_bus->name = "al mdio bus";
> > + snprintf(adapter->mdio_bus->id, MII_BUS_ID_SIZE, "%x",
> > + (adapter->pdev->bus->number << 8) | adapter->pdev->devfn);
> > + adapter->mdio_bus->priv = adapter;
> > + adapter->mdio_bus->parent = &adapter->pdev->dev;
> > + adapter->mdio_bus->read = &al_mdio_read;
> > + adapter->mdio_bus->write = &al_mdio_write;
> > + adapter->mdio_bus->phy_mask = ~BIT(adapter->phy_addr);
>
> Why do this?
Since the MDIO bus is shared, we want each interface to probe only for the PHY associated with it.
> > + * acquire mdio interface ownership
> > + * when mdio interface shared between multiple eth controllers, this function waits until the ownership granted for this controller.
> > + * this function does nothing when the mdio interface is used only by this controller.
> > + *
> > + * @param adapter
> > + * @return 0 on success, -ETIMEDOUT on timeout.
> > + */
> > +static int al_eth_mdio_lock(struct al_hw_eth_adapter *adapter)
> > +{
> > + int count = 0;
> > + u32 mdio_ctrl_1;
> > +
> > + if (!adapter->shared_mdio_if)
> > + return 0; /* nothing to do when interface is not shared */
> > +
> > + do {
> > + mdio_ctrl_1 = readl(&adapter->mac_regs_base->gen.mdio_ctrl_1);
> > + if (mdio_ctrl_1 & BIT(0)) {
> > + if (count > 0)
> > + netdev_dbg(adapter->netdev,
> > + "eth %s mdio interface still busy!\n",
> > + adapter->name);
> > + } else {
> > + return 0;
> > + }
> > + udelay(AL_ETH_MDIO_DELAY_PERIOD);
> > + } while (count++ < (AL_ETH_MDIO_DELAY_COUNT * 4));
>
> This needs explaining. How can a read alone perform a lock? How is
> this race free?
This is how this HW lock works: when the bit is 0 this means the lock is free. When a read transaction arrives
to the lock, it changes its value to 1 but sends 0 as the response, basically taking ownership.
When the owner is done, it writes a 0 which essentially "frees" the lock.
> > + if (adapter->mdio_type == AL_ETH_MDIO_TYPE_CLAUSE_22)
> > + rc = al_eth_mdio_10g_mac_type22(adapter, 1, phy_addr,
> > + reg, val);
> > + else
> > + rc = al_eth_mdio_10g_mac_type45(adapter, 1, phy_addr,
> > + device, reg, val);
>
> This seems odd. My understanding is that the device on the MDIO bus,
> the PHY, is either c22 or c45. The PHY driver will tell you this, not
> the adaptor.
The current implementation sets mdio_type according to information which is originally deduced from the
DeviceTree (the bootloader parses the ethernet node of the DeviceTree and saves this data to HW registers, which are then read by this driver).
How can this information be obtained by the PHY driver?
> Andrew
Jonathan
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH net-next 4/8] net: ethernet: add the Alpine Ethernet driver
2017-08-27 13:47 ` Chocron, Jonathan
@ 2017-08-28 18:09 ` Andrew Lunn
2017-11-02 16:05 ` Chocron, Jonathan
0 siblings, 1 reply; 23+ messages in thread
From: Andrew Lunn @ 2017-08-28 18:09 UTC (permalink / raw)
To: linux-arm-kernel
On Sun, Aug 27, 2017 at 01:47:19PM +0000, Chocron, Jonathan wrote:
> This is a fixed version of my previous response (using proper indentation and leaving only the specific questions responded to).
Wow, this is old. 3 Feb 2017. I had to go dig into the archive to
refresh my memory.
> > > +/* MDIO */
> > > +#define AL_ETH_MDIO_C45_DEV_MASK 0x1f0000
> > > +#define AL_ETH_MDIO_C45_DEV_SHIFT 16
> > > +#define AL_ETH_MDIO_C45_REG_MASK 0xffff
> > > +
> > > +static int al_mdio_read(struct mii_bus *bp, int mii_id, int reg)
> > > +{
> > > + struct al_eth_adapter *adapter = bp->priv;
> > > + u16 value = 0;
> > > + int rc;
> > > + int timeout = MDIO_TIMEOUT_MSEC;
> > > +
> > > + while (timeout > 0) {
> > > + if (reg & MII_ADDR_C45) {
> > > + netdev_dbg(adapter->netdev, "[c45]: dev %x reg %x val %x\n",
> > > + ((reg & AL_ETH_MDIO_C45_DEV_MASK) >> AL_ETH_MDIO_C45_DEV_SHIFT),
> > > + (reg & AL_ETH_MDIO_C45_REG_MASK), value);
> > > + rc = al_eth_mdio_read(&adapter->hw_adapter, adapter->phy_addr,
> > > + ((reg & AL_ETH_MDIO_C45_DEV_MASK) >> AL_ETH_MDIO_C45_DEV_SHIFT),
> > > + (reg & AL_ETH_MDIO_C45_REG_MASK), &value);
> > > + } else {
> > > + rc = al_eth_mdio_read(&adapter->hw_adapter, adapter->phy_addr,
> > > + MDIO_DEVAD_NONE, reg, &value);
> > > + }
> > > +
> > > + if (rc == 0)
> > > + return value;
> > > +
> > > + netdev_dbg(adapter->netdev,
> > > + "mdio read failed. try again in 10 msec\n");
> > > +
> > > + timeout -= 10;
> > > + msleep(10);
> > > + }
> >
> > This is rather unusual, retrying MDIO operations. Are you working
> > around a hardware bug? I suspect this also opens up race conditions,
> > in particular with PHY interrupts, which can be clear on read.
>
> The MDIO bus is shared between the ethernet units. There is a HW
> lock used to arbitrate between different interfaces trying to access
> the bus, therefore there is a retry loop. The reg isn't accessed
> before obtaining the lock, so there shouldn't be any clear on read
> issues.
>
> > > +/* al_eth_mdiobus_setup - initialize mdiobus and register to kernel */
> > > +static int al_eth_mdiobus_setup(struct al_eth_adapter *adapter)
> > > +{
> > > + struct phy_device *phydev;
> > > + int i;
> > > + int ret = 0;
> > > +
> > > + adapter->mdio_bus = mdiobus_alloc();
> > > + if (!adapter->mdio_bus)
> > > + return -ENOMEM;
> > > +
> > > + adapter->mdio_bus->name = "al mdio bus";
> > > + snprintf(adapter->mdio_bus->id, MII_BUS_ID_SIZE, "%x",
> > > + (adapter->pdev->bus->number << 8) | adapter->pdev->devfn);
> > > + adapter->mdio_bus->priv = adapter;
> > > + adapter->mdio_bus->parent = &adapter->pdev->dev;
> > > + adapter->mdio_bus->read = &al_mdio_read;
> > > + adapter->mdio_bus->write = &al_mdio_write;
> > > + adapter->mdio_bus->phy_mask = ~BIT(adapter->phy_addr);
> >
> > Why do this?
>
> Since the MDIO bus is shared, we want each interface to probe only for the PHY associated with it.
So i think this is the core of the problem. You have one physical MDIO
bus, yet you register it twice with the MDIO framework.
How about you only register it once? A lot of the complexity then goes
away. The mutex in the mdio core per bus means you don't need your
hardware locking. All that code goes away. All the retry code goes
away. Life is simple.
Andrew
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH net-next 4/8] net: ethernet: add the Alpine Ethernet driver
2017-08-28 18:09 ` Andrew Lunn
@ 2017-11-02 16:05 ` Chocron, Jonathan
2017-11-02 18:19 ` Florian Fainelli
0 siblings, 1 reply; 23+ messages in thread
From: Chocron, Jonathan @ 2017-11-02 16:05 UTC (permalink / raw)
To: linux-arm-kernel
-----Original Message-----
> From: Andrew Lunn [mailto:andrew at lunn.ch]
> Sent: Monday, August 28, 2017 9:10 PM
> To: Chocron, Jonathan <jonnyc@amazon.com>
> Cc: Antoine Tenart <antoine.tenart@free-electrons.com>;
> netdev at vger.kernel.org; davem at davemloft.net; linux-arm-
> kernel at lists.infradead.org; thomas.petazzoni at free-electrons.com;
> arnd at arndb.de
> Subject: Re: [PATCH net-next 4/8] net: ethernet: add the Alpine Ethernet
> driver
>
> On Sun, Aug 27, 2017 at 01:47:19PM +0000, Chocron, Jonathan wrote:
> > This is a fixed version of my previous response (using proper indentation
> and leaving only the specific questions responded to).
>
> Wow, this is old. 3 Feb 2017. I had to go dig into the archive to refresh my
> memory.
>
> > > > +/* MDIO */
> > > > +#define AL_ETH_MDIO_C45_DEV_MASK 0x1f0000
> > > > +#define AL_ETH_MDIO_C45_DEV_SHIFT 16
> > > > +#define AL_ETH_MDIO_C45_REG_MASK 0xffff
> > > > +
> > > > +static int al_mdio_read(struct mii_bus *bp, int mii_id, int reg)
> > > > +{
> > > > + struct al_eth_adapter *adapter = bp->priv;
> > > > + u16 value = 0;
> > > > + int rc;
> > > > + int timeout = MDIO_TIMEOUT_MSEC;
> > > > +
> > > > + while (timeout > 0) {
> > > > + if (reg & MII_ADDR_C45) {
> > > > + netdev_dbg(adapter->netdev, "[c45]: dev %x reg %x val
> %x\n",
> > > > + ((reg & AL_ETH_MDIO_C45_DEV_MASK) >>
> AL_ETH_MDIO_C45_DEV_SHIFT),
> > > > + (reg & AL_ETH_MDIO_C45_REG_MASK), value);
> > > > + rc = al_eth_mdio_read(&adapter->hw_adapter, adapter-
> >phy_addr,
> > > > + ((reg & AL_ETH_MDIO_C45_DEV_MASK) >>
> AL_ETH_MDIO_C45_DEV_SHIFT),
> > > > + (reg & AL_ETH_MDIO_C45_REG_MASK), &value);
> > > > + } else {
> > > > + rc = al_eth_mdio_read(&adapter->hw_adapter, adapter-
> >phy_addr,
> > > > + MDIO_DEVAD_NONE, reg, &value);
> > > > + }
> > > > +
> > > > + if (rc == 0)
> > > > + return value;
> > > > +
> > > > + netdev_dbg(adapter->netdev,
> > > > + "mdio read failed. try again in 10
> > > > + msec\n");
> > > > +
> > > > + timeout -= 10;
> > > > + msleep(10);
> > > > + }
> > >
> > > This is rather unusual, retrying MDIO operations. Are you working
> > > around a hardware bug? I suspect this also opens up race conditions,
> > > in particular with PHY interrupts, which can be clear on read.
> >
> > The MDIO bus is shared between the ethernet units. There is a HW lock
> > used to arbitrate between different interfaces trying to access the
> > bus, therefore there is a retry loop. The reg isn't accessed before
> > obtaining the lock, so there shouldn't be any clear on read issues.
> >
> > > > +/* al_eth_mdiobus_setup - initialize mdiobus and register to
> > > > +kernel */ static int al_eth_mdiobus_setup(struct al_eth_adapter
> > > > +*adapter) {
> > > > + struct phy_device *phydev;
> > > > + int i;
> > > > + int ret = 0;
> > > > +
> > > > + adapter->mdio_bus = mdiobus_alloc();
> > > > + if (!adapter->mdio_bus)
> > > > + return -ENOMEM;
> > > > +
> > > > + adapter->mdio_bus->name = "al mdio bus";
> > > > + snprintf(adapter->mdio_bus->id, MII_BUS_ID_SIZE, "%x",
> > > > + (adapter->pdev->bus->number << 8) | adapter->pdev-
> >devfn);
> > > > + adapter->mdio_bus->priv = adapter;
> > > > + adapter->mdio_bus->parent = &adapter->pdev->dev;
> > > > + adapter->mdio_bus->read = &al_mdio_read;
> > > > + adapter->mdio_bus->write = &al_mdio_write;
> > > > + adapter->mdio_bus->phy_mask = ~BIT(adapter->phy_addr);
> > >
> > > Why do this?
> >
> > Since the MDIO bus is shared, we want each interface to probe only for the
> PHY associated with it.
>
> So i think this is the core of the problem. You have one physical MDIO bus,
> yet you register it twice with the MDIO framework.
>
> How about you only register it once? A lot of the complexity then goes away.
> The mutex in the mdio core per bus means you don't need your hardware
> locking. All that code goes away. All the retry code goes away. Life is simple.
>
> Andrew
We indeed have one physical MDIO bus, but have multiple masters on it,
each "behind" a different internal PCIe device. Since the accesses to the bus
are done "indirectly" through each master, we can't register the bus only once.
Think of the scenario that we register it in the driver context of PCIe device A,
and then the driver is unbound from just this device. Device B won't be able
to access the bus since it was registered with callbacks that use a PCIe BAR of
device A, which is no longer valid.
Is it possible to register the mdio_bus struct as a global instance at driver load,
and someway pass the offset to the specific device's MDIO master, as part of
each read/write transaction towards the MDIO bus?
Or perhaps you have another suggestion which takes into account the issues I've described?
Jonathan
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH net-next 4/8] net: ethernet: add the Alpine Ethernet driver
2017-11-02 16:05 ` Chocron, Jonathan
@ 2017-11-02 18:19 ` Florian Fainelli
2017-11-05 12:29 ` BSHARA, Said
0 siblings, 1 reply; 23+ messages in thread
From: Florian Fainelli @ 2017-11-02 18:19 UTC (permalink / raw)
To: linux-arm-kernel
On 11/02/2017 09:05 AM, Chocron, Jonathan wrote:
> -----Original Message-----
>> From: Andrew Lunn [mailto:andrew at lunn.ch]
>> Sent: Monday, August 28, 2017 9:10 PM
>> To: Chocron, Jonathan <jonnyc@amazon.com>
>> Cc: Antoine Tenart <antoine.tenart@free-electrons.com>;
>> netdev at vger.kernel.org; davem at davemloft.net; linux-arm-
>> kernel at lists.infradead.org; thomas.petazzoni at free-electrons.com;
>> arnd at arndb.de
>> Subject: Re: [PATCH net-next 4/8] net: ethernet: add the Alpine Ethernet
>> driver
>>
>> On Sun, Aug 27, 2017 at 01:47:19PM +0000, Chocron, Jonathan wrote:
>>> This is a fixed version of my previous response (using proper indentation
>> and leaving only the specific questions responded to).
>>
>> Wow, this is old. 3 Feb 2017. I had to go dig into the archive to refresh my
>> memory.
>>
>>>>> +/* MDIO */
>>>>> +#define AL_ETH_MDIO_C45_DEV_MASK 0x1f0000
>>>>> +#define AL_ETH_MDIO_C45_DEV_SHIFT 16
>>>>> +#define AL_ETH_MDIO_C45_REG_MASK 0xffff
>>>>> +
>>>>> +static int al_mdio_read(struct mii_bus *bp, int mii_id, int reg)
>>>>> +{
>>>>> + struct al_eth_adapter *adapter = bp->priv;
>>>>> + u16 value = 0;
>>>>> + int rc;
>>>>> + int timeout = MDIO_TIMEOUT_MSEC;
>>>>> +
>>>>> + while (timeout > 0) {
>>>>> + if (reg & MII_ADDR_C45) {
>>>>> + netdev_dbg(adapter->netdev, "[c45]: dev %x reg %x val
>> %x\n",
>>>>> + ((reg & AL_ETH_MDIO_C45_DEV_MASK) >>
>> AL_ETH_MDIO_C45_DEV_SHIFT),
>>>>> + (reg & AL_ETH_MDIO_C45_REG_MASK), value);
>>>>> + rc = al_eth_mdio_read(&adapter->hw_adapter, adapter-
>>> phy_addr,
>>>>> + ((reg & AL_ETH_MDIO_C45_DEV_MASK) >>
>> AL_ETH_MDIO_C45_DEV_SHIFT),
>>>>> + (reg & AL_ETH_MDIO_C45_REG_MASK), &value);
>>>>> + } else {
>>>>> + rc = al_eth_mdio_read(&adapter->hw_adapter, adapter-
>>> phy_addr,
>>>>> + MDIO_DEVAD_NONE, reg, &value);
>>>>> + }
>>>>> +
>>>>> + if (rc == 0)
>>>>> + return value;
>>>>> +
>>>>> + netdev_dbg(adapter->netdev,
>>>>> + "mdio read failed. try again in 10
>>>>> + msec\n");
>>>>> +
>>>>> + timeout -= 10;
>>>>> + msleep(10);
>>>>> + }
>>>>
>>>> This is rather unusual, retrying MDIO operations. Are you working
>>>> around a hardware bug? I suspect this also opens up race conditions,
>>>> in particular with PHY interrupts, which can be clear on read.
>>>
>>> The MDIO bus is shared between the ethernet units. There is a HW lock
>>> used to arbitrate between different interfaces trying to access the
>>> bus, therefore there is a retry loop. The reg isn't accessed before
>>> obtaining the lock, so there shouldn't be any clear on read issues.
>>>
>>>>> +/* al_eth_mdiobus_setup - initialize mdiobus and register to
>>>>> +kernel */ static int al_eth_mdiobus_setup(struct al_eth_adapter
>>>>> +*adapter) {
>>>>> + struct phy_device *phydev;
>>>>> + int i;
>>>>> + int ret = 0;
>>>>> +
>>>>> + adapter->mdio_bus = mdiobus_alloc();
>>>>> + if (!adapter->mdio_bus)
>>>>> + return -ENOMEM;
>>>>> +
>>>>> + adapter->mdio_bus->name = "al mdio bus";
>>>>> + snprintf(adapter->mdio_bus->id, MII_BUS_ID_SIZE, "%x",
>>>>> + (adapter->pdev->bus->number << 8) | adapter->pdev-
>>> devfn);
>>>>> + adapter->mdio_bus->priv = adapter;
>>>>> + adapter->mdio_bus->parent = &adapter->pdev->dev;
>>>>> + adapter->mdio_bus->read = &al_mdio_read;
>>>>> + adapter->mdio_bus->write = &al_mdio_write;
>>>>> + adapter->mdio_bus->phy_mask = ~BIT(adapter->phy_addr);
>>>>
>>>> Why do this?
>>>
>>> Since the MDIO bus is shared, we want each interface to probe only for the
>> PHY associated with it.
>>
>> So i think this is the core of the problem. You have one physical MDIO bus,
>> yet you register it twice with the MDIO framework.
>>
>> How about you only register it once? A lot of the complexity then goes away.
>> The mutex in the mdio core per bus means you don't need your hardware
>> locking. All that code goes away. All the retry code goes away. Life is simple.
>>
>> Andrew
>
> We indeed have one physical MDIO bus, but have multiple masters on it,
> each "behind" a different internal PCIe device. Since the accesses to the bus
> are done "indirectly" through each master, we can't register the bus only once.
How do your multiple masters get arbitrated on the unique MDIO bus? Is
there hardware automatically doing that, or do you have to semaphore
those accesses at the software level?
> Think of the scenario that we register it in the driver context of PCIe device A,
> and then the driver is unbound from just this device. Device B won't be able
> to access the bus since it was registered with callbacks that use a PCIe BAR of
> device A, which is no longer valid.
You can have one single physical MDIO bus that you register once
throughout the SoC's power on lifecycle, and then you can create
"virtual" MDIO bus instances which map 1:1 with the PCIe device/function
and are nested from that single MDIO bus, this also gives you
serialization of accesses and arbitration for free.
>
> Is it possible to register the mdio_bus struct as a global instance at driver load,
> and someway pass the offset to the specific device's MDIO master, as part of
> each read/write transaction towards the MDIO bus?
You can register how many instances of the MDIO bus you want in a
system, it can be a singleton for the purpose of supporting your
specific hardware, or you can build a layer on top like I just suggested
above.
> Or perhaps you have another suggestion which takes into account the issues I've described?
Considering that binding to a MDIO bus is done by MDIO bus name
(bus->id) and/or Device Tree parent/child hierarchy, if there is only
one, just have all instances reference the same MDIO bus when they want
to bind to their devices (pure mdio_device, or phy_device) on that MDIO bus.
--
Florian
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH net-next 4/8] net: ethernet: add the Alpine Ethernet driver
2017-11-02 18:19 ` Florian Fainelli
@ 2017-11-05 12:29 ` BSHARA, Said
2017-11-05 15:22 ` Andrew Lunn
0 siblings, 1 reply; 23+ messages in thread
From: BSHARA, Said @ 2017-11-05 12:29 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, 2017-11-02 at 11:19 -0700, Florian Fainelli wrote:
> On 11/02/2017 09:05 AM, Chocron, Jonathan wrote:
> >
> > ?-----Original Message-----
> > >
> > > From: Andrew Lunn [mailto:andrew at lunn.ch]
> > > Sent: Monday, August 28, 2017 9:10 PM
> > > To: Chocron, Jonathan <jonnyc@amazon.com>
> > > Cc: Antoine Tenart <antoine.tenart@free-electrons.com>;
> > > netdev at vger.kernel.org; davem at davemloft.net; linux-arm-
> > > kernel at lists.infradead.org; thomas.petazzoni at free-electrons.com;
> > > arnd at arndb.de
> > > Subject: Re: [PATCH net-next 4/8] net: ethernet: add the Alpine
> > > Ethernet
> > > driver
> > >
> > > On Sun, Aug 27, 2017 at 01:47:19PM +0000, Chocron, Jonathan
> > > wrote:
> > > >
> > > > This is a fixed version of my previous response (using proper
> > > > indentation
> > > and leaving only the specific questions responded to).
> > >
> > > Wow, this is old.??3 Feb 2017. I had to go dig into the archive
> > > to refresh my
> > > memory.
> > >
> > > >
> > > > >
> > > > > >
> > > > > > +/* MDIO */
> > > > > > +#define AL_ETH_MDIO_C45_DEV_MASK?????0x1f0000
> > > > > > +#define AL_ETH_MDIO_C45_DEV_SHIFT????16
> > > > > > +#define AL_ETH_MDIO_C45_REG_MASK?????0xffff
> > > > > > +
> > > > > > +static int al_mdio_read(struct mii_bus *bp, int mii_id,
> > > > > > int reg)
> > > > > > +{
> > > > > > +?????struct al_eth_adapter *adapter = bp->priv;
> > > > > > +?????u16 value = 0;
> > > > > > +?????int rc;
> > > > > > +?????int timeout = MDIO_TIMEOUT_MSEC;
> > > > > > +
> > > > > > +?????while (timeout > 0) {
> > > > > > +?????????????if (reg & MII_ADDR_C45) {
> > > > > > +?????????????????????netdev_dbg(adapter->netdev, "[c45]:
> > > > > > dev %x reg %x val
> > > %x\n",
> > > >
> > > > >
> > > > > >
> > > > > > +????????????????????????????????((reg &
> > > > > > AL_ETH_MDIO_C45_DEV_MASK) >>
> > > AL_ETH_MDIO_C45_DEV_SHIFT),
> > > >
> > > > >
> > > > > >
> > > > > > +????????????????????????????????(reg &
> > > > > > AL_ETH_MDIO_C45_REG_MASK), value);
> > > > > > +?????????????????????rc = al_eth_mdio_read(&adapter-
> > > > > > >hw_adapter, adapter-
> > > > phy_addr,
> > > > >
> > > > > >
> > > > > > +?????????????????????????????((reg &
> > > > > > AL_ETH_MDIO_C45_DEV_MASK) >>
> > > AL_ETH_MDIO_C45_DEV_SHIFT),
> > > >
> > > > >
> > > > > >
> > > > > > +?????????????????????????????(reg &
> > > > > > AL_ETH_MDIO_C45_REG_MASK), &value);
> > > > > > +?????????????} else {
> > > > > > +?????????????????????rc = al_eth_mdio_read(&adapter-
> > > > > > >hw_adapter, adapter-
> > > > phy_addr,
> > > > >
> > > > > >
> > > > > > +???????????????????????????????????????????MDIO_DEVAD_NONE
> > > > > > , reg, &value);
> > > > > > +?????????????}
> > > > > > +
> > > > > > +?????????????if (rc == 0)
> > > > > > +?????????????????????return value;
> > > > > > +
> > > > > > +?????????????netdev_dbg(adapter->netdev,
> > > > > > +????????????????????????"mdio read failed. try again in 10
> > > > > > + msec\n");
> > > > > > +
> > > > > > +?????????????timeout -= 10;
> > > > > > +?????????????msleep(10);
> > > > > > +?????}
> > > > > This is rather unusual, retrying MDIO operations. Are you
> > > > > working
> > > > > around a hardware bug? I suspect this also opens up race
> > > > > conditions,
> > > > > in particular with PHY interrupts, which can be clear on
> > > > > read.
> > > > The MDIO bus is shared between the ethernet units. There is a
> > > > HW lock
> > > > used to arbitrate between different interfaces trying to access
> > > > the
> > > > bus, therefore there is a retry loop. The reg isn't accessed
> > > > before
> > > > obtaining the lock, so there shouldn't be any clear on read
> > > > issues.
> > > >
> > > > >
> > > > > >
> > > > > > +/* al_eth_mdiobus_setup - initialize mdiobus and register
> > > > > > to
> > > > > > +kernel */ static int al_eth_mdiobus_setup(struct
> > > > > > al_eth_adapter
> > > > > > +*adapter) {
> > > > > > +?????struct phy_device *phydev;
> > > > > > +?????int i;
> > > > > > +?????int ret = 0;
> > > > > > +
> > > > > > +?????adapter->mdio_bus = mdiobus_alloc();
> > > > > > +?????if (!adapter->mdio_bus)
> > > > > > +?????????????return -ENOMEM;
> > > > > > +
> > > > > > +?????adapter->mdio_bus->name?????= "al mdio bus";
> > > > > > +?????snprintf(adapter->mdio_bus->id, MII_BUS_ID_SIZE,
> > > > > > "%x",
> > > > > > +??????????????(adapter->pdev->bus->number << 8) | adapter-
> > > > > > >pdev-
> > > > devfn);
> > > > >
> > > > > >
> > > > > > +?????adapter->mdio_bus->priv?????= adapter;
> > > > > > +?????adapter->mdio_bus->parent???= &adapter->pdev->dev;
> > > > > > +?????adapter->mdio_bus->read?????= &al_mdio_read;
> > > > > > +?????adapter->mdio_bus->write????= &al_mdio_write;
> > > > > > +?????adapter->mdio_bus->phy_mask = ~BIT(adapter-
> > > > > > >phy_addr);
> > > > > Why do this?
> > > > Since the MDIO bus is shared, we want each interface to probe
> > > > only for the
> > > PHY associated with it.
> > >
> > > So i think this is the core of the problem. You have one physical
> > > MDIO bus,
> > > yet you register it twice with the MDIO framework.
> > >
> > > How about you only register it once? A lot of the complexity then
> > > goes away.
> > > The mutex in the mdio core per bus means you don't need your
> > > hardware
> > > locking. All that code goes away. All the retry code goes away.
> > > Life is simple.
> > >
> > > Andrew
> > We indeed have one physical MDIO bus, but have multiple masters on
> > it,
> > each "behind" a different internal PCIe device. Since the accesses
> > to the bus
> > are done "indirectly" through each master, we can't register the
> > bus only once.
> How do your multiple masters get arbitrated on the unique MDIO bus?
> Is
> there hardware automatically doing that, or do you have to semaphore
> those accesses at the software level?
hardware level.
>
> >
> > Think of the scenario that we register it in the driver context of
> > PCIe device A,
> > and then the driver is unbound from just this device. Device B
> > won't be able
> > to access the bus since it was registered with callbacks that use a
> > PCIe BAR of
> > device A, which is no longer valid.
> You can have one single physical MDIO bus that you register once
> throughout the SoC's power on lifecycle, and then you can create
> "virtual" MDIO bus instances which map 1:1 with the PCIe
> device/function
> and are nested from that single MDIO bus, this also gives you
> serialization of accesses and arbitration for free.
the problem is that physical MDIO controller actually belongs to one of
the pcie devices and it's not independent interface, as the registers
address belongs to that pcie device, also, a reset to that pcie device
will reset the "shared" mdio controller.
>
> >
> >
> > Is it possible to register the mdio_bus struct as a global instance
> > at driver load,
> > and someway pass the offset to the specific device's MDIO master,
> > as part of
> > each read/write transaction towards the MDIO bus?
> You can register how many instances of the MDIO bus you want in a
> system, it can be a singleton for the purpose of supporting your
> specific hardware, or you can build a layer on top like I just
> suggested
> above.
>
> >
> > Or perhaps you have another suggestion which takes into account the
> > issues I've described?
> Considering that binding to a MDIO bus is done by MDIO bus name
> (bus->id) and/or Device Tree parent/child hierarchy, if there is only
> one, just have all instances reference the same MDIO bus when they
> want
> to bind to their devices (pure mdio_device, or phy_device) on that
> MDIO bus.
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH net-next 4/8] net: ethernet: add the Alpine Ethernet driver
2017-11-05 12:29 ` BSHARA, Said
@ 2017-11-05 15:22 ` Andrew Lunn
0 siblings, 0 replies; 23+ messages in thread
From: Andrew Lunn @ 2017-11-05 15:22 UTC (permalink / raw)
To: linux-arm-kernel
> the problem is that physical MDIO controller actually belongs to one of
> the pcie devices and it's not independent interface, as the registers
> address belongs to that pcie device, also, a reset to that pcie device
> will reset the "shared" mdio controller.
What is the implications of resetting the mdio controller?
It still seems like you should only register one MDIO device with
linux, and then use phy-handle properties to point to the PHYs on the
bus.
Andrew
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2017-11-05 15:22 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-02-03 18:12 [PATCH net-next 0/8] ARM: Alpine: Ethernet support Antoine Tenart
2017-02-03 18:12 ` [PATCH net-next 1/8] alpine: add I/O fabric interrupt controller (iofic) helpers Antoine Tenart
2017-02-03 18:12 ` [PATCH net-next 2/8] soc: alpine: add udma helpers Antoine Tenart
2017-02-03 18:12 ` [PATCH net-next 3/8] pci: add Annapurna Labs PCI id Antoine Tenart
2017-02-03 18:12 ` [PATCH net-next 5/8] net: ethernet: annapurna: add statistics helper Antoine Tenart
2017-02-03 19:34 ` Florian Fainelli
2017-02-03 21:24 ` kbuild test robot
2017-02-03 18:12 ` [PATCH net-next 6/8] net: ethernet: annapurna: add wol helpers to the Alpine driver Antoine Tenart
2017-02-03 18:21 ` Sergei Shtylyov
2017-02-06 11:35 ` David Laight
2017-02-06 12:02 ` Sergei Shtylyov
2017-02-10 10:12 ` Antoine Tenart
2017-02-03 18:12 ` [PATCH net-next 7/8] net: ethernet: annapurna: add eee " Antoine Tenart
2017-02-03 19:01 ` Florian Fainelli
2017-02-03 18:12 ` [PATCH net-next 8/8] net: ethernet: annapurna: add the coalesce " Antoine Tenart
[not found] ` <20170203181216.30214-5-antoine.tenart@free-electrons.com>
2017-02-03 20:58 ` [PATCH net-next 4/8] net: ethernet: add the Alpine Ethernet driver Andrew Lunn
2017-08-07 7:39 ` Chocron, Jonathan
2017-08-27 13:47 ` Chocron, Jonathan
2017-08-28 18:09 ` Andrew Lunn
2017-11-02 16:05 ` Chocron, Jonathan
2017-11-02 18:19 ` Florian Fainelli
2017-11-05 12:29 ` BSHARA, Said
2017-11-05 15:22 ` Andrew Lunn
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).