* [RFC ipsec-next] xfrm: Remove xfrmi interface ID from flowi
From: Benedict Wong @ 2018-07-17 21:40 UTC (permalink / raw)
To: netdev; +Cc: nharold, lorenzo, Benedict Wong
In order to remove performance impact of having the extra u32 in every
single flowi, this change removes the flowi_xfrm struct, prefering to
take the if_id as a method parameter where needed.
In the inbound direction, if_id is only needed during the
__xfrm_check_policy() function, and the if_id can be determined at that
point based on the skb. As such, xfrmi_decode_session() is only called
with the skb in __xfrm_check_policy().
In the outbound direction, the only place where if_id is needed is the
xfrm_lookup() call in xfrmi_xmit2(). With this change, the if_id is
directly passed into the xfrm_lookup_with_ifid() call. All existing
callers can still call xfrm_lookup(), which uses a default if_id of 0.
This change does not change any behavior of XFRMIs except for improving
overall system performance via flowi size reduction.
This change has been tested against the Android Kernel Networking Tests:
https://android.googlesource.com/kernel/tests/+/master/net/test
Signed-off-by: Benedict Wong <benedictwong@google.com>
---
include/net/dst.h | 14 ++++++
include/net/flow.h | 7 ---
include/net/xfrm.h | 2 +-
net/xfrm/xfrm_interface.c | 4 +-
net/xfrm/xfrm_policy.c | 98 ++++++++++++++++++++++++++-------------
net/xfrm/xfrm_state.c | 3 +-
6 files changed, 83 insertions(+), 45 deletions(-)
diff --git a/include/net/dst.h b/include/net/dst.h
index b3219cd8a5a1..7f735e76ca73 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -475,6 +475,14 @@ static inline struct dst_entry *xfrm_lookup(struct net *net,
return dst_orig;
}
+static inline struct dst_entry *
+xfrm_lookup_with_ifid(struct net *net, struct dst_entry *dst_orig,
+ const struct flowi *fl, const struct sock *sk,
+ int flags, u32 if_id)
+{
+ return dst_orig;
+}
+
static inline struct dst_entry *xfrm_lookup_route(struct net *net,
struct dst_entry *dst_orig,
const struct flowi *fl,
@@ -494,6 +502,12 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
const struct flowi *fl, const struct sock *sk,
int flags);
+struct dst_entry *xfrm_lookup_with_ifid(struct net *net,
+ struct dst_entry *dst_orig,
+ const struct flowi *fl,
+ const struct sock *sk, int flags,
+ u32 if_id);
+
struct dst_entry *xfrm_lookup_route(struct net *net, struct dst_entry *dst_orig,
const struct flowi *fl, const struct sock *sk,
int flags);
diff --git a/include/net/flow.h b/include/net/flow.h
index 187c9bef672f..cb1205c526ef 100644
--- a/include/net/flow.h
+++ b/include/net/flow.h
@@ -26,10 +26,6 @@ struct flowi_tunnel {
__be64 tun_id;
};
-struct flowi_xfrm {
- __u32 if_id;
-};
-
struct flowi_common {
int flowic_oif;
int flowic_iif;
@@ -43,7 +39,6 @@ struct flowi_common {
#define FLOWI_FLAG_SKIP_NH_OIF 0x04
__u32 flowic_secid;
struct flowi_tunnel flowic_tun_key;
- struct flowi_xfrm xfrm;
kuid_t flowic_uid;
};
@@ -115,7 +110,6 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif,
fl4->flowi4_flags = flags;
fl4->flowi4_secid = 0;
fl4->flowi4_tun_key.tun_id = 0;
- fl4->flowi4_xfrm.if_id = 0;
fl4->flowi4_uid = uid;
fl4->daddr = daddr;
fl4->saddr = saddr;
@@ -193,7 +187,6 @@ struct flowi {
#define flowi_secid u.__fl_common.flowic_secid
#define flowi_tun_key u.__fl_common.flowic_tun_key
#define flowi_uid u.__fl_common.flowic_uid
-#define flowi_xfrm u.__fl_common.xfrm
} __attribute__((__aligned__(BITS_PER_LONG/8)));
static inline struct flowi *flowi4_to_flowi(struct flowi4 *fl4)
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index a5378613a49c..0721d899ce00 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -1557,7 +1557,7 @@ struct xfrm_state *xfrm_state_find(const xfrm_address_t *daddr,
const struct flowi *fl,
struct xfrm_tmpl *tmpl,
struct xfrm_policy *pol, int *err,
- unsigned short family);
+ unsigned short family, u32 if_id);
struct xfrm_state *xfrm_stateonly_find(struct net *net, u32 mark, u32 if_id,
xfrm_address_t *daddr,
xfrm_address_t *saddr,
diff --git a/net/xfrm/xfrm_interface.c b/net/xfrm/xfrm_interface.c
index 31cb1c7e3881..ccfe18d67e98 100644
--- a/net/xfrm/xfrm_interface.c
+++ b/net/xfrm/xfrm_interface.c
@@ -307,10 +307,8 @@ xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
if (!dst)
goto tx_err_link_failure;
- fl->flowi_xfrm.if_id = xi->p.if_id;
-
dst_hold(dst);
- dst = xfrm_lookup(xi->net, dst, fl, NULL, 0);
+ dst = xfrm_lookup_with_ifid(xi->net, dst, fl, NULL, 0, xi->p.if_id);
if (IS_ERR(dst)) {
err = PTR_ERR(dst);
dst = NULL;
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index ef75891450e7..33eef87914f1 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1068,14 +1068,14 @@ EXPORT_SYMBOL(xfrm_policy_walk_done);
*/
static int xfrm_policy_match(const struct xfrm_policy *pol,
const struct flowi *fl,
- u8 type, u16 family, int dir)
+ u8 type, u16 family, int dir, u32 if_id)
{
const struct xfrm_selector *sel = &pol->selector;
int ret = -ESRCH;
bool match;
if (pol->family != family ||
- pol->if_id != fl->flowi_xfrm.if_id ||
+ pol->if_id != if_id ||
(fl->flowi_mark & pol->mark.m) != pol->mark.v ||
pol->type != type)
return ret;
@@ -1090,7 +1090,8 @@ static int xfrm_policy_match(const struct xfrm_policy *pol,
static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
const struct flowi *fl,
- u16 family, u8 dir)
+ u16 family, u8 dir,
+ u32 if_id)
{
int err;
struct xfrm_policy *pol, *ret;
@@ -1114,7 +1115,7 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
priority = ~0U;
ret = NULL;
hlist_for_each_entry_rcu(pol, chain, bydst) {
- err = xfrm_policy_match(pol, fl, type, family, dir);
+ err = xfrm_policy_match(pol, fl, type, family, dir, if_id);
if (err) {
if (err == -ESRCH)
continue;
@@ -1133,7 +1134,7 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
if ((pol->priority >= priority) && ret)
break;
- err = xfrm_policy_match(pol, fl, type, family, dir);
+ err = xfrm_policy_match(pol, fl, type, family, dir, if_id);
if (err) {
if (err == -ESRCH)
continue;
@@ -1158,21 +1159,25 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
return ret;
}
-static struct xfrm_policy *
-xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir)
+static struct xfrm_policy *xfrm_policy_lookup(struct net *net,
+ const struct flowi *fl,
+ u16 family, u8 dir, u32 if_id)
{
#ifdef CONFIG_XFRM_SUB_POLICY
struct xfrm_policy *pol;
- pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_SUB, fl, family, dir);
+ pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_SUB, fl, family,
+ dir, if_id);
if (pol != NULL)
return pol;
#endif
- return xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, dir);
+ return xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family,
+ dir, if_id);
}
static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
- const struct flowi *fl, u16 family)
+ const struct flowi *fl,
+ u16 family, u32 if_id)
{
struct xfrm_policy *pol;
@@ -1191,7 +1196,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
match = xfrm_selector_match(&pol->selector, fl, family);
if (match) {
if ((sk->sk_mark & pol->mark.m) != pol->mark.v ||
- pol->if_id != fl->flowi_xfrm.if_id) {
+ pol->if_id != if_id) {
pol = NULL;
goto out;
}
@@ -1405,7 +1410,8 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, const struct flowi *fl,
}
}
- x = xfrm_state_find(remote, local, fl, tmpl, policy, &error, family);
+ x = xfrm_state_find(remote, local, fl, tmpl, policy, &error,
+ family, policy->if_id);
if (x && x->km.state == XFRM_STATE_VALID) {
xfrm[nx++] = x;
@@ -1708,7 +1714,8 @@ static int xfrm_expand_policies(const struct flowi *fl, u16 family,
pols[1] = xfrm_policy_lookup_bytype(xp_net(pols[0]),
XFRM_POLICY_TYPE_MAIN,
fl, family,
- XFRM_POLICY_OUT);
+ XFRM_POLICY_OUT,
+ pols[0]->if_id);
if (pols[1]) {
if (IS_ERR(pols[1])) {
xfrm_pols_put(pols, *num_pols);
@@ -1942,8 +1949,10 @@ static struct xfrm_dst *xfrm_create_dummy_bundle(struct net *net,
goto out;
}
-static struct xfrm_dst *
-xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, struct xfrm_flo *xflo)
+static struct xfrm_dst *xfrm_bundle_lookup(struct net *net,
+ const struct flowi *fl,
+ u16 family, u8 dir,
+ struct xfrm_flo *xflo, u32 if_id)
{
struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
int num_pols = 0, num_xfrms = 0, err;
@@ -1952,7 +1961,7 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir,
/* Resolve policies to use if we couldn't get them from
* previous cache entry */
num_pols = 1;
- pols[0] = xfrm_policy_lookup(net, fl, family, dir);
+ pols[0] = xfrm_policy_lookup(net, fl, family, dir, if_id);
err = xfrm_expand_policies(fl, family, pols,
&num_pols, &num_xfrms);
if (err < 0)
@@ -2020,14 +2029,19 @@ static struct dst_entry *make_blackhole(struct net *net, u16 family,
return ret;
}
-/* Main function: finds/creates a bundle for given flow.
+/* Finds/creates a bundle for given flow and if_id
*
* At the moment we eat a raw IP route. Mostly to speed up lookups
* on interfaces with disabled IPsec.
+ *
+ * xfrm_lookup uses an if_id of 0 by default, and is provided for
+ * compatibility
*/
-struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
- const struct flowi *fl,
- const struct sock *sk, int flags)
+struct dst_entry *xfrm_lookup_with_ifid(struct net *net,
+ struct dst_entry *dst_orig,
+ const struct flowi *fl,
+ const struct sock *sk,
+ int flags, u32 if_id)
{
struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
struct xfrm_dst *xdst;
@@ -2043,7 +2057,8 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
sk = sk_const_to_full_sk(sk);
if (sk && sk->sk_policy[XFRM_POLICY_OUT]) {
num_pols = 1;
- pols[0] = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl, family);
+ pols[0] = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl, family,
+ if_id);
err = xfrm_expand_policies(fl, family, pols,
&num_pols, &num_xfrms);
if (err < 0)
@@ -2087,7 +2102,7 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
!net->xfrm.policy_count[XFRM_POLICY_OUT])
goto nopol;
- xdst = xfrm_bundle_lookup(net, fl, family, dir, &xflo);
+ xdst = xfrm_bundle_lookup(net, fl, family, dir, &xflo, if_id);
if (xdst == NULL)
goto nopol;
if (IS_ERR(xdst)) {
@@ -2168,6 +2183,19 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
xfrm_pols_put(pols, drop_pols);
return ERR_PTR(err);
}
+EXPORT_SYMBOL(xfrm_lookup_with_ifid);
+
+/* Main function: finds/creates a bundle for given flow.
+ *
+ * At the moment we eat a raw IP route. Mostly to speed up lookups
+ * on interfaces with disabled IPsec.
+ */
+struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
+ const struct flowi *fl, const struct sock *sk,
+ int flags)
+{
+ return xfrm_lookup_with_ifid(net, dst_orig, fl, sk, flags, 0);
+}
EXPORT_SYMBOL(xfrm_lookup);
/* Callers of xfrm_lookup_route() must ensure a call to dst_output().
@@ -2257,19 +2285,12 @@ int __xfrm_decode_session(struct sk_buff *skb, struct flowi *fl,
unsigned int family, int reverse)
{
const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
- const struct xfrm_if_cb *ifcb = xfrm_if_get_cb();
- struct xfrm_if *xi;
int err;
if (unlikely(afinfo == NULL))
return -EAFNOSUPPORT;
afinfo->decode_session(skb, fl, reverse);
- if (ifcb) {
- xi = ifcb->decode_session(skb);
- if (xi)
- fl->flowi_xfrm.if_id = xi->p.if_id;
- }
err = security_xfrm_decode_session(skb, &fl->flowi_secid);
rcu_read_unlock();
@@ -2301,6 +2322,13 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
int reverse;
struct flowi fl;
int xerr_idx = -1;
+ const struct xfrm_if_cb *ifcb;
+ struct xfrm_if *xi;
+ u32 if_id = 0;
+
+ rcu_read_lock();
+ ifcb = xfrm_if_get_cb();
+ rcu_read_unlock();
reverse = dir & ~XFRM_POLICY_MASK;
dir &= XFRM_POLICY_MASK;
@@ -2325,10 +2353,16 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
}
}
+ if (ifcb) {
+ xi = ifcb->decode_session(skb);
+ if (xi)
+ if_id = xi->p.if_id;
+ }
+
pol = NULL;
sk = sk_to_full_sk(sk);
if (sk && sk->sk_policy[dir]) {
- pol = xfrm_sk_policy_lookup(sk, dir, &fl, family);
+ pol = xfrm_sk_policy_lookup(sk, dir, &fl, family, if_id);
if (IS_ERR(pol)) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
return 0;
@@ -2336,7 +2370,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
}
if (!pol)
- pol = xfrm_policy_lookup(net, &fl, family, dir);
+ pol = xfrm_policy_lookup(net, &fl, family, dir, if_id);
if (IS_ERR(pol)) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
@@ -2360,7 +2394,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
if (pols[0]->type != XFRM_POLICY_TYPE_MAIN) {
pols[1] = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN,
&fl, family,
- XFRM_POLICY_IN);
+ XFRM_POLICY_IN, if_id);
if (pols[1]) {
if (IS_ERR(pols[1])) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index e04a510ec992..045c5d525df1 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -930,7 +930,7 @@ struct xfrm_state *
xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
const struct flowi *fl, struct xfrm_tmpl *tmpl,
struct xfrm_policy *pol, int *err,
- unsigned short family)
+ unsigned short family, u32 if_id)
{
static xfrm_address_t saddr_wildcard = { };
struct net *net = xp_net(pol);
@@ -940,7 +940,6 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
int error = 0;
struct xfrm_state *best = NULL;
u32 mark = pol->mark.v & pol->mark.m;
- u32 if_id = fl->flowi_xfrm.if_id;
unsigned short encap_family = tmpl->encap_family;
unsigned int sequence;
struct km_event c;
--
2.18.0.233.g985f88cf7e-goog
^ permalink raw reply related
* [PATCH net-next] pktgen: convert safe uses of strncpy() to strcpy() to avoid string truncation warning
From: Jakub Kicinski @ 2018-07-17 21:32 UTC (permalink / raw)
To: davem; +Cc: oss-drivers, netdev, Jakub Kicinski
GCC 8 complains:
net/core/pktgen.c: In function ‘pktgen_if_write’:
net/core/pktgen.c:1419:4: warning: ‘strncpy’ output may be truncated copying between 0 and 31 bytes from a string of length 127 [-Wstringop-truncation]
strncpy(pkt_dev->src_max, buf, len);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
net/core/pktgen.c:1399:4: warning: ‘strncpy’ output may be truncated copying between 0 and 31 bytes from a string of length 127 [-Wstringop-truncation]
strncpy(pkt_dev->src_min, buf, len);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
net/core/pktgen.c:1290:4: warning: ‘strncpy’ output may be truncated copying between 0 and 31 bytes from a string of length 127 [-Wstringop-truncation]
strncpy(pkt_dev->dst_max, buf, len);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
net/core/pktgen.c:1268:4: warning: ‘strncpy’ output may be truncated copying between 0 and 31 bytes from a string of length 127 [-Wstringop-truncation]
strncpy(pkt_dev->dst_min, buf, len);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There is no bug here, but the code is not perfect either. It copies
sizeof(pkt_dev->/member/) - 1 from user space into buf, and then does
a strcmp(pkt_dev->/member/, buf) hence assuming buf will be null-terminated
and shorter than pkt_dev->/member/ (pkt_dev->/member/ is never
explicitly null-terminated, and strncpy() doesn't have to null-terminate
so the assumption must be on buf). The use of strncpy() without explicit
null-termination looks suspicious. Convert to use straight strcpy().
strncpy() would also null-pad the output, but that's clearly unnecessary
since the author calls memset(pkt_dev->/member/, 0, sizeof(..)); prior
to strncpy(), anyway.
While at it format the code for "dst_min", "dst_max", "src_min" and
"src_max" in the same way by removing extra new lines in one case.
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Jiong Wang <jiong.wang@netronome.com>
---
net/core/pktgen.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 49368e21d228..308ed04984de 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -1265,7 +1265,7 @@ static ssize_t pktgen_if_write(struct file *file,
buf[len] = 0;
if (strcmp(buf, pkt_dev->dst_min) != 0) {
memset(pkt_dev->dst_min, 0, sizeof(pkt_dev->dst_min));
- strncpy(pkt_dev->dst_min, buf, len);
+ strcpy(pkt_dev->dst_min, buf);
pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
pkt_dev->cur_daddr = pkt_dev->daddr_min;
}
@@ -1280,14 +1280,12 @@ static ssize_t pktgen_if_write(struct file *file,
if (len < 0)
return len;
-
if (copy_from_user(buf, &user_buffer[i], len))
return -EFAULT;
-
buf[len] = 0;
if (strcmp(buf, pkt_dev->dst_max) != 0) {
memset(pkt_dev->dst_max, 0, sizeof(pkt_dev->dst_max));
- strncpy(pkt_dev->dst_max, buf, len);
+ strcpy(pkt_dev->dst_max, buf);
pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
pkt_dev->cur_daddr = pkt_dev->daddr_max;
}
@@ -1396,7 +1394,7 @@ static ssize_t pktgen_if_write(struct file *file,
buf[len] = 0;
if (strcmp(buf, pkt_dev->src_min) != 0) {
memset(pkt_dev->src_min, 0, sizeof(pkt_dev->src_min));
- strncpy(pkt_dev->src_min, buf, len);
+ strcpy(pkt_dev->src_min, buf);
pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
pkt_dev->cur_saddr = pkt_dev->saddr_min;
}
@@ -1416,7 +1414,7 @@ static ssize_t pktgen_if_write(struct file *file,
buf[len] = 0;
if (strcmp(buf, pkt_dev->src_max) != 0) {
memset(pkt_dev->src_max, 0, sizeof(pkt_dev->src_max));
- strncpy(pkt_dev->src_max, buf, len);
+ strcpy(pkt_dev->src_max, buf);
pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
pkt_dev->cur_saddr = pkt_dev->saddr_max;
}
--
2.17.1
^ permalink raw reply related
* [PATCH net-next v3 10/10] r8169: don't read chip phy status register
From: Heiner Kallweit @ 2018-07-17 20:52 UTC (permalink / raw)
To: David Miller, Florian Fainelli, Andrew Lunn,
Realtek linux nic maintainers
Cc: netdev@vger.kernel.org
In-Reply-To: <a33329d7-96ba-314e-3865-b9800ab14f87@gmail.com>
Instead of accessing the PHYstatus register we can use the information
phylib stores in the phy_device structure.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/ethernet/realtek/r8169.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 2a47f9d2..9634e67e 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -1428,18 +1428,19 @@ static void rtl8169_irq_mask_and_ack(struct rtl8169_private *tp)
static void rtl_link_chg_patch(struct rtl8169_private *tp)
{
struct net_device *dev = tp->dev;
+ struct phy_device *phydev = dev->phydev;
if (!netif_running(dev))
return;
if (tp->mac_version == RTL_GIGA_MAC_VER_34 ||
tp->mac_version == RTL_GIGA_MAC_VER_38) {
- if (RTL_R8(tp, PHYstatus) & _1000bpsF) {
+ if (phydev->speed == SPEED_1000) {
rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x00000011,
ERIAR_EXGMAC);
rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005,
ERIAR_EXGMAC);
- } else if (RTL_R8(tp, PHYstatus) & _100bps) {
+ } else if (phydev->speed == SPEED_100) {
rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x0000001f,
ERIAR_EXGMAC);
rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005,
@@ -1457,7 +1458,7 @@ static void rtl_link_chg_patch(struct rtl8169_private *tp)
ERIAR_EXGMAC);
} else if (tp->mac_version == RTL_GIGA_MAC_VER_35 ||
tp->mac_version == RTL_GIGA_MAC_VER_36) {
- if (RTL_R8(tp, PHYstatus) & _1000bpsF) {
+ if (phydev->speed == SPEED_1000) {
rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x00000011,
ERIAR_EXGMAC);
rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005,
@@ -1469,7 +1470,7 @@ static void rtl_link_chg_patch(struct rtl8169_private *tp)
ERIAR_EXGMAC);
}
} else if (tp->mac_version == RTL_GIGA_MAC_VER_37) {
- if (RTL_R8(tp, PHYstatus) & _10bps) {
+ if (phydev->speed == SPEED_10) {
rtl_eri_write(tp, 0x1d0, ERIAR_MASK_0011, 0x4d02,
ERIAR_EXGMAC);
rtl_eri_write(tp, 0x1dc, ERIAR_MASK_0011, 0x0060,
--
2.18.0
^ permalink raw reply related
* [PATCH net-next v3 09/10] r8169: remove mii_if_info member from struct rtl8169_private
From: Heiner Kallweit @ 2018-07-17 20:52 UTC (permalink / raw)
To: David Miller, Florian Fainelli, Andrew Lunn,
Realtek linux nic maintainers
Cc: netdev@vger.kernel.org
In-Reply-To: <a33329d7-96ba-314e-3865-b9800ab14f87@gmail.com>
The only remaining usage of the struct mii_if_info member is to store the
information whether the chip is GMII-capable. So we can replace it with
a simple flag.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/realtek/r8169.c | 38 +++++-----------------------
1 file changed, 7 insertions(+), 31 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 5c9d35eb..2a47f9d2 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -15,7 +15,6 @@
#include <linux/etherdevice.h>
#include <linux/delay.h>
#include <linux/ethtool.h>
-#include <linux/mii.h>
#include <linux/phy.h>
#include <linux/if_vlan.h>
#include <linux/crc32.h>
@@ -754,7 +753,7 @@ struct rtl8169_private {
struct work_struct work;
} wk;
- struct mii_if_info mii;
+ unsigned supports_gmii:1;
struct mii_bus *mii_bus;
dma_addr_t counters_phys_addr;
struct rtl8169_counters *counters;
@@ -1106,21 +1105,6 @@ static void rtl_w0w1_phy(struct rtl8169_private *tp, int reg_addr, int p, int m)
rtl_writephy(tp, reg_addr, (val & ~m) | p);
}
-static void rtl_mdio_write(struct net_device *dev, int phy_id, int location,
- int val)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- rtl_writephy(tp, location, val);
-}
-
-static int rtl_mdio_read(struct net_device *dev, int phy_id, int location)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- return rtl_readphy(tp, location);
-}
-
DECLARE_RTL_COND(rtl_ephyar_cond)
{
return RTL_R32(tp, EPHYAR) & EPHYAR_FLAG;
@@ -2246,15 +2230,15 @@ static void rtl8169_get_mac_version(struct rtl8169_private *tp,
"unknown MAC, using family default\n");
tp->mac_version = default_version;
} else if (tp->mac_version == RTL_GIGA_MAC_VER_42) {
- tp->mac_version = tp->mii.supports_gmii ?
+ tp->mac_version = tp->supports_gmii ?
RTL_GIGA_MAC_VER_42 :
RTL_GIGA_MAC_VER_43;
} else if (tp->mac_version == RTL_GIGA_MAC_VER_45) {
- tp->mac_version = tp->mii.supports_gmii ?
+ tp->mac_version = tp->supports_gmii ?
RTL_GIGA_MAC_VER_45 :
RTL_GIGA_MAC_VER_47;
} else if (tp->mac_version == RTL_GIGA_MAC_VER_46) {
- tp->mac_version = tp->mii.supports_gmii ?
+ tp->mac_version = tp->supports_gmii ?
RTL_GIGA_MAC_VER_46 :
RTL_GIGA_MAC_VER_48;
}
@@ -6686,7 +6670,7 @@ static int r8169_phy_connect(struct rtl8169_private *tp)
phy_interface_t phy_mode;
int ret;
- phy_mode = tp->mii.supports_gmii ? PHY_INTERFACE_MODE_GMII :
+ phy_mode = tp->supports_gmii ? PHY_INTERFACE_MODE_GMII :
PHY_INTERFACE_MODE_MII;
ret = phy_connect_direct(tp->dev, phydev, r8169_phylink_handler,
@@ -6694,7 +6678,7 @@ static int r8169_phy_connect(struct rtl8169_private *tp)
if (ret)
return ret;
- if (!tp->mii.supports_gmii)
+ if (!tp->supports_gmii)
phy_set_max_speed(phydev, SPEED_100);
/* Ensure to advertise everything, incl. pause */
@@ -7303,7 +7287,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
const struct rtl_cfg_info *cfg = rtl_cfg_infos + ent->driver_data;
struct rtl8169_private *tp;
- struct mii_if_info *mii;
struct net_device *dev;
int chipset, region, i;
int rc;
@@ -7323,14 +7306,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
tp->dev = dev;
tp->pci_dev = pdev;
tp->msg_enable = netif_msg_init(debug.msg_enable, R8169_MSG_DEFAULT);
-
- mii = &tp->mii;
- mii->dev = dev;
- mii->mdio_read = rtl_mdio_read;
- mii->mdio_write = rtl_mdio_write;
- mii->phy_id_mask = 0x1f;
- mii->reg_num_mask = 0x1f;
- mii->supports_gmii = cfg->has_gmii;
+ tp->supports_gmii = cfg->has_gmii;
/* enable device (incl. PCI PM wakeup and hotplug setup) */
rc = pcim_enable_device(pdev);
--
2.18.0
^ permalink raw reply related
* [PATCH net-next v3 08/10] r8169: remove rtl8169_set_speed_xmii
From: Heiner Kallweit @ 2018-07-17 20:52 UTC (permalink / raw)
To: David Miller, Florian Fainelli, Andrew Lunn,
Realtek linux nic maintainers
Cc: netdev@vger.kernel.org
In-Reply-To: <a33329d7-96ba-314e-3865-b9800ab14f87@gmail.com>
We can remove rtl8169_set_speed_xmii() now that phylib handles all this.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
drivers/net/ethernet/realtek/r8169.c | 90 ----------------------------
1 file changed, 90 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 402674cf..5c9d35eb 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -1663,89 +1663,6 @@ static int rtl8169_get_regs_len(struct net_device *dev)
return R8169_REGS_SIZE;
}
-static int rtl8169_set_speed_xmii(struct net_device *dev,
- u8 autoneg, u16 speed, u8 duplex, u32 adv)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- int giga_ctrl, bmcr;
- int rc = -EINVAL;
-
- rtl_writephy(tp, 0x1f, 0x0000);
-
- if (autoneg == AUTONEG_ENABLE) {
- int auto_nego;
-
- auto_nego = rtl_readphy(tp, MII_ADVERTISE);
- auto_nego &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL |
- ADVERTISE_100HALF | ADVERTISE_100FULL);
-
- if (adv & ADVERTISED_10baseT_Half)
- auto_nego |= ADVERTISE_10HALF;
- if (adv & ADVERTISED_10baseT_Full)
- auto_nego |= ADVERTISE_10FULL;
- if (adv & ADVERTISED_100baseT_Half)
- auto_nego |= ADVERTISE_100HALF;
- if (adv & ADVERTISED_100baseT_Full)
- auto_nego |= ADVERTISE_100FULL;
-
- auto_nego |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
-
- giga_ctrl = rtl_readphy(tp, MII_CTRL1000);
- giga_ctrl &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
-
- /* The 8100e/8101e/8102e do Fast Ethernet only. */
- if (tp->mii.supports_gmii) {
- if (adv & ADVERTISED_1000baseT_Half)
- giga_ctrl |= ADVERTISE_1000HALF;
- if (adv & ADVERTISED_1000baseT_Full)
- giga_ctrl |= ADVERTISE_1000FULL;
- } else if (adv & (ADVERTISED_1000baseT_Half |
- ADVERTISED_1000baseT_Full)) {
- netif_info(tp, link, dev,
- "PHY does not support 1000Mbps\n");
- goto out;
- }
-
- bmcr = BMCR_ANENABLE | BMCR_ANRESTART;
-
- rtl_writephy(tp, MII_ADVERTISE, auto_nego);
- rtl_writephy(tp, MII_CTRL1000, giga_ctrl);
- } else {
- if (speed == SPEED_10)
- bmcr = 0;
- else if (speed == SPEED_100)
- bmcr = BMCR_SPEED100;
- else
- goto out;
-
- if (duplex == DUPLEX_FULL)
- bmcr |= BMCR_FULLDPLX;
- }
-
- rtl_writephy(tp, MII_BMCR, bmcr);
-
- if (tp->mac_version == RTL_GIGA_MAC_VER_02 ||
- tp->mac_version == RTL_GIGA_MAC_VER_03) {
- if ((speed == SPEED_100) && (autoneg != AUTONEG_ENABLE)) {
- rtl_writephy(tp, 0x17, 0x2138);
- rtl_writephy(tp, 0x0e, 0x0260);
- } else {
- rtl_writephy(tp, 0x17, 0x2108);
- rtl_writephy(tp, 0x0e, 0x0000);
- }
- }
-
- rc = 0;
-out:
- return rc;
-}
-
-static int rtl8169_set_speed(struct net_device *dev,
- u8 autoneg, u16 speed, u8 duplex, u32 advertising)
-{
- return rtl8169_set_speed_xmii(dev, autoneg, speed, duplex, advertising);
-}
-
static netdev_features_t rtl8169_fix_features(struct net_device *dev,
netdev_features_t features)
{
@@ -4237,13 +4154,6 @@ static void rtl8169_init_phy(struct net_device *dev, struct rtl8169_private *tp)
phy_speed_up(dev->phydev);
genphy_soft_reset(dev->phydev);
-
- rtl8169_set_speed(dev, AUTONEG_ENABLE, SPEED_1000, DUPLEX_FULL,
- ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
- ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full |
- (tp->mii.supports_gmii ?
- ADVERTISED_1000baseT_Half |
- ADVERTISED_1000baseT_Full : 0));
}
static void rtl_rar_set(struct rtl8169_private *tp, u8 *addr)
--
2.18.0
^ permalink raw reply related
* [PATCH net-next v3 07/10] r8169: use phy_speed_down / phy_speed_up
From: Heiner Kallweit @ 2018-07-17 20:51 UTC (permalink / raw)
To: David Miller, Florian Fainelli, Andrew Lunn,
Realtek linux nic maintainers
Cc: netdev@vger.kernel.org
In-Reply-To: <a33329d7-96ba-314e-3865-b9800ab14f87@gmail.com>
Use new phylib functions phy_speed_down() and phy_speed_up().
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
drivers/net/ethernet/realtek/r8169.c | 29 ++++------------------------
1 file changed, 4 insertions(+), 25 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 1150c3f5..402674cf 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -4233,6 +4233,9 @@ static void rtl8169_init_phy(struct net_device *dev, struct rtl8169_private *tp)
rtl_writephy(tp, 0x0b, 0x0000); //w 0x0b 15 0 0
}
+ /* We may have called phy_speed_down before */
+ phy_speed_up(dev->phydev);
+
genphy_soft_reset(dev->phydev);
rtl8169_set_speed(dev, AUTONEG_ENABLE, SPEED_1000, DUPLEX_FULL,
@@ -4316,30 +4319,6 @@ static void rtl_init_mdio_ops(struct rtl8169_private *tp)
}
}
-static void rtl_speed_down(struct rtl8169_private *tp)
-{
- u32 adv;
- int lpa;
-
- rtl_writephy(tp, 0x1f, 0x0000);
- lpa = rtl_readphy(tp, MII_LPA);
-
- if (lpa & (LPA_10HALF | LPA_10FULL))
- adv = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full;
- else if (lpa & (LPA_100HALF | LPA_100FULL))
- adv = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
- ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full;
- else
- adv = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
- ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full |
- (tp->mii.supports_gmii ?
- ADVERTISED_1000baseT_Half |
- ADVERTISED_1000baseT_Full : 0);
-
- rtl8169_set_speed(tp->dev, AUTONEG_ENABLE, SPEED_1000, DUPLEX_FULL,
- adv);
-}
-
static void rtl_wol_suspend_quirk(struct rtl8169_private *tp)
{
switch (tp->mac_version) {
@@ -4364,7 +4343,7 @@ static bool rtl_wol_pll_power_down(struct rtl8169_private *tp)
if (!netif_running(tp->dev) || !__rtl8169_get_wol(tp))
return false;
- rtl_speed_down(tp);
+ phy_speed_down(tp->dev->phydev, false);
rtl_wol_suspend_quirk(tp);
return true;
--
2.18.0
^ permalink raw reply related
* [PATCH net-next v3 06/10] r8169: use phy_mii_ioctl
From: Heiner Kallweit @ 2018-07-17 20:51 UTC (permalink / raw)
To: David Miller, Florian Fainelli, Andrew Lunn,
Realtek linux nic maintainers
Cc: netdev@vger.kernel.org
In-Reply-To: <a33329d7-96ba-314e-3865-b9800ab14f87@gmail.com>
Switch to using phy_mii_ioctl().
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/ethernet/realtek/r8169.c | 25 +++----------------------
1 file changed, 3 insertions(+), 22 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 559f1e4d..1150c3f5 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -4283,31 +4283,12 @@ static int rtl_set_mac_address(struct net_device *dev, void *p)
return 0;
}
-static int rtl_xmii_ioctl(struct rtl8169_private *tp,
- struct mii_ioctl_data *data, int cmd)
-{
- switch (cmd) {
- case SIOCGMIIPHY:
- data->phy_id = 32; /* Internal PHY */
- return 0;
-
- case SIOCGMIIREG:
- data->val_out = rtl_readphy(tp, data->reg_num & 0x1f);
- return 0;
-
- case SIOCSMIIREG:
- rtl_writephy(tp, data->reg_num & 0x1f, data->val_in);
- return 0;
- }
- return -EOPNOTSUPP;
-}
-
static int rtl8169_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- struct rtl8169_private *tp = netdev_priv(dev);
- struct mii_ioctl_data *data = if_mii(ifr);
+ if (!netif_running(dev))
+ return -ENODEV;
- return netif_running(dev) ? rtl_xmii_ioctl(tp, data, cmd) : -ENODEV;
+ return phy_mii_ioctl(dev->phydev, ifr, cmd);
}
static void rtl_init_mdio_ops(struct rtl8169_private *tp)
--
2.18.0
^ permalink raw reply related
* [PATCH net-next v3 05/10] r8169: use phy_ethtool_nway_reset
From: Heiner Kallweit @ 2018-07-17 20:51 UTC (permalink / raw)
To: David Miller, Florian Fainelli, Andrew Lunn,
Realtek linux nic maintainers
Cc: netdev@vger.kernel.org
In-Reply-To: <a33329d7-96ba-314e-3865-b9800ab14f87@gmail.com>
Switch to using phy_ethtool_nway_reset().
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/ethernet/realtek/Kconfig | 1 -
drivers/net/ethernet/realtek/r8169.c | 9 +--------
2 files changed, 1 insertion(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/realtek/Kconfig b/drivers/net/ethernet/realtek/Kconfig
index 7fb1af1f..e1cd934c 100644
--- a/drivers/net/ethernet/realtek/Kconfig
+++ b/drivers/net/ethernet/realtek/Kconfig
@@ -100,7 +100,6 @@ config R8169
select FW_LOADER
select CRC32
select PHYLIB
- select MII
---help---
Say Y here if you have a Realtek 8169 PCI Gigabit Ethernet adapter.
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index cd2605f7..559f1e4d 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -1984,13 +1984,6 @@ static void rtl8169_get_strings(struct net_device *dev, u32 stringset, u8 *data)
}
}
-static int rtl8169_nway_reset(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- return mii_nway_restart(&tp->mii);
-}
-
/*
* Interrupt coalescing
*
@@ -2221,7 +2214,7 @@ static const struct ethtool_ops rtl8169_ethtool_ops = {
.get_sset_count = rtl8169_get_sset_count,
.get_ethtool_stats = rtl8169_get_ethtool_stats,
.get_ts_info = ethtool_op_get_ts_info,
- .nway_reset = rtl8169_nway_reset,
+ .nway_reset = phy_ethtool_nway_reset,
.get_link_ksettings = phy_ethtool_get_link_ksettings,
.set_link_ksettings = phy_ethtool_set_link_ksettings,
};
--
2.18.0
^ permalink raw reply related
* [PATCH net-next v3 04/10] r8169: use phy_ethtool_(g|s)et_link_ksettings
From: Heiner Kallweit @ 2018-07-17 20:51 UTC (permalink / raw)
To: David Miller, Florian Fainelli, Andrew Lunn,
Realtek linux nic maintainers
Cc: netdev@vger.kernel.org
In-Reply-To: <a33329d7-96ba-314e-3865-b9800ab14f87@gmail.com>
Use phy_ethtool_(g|s)et_link_ksettings() for the respective ethtool_ops
callbacks.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/ethernet/realtek/r8169.c | 35 +++-------------------------
1 file changed, 3 insertions(+), 32 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 62cf6f71..cd2605f7 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -1809,35 +1809,6 @@ static void rtl8169_rx_vlan_tag(struct RxDesc *desc, struct sk_buff *skb)
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & 0xffff));
}
-static int rtl8169_get_link_ksettings(struct net_device *dev,
- struct ethtool_link_ksettings *cmd)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- mii_ethtool_get_link_ksettings(&tp->mii, cmd);
-
- return 0;
-}
-
-static int rtl8169_set_link_ksettings(struct net_device *dev,
- const struct ethtool_link_ksettings *cmd)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- int rc;
- u32 advertising;
-
- if (!ethtool_convert_link_mode_to_legacy_u32(&advertising,
- cmd->link_modes.advertising))
- return -EINVAL;
-
- rtl_lock_work(tp);
- rc = rtl8169_set_speed(dev, cmd->base.autoneg, cmd->base.speed,
- cmd->base.duplex, advertising);
- rtl_unlock_work(tp);
-
- return rc;
-}
-
static void rtl8169_get_regs(struct net_device *dev, struct ethtool_regs *regs,
void *p)
{
@@ -2092,7 +2063,7 @@ static const struct rtl_coalesce_info *rtl_coalesce_info(struct net_device *dev)
const struct rtl_coalesce_info *ci;
int rc;
- rc = rtl8169_get_link_ksettings(dev, &ecmd);
+ rc = phy_ethtool_get_link_ksettings(dev, &ecmd);
if (rc < 0)
return ERR_PTR(rc);
@@ -2251,8 +2222,8 @@ static const struct ethtool_ops rtl8169_ethtool_ops = {
.get_ethtool_stats = rtl8169_get_ethtool_stats,
.get_ts_info = ethtool_op_get_ts_info,
.nway_reset = rtl8169_nway_reset,
- .get_link_ksettings = rtl8169_get_link_ksettings,
- .set_link_ksettings = rtl8169_set_link_ksettings,
+ .get_link_ksettings = phy_ethtool_get_link_ksettings,
+ .set_link_ksettings = phy_ethtool_set_link_ksettings,
};
static void rtl8169_get_mac_version(struct rtl8169_private *tp,
--
2.18.0
^ permalink raw reply related
* [PATCH net-next v3 03/10] r8169: replace open-coded PHY soft reset with genphy_soft_reset
From: Heiner Kallweit @ 2018-07-17 20:51 UTC (permalink / raw)
To: David Miller, Florian Fainelli, Andrew Lunn,
Realtek linux nic maintainers
Cc: netdev@vger.kernel.org
In-Reply-To: <a33329d7-96ba-314e-3865-b9800ab14f87@gmail.com>
Use genphy_soft_reset() instead of open-coding a PHY soft reset. We have
to do an explicit PHY soft reset because some chips use the genphy driver
which uses a no-op as soft_reset callback.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/ethernet/realtek/r8169.c | 27 +--------------------------
1 file changed, 1 insertion(+), 26 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 1f295042..62cf6f71 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -1441,19 +1441,6 @@ static void rtl8169_irq_mask_and_ack(struct rtl8169_private *tp)
RTL_R8(tp, ChipCmd);
}
-static unsigned int rtl8169_xmii_reset_pending(struct rtl8169_private *tp)
-{
- return rtl_readphy(tp, MII_BMCR) & BMCR_RESET;
-}
-
-static void rtl8169_xmii_reset_enable(struct rtl8169_private *tp)
-{
- unsigned int val;
-
- val = rtl_readphy(tp, MII_BMCR) | BMCR_RESET;
- rtl_writephy(tp, MII_BMCR, val & 0xffff);
-}
-
static void rtl_link_chg_patch(struct rtl8169_private *tp)
{
struct net_device *dev = tp->dev;
@@ -4252,18 +4239,6 @@ static void rtl_schedule_task(struct rtl8169_private *tp, enum rtl_flag flag)
schedule_work(&tp->wk.work);
}
-DECLARE_RTL_COND(rtl_phy_reset_cond)
-{
- return rtl8169_xmii_reset_pending(tp);
-}
-
-static void rtl8169_phy_reset(struct net_device *dev,
- struct rtl8169_private *tp)
-{
- rtl8169_xmii_reset_enable(tp);
- rtl_msleep_loop_wait_low(tp, &rtl_phy_reset_cond, 1, 100);
-}
-
static bool rtl_tbi_enabled(struct rtl8169_private *tp)
{
return (tp->mac_version == RTL_GIGA_MAC_VER_01) &&
@@ -4294,7 +4269,7 @@ static void rtl8169_init_phy(struct net_device *dev, struct rtl8169_private *tp)
rtl_writephy(tp, 0x0b, 0x0000); //w 0x0b 15 0 0
}
- rtl8169_phy_reset(dev, tp);
+ genphy_soft_reset(dev->phydev);
rtl8169_set_speed(dev, AUTONEG_ENABLE, SPEED_1000, DUPLEX_FULL,
ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
--
2.18.0
^ permalink raw reply related
* [PATCH net-next v3 02/10] r8169: use phy_resume/phy_suspend
From: Heiner Kallweit @ 2018-07-17 20:51 UTC (permalink / raw)
To: David Miller, Florian Fainelli, Andrew Lunn,
Realtek linux nic maintainers
Cc: netdev@vger.kernel.org
In-Reply-To: <a33329d7-96ba-314e-3865-b9800ab14f87@gmail.com>
Use phy_resume() / phy_suspend() instead of open coding this functionality.
The chip version specific differences are handled by the respective PHY
drivers.
The call to r8168_phy_power_down() in r8168_pll_power_down() can be
removed because phylib takes care now. The relevant scenarios are:
- rtl8169_close(): phy_disconnect() powers down PHY
- suspend: mdio_bus_phy_suspend() takes care
- runtime-suspend: WoL is active, don't suspend PHY
- rtl_shutdown(): no need to power down PHY
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
v2:
- no changes
v3:
- remove unneeded call to r8168_phy_power_down()
---
drivers/net/ethernet/realtek/r8169.c | 50 ++++------------------------
1 file changed, 6 insertions(+), 44 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index cf21c483..1f295042 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -4450,47 +4450,6 @@ static bool rtl_wol_pll_power_down(struct rtl8169_private *tp)
return true;
}
-static void r8168_phy_power_up(struct rtl8169_private *tp)
-{
- rtl_writephy(tp, 0x1f, 0x0000);
- switch (tp->mac_version) {
- case RTL_GIGA_MAC_VER_11:
- case RTL_GIGA_MAC_VER_12:
- case RTL_GIGA_MAC_VER_17 ... RTL_GIGA_MAC_VER_28:
- case RTL_GIGA_MAC_VER_31:
- rtl_writephy(tp, 0x0e, 0x0000);
- break;
- default:
- break;
- }
- rtl_writephy(tp, MII_BMCR, BMCR_ANENABLE);
-
- /* give MAC/PHY some time to resume */
- msleep(20);
-}
-
-static void r8168_phy_power_down(struct rtl8169_private *tp)
-{
- rtl_writephy(tp, 0x1f, 0x0000);
- switch (tp->mac_version) {
- case RTL_GIGA_MAC_VER_32:
- case RTL_GIGA_MAC_VER_33:
- case RTL_GIGA_MAC_VER_40:
- case RTL_GIGA_MAC_VER_41:
- rtl_writephy(tp, MII_BMCR, BMCR_ANENABLE | BMCR_PDOWN);
- break;
-
- case RTL_GIGA_MAC_VER_11:
- case RTL_GIGA_MAC_VER_12:
- case RTL_GIGA_MAC_VER_17 ... RTL_GIGA_MAC_VER_28:
- case RTL_GIGA_MAC_VER_31:
- rtl_writephy(tp, 0x0e, 0x0200);
- default:
- rtl_writephy(tp, MII_BMCR, BMCR_PDOWN);
- break;
- }
-}
-
static void r8168_pll_power_down(struct rtl8169_private *tp)
{
if (r8168_check_dash(tp))
@@ -4503,8 +4462,6 @@ static void r8168_pll_power_down(struct rtl8169_private *tp)
if (rtl_wol_pll_power_down(tp))
return;
- r8168_phy_power_down(tp);
-
switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_33:
case RTL_GIGA_MAC_VER_37:
@@ -4556,7 +4513,9 @@ static void r8168_pll_power_up(struct rtl8169_private *tp)
break;
}
- r8168_phy_power_up(tp);
+ phy_resume(tp->dev->phydev);
+ /* give MAC/PHY some time to resume */
+ msleep(20);
}
static void rtl_pll_power_down(struct rtl8169_private *tp)
@@ -7470,6 +7429,9 @@ static int r8169_mdio_register(struct rtl8169_private *tp)
return -ENODEV;
}
+ /* PHY will be woken up in rtl_open() */
+ phy_suspend(phydev);
+
tp->mii_bus = new_bus;
return 0;
--
2.18.0
^ permalink raw reply related
* [PATCH net-next v3 01/10] r8169: add basic phylib support
From: Heiner Kallweit @ 2018-07-17 20:51 UTC (permalink / raw)
To: David Miller, Florian Fainelli, Andrew Lunn,
Realtek linux nic maintainers
Cc: netdev@vger.kernel.org
In-Reply-To: <a33329d7-96ba-314e-3865-b9800ab14f87@gmail.com>
Add basic phylib support to r8169. All now unneeded old PHY handling code
will be removed in subsequent patches.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
v2:
- return error in mdio ops if phyaddr > 0
- advertise pause modes
v3:
- return ENODEV in mdio ops if phyaddr > 0
- consider other minor review comments
---
drivers/net/ethernet/realtek/Kconfig | 1 +
drivers/net/ethernet/realtek/r8169.c | 159 +++++++++++++++++++++------
2 files changed, 128 insertions(+), 32 deletions(-)
diff --git a/drivers/net/ethernet/realtek/Kconfig b/drivers/net/ethernet/realtek/Kconfig
index 7c69f4c8..7fb1af1f 100644
--- a/drivers/net/ethernet/realtek/Kconfig
+++ b/drivers/net/ethernet/realtek/Kconfig
@@ -99,6 +99,7 @@ config R8169
depends on PCI
select FW_LOADER
select CRC32
+ select PHYLIB
select MII
---help---
Say Y here if you have a Realtek 8169 PCI Gigabit Ethernet adapter.
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index e5f6f8c5..cf21c483 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -16,6 +16,7 @@
#include <linux/delay.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
+#include <linux/phy.h>
#include <linux/if_vlan.h>
#include <linux/crc32.h>
#include <linux/in.h>
@@ -754,6 +755,7 @@ struct rtl8169_private {
} wk;
struct mii_if_info mii;
+ struct mii_bus *mii_bus;
dma_addr_t counters_phys_addr;
struct rtl8169_counters *counters;
struct rtl8169_tc_offsets tc_offset;
@@ -1444,11 +1446,6 @@ static unsigned int rtl8169_xmii_reset_pending(struct rtl8169_private *tp)
return rtl_readphy(tp, MII_BMCR) & BMCR_RESET;
}
-static unsigned int rtl8169_xmii_link_ok(struct rtl8169_private *tp)
-{
- return RTL_R8(tp, PHYstatus) & LinkStatus;
-}
-
static void rtl8169_xmii_reset_enable(struct rtl8169_private *tp)
{
unsigned int val;
@@ -1513,25 +1510,6 @@ static void rtl_link_chg_patch(struct rtl8169_private *tp)
}
}
-static void rtl8169_check_link_status(struct net_device *dev,
- struct rtl8169_private *tp)
-{
- struct device *d = tp_to_dev(tp);
-
- if (rtl8169_xmii_link_ok(tp)) {
- rtl_link_chg_patch(tp);
- /* This is to cancel a scheduled suspend if there's one. */
- pm_request_resume(d);
- netif_carrier_on(dev);
- if (net_ratelimit())
- netif_info(tp, ifup, dev, "link up\n");
- } else {
- netif_carrier_off(dev);
- netif_info(tp, ifdown, dev, "link down\n");
- pm_runtime_idle(d);
- }
-}
-
#define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)
static u32 __rtl8169_get_wol(struct rtl8169_private *tp)
@@ -6221,7 +6199,6 @@ static void rtl_reset_work(struct rtl8169_private *tp)
napi_enable(&tp->napi);
rtl_hw_start(tp);
netif_wake_queue(dev);
- rtl8169_check_link_status(dev, tp);
}
static void rtl8169_tx_timeout(struct net_device *dev)
@@ -6838,7 +6815,7 @@ static void rtl_slow_event_work(struct rtl8169_private *tp)
rtl8169_pcierr_interrupt(dev);
if (status & LinkChg)
- rtl8169_check_link_status(dev, tp);
+ phy_mac_interrupt(dev->phydev);
rtl_irq_enable_all(tp);
}
@@ -6920,10 +6897,52 @@ static void rtl8169_rx_missed(struct net_device *dev)
RTL_W32(tp, RxMissed, 0);
}
+static void r8169_phylink_handler(struct net_device *ndev)
+{
+ struct rtl8169_private *tp = netdev_priv(ndev);
+
+ if (netif_carrier_ok(ndev)) {
+ rtl_link_chg_patch(tp);
+ pm_request_resume(&tp->pci_dev->dev);
+ } else {
+ pm_runtime_idle(&tp->pci_dev->dev);
+ }
+
+ if (net_ratelimit())
+ phy_print_status(ndev->phydev);
+}
+
+static int r8169_phy_connect(struct rtl8169_private *tp)
+{
+ struct phy_device *phydev = mdiobus_get_phy(tp->mii_bus, 0);
+ phy_interface_t phy_mode;
+ int ret;
+
+ phy_mode = tp->mii.supports_gmii ? PHY_INTERFACE_MODE_GMII :
+ PHY_INTERFACE_MODE_MII;
+
+ ret = phy_connect_direct(tp->dev, phydev, r8169_phylink_handler,
+ phy_mode);
+ if (ret)
+ return ret;
+
+ if (!tp->mii.supports_gmii)
+ phy_set_max_speed(phydev, SPEED_100);
+
+ /* Ensure to advertise everything, incl. pause */
+ phydev->advertising = phydev->supported;
+
+ phy_attached_info(phydev);
+
+ return 0;
+}
+
static void rtl8169_down(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
+ phy_stop(dev->phydev);
+
napi_disable(&tp->napi);
netif_stop_queue(dev);
@@ -6963,6 +6982,8 @@ static int rtl8169_close(struct net_device *dev)
cancel_work_sync(&tp->wk.work);
+ phy_disconnect(dev->phydev);
+
pci_free_irq(pdev, 0, tp);
dma_free_coherent(&pdev->dev, R8169_RX_RING_BYTES, tp->RxDescArray,
@@ -7023,6 +7044,10 @@ static int rtl_open(struct net_device *dev)
if (retval < 0)
goto err_release_fw_2;
+ retval = r8169_phy_connect(tp);
+ if (retval)
+ goto err_free_irq;
+
rtl_lock_work(tp);
set_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags);
@@ -7038,16 +7063,17 @@ static int rtl_open(struct net_device *dev)
if (!rtl8169_init_counter_offsets(tp))
netif_warn(tp, hw, dev, "counter reset/update failed\n");
+ phy_start(dev->phydev);
netif_start_queue(dev);
rtl_unlock_work(tp);
pm_runtime_put_sync(&pdev->dev);
-
- rtl8169_check_link_status(dev, tp);
out:
return retval;
+err_free_irq:
+ pci_free_irq(pdev, 0, tp);
err_release_fw_2:
rtl_release_firmware(tp);
rtl8169_rx_clear(tp);
@@ -7126,6 +7152,7 @@ static void rtl8169_net_suspend(struct net_device *dev)
if (!netif_running(dev))
return;
+ phy_stop(dev->phydev);
netif_device_detach(dev);
netif_stop_queue(dev);
@@ -7158,6 +7185,8 @@ static void __rtl8169_resume(struct net_device *dev)
rtl_pll_power_up(tp);
rtl8169_init_phy(dev, tp);
+ phy_start(tp->dev->phydev);
+
rtl_lock_work(tp);
napi_enable(&tp->napi);
set_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags);
@@ -7301,6 +7330,7 @@ static void rtl_remove_one(struct pci_dev *pdev)
netif_napi_del(&tp->napi);
unregister_netdev(dev);
+ mdiobus_unregister(tp->mii_bus);
rtl_release_firmware(tp);
@@ -7386,6 +7416,65 @@ DECLARE_RTL_COND(rtl_rxtx_empty_cond)
return (RTL_R8(tp, MCU) & RXTX_EMPTY) == RXTX_EMPTY;
}
+static int r8169_mdio_read_reg(struct mii_bus *mii_bus, int phyaddr, int phyreg)
+{
+ struct rtl8169_private *tp = mii_bus->priv;
+
+ if (phyaddr > 0)
+ return -ENODEV;
+
+ return rtl_readphy(tp, phyreg);
+}
+
+static int r8169_mdio_write_reg(struct mii_bus *mii_bus, int phyaddr,
+ int phyreg, u16 val)
+{
+ struct rtl8169_private *tp = mii_bus->priv;
+
+ if (phyaddr > 0)
+ return -ENODEV;
+
+ rtl_writephy(tp, phyreg, val);
+
+ return 0;
+}
+
+static int r8169_mdio_register(struct rtl8169_private *tp)
+{
+ struct pci_dev *pdev = tp->pci_dev;
+ struct phy_device *phydev;
+ struct mii_bus *new_bus;
+ int ret;
+
+ new_bus = devm_mdiobus_alloc(&pdev->dev);
+ if (!new_bus)
+ return -ENOMEM;
+
+ new_bus->name = "r8169";
+ new_bus->priv = tp;
+ new_bus->parent = &pdev->dev;
+ new_bus->irq[0] = PHY_IGNORE_INTERRUPT;
+ snprintf(new_bus->id, MII_BUS_ID_SIZE, "r8169-%x",
+ PCI_DEVID(pdev->bus->number, pdev->devfn));
+
+ new_bus->read = r8169_mdio_read_reg;
+ new_bus->write = r8169_mdio_write_reg;
+
+ ret = mdiobus_register(new_bus);
+ if (ret)
+ return ret;
+
+ phydev = mdiobus_get_phy(new_bus, 0);
+ if (!phydev) {
+ mdiobus_unregister(new_bus);
+ return -ENODEV;
+ }
+
+ tp->mii_bus = new_bus;
+
+ return 0;
+}
+
static void rtl_hw_init_8168g(struct rtl8169_private *tp)
{
u32 data;
@@ -7646,10 +7735,14 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
/* chip gets powered up in rtl_open() */
rtl_pll_power_down(tp);
- rc = register_netdev(dev);
- if (rc < 0)
+ rc = r8169_mdio_register(tp);
+ if (rc)
return rc;
+ rc = register_netdev(dev);
+ if (rc)
+ goto err_mdio_unregister;
+
netif_info(tp, probe, dev, "%s, %pM, XID %08x, IRQ %d\n",
rtl_chip_infos[chipset].name, dev->dev_addr,
(u32)(RTL_R32(tp, TxConfig) & 0xfcf0f8ff),
@@ -7664,12 +7757,14 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (r8168_check_dash(tp))
rtl8168_driver_start(tp);
- netif_carrier_off(dev);
-
if (pci_dev_run_wake(pdev))
pm_runtime_put_sync(&pdev->dev);
return 0;
+
+err_mdio_unregister:
+ mdiobus_unregister(tp->mii_bus);
+ return rc;
}
static struct pci_driver rtl8169_pci_driver = {
--
2.18.0
^ permalink raw reply related
* Re: [PATCH net-next v6 01/11] net: sched: use rcu for action cookie update
From: Cong Wang @ 2018-07-17 20:46 UTC (permalink / raw)
To: Vlad Buslov
Cc: Linux Kernel Network Developers, David Miller, Jamal Hadi Salim,
Jiri Pirko, Alexei Starovoitov, Daniel Borkmann,
Yevgeny Kliteynik, Jiri Pirko
In-Reply-To: <vbfy3ebbn6j.fsf@reg-r-vrt-018-180.mtr.labs.mlnx>
On Mon, Jul 16, 2018 at 1:31 AM Vlad Buslov <vladbu@mellanox.com> wrote:
>
>
> On Fri 13 Jul 2018 at 21:51, Cong Wang <xiyou.wangcong@gmail.com> wrote:
> > On Fri, Jul 13, 2018 at 6:30 AM Vlad Buslov <vladbu@mellanox.com> wrote:
> >>
> >>
> >> On Fri 13 Jul 2018 at 03:52, Cong Wang <xiyou.wangcong@gmail.com> wrote:
> >> > On Thu, Jul 5, 2018 at 7:24 AM Vlad Buslov <vladbu@mellanox.com> wrote:
> >> >>
> >> >> Implement functions to atomically update and free action cookie
> >> >> using rcu mechanism.
> >> >
> >> > Without stating any reason..... Is this even a changelog?
> >>
> >> Yes, it is.
> >
> > What do you expect in a changelog generally? Repeating what
> > your code does? Thanks but we don't even want to read any code
> > unless the need of this code is reasonably justified.
>
> In my cover letter:
> - Motivation for patchset is presented in first paragraph.
> - Problems that prevent us from removing rtnl lock dependency are
> described, problem 3 is about cookie pointer.
> - In implementation section, point 3 presents solution for that
> problem.
Do you want to use cover letter as a changelog for all patches in
your patchset? Seriously? :)
Every patch is your patchset is unique, because you are not fixing
a problem can be expressed by a pattern.
Given how hard lockless is generally, probably you even can't
find out a pattern. If you really do, I am happy to learn!
>
> >
> > Can we at least agree you have no justification for this change
> > in this changelog? Or you believe this patch is as trivial as
> > a white space change which doesn't need a justification?
>
> Cong, from your last letter I understand that you want to have
> justification specifically for using atomic operation in this particular
> patch. I agree with you that I should have explained it in more details.
> I found a lot of prior art for same or similar atomic ops usage for rcu
> pointers(examples in my previous mail) and assumed it to be trivial, but
> now I understand that I was wrong in this case.
Thanks for having an agreement!
I expect to see more detailed changelog in your future patches! :)
^ permalink raw reply
* [PATCH net-next v3 00/10] r8169: add phylib support
From: Heiner Kallweit @ 2018-07-17 20:42 UTC (permalink / raw)
To: David Miller, Florian Fainelli, Andrew Lunn,
Realtek linux nic maintainers
Cc: netdev@vger.kernel.org
Now that all the basic refactoring has been done we can add phylib
support. This patch series was successfully tested on:
RTL8168h
RTL8168evl
RTL8169sb
Changes in v2:
- return error in mdio ops if phyaddr > 0
- advertise pause modes
- added reviewed-by for several patches
Changes in v3:
- return ENODEV for unused phy addresses in mdio ops
- remove unneeded PHY suspend in patch 2
- use recently added phy_speed_down and phy_speed_up in patch 7
- other minor changes based on review comments
Heiner Kallweit (10):
r8169: add basic phylib support
r8169: use phy_resume/phy_suspend
r8169: replace open-coded PHY soft reset with genphy_soft_reset
r8169: use phy_ethtool_(g|s)et_link_ksettings
r8169: use phy_ethtool_nway_reset
r8169: use phy_mii_ioctl
r8169: use phy_speed_down / phy_speed_up
r8169: remove rtl8169_set_speed_xmii
r8169: remove mii_if_info member from struct rtl8169_private
r8169: don't read chip phy status register
drivers/net/ethernet/realtek/Kconfig | 2 +-
drivers/net/ethernet/realtek/r8169.c | 465 +++++++++------------------
2 files changed, 155 insertions(+), 312 deletions(-)
--
2.18.0
^ permalink raw reply
* Re: [PATCH net-next 4/4] net: phy: sfp: Add HWMON support for module sensors
From: Guenter Roeck @ 2018-07-17 20:13 UTC (permalink / raw)
To: Andrew Lunn
Cc: David Miller, netdev, Florian Fainelli, Russell King, linux-hwmon
In-Reply-To: <1531856893-27884-5-git-send-email-andrew@lunn.ch>
On Tue, Jul 17, 2018 at 09:48:13PM +0200, Andrew Lunn wrote:
> SFP modules can contain a number of sensors. The EEPROM also contains
> recommended alarm and critical values for each sensor, and indications
> of if these have been exceeded. Export this information via
> HWMON. Currently temperature, VCC, bias current, transmit power, and
> possibly receiver power is supported.
>
> The sensors in the modules can either return calibrate or uncalibrated
> values. Uncalibrated values need to be manipulated, using coefficients
> provided in the SFP EEPROM. Uncalibrated receive power values require
> floating point maths in order to calibrate them. Performing this in
> the kernel is hard. So if the SFP module indicates it uses
> uncalibrated values, RX power is not made available.
>
> With this hwmon device, it is possible to view the sensor values using
> lm-sensors programs:
>
> in0: +3.29 V (crit min = +2.90 V, min = +3.00 V)
> (max = +3.60 V, crit max = +3.70 V)
> temp1: +33.0°C (low = -5.0°C, high = +80.0°C)
> (crit low = -10.0°C, crit = +85.0°C)
> power1: 1000.00 nW (max = 794.00 uW, min = 50.00 uW) ALARM (LCRIT)
> (lcrit = 40.00 uW, crit = 1000.00 uW)
> curr1: +0.00 A (crit min = +0.00 A, min = +0.00 A) ALARM (LCRIT, MIN)
> (max = +0.01 A, crit max = +0.01 A)
>
> The scaling sensors performs on the bias current is not particularly
> good. The raw values are more useful:
>
> curr1:
> curr1_input: 0.000
> curr1_min: 0.002
> curr1_max: 0.010
> curr1_lcrit: 0.000
> curr1_crit: 0.011
> curr1_min_alarm: 1.000
> curr1_max_alarm: 0.000
> curr1_lcrit_alarm: 1.000
> curr1_crit_alarm: 0.000
>
> In order to keep the I2C overhead to a minimum, the constant values,
> such as limits and calibration coefficients are read once at module
> insertion time. Thus only reading *_input and *_alarm properties
> requires i2c read operations.
>
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Acked-by: Guenter Roeck <linux@roeck-us.net>
> ---
> RFC->V1
> Use __be16 when reading sensor
> DIV_ROUND_CLOSEST()
> Don't use devm_hwmon_* to fix lifetime issues
> Replace invalid chars in hwmon name with _
> ---
> drivers/net/phy/Kconfig | 1 +
> drivers/net/phy/sfp.c | 727 ++++++++++++++++++++++++++++++++++++++++
> include/linux/sfp.h | 72 +++-
> 3 files changed, 799 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
> index 7761536974bf..f31ae4faf4e5 100644
> --- a/drivers/net/phy/Kconfig
> +++ b/drivers/net/phy/Kconfig
> @@ -215,6 +215,7 @@ config SFP
> tristate "SFP cage support"
> depends on I2C
> select MDIO_I2C
> + imply HWMON
>
> config AMD_PHY
> tristate "AMD PHYs"
> diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
> index c4c92db86dfa..5661226cf75b 100644
> --- a/drivers/net/phy/sfp.c
> +++ b/drivers/net/phy/sfp.c
> @@ -1,5 +1,7 @@
> +#include <linux/ctype.h>
> #include <linux/delay.h>
> #include <linux/gpio/consumer.h>
> +#include <linux/hwmon.h>
> #include <linux/i2c.h>
> #include <linux/interrupt.h>
> #include <linux/jiffies.h>
> @@ -131,6 +133,12 @@ struct sfp {
> unsigned int sm_retries;
>
> struct sfp_eeprom_id id;
> +#if IS_ENABLED(CONFIG_HWMON)
> + struct sfp_diag diag;
> + struct device *hwmon_dev;
> + char *hwmon_name;
> +#endif
> +
> };
>
> static bool sff_module_supported(const struct sfp_eeprom_id *id)
> @@ -316,6 +324,719 @@ static unsigned int sfp_check(void *buf, size_t len)
> return check;
> }
>
> +/* hwmon */
> +#if IS_ENABLED(CONFIG_HWMON)
> +static umode_t sfp_hwmon_is_visible(const void *data,
> + enum hwmon_sensor_types type,
> + u32 attr, int channel)
> +{
> + const struct sfp *sfp = data;
> +
> + switch (type) {
> + case hwmon_temp:
> + switch (attr) {
> + case hwmon_temp_input:
> + case hwmon_temp_min_alarm:
> + case hwmon_temp_max_alarm:
> + case hwmon_temp_lcrit_alarm:
> + case hwmon_temp_crit_alarm:
> + case hwmon_temp_min:
> + case hwmon_temp_max:
> + case hwmon_temp_lcrit:
> + case hwmon_temp_crit:
> + return 0444;
> + default:
> + return 0;
> + }
> + case hwmon_in:
> + switch (attr) {
> + case hwmon_in_input:
> + case hwmon_in_min_alarm:
> + case hwmon_in_max_alarm:
> + case hwmon_in_lcrit_alarm:
> + case hwmon_in_crit_alarm:
> + case hwmon_in_min:
> + case hwmon_in_max:
> + case hwmon_in_lcrit:
> + case hwmon_in_crit:
> + return 0444;
> + default:
> + return 0;
> + }
> + case hwmon_curr:
> + switch (attr) {
> + case hwmon_curr_input:
> + case hwmon_curr_min_alarm:
> + case hwmon_curr_max_alarm:
> + case hwmon_curr_lcrit_alarm:
> + case hwmon_curr_crit_alarm:
> + case hwmon_curr_min:
> + case hwmon_curr_max:
> + case hwmon_curr_lcrit:
> + case hwmon_curr_crit:
> + return 0444;
> + default:
> + return 0;
> + }
> + case hwmon_power:
> + /* External calibration of receive power requires
> + * floating point arithmetic. Doing that in the kernel
> + * is not easy, so just skip it. If the module does
> + * not require external calibration, we can however
> + * show receiver power, since FP is then not needed.
> + */
> + if (sfp->id.ext.diagmon & SFP_DIAGMON_EXT_CAL &&
> + channel == 1)
> + return 0;
> + switch (attr) {
> + case hwmon_power_input:
> + case hwmon_power_min_alarm:
> + case hwmon_power_max_alarm:
> + case hwmon_power_lcrit_alarm:
> + case hwmon_power_crit_alarm:
> + case hwmon_power_min:
> + case hwmon_power_max:
> + case hwmon_power_lcrit:
> + case hwmon_power_crit:
> + return 0444;
> + default:
> + return 0;
> + }
> + default:
> + return 0;
> + }
> +}
> +
> +static int sfp_hwmon_read_sensor(struct sfp *sfp, int reg, long *value)
> +{
> + __be16 val;
> + int err;
> +
> + err = sfp_read(sfp, true, reg, &val, sizeof(val));
> + if (err < 0)
> + return err;
> +
> + *value = be16_to_cpu(val);
> +
> + return 0;
> +}
> +
> +static void sfp_hwmon_to_rx_power(long *value)
> +{
> + *value = DIV_ROUND_CLOSEST(*value, 100);
> +}
> +
> +static void sfp_hwmon_calibrate(struct sfp *sfp, unsigned int slope, int offset,
> + long *value)
> +{
> + if (sfp->id.ext.diagmon & SFP_DIAGMON_EXT_CAL)
> + *value = DIV_ROUND_CLOSEST(*value * slope, 256) + offset;
> +}
> +
> +static void sfp_hwmon_calibrate_temp(struct sfp *sfp, long *value)
> +{
> + sfp_hwmon_calibrate(sfp, be16_to_cpu(sfp->diag.cal_t_slope),
> + be16_to_cpu(sfp->diag.cal_t_offset), value);
> +
> + if (*value >= 0x8000)
> + *value -= 0x10000;
> +
> + *value = DIV_ROUND_CLOSEST(*value * 1000, 256);
> +}
> +
> +static void sfp_hwmon_calibrate_vcc(struct sfp *sfp, long *value)
> +{
> + sfp_hwmon_calibrate(sfp, be16_to_cpu(sfp->diag.cal_v_slope),
> + be16_to_cpu(sfp->diag.cal_v_offset), value);
> +
> + *value = DIV_ROUND_CLOSEST(*value, 10);
> +}
> +
> +static void sfp_hwmon_calibrate_bias(struct sfp *sfp, long *value)
> +{
> + sfp_hwmon_calibrate(sfp, be16_to_cpu(sfp->diag.cal_txi_slope),
> + be16_to_cpu(sfp->diag.cal_txi_offset), value);
> +
> + *value = DIV_ROUND_CLOSEST(*value, 500);
> +}
> +
> +static void sfp_hwmon_calibrate_tx_power(struct sfp *sfp, long *value)
> +{
> + sfp_hwmon_calibrate(sfp, be16_to_cpu(sfp->diag.cal_txpwr_slope),
> + be16_to_cpu(sfp->diag.cal_txpwr_offset), value);
> +
> + *value = DIV_ROUND_CLOSEST(*value, 10);
> +}
> +
> +static int sfp_hwmon_read_temp(struct sfp *sfp, int reg, long *value)
> +{
> + int err;
> +
> + err = sfp_hwmon_read_sensor(sfp, reg, value);
> + if (err < 0)
> + return err;
> +
> + sfp_hwmon_calibrate_temp(sfp, value);
> +
> + return 0;
> +}
> +
> +static int sfp_hwmon_read_vcc(struct sfp *sfp, int reg, long *value)
> +{
> + int err;
> +
> + err = sfp_hwmon_read_sensor(sfp, reg, value);
> + if (err < 0)
> + return err;
> +
> + sfp_hwmon_calibrate_vcc(sfp, value);
> +
> + return 0;
> +}
> +
> +static int sfp_hwmon_read_bias(struct sfp *sfp, int reg, long *value)
> +{
> + int err;
> +
> + err = sfp_hwmon_read_sensor(sfp, reg, value);
> + if (err < 0)
> + return err;
> +
> + sfp_hwmon_calibrate_bias(sfp, value);
> +
> + return 0;
> +}
> +
> +static int sfp_hwmon_read_tx_power(struct sfp *sfp, int reg, long *value)
> +{
> + int err;
> +
> + err = sfp_hwmon_read_sensor(sfp, reg, value);
> + if (err < 0)
> + return err;
> +
> + sfp_hwmon_calibrate_tx_power(sfp, value);
> +
> + return 0;
> +}
> +
> +static int sfp_hwmon_read_rx_power(struct sfp *sfp, int reg, long *value)
> +{
> + int err;
> +
> + err = sfp_hwmon_read_sensor(sfp, reg, value);
> + if (err < 0)
> + return err;
> +
> + sfp_hwmon_to_rx_power(value);
> +
> + return 0;
> +}
> +
> +static int sfp_hwmon_temp(struct sfp *sfp, u32 attr, long *value)
> +{
> + u8 status;
> + int err;
> +
> + switch (attr) {
> + case hwmon_temp_input:
> + return sfp_hwmon_read_temp(sfp, SFP_TEMP, value);
> +
> + case hwmon_temp_lcrit:
> + *value = be16_to_cpu(sfp->diag.temp_low_alarm);
> + sfp_hwmon_calibrate_temp(sfp, value);
> + return 0;
> +
> + case hwmon_temp_min:
> + *value = be16_to_cpu(sfp->diag.temp_low_warn);
> + sfp_hwmon_calibrate_temp(sfp, value);
> + return 0;
> + case hwmon_temp_max:
> + *value = be16_to_cpu(sfp->diag.temp_high_warn);
> + sfp_hwmon_calibrate_temp(sfp, value);
> + return 0;
> +
> + case hwmon_temp_crit:
> + *value = be16_to_cpu(sfp->diag.temp_high_alarm);
> + sfp_hwmon_calibrate_temp(sfp, value);
> + return 0;
> +
> + case hwmon_temp_lcrit_alarm:
> + err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_ALARM0_TEMP_LOW);
> + return 0;
> +
> + case hwmon_temp_min_alarm:
> + err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_WARN0_TEMP_LOW);
> + return 0;
> +
> + case hwmon_temp_max_alarm:
> + err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_WARN0_TEMP_HIGH);
> + return 0;
> +
> + case hwmon_temp_crit_alarm:
> + err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_ALARM0_TEMP_HIGH);
> + return 0;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return -EOPNOTSUPP;
> +}
> +
> +static int sfp_hwmon_vcc(struct sfp *sfp, u32 attr, long *value)
> +{
> + u8 status;
> + int err;
> +
> + switch (attr) {
> + case hwmon_in_input:
> + return sfp_hwmon_read_vcc(sfp, SFP_VCC, value);
> +
> + case hwmon_in_lcrit:
> + *value = be16_to_cpu(sfp->diag.volt_low_alarm);
> + sfp_hwmon_calibrate_vcc(sfp, value);
> + return 0;
> +
> + case hwmon_in_min:
> + *value = be16_to_cpu(sfp->diag.volt_low_warn);
> + sfp_hwmon_calibrate_vcc(sfp, value);
> + return 0;
> +
> + case hwmon_in_max:
> + *value = be16_to_cpu(sfp->diag.volt_high_warn);
> + sfp_hwmon_calibrate_vcc(sfp, value);
> + return 0;
> +
> + case hwmon_in_crit:
> + *value = be16_to_cpu(sfp->diag.volt_high_alarm);
> + sfp_hwmon_calibrate_vcc(sfp, value);
> + return 0;
> +
> + case hwmon_in_lcrit_alarm:
> + err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_ALARM0_VCC_LOW);
> + return 0;
> +
> + case hwmon_in_min_alarm:
> + err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_WARN0_VCC_LOW);
> + return 0;
> +
> + case hwmon_in_max_alarm:
> + err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_WARN0_VCC_HIGH);
> + return 0;
> +
> + case hwmon_in_crit_alarm:
> + err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_ALARM0_VCC_HIGH);
> + return 0;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return -EOPNOTSUPP;
> +}
> +
> +static int sfp_hwmon_bias(struct sfp *sfp, u32 attr, long *value)
> +{
> + u8 status;
> + int err;
> +
> + switch (attr) {
> + case hwmon_curr_input:
> + return sfp_hwmon_read_bias(sfp, SFP_TX_BIAS, value);
> +
> + case hwmon_curr_lcrit:
> + *value = be16_to_cpu(sfp->diag.bias_low_alarm);
> + sfp_hwmon_calibrate_bias(sfp, value);
> + return 0;
> +
> + case hwmon_curr_min:
> + *value = be16_to_cpu(sfp->diag.bias_low_warn);
> + sfp_hwmon_calibrate_bias(sfp, value);
> + return 0;
> +
> + case hwmon_curr_max:
> + *value = be16_to_cpu(sfp->diag.bias_high_warn);
> + sfp_hwmon_calibrate_bias(sfp, value);
> + return 0;
> +
> + case hwmon_curr_crit:
> + *value = be16_to_cpu(sfp->diag.bias_high_alarm);
> + sfp_hwmon_calibrate_bias(sfp, value);
> + return 0;
> +
> + case hwmon_curr_lcrit_alarm:
> + err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_ALARM0_TX_BIAS_LOW);
> + return 0;
> +
> + case hwmon_curr_min_alarm:
> + err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_WARN0_TX_BIAS_LOW);
> + return 0;
> +
> + case hwmon_curr_max_alarm:
> + err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_WARN0_TX_BIAS_HIGH);
> + return 0;
> +
> + case hwmon_curr_crit_alarm:
> + err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_ALARM0_TX_BIAS_HIGH);
> + return 0;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return -EOPNOTSUPP;
> +}
> +
> +static int sfp_hwmon_tx_power(struct sfp *sfp, u32 attr, long *value)
> +{
> + u8 status;
> + int err;
> +
> + switch (attr) {
> + case hwmon_power_input:
> + return sfp_hwmon_read_tx_power(sfp, SFP_TX_POWER, value);
> +
> + case hwmon_power_lcrit:
> + *value = be16_to_cpu(sfp->diag.txpwr_low_alarm);
> + sfp_hwmon_calibrate_tx_power(sfp, value);
> + return 0;
> +
> + case hwmon_power_min:
> + *value = be16_to_cpu(sfp->diag.txpwr_low_warn);
> + sfp_hwmon_calibrate_tx_power(sfp, value);
> + return 0;
> +
> + case hwmon_power_max:
> + *value = be16_to_cpu(sfp->diag.txpwr_high_warn);
> + sfp_hwmon_calibrate_tx_power(sfp, value);
> + return 0;
> +
> + case hwmon_power_crit:
> + *value = be16_to_cpu(sfp->diag.txpwr_high_alarm);
> + sfp_hwmon_calibrate_tx_power(sfp, value);
> + return 0;
> +
> + case hwmon_power_lcrit_alarm:
> + err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_ALARM0_TXPWR_LOW);
> + return 0;
> +
> + case hwmon_power_min_alarm:
> + err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_WARN0_TXPWR_LOW);
> + return 0;
> +
> + case hwmon_power_max_alarm:
> + err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_WARN0_TXPWR_HIGH);
> + return 0;
> +
> + case hwmon_power_crit_alarm:
> + err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_ALARM0_TXPWR_HIGH);
> + return 0;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return -EOPNOTSUPP;
> +}
> +
> +static int sfp_hwmon_rx_power(struct sfp *sfp, u32 attr, long *value)
> +{
> + u8 status;
> + int err;
> +
> + switch (attr) {
> + case hwmon_power_input:
> + return sfp_hwmon_read_rx_power(sfp, SFP_RX_POWER, value);
> +
> + case hwmon_power_lcrit:
> + *value = be16_to_cpu(sfp->diag.rxpwr_low_alarm);
> + sfp_hwmon_to_rx_power(value);
> + return 0;
> +
> + case hwmon_power_min:
> + *value = be16_to_cpu(sfp->diag.rxpwr_low_warn);
> + sfp_hwmon_to_rx_power(value);
> + return 0;
> +
> + case hwmon_power_max:
> + *value = be16_to_cpu(sfp->diag.rxpwr_high_warn);
> + sfp_hwmon_to_rx_power(value);
> + return 0;
> +
> + case hwmon_power_crit:
> + *value = be16_to_cpu(sfp->diag.rxpwr_high_alarm);
> + sfp_hwmon_to_rx_power(value);
> + return 0;
> +
> + case hwmon_power_lcrit_alarm:
> + err = sfp_read(sfp, true, SFP_ALARM1, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_ALARM1_RXPWR_LOW);
> + return 0;
> +
> + case hwmon_power_min_alarm:
> + err = sfp_read(sfp, true, SFP_WARN1, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_WARN1_RXPWR_LOW);
> + return 0;
> +
> + case hwmon_power_max_alarm:
> + err = sfp_read(sfp, true, SFP_WARN1, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_WARN1_RXPWR_HIGH);
> + return 0;
> +
> + case hwmon_power_crit_alarm:
> + err = sfp_read(sfp, true, SFP_ALARM1, &status, sizeof(status));
> + if (err < 0)
> + return err;
> +
> + *value = !!(status & SFP_ALARM1_RXPWR_HIGH);
> + return 0;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return -EOPNOTSUPP;
> +}
> +
> +static int sfp_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long *value)
> +{
> + struct sfp *sfp = dev_get_drvdata(dev);
> +
> + switch (type) {
> + case hwmon_temp:
> + return sfp_hwmon_temp(sfp, attr, value);
> + case hwmon_in:
> + return sfp_hwmon_vcc(sfp, attr, value);
> + case hwmon_curr:
> + return sfp_hwmon_bias(sfp, attr, value);
> + case hwmon_power:
> + switch (channel) {
> + case 0:
> + return sfp_hwmon_tx_power(sfp, attr, value);
> + case 1:
> + return sfp_hwmon_rx_power(sfp, attr, value);
> + default:
> + return -EOPNOTSUPP;
> + }
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static const struct hwmon_ops sfp_hwmon_ops = {
> + .is_visible = sfp_hwmon_is_visible,
> + .read = sfp_hwmon_read,
> +};
> +
> +static u32 sfp_hwmon_chip_config[] = {
> + HWMON_C_REGISTER_TZ,
> + 0,
> +};
> +
> +static const struct hwmon_channel_info sfp_hwmon_chip = {
> + .type = hwmon_chip,
> + .config = sfp_hwmon_chip_config,
> +};
> +
> +static u32 sfp_hwmon_temp_config[] = {
> + HWMON_T_INPUT |
> + HWMON_T_MAX | HWMON_T_MIN |
> + HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM |
> + HWMON_T_CRIT | HWMON_T_LCRIT |
> + HWMON_T_CRIT_ALARM | HWMON_T_LCRIT_ALARM,
> + 0,
> +};
> +
> +static const struct hwmon_channel_info sfp_hwmon_temp_channel_info = {
> + .type = hwmon_temp,
> + .config = sfp_hwmon_temp_config,
> +};
> +
> +static u32 sfp_hwmon_vcc_config[] = {
> + HWMON_I_INPUT |
> + HWMON_I_MAX | HWMON_I_MIN |
> + HWMON_I_MAX_ALARM | HWMON_I_MIN_ALARM |
> + HWMON_I_CRIT | HWMON_I_LCRIT |
> + HWMON_I_CRIT_ALARM | HWMON_I_LCRIT_ALARM,
> + 0,
> +};
> +
> +static const struct hwmon_channel_info sfp_hwmon_vcc_channel_info = {
> + .type = hwmon_in,
> + .config = sfp_hwmon_vcc_config,
> +};
> +
> +static u32 sfp_hwmon_bias_config[] = {
> + HWMON_C_INPUT |
> + HWMON_C_MAX | HWMON_C_MIN |
> + HWMON_C_MAX_ALARM | HWMON_C_MIN_ALARM |
> + HWMON_C_CRIT | HWMON_C_LCRIT |
> + HWMON_C_CRIT_ALARM | HWMON_C_LCRIT_ALARM,
> + 0,
> +};
> +
> +static const struct hwmon_channel_info sfp_hwmon_bias_channel_info = {
> + .type = hwmon_curr,
> + .config = sfp_hwmon_bias_config,
> +};
> +
> +static u32 sfp_hwmon_power_config[] = {
> + /* Transmit power */
> + HWMON_P_INPUT |
> + HWMON_P_MAX | HWMON_P_MIN |
> + HWMON_P_MAX_ALARM | HWMON_P_MIN_ALARM |
> + HWMON_P_CRIT | HWMON_P_LCRIT |
> + HWMON_P_CRIT_ALARM | HWMON_P_LCRIT_ALARM,
> + /* Receive power */
> + HWMON_P_INPUT |
> + HWMON_P_MAX | HWMON_P_MIN |
> + HWMON_P_MAX_ALARM | HWMON_P_MIN_ALARM |
> + HWMON_P_CRIT | HWMON_P_LCRIT |
> + HWMON_P_CRIT_ALARM | HWMON_P_LCRIT_ALARM,
> + 0,
> +};
> +
> +static const struct hwmon_channel_info sfp_hwmon_power_channel_info = {
> + .type = hwmon_power,
> + .config = sfp_hwmon_power_config,
> +};
> +
> +static const struct hwmon_channel_info *sfp_hwmon_info[] = {
> + &sfp_hwmon_chip,
> + &sfp_hwmon_vcc_channel_info,
> + &sfp_hwmon_temp_channel_info,
> + &sfp_hwmon_bias_channel_info,
> + &sfp_hwmon_power_channel_info,
> + NULL,
> +};
> +
> +static const struct hwmon_chip_info sfp_hwmon_chip_info = {
> + .ops = &sfp_hwmon_ops,
> + .info = sfp_hwmon_info,
> +};
> +
> +static int sfp_hwmon_insert(struct sfp *sfp)
> +{
> + int err, i;
> +
> + if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE)
> + return 0;
> +
> + if (!(sfp->id.ext.diagmon & SFP_DIAGMON_DDM))
> + return 0;
> +
> + if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE)
> + /* This driver in general does not support address
> + * change.
> + */
> + return 0;
> +
> + err = sfp_read(sfp, true, 0, &sfp->diag, sizeof(sfp->diag));
> + if (err < 0)
> + return err;
> +
> + sfp->hwmon_name = kstrdup(dev_name(sfp->dev), GFP_KERNEL);
> + if (!sfp->hwmon_name)
> + return -ENODEV;
> +
> + for (i = 0; sfp->hwmon_name[i]; i++)
> + if (hwmon_is_bad_char(sfp->hwmon_name[i]))
> + sfp->hwmon_name[i] = '_';
> +
> + sfp->hwmon_dev = hwmon_device_register_with_info(sfp->dev,
> + sfp->hwmon_name, sfp,
> + &sfp_hwmon_chip_info,
> + NULL);
> +
> + return PTR_ERR_OR_ZERO(sfp->hwmon_dev);
> +}
> +
> +static void sfp_hwmon_remove(struct sfp *sfp)
> +{
> + hwmon_device_unregister(sfp->hwmon_dev);
> + kfree(sfp->hwmon_name);
> +}
> +#else
> +static int sfp_hwmon_insert(struct sfp *sfp)
> +{
> + return 0;
> +}
> +
> +static void sfp_hwmon_remove(struct sfp *sfp)
> +{
> +}
> +#endif
> +
> /* Helpers */
> static void sfp_module_tx_disable(struct sfp *sfp)
> {
> @@ -636,6 +1357,10 @@ static int sfp_sm_mod_probe(struct sfp *sfp)
> dev_warn(sfp->dev,
> "module address swap to access page 0xA2 is not supported.\n");
>
> + ret = sfp_hwmon_insert(sfp);
> + if (ret < 0)
> + return ret;
> +
> ret = sfp_module_insert(sfp->sfp_bus, &sfp->id);
> if (ret < 0)
> return ret;
> @@ -647,6 +1372,8 @@ static void sfp_sm_mod_remove(struct sfp *sfp)
> {
> sfp_module_remove(sfp->sfp_bus);
>
> + sfp_hwmon_remove(sfp);
> +
> if (sfp->mod_phy)
> sfp_sm_phy_detach(sfp);
>
> diff --git a/include/linux/sfp.h b/include/linux/sfp.h
> index ebce9e24906a..d37518e89db2 100644
> --- a/include/linux/sfp.h
> +++ b/include/linux/sfp.h
> @@ -231,6 +231,50 @@ struct sfp_eeprom_id {
> struct sfp_eeprom_ext ext;
> } __packed;
>
> +struct sfp_diag {
> + __be16 temp_high_alarm;
> + __be16 temp_low_alarm;
> + __be16 temp_high_warn;
> + __be16 temp_low_warn;
> + __be16 volt_high_alarm;
> + __be16 volt_low_alarm;
> + __be16 volt_high_warn;
> + __be16 volt_low_warn;
> + __be16 bias_high_alarm;
> + __be16 bias_low_alarm;
> + __be16 bias_high_warn;
> + __be16 bias_low_warn;
> + __be16 txpwr_high_alarm;
> + __be16 txpwr_low_alarm;
> + __be16 txpwr_high_warn;
> + __be16 txpwr_low_warn;
> + __be16 rxpwr_high_alarm;
> + __be16 rxpwr_low_alarm;
> + __be16 rxpwr_high_warn;
> + __be16 rxpwr_low_warn;
> + __be16 laser_temp_high_alarm;
> + __be16 laser_temp_low_alarm;
> + __be16 laser_temp_high_warn;
> + __be16 laser_temp_low_warn;
> + __be16 tec_cur_high_alarm;
> + __be16 tec_cur_low_alarm;
> + __be16 tec_cur_high_warn;
> + __be16 tec_cur_low_warn;
> + __be32 cal_rxpwr4;
> + __be32 cal_rxpwr3;
> + __be32 cal_rxpwr2;
> + __be32 cal_rxpwr1;
> + __be32 cal_rxpwr0;
> + __be16 cal_txi_slope;
> + __be16 cal_txi_offset;
> + __be16 cal_txpwr_slope;
> + __be16 cal_txpwr_offset;
> + __be16 cal_t_slope;
> + __be16 cal_t_offset;
> + __be16 cal_v_slope;
> + __be16 cal_v_offset;
> +} __packed;
> +
> /* SFP EEPROM registers */
> enum {
> SFP_PHYS_ID = 0x00,
> @@ -384,7 +428,33 @@ enum {
> SFP_TEC_CUR = 0x6c,
>
> SFP_STATUS = 0x6e,
> - SFP_ALARM = 0x70,
> + SFP_ALARM0 = 0x70,
> + SFP_ALARM0_TEMP_HIGH = BIT(7),
> + SFP_ALARM0_TEMP_LOW = BIT(6),
> + SFP_ALARM0_VCC_HIGH = BIT(5),
> + SFP_ALARM0_VCC_LOW = BIT(4),
> + SFP_ALARM0_TX_BIAS_HIGH = BIT(3),
> + SFP_ALARM0_TX_BIAS_LOW = BIT(2),
> + SFP_ALARM0_TXPWR_HIGH = BIT(1),
> + SFP_ALARM0_TXPWR_LOW = BIT(0),
> +
> + SFP_ALARM1 = 0x71,
> + SFP_ALARM1_RXPWR_HIGH = BIT(7),
> + SFP_ALARM1_RXPWR_LOW = BIT(6),
> +
> + SFP_WARN0 = 0x74,
> + SFP_WARN0_TEMP_HIGH = BIT(7),
> + SFP_WARN0_TEMP_LOW = BIT(6),
> + SFP_WARN0_VCC_HIGH = BIT(5),
> + SFP_WARN0_VCC_LOW = BIT(4),
> + SFP_WARN0_TX_BIAS_HIGH = BIT(3),
> + SFP_WARN0_TX_BIAS_LOW = BIT(2),
> + SFP_WARN0_TXPWR_HIGH = BIT(1),
> + SFP_WARN0_TXPWR_LOW = BIT(0),
> +
> + SFP_WARN1 = 0x75,
> + SFP_WARN1_RXPWR_HIGH = BIT(7),
> + SFP_WARN1_RXPWR_LOW = BIT(6),
>
> SFP_EXT_STATUS = 0x76,
> SFP_VSL = 0x78,
> --
> 2.18.0
>
^ permalink raw reply
* Re: [PATCH net-next 3/4] hwmon: Add helper to tell if a char is invalid in a name
From: Guenter Roeck @ 2018-07-17 20:12 UTC (permalink / raw)
To: Andrew Lunn
Cc: David Miller, netdev, Florian Fainelli, Russell King, linux-hwmon
In-Reply-To: <1531856893-27884-4-git-send-email-andrew@lunn.ch>
On Tue, Jul 17, 2018 at 09:48:12PM +0200, Andrew Lunn wrote:
> HWMON device names are not allowed to contain "-* \t\n". Add a helper
> which will return true if passed an invalid character. It can be used
> to massage a string into a hwmon compatible name by replacing invalid
> characters with '_'.
>
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Acked-by: Guenter Roeck <linux@roeck-us.net>
> ---
> include/linux/hwmon.h | 23 +++++++++++++++++++++++
> 1 file changed, 23 insertions(+)
>
> diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h
> index b217101ca76e..9493d4a388db 100644
> --- a/include/linux/hwmon.h
> +++ b/include/linux/hwmon.h
> @@ -398,4 +398,27 @@ devm_hwmon_device_register_with_info(struct device *dev,
> void hwmon_device_unregister(struct device *dev);
> void devm_hwmon_device_unregister(struct device *dev);
>
> +/**
> + * hwmon_is_bad_char - Is the char invalid in a hwmon name
> + * @ch: the char to be considered
> + *
> + * hwmon_is_bad_char() can be used to determine if the given character
> + * may not be used in a hwmon name.
> + *
> + * Returns true if the char is invalid, false otherwise.
> + */
> +static inline bool hwmon_is_bad_char(const char ch)
> +{
> + switch (ch) {
> + case '-':
> + case '*':
> + case ' ':
> + case '\t':
> + case '\n':
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> #endif
> --
> 2.18.0
>
^ permalink raw reply
* Re: [PATCH net-next 2/4] hwmon: Add support for power min, lcrit, min_alarm and lcrit_alarm
From: Guenter Roeck @ 2018-07-17 20:12 UTC (permalink / raw)
To: Andrew Lunn
Cc: David Miller, netdev, Florian Fainelli, Russell King, linux-hwmon
In-Reply-To: <1531856893-27884-3-git-send-email-andrew@lunn.ch>
On Tue, Jul 17, 2018 at 09:48:11PM +0200, Andrew Lunn wrote:
> Some sensors support reporting minimal and lower critical power, as
> well as alarms when these thresholds are reached. Add support for
> these attributes to the hwmon core.
>
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Acked-by: Guenter Roeck <linux@roeck-us.net>
> ---
> drivers/hwmon/hwmon.c | 4 ++++
> include/linux/hwmon.h | 8 ++++++++
> 2 files changed, 12 insertions(+)
>
> diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
> index e88c01961948..33d51281272b 100644
> --- a/drivers/hwmon/hwmon.c
> +++ b/drivers/hwmon/hwmon.c
> @@ -394,12 +394,16 @@ static const char * const hwmon_power_attr_templates[] = {
> [hwmon_power_cap_hyst] = "power%d_cap_hyst",
> [hwmon_power_cap_max] = "power%d_cap_max",
> [hwmon_power_cap_min] = "power%d_cap_min",
> + [hwmon_power_min] = "power%d_min",
> [hwmon_power_max] = "power%d_max",
> + [hwmon_power_lcrit] = "power%d_lcrit",
> [hwmon_power_crit] = "power%d_crit",
> [hwmon_power_label] = "power%d_label",
> [hwmon_power_alarm] = "power%d_alarm",
> [hwmon_power_cap_alarm] = "power%d_cap_alarm",
> + [hwmon_power_min_alarm] = "power%d_min_alarm",
> [hwmon_power_max_alarm] = "power%d_max_alarm",
> + [hwmon_power_lcrit_alarm] = "power%d_lcrit_alarm",
> [hwmon_power_crit_alarm] = "power%d_crit_alarm",
> };
>
> diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h
> index 1b74ad11a5a4..b217101ca76e 100644
> --- a/include/linux/hwmon.h
> +++ b/include/linux/hwmon.h
> @@ -188,12 +188,16 @@ enum hwmon_power_attributes {
> hwmon_power_cap_hyst,
> hwmon_power_cap_max,
> hwmon_power_cap_min,
> + hwmon_power_min,
> hwmon_power_max,
> hwmon_power_crit,
> + hwmon_power_lcrit,
> hwmon_power_label,
> hwmon_power_alarm,
> hwmon_power_cap_alarm,
> + hwmon_power_min_alarm,
> hwmon_power_max_alarm,
> + hwmon_power_lcrit_alarm,
> hwmon_power_crit_alarm,
> };
>
> @@ -214,12 +218,16 @@ enum hwmon_power_attributes {
> #define HWMON_P_CAP_HYST BIT(hwmon_power_cap_hyst)
> #define HWMON_P_CAP_MAX BIT(hwmon_power_cap_max)
> #define HWMON_P_CAP_MIN BIT(hwmon_power_cap_min)
> +#define HWMON_P_MIN BIT(hwmon_power_min)
> #define HWMON_P_MAX BIT(hwmon_power_max)
> +#define HWMON_P_LCRIT BIT(hwmon_power_lcrit)
> #define HWMON_P_CRIT BIT(hwmon_power_crit)
> #define HWMON_P_LABEL BIT(hwmon_power_label)
> #define HWMON_P_ALARM BIT(hwmon_power_alarm)
> #define HWMON_P_CAP_ALARM BIT(hwmon_power_cap_alarm)
> +#define HWMON_P_MIN_ALARM BIT(hwmon_power_max_alarm)
> #define HWMON_P_MAX_ALARM BIT(hwmon_power_max_alarm)
> +#define HWMON_P_LCRIT_ALARM BIT(hwmon_power_lcrit_alarm)
> #define HWMON_P_CRIT_ALARM BIT(hwmon_power_crit_alarm)
>
> enum hwmon_energy_attributes {
> --
> 2.18.0
>
^ permalink raw reply
* Re: [PATCH net-next 1/4] hwmon: Add missing HWMON_T_LCRIT_ALARM define
From: Guenter Roeck @ 2018-07-17 20:12 UTC (permalink / raw)
To: Andrew Lunn
Cc: David Miller, netdev, Florian Fainelli, Russell King, linux-hwmon
In-Reply-To: <1531856893-27884-2-git-send-email-andrew@lunn.ch>
On Tue, Jul 17, 2018 at 09:48:10PM +0200, Andrew Lunn wrote:
> The enum hwmon_temp_lcrit_alarm exists, but the BIT definition is
> missing.
>
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Acked-by: Guenter Roeck <linux@roeck-us.net>
... and, yes, I assume the series will be picked up by David.
> ---
> include/linux/hwmon.h | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h
> index e5fd2707b6df..1b74ad11a5a4 100644
> --- a/include/linux/hwmon.h
> +++ b/include/linux/hwmon.h
> @@ -93,6 +93,7 @@ enum hwmon_temp_attributes {
> #define HWMON_T_MIN_ALARM BIT(hwmon_temp_min_alarm)
> #define HWMON_T_MAX_ALARM BIT(hwmon_temp_max_alarm)
> #define HWMON_T_CRIT_ALARM BIT(hwmon_temp_crit_alarm)
> +#define HWMON_T_LCRIT_ALARM BIT(hwmon_temp_lcrit_alarm)
> #define HWMON_T_EMERGENCY_ALARM BIT(hwmon_temp_emergency_alarm)
> #define HWMON_T_FAULT BIT(hwmon_temp_fault)
> #define HWMON_T_OFFSET BIT(hwmon_temp_offset)
> --
> 2.18.0
>
^ permalink raw reply
* Re: [PATCH RFC/RFT net-next 00/17] net: Convert neighbor tables to per-namespace
From: Cong Wang @ 2018-07-17 20:37 UTC (permalink / raw)
To: David Ahern
Cc: Linux Kernel Network Developers, nikita.leshchenko, Roopa Prabhu,
Stephen Hemminger, Ido Schimmel, Jiri Pirko, Saeed Mahameed,
Alexander Aring, linux-wpan, NetFilter, LKML
In-Reply-To: <1a27e301-3275-b349-a2f8-afdfdc02f04f@gmail.com>
On Tue, Jul 17, 2018 at 12:02 PM David Ahern <dsahern@gmail.com> wrote:
> As for the per-namespace tables, it is 4 years later and over that time
> Linux supports a number of features: EVPN which is very mac heavy, VRR
> which doubles mac entries (one against the VRR device and one against
> the lower device) and NOS level features such as mlxsw which has to
> ensure mac entries for nexthop gateaways stay active. In addition there
> are other features on the horizon - like the ability to use namespaces
> to create virtual switches (what Cisco calls a VDC) where you absolutely
> want isolation and not allowing entries from virtual switch to evict
> entries from another. And of course the continued proliferation of
> containerized workloads where isolation is desired.
As long as no change in neigh table code base itself, these can't
address the concern people raised before.
>
> I understand the concern about global resource and limits: as it stands
> you have to increase the limits in init_net to the max expected and hope
> for the best. With per namespace limits you can lower the limits of each
> namespace better control the total impact on the total memory used.
The problem is that the number of containers in a host is usually
not predictable.
Of course, you can say containers limit kernel memory too, but
memcg is not part of netns. I once told David Miller cpuset is the
isolation for isolating per-CPU softnet_data, he didn't like it. Based
on that I don't think you can convince him with memcg as a solution
here.
^ permalink raw reply
* [PATCH net-next 0/4] HWMON support for SFP modules
From: Andrew Lunn @ 2018-07-17 19:48 UTC (permalink / raw)
To: David Miller, Guenter Roeck
Cc: netdev, Florian Fainelli, Russell King, linux-hwmon, Andrew Lunn
This patchset adds HWMON support to SFP modules. The two patches add
some attributes for temperature and power sensors which are currently
missing from the hwmon core. The third patch adds a helper for
filtering out characters in hwmon names which are invalid. The last
patch then extends the core SFP code to export the sensors found in
SFP modules.
This code has been tested with two SFP modules:
module OEM SFP-7000-85 rev 11.0 sn M1512220075 dc 160221
module FINISAR CORP. FTLF8524E2GNL rev A sn PW40MNN dc 160725
The anonymous module uses external calibration, while the FINISAR uses
internal calibration. Thus both code paths have been tested.
Due to the cross subsystem nature of these patches, as discussed with
the RFC, it is hoped Guenter Roeck will ACK the patches, and then Dave
Miller will merge them all via net-next.
Andrew Lunn (4):
hwmon: Add missing HWMON_T_LCRIT_ALARM define
hwmon: Add support for power min, lcrit, min_alarm and lcrit_alarm
hwmon: Add helper to tell if a char is invalid in a name
net: phy: sfp: Add HWMON support for module sensors
drivers/hwmon/hwmon.c | 4 +
drivers/net/phy/Kconfig | 1 +
drivers/net/phy/sfp.c | 727 ++++++++++++++++++++++++++++++++++++++++
include/linux/hwmon.h | 32 ++
include/linux/sfp.h | 72 +++-
5 files changed, 835 insertions(+), 1 deletion(-)
--
2.18.0
^ permalink raw reply
* [PATCH net-next 1/4] hwmon: Add missing HWMON_T_LCRIT_ALARM define
From: Andrew Lunn @ 2018-07-17 19:48 UTC (permalink / raw)
To: David Miller, Guenter Roeck
Cc: netdev, Florian Fainelli, Russell King, linux-hwmon, Andrew Lunn
In-Reply-To: <1531856893-27884-1-git-send-email-andrew@lunn.ch>
The enum hwmon_temp_lcrit_alarm exists, but the BIT definition is
missing.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
include/linux/hwmon.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h
index e5fd2707b6df..1b74ad11a5a4 100644
--- a/include/linux/hwmon.h
+++ b/include/linux/hwmon.h
@@ -93,6 +93,7 @@ enum hwmon_temp_attributes {
#define HWMON_T_MIN_ALARM BIT(hwmon_temp_min_alarm)
#define HWMON_T_MAX_ALARM BIT(hwmon_temp_max_alarm)
#define HWMON_T_CRIT_ALARM BIT(hwmon_temp_crit_alarm)
+#define HWMON_T_LCRIT_ALARM BIT(hwmon_temp_lcrit_alarm)
#define HWMON_T_EMERGENCY_ALARM BIT(hwmon_temp_emergency_alarm)
#define HWMON_T_FAULT BIT(hwmon_temp_fault)
#define HWMON_T_OFFSET BIT(hwmon_temp_offset)
--
2.18.0
^ permalink raw reply related
* RE: [PATCH net-next] tipc: remove unused tipc_link_is_active
From: Jon Maloy @ 2018-07-17 19:49 UTC (permalink / raw)
To: YueHaibing, davem@davemloft.net, ying.xue@windriver.com
Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
tipc-discussion@lists.sourceforge.net
In-Reply-To: <20180717135846.18600-1-yuehaibing@huawei.com>
Acked-by: Jon Maloy <jon.maloy@ericsson.com>
Thank you for fixing this.
> -----Original Message-----
> From: YueHaibing <yuehaibing@huawei.com>
> Sent: Tuesday, 17 July, 2018 09:59
> To: davem@davemloft.net; Jon Maloy <jon.maloy@ericsson.com>; ying.xue@windriver.com
> Cc: linux-kernel@vger.kernel.org; netdev@vger.kernel.org; tipc-discussion@lists.sourceforge.net; YueHaibing
> <yuehaibing@huawei.com>
> Subject: [PATCH net-next] tipc: remove unused tipc_link_is_active
>
> tipc_link_is_active is no longer used and can be removed.
>
> Signed-off-by: YueHaibing <yuehaibing@huawei.com>
> ---
> net/tipc/link.c | 5 -----
> 1 file changed, 5 deletions(-)
>
> diff --git a/net/tipc/link.c b/net/tipc/link.c
> index df763be..6987ffc 100644
> --- a/net/tipc/link.c
> +++ b/net/tipc/link.c
> @@ -297,11 +297,6 @@ static bool link_is_bc_rcvlink(struct tipc_link *l)
> return ((l->bc_rcvlink == l) && !link_is_bc_sndlink(l));
> }
>
> -int tipc_link_is_active(struct tipc_link *l)
> -{
> - return l->active;
> -}
> -
> void tipc_link_set_active(struct tipc_link *l, bool active)
> {
> l->active = active;
> --
> 2.7.0
>
^ permalink raw reply
* [PATCH net-next 2/4] hwmon: Add support for power min, lcrit, min_alarm and lcrit_alarm
From: Andrew Lunn @ 2018-07-17 19:48 UTC (permalink / raw)
To: David Miller, Guenter Roeck
Cc: netdev, Florian Fainelli, Russell King, linux-hwmon, Andrew Lunn
In-Reply-To: <1531856893-27884-1-git-send-email-andrew@lunn.ch>
Some sensors support reporting minimal and lower critical power, as
well as alarms when these thresholds are reached. Add support for
these attributes to the hwmon core.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/hwmon/hwmon.c | 4 ++++
include/linux/hwmon.h | 8 ++++++++
2 files changed, 12 insertions(+)
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index e88c01961948..33d51281272b 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -394,12 +394,16 @@ static const char * const hwmon_power_attr_templates[] = {
[hwmon_power_cap_hyst] = "power%d_cap_hyst",
[hwmon_power_cap_max] = "power%d_cap_max",
[hwmon_power_cap_min] = "power%d_cap_min",
+ [hwmon_power_min] = "power%d_min",
[hwmon_power_max] = "power%d_max",
+ [hwmon_power_lcrit] = "power%d_lcrit",
[hwmon_power_crit] = "power%d_crit",
[hwmon_power_label] = "power%d_label",
[hwmon_power_alarm] = "power%d_alarm",
[hwmon_power_cap_alarm] = "power%d_cap_alarm",
+ [hwmon_power_min_alarm] = "power%d_min_alarm",
[hwmon_power_max_alarm] = "power%d_max_alarm",
+ [hwmon_power_lcrit_alarm] = "power%d_lcrit_alarm",
[hwmon_power_crit_alarm] = "power%d_crit_alarm",
};
diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h
index 1b74ad11a5a4..b217101ca76e 100644
--- a/include/linux/hwmon.h
+++ b/include/linux/hwmon.h
@@ -188,12 +188,16 @@ enum hwmon_power_attributes {
hwmon_power_cap_hyst,
hwmon_power_cap_max,
hwmon_power_cap_min,
+ hwmon_power_min,
hwmon_power_max,
hwmon_power_crit,
+ hwmon_power_lcrit,
hwmon_power_label,
hwmon_power_alarm,
hwmon_power_cap_alarm,
+ hwmon_power_min_alarm,
hwmon_power_max_alarm,
+ hwmon_power_lcrit_alarm,
hwmon_power_crit_alarm,
};
@@ -214,12 +218,16 @@ enum hwmon_power_attributes {
#define HWMON_P_CAP_HYST BIT(hwmon_power_cap_hyst)
#define HWMON_P_CAP_MAX BIT(hwmon_power_cap_max)
#define HWMON_P_CAP_MIN BIT(hwmon_power_cap_min)
+#define HWMON_P_MIN BIT(hwmon_power_min)
#define HWMON_P_MAX BIT(hwmon_power_max)
+#define HWMON_P_LCRIT BIT(hwmon_power_lcrit)
#define HWMON_P_CRIT BIT(hwmon_power_crit)
#define HWMON_P_LABEL BIT(hwmon_power_label)
#define HWMON_P_ALARM BIT(hwmon_power_alarm)
#define HWMON_P_CAP_ALARM BIT(hwmon_power_cap_alarm)
+#define HWMON_P_MIN_ALARM BIT(hwmon_power_max_alarm)
#define HWMON_P_MAX_ALARM BIT(hwmon_power_max_alarm)
+#define HWMON_P_LCRIT_ALARM BIT(hwmon_power_lcrit_alarm)
#define HWMON_P_CRIT_ALARM BIT(hwmon_power_crit_alarm)
enum hwmon_energy_attributes {
--
2.18.0
^ permalink raw reply related
* [PATCH net-next 3/4] hwmon: Add helper to tell if a char is invalid in a name
From: Andrew Lunn @ 2018-07-17 19:48 UTC (permalink / raw)
To: David Miller, Guenter Roeck
Cc: netdev, Florian Fainelli, Russell King, linux-hwmon, Andrew Lunn
In-Reply-To: <1531856893-27884-1-git-send-email-andrew@lunn.ch>
HWMON device names are not allowed to contain "-* \t\n". Add a helper
which will return true if passed an invalid character. It can be used
to massage a string into a hwmon compatible name by replacing invalid
characters with '_'.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
include/linux/hwmon.h | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h
index b217101ca76e..9493d4a388db 100644
--- a/include/linux/hwmon.h
+++ b/include/linux/hwmon.h
@@ -398,4 +398,27 @@ devm_hwmon_device_register_with_info(struct device *dev,
void hwmon_device_unregister(struct device *dev);
void devm_hwmon_device_unregister(struct device *dev);
+/**
+ * hwmon_is_bad_char - Is the char invalid in a hwmon name
+ * @ch: the char to be considered
+ *
+ * hwmon_is_bad_char() can be used to determine if the given character
+ * may not be used in a hwmon name.
+ *
+ * Returns true if the char is invalid, false otherwise.
+ */
+static inline bool hwmon_is_bad_char(const char ch)
+{
+ switch (ch) {
+ case '-':
+ case '*':
+ case ' ':
+ case '\t':
+ case '\n':
+ return true;
+ default:
+ return false;
+ }
+}
+
#endif
--
2.18.0
^ permalink raw reply related
* [PATCH net-next 4/4] net: phy: sfp: Add HWMON support for module sensors
From: Andrew Lunn @ 2018-07-17 19:48 UTC (permalink / raw)
To: David Miller, Guenter Roeck
Cc: netdev, Florian Fainelli, Russell King, linux-hwmon, Andrew Lunn
In-Reply-To: <1531856893-27884-1-git-send-email-andrew@lunn.ch>
SFP modules can contain a number of sensors. The EEPROM also contains
recommended alarm and critical values for each sensor, and indications
of if these have been exceeded. Export this information via
HWMON. Currently temperature, VCC, bias current, transmit power, and
possibly receiver power is supported.
The sensors in the modules can either return calibrate or uncalibrated
values. Uncalibrated values need to be manipulated, using coefficients
provided in the SFP EEPROM. Uncalibrated receive power values require
floating point maths in order to calibrate them. Performing this in
the kernel is hard. So if the SFP module indicates it uses
uncalibrated values, RX power is not made available.
With this hwmon device, it is possible to view the sensor values using
lm-sensors programs:
in0: +3.29 V (crit min = +2.90 V, min = +3.00 V)
(max = +3.60 V, crit max = +3.70 V)
temp1: +33.0°C (low = -5.0°C, high = +80.0°C)
(crit low = -10.0°C, crit = +85.0°C)
power1: 1000.00 nW (max = 794.00 uW, min = 50.00 uW) ALARM (LCRIT)
(lcrit = 40.00 uW, crit = 1000.00 uW)
curr1: +0.00 A (crit min = +0.00 A, min = +0.00 A) ALARM (LCRIT, MIN)
(max = +0.01 A, crit max = +0.01 A)
The scaling sensors performs on the bias current is not particularly
good. The raw values are more useful:
curr1:
curr1_input: 0.000
curr1_min: 0.002
curr1_max: 0.010
curr1_lcrit: 0.000
curr1_crit: 0.011
curr1_min_alarm: 1.000
curr1_max_alarm: 0.000
curr1_lcrit_alarm: 1.000
curr1_crit_alarm: 0.000
In order to keep the I2C overhead to a minimum, the constant values,
such as limits and calibration coefficients are read once at module
insertion time. Thus only reading *_input and *_alarm properties
requires i2c read operations.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
RFC->V1
Use __be16 when reading sensor
DIV_ROUND_CLOSEST()
Don't use devm_hwmon_* to fix lifetime issues
Replace invalid chars in hwmon name with _
---
drivers/net/phy/Kconfig | 1 +
drivers/net/phy/sfp.c | 727 ++++++++++++++++++++++++++++++++++++++++
include/linux/sfp.h | 72 +++-
3 files changed, 799 insertions(+), 1 deletion(-)
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 7761536974bf..f31ae4faf4e5 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -215,6 +215,7 @@ config SFP
tristate "SFP cage support"
depends on I2C
select MDIO_I2C
+ imply HWMON
config AMD_PHY
tristate "AMD PHYs"
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index c4c92db86dfa..5661226cf75b 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -1,5 +1,7 @@
+#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
+#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
@@ -131,6 +133,12 @@ struct sfp {
unsigned int sm_retries;
struct sfp_eeprom_id id;
+#if IS_ENABLED(CONFIG_HWMON)
+ struct sfp_diag diag;
+ struct device *hwmon_dev;
+ char *hwmon_name;
+#endif
+
};
static bool sff_module_supported(const struct sfp_eeprom_id *id)
@@ -316,6 +324,719 @@ static unsigned int sfp_check(void *buf, size_t len)
return check;
}
+/* hwmon */
+#if IS_ENABLED(CONFIG_HWMON)
+static umode_t sfp_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct sfp *sfp = data;
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_lcrit_alarm:
+ case hwmon_temp_crit_alarm:
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ case hwmon_temp_lcrit:
+ case hwmon_temp_crit:
+ return 0444;
+ default:
+ return 0;
+ }
+ case hwmon_in:
+ switch (attr) {
+ case hwmon_in_input:
+ case hwmon_in_min_alarm:
+ case hwmon_in_max_alarm:
+ case hwmon_in_lcrit_alarm:
+ case hwmon_in_crit_alarm:
+ case hwmon_in_min:
+ case hwmon_in_max:
+ case hwmon_in_lcrit:
+ case hwmon_in_crit:
+ return 0444;
+ default:
+ return 0;
+ }
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_input:
+ case hwmon_curr_min_alarm:
+ case hwmon_curr_max_alarm:
+ case hwmon_curr_lcrit_alarm:
+ case hwmon_curr_crit_alarm:
+ case hwmon_curr_min:
+ case hwmon_curr_max:
+ case hwmon_curr_lcrit:
+ case hwmon_curr_crit:
+ return 0444;
+ default:
+ return 0;
+ }
+ case hwmon_power:
+ /* External calibration of receive power requires
+ * floating point arithmetic. Doing that in the kernel
+ * is not easy, so just skip it. If the module does
+ * not require external calibration, we can however
+ * show receiver power, since FP is then not needed.
+ */
+ if (sfp->id.ext.diagmon & SFP_DIAGMON_EXT_CAL &&
+ channel == 1)
+ return 0;
+ switch (attr) {
+ case hwmon_power_input:
+ case hwmon_power_min_alarm:
+ case hwmon_power_max_alarm:
+ case hwmon_power_lcrit_alarm:
+ case hwmon_power_crit_alarm:
+ case hwmon_power_min:
+ case hwmon_power_max:
+ case hwmon_power_lcrit:
+ case hwmon_power_crit:
+ return 0444;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+static int sfp_hwmon_read_sensor(struct sfp *sfp, int reg, long *value)
+{
+ __be16 val;
+ int err;
+
+ err = sfp_read(sfp, true, reg, &val, sizeof(val));
+ if (err < 0)
+ return err;
+
+ *value = be16_to_cpu(val);
+
+ return 0;
+}
+
+static void sfp_hwmon_to_rx_power(long *value)
+{
+ *value = DIV_ROUND_CLOSEST(*value, 100);
+}
+
+static void sfp_hwmon_calibrate(struct sfp *sfp, unsigned int slope, int offset,
+ long *value)
+{
+ if (sfp->id.ext.diagmon & SFP_DIAGMON_EXT_CAL)
+ *value = DIV_ROUND_CLOSEST(*value * slope, 256) + offset;
+}
+
+static void sfp_hwmon_calibrate_temp(struct sfp *sfp, long *value)
+{
+ sfp_hwmon_calibrate(sfp, be16_to_cpu(sfp->diag.cal_t_slope),
+ be16_to_cpu(sfp->diag.cal_t_offset), value);
+
+ if (*value >= 0x8000)
+ *value -= 0x10000;
+
+ *value = DIV_ROUND_CLOSEST(*value * 1000, 256);
+}
+
+static void sfp_hwmon_calibrate_vcc(struct sfp *sfp, long *value)
+{
+ sfp_hwmon_calibrate(sfp, be16_to_cpu(sfp->diag.cal_v_slope),
+ be16_to_cpu(sfp->diag.cal_v_offset), value);
+
+ *value = DIV_ROUND_CLOSEST(*value, 10);
+}
+
+static void sfp_hwmon_calibrate_bias(struct sfp *sfp, long *value)
+{
+ sfp_hwmon_calibrate(sfp, be16_to_cpu(sfp->diag.cal_txi_slope),
+ be16_to_cpu(sfp->diag.cal_txi_offset), value);
+
+ *value = DIV_ROUND_CLOSEST(*value, 500);
+}
+
+static void sfp_hwmon_calibrate_tx_power(struct sfp *sfp, long *value)
+{
+ sfp_hwmon_calibrate(sfp, be16_to_cpu(sfp->diag.cal_txpwr_slope),
+ be16_to_cpu(sfp->diag.cal_txpwr_offset), value);
+
+ *value = DIV_ROUND_CLOSEST(*value, 10);
+}
+
+static int sfp_hwmon_read_temp(struct sfp *sfp, int reg, long *value)
+{
+ int err;
+
+ err = sfp_hwmon_read_sensor(sfp, reg, value);
+ if (err < 0)
+ return err;
+
+ sfp_hwmon_calibrate_temp(sfp, value);
+
+ return 0;
+}
+
+static int sfp_hwmon_read_vcc(struct sfp *sfp, int reg, long *value)
+{
+ int err;
+
+ err = sfp_hwmon_read_sensor(sfp, reg, value);
+ if (err < 0)
+ return err;
+
+ sfp_hwmon_calibrate_vcc(sfp, value);
+
+ return 0;
+}
+
+static int sfp_hwmon_read_bias(struct sfp *sfp, int reg, long *value)
+{
+ int err;
+
+ err = sfp_hwmon_read_sensor(sfp, reg, value);
+ if (err < 0)
+ return err;
+
+ sfp_hwmon_calibrate_bias(sfp, value);
+
+ return 0;
+}
+
+static int sfp_hwmon_read_tx_power(struct sfp *sfp, int reg, long *value)
+{
+ int err;
+
+ err = sfp_hwmon_read_sensor(sfp, reg, value);
+ if (err < 0)
+ return err;
+
+ sfp_hwmon_calibrate_tx_power(sfp, value);
+
+ return 0;
+}
+
+static int sfp_hwmon_read_rx_power(struct sfp *sfp, int reg, long *value)
+{
+ int err;
+
+ err = sfp_hwmon_read_sensor(sfp, reg, value);
+ if (err < 0)
+ return err;
+
+ sfp_hwmon_to_rx_power(value);
+
+ return 0;
+}
+
+static int sfp_hwmon_temp(struct sfp *sfp, u32 attr, long *value)
+{
+ u8 status;
+ int err;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ return sfp_hwmon_read_temp(sfp, SFP_TEMP, value);
+
+ case hwmon_temp_lcrit:
+ *value = be16_to_cpu(sfp->diag.temp_low_alarm);
+ sfp_hwmon_calibrate_temp(sfp, value);
+ return 0;
+
+ case hwmon_temp_min:
+ *value = be16_to_cpu(sfp->diag.temp_low_warn);
+ sfp_hwmon_calibrate_temp(sfp, value);
+ return 0;
+ case hwmon_temp_max:
+ *value = be16_to_cpu(sfp->diag.temp_high_warn);
+ sfp_hwmon_calibrate_temp(sfp, value);
+ return 0;
+
+ case hwmon_temp_crit:
+ *value = be16_to_cpu(sfp->diag.temp_high_alarm);
+ sfp_hwmon_calibrate_temp(sfp, value);
+ return 0;
+
+ case hwmon_temp_lcrit_alarm:
+ err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_ALARM0_TEMP_LOW);
+ return 0;
+
+ case hwmon_temp_min_alarm:
+ err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_WARN0_TEMP_LOW);
+ return 0;
+
+ case hwmon_temp_max_alarm:
+ err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_WARN0_TEMP_HIGH);
+ return 0;
+
+ case hwmon_temp_crit_alarm:
+ err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_ALARM0_TEMP_HIGH);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int sfp_hwmon_vcc(struct sfp *sfp, u32 attr, long *value)
+{
+ u8 status;
+ int err;
+
+ switch (attr) {
+ case hwmon_in_input:
+ return sfp_hwmon_read_vcc(sfp, SFP_VCC, value);
+
+ case hwmon_in_lcrit:
+ *value = be16_to_cpu(sfp->diag.volt_low_alarm);
+ sfp_hwmon_calibrate_vcc(sfp, value);
+ return 0;
+
+ case hwmon_in_min:
+ *value = be16_to_cpu(sfp->diag.volt_low_warn);
+ sfp_hwmon_calibrate_vcc(sfp, value);
+ return 0;
+
+ case hwmon_in_max:
+ *value = be16_to_cpu(sfp->diag.volt_high_warn);
+ sfp_hwmon_calibrate_vcc(sfp, value);
+ return 0;
+
+ case hwmon_in_crit:
+ *value = be16_to_cpu(sfp->diag.volt_high_alarm);
+ sfp_hwmon_calibrate_vcc(sfp, value);
+ return 0;
+
+ case hwmon_in_lcrit_alarm:
+ err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_ALARM0_VCC_LOW);
+ return 0;
+
+ case hwmon_in_min_alarm:
+ err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_WARN0_VCC_LOW);
+ return 0;
+
+ case hwmon_in_max_alarm:
+ err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_WARN0_VCC_HIGH);
+ return 0;
+
+ case hwmon_in_crit_alarm:
+ err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_ALARM0_VCC_HIGH);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int sfp_hwmon_bias(struct sfp *sfp, u32 attr, long *value)
+{
+ u8 status;
+ int err;
+
+ switch (attr) {
+ case hwmon_curr_input:
+ return sfp_hwmon_read_bias(sfp, SFP_TX_BIAS, value);
+
+ case hwmon_curr_lcrit:
+ *value = be16_to_cpu(sfp->diag.bias_low_alarm);
+ sfp_hwmon_calibrate_bias(sfp, value);
+ return 0;
+
+ case hwmon_curr_min:
+ *value = be16_to_cpu(sfp->diag.bias_low_warn);
+ sfp_hwmon_calibrate_bias(sfp, value);
+ return 0;
+
+ case hwmon_curr_max:
+ *value = be16_to_cpu(sfp->diag.bias_high_warn);
+ sfp_hwmon_calibrate_bias(sfp, value);
+ return 0;
+
+ case hwmon_curr_crit:
+ *value = be16_to_cpu(sfp->diag.bias_high_alarm);
+ sfp_hwmon_calibrate_bias(sfp, value);
+ return 0;
+
+ case hwmon_curr_lcrit_alarm:
+ err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_ALARM0_TX_BIAS_LOW);
+ return 0;
+
+ case hwmon_curr_min_alarm:
+ err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_WARN0_TX_BIAS_LOW);
+ return 0;
+
+ case hwmon_curr_max_alarm:
+ err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_WARN0_TX_BIAS_HIGH);
+ return 0;
+
+ case hwmon_curr_crit_alarm:
+ err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_ALARM0_TX_BIAS_HIGH);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int sfp_hwmon_tx_power(struct sfp *sfp, u32 attr, long *value)
+{
+ u8 status;
+ int err;
+
+ switch (attr) {
+ case hwmon_power_input:
+ return sfp_hwmon_read_tx_power(sfp, SFP_TX_POWER, value);
+
+ case hwmon_power_lcrit:
+ *value = be16_to_cpu(sfp->diag.txpwr_low_alarm);
+ sfp_hwmon_calibrate_tx_power(sfp, value);
+ return 0;
+
+ case hwmon_power_min:
+ *value = be16_to_cpu(sfp->diag.txpwr_low_warn);
+ sfp_hwmon_calibrate_tx_power(sfp, value);
+ return 0;
+
+ case hwmon_power_max:
+ *value = be16_to_cpu(sfp->diag.txpwr_high_warn);
+ sfp_hwmon_calibrate_tx_power(sfp, value);
+ return 0;
+
+ case hwmon_power_crit:
+ *value = be16_to_cpu(sfp->diag.txpwr_high_alarm);
+ sfp_hwmon_calibrate_tx_power(sfp, value);
+ return 0;
+
+ case hwmon_power_lcrit_alarm:
+ err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_ALARM0_TXPWR_LOW);
+ return 0;
+
+ case hwmon_power_min_alarm:
+ err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_WARN0_TXPWR_LOW);
+ return 0;
+
+ case hwmon_power_max_alarm:
+ err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_WARN0_TXPWR_HIGH);
+ return 0;
+
+ case hwmon_power_crit_alarm:
+ err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_ALARM0_TXPWR_HIGH);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int sfp_hwmon_rx_power(struct sfp *sfp, u32 attr, long *value)
+{
+ u8 status;
+ int err;
+
+ switch (attr) {
+ case hwmon_power_input:
+ return sfp_hwmon_read_rx_power(sfp, SFP_RX_POWER, value);
+
+ case hwmon_power_lcrit:
+ *value = be16_to_cpu(sfp->diag.rxpwr_low_alarm);
+ sfp_hwmon_to_rx_power(value);
+ return 0;
+
+ case hwmon_power_min:
+ *value = be16_to_cpu(sfp->diag.rxpwr_low_warn);
+ sfp_hwmon_to_rx_power(value);
+ return 0;
+
+ case hwmon_power_max:
+ *value = be16_to_cpu(sfp->diag.rxpwr_high_warn);
+ sfp_hwmon_to_rx_power(value);
+ return 0;
+
+ case hwmon_power_crit:
+ *value = be16_to_cpu(sfp->diag.rxpwr_high_alarm);
+ sfp_hwmon_to_rx_power(value);
+ return 0;
+
+ case hwmon_power_lcrit_alarm:
+ err = sfp_read(sfp, true, SFP_ALARM1, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_ALARM1_RXPWR_LOW);
+ return 0;
+
+ case hwmon_power_min_alarm:
+ err = sfp_read(sfp, true, SFP_WARN1, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_WARN1_RXPWR_LOW);
+ return 0;
+
+ case hwmon_power_max_alarm:
+ err = sfp_read(sfp, true, SFP_WARN1, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_WARN1_RXPWR_HIGH);
+ return 0;
+
+ case hwmon_power_crit_alarm:
+ err = sfp_read(sfp, true, SFP_ALARM1, &status, sizeof(status));
+ if (err < 0)
+ return err;
+
+ *value = !!(status & SFP_ALARM1_RXPWR_HIGH);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int sfp_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *value)
+{
+ struct sfp *sfp = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_temp:
+ return sfp_hwmon_temp(sfp, attr, value);
+ case hwmon_in:
+ return sfp_hwmon_vcc(sfp, attr, value);
+ case hwmon_curr:
+ return sfp_hwmon_bias(sfp, attr, value);
+ case hwmon_power:
+ switch (channel) {
+ case 0:
+ return sfp_hwmon_tx_power(sfp, attr, value);
+ case 1:
+ return sfp_hwmon_rx_power(sfp, attr, value);
+ default:
+ return -EOPNOTSUPP;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const struct hwmon_ops sfp_hwmon_ops = {
+ .is_visible = sfp_hwmon_is_visible,
+ .read = sfp_hwmon_read,
+};
+
+static u32 sfp_hwmon_chip_config[] = {
+ HWMON_C_REGISTER_TZ,
+ 0,
+};
+
+static const struct hwmon_channel_info sfp_hwmon_chip = {
+ .type = hwmon_chip,
+ .config = sfp_hwmon_chip_config,
+};
+
+static u32 sfp_hwmon_temp_config[] = {
+ HWMON_T_INPUT |
+ HWMON_T_MAX | HWMON_T_MIN |
+ HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM |
+ HWMON_T_CRIT | HWMON_T_LCRIT |
+ HWMON_T_CRIT_ALARM | HWMON_T_LCRIT_ALARM,
+ 0,
+};
+
+static const struct hwmon_channel_info sfp_hwmon_temp_channel_info = {
+ .type = hwmon_temp,
+ .config = sfp_hwmon_temp_config,
+};
+
+static u32 sfp_hwmon_vcc_config[] = {
+ HWMON_I_INPUT |
+ HWMON_I_MAX | HWMON_I_MIN |
+ HWMON_I_MAX_ALARM | HWMON_I_MIN_ALARM |
+ HWMON_I_CRIT | HWMON_I_LCRIT |
+ HWMON_I_CRIT_ALARM | HWMON_I_LCRIT_ALARM,
+ 0,
+};
+
+static const struct hwmon_channel_info sfp_hwmon_vcc_channel_info = {
+ .type = hwmon_in,
+ .config = sfp_hwmon_vcc_config,
+};
+
+static u32 sfp_hwmon_bias_config[] = {
+ HWMON_C_INPUT |
+ HWMON_C_MAX | HWMON_C_MIN |
+ HWMON_C_MAX_ALARM | HWMON_C_MIN_ALARM |
+ HWMON_C_CRIT | HWMON_C_LCRIT |
+ HWMON_C_CRIT_ALARM | HWMON_C_LCRIT_ALARM,
+ 0,
+};
+
+static const struct hwmon_channel_info sfp_hwmon_bias_channel_info = {
+ .type = hwmon_curr,
+ .config = sfp_hwmon_bias_config,
+};
+
+static u32 sfp_hwmon_power_config[] = {
+ /* Transmit power */
+ HWMON_P_INPUT |
+ HWMON_P_MAX | HWMON_P_MIN |
+ HWMON_P_MAX_ALARM | HWMON_P_MIN_ALARM |
+ HWMON_P_CRIT | HWMON_P_LCRIT |
+ HWMON_P_CRIT_ALARM | HWMON_P_LCRIT_ALARM,
+ /* Receive power */
+ HWMON_P_INPUT |
+ HWMON_P_MAX | HWMON_P_MIN |
+ HWMON_P_MAX_ALARM | HWMON_P_MIN_ALARM |
+ HWMON_P_CRIT | HWMON_P_LCRIT |
+ HWMON_P_CRIT_ALARM | HWMON_P_LCRIT_ALARM,
+ 0,
+};
+
+static const struct hwmon_channel_info sfp_hwmon_power_channel_info = {
+ .type = hwmon_power,
+ .config = sfp_hwmon_power_config,
+};
+
+static const struct hwmon_channel_info *sfp_hwmon_info[] = {
+ &sfp_hwmon_chip,
+ &sfp_hwmon_vcc_channel_info,
+ &sfp_hwmon_temp_channel_info,
+ &sfp_hwmon_bias_channel_info,
+ &sfp_hwmon_power_channel_info,
+ NULL,
+};
+
+static const struct hwmon_chip_info sfp_hwmon_chip_info = {
+ .ops = &sfp_hwmon_ops,
+ .info = sfp_hwmon_info,
+};
+
+static int sfp_hwmon_insert(struct sfp *sfp)
+{
+ int err, i;
+
+ if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE)
+ return 0;
+
+ if (!(sfp->id.ext.diagmon & SFP_DIAGMON_DDM))
+ return 0;
+
+ if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE)
+ /* This driver in general does not support address
+ * change.
+ */
+ return 0;
+
+ err = sfp_read(sfp, true, 0, &sfp->diag, sizeof(sfp->diag));
+ if (err < 0)
+ return err;
+
+ sfp->hwmon_name = kstrdup(dev_name(sfp->dev), GFP_KERNEL);
+ if (!sfp->hwmon_name)
+ return -ENODEV;
+
+ for (i = 0; sfp->hwmon_name[i]; i++)
+ if (hwmon_is_bad_char(sfp->hwmon_name[i]))
+ sfp->hwmon_name[i] = '_';
+
+ sfp->hwmon_dev = hwmon_device_register_with_info(sfp->dev,
+ sfp->hwmon_name, sfp,
+ &sfp_hwmon_chip_info,
+ NULL);
+
+ return PTR_ERR_OR_ZERO(sfp->hwmon_dev);
+}
+
+static void sfp_hwmon_remove(struct sfp *sfp)
+{
+ hwmon_device_unregister(sfp->hwmon_dev);
+ kfree(sfp->hwmon_name);
+}
+#else
+static int sfp_hwmon_insert(struct sfp *sfp)
+{
+ return 0;
+}
+
+static void sfp_hwmon_remove(struct sfp *sfp)
+{
+}
+#endif
+
/* Helpers */
static void sfp_module_tx_disable(struct sfp *sfp)
{
@@ -636,6 +1357,10 @@ static int sfp_sm_mod_probe(struct sfp *sfp)
dev_warn(sfp->dev,
"module address swap to access page 0xA2 is not supported.\n");
+ ret = sfp_hwmon_insert(sfp);
+ if (ret < 0)
+ return ret;
+
ret = sfp_module_insert(sfp->sfp_bus, &sfp->id);
if (ret < 0)
return ret;
@@ -647,6 +1372,8 @@ static void sfp_sm_mod_remove(struct sfp *sfp)
{
sfp_module_remove(sfp->sfp_bus);
+ sfp_hwmon_remove(sfp);
+
if (sfp->mod_phy)
sfp_sm_phy_detach(sfp);
diff --git a/include/linux/sfp.h b/include/linux/sfp.h
index ebce9e24906a..d37518e89db2 100644
--- a/include/linux/sfp.h
+++ b/include/linux/sfp.h
@@ -231,6 +231,50 @@ struct sfp_eeprom_id {
struct sfp_eeprom_ext ext;
} __packed;
+struct sfp_diag {
+ __be16 temp_high_alarm;
+ __be16 temp_low_alarm;
+ __be16 temp_high_warn;
+ __be16 temp_low_warn;
+ __be16 volt_high_alarm;
+ __be16 volt_low_alarm;
+ __be16 volt_high_warn;
+ __be16 volt_low_warn;
+ __be16 bias_high_alarm;
+ __be16 bias_low_alarm;
+ __be16 bias_high_warn;
+ __be16 bias_low_warn;
+ __be16 txpwr_high_alarm;
+ __be16 txpwr_low_alarm;
+ __be16 txpwr_high_warn;
+ __be16 txpwr_low_warn;
+ __be16 rxpwr_high_alarm;
+ __be16 rxpwr_low_alarm;
+ __be16 rxpwr_high_warn;
+ __be16 rxpwr_low_warn;
+ __be16 laser_temp_high_alarm;
+ __be16 laser_temp_low_alarm;
+ __be16 laser_temp_high_warn;
+ __be16 laser_temp_low_warn;
+ __be16 tec_cur_high_alarm;
+ __be16 tec_cur_low_alarm;
+ __be16 tec_cur_high_warn;
+ __be16 tec_cur_low_warn;
+ __be32 cal_rxpwr4;
+ __be32 cal_rxpwr3;
+ __be32 cal_rxpwr2;
+ __be32 cal_rxpwr1;
+ __be32 cal_rxpwr0;
+ __be16 cal_txi_slope;
+ __be16 cal_txi_offset;
+ __be16 cal_txpwr_slope;
+ __be16 cal_txpwr_offset;
+ __be16 cal_t_slope;
+ __be16 cal_t_offset;
+ __be16 cal_v_slope;
+ __be16 cal_v_offset;
+} __packed;
+
/* SFP EEPROM registers */
enum {
SFP_PHYS_ID = 0x00,
@@ -384,7 +428,33 @@ enum {
SFP_TEC_CUR = 0x6c,
SFP_STATUS = 0x6e,
- SFP_ALARM = 0x70,
+ SFP_ALARM0 = 0x70,
+ SFP_ALARM0_TEMP_HIGH = BIT(7),
+ SFP_ALARM0_TEMP_LOW = BIT(6),
+ SFP_ALARM0_VCC_HIGH = BIT(5),
+ SFP_ALARM0_VCC_LOW = BIT(4),
+ SFP_ALARM0_TX_BIAS_HIGH = BIT(3),
+ SFP_ALARM0_TX_BIAS_LOW = BIT(2),
+ SFP_ALARM0_TXPWR_HIGH = BIT(1),
+ SFP_ALARM0_TXPWR_LOW = BIT(0),
+
+ SFP_ALARM1 = 0x71,
+ SFP_ALARM1_RXPWR_HIGH = BIT(7),
+ SFP_ALARM1_RXPWR_LOW = BIT(6),
+
+ SFP_WARN0 = 0x74,
+ SFP_WARN0_TEMP_HIGH = BIT(7),
+ SFP_WARN0_TEMP_LOW = BIT(6),
+ SFP_WARN0_VCC_HIGH = BIT(5),
+ SFP_WARN0_VCC_LOW = BIT(4),
+ SFP_WARN0_TX_BIAS_HIGH = BIT(3),
+ SFP_WARN0_TX_BIAS_LOW = BIT(2),
+ SFP_WARN0_TXPWR_HIGH = BIT(1),
+ SFP_WARN0_TXPWR_LOW = BIT(0),
+
+ SFP_WARN1 = 0x75,
+ SFP_WARN1_RXPWR_HIGH = BIT(7),
+ SFP_WARN1_RXPWR_LOW = BIT(6),
SFP_EXT_STATUS = 0x76,
SFP_VSL = 0x78,
--
2.18.0
^ permalink raw reply related
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