15b79c72eSTristram Ha // SPDX-License-Identifier: GPL-2.0
2b987e98eSWoojung Huh /*
3b987e98eSWoojung Huh  * Microchip switch driver main logic
4b987e98eSWoojung Huh  *
542fc6a4cSTristram Ha  * Copyright (C) 2017-2019 Microchip Technology Inc.
6b987e98eSWoojung Huh  */
7b987e98eSWoojung Huh 
8b987e98eSWoojung Huh #include <linux/delay.h>
9b987e98eSWoojung Huh #include <linux/export.h>
10924352c3SMarek Vasut #include <linux/gpio/consumer.h>
11b987e98eSWoojung Huh #include <linux/kernel.h>
12b987e98eSWoojung Huh #include <linux/module.h>
13b987e98eSWoojung Huh #include <linux/platform_data/microchip-ksz.h>
14b987e98eSWoojung Huh #include <linux/phy.h>
15b987e98eSWoojung Huh #include <linux/etherdevice.h>
16b987e98eSWoojung Huh #include <linux/if_bridge.h>
17c2e86691STristram Ha #include <linux/of_net.h>
18b987e98eSWoojung Huh #include <net/dsa.h>
19b987e98eSWoojung Huh #include <net/switchdev.h>
20b987e98eSWoojung Huh 
21b987e98eSWoojung Huh #include "ksz_priv.h"
22b987e98eSWoojung Huh 
237049f9b5STristram Ha void ksz_port_cleanup(struct ksz_device *dev, int port)
247049f9b5STristram Ha {
257049f9b5STristram Ha 	/* Common code for port cleanup. */
267049f9b5STristram Ha 	mutex_lock(&dev->dev_mutex);
277049f9b5STristram Ha 	dev->on_ports &= ~(1 << port);
287049f9b5STristram Ha 	dev->live_ports &= ~(1 << port);
297049f9b5STristram Ha 	mutex_unlock(&dev->dev_mutex);
307049f9b5STristram Ha }
317049f9b5STristram Ha EXPORT_SYMBOL_GPL(ksz_port_cleanup);
327049f9b5STristram Ha 
33c2e86691STristram Ha void ksz_update_port_member(struct ksz_device *dev, int port)
34b987e98eSWoojung Huh {
35c2e86691STristram Ha 	struct ksz_port *p;
36b987e98eSWoojung Huh 	int i;
37b987e98eSWoojung Huh 
38c2e86691STristram Ha 	for (i = 0; i < dev->port_cnt; i++) {
39c2e86691STristram Ha 		if (i == port || i == dev->cpu_port)
40c2e86691STristram Ha 			continue;
41c2e86691STristram Ha 		p = &dev->ports[i];
42c2e86691STristram Ha 		if (!(dev->member & (1 << i)))
43c2e86691STristram Ha 			continue;
44b987e98eSWoojung Huh 
45c2e86691STristram Ha 		/* Port is a member of the bridge and is forwarding. */
46c2e86691STristram Ha 		if (p->stp_state == BR_STATE_FORWARDING &&
47c2e86691STristram Ha 		    p->member != dev->member)
48c2e86691STristram Ha 			dev->dev_ops->cfg_port_member(dev, i, dev->member);
49b987e98eSWoojung Huh 	}
50b987e98eSWoojung Huh }
51c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_update_port_member);
52b987e98eSWoojung Huh 
537c6ff470STristram Ha static void port_r_cnt(struct ksz_device *dev, int port)
547c6ff470STristram Ha {
557c6ff470STristram Ha 	struct ksz_port_mib *mib = &dev->ports[port].mib;
567c6ff470STristram Ha 	u64 *dropped;
577c6ff470STristram Ha 
587c6ff470STristram Ha 	/* Some ports may not have MIB counters before SWITCH_COUNTER_NUM. */
597c6ff470STristram Ha 	while (mib->cnt_ptr < dev->reg_mib_cnt) {
607c6ff470STristram Ha 		dev->dev_ops->r_mib_cnt(dev, port, mib->cnt_ptr,
617c6ff470STristram Ha 					&mib->counters[mib->cnt_ptr]);
627c6ff470STristram Ha 		++mib->cnt_ptr;
637c6ff470STristram Ha 	}
647c6ff470STristram Ha 
657c6ff470STristram Ha 	/* last one in storage */
667c6ff470STristram Ha 	dropped = &mib->counters[dev->mib_cnt];
677c6ff470STristram Ha 
687c6ff470STristram Ha 	/* Some ports may not have MIB counters after SWITCH_COUNTER_NUM. */
697c6ff470STristram Ha 	while (mib->cnt_ptr < dev->mib_cnt) {
707c6ff470STristram Ha 		dev->dev_ops->r_mib_pkt(dev, port, mib->cnt_ptr,
717c6ff470STristram Ha 					dropped, &mib->counters[mib->cnt_ptr]);
727c6ff470STristram Ha 		++mib->cnt_ptr;
737c6ff470STristram Ha 	}
747c6ff470STristram Ha 	mib->cnt_ptr = 0;
757c6ff470STristram Ha }
767c6ff470STristram Ha 
777c6ff470STristram Ha static void ksz_mib_read_work(struct work_struct *work)
787c6ff470STristram Ha {
797c6ff470STristram Ha 	struct ksz_device *dev = container_of(work, struct ksz_device,
807c6ff470STristram Ha 					      mib_read);
817c6ff470STristram Ha 	struct ksz_port_mib *mib;
827c6ff470STristram Ha 	struct ksz_port *p;
837c6ff470STristram Ha 	int i;
847c6ff470STristram Ha 
857c6ff470STristram Ha 	for (i = 0; i < dev->mib_port_cnt; i++) {
866bb9e376SRobert Hancock 		if (dsa_is_unused_port(dev->ds, i))
876bb9e376SRobert Hancock 			continue;
886bb9e376SRobert Hancock 
897c6ff470STristram Ha 		p = &dev->ports[i];
907c6ff470STristram Ha 		mib = &p->mib;
917c6ff470STristram Ha 		mutex_lock(&mib->cnt_mutex);
927c6ff470STristram Ha 
937c6ff470STristram Ha 		/* Only read MIB counters when the port is told to do.
947c6ff470STristram Ha 		 * If not, read only dropped counters when link is not up.
957c6ff470STristram Ha 		 */
967c6ff470STristram Ha 		if (!p->read) {
977c6ff470STristram Ha 			const struct dsa_port *dp = dsa_to_port(dev->ds, i);
987c6ff470STristram Ha 
997c6ff470STristram Ha 			if (!netif_carrier_ok(dp->slave))
1007c6ff470STristram Ha 				mib->cnt_ptr = dev->reg_mib_cnt;
1017c6ff470STristram Ha 		}
1027c6ff470STristram Ha 		port_r_cnt(dev, i);
1037c6ff470STristram Ha 		p->read = false;
1047c6ff470STristram Ha 		mutex_unlock(&mib->cnt_mutex);
1057c6ff470STristram Ha 	}
1067c6ff470STristram Ha }
1077c6ff470STristram Ha 
1087c6ff470STristram Ha static void mib_monitor(struct timer_list *t)
1097c6ff470STristram Ha {
1107c6ff470STristram Ha 	struct ksz_device *dev = from_timer(dev, t, mib_read_timer);
1117c6ff470STristram Ha 
1127c6ff470STristram Ha 	mod_timer(&dev->mib_read_timer, jiffies + dev->mib_read_interval);
1137c6ff470STristram Ha 	schedule_work(&dev->mib_read);
1147c6ff470STristram Ha }
1157c6ff470STristram Ha 
1167c6ff470STristram Ha void ksz_init_mib_timer(struct ksz_device *dev)
1177c6ff470STristram Ha {
1187c6ff470STristram Ha 	int i;
1197c6ff470STristram Ha 
1207c6ff470STristram Ha 	/* Read MIB counters every 30 seconds to avoid overflow. */
1217c6ff470STristram Ha 	dev->mib_read_interval = msecs_to_jiffies(30000);
1227c6ff470STristram Ha 
1237c6ff470STristram Ha 	INIT_WORK(&dev->mib_read, ksz_mib_read_work);
1247c6ff470STristram Ha 	timer_setup(&dev->mib_read_timer, mib_monitor, 0);
1257c6ff470STristram Ha 
1267c6ff470STristram Ha 	for (i = 0; i < dev->mib_port_cnt; i++)
1277c6ff470STristram Ha 		dev->dev_ops->port_init_cnt(dev, i);
1287c6ff470STristram Ha 
1297c6ff470STristram Ha 	/* Start the timer 2 seconds later. */
1307c6ff470STristram Ha 	dev->mib_read_timer.expires = jiffies + msecs_to_jiffies(2000);
1317c6ff470STristram Ha 	add_timer(&dev->mib_read_timer);
1327c6ff470STristram Ha }
1337c6ff470STristram Ha EXPORT_SYMBOL_GPL(ksz_init_mib_timer);
1347c6ff470STristram Ha 
135c2e86691STristram Ha int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg)
136b987e98eSWoojung Huh {
137b987e98eSWoojung Huh 	struct ksz_device *dev = ds->priv;
138c2e86691STristram Ha 	u16 val = 0xffff;
139b987e98eSWoojung Huh 
140c2e86691STristram Ha 	dev->dev_ops->r_phy(dev, addr, reg, &val);
141b987e98eSWoojung Huh 
142b987e98eSWoojung Huh 	return val;
143b987e98eSWoojung Huh }
144c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_phy_read16);
145b987e98eSWoojung Huh 
146c2e86691STristram Ha int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val)
147b987e98eSWoojung Huh {
148b987e98eSWoojung Huh 	struct ksz_device *dev = ds->priv;
149b987e98eSWoojung Huh 
150c2e86691STristram Ha 	dev->dev_ops->w_phy(dev, addr, reg, val);
151b987e98eSWoojung Huh 
152b987e98eSWoojung Huh 	return 0;
153b987e98eSWoojung Huh }
154c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_phy_write16);
155b987e98eSWoojung Huh 
156c30d894bSTristram Ha void ksz_adjust_link(struct dsa_switch *ds, int port,
157c30d894bSTristram Ha 		     struct phy_device *phydev)
158c30d894bSTristram Ha {
159c30d894bSTristram Ha 	struct ksz_device *dev = ds->priv;
160c30d894bSTristram Ha 	struct ksz_port *p = &dev->ports[port];
161c30d894bSTristram Ha 
162c30d894bSTristram Ha 	/* Read all MIB counters when the link is going down. */
163c30d894bSTristram Ha 	if (!phydev->link) {
164c30d894bSTristram Ha 		p->read = true;
165c30d894bSTristram Ha 		schedule_work(&dev->mib_read);
166c30d894bSTristram Ha 	}
1677049f9b5STristram Ha 	mutex_lock(&dev->dev_mutex);
1687049f9b5STristram Ha 	if (!phydev->link)
1697049f9b5STristram Ha 		dev->live_ports &= ~(1 << port);
1707049f9b5STristram Ha 	else
1717049f9b5STristram Ha 		/* Remember which port is connected and active. */
1727049f9b5STristram Ha 		dev->live_ports |= (1 << port) & dev->on_ports;
1737049f9b5STristram Ha 	mutex_unlock(&dev->dev_mutex);
174c30d894bSTristram Ha }
175c30d894bSTristram Ha EXPORT_SYMBOL_GPL(ksz_adjust_link);
176c30d894bSTristram Ha 
177c2e86691STristram Ha int ksz_sset_count(struct dsa_switch *ds, int port, int sset)
178b987e98eSWoojung Huh {
179b987e98eSWoojung Huh 	struct ksz_device *dev = ds->priv;
180b987e98eSWoojung Huh 
18189f09048SFlorian Fainelli 	if (sset != ETH_SS_STATS)
18289f09048SFlorian Fainelli 		return 0;
18389f09048SFlorian Fainelli 
184c2e86691STristram Ha 	return dev->mib_cnt;
185b987e98eSWoojung Huh }
186c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_sset_count);
187b987e98eSWoojung Huh 
1887c6ff470STristram Ha void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf)
1897c6ff470STristram Ha {
1907c6ff470STristram Ha 	const struct dsa_port *dp = dsa_to_port(ds, port);
1917c6ff470STristram Ha 	struct ksz_device *dev = ds->priv;
1927c6ff470STristram Ha 	struct ksz_port_mib *mib;
1937c6ff470STristram Ha 
1947c6ff470STristram Ha 	mib = &dev->ports[port].mib;
1957c6ff470STristram Ha 	mutex_lock(&mib->cnt_mutex);
1967c6ff470STristram Ha 
1977c6ff470STristram Ha 	/* Only read dropped counters if no link. */
1987c6ff470STristram Ha 	if (!netif_carrier_ok(dp->slave))
1997c6ff470STristram Ha 		mib->cnt_ptr = dev->reg_mib_cnt;
2007c6ff470STristram Ha 	port_r_cnt(dev, port);
2017c6ff470STristram Ha 	memcpy(buf, mib->counters, dev->mib_cnt * sizeof(u64));
2027c6ff470STristram Ha 	mutex_unlock(&mib->cnt_mutex);
2037c6ff470STristram Ha }
2047c6ff470STristram Ha EXPORT_SYMBOL_GPL(ksz_get_ethtool_stats);
2057c6ff470STristram Ha 
206c2e86691STristram Ha int ksz_port_bridge_join(struct dsa_switch *ds, int port,
207c2e86691STristram Ha 			 struct net_device *br)
208b987e98eSWoojung Huh {
209b987e98eSWoojung Huh 	struct ksz_device *dev = ds->priv;
210b987e98eSWoojung Huh 
2117049f9b5STristram Ha 	mutex_lock(&dev->dev_mutex);
212c2e86691STristram Ha 	dev->br_member |= (1 << port);
2137049f9b5STristram Ha 	mutex_unlock(&dev->dev_mutex);
214c2e86691STristram Ha 
215c2e86691STristram Ha 	/* port_stp_state_set() will be called after to put the port in
216c2e86691STristram Ha 	 * appropriate state so there is no need to do anything.
217c2e86691STristram Ha 	 */
218b987e98eSWoojung Huh 
219b987e98eSWoojung Huh 	return 0;
220b987e98eSWoojung Huh }
221c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_bridge_join);
222b987e98eSWoojung Huh 
223c2e86691STristram Ha void ksz_port_bridge_leave(struct dsa_switch *ds, int port,
224c2e86691STristram Ha 			   struct net_device *br)
225c2e86691STristram Ha {
226c2e86691STristram Ha 	struct ksz_device *dev = ds->priv;
227c2e86691STristram Ha 
2287049f9b5STristram Ha 	mutex_lock(&dev->dev_mutex);
229c2e86691STristram Ha 	dev->br_member &= ~(1 << port);
230c2e86691STristram Ha 	dev->member &= ~(1 << port);
2317049f9b5STristram Ha 	mutex_unlock(&dev->dev_mutex);
232c2e86691STristram Ha 
233c2e86691STristram Ha 	/* port_stp_state_set() will be called after to put the port in
234c2e86691STristram Ha 	 * forwarding state so there is no need to do anything.
235c2e86691STristram Ha 	 */
236c2e86691STristram Ha }
237c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_bridge_leave);
238c2e86691STristram Ha 
239c2e86691STristram Ha void ksz_port_fast_age(struct dsa_switch *ds, int port)
240c2e86691STristram Ha {
241c2e86691STristram Ha 	struct ksz_device *dev = ds->priv;
242c2e86691STristram Ha 
243c2e86691STristram Ha 	dev->dev_ops->flush_dyn_mac_table(dev, port);
244c2e86691STristram Ha }
245c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_fast_age);
246c2e86691STristram Ha 
247c2e86691STristram Ha int ksz_port_vlan_prepare(struct dsa_switch *ds, int port,
24880e02360SVivien Didelot 			  const struct switchdev_obj_port_vlan *vlan)
249b987e98eSWoojung Huh {
250b987e98eSWoojung Huh 	/* nothing needed */
251b987e98eSWoojung Huh 
252b987e98eSWoojung Huh 	return 0;
253b987e98eSWoojung Huh }
254c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_vlan_prepare);
255b987e98eSWoojung Huh 
256c2e86691STristram Ha int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb,
257c2e86691STristram Ha 		      void *data)
258b987e98eSWoojung Huh {
259b987e98eSWoojung Huh 	struct ksz_device *dev = ds->priv;
260b987e98eSWoojung Huh 	int ret = 0;
261c2e86691STristram Ha 	u16 i = 0;
262c2e86691STristram Ha 	u16 entries = 0;
263c2e86691STristram Ha 	u8 timestamp = 0;
264c2e86691STristram Ha 	u8 fid;
265c2e86691STristram Ha 	u8 member;
266b987e98eSWoojung Huh 	struct alu_struct alu;
267b987e98eSWoojung Huh 
268b987e98eSWoojung Huh 	do {
269c2e86691STristram Ha 		alu.is_static = false;
270c2e86691STristram Ha 		ret = dev->dev_ops->r_dyn_mac_table(dev, i, alu.mac, &fid,
271c2e86691STristram Ha 						    &member, &timestamp,
272c2e86691STristram Ha 						    &entries);
273c2e86691STristram Ha 		if (!ret && (member & BIT(port))) {
2742bedde1aSArkadi Sharshevsky 			ret = cb(alu.mac, alu.fid, alu.is_static, data);
275b987e98eSWoojung Huh 			if (ret)
276c2e86691STristram Ha 				break;
277b987e98eSWoojung Huh 		}
278c2e86691STristram Ha 		i++;
279c2e86691STristram Ha 	} while (i < entries);
280c2e86691STristram Ha 	if (i >= entries)
281c2e86691STristram Ha 		ret = 0;
282b987e98eSWoojung Huh 
283b987e98eSWoojung Huh 	return ret;
284b987e98eSWoojung Huh }
285c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_fdb_dump);
286b987e98eSWoojung Huh 
287c2e86691STristram Ha int ksz_port_mdb_prepare(struct dsa_switch *ds, int port,
2883709aadcSVivien Didelot 			 const struct switchdev_obj_port_mdb *mdb)
289b987e98eSWoojung Huh {
290b987e98eSWoojung Huh 	/* nothing to do */
291b987e98eSWoojung Huh 	return 0;
292b987e98eSWoojung Huh }
293c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_mdb_prepare);
294b987e98eSWoojung Huh 
295c2e86691STristram Ha void ksz_port_mdb_add(struct dsa_switch *ds, int port,
2963709aadcSVivien Didelot 		      const struct switchdev_obj_port_mdb *mdb)
297b987e98eSWoojung Huh {
298b987e98eSWoojung Huh 	struct ksz_device *dev = ds->priv;
299c2e86691STristram Ha 	struct alu_struct alu;
300b987e98eSWoojung Huh 	int index;
301c2e86691STristram Ha 	int empty = 0;
302b987e98eSWoojung Huh 
303c2e86691STristram Ha 	alu.port_forward = 0;
304c2e86691STristram Ha 	for (index = 0; index < dev->num_statics; index++) {
305c2e86691STristram Ha 		if (!dev->dev_ops->r_sta_mac_table(dev, index, &alu)) {
306c2e86691STristram Ha 			/* Found one already in static MAC table. */
307c2e86691STristram Ha 			if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) &&
308c2e86691STristram Ha 			    alu.fid == mdb->vid)
309c2e86691STristram Ha 				break;
310c2e86691STristram Ha 		/* Remember the first empty entry. */
311c2e86691STristram Ha 		} else if (!empty) {
312c2e86691STristram Ha 			empty = index + 1;
313c2e86691STristram Ha 		}
314c2e86691STristram Ha 	}
315b987e98eSWoojung Huh 
316c2e86691STristram Ha 	/* no available entry */
317c2e86691STristram Ha 	if (index == dev->num_statics && !empty)
318c2e86691STristram Ha 		return;
319c2e86691STristram Ha 
320c2e86691STristram Ha 	/* add entry */
321c2e86691STristram Ha 	if (index == dev->num_statics) {
322c2e86691STristram Ha 		index = empty - 1;
323c2e86691STristram Ha 		memset(&alu, 0, sizeof(alu));
324c2e86691STristram Ha 		memcpy(alu.mac, mdb->addr, ETH_ALEN);
325c2e86691STristram Ha 		alu.is_static = true;
326c2e86691STristram Ha 	}
327c2e86691STristram Ha 	alu.port_forward |= BIT(port);
328c2e86691STristram Ha 	if (mdb->vid) {
329c2e86691STristram Ha 		alu.is_use_fid = true;
330c2e86691STristram Ha 
331c2e86691STristram Ha 		/* Need a way to map VID to FID. */
332c2e86691STristram Ha 		alu.fid = mdb->vid;
333c2e86691STristram Ha 	}
334c2e86691STristram Ha 	dev->dev_ops->w_sta_mac_table(dev, index, &alu);
335c2e86691STristram Ha }
336c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_mdb_add);
337c2e86691STristram Ha 
338c2e86691STristram Ha int ksz_port_mdb_del(struct dsa_switch *ds, int port,
339c2e86691STristram Ha 		     const struct switchdev_obj_port_mdb *mdb)
340c2e86691STristram Ha {
341c2e86691STristram Ha 	struct ksz_device *dev = ds->priv;
342c2e86691STristram Ha 	struct alu_struct alu;
343c2e86691STristram Ha 	int index;
344c2e86691STristram Ha 	int ret = 0;
345b987e98eSWoojung Huh 
346b987e98eSWoojung Huh 	for (index = 0; index < dev->num_statics; index++) {
347c2e86691STristram Ha 		if (!dev->dev_ops->r_sta_mac_table(dev, index, &alu)) {
348c2e86691STristram Ha 			/* Found one already in static MAC table. */
349c2e86691STristram Ha 			if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) &&
350c2e86691STristram Ha 			    alu.fid == mdb->vid)
351b987e98eSWoojung Huh 				break;
352b987e98eSWoojung Huh 		}
353b987e98eSWoojung Huh 	}
354b987e98eSWoojung Huh 
355b987e98eSWoojung Huh 	/* no available entry */
356b987e98eSWoojung Huh 	if (index == dev->num_statics)
357b987e98eSWoojung Huh 		goto exit;
358b987e98eSWoojung Huh 
359b987e98eSWoojung Huh 	/* clear port */
360c2e86691STristram Ha 	alu.port_forward &= ~BIT(port);
361c2e86691STristram Ha 	if (!alu.port_forward)
362c2e86691STristram Ha 		alu.is_static = false;
363c2e86691STristram Ha 	dev->dev_ops->w_sta_mac_table(dev, index, &alu);
364b987e98eSWoojung Huh 
365b987e98eSWoojung Huh exit:
366b987e98eSWoojung Huh 	return ret;
367b987e98eSWoojung Huh }
368c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_mdb_del);
369b987e98eSWoojung Huh 
370c2e86691STristram Ha int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy)
371b987e98eSWoojung Huh {
372b987e98eSWoojung Huh 	struct ksz_device *dev = ds->priv;
373b987e98eSWoojung Huh 
374c2e86691STristram Ha 	/* setup slave port */
375c2e86691STristram Ha 	dev->dev_ops->port_setup(dev, port, false);
37642fc6a4cSTristram Ha 	dev->dev_ops->phy_setup(dev, port, phy);
377b987e98eSWoojung Huh 
378c2e86691STristram Ha 	/* port_stp_state_set() will be called after to enable the port so
379c2e86691STristram Ha 	 * there is no need to do anything.
380c2e86691STristram Ha 	 */
381b987e98eSWoojung Huh 
382b987e98eSWoojung Huh 	return 0;
383b987e98eSWoojung Huh }
384c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_enable_port);
385b987e98eSWoojung Huh 
38675104db0SAndrew Lunn void ksz_disable_port(struct dsa_switch *ds, int port)
387b987e98eSWoojung Huh {
388b987e98eSWoojung Huh 	struct ksz_device *dev = ds->priv;
389b987e98eSWoojung Huh 
390c2e86691STristram Ha 	dev->on_ports &= ~(1 << port);
391c2e86691STristram Ha 	dev->live_ports &= ~(1 << port);
392b987e98eSWoojung Huh 
393c2e86691STristram Ha 	/* port_stp_state_set() will be called after to disable the port so
394c2e86691STristram Ha 	 * there is no need to do anything.
395c2e86691STristram Ha 	 */
396b987e98eSWoojung Huh }
397c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_disable_port);
398b987e98eSWoojung Huh 
399*ee394feaSMarek Vasut struct ksz_device *ksz_switch_alloc(struct device *base, void *priv)
400b987e98eSWoojung Huh {
401b987e98eSWoojung Huh 	struct dsa_switch *ds;
402b987e98eSWoojung Huh 	struct ksz_device *swdev;
403b987e98eSWoojung Huh 
404b987e98eSWoojung Huh 	ds = dsa_switch_alloc(base, DSA_MAX_PORTS);
405b987e98eSWoojung Huh 	if (!ds)
406b987e98eSWoojung Huh 		return NULL;
407b987e98eSWoojung Huh 
408b987e98eSWoojung Huh 	swdev = devm_kzalloc(base, sizeof(*swdev), GFP_KERNEL);
409b987e98eSWoojung Huh 	if (!swdev)
410b987e98eSWoojung Huh 		return NULL;
411b987e98eSWoojung Huh 
412b987e98eSWoojung Huh 	ds->priv = swdev;
413b987e98eSWoojung Huh 	swdev->dev = base;
414b987e98eSWoojung Huh 
415b987e98eSWoojung Huh 	swdev->ds = ds;
416b987e98eSWoojung Huh 	swdev->priv = priv;
417b987e98eSWoojung Huh 
418b987e98eSWoojung Huh 	return swdev;
419b987e98eSWoojung Huh }
420b987e98eSWoojung Huh EXPORT_SYMBOL(ksz_switch_alloc);
421b987e98eSWoojung Huh 
422c2e86691STristram Ha int ksz_switch_register(struct ksz_device *dev,
423c2e86691STristram Ha 			const struct ksz_dev_ops *ops)
424b987e98eSWoojung Huh {
425b987e98eSWoojung Huh 	int ret;
426b987e98eSWoojung Huh 
427b987e98eSWoojung Huh 	if (dev->pdata)
428b987e98eSWoojung Huh 		dev->chip_id = dev->pdata->chip_id;
429b987e98eSWoojung Huh 
430924352c3SMarek Vasut 	dev->reset_gpio = devm_gpiod_get_optional(dev->dev, "reset",
431924352c3SMarek Vasut 						  GPIOD_OUT_LOW);
432924352c3SMarek Vasut 	if (IS_ERR(dev->reset_gpio))
433924352c3SMarek Vasut 		return PTR_ERR(dev->reset_gpio);
434924352c3SMarek Vasut 
435924352c3SMarek Vasut 	if (dev->reset_gpio) {
436924352c3SMarek Vasut 		gpiod_set_value(dev->reset_gpio, 1);
437924352c3SMarek Vasut 		mdelay(10);
438924352c3SMarek Vasut 		gpiod_set_value(dev->reset_gpio, 0);
439924352c3SMarek Vasut 	}
440924352c3SMarek Vasut 
4417049f9b5STristram Ha 	mutex_init(&dev->dev_mutex);
442284fb78eSTristram Ha 	mutex_init(&dev->stats_mutex);
443284fb78eSTristram Ha 	mutex_init(&dev->alu_mutex);
444284fb78eSTristram Ha 	mutex_init(&dev->vlan_mutex);
445284fb78eSTristram Ha 
446c2e86691STristram Ha 	dev->dev_ops = ops;
447c2e86691STristram Ha 
448c2e86691STristram Ha 	if (dev->dev_ops->detect(dev))
449b987e98eSWoojung Huh 		return -EINVAL;
450b987e98eSWoojung Huh 
451c2e86691STristram Ha 	ret = dev->dev_ops->init(dev);
452b987e98eSWoojung Huh 	if (ret)
453b987e98eSWoojung Huh 		return ret;
454b987e98eSWoojung Huh 
4558c29bebbSTristram Ha 	/* Host port interface will be self detected, or specifically set in
4568c29bebbSTristram Ha 	 * device tree.
4578c29bebbSTristram Ha 	 */
458c2e86691STristram Ha 	if (dev->dev->of_node) {
459c2e86691STristram Ha 		ret = of_get_phy_mode(dev->dev->of_node);
460c2e86691STristram Ha 		if (ret >= 0)
461c2e86691STristram Ha 			dev->interface = ret;
46279c8bd15SRobert Hancock 		dev->synclko_125 = of_property_read_bool(dev->dev->of_node,
46379c8bd15SRobert Hancock 							 "microchip,synclko-125");
464c2e86691STristram Ha 	}
465c2e86691STristram Ha 
466c2e86691STristram Ha 	ret = dsa_register_switch(dev->ds);
467c2e86691STristram Ha 	if (ret) {
468c2e86691STristram Ha 		dev->dev_ops->exit(dev);
469c2e86691STristram Ha 		return ret;
470c2e86691STristram Ha 	}
471c2e86691STristram Ha 
472c2e86691STristram Ha 	return 0;
473b987e98eSWoojung Huh }
474b987e98eSWoojung Huh EXPORT_SYMBOL(ksz_switch_register);
475b987e98eSWoojung Huh 
476b987e98eSWoojung Huh void ksz_switch_remove(struct ksz_device *dev)
477b987e98eSWoojung Huh {
4787c6ff470STristram Ha 	/* timer started */
4797c6ff470STristram Ha 	if (dev->mib_read_timer.expires) {
4807c6ff470STristram Ha 		del_timer_sync(&dev->mib_read_timer);
4817c6ff470STristram Ha 		flush_work(&dev->mib_read);
4827c6ff470STristram Ha 	}
4837c6ff470STristram Ha 
484c2e86691STristram Ha 	dev->dev_ops->exit(dev);
485b987e98eSWoojung Huh 	dsa_unregister_switch(dev->ds);
486924352c3SMarek Vasut 
487924352c3SMarek Vasut 	if (dev->reset_gpio)
488924352c3SMarek Vasut 		gpiod_set_value(dev->reset_gpio, 1);
489924352c3SMarek Vasut 
490b987e98eSWoojung Huh }
491b987e98eSWoojung Huh EXPORT_SYMBOL(ksz_switch_remove);
492b987e98eSWoojung Huh 
493b987e98eSWoojung Huh MODULE_AUTHOR("Woojung Huh <Woojung.Huh@microchip.com>");
494b987e98eSWoojung Huh MODULE_DESCRIPTION("Microchip KSZ Series Switch DSA Driver");
495b987e98eSWoojung Huh MODULE_LICENSE("GPL");
496