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 21ffc60b55SMarek Vasut #include "ksz_common.h" 227049f9b5STristram Ha 23c2e86691STristram Ha void ksz_update_port_member(struct ksz_device *dev, int port) 24b987e98eSWoojung Huh { 25*b3612ccdSOleksij Rempel struct ksz_port *p = &dev->ports[port]; 26*b3612ccdSOleksij Rempel struct dsa_switch *ds = dev->ds; 27*b3612ccdSOleksij Rempel u8 port_member = 0, cpu_port; 28*b3612ccdSOleksij Rempel const struct dsa_port *dp; 29b987e98eSWoojung Huh int i; 30b987e98eSWoojung Huh 31*b3612ccdSOleksij Rempel if (!dsa_is_user_port(ds, port)) 32*b3612ccdSOleksij Rempel return; 33*b3612ccdSOleksij Rempel 34*b3612ccdSOleksij Rempel dp = dsa_to_port(ds, port); 35*b3612ccdSOleksij Rempel cpu_port = BIT(dsa_upstream_port(ds, port)); 36*b3612ccdSOleksij Rempel 37*b3612ccdSOleksij Rempel for (i = 0; i < ds->num_ports; i++) { 38*b3612ccdSOleksij Rempel const struct dsa_port *other_dp = dsa_to_port(ds, i); 39*b3612ccdSOleksij Rempel struct ksz_port *other_p = &dev->ports[i]; 40*b3612ccdSOleksij Rempel u8 val = 0; 41*b3612ccdSOleksij Rempel 42*b3612ccdSOleksij Rempel if (!dsa_is_user_port(ds, i)) 43c2e86691STristram Ha continue; 44*b3612ccdSOleksij Rempel if (port == i) 45*b3612ccdSOleksij Rempel continue; 46*b3612ccdSOleksij Rempel if (!dp->bridge_dev || dp->bridge_dev != other_dp->bridge_dev) 47c2e86691STristram Ha continue; 48b987e98eSWoojung Huh 49*b3612ccdSOleksij Rempel if (other_p->stp_state == BR_STATE_FORWARDING && 50*b3612ccdSOleksij Rempel p->stp_state == BR_STATE_FORWARDING) { 51*b3612ccdSOleksij Rempel val |= BIT(port); 52*b3612ccdSOleksij Rempel port_member |= BIT(i); 53b987e98eSWoojung Huh } 54*b3612ccdSOleksij Rempel 55*b3612ccdSOleksij Rempel dev->dev_ops->cfg_port_member(dev, i, val | cpu_port); 56*b3612ccdSOleksij Rempel } 57*b3612ccdSOleksij Rempel 58*b3612ccdSOleksij Rempel dev->dev_ops->cfg_port_member(dev, port, port_member | cpu_port); 59b987e98eSWoojung Huh } 60c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_update_port_member); 61b987e98eSWoojung Huh 627c6ff470STristram Ha static void port_r_cnt(struct ksz_device *dev, int port) 637c6ff470STristram Ha { 647c6ff470STristram Ha struct ksz_port_mib *mib = &dev->ports[port].mib; 657c6ff470STristram Ha u64 *dropped; 667c6ff470STristram Ha 677c6ff470STristram Ha /* Some ports may not have MIB counters before SWITCH_COUNTER_NUM. */ 687c6ff470STristram Ha while (mib->cnt_ptr < dev->reg_mib_cnt) { 697c6ff470STristram Ha dev->dev_ops->r_mib_cnt(dev, port, mib->cnt_ptr, 707c6ff470STristram Ha &mib->counters[mib->cnt_ptr]); 717c6ff470STristram Ha ++mib->cnt_ptr; 727c6ff470STristram Ha } 737c6ff470STristram Ha 747c6ff470STristram Ha /* last one in storage */ 757c6ff470STristram Ha dropped = &mib->counters[dev->mib_cnt]; 767c6ff470STristram Ha 777c6ff470STristram Ha /* Some ports may not have MIB counters after SWITCH_COUNTER_NUM. */ 787c6ff470STristram Ha while (mib->cnt_ptr < dev->mib_cnt) { 797c6ff470STristram Ha dev->dev_ops->r_mib_pkt(dev, port, mib->cnt_ptr, 807c6ff470STristram Ha dropped, &mib->counters[mib->cnt_ptr]); 817c6ff470STristram Ha ++mib->cnt_ptr; 827c6ff470STristram Ha } 837c6ff470STristram Ha mib->cnt_ptr = 0; 847c6ff470STristram Ha } 857c6ff470STristram Ha 867c6ff470STristram Ha static void ksz_mib_read_work(struct work_struct *work) 877c6ff470STristram Ha { 887c6ff470STristram Ha struct ksz_device *dev = container_of(work, struct ksz_device, 89469b390eSGeorge McCollister mib_read.work); 907c6ff470STristram Ha struct ksz_port_mib *mib; 917c6ff470STristram Ha struct ksz_port *p; 927c6ff470STristram Ha int i; 937c6ff470STristram Ha 94c9f4633bSMichael Grzeschik for (i = 0; i < dev->port_cnt; i++) { 956bb9e376SRobert Hancock if (dsa_is_unused_port(dev->ds, i)) 966bb9e376SRobert Hancock continue; 976bb9e376SRobert Hancock 987c6ff470STristram Ha p = &dev->ports[i]; 997c6ff470STristram Ha mib = &p->mib; 1007c6ff470STristram Ha mutex_lock(&mib->cnt_mutex); 1017c6ff470STristram Ha 1027c6ff470STristram Ha /* Only read MIB counters when the port is told to do. 1037c6ff470STristram Ha * If not, read only dropped counters when link is not up. 1047c6ff470STristram Ha */ 1057c6ff470STristram Ha if (!p->read) { 1067c6ff470STristram Ha const struct dsa_port *dp = dsa_to_port(dev->ds, i); 1077c6ff470STristram Ha 1087c6ff470STristram Ha if (!netif_carrier_ok(dp->slave)) 1097c6ff470STristram Ha mib->cnt_ptr = dev->reg_mib_cnt; 1107c6ff470STristram Ha } 1117c6ff470STristram Ha port_r_cnt(dev, i); 1127c6ff470STristram Ha p->read = false; 1137c6ff470STristram Ha mutex_unlock(&mib->cnt_mutex); 1147c6ff470STristram Ha } 1157c6ff470STristram Ha 116469b390eSGeorge McCollister schedule_delayed_work(&dev->mib_read, dev->mib_read_interval); 1177c6ff470STristram Ha } 1187c6ff470STristram Ha 1197c6ff470STristram Ha void ksz_init_mib_timer(struct ksz_device *dev) 1207c6ff470STristram Ha { 1217c6ff470STristram Ha int i; 1227c6ff470STristram Ha 123469b390eSGeorge McCollister INIT_DELAYED_WORK(&dev->mib_read, ksz_mib_read_work); 124469b390eSGeorge McCollister 125c9f4633bSMichael Grzeschik for (i = 0; i < dev->port_cnt; i++) 1267c6ff470STristram Ha dev->dev_ops->port_init_cnt(dev, i); 1277c6ff470STristram Ha } 1287c6ff470STristram Ha EXPORT_SYMBOL_GPL(ksz_init_mib_timer); 1297c6ff470STristram Ha 130c2e86691STristram Ha int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg) 131b987e98eSWoojung Huh { 132b987e98eSWoojung Huh struct ksz_device *dev = ds->priv; 133c2e86691STristram Ha u16 val = 0xffff; 134b987e98eSWoojung Huh 135c2e86691STristram Ha dev->dev_ops->r_phy(dev, addr, reg, &val); 136b987e98eSWoojung Huh 137b987e98eSWoojung Huh return val; 138b987e98eSWoojung Huh } 139c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_phy_read16); 140b987e98eSWoojung Huh 141c2e86691STristram Ha int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val) 142b987e98eSWoojung Huh { 143b987e98eSWoojung Huh struct ksz_device *dev = ds->priv; 144b987e98eSWoojung Huh 145c2e86691STristram Ha dev->dev_ops->w_phy(dev, addr, reg, val); 146b987e98eSWoojung Huh 147b987e98eSWoojung Huh return 0; 148b987e98eSWoojung Huh } 149c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_phy_write16); 150b987e98eSWoojung Huh 151143a102eSCodrin Ciubotariu void ksz_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, 152143a102eSCodrin Ciubotariu phy_interface_t interface) 153c30d894bSTristram Ha { 154c30d894bSTristram Ha struct ksz_device *dev = ds->priv; 155c30d894bSTristram Ha struct ksz_port *p = &dev->ports[port]; 156c30d894bSTristram Ha 157c30d894bSTristram Ha /* Read all MIB counters when the link is going down. */ 158c30d894bSTristram Ha p->read = true; 1598098bd69SChristian Eggers /* timer started */ 1608098bd69SChristian Eggers if (dev->mib_read_interval) 161469b390eSGeorge McCollister schedule_delayed_work(&dev->mib_read, 0); 162143a102eSCodrin Ciubotariu } 163143a102eSCodrin Ciubotariu EXPORT_SYMBOL_GPL(ksz_mac_link_down); 164143a102eSCodrin Ciubotariu 165c2e86691STristram Ha int ksz_sset_count(struct dsa_switch *ds, int port, int sset) 166b987e98eSWoojung Huh { 167b987e98eSWoojung Huh struct ksz_device *dev = ds->priv; 168b987e98eSWoojung Huh 16989f09048SFlorian Fainelli if (sset != ETH_SS_STATS) 17089f09048SFlorian Fainelli return 0; 17189f09048SFlorian Fainelli 172c2e86691STristram Ha return dev->mib_cnt; 173b987e98eSWoojung Huh } 174c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_sset_count); 175b987e98eSWoojung Huh 1767c6ff470STristram Ha void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf) 1777c6ff470STristram Ha { 1787c6ff470STristram Ha const struct dsa_port *dp = dsa_to_port(ds, port); 1797c6ff470STristram Ha struct ksz_device *dev = ds->priv; 1807c6ff470STristram Ha struct ksz_port_mib *mib; 1817c6ff470STristram Ha 1827c6ff470STristram Ha mib = &dev->ports[port].mib; 1837c6ff470STristram Ha mutex_lock(&mib->cnt_mutex); 1847c6ff470STristram Ha 1857c6ff470STristram Ha /* Only read dropped counters if no link. */ 1867c6ff470STristram Ha if (!netif_carrier_ok(dp->slave)) 1877c6ff470STristram Ha mib->cnt_ptr = dev->reg_mib_cnt; 1887c6ff470STristram Ha port_r_cnt(dev, port); 1897c6ff470STristram Ha memcpy(buf, mib->counters, dev->mib_cnt * sizeof(u64)); 1907c6ff470STristram Ha mutex_unlock(&mib->cnt_mutex); 1917c6ff470STristram Ha } 1927c6ff470STristram Ha EXPORT_SYMBOL_GPL(ksz_get_ethtool_stats); 1937c6ff470STristram Ha 194c2e86691STristram Ha int ksz_port_bridge_join(struct dsa_switch *ds, int port, 195c2e86691STristram Ha struct net_device *br) 196b987e98eSWoojung Huh { 197c2e86691STristram Ha /* port_stp_state_set() will be called after to put the port in 198c2e86691STristram Ha * appropriate state so there is no need to do anything. 199c2e86691STristram Ha */ 200b987e98eSWoojung Huh 201b987e98eSWoojung Huh return 0; 202b987e98eSWoojung Huh } 203c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_bridge_join); 204b987e98eSWoojung Huh 205c2e86691STristram Ha void ksz_port_bridge_leave(struct dsa_switch *ds, int port, 206c2e86691STristram Ha struct net_device *br) 207c2e86691STristram Ha { 208c2e86691STristram Ha /* port_stp_state_set() will be called after to put the port in 209c2e86691STristram Ha * forwarding state so there is no need to do anything. 210c2e86691STristram Ha */ 211c2e86691STristram Ha } 212c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_bridge_leave); 213c2e86691STristram Ha 214c2e86691STristram Ha void ksz_port_fast_age(struct dsa_switch *ds, int port) 215c2e86691STristram Ha { 216c2e86691STristram Ha struct ksz_device *dev = ds->priv; 217c2e86691STristram Ha 218c2e86691STristram Ha dev->dev_ops->flush_dyn_mac_table(dev, port); 219c2e86691STristram Ha } 220c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_fast_age); 221c2e86691STristram Ha 222c2e86691STristram Ha int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, 223c2e86691STristram Ha void *data) 224b987e98eSWoojung Huh { 225b987e98eSWoojung Huh struct ksz_device *dev = ds->priv; 226b987e98eSWoojung Huh int ret = 0; 227c2e86691STristram Ha u16 i = 0; 228c2e86691STristram Ha u16 entries = 0; 229c2e86691STristram Ha u8 timestamp = 0; 230c2e86691STristram Ha u8 fid; 231c2e86691STristram Ha u8 member; 232b987e98eSWoojung Huh struct alu_struct alu; 233b987e98eSWoojung Huh 234b987e98eSWoojung Huh do { 235c2e86691STristram Ha alu.is_static = false; 236c2e86691STristram Ha ret = dev->dev_ops->r_dyn_mac_table(dev, i, alu.mac, &fid, 237c2e86691STristram Ha &member, ×tamp, 238c2e86691STristram Ha &entries); 239c2e86691STristram Ha if (!ret && (member & BIT(port))) { 2402bedde1aSArkadi Sharshevsky ret = cb(alu.mac, alu.fid, alu.is_static, data); 241b987e98eSWoojung Huh if (ret) 242c2e86691STristram Ha break; 243b987e98eSWoojung Huh } 244c2e86691STristram Ha i++; 245c2e86691STristram Ha } while (i < entries); 246c2e86691STristram Ha if (i >= entries) 247c2e86691STristram Ha ret = 0; 248b987e98eSWoojung Huh 249b987e98eSWoojung Huh return ret; 250b987e98eSWoojung Huh } 251c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_fdb_dump); 252b987e98eSWoojung Huh 253a52b2da7SVladimir Oltean int ksz_port_mdb_add(struct dsa_switch *ds, int port, 2543709aadcSVivien Didelot const struct switchdev_obj_port_mdb *mdb) 255b987e98eSWoojung Huh { 256b987e98eSWoojung Huh struct ksz_device *dev = ds->priv; 257c2e86691STristram Ha struct alu_struct alu; 258b987e98eSWoojung Huh int index; 259c2e86691STristram Ha int empty = 0; 260b987e98eSWoojung Huh 261c2e86691STristram Ha alu.port_forward = 0; 262c2e86691STristram Ha for (index = 0; index < dev->num_statics; index++) { 263c2e86691STristram Ha if (!dev->dev_ops->r_sta_mac_table(dev, index, &alu)) { 264c2e86691STristram Ha /* Found one already in static MAC table. */ 265c2e86691STristram Ha if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) && 266c2e86691STristram Ha alu.fid == mdb->vid) 267c2e86691STristram Ha break; 268c2e86691STristram Ha /* Remember the first empty entry. */ 269c2e86691STristram Ha } else if (!empty) { 270c2e86691STristram Ha empty = index + 1; 271c2e86691STristram Ha } 272c2e86691STristram Ha } 273b987e98eSWoojung Huh 274c2e86691STristram Ha /* no available entry */ 275c2e86691STristram Ha if (index == dev->num_statics && !empty) 276a52b2da7SVladimir Oltean return -ENOSPC; 277c2e86691STristram Ha 278c2e86691STristram Ha /* add entry */ 279c2e86691STristram Ha if (index == dev->num_statics) { 280c2e86691STristram Ha index = empty - 1; 281c2e86691STristram Ha memset(&alu, 0, sizeof(alu)); 282c2e86691STristram Ha memcpy(alu.mac, mdb->addr, ETH_ALEN); 283c2e86691STristram Ha alu.is_static = true; 284c2e86691STristram Ha } 285c2e86691STristram Ha alu.port_forward |= BIT(port); 286c2e86691STristram Ha if (mdb->vid) { 287c2e86691STristram Ha alu.is_use_fid = true; 288c2e86691STristram Ha 289c2e86691STristram Ha /* Need a way to map VID to FID. */ 290c2e86691STristram Ha alu.fid = mdb->vid; 291c2e86691STristram Ha } 292c2e86691STristram Ha dev->dev_ops->w_sta_mac_table(dev, index, &alu); 293a52b2da7SVladimir Oltean 294a52b2da7SVladimir Oltean return 0; 295c2e86691STristram Ha } 296c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_mdb_add); 297c2e86691STristram Ha 298c2e86691STristram Ha int ksz_port_mdb_del(struct dsa_switch *ds, int port, 299c2e86691STristram Ha const struct switchdev_obj_port_mdb *mdb) 300c2e86691STristram Ha { 301c2e86691STristram Ha struct ksz_device *dev = ds->priv; 302c2e86691STristram Ha struct alu_struct alu; 303c2e86691STristram Ha int index; 304c2e86691STristram Ha int ret = 0; 305b987e98eSWoojung Huh 306b987e98eSWoojung Huh for (index = 0; index < dev->num_statics; index++) { 307c2e86691STristram Ha if (!dev->dev_ops->r_sta_mac_table(dev, index, &alu)) { 308c2e86691STristram Ha /* Found one already in static MAC table. */ 309c2e86691STristram Ha if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) && 310c2e86691STristram Ha alu.fid == mdb->vid) 311b987e98eSWoojung Huh break; 312b987e98eSWoojung Huh } 313b987e98eSWoojung Huh } 314b987e98eSWoojung Huh 315b987e98eSWoojung Huh /* no available entry */ 316b987e98eSWoojung Huh if (index == dev->num_statics) 317b987e98eSWoojung Huh goto exit; 318b987e98eSWoojung Huh 319b987e98eSWoojung Huh /* clear port */ 320c2e86691STristram Ha alu.port_forward &= ~BIT(port); 321c2e86691STristram Ha if (!alu.port_forward) 322c2e86691STristram Ha alu.is_static = false; 323c2e86691STristram Ha dev->dev_ops->w_sta_mac_table(dev, index, &alu); 324b987e98eSWoojung Huh 325b987e98eSWoojung Huh exit: 326b987e98eSWoojung Huh return ret; 327b987e98eSWoojung Huh } 328c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_mdb_del); 329b987e98eSWoojung Huh 330c2e86691STristram Ha int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) 331b987e98eSWoojung Huh { 332b987e98eSWoojung Huh struct ksz_device *dev = ds->priv; 333b987e98eSWoojung Huh 33474be4babSVivien Didelot if (!dsa_is_user_port(ds, port)) 33574be4babSVivien Didelot return 0; 33674be4babSVivien Didelot 337c2e86691STristram Ha /* setup slave port */ 338c2e86691STristram Ha dev->dev_ops->port_setup(dev, port, false); 339b987e98eSWoojung Huh 340c2e86691STristram Ha /* port_stp_state_set() will be called after to enable the port so 341c2e86691STristram Ha * there is no need to do anything. 342c2e86691STristram Ha */ 343b987e98eSWoojung Huh 344b987e98eSWoojung Huh return 0; 345b987e98eSWoojung Huh } 346c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_enable_port); 347b987e98eSWoojung Huh 348ee394feaSMarek Vasut struct ksz_device *ksz_switch_alloc(struct device *base, void *priv) 349b987e98eSWoojung Huh { 350b987e98eSWoojung Huh struct dsa_switch *ds; 351b987e98eSWoojung Huh struct ksz_device *swdev; 352b987e98eSWoojung Huh 3537e99e347SVivien Didelot ds = devm_kzalloc(base, sizeof(*ds), GFP_KERNEL); 354b987e98eSWoojung Huh if (!ds) 355b987e98eSWoojung Huh return NULL; 356b987e98eSWoojung Huh 3577e99e347SVivien Didelot ds->dev = base; 3587e99e347SVivien Didelot ds->num_ports = DSA_MAX_PORTS; 3597e99e347SVivien Didelot 360b987e98eSWoojung Huh swdev = devm_kzalloc(base, sizeof(*swdev), GFP_KERNEL); 361b987e98eSWoojung Huh if (!swdev) 362b987e98eSWoojung Huh return NULL; 363b987e98eSWoojung Huh 364b987e98eSWoojung Huh ds->priv = swdev; 365b987e98eSWoojung Huh swdev->dev = base; 366b987e98eSWoojung Huh 367b987e98eSWoojung Huh swdev->ds = ds; 368b987e98eSWoojung Huh swdev->priv = priv; 369b987e98eSWoojung Huh 370b987e98eSWoojung Huh return swdev; 371b987e98eSWoojung Huh } 372b987e98eSWoojung Huh EXPORT_SYMBOL(ksz_switch_alloc); 373b987e98eSWoojung Huh 374c2e86691STristram Ha int ksz_switch_register(struct ksz_device *dev, 375c2e86691STristram Ha const struct ksz_dev_ops *ops) 376b987e98eSWoojung Huh { 377912aae27SHelmut Grohne struct device_node *port, *ports; 3780c65b2b9SAndrew Lunn phy_interface_t interface; 379edecfa98SHelmut Grohne unsigned int port_num; 380b987e98eSWoojung Huh int ret; 381b987e98eSWoojung Huh 382b987e98eSWoojung Huh if (dev->pdata) 383b987e98eSWoojung Huh dev->chip_id = dev->pdata->chip_id; 384b987e98eSWoojung Huh 385924352c3SMarek Vasut dev->reset_gpio = devm_gpiod_get_optional(dev->dev, "reset", 386924352c3SMarek Vasut GPIOD_OUT_LOW); 387924352c3SMarek Vasut if (IS_ERR(dev->reset_gpio)) 388924352c3SMarek Vasut return PTR_ERR(dev->reset_gpio); 389924352c3SMarek Vasut 390924352c3SMarek Vasut if (dev->reset_gpio) { 39122e72b5eSMarek Vasut gpiod_set_value_cansleep(dev->reset_gpio, 1); 3925b797980SPaul Barker usleep_range(10000, 12000); 39322e72b5eSMarek Vasut gpiod_set_value_cansleep(dev->reset_gpio, 0); 3941c45ba93SMarek Vasut msleep(100); 395924352c3SMarek Vasut } 396924352c3SMarek Vasut 3977049f9b5STristram Ha mutex_init(&dev->dev_mutex); 398013572a2SMarek Vasut mutex_init(&dev->regmap_mutex); 399284fb78eSTristram Ha mutex_init(&dev->alu_mutex); 400284fb78eSTristram Ha mutex_init(&dev->vlan_mutex); 401284fb78eSTristram Ha 402c2e86691STristram Ha dev->dev_ops = ops; 403c2e86691STristram Ha 404c2e86691STristram Ha if (dev->dev_ops->detect(dev)) 405b987e98eSWoojung Huh return -EINVAL; 406b987e98eSWoojung Huh 407c2e86691STristram Ha ret = dev->dev_ops->init(dev); 408b987e98eSWoojung Huh if (ret) 409b987e98eSWoojung Huh return ret; 410b987e98eSWoojung Huh 4118c29bebbSTristram Ha /* Host port interface will be self detected, or specifically set in 4128c29bebbSTristram Ha * device tree. 4138c29bebbSTristram Ha */ 414edecfa98SHelmut Grohne for (port_num = 0; port_num < dev->port_cnt; ++port_num) 415edecfa98SHelmut Grohne dev->ports[port_num].interface = PHY_INTERFACE_MODE_NA; 416c2e86691STristram Ha if (dev->dev->of_node) { 4170c65b2b9SAndrew Lunn ret = of_get_phy_mode(dev->dev->of_node, &interface); 4180c65b2b9SAndrew Lunn if (ret == 0) 419edecfa98SHelmut Grohne dev->compat_interface = interface; 42044e53c88SChristian Eggers ports = of_get_child_by_name(dev->dev->of_node, "ethernet-ports"); 42144e53c88SChristian Eggers if (!ports) 422912aae27SHelmut Grohne ports = of_get_child_by_name(dev->dev->of_node, "ports"); 423912aae27SHelmut Grohne if (ports) 424912aae27SHelmut Grohne for_each_available_child_of_node(ports, port) { 425912aae27SHelmut Grohne if (of_property_read_u32(port, "reg", 426912aae27SHelmut Grohne &port_num)) 427edecfa98SHelmut Grohne continue; 42884f7e0bbSkernel test robot if (!(dev->port_mask & BIT(port_num))) { 42984f7e0bbSkernel test robot of_node_put(port); 430edecfa98SHelmut Grohne return -EINVAL; 43184f7e0bbSkernel test robot } 432912aae27SHelmut Grohne of_get_phy_mode(port, 433912aae27SHelmut Grohne &dev->ports[port_num].interface); 434edecfa98SHelmut Grohne } 43579c8bd15SRobert Hancock dev->synclko_125 = of_property_read_bool(dev->dev->of_node, 43679c8bd15SRobert Hancock "microchip,synclko-125"); 437c2e86691STristram Ha } 438c2e86691STristram Ha 439c2e86691STristram Ha ret = dsa_register_switch(dev->ds); 440c2e86691STristram Ha if (ret) { 441c2e86691STristram Ha dev->dev_ops->exit(dev); 442c2e86691STristram Ha return ret; 443c2e86691STristram Ha } 444c2e86691STristram Ha 4458098bd69SChristian Eggers /* Read MIB counters every 30 seconds to avoid overflow. */ 4468098bd69SChristian Eggers dev->mib_read_interval = msecs_to_jiffies(30000); 4478098bd69SChristian Eggers 4488098bd69SChristian Eggers /* Start the MIB timer. */ 4498098bd69SChristian Eggers schedule_delayed_work(&dev->mib_read, 0); 4508098bd69SChristian Eggers 451c2e86691STristram Ha return 0; 452b987e98eSWoojung Huh } 453b987e98eSWoojung Huh EXPORT_SYMBOL(ksz_switch_register); 454b987e98eSWoojung Huh 455b987e98eSWoojung Huh void ksz_switch_remove(struct ksz_device *dev) 456b987e98eSWoojung Huh { 4577c6ff470STristram Ha /* timer started */ 458ef1100efSArun Ramadoss if (dev->mib_read_interval) { 459ef1100efSArun Ramadoss dev->mib_read_interval = 0; 460469b390eSGeorge McCollister cancel_delayed_work_sync(&dev->mib_read); 461ef1100efSArun Ramadoss } 4627c6ff470STristram Ha 463c2e86691STristram Ha dev->dev_ops->exit(dev); 464b987e98eSWoojung Huh dsa_unregister_switch(dev->ds); 465924352c3SMarek Vasut 466924352c3SMarek Vasut if (dev->reset_gpio) 46722e72b5eSMarek Vasut gpiod_set_value_cansleep(dev->reset_gpio, 1); 468924352c3SMarek Vasut 469b987e98eSWoojung Huh } 470b987e98eSWoojung Huh EXPORT_SYMBOL(ksz_switch_remove); 471b987e98eSWoojung Huh 472b987e98eSWoojung Huh MODULE_AUTHOR("Woojung Huh <Woojung.Huh@microchip.com>"); 473b987e98eSWoojung Huh MODULE_DESCRIPTION("Microchip KSZ Series Switch DSA Driver"); 474b987e98eSWoojung Huh MODULE_LICENSE("GPL"); 475