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, ×tamp, 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