* Re: [PATCH net-next,v3] net/tls: Calculate nsg for zerocopy path without skb_cow_data.
From: Doron Roberts-Kedes @ 2018-08-07 17:55 UTC (permalink / raw)
To: David S . Miller
Cc: David S . Miller, Dave Watson, Boris Pismenny, Aviad Yehezkel,
netdev@vger.kernel.org
In-Reply-To: <DB7PR04MB425253CED3A31CF0D5919D2F8B270@DB7PR04MB4252.eurprd04.prod.outlook.com>
On Tue, Aug 07, 2018 at 04:10:50PM +0000, Vakul Garg wrote:
>
>
> > -----Original Message-----
> > From: Doron Roberts-Kedes [mailto:doronrk@fb.com]
> > Sent: Tuesday, August 7, 2018 1:18 AM
> > To: David S . Miller <davem@davemloft.net>
> > Cc: Vakul Garg <vakul.garg@nxp.com>; Dave Watson
> > <davejwatson@fb.com>; Boris Pismenny <borisp@mellanox.com>; Aviad
> > Yehezkel <aviadye@mellanox.com>; netdev@vger.kernel.org; Doron
> > Roberts-Kedes <doronrk@fb.com>
> > Subject: [PATCH net-next,v3] net/tls: Calculate nsg for zerocopy path
> > without skb_cow_data.
>
> I prefer my name be removed from commit message.
>
Sure thing. Dave is this something you can handle on your end, or would you
prefer that I submit v4 with Vakul's name removed from the commit
message?
> Reported-by: Vakul Garg <Vakul.garg@nxp.com>
> Reviewed-by: Vakul Garg <Vakul.garg@nxp.com>
> Tested-by: Vakul Garg <Vakul.garg@nxp.com>
^ permalink raw reply
* [PATCH net-next v2 3/3] net: systemport: Add support for WAKE_FILTER
From: Florian Fainelli @ 2018-08-07 17:50 UTC (permalink / raw)
To: netdev; +Cc: Florian Fainelli, andrew, vivien.didelot, davem, linville
In-Reply-To: <20180807175023.30399-1-f.fainelli@gmail.com>
The SYSTEMPORT MAC allows up to 8 filters to be programmed to wake-up
from LAN. Verify that we have up to 8 filters and program them to the
appropriate RXCHK entries to be matched (along with their masks).
We need to update the entry and exit to Wake-on-LAN mode to keep the
RXCHK engine running to match during suspend, but this is otherwise
fairly similar to Magic Packet detection.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/ethernet/broadcom/bcmsysport.c | 193 ++++++++++++++++++++-
drivers/net/ethernet/broadcom/bcmsysport.h | 11 +-
2 files changed, 195 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c
index 284581c9680e..ca47309d4494 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.c
+++ b/drivers/net/ethernet/broadcom/bcmsysport.c
@@ -521,7 +521,7 @@ static void bcm_sysport_get_wol(struct net_device *dev,
struct bcm_sysport_priv *priv = netdev_priv(dev);
u32 reg;
- wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE;
+ wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
wol->wolopts = priv->wolopts;
if (!(priv->wolopts & WAKE_MAGICSECURE))
@@ -539,7 +539,7 @@ static int bcm_sysport_set_wol(struct net_device *dev,
{
struct bcm_sysport_priv *priv = netdev_priv(dev);
struct device *kdev = &priv->pdev->dev;
- u32 supported = WAKE_MAGIC | WAKE_MAGICSECURE;
+ u32 supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
if (!device_can_wakeup(kdev))
return -ENOTSUPP;
@@ -1043,7 +1043,7 @@ static int bcm_sysport_poll(struct napi_struct *napi, int budget)
static void mpd_enable_set(struct bcm_sysport_priv *priv, bool enable)
{
- u32 reg;
+ u32 reg, bit;
reg = umac_readl(priv, UMAC_MPD_CTRL);
if (enable)
@@ -1051,12 +1051,32 @@ static void mpd_enable_set(struct bcm_sysport_priv *priv, bool enable)
else
reg &= ~MPD_EN;
umac_writel(priv, reg, UMAC_MPD_CTRL);
+
+ if (priv->is_lite)
+ bit = RBUF_ACPI_EN_LITE;
+ else
+ bit = RBUF_ACPI_EN;
+
+ reg = rbuf_readl(priv, RBUF_CONTROL);
+ if (enable)
+ reg |= bit;
+ else
+ reg &= ~bit;
+ rbuf_writel(priv, reg, RBUF_CONTROL);
}
static void bcm_sysport_resume_from_wol(struct bcm_sysport_priv *priv)
{
+ u32 reg;
+
/* Stop monitoring MPD interrupt */
- intrl2_0_mask_set(priv, INTRL2_0_MPD);
+ intrl2_0_mask_set(priv, INTRL2_0_MPD | INTRL2_0_BRCM_MATCH_TAG);
+
+ /* Disable RXCHK, active filters and Broadcom tag matching */
+ reg = rxchk_readl(priv, RXCHK_CONTROL);
+ reg &= ~(RXCHK_BRCM_TAG_MATCH_MASK <<
+ RXCHK_BRCM_TAG_MATCH_SHIFT | RXCHK_EN | RXCHK_BRCM_TAG_EN);
+ rxchk_writel(priv, reg, RXCHK_CONTROL);
/* Clear the MagicPacket detection logic */
mpd_enable_set(priv, false);
@@ -1085,6 +1105,7 @@ static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id)
struct bcm_sysport_priv *priv = netdev_priv(dev);
struct bcm_sysport_tx_ring *txr;
unsigned int ring, ring_bit;
+ u32 reg;
priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) &
~intrl2_0_readl(priv, INTRL2_CPU_MASK_STATUS);
@@ -1111,7 +1132,14 @@ static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id)
bcm_sysport_tx_reclaim_all(priv);
if (priv->irq0_stat & INTRL2_0_MPD)
- netdev_info(priv->netdev, "Wake-on-LAN interrupt!\n");
+ netdev_info(priv->netdev, "Wake-on-LAN (MPD) interrupt!\n");
+
+ if (priv->irq0_stat & INTRL2_0_BRCM_MATCH_TAG) {
+ reg = rxchk_readl(priv, RXCHK_BRCM_TAG_MATCH_STATUS) &
+ RXCHK_BRCM_TAG_MATCH_MASK;
+ netdev_info(priv->netdev,
+ "Wake-on-LAN (filters 0x%02x) interrupt!\n", reg);
+ }
if (!priv->is_lite)
goto out;
@@ -2096,6 +2124,132 @@ static int bcm_sysport_stop(struct net_device *dev)
return 0;
}
+static int bcm_sysport_rule_find(struct bcm_sysport_priv *priv,
+ u64 location)
+{
+ unsigned int index;
+ u32 reg;
+
+ for_each_set_bit(index, priv->filters, RXCHK_BRCM_TAG_MAX) {
+ reg = rxchk_readl(priv, RXCHK_BRCM_TAG(index));
+ reg >>= RXCHK_BRCM_TAG_CID_SHIFT;
+ reg &= RXCHK_BRCM_TAG_CID_MASK;
+ if (reg == location)
+ return index;
+ }
+
+ return -EINVAL;
+}
+
+static int bcm_sysport_rule_get(struct bcm_sysport_priv *priv,
+ struct ethtool_rxnfc *nfc)
+{
+ int index;
+
+ /* This is not a rule that we know about */
+ index = bcm_sysport_rule_find(priv, nfc->fs.location);
+ if (index < 0)
+ return -EOPNOTSUPP;
+
+ nfc->fs.ring_cookie = RX_CLS_FLOW_WAKE;
+
+ return 0;
+}
+
+static int bcm_sysport_rule_set(struct bcm_sysport_priv *priv,
+ struct ethtool_rxnfc *nfc)
+{
+ unsigned int index;
+ u32 reg;
+
+ /* We cannot match locations greater than what the classification ID
+ * permits (256 entries)
+ */
+ if (nfc->fs.location > RXCHK_BRCM_TAG_CID_MASK)
+ return -E2BIG;
+
+ /* We cannot support flows that are not destined for a wake-up */
+ if (nfc->fs.ring_cookie != RX_CLS_FLOW_WAKE)
+ return -EOPNOTSUPP;
+
+ /* All filters are already in use, we cannot match more rules */
+ if (bitmap_weight(priv->filters, RXCHK_BRCM_TAG_MAX) ==
+ RXCHK_BRCM_TAG_MAX)
+ return -ENOSPC;
+
+ index = find_first_zero_bit(priv->filters, RXCHK_BRCM_TAG_MAX);
+ if (index > RXCHK_BRCM_TAG_MAX)
+ return -ENOSPC;
+
+ /* Location is the classification ID, and index is the position
+ * within one of our 8 possible filters to be programmed
+ */
+ reg = rxchk_readl(priv, RXCHK_BRCM_TAG(index));
+ reg &= ~(RXCHK_BRCM_TAG_CID_MASK << RXCHK_BRCM_TAG_CID_SHIFT);
+ reg |= nfc->fs.location << RXCHK_BRCM_TAG_CID_SHIFT;
+ rxchk_writel(priv, reg, RXCHK_BRCM_TAG(index));
+ rxchk_writel(priv, 0xff00ffff, RXCHK_BRCM_TAG_MASK(index));
+
+ set_bit(index, priv->filters);
+
+ return 0;
+}
+
+static int bcm_sysport_rule_del(struct bcm_sysport_priv *priv,
+ u64 location)
+{
+ int index;
+
+ /* This is not a rule that we know about */
+ index = bcm_sysport_rule_find(priv, location);
+ if (index < 0)
+ return -EOPNOTSUPP;
+
+ /* No need to disable this filter if it was enabled, this will
+ * be taken care of during suspend time by bcm_sysport_suspend_to_wol
+ */
+ clear_bit(index, priv->filters);
+
+ return 0;
+}
+
+static int bcm_sysport_get_rxnfc(struct net_device *dev,
+ struct ethtool_rxnfc *nfc, u32 *rule_locs)
+{
+ struct bcm_sysport_priv *priv = netdev_priv(dev);
+ int ret = -EOPNOTSUPP;
+
+ switch (nfc->cmd) {
+ case ETHTOOL_GRXCLSRULE:
+ ret = bcm_sysport_rule_get(priv, nfc);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int bcm_sysport_set_rxnfc(struct net_device *dev,
+ struct ethtool_rxnfc *nfc)
+{
+ struct bcm_sysport_priv *priv = netdev_priv(dev);
+ int ret = -EOPNOTSUPP;
+
+ switch (nfc->cmd) {
+ case ETHTOOL_SRXCLSRLINS:
+ ret = bcm_sysport_rule_set(priv, nfc);
+ break;
+ case ETHTOOL_SRXCLSRLDEL:
+ ret = bcm_sysport_rule_del(priv, nfc->fs.location);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
static const struct ethtool_ops bcm_sysport_ethtool_ops = {
.get_drvinfo = bcm_sysport_get_drvinfo,
.get_msglevel = bcm_sysport_get_msglvl,
@@ -2110,6 +2264,8 @@ static const struct ethtool_ops bcm_sysport_ethtool_ops = {
.set_coalesce = bcm_sysport_set_coalesce,
.get_link_ksettings = phy_ethtool_get_link_ksettings,
.set_link_ksettings = phy_ethtool_set_link_ksettings,
+ .get_rxnfc = bcm_sysport_get_rxnfc,
+ .set_rxnfc = bcm_sysport_set_rxnfc,
};
static u16 bcm_sysport_select_queue(struct net_device *dev, struct sk_buff *skb,
@@ -2434,16 +2590,39 @@ static int bcm_sysport_suspend_to_wol(struct bcm_sysport_priv *priv)
{
struct net_device *ndev = priv->netdev;
unsigned int timeout = 1000;
+ unsigned int index, i = 0;
u32 reg;
/* Password has already been programmed */
reg = umac_readl(priv, UMAC_MPD_CTRL);
- reg |= MPD_EN;
+ if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE))
+ reg |= MPD_EN;
reg &= ~PSW_EN;
if (priv->wolopts & WAKE_MAGICSECURE)
reg |= PSW_EN;
umac_writel(priv, reg, UMAC_MPD_CTRL);
+ if (priv->wolopts & WAKE_FILTER) {
+ /* Turn on ACPI matching to steal packets from RBUF */
+ reg = rbuf_readl(priv, RBUF_CONTROL);
+ if (priv->is_lite)
+ reg |= RBUF_ACPI_EN_LITE;
+ else
+ reg |= RBUF_ACPI_EN;
+ rbuf_writel(priv, reg, RBUF_CONTROL);
+
+ /* Enable RXCHK, active filters and Broadcom tag matching */
+ reg = rxchk_readl(priv, RXCHK_CONTROL);
+ reg &= ~(RXCHK_BRCM_TAG_MATCH_MASK <<
+ RXCHK_BRCM_TAG_MATCH_SHIFT);
+ for_each_set_bit(index, priv->filters, RXCHK_BRCM_TAG_MAX) {
+ reg |= BIT(RXCHK_BRCM_TAG_MATCH_SHIFT + i);
+ i++;
+ }
+ reg |= RXCHK_EN | RXCHK_BRCM_TAG_EN;
+ rxchk_writel(priv, reg, RXCHK_CONTROL);
+ }
+
/* Make sure RBUF entered WoL mode as result */
do {
reg = rbuf_readl(priv, RBUF_STATUS);
@@ -2464,7 +2643,7 @@ static int bcm_sysport_suspend_to_wol(struct bcm_sysport_priv *priv)
umac_enable_set(priv, CMD_RX_EN, 1);
/* Enable the interrupt wake-up source */
- intrl2_0_mask_clear(priv, INTRL2_0_MPD);
+ intrl2_0_mask_clear(priv, INTRL2_0_MPD | INTRL2_0_BRCM_MATCH_TAG);
netif_dbg(priv, wol, ndev, "entered WOL mode\n");
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.h b/drivers/net/ethernet/broadcom/bcmsysport.h
index cf440b91fd04..046c6c1d97fd 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.h
+++ b/drivers/net/ethernet/broadcom/bcmsysport.h
@@ -11,6 +11,7 @@
#ifndef __BCM_SYSPORT_H
#define __BCM_SYSPORT_H
+#include <linux/bitmap.h>
#include <linux/if_vlan.h>
#include <linux/net_dim.h>
@@ -155,14 +156,18 @@ struct bcm_rsb {
#define RXCHK_PARSE_AUTH (1 << 22)
#define RXCHK_BRCM_TAG0 0x04
-#define RXCHK_BRCM_TAG(i) ((i) * RXCHK_BRCM_TAG0)
+#define RXCHK_BRCM_TAG(i) ((i) * 0x4 + RXCHK_BRCM_TAG0)
#define RXCHK_BRCM_TAG0_MASK 0x24
-#define RXCHK_BRCM_TAG_MASK(i) ((i) * RXCHK_BRCM_TAG0_MASK)
+#define RXCHK_BRCM_TAG_MASK(i) ((i) * 0x4 + RXCHK_BRCM_TAG0_MASK)
#define RXCHK_BRCM_TAG_MATCH_STATUS 0x44
#define RXCHK_ETHERTYPE 0x48
#define RXCHK_BAD_CSUM_CNTR 0x4C
#define RXCHK_OTHER_DISC_CNTR 0x50
+#define RXCHK_BRCM_TAG_MAX 8
+#define RXCHK_BRCM_TAG_CID_SHIFT 16
+#define RXCHK_BRCM_TAG_CID_MASK 0xff
+
/* TXCHCK offsets and defines */
#define SYS_PORT_TXCHK_OFFSET 0x380
#define TXCHK_PKT_RDY_THRESH 0x00
@@ -185,6 +190,7 @@ struct bcm_rsb {
#define RBUF_RSB_SWAP0 (1 << 22)
#define RBUF_RSB_SWAP1 (1 << 23)
#define RBUF_ACPI_EN (1 << 23)
+#define RBUF_ACPI_EN_LITE (1 << 24)
#define RBUF_PKT_RDY_THRESH 0x04
@@ -777,6 +783,7 @@ struct bcm_sysport_priv {
/* Ethtool */
u32 msg_enable;
+ DECLARE_BITMAP(filters, RXCHK_BRCM_TAG_MAX);
struct bcm_sysport_stats64 stats64;
--
2.17.1
^ permalink raw reply related
* [PATCH net-next v2 2/3] net: dsa: bcm_sf2: Propagate ethtool::rxnfc to CPU port
From: Florian Fainelli @ 2018-08-07 17:50 UTC (permalink / raw)
To: netdev; +Cc: Florian Fainelli, andrew, vivien.didelot, davem, linville
In-Reply-To: <20180807175023.30399-1-f.fainelli@gmail.com>
Allow propagating ethtool::rxnfc programming to the CPU/management port
such that it is possible for such a CPU to perform e.g: Wake-on-LAN
using filters configured by the switch. We need a tiny bit of
cooperation between the switch drivers which is able to do the full flow
matching, whereas the CPU/management port might not. The CPU/management
driver needs to return -EOPNOTSUPP to indicate an non critical error,
any other error code otherwise.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/dsa/bcm_sf2_cfp.c | 43 ++++++++++++++++++++++++++++++++---
1 file changed, 40 insertions(+), 3 deletions(-)
diff --git a/drivers/net/dsa/bcm_sf2_cfp.c b/drivers/net/dsa/bcm_sf2_cfp.c
index 1e37b65aab93..47c5f272a084 100644
--- a/drivers/net/dsa/bcm_sf2_cfp.c
+++ b/drivers/net/dsa/bcm_sf2_cfp.c
@@ -732,6 +732,8 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,
struct ethtool_rx_flow_spec *fs)
{
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+ s8 cpu_port = ds->ports[port].cpu_dp->index;
+ __u64 ring_cookie = fs->ring_cookie;
unsigned int queue_num, port_num;
int ret = -EINVAL;
@@ -748,13 +750,19 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,
fs->location > bcm_sf2_cfp_rule_size(priv))
return -EINVAL;
+ /* This rule is a Wake-on-LAN filter and we must specifically
+ * target the CPU port in order for it to be working.
+ */
+ if (ring_cookie == RX_CLS_FLOW_WAKE)
+ ring_cookie = cpu_port * SF2_NUM_EGRESS_QUEUES;
+
/* We do not support discarding packets, check that the
* destination port is enabled and that we are within the
* number of ports supported by the switch
*/
- port_num = fs->ring_cookie / SF2_NUM_EGRESS_QUEUES;
+ port_num = ring_cookie / SF2_NUM_EGRESS_QUEUES;
- if (fs->ring_cookie == RX_CLS_FLOW_DISC ||
+ if (ring_cookie == RX_CLS_FLOW_DISC ||
!(dsa_is_user_port(ds, port_num) ||
dsa_is_cpu_port(ds, port_num)) ||
port_num >= priv->hw_params.num_ports)
@@ -763,7 +771,7 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,
* We have a small oddity where Port 6 just does not have a
* valid bit here (so we substract by one).
*/
- queue_num = fs->ring_cookie % SF2_NUM_EGRESS_QUEUES;
+ queue_num = ring_cookie % SF2_NUM_EGRESS_QUEUES;
if (port_num >= 7)
port_num -= 1;
@@ -1188,6 +1196,7 @@ static int bcm_sf2_cfp_rule_get_all(struct bcm_sf2_priv *priv,
int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port,
struct ethtool_rxnfc *nfc, u32 *rule_locs)
{
+ struct net_device *p = ds->ports[port].cpu_dp->master;
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
int ret = 0;
@@ -1214,12 +1223,23 @@ int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port,
mutex_unlock(&priv->cfp.lock);
+ if (ret)
+ return ret;
+
+ /* Pass up the commands to the attached master network device */
+ if (p->ethtool_ops->get_rxnfc) {
+ ret = p->ethtool_ops->get_rxnfc(p, nfc, rule_locs);
+ if (ret == -EOPNOTSUPP)
+ ret = 0;
+ }
+
return ret;
}
int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port,
struct ethtool_rxnfc *nfc)
{
+ struct net_device *p = ds->ports[port].cpu_dp->master;
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
int ret = 0;
@@ -1240,6 +1260,23 @@ int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port,
mutex_unlock(&priv->cfp.lock);
+ if (ret)
+ return ret;
+
+ /* Pass up the commands to the attached master network device.
+ * This can fail, so rollback the operation if we need to.
+ */
+ if (p->ethtool_ops->set_rxnfc) {
+ ret = p->ethtool_ops->set_rxnfc(p, nfc);
+ if (ret && ret != -EOPNOTSUPP) {
+ mutex_lock(&priv->cfp.lock);
+ bcm_sf2_cfp_rule_del(priv, port, nfc->fs.location);
+ mutex_unlock(&priv->cfp.lock);
+ } else {
+ ret = 0;
+ }
+ }
+
return ret;
}
--
2.17.1
^ permalink raw reply related
* [PATCH] ethtool: Add support for Wake-on-LAN using filters
From: Florian Fainelli @ 2018-08-07 17:50 UTC (permalink / raw)
To: netdev; +Cc: Florian Fainelli, andrew, vivien.didelot, davem, linville
In-Reply-To: <20180807175023.30399-1-f.fainelli@gmail.com>
Define a way to specify that a flow's action is to be used for
Wake-on-LAN purposes (using -2 as an action value) and define a new
Wake-on-LAN flag: 'f' which enables the Ethernet adapter for Wake-on-LAN
using filters.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
ethtool-copy.h | 2 ++
ethtool.8.in | 4 +++-
ethtool.c | 7 ++++++-
rxclass.c | 8 +++++---
4 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/ethtool-copy.h b/ethtool-copy.h
index 8cc61e9ab40b..943af3882aa1 100644
--- a/ethtool-copy.h
+++ b/ethtool-copy.h
@@ -1628,6 +1628,7 @@ static __inline__ int ethtool_validate_duplex(__u8 duplex)
#define WAKE_ARP (1 << 4)
#define WAKE_MAGIC (1 << 5)
#define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */
+#define WAKE_FILTER (1 << 7)
/* L2-L4 network traffic flow types */
#define TCP_V4_FLOW 0x01 /* hash or spec (tcp_ip4_spec) */
@@ -1665,6 +1666,7 @@ static __inline__ int ethtool_validate_duplex(__u8 duplex)
#define RXH_DISCARD (1 << 31)
#define RX_CLS_FLOW_DISC 0xffffffffffffffffULL
+#define RX_CLS_FLOW_WAKE 0xfffffffffffffffeULL
/* Special RX classification rule insert location values */
#define RX_CLS_LOC_SPECIAL 0x80000000 /* flag */
diff --git a/ethtool.8.in b/ethtool.8.in
index 0a366aa536ae..61923eed7c27 100644
--- a/ethtool.8.in
+++ b/ethtool.8.in
@@ -58,7 +58,7 @@
.\"
.\" \(*WO - wol flags
.\"
-.ds WO \fBp\fP|\fBu\fP|\fBm\fP|\fBb\fP|\fBa\fP|\fBg\fP|\fBs\fP|\fBd\fP...
+.ds WO \fBp\fP|\fBu\fP|\fBm\fP|\fBb\fP|\fBa\fP|\fBg\fP|\fBs\fP|\fBd|\fBf\fP...
.\"
.\" \(*FL - flow type values
.\"
@@ -679,6 +679,7 @@ b Wake on broadcast messages
a Wake on ARP
g Wake on MagicPacket\[tm]
s Enable SecureOn\[tm] password for MagicPacket\[tm]
+f Wake on filter(s)
d T{
Disable (wake on nothing). This option clears all previous options.
T}
@@ -870,6 +871,7 @@ Specifies the Rx queue to send packets to, or some other action.
nokeep;
lB l.
-1 Drop the matched flow
+-2 Use the matched flow as a Wake-on-LAN filter
0 or higher Rx queue to route the flow
.TE
.TP
diff --git a/ethtool.c b/ethtool.c
index fb93ae898312..5e91ef9cecdb 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -931,6 +931,9 @@ static int parse_wolopts(char *optstr, u32 *data)
case 's':
*data |= WAKE_MAGICSECURE;
break;
+ case 'f':
+ *data |= WAKE_FILTER;
+ break;
case 'd':
*data = 0;
break;
@@ -964,6 +967,8 @@ static char *unparse_wolopts(int wolopts)
*p++ = 'g';
if (wolopts & WAKE_MAGICSECURE)
*p++ = 's';
+ if (wolopts & WAKE_FILTER)
+ *p++ = 'f';
} else {
*p = 'd';
}
@@ -4224,7 +4229,7 @@ static int flow_spec_to_ntuple(struct ethtool_rx_flow_spec *fsp,
return -1;
/* verify ring cookie can transfer to action */
- if (fsp->ring_cookie > INT_MAX && fsp->ring_cookie < (u64)(-2))
+ if (fsp->ring_cookie > INT_MAX && fsp->ring_cookie < (u64)(-3))
return -1;
/* verify only one field is setting data field */
diff --git a/rxclass.c b/rxclass.c
index 42d122d1ed86..79972651e706 100644
--- a/rxclass.c
+++ b/rxclass.c
@@ -251,7 +251,11 @@ static void rxclass_print_nfc_rule(struct ethtool_rx_flow_spec *fsp,
if (fsp->flow_type & FLOW_RSS)
fprintf(stdout, "\tRSS Context ID: %u\n", rss_context);
- if (fsp->ring_cookie != RX_CLS_FLOW_DISC) {
+ if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {
+ fprintf(stdout, "\tAction: Drop\n");
+ } else if (fsp->ring_cookie == RX_CLS_FLOW_WAKE) {
+ fprintf(stdout, "\tAction: Wake-on-LAN\n");
+ } else {
u64 vf = ethtool_get_flow_spec_ring_vf(fsp->ring_cookie);
u64 queue = ethtool_get_flow_spec_ring(fsp->ring_cookie);
@@ -266,8 +270,6 @@ static void rxclass_print_nfc_rule(struct ethtool_rx_flow_spec *fsp,
else
fprintf(stdout, "\tAction: Direct to queue %llu\n",
queue);
- } else {
- fprintf(stdout, "\tAction: Drop\n");
}
fprintf(stdout, "\n");
--
2.17.1
^ permalink raw reply related
* [PATCH net-next v2 1/3] ethtool: Add WAKE_FILTER and RX_CLS_FLOW_WAKE
From: Florian Fainelli @ 2018-08-07 17:50 UTC (permalink / raw)
To: netdev; +Cc: Florian Fainelli, andrew, vivien.didelot, davem, linville
In-Reply-To: <20180807175023.30399-1-f.fainelli@gmail.com>
Add the ability to specify through ethtool::rxnfc that a rule location is
special and will be used to participate in Wake-on-LAN, by e.g: having a
specific pattern be matched. When this is the case, fs->ring_cookie must
be set to the special value RX_CLS_FLOW_WAKE.
We also define an additional ethtool::wolinfo flag: WAKE_FILTER which
can be used to configure an Ethernet adapter to allow Wake-on-LAN using
previously programmed filters.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
include/uapi/linux/ethtool.h | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index 813282cc8af6..dc69391d2bba 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -870,7 +870,8 @@ struct ethtool_flow_ext {
* includes the %FLOW_EXT or %FLOW_MAC_EXT flag
* (see &struct ethtool_flow_ext description).
* @ring_cookie: RX ring/queue index to deliver to, or %RX_CLS_FLOW_DISC
- * if packets should be discarded
+ * if packets should be discarded, or %RX_CLS_FLOW_WAKE if the
+ * packets should be used for Wake-on-LAN with %WAKE_FILTER
* @location: Location of rule in the table. Locations must be
* numbered such that a flow matching multiple rules will be
* classified according to the first (lowest numbered) rule.
@@ -1634,6 +1635,7 @@ static inline int ethtool_validate_duplex(__u8 duplex)
#define WAKE_ARP (1 << 4)
#define WAKE_MAGIC (1 << 5)
#define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */
+#define WAKE_FILTER (1 << 7)
/* L2-L4 network traffic flow types */
#define TCP_V4_FLOW 0x01 /* hash or spec (tcp_ip4_spec) */
@@ -1671,6 +1673,7 @@ static inline int ethtool_validate_duplex(__u8 duplex)
#define RXH_DISCARD (1 << 31)
#define RX_CLS_FLOW_DISC 0xffffffffffffffffULL
+#define RX_CLS_FLOW_WAKE 0xfffffffffffffffeULL
/* Special RX classification rule insert location values */
#define RX_CLS_LOC_SPECIAL 0x80000000 /* flag */
--
2.17.1
^ permalink raw reply related
* [PATCH net-next v2 0/3] net: Support Wake-on-LAN using filters
From: Florian Fainelli @ 2018-08-07 17:50 UTC (permalink / raw)
To: netdev; +Cc: Florian Fainelli, andrew, vivien.didelot, davem, linville
Hi David, John,
This is technically a v2, but this patch series builds on your feedback
and defines the following:
- a new WAKE_* bit: WAKE_FILTER which can be enabled alongside other type
of Wake-on-LAN to support waking up on a programmed filter (match + action)
- a new RX_CLS_FLOW_WAKE flow action which can be specified by an user when
inserting a flow using ethtool::rxnfc, similar to the existing RX_CLS_FLOW_DISC
The bcm_sf2 and bcmsysport drivers are updated accordingly to work in concert to
allow matching packets at the switch level, identified by their filter location
to be used as a match by the SYSTEM PORT (CPU/management controller) during
Wake-on-LAN.
Let me know if this looks better than the previous incarnation of the patch
series.
Attached is also the ethtool patch that I would be submitting once the uapi
changes are committed.
Thank you!
Changes in v2:
- bail out earlier in bcm_sf2_cfp's get_rxnfc if an error is
encountered (Andrew)
Florian Fainelli (3):
ethtool: Add WAKE_FILTER and RX_CLS_FLOW_WAKE
net: dsa: bcm_sf2: Propagate ethtool::rxnfc to CPU port
net: systemport: Add support for WAKE_FILTER
drivers/net/dsa/bcm_sf2_cfp.c | 43 ++++-
drivers/net/ethernet/broadcom/bcmsysport.c | 193 ++++++++++++++++++++-
drivers/net/ethernet/broadcom/bcmsysport.h | 11 +-
include/uapi/linux/ethtool.h | 5 +-
4 files changed, 239 insertions(+), 13 deletions(-)
--
2.17.1
^ permalink raw reply
* Re: [PATCH net-next 2/6] nfp: flower: allow matching on ipv4 UDP tunnel tos and ttl
From: Or Gerlitz @ 2018-08-07 17:46 UTC (permalink / raw)
To: Simon Horman
Cc: David Miller, Jiri Pirko, Cong Wang, Jakub Kicinski,
Linux Netdev List, oss-drivers
In-Reply-To: <20180807153603.1815-3-simon.horman@netronome.com>
On Tue, Aug 7, 2018 at 6:35 PM, Simon Horman <simon.horman@netronome.com> wrote:
> From: John Hurley <john.hurley@netronome.com>
>
> The addition of FLOW_DISSECTOR_KEY_ENC_IP to TC flower means that the ToS
> and TTL of the tunnel header can now be matched on.
>
> Extend the NFP tunnel match function to include these new fields.
(referring to you patch title) where is this explicitly dealing with
udp tunnel? tos/ttl belong to any IP tunnel, e.g GRE as well
Or.
^ permalink raw reply
* Re: [PATCH net-next 4/6] net/sched: allow flower to match tunnel options
From: Or Gerlitz @ 2018-08-07 17:44 UTC (permalink / raw)
To: Simon Horman
Cc: David Miller, Jiri Pirko, Cong Wang, Jakub Kicinski,
Linux Netdev List, oss-drivers
In-Reply-To: <20180807153603.1815-5-simon.horman@netronome.com>
On Tue, Aug 7, 2018 at 6:36 PM, Simon Horman <simon.horman@netronome.com> wrote:
> From: Pieter Jansen van Vuuren <pieter.jansenvanvuuren@netronome.com>
>
> Allow matching on options in Geneve tunnel headers.
> This makes use of existing tunnel metadata support.
>
> The options can be described in the form
> CLASS:TYPE:DATA/CLASS_MASK:TYPE_MASK:DATA_MASK, where CLASS is
> represented as a 16bit hexadecimal value, TYPE as an 8bit
> hexadecimal value and DATA as a variable length hexadecimal value.
>
> e.g.
> # ip link add name geneve0 type geneve dstport 0 external
> # tc qdisc add dev geneve0 ingress
> # tc filter add dev geneve0 protocol ip parent ffff: \
> flower \
> enc_src_ip 10.0.99.192 \
> enc_dst_ip 10.0.99.193 \
> enc_key_id 11 \
> geneve_opts 0102:80:1122334421314151/ffff:ff:ffffffffffffffff \
> ip_proto udp \
> action mirred egress redirect dev eth1
>
> This patch adds support for matching Geneve options in the order
> supplied by the user. This leads to an efficient implementation in
> the software datapath (and in our opinion hardware datapaths that
> offload this feature). It is also compatible with Geneve options
> matching provided by the Open vSwitch kernel datapath which is
> relevant here as the Flower classifier may be used as a mechanism
> to program flows into hardware as a form of Open vSwitch datapath
> offload (sometimes referred to as OVS-TC). The netlink
> Kernel/Userspace API may be extended, for example by adding a flag,
> if other matching options are desired, for example matching given
> options in any order. This would require an implementation in the
> TC software datapath. And be done in a way that drivers that
> facilitate offload of the Flower classifier can reject or accept
> such flows based on hardware datapath capabilities.
>
> This approach was discussed and agreed on at Netconf 2018 in Seoul.
2017, please fix
2018 was in Montreal
^ permalink raw reply
* [PATCH] netfilter/x_tables: do not fail xt_alloc_table_info too easilly
From: Michal Hocko @ 2018-08-07 19:54 UTC (permalink / raw)
To: Florian Westphal
Cc: Vlastimil Babka, Georgi Nikolov, Andrew Morton, David S. Miller,
netfilter-devel, coreteam, netdev, linux-kernel, linux-mm,
Michal Hocko
From: Michal Hocko <mhocko@suse.com>
eacd86ca3b03 ("net/netfilter/x_tables.c: use kvmalloc()
in xt_alloc_table_info()") has unintentionally fortified
xt_alloc_table_info allocation when __GFP_RETRY has been dropped from
the vmalloc fallback. Later on there was a syzbot report that this
can lead to OOM killer invocations when tables are too large and
0537250fdc6c ("netfilter: x_tables: make allocation less aggressive")
has been merged to restore the original behavior. Georgi Nikolov however
noticed that he is not able to install his iptables anymore so this can
be seen as a regression.
The primary argument for 0537250fdc6c was that this allocation path
shouldn't really trigger the OOM killer and kill innocent tasks. On the
other hand the interface requires root and as such should allow what the
admin asks for. Root inside a namespaces makes this more complicated
because those might be not trusted in general. If they are not then such
namespaces should be restricted anyway. Therefore drop the __GFP_NORETRY
and replace it by __GFP_ACCOUNT to enfore memcg constrains on it.
Fixes: 0537250fdc6c ("netfilter: x_tables: make allocation less aggressive")
Reported-by: Georgi Nikolov <gnikolov@icdsoft.com>
Suggested-by: Vlastimil Babka <vbabka@suse.cz>
Acked-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Michal Hocko <mhocko@suse.com>
---
net/netfilter/x_tables.c | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
index d0d8397c9588..aecadd471e1d 100644
--- a/net/netfilter/x_tables.c
+++ b/net/netfilter/x_tables.c
@@ -1178,12 +1178,7 @@ struct xt_table_info *xt_alloc_table_info(unsigned int size)
if (sz < sizeof(*info) || sz >= XT_MAX_TABLE_SIZE)
return NULL;
- /* __GFP_NORETRY is not fully supported by kvmalloc but it should
- * work reasonably well if sz is too large and bail out rather
- * than shoot all processes down before realizing there is nothing
- * more to reclaim.
- */
- info = kvmalloc(sz, GFP_KERNEL | __GFP_NORETRY);
+ info = kvmalloc(sz, GFP_KERNEL_ACCOUNT);
if (!info)
return NULL;
--
2.18.0
^ permalink raw reply related
* Re: [PATCH v3 net-next 9/9] dt-bindings: net: stmmac: Add the bindings documentation for XGMAC2.
From: Rob Herring @ 2018-08-07 17:36 UTC (permalink / raw)
To: Jose Abreu
Cc: netdev, David S. Miller, Joao Pinto, Giuseppe Cavallaro,
Alexandre Torgue, Sergei Shtylyov, devicetree
In-Reply-To: <782f85bb556f1f1d8b4eb0b803092ecb4a2c98a8.1533311285.git.joabreu@synopsys.com>
On Fri, Aug 03, 2018 at 04:50:27PM +0100, Jose Abreu wrote:
> Adds the documentation for XGMAC2 DT bindings.
>
> Signed-off-by: Jose Abreu <joabreu@synopsys.com>
> Cc: David S. Miller <davem@davemloft.net>
> Cc: Joao Pinto <jpinto@synopsys.com>
> Cc: Giuseppe Cavallaro <peppe.cavallaro@st.com>
> Cc: Alexandre Torgue <alexandre.torgue@st.com>
> Cc: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
> Cc: devicetree@vger.kernel.org
> Cc: Rob Herring <robh+dt@kernel.org>
> ---
> Changes from v1:
> - Correct header, now we also support 2.5/10G.
> - Add missing '>' (Sergei)
> ---
> Documentation/devicetree/bindings/net/stmmac.txt | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt
> index 3a28a5d8857d..a32fd590ce8f 100644
> --- a/Documentation/devicetree/bindings/net/stmmac.txt
> +++ b/Documentation/devicetree/bindings/net/stmmac.txt
> @@ -1,7 +1,8 @@
> -* STMicroelectronics 10/100/1000 Ethernet driver (GMAC)
> +* STMicroelectronics 10/100/1000/2500/10000 Ethernet driver (GMAC/XGMAC)
Bindings don't describe drivers.
>
> Required properties:
> -- compatible: Should be "snps,dwmac-<ip_version>", "snps,dwmac"
> +- compatible: Should be "snps,dwmac-<ip_version>", "snps,dwmac" or
> + "snps,dwxgmac-<ip_version>", "snps,dwxgmac".
> For backwards compatibility: "st,spear600-gmac" is also supported.
> - reg: Address and length of the register set for the device
> - interrupt-parent: Should be the phandle for the interrupt controller
> --
> 2.7.4
>
>
^ permalink raw reply
* [PATCH lora-next 10/10] net: lora: sx1301: convert all firmware to regmap
From: Ben Whitten @ 2018-08-07 17:32 UTC (permalink / raw)
To: afaerber, starnight, hasnain.virk; +Cc: netdev, Ben Whitten
In-Reply-To: <1533663131-16313-1-git-send-email-ben.whitten@gmail.com>
The last stage in the probe function loads the final firmwares to
the sx1301.
Signed-off-by: Ben Whitten <ben.whitten@gmail.com>
---
drivers/net/lora/sx1301.c | 67 ++++++++++++++++++++---------------------------
1 file changed, 28 insertions(+), 39 deletions(-)
diff --git a/drivers/net/lora/sx1301.c b/drivers/net/lora/sx1301.c
index 21b3f9a..7f4bfa1 100644
--- a/drivers/net/lora/sx1301.c
+++ b/drivers/net/lora/sx1301.c
@@ -663,16 +663,15 @@ static int sx1301_agc_calibrate(struct sx1301_priv *priv)
return 0;
}
-static int sx1301_load_all_firmware(struct spi_device *spi)
+static int sx1301_load_all_firmware(struct sx1301_priv *priv)
{
- struct sx1301_priv *priv = spi_get_drvdata(spi);
const struct firmware *fw;
u8 val;
int ret;
ret = request_firmware(&fw, "sx1301_arb.bin", priv->dev);
if (ret) {
- dev_err(priv->dev, "arb firmware file load failed\n");
+ dev_err(priv->dev, "ARB firmware file load failed\n");
return ret;
}
@@ -683,7 +682,7 @@ static int sx1301_load_all_firmware(struct spi_device *spi)
ret = request_firmware(&fw, "sx1301_agc.bin", priv->dev);
if (ret) {
- dev_err(priv->dev, "agc firmware file load failed\n");
+ dev_err(priv->dev, "AGC firmware file load failed\n");
return ret;
}
@@ -692,63 +691,53 @@ static int sx1301_load_all_firmware(struct spi_device *spi)
if (ret)
return ret;
- ret = sx1301_page_read(spi, 0, 105, &val);
- if (ret) {
- dev_err(&spi->dev, "0|105 read failed\n");
+ ret = sx1301_field_write(priv, F_FORCE_HOST_RADIO_CTRL, 0);
+ if (ret)
return ret;
- }
-
- val &= ~(REG_0_105_FORCE_HOST_RADIO_CTRL | REG_0_105_FORCE_HOST_FE_CTRL | REG_0_105_FORCE_DEC_FILTER_GAIN);
-
- ret = sx1301_page_write(spi, 0, 105, val);
- if (ret) {
- dev_err(&spi->dev, "0|105 write failed\n");
+ ret = sx1301_field_write(priv, F_FORCE_HOST_FE_CTRL, 0);
+ if (ret)
return ret;
- }
-
- ret = sx1301_page_write(spi, 0, REG_0_RADIO_SELECT, 0);
- if (ret) {
- dev_err(&spi->dev, "radio select write failed\n");
+ ret = sx1301_field_write(priv, F_FORCE_DEC_FILTER_GAIN, 0);
+ if (ret)
return ret;
- }
- ret = sx1301_page_read(spi, 0, REG_0_MCU, &val);
- if (ret) {
- dev_err(&spi->dev, "MCU read (0) failed\n");
+ ret = regmap_write(priv->regmap, SX1301_CHRS, 0);
+ if (ret)
return ret;
- }
-
- val &= ~(REG_0_MCU_RST_1 | REG_0_MCU_RST_0);
- ret = sx1301_page_write(spi, 0, REG_0_MCU, val);
- if (ret) {
- dev_err(&spi->dev, "MCU write (0) failed\n");
+ /* Release the CPUs */
+ ret = sx1301_field_write(priv, F_MCU_RST_0, 0);
+ if (ret)
+ return ret;
+ ret = sx1301_field_write(priv, F_MCU_RST_1, 0);
+ if (ret)
return ret;
- }
ret = sx1301_agc_ram_read(priv, 0x20, &val);
if (ret) {
- dev_err(&spi->dev, "AGC RAM data read failed\n");
+ dev_err(priv->dev, "AGC RAM data read failed\n");
return ret;
}
- dev_info(&spi->dev, "AGC firmware version %u\n", (unsigned)val);
+ dev_info(priv->dev, "AGC firmware version %u\n", (unsigned)val);
- if (val != 4) {
- dev_err(&spi->dev, "unexpected firmware version, expecting %u\n", 4);
+ if (val != SX1301_MCU_AGC_FW_VERSION) {
+ dev_err(priv->dev, "unexpected firmware version, expecting %u\n",
+ SX1301_MCU_AGC_FW_VERSION);
return -ENXIO;
}
ret = sx1301_arb_ram_read(priv, 0x20, &val);
if (ret) {
- dev_err(&spi->dev, "ARB RAM data read failed\n");
+ dev_err(priv->dev, "ARB RAM data read failed\n");
return ret;
}
- dev_info(&spi->dev, "ARB firmware version %u\n", (unsigned)val);
+ dev_info(priv->dev, "ARB firmware version %u\n", (unsigned)val);
- if (val != 1) {
- dev_err(&spi->dev, "unexpected firmware version, expecting %u\n", 1);
+ if (val != SX1301_MCU_ARB_FW_VERSION) {
+ dev_err(priv->dev, "unexpected firmware version, expecting %u\n",
+ SX1301_MCU_ARB_FW_VERSION);
return -ENXIO;
}
@@ -942,7 +931,7 @@ static int sx1301_probe(struct spi_device *spi)
/* TODO */
- ret = sx1301_load_all_firmware(spi);
+ ret = sx1301_load_all_firmware(priv);
if (ret)
return ret;
--
2.7.4
^ permalink raw reply related
* [PATCH lora-next 09/10] net: lora: sx1301: convert agc calibrate to regmap functions
From: Ben Whitten @ 2018-08-07 17:32 UTC (permalink / raw)
To: afaerber, starnight, hasnain.virk; +Cc: netdev, Ben Whitten
In-Reply-To: <1533663131-16313-1-git-send-email-ben.whitten@gmail.com>
From: Ben Whitten <ben.whitten@lairdtech.com>
We convert the AGC calibration function over to regmap and take the
opportunity to make the expected firmware versions defines.
Switch to taking a pointer to our private data.
Signed-off-by: Ben Whitten <ben.whitten@lairdtech.com>
---
drivers/net/lora/sx1301.c | 101 ++++++++++++----------------------------------
1 file changed, 26 insertions(+), 75 deletions(-)
diff --git a/drivers/net/lora/sx1301.c b/drivers/net/lora/sx1301.c
index f84a6ce..21b3f9a 100644
--- a/drivers/net/lora/sx1301.c
+++ b/drivers/net/lora/sx1301.c
@@ -589,123 +589,74 @@ static int sx1301_load_firmware(struct sx1301_priv *priv, int mcu, const struct
return 0;
}
-static int sx1301_agc_calibrate(struct spi_device *spi)
+static int sx1301_agc_calibrate(struct sx1301_priv *priv)
{
const struct firmware *fw;
- struct sx1301_priv *priv = spi_get_drvdata(spi);
u8 val;
int ret;
+ unsigned int cal;
ret = request_firmware(&fw, "sx1301_agc_calibration.bin", priv->dev);
if (ret) {
- dev_err(&spi->dev, "agc cal firmware file load failed\n");
+ dev_err(priv->dev, "AGC CAL firmware file load failed\n");
return ret;
}
ret = sx1301_load_firmware(priv, 1, fw);
release_firmware(fw);
if (ret) {
- dev_err(&spi->dev, "agc cal firmware load failed\n");
- return ret;
- }
-
- ret = sx1301_page_read(spi, 0, 105, &val);
- if (ret) {
- dev_err(&spi->dev, "0|105 read failed\n");
+ dev_err(priv->dev, "AGC CAL firmware load failed\n");
return ret;
}
- val &= ~REG_0_105_FORCE_HOST_RADIO_CTRL;
-
- ret = sx1301_page_write(spi, 0, 105, val);
- if (ret) {
- dev_err(&spi->dev, "0|105 write failed\n");
+ ret = sx1301_field_write(priv, F_FORCE_HOST_RADIO_CTRL, 0);
+ if (ret)
return ret;
- }
val = BIT(4); /* with DAC gain=3 */
if (false)
val |= BIT(5); /* SX1255 */
- ret = sx1301_page_write(spi, 0, REG_0_RADIO_SELECT, val);
- if (ret) {
- dev_err(&spi->dev, "radio select write failed\n");
- return ret;
- }
-
- ret = sx1301_page_read(spi, 0, REG_0_MCU, &val);
- if (ret) {
- dev_err(&spi->dev, "MCU read (0) failed\n");
+ ret = regmap_write(priv->regmap, SX1301_CHRS, val);
+ if (ret)
return ret;
- }
-
- val &= ~REG_0_MCU_RST_1;
- ret = sx1301_page_write(spi, 0, REG_0_MCU, val);
- if (ret) {
- dev_err(&spi->dev, "MCU write (0) failed\n");
+ ret = sx1301_field_write(priv, F_MCU_RST_1, 0);
+ if (ret)
return ret;
- }
ret = sx1301_agc_ram_read(priv, 0x20, &val);
if (ret) {
- dev_err(&spi->dev, "AGC RAM data read failed\n");
+ dev_err(priv->dev, "AGC RAM data read failed\n");
return ret;
}
- dev_info(&spi->dev, "AGC calibration firmware version %u\n", (unsigned)val);
+ dev_info(priv->dev, "AGC calibration firmware version %u\n", (unsigned)val);
- if (val != 2) {
- dev_err(&spi->dev, "unexpected firmware version, expecting %u\n", 2);
+ if (val != SX1301_MCU_AGC_CAL_FW_VERSION) {
+ dev_err(priv->dev, "unexpected firmware version, expecting %u\n",
+ SX1301_MCU_AGC_CAL_FW_VERSION);
return -ENXIO;
}
- ret = sx1301_page_switch(spi, 3);
- if (ret) {
- dev_err(&spi->dev, "page switch 3 failed\n");
- return ret;
- }
-
- ret = sx1301_read(priv, REG_EMERGENCY_FORCE, &val);
- if (ret) {
- dev_err(&spi->dev, "emergency force read failed\n");
- return ret;
- }
-
- val &= ~REG_EMERGENCY_FORCE_HOST_CTRL;
-
- ret = sx1301_write(priv, REG_EMERGENCY_FORCE, val);
- if (ret) {
- dev_err(&spi->dev, "emergency force write failed\n");
+ ret = sx1301_field_write(priv, F_EMERGENCY_FORCE_HOST_CTRL, 0);
+ if (ret)
return ret;
- }
- dev_err(&spi->dev, "starting calibration...\n");
+ dev_err(priv->dev, "starting calibration...\n");
msleep(2300);
- ret = sx1301_read(priv, REG_EMERGENCY_FORCE, &val);
- if (ret) {
- dev_err(&spi->dev, "emergency force read (1) failed\n");
- return ret;
- }
-
- val |= REG_EMERGENCY_FORCE_HOST_CTRL;
-
- ret = sx1301_write(priv, REG_EMERGENCY_FORCE, val);
- if (ret) {
- dev_err(&spi->dev, "emergency force write (1) failed\n");
+ ret = sx1301_field_write(priv, F_EMERGENCY_FORCE_HOST_CTRL, 1);
+ if (ret)
return ret;
- }
- ret = sx1301_read(priv, REG_MCU_AGC_STATUS, &val);
- if (ret) {
- dev_err(&spi->dev, "AGC status read failed\n");
+ ret = regmap_read(priv->regmap, SX1301_AGCSTS, &cal);
+ if (ret)
return ret;
- }
- dev_info(&spi->dev, "AGC status: %02x\n", (unsigned)val);
- if ((val & (BIT(7) | BIT(0))) != (BIT(7) | BIT(0))) {
- dev_err(&spi->dev, "AGC calibration failed\n");
+ dev_info(priv->dev, "AGC status: %02x\n", cal);
+ if ((cal & (BIT(7) | BIT(0))) != (BIT(7) | BIT(0))) {
+ dev_err(priv->dev, "AGC calibration failed\n");
return -ENXIO;
}
@@ -985,7 +936,7 @@ static int sx1301_probe(struct spi_device *spi)
/* calibration */
- ret = sx1301_agc_calibrate(spi);
+ ret = sx1301_agc_calibrate(priv);
if (ret)
return ret;
--
2.7.4
^ permalink raw reply related
* [PATCH lora-next 08/10] net: lora: sx1301: convert read and write to priv pointer
From: Ben Whitten @ 2018-08-07 17:32 UTC (permalink / raw)
To: afaerber, starnight, hasnain.virk; +Cc: netdev, Ben Whitten
In-Reply-To: <1533663131-16313-1-git-send-email-ben.whitten@gmail.com>
From: Ben Whitten <ben.whitten@lairdtech.com>
Convert the next layer of read and writes to our private pointer,
once again compatiblity later in the page read and write layer.
The page functions will be removed with this full stack as page switches
are handled by regmap automatically.
Signed-off-by: Ben Whitten <ben.whitten@lairdtech.com>
---
drivers/net/lora/sx1301.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/drivers/net/lora/sx1301.c b/drivers/net/lora/sx1301.c
index ab45b5b..f84a6ce 100644
--- a/drivers/net/lora/sx1301.c
+++ b/drivers/net/lora/sx1301.c
@@ -318,9 +318,8 @@ static int sx1301_read_burst(struct sx1301_priv *priv, u8 reg, u8 *val, size_t l
return spi_write_then_read(priv->spi, &addr, 1, val, len);
}
-static int sx1301_read(struct spi_device *spi, u8 reg, u8 *val)
+static int sx1301_read(struct sx1301_priv *priv, u8 reg, u8 *val)
{
- struct sx1301_priv *priv = spi_get_drvdata(spi);
return sx1301_read_burst(priv, reg, val, 1);
}
@@ -335,9 +334,8 @@ static int sx1301_write_burst(struct sx1301_priv *priv, u8 reg, const u8 *val, s
return spi_sync_transfer(priv->spi, xfr, 2);
}
-static int sx1301_write(struct spi_device *spi, u8 reg, u8 val)
+static int sx1301_write(struct sx1301_priv *priv, u8 reg, u8 val)
{
- struct sx1301_priv *priv = spi_get_drvdata(spi);
return sx1301_write_burst(priv, reg, &val, 1);
}
@@ -350,7 +348,7 @@ static int sx1301_page_switch(struct spi_device *spi, u8 page)
return 0;
dev_dbg(&spi->dev, "switching to page %u\n", (unsigned)page);
- ret = sx1301_write(spi, REG_PAGE_RESET, page & 0x3);
+ ret = sx1301_write(priv, REG_PAGE_RESET, page & 0x3);
if (ret) {
dev_err(&spi->dev, "switching to page %u failed\n", (unsigned)page);
return ret;
@@ -364,23 +362,25 @@ static int sx1301_page_switch(struct spi_device *spi, u8 page)
static int sx1301_page_read(struct spi_device *spi, u8 page, u8 reg, u8 *val)
{
int ret;
+ struct sx1301_priv *priv = spi_get_drvdata(spi);
ret = sx1301_page_switch(spi, page);
if (ret)
return ret;
- return sx1301_read(spi, reg, val);
+ return sx1301_read(priv, reg, val);
}
static int sx1301_page_write(struct spi_device *spi, u8 page, u8 reg, u8 val)
{
int ret;
+ struct sx1301_priv *priv = spi_get_drvdata(spi);
ret = sx1301_page_switch(spi, page);
if (ret)
return ret;
- return sx1301_write(spi, reg, val);
+ return sx1301_write(priv, reg, val);
}
#define REG_RADIO_X_DATA 0
@@ -666,7 +666,7 @@ static int sx1301_agc_calibrate(struct spi_device *spi)
return ret;
}
- ret = sx1301_read(spi, REG_EMERGENCY_FORCE, &val);
+ ret = sx1301_read(priv, REG_EMERGENCY_FORCE, &val);
if (ret) {
dev_err(&spi->dev, "emergency force read failed\n");
return ret;
@@ -674,7 +674,7 @@ static int sx1301_agc_calibrate(struct spi_device *spi)
val &= ~REG_EMERGENCY_FORCE_HOST_CTRL;
- ret = sx1301_write(spi, REG_EMERGENCY_FORCE, val);
+ ret = sx1301_write(priv, REG_EMERGENCY_FORCE, val);
if (ret) {
dev_err(&spi->dev, "emergency force write failed\n");
return ret;
@@ -683,7 +683,7 @@ static int sx1301_agc_calibrate(struct spi_device *spi)
dev_err(&spi->dev, "starting calibration...\n");
msleep(2300);
- ret = sx1301_read(spi, REG_EMERGENCY_FORCE, &val);
+ ret = sx1301_read(priv, REG_EMERGENCY_FORCE, &val);
if (ret) {
dev_err(&spi->dev, "emergency force read (1) failed\n");
return ret;
@@ -691,13 +691,13 @@ static int sx1301_agc_calibrate(struct spi_device *spi)
val |= REG_EMERGENCY_FORCE_HOST_CTRL;
- ret = sx1301_write(spi, REG_EMERGENCY_FORCE, val);
+ ret = sx1301_write(priv, REG_EMERGENCY_FORCE, val);
if (ret) {
dev_err(&spi->dev, "emergency force write (1) failed\n");
return ret;
}
- ret = sx1301_read(spi, REG_MCU_AGC_STATUS, &val);
+ ret = sx1301_read(priv, REG_MCU_AGC_STATUS, &val);
if (ret) {
dev_err(&spi->dev, "AGC status read failed\n");
return ret;
--
2.7.4
^ permalink raw reply related
* [PATCH lora-next 07/10] net: lora: sx1301: convert read and write burst to take priv data
From: Ben Whitten @ 2018-08-07 17:32 UTC (permalink / raw)
To: afaerber, starnight, hasnain.virk; +Cc: netdev, Ben Whitten
In-Reply-To: <1533663131-16313-1-git-send-email-ben.whitten@gmail.com>
From: Ben Whitten <ben.whitten@lairdtech.com>
As part of standardising on passing our priv data around we convert read
and write burst to take the priv data, there is a small compat step
needed in the old _read and _write functions and will be removed in the
next step.
Signed-off-by: Ben Whitten <ben.whitten@lairdtech.com>
---
drivers/net/lora/sx1301.c | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/drivers/net/lora/sx1301.c b/drivers/net/lora/sx1301.c
index 4cec524..ab45b5b 100644
--- a/drivers/net/lora/sx1301.c
+++ b/drivers/net/lora/sx1301.c
@@ -312,18 +312,19 @@ static int sx1301_field_write(struct sx1301_priv *priv,
return regmap_field_write(priv->regmap_fields[field_id], val);
}
-static int sx1301_read_burst(struct spi_device *spi, u8 reg, u8 *val, size_t len)
+static int sx1301_read_burst(struct sx1301_priv *priv, u8 reg, u8 *val, size_t len)
{
u8 addr = reg & 0x7f;
- return spi_write_then_read(spi, &addr, 1, val, len);
+ return spi_write_then_read(priv->spi, &addr, 1, val, len);
}
static int sx1301_read(struct spi_device *spi, u8 reg, u8 *val)
{
- return sx1301_read_burst(spi, reg, val, 1);
+ struct sx1301_priv *priv = spi_get_drvdata(spi);
+ return sx1301_read_burst(priv, reg, val, 1);
}
-static int sx1301_write_burst(struct spi_device *spi, u8 reg, const u8 *val, size_t len)
+static int sx1301_write_burst(struct sx1301_priv *priv, u8 reg, const u8 *val, size_t len)
{
u8 addr = reg | BIT(7);
struct spi_transfer xfr[2] = {
@@ -331,12 +332,13 @@ static int sx1301_write_burst(struct spi_device *spi, u8 reg, const u8 *val, siz
{ .tx_buf = val, .len = len },
};
- return spi_sync_transfer(spi, xfr, 2);
+ return spi_sync_transfer(priv->spi, xfr, 2);
}
static int sx1301_write(struct spi_device *spi, u8 reg, u8 val)
{
- return sx1301_write_burst(spi, reg, &val, 1);
+ struct sx1301_priv *priv = spi_get_drvdata(spi);
+ return sx1301_write_burst(priv, reg, &val, 1);
}
static int sx1301_page_switch(struct spi_device *spi, u8 page)
@@ -551,7 +553,7 @@ static int sx1301_load_firmware(struct sx1301_priv *priv, int mcu, const struct
if (ret)
return ret;
- ret = sx1301_write_burst(priv->spi, REG_MCU_PROM_DATA, fw->data, fw->size);
+ ret = sx1301_write_burst(priv, REG_MCU_PROM_DATA, fw->data, fw->size);
if (ret) {
dev_err(priv->dev, "MCU prom data write failed\n");
return ret;
@@ -567,7 +569,7 @@ static int sx1301_load_firmware(struct sx1301_priv *priv, int mcu, const struct
if (!buf)
return -ENOMEM;
- ret = sx1301_read_burst(priv->spi, REG_MCU_PROM_DATA, buf, fw->size);
+ ret = sx1301_read_burst(priv, REG_MCU_PROM_DATA, buf, fw->size);
if (ret) {
dev_err(priv->dev, "MCU prom data read failed\n");
return ret;
--
2.7.4
^ permalink raw reply related
* [PATCH lora-next 06/10] net: lora: sx1301: simplify firmware loading and convert
From: Ben Whitten @ 2018-08-07 17:32 UTC (permalink / raw)
To: afaerber, starnight, hasnain.virk; +Cc: netdev, Ben Whitten
In-Reply-To: <1533663131-16313-1-git-send-email-ben.whitten@gmail.com>
From: Ben Whitten <ben.whitten@lairdtech.com>
As part of changing our internal pointers to our priv data we add the
spi_device temporarily for downstream burst read/writes.
Just pass firmware pointer to the load and remove redundant size checks,
as both MCUs have the same program size add a define.
Use devm_kzalloc for automatic cleanup on failure to load firmware,
manually cleaup this memory on success.
Convert firmware loading to regmap functions.
Signed-off-by: Ben Whitten <ben.whitten@lairdtech.com>
---
drivers/net/lora/sx1301.c | 109 ++++++++++++++++------------------------------
1 file changed, 37 insertions(+), 72 deletions(-)
diff --git a/drivers/net/lora/sx1301.c b/drivers/net/lora/sx1301.c
index 249551b..4cec524 100644
--- a/drivers/net/lora/sx1301.c
+++ b/drivers/net/lora/sx1301.c
@@ -284,6 +284,7 @@ struct spi_sx1301 {
struct sx1301_priv {
struct device *dev;
+ struct spi_device *spi;
struct lora_priv lora;
struct gpio_desc *rst_gpio;
u8 cur_page;
@@ -515,93 +516,73 @@ static int sx1301_arb_ram_read(struct sx1301_priv *priv, u8 addr, u8 *val)
return 0;
}
-static int sx1301_load_firmware(struct spi_device *spi, int mcu, const u8 *data, size_t len)
+static int sx1301_load_firmware(struct sx1301_priv *priv, int mcu, const struct firmware *fw)
{
u8 *buf;
- u8 val, rst, select_mux;
int ret;
+ unsigned int read;
+ enum sx1301_fields rst, select_mux;
- if (len > 8192)
+ if (fw->size > SX1301_MCU_FW_BYTE)
return -EINVAL;
switch (mcu) {
case 0:
- rst = REG_0_MCU_RST_0;
- select_mux = REG_0_MCU_SELECT_MUX_0;
+ rst = F_MCU_RST_0;
+ select_mux = F_MCU_SELECT_MUX_0;
break;
case 1:
- rst = REG_0_MCU_RST_1;
- select_mux = REG_0_MCU_SELECT_MUX_1;
+ rst = F_MCU_RST_1;
+ select_mux = F_MCU_SELECT_MUX_1;
break;
default:
return -EINVAL;
}
- ret = sx1301_page_read(spi, 0, REG_0_MCU, &val);
- if (ret) {
- dev_err(&spi->dev, "MCU read failed\n");
+ ret = sx1301_field_write(priv, rst, 1);
+ if (ret)
return ret;
- }
-
- val |= rst;
- val &= ~select_mux;
-
- ret = sx1301_page_write(spi, 0, REG_0_MCU, val);
- if (ret) {
- dev_err(&spi->dev, "MCU reset / select mux write failed\n");
+ ret = sx1301_field_write(priv, select_mux, 0);
+ if (ret)
return ret;
- }
- ret = sx1301_write(spi, REG_MCU_PROM_ADDR, 0);
- if (ret) {
- dev_err(&spi->dev, "MCU prom addr write failed\n");
+ /* Load firmware into the data register */
+ ret = regmap_write(priv->regmap, SX1301_MPA, 0);
+ if (ret)
return ret;
- }
- ret = sx1301_write_burst(spi, REG_MCU_PROM_DATA, data, len);
+ ret = sx1301_write_burst(priv->spi, REG_MCU_PROM_DATA, fw->data, fw->size);
if (ret) {
- dev_err(&spi->dev, "MCU prom data write failed\n");
+ dev_err(priv->dev, "MCU prom data write failed\n");
return ret;
}
- ret = sx1301_read(spi, REG_MCU_PROM_DATA, &val);
+ ret = regmap_read(priv->regmap, SX1301_MPD, &read);
if (ret) {
- dev_err(&spi->dev, "MCU prom data dummy read failed\n");
+ dev_err(priv->dev, "MCU prom data dummy read failed\n");
return ret;
}
- buf = kzalloc(len, GFP_KERNEL);
+ buf = devm_kzalloc(priv->dev, fw->size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
- ret = sx1301_read_burst(spi, REG_MCU_PROM_DATA, buf, len);
+ ret = sx1301_read_burst(priv->spi, REG_MCU_PROM_DATA, buf, fw->size);
if (ret) {
- dev_err(&spi->dev, "MCU prom data read failed\n");
- kfree(buf);
+ dev_err(priv->dev, "MCU prom data read failed\n");
return ret;
}
- if (memcmp(data, buf, len)) {
- dev_err(&spi->dev, "MCU prom data read does not match data written\n");
- kfree(buf);
+ if (memcmp(fw->data, buf, fw->size)) {
+ dev_err(priv->dev, "MCU prom data read does not match data written\n");
return -ENXIO;
}
- kfree(buf);
+ devm_kfree(priv->dev, buf);
- ret = sx1301_page_read(spi, 0, REG_0_MCU, &val);
- if (ret) {
- dev_err(&spi->dev, "MCU read (1) failed\n");
- return ret;
- }
-
- val |= select_mux;
-
- ret = sx1301_page_write(spi, 0, REG_0_MCU, val);
- if (ret) {
- dev_err(&spi->dev, "MCU reset / select mux write (1) failed\n");
+ ret = sx1301_field_write(priv, select_mux, 1);
+ if (ret)
return ret;
- }
return 0;
}
@@ -613,18 +594,13 @@ static int sx1301_agc_calibrate(struct spi_device *spi)
u8 val;
int ret;
- ret = request_firmware(&fw, "sx1301_agc_calibration.bin", &spi->dev);
+ ret = request_firmware(&fw, "sx1301_agc_calibration.bin", priv->dev);
if (ret) {
dev_err(&spi->dev, "agc cal firmware file load failed\n");
return ret;
}
- if (fw->size != 8192) {
- dev_err(&spi->dev, "unexpected agc cal firmware size\n");
- return -EINVAL;
- }
-
- ret = sx1301_load_firmware(spi, 1, fw->data, fw->size);
+ ret = sx1301_load_firmware(priv, 1, fw);
release_firmware(fw);
if (ret) {
dev_err(&spi->dev, "agc cal firmware load failed\n");
@@ -741,36 +717,24 @@ static int sx1301_load_all_firmware(struct spi_device *spi)
u8 val;
int ret;
- ret = request_firmware(&fw, "sx1301_arb.bin", &spi->dev);
+ ret = request_firmware(&fw, "sx1301_arb.bin", priv->dev);
if (ret) {
- dev_err(&spi->dev, "arb firmware file load failed\n");
+ dev_err(priv->dev, "arb firmware file load failed\n");
return ret;
}
- if (fw->size != 8192) {
- dev_err(&spi->dev, "unexpected arb firmware size\n");
- release_firmware(fw);
- return -EINVAL;
- }
-
- ret = sx1301_load_firmware(spi, 0, fw->data, fw->size);
+ ret = sx1301_load_firmware(priv, 0, fw);
release_firmware(fw);
if (ret)
return ret;
- ret = request_firmware(&fw, "sx1301_agc.bin", &spi->dev);
+ ret = request_firmware(&fw, "sx1301_agc.bin", priv->dev);
if (ret) {
- dev_err(&spi->dev, "agc firmware file load failed\n");
+ dev_err(priv->dev, "agc firmware file load failed\n");
return ret;
}
- if (fw->size != 8192) {
- dev_err(&spi->dev, "unexpected agc firmware size\n");
- release_firmware(fw);
- return -EINVAL;
- }
-
- ret = sx1301_load_firmware(spi, 1, fw->data, fw->size);
+ ret = sx1301_load_firmware(priv, 1, fw);
release_firmware(fw);
if (ret)
return ret;
@@ -886,6 +850,7 @@ static int sx1301_probe(struct spi_device *spi)
spi_set_drvdata(spi, netdev);
SET_NETDEV_DEV(netdev, &spi->dev);
priv->dev = &spi->dev;
+ priv->spi = spi;
priv->regmap = devm_regmap_init_spi(spi, &sx1301_regmap_config);
if (IS_ERR(priv->regmap)) {
--
2.7.4
^ permalink raw reply related
* [PATCH lora-next 05/10] net: lora: sx1301: add device to private data and convert ram reads
From: Ben Whitten @ 2018-08-07 17:32 UTC (permalink / raw)
To: afaerber, starnight, hasnain.virk; +Cc: netdev, Ben Whitten
In-Reply-To: <1533663131-16313-1-git-send-email-ben.whitten@gmail.com>
From: Ben Whitten <ben.whitten@lairdtech.com>
It's easier to simply add a pointer to our device to our private data,
this way we can avoid passing around our spi_device and instead just
pass private data which is more flexible.
As its a two line change its coupled with converting AGC and ARB ram reads
to regmap functions.
Signed-off-by: Ben Whitten <ben.whitten@lairdtech.com>
---
drivers/net/lora/sx1301.c | 34 +++++++++++++++++++++-------------
1 file changed, 21 insertions(+), 13 deletions(-)
diff --git a/drivers/net/lora/sx1301.c b/drivers/net/lora/sx1301.c
index 118e8d8..249551b 100644
--- a/drivers/net/lora/sx1301.c
+++ b/drivers/net/lora/sx1301.c
@@ -283,6 +283,7 @@ struct spi_sx1301 {
};
struct sx1301_priv {
+ struct device *dev;
struct lora_priv lora;
struct gpio_desc *rst_gpio;
u8 cur_page;
@@ -472,40 +473,44 @@ static int sx1301_radio_spi_transfer_one(struct spi_controller *ctrl,
return 0;
}
-static int sx1301_agc_ram_read(struct spi_device *spi, u8 addr, u8 *val)
+static int sx1301_agc_ram_read(struct sx1301_priv *priv, u8 addr, u8 *val)
{
int ret;
+ unsigned int read;
- ret = sx1301_page_write(spi, 2, REG_2_DBG_AGC_MCU_RAM_ADDR, addr);
+ ret = regmap_write(priv->regmap, SX1301_DBG_AGC_MCU_RAM_ADDR, addr);
if (ret) {
- dev_err(&spi->dev, "AGC RAM addr write failed\n");
+ dev_err(priv->dev, "AGC RAM addr write failed\n");
return ret;
}
- ret = sx1301_page_read(spi, 2, REG_2_DBG_AGC_MCU_RAM_DATA, val);
+ ret = regmap_read(priv->regmap, SX1301_DBG_AGC_MCU_RAM_DATA, &read);
if (ret) {
- dev_err(&spi->dev, "AGC RAM data read failed\n");
+ dev_err(priv->dev, "AGC RAM data read failed\n");
return ret;
}
+ *val = read;
return 0;
}
-static int sx1301_arb_ram_read(struct spi_device *spi, u8 addr, u8 *val)
+static int sx1301_arb_ram_read(struct sx1301_priv *priv, u8 addr, u8 *val)
{
int ret;
+ unsigned int read;
- ret = sx1301_page_write(spi, 2, REG_2_DBG_ARB_MCU_RAM_ADDR, addr);
+ ret = regmap_write(priv->regmap, SX1301_DBG_ARB_MCU_RAM_ADDR, addr);
if (ret) {
- dev_err(&spi->dev, "ARB RAM addr write failed\n");
+ dev_err(priv->dev, "ARB RAM addr write failed\n");
return ret;
}
- ret = sx1301_page_read(spi, 2, REG_2_DBG_ARB_MCU_RAM_DATA, val);
+ ret = regmap_read(priv->regmap, SX1301_DBG_ARB_MCU_RAM_DATA, &read);
if (ret) {
- dev_err(&spi->dev, "ARB RAM data read failed\n");
+ dev_err(priv->dev, "ARB RAM data read failed\n");
return ret;
}
+ *val = read;
return 0;
}
@@ -604,6 +609,7 @@ static int sx1301_load_firmware(struct spi_device *spi, int mcu, const u8 *data,
static int sx1301_agc_calibrate(struct spi_device *spi)
{
const struct firmware *fw;
+ struct sx1301_priv *priv = spi_get_drvdata(spi);
u8 val;
int ret;
@@ -663,7 +669,7 @@ static int sx1301_agc_calibrate(struct spi_device *spi)
return ret;
}
- ret = sx1301_agc_ram_read(spi, 0x20, &val);
+ ret = sx1301_agc_ram_read(priv, 0x20, &val);
if (ret) {
dev_err(&spi->dev, "AGC RAM data read failed\n");
return ret;
@@ -730,6 +736,7 @@ static int sx1301_agc_calibrate(struct spi_device *spi)
static int sx1301_load_all_firmware(struct spi_device *spi)
{
+ struct sx1301_priv *priv = spi_get_drvdata(spi);
const struct firmware *fw;
u8 val;
int ret;
@@ -802,7 +809,7 @@ static int sx1301_load_all_firmware(struct spi_device *spi)
return ret;
}
- ret = sx1301_agc_ram_read(spi, 0x20, &val);
+ ret = sx1301_agc_ram_read(priv, 0x20, &val);
if (ret) {
dev_err(&spi->dev, "AGC RAM data read failed\n");
return ret;
@@ -815,7 +822,7 @@ static int sx1301_load_all_firmware(struct spi_device *spi)
return -ENXIO;
}
- ret = sx1301_arb_ram_read(spi, 0x20, &val);
+ ret = sx1301_arb_ram_read(priv, 0x20, &val);
if (ret) {
dev_err(&spi->dev, "ARB RAM data read failed\n");
return ret;
@@ -878,6 +885,7 @@ static int sx1301_probe(struct spi_device *spi)
spi_set_drvdata(spi, netdev);
SET_NETDEV_DEV(netdev, &spi->dev);
+ priv->dev = &spi->dev;
priv->regmap = devm_regmap_init_spi(spi, &sx1301_regmap_config);
if (IS_ERR(priv->regmap)) {
--
2.7.4
^ permalink raw reply related
* [PATCH lora-next 04/10] net: lora: sx1301: convert probe function to regmap access
From: Ben Whitten @ 2018-08-07 17:32 UTC (permalink / raw)
To: afaerber, starnight, hasnain.virk; +Cc: netdev, Ben Whitten
In-Reply-To: <1533663131-16313-1-git-send-email-ben.whitten@gmail.com>
From: Ben Whitten <ben.whitten@lairdtech.com>
Keeping change sets small we convert the probe function over to regmap
and regmap_field access for initialisation.
Signed-off-by: Ben Whitten <ben.whitten@lairdtech.com>
---
drivers/net/lora/sx1301.c | 155 +++++++++-------------------------------------
drivers/net/lora/sx1301.h | 7 +++
2 files changed, 35 insertions(+), 127 deletions(-)
diff --git a/drivers/net/lora/sx1301.c b/drivers/net/lora/sx1301.c
index 54bfc31..118e8d8 100644
--- a/drivers/net/lora/sx1301.c
+++ b/drivers/net/lora/sx1301.c
@@ -379,11 +379,6 @@ static int sx1301_page_write(struct spi_device *spi, u8 page, u8 reg, u8 val)
return sx1301_write(spi, reg, val);
}
-static int sx1301_soft_reset(struct spi_device *spi)
-{
- return sx1301_write(spi, REG_PAGE_RESET, REG_PAGE_RESET_SOFT_RESET);
-}
-
#define REG_RADIO_X_DATA 0
#define REG_RADIO_X_DATA_READBACK 1
#define REG_RADIO_X_ADDR 2
@@ -854,7 +849,6 @@ static int sx1301_probe(struct spi_device *spi)
int ret;
int i;
unsigned int ver;
- u8 val;
rst = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(rst))
@@ -911,96 +905,46 @@ static int sx1301_probe(struct spi_device *spi)
return ret;
}
- if (ver != 103) {
+ if (ver != SX1301_CHIP_VERSION) {
dev_err(&spi->dev, "unexpected version: %u\n", ver);
return -ENXIO;
}
- ret = sx1301_write(spi, REG_PAGE_RESET, 0);
+ ret = regmap_write(priv->regmap, SX1301_PAGE, 0);
if (ret) {
dev_err(&spi->dev, "page/reset write failed\n");
return ret;
}
- ret = sx1301_soft_reset(spi);
+ ret = sx1301_field_write(priv, F_SOFT_RESET, 1);
if (ret) {
dev_err(&spi->dev, "soft reset failed\n");
return ret;
}
- ret = sx1301_read(spi, 16, &val);
- if (ret) {
- dev_err(&spi->dev, "16 read failed\n");
- return ret;
- }
-
- val &= ~REG_16_GLOBAL_EN;
-
- ret = sx1301_write(spi, 16, val);
- if (ret) {
- dev_err(&spi->dev, "16 write failed\n");
- return ret;
- }
-
- ret = sx1301_read(spi, 17, &val);
- if (ret) {
- dev_err(&spi->dev, "17 read failed\n");
- return ret;
- }
-
- val &= ~REG_17_CLK32M_EN;
-
- ret = sx1301_write(spi, 17, val);
- if (ret) {
- dev_err(&spi->dev, "17 write failed\n");
- return ret;
- }
-
- ret = sx1301_page_read(spi, 2, 43, &val);
- if (ret) {
- dev_err(&spi->dev, "2|43 read failed\n");
+ /* gate clocks */
+ ret = sx1301_field_write(priv, F_GLOBAL_EN, 0);
+ if (ret)
return ret;
- }
-
- val |= REG_2_43_RADIO_B_EN | REG_2_43_RADIO_A_EN;
-
- ret = sx1301_page_write(spi, 2, 43, val);
- if (ret) {
- dev_err(&spi->dev, "2|43 write failed\n");
+ ret = sx1301_field_write(priv, F_CLK32M_EN, 0);
+ if (ret)
return ret;
- }
- msleep(500);
-
- ret = sx1301_page_read(spi, 2, 43, &val);
- if (ret) {
- dev_err(&spi->dev, "2|43 read failed\n");
+ /* switch on and reset the radios (also starts the 32 MHz XTAL) */
+ ret = sx1301_field_write(priv, F_RADIO_A_EN, 1);
+ if (ret)
return ret;
- }
-
- val |= REG_2_43_RADIO_RST;
-
- ret = sx1301_page_write(spi, 2, 43, val);
- if (ret) {
- dev_err(&spi->dev, "2|43 write failed\n");
+ ret = sx1301_field_write(priv, F_RADIO_B_EN, 1);
+ if (ret)
return ret;
- }
-
- msleep(5);
-
- ret = sx1301_page_read(spi, 2, 43, &val);
- if (ret) {
- dev_err(&spi->dev, "2|43 read failed\n");
+ mdelay(500);
+ ret = sx1301_field_write(priv, F_RADIO_RST, 1);
+ if (ret)
return ret;
- }
-
- val &= ~REG_2_43_RADIO_RST;
-
- ret = sx1301_page_write(spi, 2, 43, val);
- if (ret) {
- dev_err(&spi->dev, "2|43 write failed\n");
+ mdelay(5);
+ ret = sx1301_field_write(priv, F_RADIO_RST, 0);
+ if (ret)
return ret;
- }
/* radio A */
@@ -1047,65 +991,22 @@ static int sx1301_probe(struct spi_device *spi)
}
/* GPIO */
-
- ret = sx1301_read(spi, REG_GPIO_MODE, &val);
- if (ret) {
- dev_err(&spi->dev, "GPIO mode read failed\n");
- return ret;
- }
-
- val |= GENMASK(4, 0);
-
- ret = sx1301_write(spi, REG_GPIO_MODE, val);
- if (ret) {
- dev_err(&spi->dev, "GPIO mode write failed\n");
- return ret;
- }
-
- ret = sx1301_read(spi, REG_GPIO_SELECT_OUTPUT, &val);
- if (ret) {
- dev_err(&spi->dev, "GPIO select output read failed\n");
+ ret = regmap_write(priv->regmap, SX1301_GPMODE, 0x1F);
+ if (ret)
return ret;
- }
-
- val &= ~GENMASK(3, 0);
- val |= 2;
-
- ret = sx1301_write(spi, REG_GPIO_SELECT_OUTPUT, val);
- if (ret) {
- dev_err(&spi->dev, "GPIO select output write failed\n");
+ ret = regmap_write(priv->regmap, SX1301_GPSO, 0x2);
+ if (ret)
return ret;
- }
/* TODO LBT */
- ret = sx1301_read(spi, 16, &val);
- if (ret) {
- dev_err(&spi->dev, "16 read (1) failed\n");
- return ret;
- }
-
- val |= REG_16_GLOBAL_EN;
-
- ret = sx1301_write(spi, 16, val);
- if (ret) {
- dev_err(&spi->dev, "16 write (1) failed\n");
- return ret;
- }
-
- ret = sx1301_read(spi, 17, &val);
- if (ret) {
- dev_err(&spi->dev, "17 read (1) failed\n");
+ /* start clocks */
+ ret = sx1301_field_write(priv, F_GLOBAL_EN, 1);
+ if (ret)
return ret;
- }
-
- val |= REG_17_CLK32M_EN;
-
- ret = sx1301_write(spi, 17, val);
- if (ret) {
- dev_err(&spi->dev, "17 write (1) failed\n");
+ ret = sx1301_field_write(priv, F_CLK32M_EN, 1);
+ if (ret)
return ret;
- }
/* calibration */
diff --git a/drivers/net/lora/sx1301.h b/drivers/net/lora/sx1301.h
index abfb7b5..0e84703 100644
--- a/drivers/net/lora/sx1301.h
+++ b/drivers/net/lora/sx1301.h
@@ -8,6 +8,13 @@
#ifndef _SX1301_
#define _SX1301_
+#define SX1301_CHIP_VERSION 103
+
+#define SX1301_MCU_FW_BYTE 8192
+#define SX1301_MCU_ARB_FW_VERSION 1
+#define SX1301_MCU_AGC_FW_VERSION 4
+#define SX1301_MCU_AGC_CAL_FW_VERSION 2
+
/* Page independent */
#define SX1301_PAGE 0x00
#define SX1301_VER 0x01
--
2.7.4
^ permalink raw reply related
* [PATCH lora-next 03/10] net: lora: sx1301: convert to devm registration of netdev
From: Ben Whitten @ 2018-08-07 17:32 UTC (permalink / raw)
To: afaerber, starnight, hasnain.virk; +Cc: netdev, Ben Whitten
In-Reply-To: <1533663131-16313-1-git-send-email-ben.whitten@gmail.com>
From: Ben Whitten <ben.whitten@lairdtech.com>
We allow the devres framework handle the clean removal of resources on
teardown of the device, in this case the SPI device, saving lengthy
unwind code and improving clarity.
Signed-off-by: Ben Whitten <ben.whitten@lairdtech.com>
---
drivers/net/lora/sx1301.c | 87 +++++++++++++++++------------------------------
1 file changed, 31 insertions(+), 56 deletions(-)
diff --git a/drivers/net/lora/sx1301.c b/drivers/net/lora/sx1301.c
index 49958f0..54bfc31 100644
--- a/drivers/net/lora/sx1301.c
+++ b/drivers/net/lora/sx1301.c
@@ -869,9 +869,13 @@ static int sx1301_probe(struct spi_device *spi)
spi_setup(spi);
netdev = alloc_loradev(sizeof(*priv));
- if (!netdev) {
- ret = -ENOMEM;
- goto err_alloc_loradev;
+ if (!netdev)
+ return -ENOMEM;
+
+ ret = devm_lora_register_netdev(&spi->dev, netdev);
+ if (ret) {
+ free_loradev(netdev);
+ return ret;
}
priv = netdev_priv(netdev);
@@ -885,7 +889,7 @@ static int sx1301_probe(struct spi_device *spi)
if (IS_ERR(priv->regmap)) {
ret = PTR_ERR(priv->regmap);
dev_err(&spi->dev, "Regmap allocation failed: %d\n", ret);
- return err_regmap;
+ return ret;
}
for (i = 0; i < ARRAY_SIZE(sx1301_reg_fields); i++) {
@@ -897,38 +901,37 @@ static int sx1301_probe(struct spi_device *spi)
if (IS_ERR(priv->regmap_fields[i])) {
ret = PTR_ERR(priv->regmap_fields[i]);
dev_err(&spi->dev, "Cannot allocate regmap field: %d\n", ret);
- goto err_regmap;
+ return ret;
}
}
ret = regmap_read(priv->regmap, SX1301_VER, &ver);
if (ret) {
dev_err(&spi->dev, "version read failed\n");
- goto err_version;
+ return ret;
}
if (ver != 103) {
dev_err(&spi->dev, "unexpected version: %u\n", ver);
- ret = -ENXIO;
- goto err_version;
+ return -ENXIO;
}
ret = sx1301_write(spi, REG_PAGE_RESET, 0);
if (ret) {
dev_err(&spi->dev, "page/reset write failed\n");
- goto err_init_page;
+ return ret;
}
ret = sx1301_soft_reset(spi);
if (ret) {
dev_err(&spi->dev, "soft reset failed\n");
- goto err_soft_reset;
+ return ret;
}
ret = sx1301_read(spi, 16, &val);
if (ret) {
dev_err(&spi->dev, "16 read failed\n");
- goto err_read_global_en_0;
+ return ret;
}
val &= ~REG_16_GLOBAL_EN;
@@ -936,13 +939,13 @@ static int sx1301_probe(struct spi_device *spi)
ret = sx1301_write(spi, 16, val);
if (ret) {
dev_err(&spi->dev, "16 write failed\n");
- goto err_write_global_en_0;
+ return ret;
}
ret = sx1301_read(spi, 17, &val);
if (ret) {
dev_err(&spi->dev, "17 read failed\n");
- goto err_read_clk32m_0;
+ return ret;
}
val &= ~REG_17_CLK32M_EN;
@@ -950,7 +953,7 @@ static int sx1301_probe(struct spi_device *spi)
ret = sx1301_write(spi, 17, val);
if (ret) {
dev_err(&spi->dev, "17 write failed\n");
- goto err_write_clk32m_0;
+ return ret;
}
ret = sx1301_page_read(spi, 2, 43, &val);
@@ -1003,8 +1006,7 @@ static int sx1301_probe(struct spi_device *spi)
priv->radio_a_ctrl = spi_alloc_master(&spi->dev, sizeof(*radio));
if (!priv->radio_a_ctrl) {
- ret = -ENOMEM;
- goto err_radio_a_alloc;
+ return -ENOMEM;
}
sx1301_radio_setup(priv->radio_a_ctrl);
@@ -1019,15 +1021,14 @@ static int sx1301_probe(struct spi_device *spi)
if (ret) {
dev_err(&spi->dev, "radio A SPI register failed\n");
spi_controller_put(priv->radio_a_ctrl);
- goto err_radio_a_register;
+ return ret;
}
/* radio B */
priv->radio_b_ctrl = spi_alloc_master(&spi->dev, sizeof(*radio));
if (!priv->radio_b_ctrl) {
- ret = -ENOMEM;
- goto err_radio_b_alloc;
+ return -ENOMEM;
}
sx1301_radio_setup(priv->radio_b_ctrl);
@@ -1042,7 +1043,7 @@ static int sx1301_probe(struct spi_device *spi)
if (ret) {
dev_err(&spi->dev, "radio B SPI register failed\n");
spi_controller_put(priv->radio_b_ctrl);
- goto err_radio_b_register;
+ return ret;
}
/* GPIO */
@@ -1050,7 +1051,7 @@ static int sx1301_probe(struct spi_device *spi)
ret = sx1301_read(spi, REG_GPIO_MODE, &val);
if (ret) {
dev_err(&spi->dev, "GPIO mode read failed\n");
- goto err_read_gpio_mode;
+ return ret;
}
val |= GENMASK(4, 0);
@@ -1058,13 +1059,13 @@ static int sx1301_probe(struct spi_device *spi)
ret = sx1301_write(spi, REG_GPIO_MODE, val);
if (ret) {
dev_err(&spi->dev, "GPIO mode write failed\n");
- goto err_write_gpio_mode;
+ return ret;
}
ret = sx1301_read(spi, REG_GPIO_SELECT_OUTPUT, &val);
if (ret) {
dev_err(&spi->dev, "GPIO select output read failed\n");
- goto err_read_gpio_select_output;
+ return ret;
}
val &= ~GENMASK(3, 0);
@@ -1073,7 +1074,7 @@ static int sx1301_probe(struct spi_device *spi)
ret = sx1301_write(spi, REG_GPIO_SELECT_OUTPUT, val);
if (ret) {
dev_err(&spi->dev, "GPIO select output write failed\n");
- goto err_write_gpio_select_output;
+ return ret;
}
/* TODO LBT */
@@ -1081,7 +1082,7 @@ static int sx1301_probe(struct spi_device *spi)
ret = sx1301_read(spi, 16, &val);
if (ret) {
dev_err(&spi->dev, "16 read (1) failed\n");
- goto err_read_global_en_1;
+ return ret;
}
val |= REG_16_GLOBAL_EN;
@@ -1089,13 +1090,13 @@ static int sx1301_probe(struct spi_device *spi)
ret = sx1301_write(spi, 16, val);
if (ret) {
dev_err(&spi->dev, "16 write (1) failed\n");
- goto err_write_global_en_1;
+ return ret;
}
ret = sx1301_read(spi, 17, &val);
if (ret) {
dev_err(&spi->dev, "17 read (1) failed\n");
- goto err_read_clk32m_1;
+ return ret;
}
val |= REG_17_CLK32M_EN;
@@ -1103,50 +1104,24 @@ static int sx1301_probe(struct spi_device *spi)
ret = sx1301_write(spi, 17, val);
if (ret) {
dev_err(&spi->dev, "17 write (1) failed\n");
- goto err_write_clk32m_1;
+ return ret;
}
/* calibration */
ret = sx1301_agc_calibrate(spi);
if (ret)
- goto err_agc_calibrate;
+ return ret;
/* TODO */
ret = sx1301_load_all_firmware(spi);
if (ret)
- goto err_load_firmware;
+ return ret;
dev_info(&spi->dev, "SX1301 module probed\n");
return 0;
-
-err_load_firmware:
-err_agc_calibrate:
-err_write_clk32m_1:
-err_read_clk32m_1:
-err_write_global_en_1:
-err_read_global_en_1:
-err_write_gpio_select_output:
-err_read_gpio_select_output:
-err_write_gpio_mode:
-err_read_gpio_mode:
-err_radio_b_register:
-err_radio_b_alloc:
-err_radio_a_register:
-err_radio_a_alloc:
-err_write_clk32m_0:
-err_read_clk32m_0:
-err_write_global_en_0:
-err_read_global_en_0:
-err_soft_reset:
-err_init_page:
-err_version:
-err_regmap:
- free_loradev(netdev);
-err_alloc_loradev:
- return ret;
}
static int sx1301_remove(struct spi_device *spi)
--
2.7.4
^ permalink raw reply related
* [PATCH lora-next 02/10] net: lora: add methods for devm registration
From: Ben Whitten @ 2018-08-07 17:32 UTC (permalink / raw)
To: afaerber, starnight, hasnain.virk; +Cc: netdev, Ben Whitten
In-Reply-To: <1533663131-16313-1-git-send-email-ben.whitten@gmail.com>
From: Ben Whitten <ben.whitten@lairdtech.com>
Follow the devm model so that we can avoid lengthy unwind code.
Signed-off-by: Ben Whitten <ben.whitten@lairdtech.com>
---
drivers/net/lora/dev.c | 20 ++++++++++++++++++++
include/linux/lora/dev.h | 1 +
2 files changed, 21 insertions(+)
diff --git a/drivers/net/lora/dev.c b/drivers/net/lora/dev.c
index 8c01106..69a8b52 100644
--- a/drivers/net/lora/dev.c
+++ b/drivers/net/lora/dev.c
@@ -84,6 +84,26 @@ void free_loradev(struct net_device *dev)
}
EXPORT_SYMBOL_GPL(free_loradev);
+static void devm_lora_unregister(struct device *dev, void *res)
+{
+ free_loradev(*(struct net_device **)res);
+}
+
+int devm_lora_register_netdev(struct device *dev, struct net_device *net)
+{
+ struct net_device **ptr;
+
+ ptr = devres_alloc(devm_lora_unregister, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ *ptr = net;
+ devres_add(dev, ptr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_lora_register_netdev);
+
static struct rtnl_link_ops lora_link_ops __read_mostly = {
.kind = "lora",
.setup = lora_setup,
diff --git a/include/linux/lora/dev.h b/include/linux/lora/dev.h
index 153f9b2..3f871c6 100644
--- a/include/linux/lora/dev.h
+++ b/include/linux/lora/dev.h
@@ -32,6 +32,7 @@ static inline int lora_strtoeui(const char *str, lora_eui *val)
struct net_device *alloc_loradev(int sizeof_priv);
void free_loradev(struct net_device *dev);
+int devm_lora_register_netdev(struct device *dev, struct net_device *net);
int register_loradev(struct net_device *dev);
void unregister_loradev(struct net_device *dev);
int open_loradev(struct net_device *dev);
--
2.7.4
^ permalink raw reply related
* [RFC] spi: add spi multiplexing functions for dt
From: Ben Whitten @ 2018-08-07 17:32 UTC (permalink / raw)
To: afaerber, starnight, hasnain.virk; +Cc: netdev, Ben Whitten
In-Reply-To: <1533663131-16313-1-git-send-email-ben.whitten@gmail.com>
Like I2C busses SPI devices can also sit behind multiplexers.
This patch adds is based off the I2C implementation and allows
description in the devicetree.
Signed-off-by: Ben Whitten <ben.whitten@gmail.com>
---
drivers/spi/Kconfig | 10 +++
drivers/spi/Makefile | 3 +
drivers/spi/spi-mux.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/spi-mux.h | 55 +++++++++++++++
4 files changed, 249 insertions(+)
create mode 100644 drivers/spi/spi-mux.c
create mode 100644 include/linux/spi-mux.h
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index a75f2a2..58eba70 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -51,6 +51,16 @@ config SPI_MASTER
if SPI_MASTER
+config SPI_MUX
+ tristate "SPI bus multiplexing support"
+ help
+ Say Y here if you want the SPI core to support the ability to
+ handle multiplexed SPI bus topologies, by presenting each
+ multiplexed segment as an SPI controller.
+
+ This support is also available as a module. If so, the module
+ will be called spi-mux.
+
comment "SPI Master Controller Drivers"
config SPI_ALTERA
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 8e0cda7..ef525fe 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -109,6 +109,9 @@ obj-$(CONFIG_SPI_XLP) += spi-xlp.o
obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o
obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o
+# SPI muxs
+obj-$(CONFIG_SPI_MUX) += spi-mux.o
+
# SPI slave protocol handlers
obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o
obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o
diff --git a/drivers/spi/spi-mux.c b/drivers/spi/spi-mux.c
new file mode 100644
index 0000000..a2008c1
--- /dev/null
+++ b/drivers/spi/spi-mux.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for an SPI multiplexer
+ *
+ * Copyright (c) 2018 Ben Whitten
+ */
+
+#include <linux/spi/spi.h>
+#include <linux/spi-mux.h>
+#include <linux/of.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+struct spi_mux_priv {
+ struct spi_controller *controller;
+ struct spi_mux_core *muxc;
+ u32 chan_id;
+};
+
+struct spi_mux_core *spi_mux_alloc(struct spi_controller *parent,
+ struct device *dev,
+ int max_controllers,
+ int sizeof_priv,
+ int (*select)(struct spi_mux_core *, u32),
+ int (*deselect)(struct spi_mux_core *, u32),
+ int (*transfer_one_message)
+ (struct spi_controller *controller,
+ struct spi_message *msg))
+{
+ struct spi_mux_core *muxc;
+
+ muxc = devm_kzalloc(dev, sizeof(*muxc)
+ + max_controllers * sizeof(muxc->controller[0])
+ + sizeof_priv, GFP_KERNEL);
+ if (!muxc)
+ return NULL;
+ if (sizeof_priv)
+ muxc->priv = &muxc->controller[max_controllers];
+
+ muxc->parent = parent;
+ muxc->dev = dev;
+
+ muxc->select = select;
+ muxc->deselect = deselect;
+ muxc->transfer_one_message = transfer_one_message;
+ muxc->max_controllers = max_controllers;
+
+ return muxc;
+}
+EXPORT_SYMBOL_GPL(spi_mux_alloc);
+
+u32 spi_mux_get_chan_id(struct spi_controller *controller)
+{
+ struct spi_mux_priv *priv = spi_controller_get_devdata(controller);
+
+ return priv->chan_id;
+}
+EXPORT_SYMBOL_GPL(spi_mux_get_chan_id);
+
+static int spi_mux_transfer_one_message(struct spi_controller *controller,
+ struct spi_message *msg)
+{
+ struct spi_mux_priv *priv = spi_controller_get_devdata(controller);
+ struct spi_mux_core *muxc = priv->muxc;
+ struct spi_device *spi = to_spi_device(muxc->dev);
+ int ret;
+
+ ret = muxc->select(muxc, priv->chan_id);
+ if (ret < 0)
+ return ret;
+
+ /* If we have a custom transfer, use it */
+ if (muxc->transfer_one_message)
+ ret = muxc->transfer_one_message(controller, msg);
+ else
+ ret = spi_sync(spi, msg);
+
+ if (muxc->deselect)
+ muxc->deselect(muxc, priv->chan_id);
+
+ return ret;
+}
+
+static int spi_mux_add_controller(struct spi_mux_core *muxc, u32 chan_id)
+{
+ struct spi_controller *controller;
+ struct spi_mux_priv *priv;
+ int ret;
+
+ if (muxc->num_controllers >= muxc->max_controllers) {
+ dev_err(muxc->dev, "No room for more spi-mux controllers");
+ return -EINVAL;
+ }
+
+ controller = spi_alloc_master(muxc->dev, sizeof(*priv));
+ if (!controller)
+ return -ENOMEM;
+ priv = spi_controller_get_devdata(controller);
+
+ /* Setup private controller data */
+ priv->muxc = muxc;
+ priv->controller = controller;
+ priv->chan_id = chan_id;
+
+ priv->controller->transfer_one_message = spi_mux_transfer_one_message;
+
+ /* Look for the child of this controller */
+ if (muxc->dev->of_node) {
+ struct device_node *dev_node = muxc->dev->of_node;
+ struct device_node *mux_node, *child = NULL;
+ u32 reg;
+
+ mux_node = of_get_child_by_name(dev_node, "spi-mux");
+ if (!mux_node)
+ mux_node = of_node_get(dev_node);
+
+ for_each_child_of_node(mux_node, child) {
+ ret = of_property_read_u32(child, "reg", ®);
+ if (ret)
+ continue;
+ if (chan_id == reg)
+ break;
+ }
+
+ priv->controller->dev.of_node = child;
+ of_node_put(mux_node);
+ }
+
+ ret = devm_spi_register_controller(muxc->dev, priv->controller);
+ if (ret) {
+ spi_controller_put(priv->controller);
+ dev_err(muxc->dev, "Problem registering spi controller: %d\n",
+ ret);
+ return ret;
+ }
+
+ muxc->controller[muxc->num_controllers++] = priv->controller;
+
+ return ret;
+}
+
+static void spi_mux_del_controllers(struct spi_mux_core *muxc)
+{
+ struct spi_controller *controller =
+ muxc->controller[--muxc->num_controllers];
+ struct device_node *np = controller->dev.of_node;
+
+ muxc->controller[muxc->num_controllers] = NULL;
+ of_node_put(np);
+}
+
+static void devm_spi_mux_del_controllers(struct device *dev, void *res)
+{
+ spi_mux_del_controllers(*(struct spi_mux_core **)res);
+}
+
+int devm_spi_mux_add_controller(struct spi_mux_core *muxc, u32 chan_id)
+{
+ struct spi_mux_core **ptr;
+ int ret;
+
+ ptr = devres_alloc(devm_spi_mux_del_controllers, sizeof(*ptr),
+ GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ ret = spi_mux_add_controller(muxc, chan_id);
+ if (!ret) {
+ *ptr = muxc;
+ devres_add(muxc->dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(devm_spi_mux_add_controller);
+
+MODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>");
+MODULE_DESCRIPTION("SPI driver for multiplexed SPI busses");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/spi-mux.h b/include/linux/spi-mux.h
new file mode 100644
index 0000000..5978f86
--- /dev/null
+++ b/include/linux/spi-mux.h
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for an SPI multiplexer
+ *
+ * Copyright (c) 2018 Ben Whitten
+ */
+
+#ifndef _LINUX_SPI_MUX_H_
+#define _LINUX_SPI_MUX_H_
+
+#ifdef __KERNEL__
+
+struct spi_mux_core {
+ struct spi_controller *parent;
+ struct device *dev;
+
+ void *priv;
+
+ int (*select)(struct spi_mux_core *, u32 chan_id);
+ int (*deselect)(struct spi_mux_core *, u32 chan_id);
+ int (*transfer_one_message)(struct spi_controller *controller,
+ struct spi_message *msg);
+
+ int num_controllers;
+ int max_controllers;
+ struct spi_controller *controller[0];
+};
+
+struct spi_mux_core *spi_mux_alloc(struct spi_controller *parent,
+ struct device *dev,
+ int max_controllers,
+ int sizeof_priv,
+ int (*select)(struct spi_mux_core *, u32),
+ int (*deselect)(struct spi_mux_core *, u32),
+ int (*transfer_one_message)
+ (struct spi_controller *controller,
+ struct spi_message *msg));
+
+static inline void *spi_mux_priv(struct spi_mux_core *muxc)
+{
+ return muxc->priv;
+}
+
+u32 spi_mux_get_chan_id(struct spi_controller *controller);
+
+/*
+ * Called to create an spi bus on a multiplexed bus segment.
+ * The chan_id parameter is passed to the select and deselect
+ * callback functions to perform hardware-specific mux control.
+ */
+int devm_spi_mux_add_controller(struct spi_mux_core *muxc, u32 chan_id);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SPI_MUX_H_ */
--
2.7.4
^ permalink raw reply related
* [PATCH lora-next 01/10] net: lora: sx1301: add register, bit-fields, and helpers for regmap
From: Ben Whitten @ 2018-08-07 17:32 UTC (permalink / raw)
To: afaerber, starnight, hasnain.virk; +Cc: netdev, Ben Whitten
In-Reply-To: <1533663131-16313-1-git-send-email-ben.whitten@gmail.com>
From: Ben Whitten <ben.whitten@lairdtech.com>
The register and bit-field definitions are taken from the SX1301
datasheet version 2.01 dated June 2014 with the revision information
'First released version'.
The reset state and RW capability of each field is not reflected in this
patch however from the datasheet:
"Bits and registers that are not documented are reserved. They may
include calibration values. It is important not to modify these bits and
registers. If specific bits must be changed in a register with reserved
bits, the register must be read first, specific bits modified while
masking reserved bits and then the register can be written."
Then goes on to state:
"Reserved bits should be written with their reset state, they may be
read different states."
Caching is currently disabled.
The version is read back using regmap_read to verify regmap operation,
in doing so needs to be moved after priv and regmap allocation.
Signed-off-by: Ben Whitten <ben.whitten@lairdtech.com>
---
drivers/net/lora/Kconfig | 1 +
drivers/net/lora/sx1301.c | 282 +++++++++++++++++++++++++++++++++++++++++++---
drivers/net/lora/sx1301.h | 169 +++++++++++++++++++++++++++
3 files changed, 439 insertions(+), 13 deletions(-)
create mode 100644 drivers/net/lora/sx1301.h
diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig
index bb57a01..79d23f2 100644
--- a/drivers/net/lora/Kconfig
+++ b/drivers/net/lora/Kconfig
@@ -49,6 +49,7 @@ config LORA_SX1301
tristate "Semtech SX1301 SPI driver"
default y
depends on SPI
+ select REGMAP_SPI
help
Semtech SX1301
diff --git a/drivers/net/lora/sx1301.c b/drivers/net/lora/sx1301.c
index 5342b61..49958f0 100644
--- a/drivers/net/lora/sx1301.c
+++ b/drivers/net/lora/sx1301.c
@@ -3,6 +3,7 @@
* Semtech SX1301 LoRa concentrator
*
* Copyright (c) 2018 Andreas Färber
+ * Copyright (c) 2018 Ben Whitten
*
* Based on SX1301 HAL code:
* Copyright (c) 2013 Semtech-Cycleo
@@ -19,6 +20,9 @@
#include <linux/of_gpio.h>
#include <linux/lora/dev.h>
#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
+#include "sx1301.h"
#define REG_PAGE_RESET 0
#define REG_VERSION 1
@@ -65,6 +69,213 @@
#define REG_EMERGENCY_FORCE_HOST_CTRL BIT(0)
+enum sx1301_fields {
+ F_SOFT_RESET,
+ F_START_BIST0,
+ F_START_BIST1,
+ F_BIST0_FINISHED,
+ F_BIST1_FINISHED,
+ F_GLOBAL_EN,
+ F_CLK32M_EN,
+ F_RADIO_A_EN,
+ F_RADIO_B_EN,
+ F_RADIO_RST,
+
+ F_RX_INVERT_IQ,
+ F_MODEM_INVERT_IQ,
+ F_MBWSSF_MODEM_INVERT_IQ,
+ F_RX_EDGE_SELECT,
+ F_MISC_RADIO_EN,
+ F_FSK_MODEM_INVERT_IQ,
+
+ F_RSSI_BB_FILTER_ALPHA,
+ F_RSSI_DEC_FILTER_ALPHA,
+ F_RSSI_CHANN_FILTER_ALPHA,
+
+ F_DEC_GAIN_OFFSET,
+ F_CHAN_GAIN_OFFSET,
+
+ F_LLR_SCALE,
+ F_SNR_AVG_CST,
+
+ F_CORR_NUM_SAME_PEAK,
+ F_CORR_MAC_GAIN,
+
+ F_FSK_CH_BW_EXPO,
+ F_FSK_RSSI_LENGTH,
+ F_FSK_RX_INVERT,
+ F_FSK_PKT_MODE,
+
+ F_FSK_PSIZE,
+ F_FSK_CRC_EN,
+ F_FSK_DCFREE_ENC,
+ F_FSK_CRC_IBM,
+
+ F_FSK_ERROR_OSR_TOL,
+ F_FSK_RADIO_SELECT,
+
+ F_TX_MODE,
+ F_TX_ZERO_PAD,
+ F_TX_EDGE_SELECT,
+ F_TX_EDGE_SELECT_TOP,
+
+ F_TX_GAIN,
+ F_TX_CHIRP_LOW_PASS,
+ F_TX_FCC_WIDEBAND,
+ F_TX_SWAP_IQ,
+
+ F_FSK_TX_GAUSSIAN_EN,
+ F_FSK_TX_GAUSSIAN_SELECT_BT,
+ F_FSK_TX_PATTERN_EN,
+ F_FSK_TX_PREAMBLE_SEQ,
+ F_FSK_TX_PSIZE,
+
+ F_FORCE_HOST_RADIO_CTRL,
+ F_FORCE_HOST_FE_CTRL,
+ F_FORCE_DEC_FILTER_GAIN,
+
+ F_MCU_RST_0,
+ F_MCU_RST_1,
+ F_MCU_SELECT_MUX_0,
+ F_MCU_SELECT_MUX_1,
+ F_MCU_CORRUPTION_DETECTED_0,
+ F_MCU_CORRUPTION_DETECTED_1,
+ F_MCU_SELECT_EDGE_0,
+ F_MCU_SELECT_EDGE_1,
+
+ F_EMERGENCY_FORCE_HOST_CTRL,
+};
+
+static const struct reg_field sx1301_reg_fields[] = {
+ /* PAGE */
+ [F_SOFT_RESET] = REG_FIELD(SX1301_PAGE, 7, 7),
+ /* BIST */
+ [F_START_BIST0] = REG_FIELD(SX1301_BIST, 0, 0),
+ [F_START_BIST1] = REG_FIELD(SX1301_BIST, 1, 1),
+ /* BIST_S */
+ [F_BIST0_FINISHED] = REG_FIELD(SX1301_BIST_S, 0, 0),
+ [F_BIST1_FINISHED] = REG_FIELD(SX1301_BIST_S, 1, 1),
+ /* GEN */
+ [F_GLOBAL_EN] = REG_FIELD(SX1301_GEN, 3, 3),
+ /* CKEN */
+ [F_CLK32M_EN] = REG_FIELD(SX1301_CKEN, 0, 0),
+ /* RADIO_CFG */
+ [F_RADIO_A_EN] = REG_FIELD(SX1301_RADIO_CFG, 0, 0),
+ [F_RADIO_B_EN] = REG_FIELD(SX1301_RADIO_CFG, 1, 1),
+ [F_RADIO_RST] = REG_FIELD(SX1301_RADIO_CFG, 2, 2),
+
+ /* IQCFG */
+ [F_RX_INVERT_IQ] = REG_FIELD(SX1301_IQCFG, 0, 0),
+ [F_MODEM_INVERT_IQ] = REG_FIELD(SX1301_IQCFG, 1, 1),
+ [F_MBWSSF_MODEM_INVERT_IQ] = REG_FIELD(SX1301_IQCFG, 2, 2),
+ [F_RX_EDGE_SELECT] = REG_FIELD(SX1301_IQCFG, 3, 3),
+ [F_MISC_RADIO_EN] = REG_FIELD(SX1301_IQCFG, 4, 4),
+ [F_FSK_MODEM_INVERT_IQ] = REG_FIELD(SX1301_IQCFG, 5, 5),
+
+ /* RSSI_X_FILTER_ALPHA */
+ [F_RSSI_BB_FILTER_ALPHA] =
+ REG_FIELD(SX1301_RSSI_BB_FILTER_ALPHA, 0, 4),
+ [F_RSSI_DEC_FILTER_ALPHA] =
+ REG_FIELD(SX1301_RSSI_DEC_FILTER_ALPHA, 0, 4),
+ [F_RSSI_CHANN_FILTER_ALPHA] =
+ REG_FIELD(SX1301_RSSI_CHANN_FILTER_ALPHA, 0, 4),
+
+ /* GAIN_OFFSET */
+ [F_DEC_GAIN_OFFSET] = REG_FIELD(SX1301_GAIN_OFFSET, 0, 3),
+ [F_CHAN_GAIN_OFFSET] = REG_FIELD(SX1301_GAIN_OFFSET, 4, 7),
+
+ /* MISC_CFG1 */
+ [F_LLR_SCALE] = REG_FIELD(SX1301_MISC_CFG1, 0, 3),
+ [F_SNR_AVG_CST] = REG_FIELD(SX1301_MISC_CFG1, 4, 5),
+
+ /* CORR_CFG */
+ [F_CORR_NUM_SAME_PEAK] = REG_FIELD(SX1301_CORR_CFG, 0, 3),
+ [F_CORR_MAC_GAIN] = REG_FIELD(SX1301_CORR_CFG, 4, 6),
+
+ /* FSK_CFG1 */
+ [F_FSK_CH_BW_EXPO] = REG_FIELD(SX1301_FSK_CFG1, 0, 2),
+ [F_FSK_RSSI_LENGTH] = REG_FIELD(SX1301_FSK_CFG1, 3, 5),
+ [F_FSK_RX_INVERT] = REG_FIELD(SX1301_FSK_CFG1, 6, 6),
+ [F_FSK_PKT_MODE] = REG_FIELD(SX1301_FSK_CFG1, 7, 7),
+
+ /* FSK_CFG2 */
+ [F_FSK_PSIZE] = REG_FIELD(SX1301_FSK_CFG2, 0, 2),
+ [F_FSK_CRC_EN] = REG_FIELD(SX1301_FSK_CFG2, 3, 3),
+ [F_FSK_DCFREE_ENC] = REG_FIELD(SX1301_FSK_CFG2, 4, 5),
+ [F_FSK_CRC_IBM] = REG_FIELD(SX1301_FSK_CFG2, 6, 6),
+
+ /* FSK_ERROR_OSR_TOL */
+ [F_FSK_ERROR_OSR_TOL] = REG_FIELD(SX1301_FSK_ERROR_OSR_TOL, 0, 4),
+ [F_FSK_RADIO_SELECT] = REG_FIELD(SX1301_FSK_ERROR_OSR_TOL, 7, 7),
+
+ /* TX_CFG1 */
+ [F_TX_MODE] = REG_FIELD(SX1301_TX_CFG1, 0, 0),
+ [F_TX_ZERO_PAD] = REG_FIELD(SX1301_TX_CFG1, 1, 4),
+ [F_TX_EDGE_SELECT] = REG_FIELD(SX1301_TX_CFG1, 5, 5),
+ [F_TX_EDGE_SELECT_TOP] = REG_FIELD(SX1301_TX_CFG1, 6, 6),
+
+ /* TX_CFG2 */
+ [F_TX_GAIN] = REG_FIELD(SX1301_TX_CFG2, 0, 1),
+ [F_TX_CHIRP_LOW_PASS] = REG_FIELD(SX1301_TX_CFG2, 2, 4),
+ [F_TX_FCC_WIDEBAND] = REG_FIELD(SX1301_TX_CFG2, 5, 6),
+ [F_TX_SWAP_IQ] = REG_FIELD(SX1301_TX_CFG2, 7, 7),
+
+ /* FSK_TX */
+ [F_FSK_TX_GAUSSIAN_EN] = REG_FIELD(SX1301_FSK_TX, 0, 0),
+ [F_FSK_TX_GAUSSIAN_SELECT_BT] = REG_FIELD(SX1301_FSK_TX, 1, 2),
+ [F_FSK_TX_PATTERN_EN] = REG_FIELD(SX1301_FSK_TX, 3, 3),
+ [F_FSK_TX_PREAMBLE_SEQ] = REG_FIELD(SX1301_FSK_TX, 4, 4),
+ [F_FSK_TX_PSIZE] = REG_FIELD(SX1301_FSK_TX, 5, 7),
+
+ /* FORCE_CTRL */
+ [F_FORCE_HOST_RADIO_CTRL] = REG_FIELD(SX1301_FORCE_CTRL, 1, 1),
+ [F_FORCE_HOST_FE_CTRL] = REG_FIELD(SX1301_FORCE_CTRL, 2, 2),
+ [F_FORCE_DEC_FILTER_GAIN] = REG_FIELD(SX1301_FORCE_CTRL, 3, 3),
+
+ /* MCU_CTRL */
+ [F_MCU_RST_0] = REG_FIELD(SX1301_MCU_CTRL, 0, 0),
+ [F_MCU_RST_1] = REG_FIELD(SX1301_MCU_CTRL, 1, 1),
+ [F_MCU_SELECT_MUX_0] = REG_FIELD(SX1301_MCU_CTRL, 2, 2),
+ [F_MCU_SELECT_MUX_1] = REG_FIELD(SX1301_MCU_CTRL, 3, 3),
+ [F_MCU_CORRUPTION_DETECTED_0] = REG_FIELD(SX1301_MCU_CTRL, 4, 4),
+ [F_MCU_CORRUPTION_DETECTED_1] = REG_FIELD(SX1301_MCU_CTRL, 5, 5),
+ [F_MCU_SELECT_EDGE_0] = REG_FIELD(SX1301_MCU_CTRL, 6, 6),
+ [F_MCU_SELECT_EDGE_1] = REG_FIELD(SX1301_MCU_CTRL, 7, 7),
+
+ /* EMERGENCY_FORCE_HOST_CTRL */
+ [F_EMERGENCY_FORCE_HOST_CTRL] =
+ REG_FIELD(SX1301_EMERGENCY_FORCE_HOST_CTRL, 0, 0),
+};
+
+static const struct regmap_range_cfg sx1301_ranges[] = {
+ {
+ .name = "Pages",
+
+ .range_min = SX1301_VIRT_BASE,
+ .range_max = SX1301_MAX_REGISTER,
+
+ .selector_reg = SX1301_PAGE,
+ .selector_mask = 0x3,
+
+ .window_start = 0,
+ .window_len = SX1301_PAGE_LEN,
+ },
+};
+
+static struct regmap_config sx1301_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .cache_type = REGCACHE_NONE,
+
+ .read_flag_mask = 0,
+ .write_flag_mask = BIT(7),
+
+ .ranges = sx1301_ranges,
+ .num_ranges = ARRAY_SIZE(sx1301_ranges),
+ .max_register = SX1301_MAX_REGISTER,
+};
+
struct spi_sx1301 {
struct spi_device *parent;
u8 page;
@@ -76,8 +287,29 @@ struct sx1301_priv {
struct gpio_desc *rst_gpio;
u8 cur_page;
struct spi_controller *radio_a_ctrl, *radio_b_ctrl;
+ struct regmap *regmap;
+ struct regmap_field *regmap_fields[ARRAY_SIZE(sx1301_reg_fields)];
};
+static int sx1301_field_read(struct sx1301_priv *priv,
+ enum sx1301_fields field_id)
+{
+ int ret;
+ int val;
+
+ ret = regmap_field_read(priv->regmap_fields[field_id], &val);
+ if (ret)
+ return ret;
+
+ return val;
+}
+
+static int sx1301_field_write(struct sx1301_priv *priv,
+ enum sx1301_fields field_id, u8 val)
+{
+ return regmap_field_write(priv->regmap_fields[field_id], val);
+}
+
static int sx1301_read_burst(struct spi_device *spi, u8 reg, u8 *val, size_t len)
{
u8 addr = reg & 0x7f;
@@ -620,6 +852,8 @@ static int sx1301_probe(struct spi_device *spi)
struct spi_sx1301 *radio;
struct gpio_desc *rst;
int ret;
+ int i;
+ unsigned int ver;
u8 val;
rst = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_LOW);
@@ -634,18 +868,6 @@ static int sx1301_probe(struct spi_device *spi)
spi->bits_per_word = 8;
spi_setup(spi);
- ret = sx1301_read(spi, REG_VERSION, &val);
- if (ret) {
- dev_err(&spi->dev, "version read failed\n");
- goto err_version;
- }
-
- if (val != 103) {
- dev_err(&spi->dev, "unexpected version: %u\n", val);
- ret = -ENXIO;
- goto err_version;
- }
-
netdev = alloc_loradev(sizeof(*priv));
if (!netdev) {
ret = -ENOMEM;
@@ -659,6 +881,38 @@ static int sx1301_probe(struct spi_device *spi)
spi_set_drvdata(spi, netdev);
SET_NETDEV_DEV(netdev, &spi->dev);
+ priv->regmap = devm_regmap_init_spi(spi, &sx1301_regmap_config);
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ dev_err(&spi->dev, "Regmap allocation failed: %d\n", ret);
+ return err_regmap;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sx1301_reg_fields); i++) {
+ const struct reg_field *reg_fields = sx1301_reg_fields;
+
+ priv->regmap_fields[i] = devm_regmap_field_alloc(&spi->dev,
+ priv->regmap,
+ reg_fields[i]);
+ if (IS_ERR(priv->regmap_fields[i])) {
+ ret = PTR_ERR(priv->regmap_fields[i]);
+ dev_err(&spi->dev, "Cannot allocate regmap field: %d\n", ret);
+ goto err_regmap;
+ }
+ }
+
+ ret = regmap_read(priv->regmap, SX1301_VER, &ver);
+ if (ret) {
+ dev_err(&spi->dev, "version read failed\n");
+ goto err_version;
+ }
+
+ if (ver != 103) {
+ dev_err(&spi->dev, "unexpected version: %u\n", ver);
+ ret = -ENXIO;
+ goto err_version;
+ }
+
ret = sx1301_write(spi, REG_PAGE_RESET, 0);
if (ret) {
dev_err(&spi->dev, "page/reset write failed\n");
@@ -888,9 +1142,10 @@ static int sx1301_probe(struct spi_device *spi)
err_read_global_en_0:
err_soft_reset:
err_init_page:
+err_version:
+err_regmap:
free_loradev(netdev);
err_alloc_loradev:
-err_version:
return ret;
}
@@ -927,4 +1182,5 @@ module_spi_driver(sx1301_spi_driver);
MODULE_DESCRIPTION("SX1301 SPI driver");
MODULE_AUTHOR("Andreas Färber <afaerber@suse.de>");
+MODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/lora/sx1301.h b/drivers/net/lora/sx1301.h
new file mode 100644
index 0000000..abfb7b5
--- /dev/null
+++ b/drivers/net/lora/sx1301.h
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Semtech SX1301 lora concentrator
+ *
+ * Copyright (c) 2018 Ben Whitten
+ */
+
+#ifndef _SX1301_
+#define _SX1301_
+
+/* Page independent */
+#define SX1301_PAGE 0x00
+#define SX1301_VER 0x01
+#define SX1301_RDBAL 0x02
+#define SX1301_RDBAH 0x03
+#define SX1301_RDBD 0x04
+#define SX1301_TDBA 0x05
+#define SX1301_TDBD 0x06
+#define SX1301_MPA 0x09
+#define SX1301_MPD 0x0A
+#define SX1301_RPNS 0x0B
+#define SX1301_RPAPL 0x0C
+#define SX1301_RPAPH 0x0D
+#define SX1301_RPS 0x0E
+#define SX1301_RPPS 0x0F
+#define SX1301_GEN 0x10
+#define SX1301_CKEN 0x11
+#define SX1301_BIST 0x12
+#define SX1301_BIST_S 0x13
+#define SX1301_GPSI 0x1B
+#define SX1301_GPSO 0x1C
+#define SX1301_GPMODE 0x1D
+#define SX1301_GPREGI 0x1E
+#define SX1301_GPREGO 0x1F
+#define SX1301_AGCSTS 0x20
+#define SX1301_ARBSTS 0x7D
+#define SX1301_ID 0x7F
+
+#define SX1301_VIRT_BASE 0x100
+#define SX1301_PAGE_LEN 0x80
+#define SX1301_PAGE_BASE(n) (SX1301_VIRT_BASE + (SX1301_PAGE_LEN * n))
+
+/* Page 0 */
+#define SX1301_IQCFG (SX1301_PAGE_BASE(0) + 0x21)
+#define SX1301_DECCFG (SX1301_PAGE_BASE(0) + 0x22)
+#define SX1301_CHRS (SX1301_PAGE_BASE(0) + 0x23)
+#define SX1301_IF0L (SX1301_PAGE_BASE(0) + 0x24)
+#define SX1301_IF0H (SX1301_PAGE_BASE(0) + 0x25)
+#define SX1301_IF1L (SX1301_PAGE_BASE(0) + 0x26)
+#define SX1301_IF1H (SX1301_PAGE_BASE(0) + 0x27)
+#define SX1301_IF2L (SX1301_PAGE_BASE(0) + 0x28)
+#define SX1301_IF2H (SX1301_PAGE_BASE(0) + 0x29)
+#define SX1301_IF3L (SX1301_PAGE_BASE(0) + 0x2A)
+#define SX1301_IF3H (SX1301_PAGE_BASE(0) + 0x2B)
+#define SX1301_IF4L (SX1301_PAGE_BASE(0) + 0x2C)
+#define SX1301_IF4H (SX1301_PAGE_BASE(0) + 0x2D)
+#define SX1301_IF5L (SX1301_PAGE_BASE(0) + 0x2E)
+#define SX1301_IF5H (SX1301_PAGE_BASE(0) + 0x2F)
+#define SX1301_IF6L (SX1301_PAGE_BASE(0) + 0x30)
+#define SX1301_IF6H (SX1301_PAGE_BASE(0) + 0x31)
+#define SX1301_IF7L (SX1301_PAGE_BASE(0) + 0x32)
+#define SX1301_IF7H (SX1301_PAGE_BASE(0) + 0x33)
+#define SX1301_IF8L (SX1301_PAGE_BASE(0) + 0x34)
+#define SX1301_IF8H (SX1301_PAGE_BASE(0) + 0x35)
+#define SX1301_IF9L (SX1301_PAGE_BASE(0) + 0x36)
+#define SX1301_IF9H (SX1301_PAGE_BASE(0) + 0x37)
+#define SX1301_COR0DETEN (SX1301_PAGE_BASE(0) + 0x41)
+#define SX1301_COR1DETEN (SX1301_PAGE_BASE(0) + 0x42)
+#define SX1301_COR2DETEN (SX1301_PAGE_BASE(0) + 0x43)
+#define SX1301_COR3DETEN (SX1301_PAGE_BASE(0) + 0x44)
+#define SX1301_COR4DETEN (SX1301_PAGE_BASE(0) + 0x45)
+#define SX1301_COR5DETEN (SX1301_PAGE_BASE(0) + 0x46)
+#define SX1301_COR6DETEN (SX1301_PAGE_BASE(0) + 0x47)
+#define SX1301_COR7DETEN (SX1301_PAGE_BASE(0) + 0x48)
+#define SX1301_CORR_CFG (SX1301_PAGE_BASE(0) + 0x4E)
+#define SX1301_MODEM_START_RDX4_L (SX1301_PAGE_BASE(0) + 0x51)
+#define SX1301_MODEM_START_RDX4_H (SX1301_PAGE_BASE(0) + 0x52)
+#define SX1301_MODEM_START_SF12_RDX4_L (SX1301_PAGE_BASE(0) + 0x53)
+#define SX1301_MODEM_START_SF12_RDX4_H (SX1301_PAGE_BASE(0) + 0x54)
+#define SX1301_TIMTRAK2 (SX1301_PAGE_BASE(0) + 0x5D)
+#define SX1301_PRSYMBNBL (SX1301_PAGE_BASE(0) + 0x60)
+#define SX1301_PRSYMBNBH (SX1301_PAGE_BASE(0) + 0x61)
+#define SX1301_MISC_CFG1 (SX1301_PAGE_BASE(0) + 0x63)
+#define SX1301_MISC_CFG2 (SX1301_PAGE_BASE(0) + 0x64)
+#define SX1301_HEADER_CFG1 (SX1301_PAGE_BASE(0) + 0x65)
+#define SX1301_HEADER_CFG2 (SX1301_PAGE_BASE(0) + 0x66)
+#define SX1301_GAIN_OFFSET (SX1301_PAGE_BASE(0) + 0x68)
+#define SX1301_FORCE_CTRL (SX1301_PAGE_BASE(0) + 0x69)
+#define SX1301_MCU_CTRL (SX1301_PAGE_BASE(0) + 0x6A)
+#define SX1301_CHANN_SELECT_RSSI (SX1301_PAGE_BASE(0) + 0x6B)
+#define SX1301_RSSI_BB_DEFAULT_VALUE (SX1301_PAGE_BASE(0) + 0x6C)
+#define SX1301_RSSI_DEC_DEFAULT_VALUE (SX1301_PAGE_BASE(0) + 0x6D)
+#define SX1301_RSSI_CHANN_DEFAULT_VALUE (SX1301_PAGE_BASE(0) + 0x6E)
+#define SX1301_RSSI_BB_FILTER_ALPHA (SX1301_PAGE_BASE(0) + 0x6F)
+#define SX1301_RSSI_DEC_FILTER_ALPHA (SX1301_PAGE_BASE(0) + 0x70)
+#define SX1301_RSSI_CHANN_FILTER_ALPHA (SX1301_PAGE_BASE(0) + 0x71)
+
+/* Page 1 */
+#define SX1301_TXTRIG (SX1301_PAGE_BASE(1) + 0x21)
+#define SX1301_TX_OFFSET_I (SX1301_PAGE_BASE(1) + 0x27)
+#define SX1301_TX_OFFSET_Q (SX1301_PAGE_BASE(1) + 0x28)
+#define SX1301_TX_CFG1 (SX1301_PAGE_BASE(1) + 0x29)
+#define SX1301_TX_CFG2 (SX1301_PAGE_BASE(1) + 0x2A)
+#define SX1301_BHIMPCFG1 (SX1301_PAGE_BASE(1) + 0x2B)
+#define SX1301_BHIMPCFG2 (SX1301_PAGE_BASE(1) + 0x2C)
+#define SX1301_BHSYNCPOS (SX1301_PAGE_BASE(1) + 0x2E)
+#define SX1301_BHPRSYMNBL (SX1301_PAGE_BASE(1) + 0x2F)
+#define SX1301_MBWSSF_MISC_CFG1 (SX1301_PAGE_BASE(1) + 0x3A)
+#define SX1301_MBWSSF_MISC_CFG2 (SX1301_PAGE_BASE(1) + 0x3B)
+#define SX1301_MBWSSF_MISC_CFG3 (SX1301_PAGE_BASE(1) + 0x3C)
+#define SX1301_MBWSSF_MISC_CFG4 (SX1301_PAGE_BASE(1) + 0x3D)
+#define SX1301_TX_STATUS (SX1301_PAGE_BASE(1) + 0x3E)
+#define SX1301_FSK_CFG1 (SX1301_PAGE_BASE(1) + 0x3F)
+#define SX1301_FSK_CFG2 (SX1301_PAGE_BASE(1) + 0x40)
+#define SX1301_FSK_ERROR_OSR_TOL (SX1301_PAGE_BASE(1) + 0x41)
+#define SX1301_FSK_BR_RATIOL (SX1301_PAGE_BASE(1) + 0x42)
+#define SX1301_FSK_BR_RATIOH (SX1301_PAGE_BASE(1) + 0x43)
+#define SX1301_FSK_REF_PATTERN_0 (SX1301_PAGE_BASE(1) + 0x44)
+#define SX1301_FSK_REF_PATTERN_1 (SX1301_PAGE_BASE(1) + 0x45)
+#define SX1301_FSK_REF_PATTERN_2 (SX1301_PAGE_BASE(1) + 0x46)
+#define SX1301_FSK_REF_PATTERN_3 (SX1301_PAGE_BASE(1) + 0x47)
+#define SX1301_FSK_REF_PATTERN_4 (SX1301_PAGE_BASE(1) + 0x48)
+#define SX1301_FSK_REF_PATTERN_5 (SX1301_PAGE_BASE(1) + 0x49)
+#define SX1301_FSK_REF_PATTERN_6 (SX1301_PAGE_BASE(1) + 0x4A)
+#define SX1301_FSK_REF_PATTERN_7 (SX1301_PAGE_BASE(1) + 0x4B)
+#define SX1301_FSK_PKT_LENGTH (SX1301_PAGE_BASE(1) + 0x4C)
+#define SX1301_FSK_TX (SX1301_PAGE_BASE(1) + 0x4D)
+#define SX1301_FSK_AAFC (SX1301_PAGE_BASE(1) + 0x52)
+#define SX1301_FSK_PATTERN_TIMEOUT_CFGL (SX1301_PAGE_BASE(1) + 0x53)
+#define SX1301_FSK_PATTERN_TIMEOUT_CFGH (SX1301_PAGE_BASE(1) + 0x54)
+
+/* Page 2 */
+#define SX1301_RADIO_A_SPI_DATA (SX1301_PAGE_BASE(2) + 0x21)
+#define SX1301_RADIO_A_SPI_DATA_RB (SX1301_PAGE_BASE(2) + 0x22)
+#define SX1301_RADIO_A_SPI_ADDR (SX1301_PAGE_BASE(2) + 0x23)
+#define SX1301_RADIO_A_SPI_CS (SX1301_PAGE_BASE(2) + 0x25)
+#define SX1301_RADIO_B_SPI_DATA (SX1301_PAGE_BASE(2) + 0x26)
+#define SX1301_RADIO_B_SPI_DATA_RB (SX1301_PAGE_BASE(2) + 0x27)
+#define SX1301_RADIO_B_SPI_ADDR (SX1301_PAGE_BASE(2) + 0x28)
+#define SX1301_RADIO_B_SPI_CS (SX1301_PAGE_BASE(2) + 0x2A)
+#define SX1301_RADIO_CFG (SX1301_PAGE_BASE(2) + 0x2B)
+#define SX1301_PA_GAIN (SX1301_PAGE_BASE(2) + 0x2C)
+#define SX1301_FE_A_CTRL_LUT (SX1301_PAGE_BASE(2) + 0x2D)
+#define SX1301_FE_B_CTRL_LUT (SX1301_PAGE_BASE(2) + 0x2E)
+#define SX1301_VALID_HEADER_COUNTER_M_WSSF (SX1301_PAGE_BASE(2) + 0x38)
+#define SX1301_VALID_HEADER_COUNTER_FSK (SX1301_PAGE_BASE(2) + 0x39)
+#define SX1301_VALID_PACKET_COUNTER_MB_SSF (SX1301_PAGE_BASE(2) + 0x3A)
+#define SX1301_VALID_PACKET_COUNTER_FSK (SX1301_PAGE_BASE(2) + 0x3B)
+#define SX1301_CHANN_RSSI (SX1301_PAGE_BASE(2) + 0x3C)
+#define SX1301_BB_RSSI (SX1301_PAGE_BASE(2) + 0x3D)
+#define SX1301_DEC_RSSI (SX1301_PAGE_BASE(2) + 0x3E)
+#define SX1301_DBG_MCU_DATA (SX1301_PAGE_BASE(2) + 0x3F)
+#define SX1301_DBG_ARB_MCU_RAM_DATA (SX1301_PAGE_BASE(2) + 0x40)
+#define SX1301_DBG_AGC_MCU_RAM_DATA (SX1301_PAGE_BASE(2) + 0x41)
+#define SX1301_TIMESTAMP_0 (SX1301_PAGE_BASE(2) + 0x46)
+#define SX1301_TIMESTAMP_1 (SX1301_PAGE_BASE(2) + 0x47)
+#define SX1301_TIMESTAMP_2 (SX1301_PAGE_BASE(2) + 0x48)
+#define SX1301_TIMESTAMP_3 (SX1301_PAGE_BASE(2) + 0x49)
+#define SX1301_DBG_ARB_MCU_RAM_ADDR (SX1301_PAGE_BASE(2) + 0x50)
+#define SX1301_DBG_AGC_MCU_RAM_ADDR (SX1301_PAGE_BASE(2) + 0x51)
+#define SX1301_SPI_MASTER_CFG (SX1301_PAGE_BASE(2) + 0x52)
+#define SX1301_GPS_CFG (SX1301_PAGE_BASE(2) + 0x59)
+
+/* Page 3 */
+#define SX1301_EMERGENCY_FORCE_HOST_CTRL (SX1301_PAGE_BASE(3) + 0x7F)
+
+#define SX1301_MAX_REGISTER (SX1301_PAGE_BASE(3) + 0x7F)
+
+#endif
--
2.7.4
^ permalink raw reply related
* [PATCH lora-next 00/10] Conversing sx1301 to regmap and regfield
From: Ben Whitten @ 2018-08-07 17:32 UTC (permalink / raw)
To: afaerber, starnight, hasnain.virk; +Cc: netdev, Ben Whitten
This series converts the majority of sx1301 code to using regmap and regfield
system.
The SPI subsystem is as yet untouched but will be converted to a regmap_bus
in a future series with sx1257 conversions.
Interestingly the initial state did not work for me as I was getting kernel
oopses somewhere todo with sx1301_page_read on my hardware.
Since conversion to regmap the sx1301 initialises properly, sx1257 still oopses
because of the SPI bus read but presumably will go away with regmap_bus.
Ben Whitten (10):
net: lora: sx1301: add register, bit-fields, and helpers for regmap
net: lora: add methods for devm registration
net: lora: sx1301: convert to devm registration of netdev
net: lora: sx1301: convert probe function to regmap access
net: lora: sx1301: add device to private data and convert ram reads
net: lora: sx1301: simplify firmware loading and convert
net: lora: sx1301: convert read and write burst to take priv data
net: lora: sx1301: convert read and write to priv pointer
net: lora: sx1301: convert agc calibrate to regmap functions
net: lora: sx1301: convert all firmware to regmap
drivers/net/lora/Kconfig | 1 +
drivers/net/lora/dev.c | 20 ++
drivers/net/lora/sx1301.c | 803 ++++++++++++++++++++++++----------------------
drivers/net/lora/sx1301.h | 176 ++++++++++
include/linux/lora/dev.h | 1 +
5 files changed, 623 insertions(+), 378 deletions(-)
create mode 100644 drivers/net/lora/sx1301.h
--
2.7.4
^ permalink raw reply
* Re: [PATCH v3 net-next 5/9] net: stmmac: Add MDIO related functions for XGMAC2
From: Florian Fainelli @ 2018-08-07 17:31 UTC (permalink / raw)
To: Jose Abreu, netdev
Cc: David S. Miller, Joao Pinto, Giuseppe Cavallaro, Alexandre Torgue,
Andrew Lunn
In-Reply-To: <3a097ddf-cff1-460f-2c63-7347274c1ee5@synopsys.com>
On 08/07/2018 06:35 AM, Jose Abreu wrote:
> Hi Florian,
>
> On 06-08-2018 16:25, Florian Fainelli wrote:
>> On August 6, 2018 12:59:54 AM PDT, Jose Abreu <Jose.Abreu@synopsys.com> wrote:
>>> On 03-08-2018 20:06, Florian Fainelli wrote:
>>>> On 08/03/2018 08:50 AM, Jose Abreu wrote:
>>>>> Add the MDIO related funcionalities for the new IP block XGMAC2.
>>>>>
>>>>> Signed-off-by: Jose Abreu <joabreu@synopsys.com>
>>>>> Cc: David S. Miller <davem@davemloft.net>
>>>>> Cc: Joao Pinto <jpinto@synopsys.com>
>>>>> Cc: Giuseppe Cavallaro <peppe.cavallaro@st.com>
>>>>> Cc: Alexandre Torgue <alexandre.torgue@st.com>
>>>>> Cc: Andrew Lunn <andrew@lunn.ch>
>>>>> ---
>>>>> +satic int stmmac_xgmac2_c22_format(struct stmmac_priv *priv, int
>>> phyaddr,
>>>>> + int phyreg, u32 *hw_addr)
>>>>> +{
>>>>> + unsigned int mii_data = priv->hw->mii.data;
>>>>> + u32 tmp;
>>>>> +
>>>>> + /* HW does not support C22 addr >= 4 */
>>>>> + if (phyaddr >= 4)
>>>>> + return -ENODEV;
>>>> It would be nice if this could be moved at probe time so you don't
>>> have
>>>> to wait until you connect to the PHY, read its PHY OUI and find out
>>> it
>>>> has a MDIO address >= 4. Not a blocker, but something that could be
>>>> improved further on.
>>>>
>>>> In premise you could even scan the MDIO bus' device tree node, and
>>> find
>>>> that out ahead of time.
>>> Oh, but I use phylib ... I only provide the read/write callbacks
>>> so I think we should avoid duplicating the code that's already in
>>> the phylib ... No?
>> You can always extract the code that scans a MDIO bus into a helper function and make it parametrized with a callback of some kind. In that case I would be fine with you open coding the MDIO bus scan to find out if there is an address >= 4.
>
> Sorry but I dont' think thats the best solution because
> of_mdiobus_register() already scans the bus. Duplicating this
> should be avoided, no?
I suggested you extract the code into a helper to avoid the duplication.
>
> Note all of this is probably never needed because stmmac just
> picks the first phy it finds, if phy_addr is not specified ...
No, it's much more involved than that, there are several modes in which
MDIO on stmmac can be instantiated:
- platform data instantiation, which uses a loop and finds the first
available address
- Device Tree which will scan the MDIO bus' node child nodes and create
PHYs for valid nodes
It feels a bit late to check for PHY address >= 4 during the first read,
because it won't be obvious to somehow why this fails, but if that
fulfills your needs, keep it that way.
>
>>
>>>>> + /* Wait until any existing MII operation is complete */
>>>>> + if (readl_poll_timeout(priv->ioaddr + mii_data, tmp,
>>>>> + !(tmp & MII_XGMAC_BUSY), 100, 10000))
>>>>> + return -EBUSY;
>>>>> +
>>>>> + /* Set port as Clause 22 */
>>>>> + tmp = readl(priv->ioaddr + XGMAC_MDIO_C22P);
>>>>> + tmp |= BIT(phyaddr);
>>>> Since the registers are being Read/Modify/Write here, don't you need
>>> to
>>>> clear the previous address bits as well?
>>>>
>>>> You probably did not encounter any problems in your testing if you
>>> had
>>>> only one PHY on the MDIO bus, but this is not something that is
>>>> necessarily true, e.g: if you have an Ethernet switch, several MDIO
>>> bus
>>>> addresses are going to be responding.
>>>>
>>>> Your MDIO bus implementation must be able to support one transaction
>>>> with one PHY address and the next transaction with another PHY
>>> address ,
>>>> etc...
>>>>
>>>> That is something that should be easy to fix and be resubmitted as
>>> part
>>>> of v4.
>>> I'm not following you here... I only set/unset the bit for the
>>> corresponding phyaddr that phylib wants to read/write. Why would
>>> I clear the remaining addresses?
>> Because this is all about transactions, the HW must be in a state that it will be able to perform that transaction correctly. So here for instance if you needed to support a C45 transaction you would have to clear that bit for that particular PHY address. Since you don't appear to support those yet then yes the code appears fine though it would not hurt if you did clear all other PHY's c22 bits to make it clear what this does.
>
> I can't test C45 right now but I will in a near future. In that
> case then we will need to support C22 and C45 so I may want to
> only set one bit for a specific phy that only supports c22.
We are really talking about adding something like:
reg &= ~(MASK);
before setting the bit, anyhow, if you want to do that later on when you
add C45 support, be my guest...
--
Florian
^ permalink raw reply
* Re: [PATCH v6 1/9] PCI: Check for PCIe downtraining conditions
From: David Miller @ 2018-08-07 19:44 UTC (permalink / raw)
To: mr.nuke.me
Cc: linux-pci, bhelgaas, jakub.kicinski, keith.busch, alex_gagniuc,
austin_bolen, shyam_iyer, ariel.elior, everest-linux-l2,
michael.chan, ganeshgr, jeffrey.t.kirsher, tariqt, saeedm, leon,
dirk.vandermerwe, netdev, linux-kernel, intel-wired-lan,
linux-rdma, oss-drivers
In-Reply-To: <20180806232600.25694-1-mr.nuke.me@gmail.com>
From: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Date: Mon, 6 Aug 2018 18:25:35 -0500
> PCIe downtraining happens when both the device and PCIe port are
> capable of a larger bus width or higher speed than negotiated.
> Downtraining might be indicative of other problems in the system, and
> identifying this from userspace is neither intuitive, nor
> straightforward.
>
> The easiest way to detect this is with pcie_print_link_status(),
> since the bottleneck is usually the link that is downtrained. It's not
> a perfect solution, but it works extremely well in most cases.
>
> Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Feel free to merge this entire series via the PCI tree.
For the series:
Acked-by: David S. Miller <davem@davemloft.net>
^ permalink raw reply
* Re: [PATCH] net: thunderx: check for failed allocation lmac->dmacs
From: David Miller @ 2018-08-07 19:38 UTC (permalink / raw)
To: colin.king
Cc: sgoutham, rric, linux-arm-kernel, netdev, kernel-janitors,
linux-kernel
In-Reply-To: <20180806165045.15707-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Mon, 6 Aug 2018 17:50:45 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> The allocation of lmac->dmacs is not being checked for allocation
> failure. Add the check.
>
> Fixes: 3a34ecfd9d3f ("net: thunderx: add MAC address filter tracking for LMAC")
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox