diff options
Diffstat (limited to 'drivers/net/dsa/microchip/ksz_common.c')
-rw-r--r-- | drivers/net/dsa/microchip/ksz_common.c | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 6f728429eec6..eecfbf30cdc2 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c | |||
@@ -40,6 +40,85 @@ void ksz_update_port_member(struct ksz_device *dev, int port) | |||
40 | } | 40 | } |
41 | EXPORT_SYMBOL_GPL(ksz_update_port_member); | 41 | EXPORT_SYMBOL_GPL(ksz_update_port_member); |
42 | 42 | ||
43 | static void port_r_cnt(struct ksz_device *dev, int port) | ||
44 | { | ||
45 | struct ksz_port_mib *mib = &dev->ports[port].mib; | ||
46 | u64 *dropped; | ||
47 | |||
48 | /* Some ports may not have MIB counters before SWITCH_COUNTER_NUM. */ | ||
49 | while (mib->cnt_ptr < dev->reg_mib_cnt) { | ||
50 | dev->dev_ops->r_mib_cnt(dev, port, mib->cnt_ptr, | ||
51 | &mib->counters[mib->cnt_ptr]); | ||
52 | ++mib->cnt_ptr; | ||
53 | } | ||
54 | |||
55 | /* last one in storage */ | ||
56 | dropped = &mib->counters[dev->mib_cnt]; | ||
57 | |||
58 | /* Some ports may not have MIB counters after SWITCH_COUNTER_NUM. */ | ||
59 | while (mib->cnt_ptr < dev->mib_cnt) { | ||
60 | dev->dev_ops->r_mib_pkt(dev, port, mib->cnt_ptr, | ||
61 | dropped, &mib->counters[mib->cnt_ptr]); | ||
62 | ++mib->cnt_ptr; | ||
63 | } | ||
64 | mib->cnt_ptr = 0; | ||
65 | } | ||
66 | |||
67 | static void ksz_mib_read_work(struct work_struct *work) | ||
68 | { | ||
69 | struct ksz_device *dev = container_of(work, struct ksz_device, | ||
70 | mib_read); | ||
71 | struct ksz_port_mib *mib; | ||
72 | struct ksz_port *p; | ||
73 | int i; | ||
74 | |||
75 | for (i = 0; i < dev->mib_port_cnt; i++) { | ||
76 | p = &dev->ports[i]; | ||
77 | mib = &p->mib; | ||
78 | mutex_lock(&mib->cnt_mutex); | ||
79 | |||
80 | /* Only read MIB counters when the port is told to do. | ||
81 | * If not, read only dropped counters when link is not up. | ||
82 | */ | ||
83 | if (!p->read) { | ||
84 | const struct dsa_port *dp = dsa_to_port(dev->ds, i); | ||
85 | |||
86 | if (!netif_carrier_ok(dp->slave)) | ||
87 | mib->cnt_ptr = dev->reg_mib_cnt; | ||
88 | } | ||
89 | port_r_cnt(dev, i); | ||
90 | p->read = false; | ||
91 | mutex_unlock(&mib->cnt_mutex); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | static void mib_monitor(struct timer_list *t) | ||
96 | { | ||
97 | struct ksz_device *dev = from_timer(dev, t, mib_read_timer); | ||
98 | |||
99 | mod_timer(&dev->mib_read_timer, jiffies + dev->mib_read_interval); | ||
100 | schedule_work(&dev->mib_read); | ||
101 | } | ||
102 | |||
103 | void ksz_init_mib_timer(struct ksz_device *dev) | ||
104 | { | ||
105 | int i; | ||
106 | |||
107 | /* Read MIB counters every 30 seconds to avoid overflow. */ | ||
108 | dev->mib_read_interval = msecs_to_jiffies(30000); | ||
109 | |||
110 | INIT_WORK(&dev->mib_read, ksz_mib_read_work); | ||
111 | timer_setup(&dev->mib_read_timer, mib_monitor, 0); | ||
112 | |||
113 | for (i = 0; i < dev->mib_port_cnt; i++) | ||
114 | dev->dev_ops->port_init_cnt(dev, i); | ||
115 | |||
116 | /* Start the timer 2 seconds later. */ | ||
117 | dev->mib_read_timer.expires = jiffies + msecs_to_jiffies(2000); | ||
118 | add_timer(&dev->mib_read_timer); | ||
119 | } | ||
120 | EXPORT_SYMBOL_GPL(ksz_init_mib_timer); | ||
121 | |||
43 | int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg) | 122 | int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg) |
44 | { | 123 | { |
45 | struct ksz_device *dev = ds->priv; | 124 | struct ksz_device *dev = ds->priv; |
@@ -72,6 +151,24 @@ int ksz_sset_count(struct dsa_switch *ds, int port, int sset) | |||
72 | } | 151 | } |
73 | EXPORT_SYMBOL_GPL(ksz_sset_count); | 152 | EXPORT_SYMBOL_GPL(ksz_sset_count); |
74 | 153 | ||
154 | void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf) | ||
155 | { | ||
156 | const struct dsa_port *dp = dsa_to_port(ds, port); | ||
157 | struct ksz_device *dev = ds->priv; | ||
158 | struct ksz_port_mib *mib; | ||
159 | |||
160 | mib = &dev->ports[port].mib; | ||
161 | mutex_lock(&mib->cnt_mutex); | ||
162 | |||
163 | /* Only read dropped counters if no link. */ | ||
164 | if (!netif_carrier_ok(dp->slave)) | ||
165 | mib->cnt_ptr = dev->reg_mib_cnt; | ||
166 | port_r_cnt(dev, port); | ||
167 | memcpy(buf, mib->counters, dev->mib_cnt * sizeof(u64)); | ||
168 | mutex_unlock(&mib->cnt_mutex); | ||
169 | } | ||
170 | EXPORT_SYMBOL_GPL(ksz_get_ethtool_stats); | ||
171 | |||
75 | int ksz_port_bridge_join(struct dsa_switch *ds, int port, | 172 | int ksz_port_bridge_join(struct dsa_switch *ds, int port, |
76 | struct net_device *br) | 173 | struct net_device *br) |
77 | { | 174 | { |
@@ -339,6 +436,12 @@ EXPORT_SYMBOL(ksz_switch_register); | |||
339 | 436 | ||
340 | void ksz_switch_remove(struct ksz_device *dev) | 437 | void ksz_switch_remove(struct ksz_device *dev) |
341 | { | 438 | { |
439 | /* timer started */ | ||
440 | if (dev->mib_read_timer.expires) { | ||
441 | del_timer_sync(&dev->mib_read_timer); | ||
442 | flush_work(&dev->mib_read); | ||
443 | } | ||
444 | |||
342 | dev->dev_ops->exit(dev); | 445 | dev->dev_ops->exit(dev); |
343 | dsa_unregister_switch(dev->ds); | 446 | dsa_unregister_switch(dev->ds); |
344 | 447 | ||