15b79c72eSTristram Ha // SPDX-License-Identifier: GPL-2.0 2b987e98eSWoojung Huh /* 3b987e98eSWoojung Huh * Microchip switch driver main logic 4b987e98eSWoojung Huh * 5*42fc6a4cSTristram 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 23c2e86691STristram Ha void ksz_update_port_member(struct ksz_device *dev, int port) 24b987e98eSWoojung Huh { 25c2e86691STristram Ha struct ksz_port *p; 26b987e98eSWoojung Huh int i; 27b987e98eSWoojung Huh 28c2e86691STristram Ha for (i = 0; i < dev->port_cnt; i++) { 29c2e86691STristram Ha if (i == port || i == dev->cpu_port) 30c2e86691STristram Ha continue; 31c2e86691STristram Ha p = &dev->ports[i]; 32c2e86691STristram Ha if (!(dev->member & (1 << i))) 33c2e86691STristram Ha continue; 34b987e98eSWoojung Huh 35c2e86691STristram Ha /* Port is a member of the bridge and is forwarding. */ 36c2e86691STristram Ha if (p->stp_state == BR_STATE_FORWARDING && 37c2e86691STristram Ha p->member != dev->member) 38c2e86691STristram Ha dev->dev_ops->cfg_port_member(dev, i, dev->member); 39b987e98eSWoojung Huh } 40b987e98eSWoojung Huh } 41c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_update_port_member); 42b987e98eSWoojung Huh 43c2e86691STristram Ha int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg) 44b987e98eSWoojung Huh { 45b987e98eSWoojung Huh struct ksz_device *dev = ds->priv; 46c2e86691STristram Ha u16 val = 0xffff; 47b987e98eSWoojung Huh 48c2e86691STristram Ha dev->dev_ops->r_phy(dev, addr, reg, &val); 49b987e98eSWoojung Huh 50b987e98eSWoojung Huh return val; 51b987e98eSWoojung Huh } 52c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_phy_read16); 53b987e98eSWoojung Huh 54c2e86691STristram Ha int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val) 55b987e98eSWoojung Huh { 56b987e98eSWoojung Huh struct ksz_device *dev = ds->priv; 57b987e98eSWoojung Huh 58c2e86691STristram Ha dev->dev_ops->w_phy(dev, addr, reg, val); 59b987e98eSWoojung Huh 60b987e98eSWoojung Huh return 0; 61b987e98eSWoojung Huh } 62c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_phy_write16); 63b987e98eSWoojung Huh 64c2e86691STristram Ha int ksz_sset_count(struct dsa_switch *ds, int port, int sset) 65b987e98eSWoojung Huh { 66b987e98eSWoojung Huh struct ksz_device *dev = ds->priv; 67b987e98eSWoojung Huh 6889f09048SFlorian Fainelli if (sset != ETH_SS_STATS) 6989f09048SFlorian Fainelli return 0; 7089f09048SFlorian Fainelli 71c2e86691STristram Ha return dev->mib_cnt; 72b987e98eSWoojung Huh } 73c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_sset_count); 74b987e98eSWoojung Huh 75c2e86691STristram Ha int ksz_port_bridge_join(struct dsa_switch *ds, int port, 76c2e86691STristram Ha struct net_device *br) 77b987e98eSWoojung Huh { 78b987e98eSWoojung Huh struct ksz_device *dev = ds->priv; 79b987e98eSWoojung Huh 80c2e86691STristram Ha dev->br_member |= (1 << port); 81c2e86691STristram Ha 82c2e86691STristram Ha /* port_stp_state_set() will be called after to put the port in 83c2e86691STristram Ha * appropriate state so there is no need to do anything. 84c2e86691STristram Ha */ 85b987e98eSWoojung Huh 86b987e98eSWoojung Huh return 0; 87b987e98eSWoojung Huh } 88c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_bridge_join); 89b987e98eSWoojung Huh 90c2e86691STristram Ha void ksz_port_bridge_leave(struct dsa_switch *ds, int port, 91c2e86691STristram Ha struct net_device *br) 92c2e86691STristram Ha { 93c2e86691STristram Ha struct ksz_device *dev = ds->priv; 94c2e86691STristram Ha 95c2e86691STristram Ha dev->br_member &= ~(1 << port); 96c2e86691STristram Ha dev->member &= ~(1 << port); 97c2e86691STristram Ha 98c2e86691STristram Ha /* port_stp_state_set() will be called after to put the port in 99c2e86691STristram Ha * forwarding state so there is no need to do anything. 100c2e86691STristram Ha */ 101c2e86691STristram Ha } 102c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_bridge_leave); 103c2e86691STristram Ha 104c2e86691STristram Ha void ksz_port_fast_age(struct dsa_switch *ds, int port) 105c2e86691STristram Ha { 106c2e86691STristram Ha struct ksz_device *dev = ds->priv; 107c2e86691STristram Ha 108c2e86691STristram Ha dev->dev_ops->flush_dyn_mac_table(dev, port); 109c2e86691STristram Ha } 110c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_fast_age); 111c2e86691STristram Ha 112c2e86691STristram Ha int ksz_port_vlan_prepare(struct dsa_switch *ds, int port, 11380e02360SVivien Didelot const struct switchdev_obj_port_vlan *vlan) 114b987e98eSWoojung Huh { 115b987e98eSWoojung Huh /* nothing needed */ 116b987e98eSWoojung Huh 117b987e98eSWoojung Huh return 0; 118b987e98eSWoojung Huh } 119c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_vlan_prepare); 120b987e98eSWoojung Huh 121c2e86691STristram Ha int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, 122c2e86691STristram Ha void *data) 123b987e98eSWoojung Huh { 124b987e98eSWoojung Huh struct ksz_device *dev = ds->priv; 125b987e98eSWoojung Huh int ret = 0; 126c2e86691STristram Ha u16 i = 0; 127c2e86691STristram Ha u16 entries = 0; 128c2e86691STristram Ha u8 timestamp = 0; 129c2e86691STristram Ha u8 fid; 130c2e86691STristram Ha u8 member; 131b987e98eSWoojung Huh struct alu_struct alu; 132b987e98eSWoojung Huh 133b987e98eSWoojung Huh do { 134c2e86691STristram Ha alu.is_static = false; 135c2e86691STristram Ha ret = dev->dev_ops->r_dyn_mac_table(dev, i, alu.mac, &fid, 136c2e86691STristram Ha &member, ×tamp, 137c2e86691STristram Ha &entries); 138c2e86691STristram Ha if (!ret && (member & BIT(port))) { 1392bedde1aSArkadi Sharshevsky ret = cb(alu.mac, alu.fid, alu.is_static, data); 140b987e98eSWoojung Huh if (ret) 141c2e86691STristram Ha break; 142b987e98eSWoojung Huh } 143c2e86691STristram Ha i++; 144c2e86691STristram Ha } while (i < entries); 145c2e86691STristram Ha if (i >= entries) 146c2e86691STristram Ha ret = 0; 147b987e98eSWoojung Huh 148b987e98eSWoojung Huh return ret; 149b987e98eSWoojung Huh } 150c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_fdb_dump); 151b987e98eSWoojung Huh 152c2e86691STristram Ha int ksz_port_mdb_prepare(struct dsa_switch *ds, int port, 1533709aadcSVivien Didelot const struct switchdev_obj_port_mdb *mdb) 154b987e98eSWoojung Huh { 155b987e98eSWoojung Huh /* nothing to do */ 156b987e98eSWoojung Huh return 0; 157b987e98eSWoojung Huh } 158c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_mdb_prepare); 159b987e98eSWoojung Huh 160c2e86691STristram Ha void ksz_port_mdb_add(struct dsa_switch *ds, int port, 1613709aadcSVivien Didelot const struct switchdev_obj_port_mdb *mdb) 162b987e98eSWoojung Huh { 163b987e98eSWoojung Huh struct ksz_device *dev = ds->priv; 164c2e86691STristram Ha struct alu_struct alu; 165b987e98eSWoojung Huh int index; 166c2e86691STristram Ha int empty = 0; 167b987e98eSWoojung Huh 168c2e86691STristram Ha alu.port_forward = 0; 169c2e86691STristram Ha for (index = 0; index < dev->num_statics; index++) { 170c2e86691STristram Ha if (!dev->dev_ops->r_sta_mac_table(dev, index, &alu)) { 171c2e86691STristram Ha /* Found one already in static MAC table. */ 172c2e86691STristram Ha if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) && 173c2e86691STristram Ha alu.fid == mdb->vid) 174c2e86691STristram Ha break; 175c2e86691STristram Ha /* Remember the first empty entry. */ 176c2e86691STristram Ha } else if (!empty) { 177c2e86691STristram Ha empty = index + 1; 178c2e86691STristram Ha } 179c2e86691STristram Ha } 180b987e98eSWoojung Huh 181c2e86691STristram Ha /* no available entry */ 182c2e86691STristram Ha if (index == dev->num_statics && !empty) 183c2e86691STristram Ha return; 184c2e86691STristram Ha 185c2e86691STristram Ha /* add entry */ 186c2e86691STristram Ha if (index == dev->num_statics) { 187c2e86691STristram Ha index = empty - 1; 188c2e86691STristram Ha memset(&alu, 0, sizeof(alu)); 189c2e86691STristram Ha memcpy(alu.mac, mdb->addr, ETH_ALEN); 190c2e86691STristram Ha alu.is_static = true; 191c2e86691STristram Ha } 192c2e86691STristram Ha alu.port_forward |= BIT(port); 193c2e86691STristram Ha if (mdb->vid) { 194c2e86691STristram Ha alu.is_use_fid = true; 195c2e86691STristram Ha 196c2e86691STristram Ha /* Need a way to map VID to FID. */ 197c2e86691STristram Ha alu.fid = mdb->vid; 198c2e86691STristram Ha } 199c2e86691STristram Ha dev->dev_ops->w_sta_mac_table(dev, index, &alu); 200c2e86691STristram Ha } 201c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_mdb_add); 202c2e86691STristram Ha 203c2e86691STristram Ha int ksz_port_mdb_del(struct dsa_switch *ds, int port, 204c2e86691STristram Ha const struct switchdev_obj_port_mdb *mdb) 205c2e86691STristram Ha { 206c2e86691STristram Ha struct ksz_device *dev = ds->priv; 207c2e86691STristram Ha struct alu_struct alu; 208c2e86691STristram Ha int index; 209c2e86691STristram Ha int ret = 0; 210b987e98eSWoojung Huh 211b987e98eSWoojung Huh for (index = 0; index < dev->num_statics; index++) { 212c2e86691STristram Ha if (!dev->dev_ops->r_sta_mac_table(dev, index, &alu)) { 213c2e86691STristram Ha /* Found one already in static MAC table. */ 214c2e86691STristram Ha if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) && 215c2e86691STristram Ha alu.fid == mdb->vid) 216b987e98eSWoojung Huh break; 217b987e98eSWoojung Huh } 218b987e98eSWoojung Huh } 219b987e98eSWoojung Huh 220b987e98eSWoojung Huh /* no available entry */ 221b987e98eSWoojung Huh if (index == dev->num_statics) 222b987e98eSWoojung Huh goto exit; 223b987e98eSWoojung Huh 224b987e98eSWoojung Huh /* clear port */ 225c2e86691STristram Ha alu.port_forward &= ~BIT(port); 226c2e86691STristram Ha if (!alu.port_forward) 227c2e86691STristram Ha alu.is_static = false; 228c2e86691STristram Ha dev->dev_ops->w_sta_mac_table(dev, index, &alu); 229b987e98eSWoojung Huh 230b987e98eSWoojung Huh exit: 231b987e98eSWoojung Huh return ret; 232b987e98eSWoojung Huh } 233c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_port_mdb_del); 234b987e98eSWoojung Huh 235c2e86691STristram Ha int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) 236b987e98eSWoojung Huh { 237b987e98eSWoojung Huh struct ksz_device *dev = ds->priv; 238b987e98eSWoojung Huh 239c2e86691STristram Ha /* setup slave port */ 240c2e86691STristram Ha dev->dev_ops->port_setup(dev, port, false); 241*42fc6a4cSTristram Ha dev->dev_ops->phy_setup(dev, port, phy); 242b987e98eSWoojung Huh 243c2e86691STristram Ha /* port_stp_state_set() will be called after to enable the port so 244c2e86691STristram Ha * there is no need to do anything. 245c2e86691STristram Ha */ 246b987e98eSWoojung Huh 247b987e98eSWoojung Huh return 0; 248b987e98eSWoojung Huh } 249c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_enable_port); 250b987e98eSWoojung Huh 251c2e86691STristram Ha void ksz_disable_port(struct dsa_switch *ds, int port, struct phy_device *phy) 252b987e98eSWoojung Huh { 253b987e98eSWoojung Huh struct ksz_device *dev = ds->priv; 254b987e98eSWoojung Huh 255c2e86691STristram Ha dev->on_ports &= ~(1 << port); 256c2e86691STristram Ha dev->live_ports &= ~(1 << port); 257b987e98eSWoojung Huh 258c2e86691STristram Ha /* port_stp_state_set() will be called after to disable the port so 259c2e86691STristram Ha * there is no need to do anything. 260c2e86691STristram Ha */ 261b987e98eSWoojung Huh } 262c2e86691STristram Ha EXPORT_SYMBOL_GPL(ksz_disable_port); 263b987e98eSWoojung Huh 264b987e98eSWoojung Huh struct ksz_device *ksz_switch_alloc(struct device *base, 265b987e98eSWoojung Huh const struct ksz_io_ops *ops, 266b987e98eSWoojung Huh void *priv) 267b987e98eSWoojung Huh { 268b987e98eSWoojung Huh struct dsa_switch *ds; 269b987e98eSWoojung Huh struct ksz_device *swdev; 270b987e98eSWoojung Huh 271b987e98eSWoojung Huh ds = dsa_switch_alloc(base, DSA_MAX_PORTS); 272b987e98eSWoojung Huh if (!ds) 273b987e98eSWoojung Huh return NULL; 274b987e98eSWoojung Huh 275b987e98eSWoojung Huh swdev = devm_kzalloc(base, sizeof(*swdev), GFP_KERNEL); 276b987e98eSWoojung Huh if (!swdev) 277b987e98eSWoojung Huh return NULL; 278b987e98eSWoojung Huh 279b987e98eSWoojung Huh ds->priv = swdev; 280b987e98eSWoojung Huh swdev->dev = base; 281b987e98eSWoojung Huh 282b987e98eSWoojung Huh swdev->ds = ds; 283b987e98eSWoojung Huh swdev->priv = priv; 284b987e98eSWoojung Huh swdev->ops = ops; 285b987e98eSWoojung Huh 286b987e98eSWoojung Huh return swdev; 287b987e98eSWoojung Huh } 288b987e98eSWoojung Huh EXPORT_SYMBOL(ksz_switch_alloc); 289b987e98eSWoojung Huh 290c2e86691STristram Ha int ksz_switch_register(struct ksz_device *dev, 291c2e86691STristram Ha const struct ksz_dev_ops *ops) 292b987e98eSWoojung Huh { 293b987e98eSWoojung Huh int ret; 294b987e98eSWoojung Huh 295b987e98eSWoojung Huh if (dev->pdata) 296b987e98eSWoojung Huh dev->chip_id = dev->pdata->chip_id; 297b987e98eSWoojung Huh 298924352c3SMarek Vasut dev->reset_gpio = devm_gpiod_get_optional(dev->dev, "reset", 299924352c3SMarek Vasut GPIOD_OUT_LOW); 300924352c3SMarek Vasut if (IS_ERR(dev->reset_gpio)) 301924352c3SMarek Vasut return PTR_ERR(dev->reset_gpio); 302924352c3SMarek Vasut 303924352c3SMarek Vasut if (dev->reset_gpio) { 304924352c3SMarek Vasut gpiod_set_value(dev->reset_gpio, 1); 305924352c3SMarek Vasut mdelay(10); 306924352c3SMarek Vasut gpiod_set_value(dev->reset_gpio, 0); 307924352c3SMarek Vasut } 308924352c3SMarek Vasut 309284fb78eSTristram Ha mutex_init(&dev->reg_mutex); 310284fb78eSTristram Ha mutex_init(&dev->stats_mutex); 311284fb78eSTristram Ha mutex_init(&dev->alu_mutex); 312284fb78eSTristram Ha mutex_init(&dev->vlan_mutex); 313284fb78eSTristram Ha 314c2e86691STristram Ha dev->dev_ops = ops; 315c2e86691STristram Ha 316c2e86691STristram Ha if (dev->dev_ops->detect(dev)) 317b987e98eSWoojung Huh return -EINVAL; 318b987e98eSWoojung Huh 319c2e86691STristram Ha ret = dev->dev_ops->init(dev); 320b987e98eSWoojung Huh if (ret) 321b987e98eSWoojung Huh return ret; 322b987e98eSWoojung Huh 323c2e86691STristram Ha dev->interface = PHY_INTERFACE_MODE_MII; 324c2e86691STristram Ha if (dev->dev->of_node) { 325c2e86691STristram Ha ret = of_get_phy_mode(dev->dev->of_node); 326c2e86691STristram Ha if (ret >= 0) 327c2e86691STristram Ha dev->interface = ret; 328c2e86691STristram Ha } 329c2e86691STristram Ha 330c2e86691STristram Ha ret = dsa_register_switch(dev->ds); 331c2e86691STristram Ha if (ret) { 332c2e86691STristram Ha dev->dev_ops->exit(dev); 333c2e86691STristram Ha return ret; 334c2e86691STristram Ha } 335c2e86691STristram Ha 336c2e86691STristram Ha return 0; 337b987e98eSWoojung Huh } 338b987e98eSWoojung Huh EXPORT_SYMBOL(ksz_switch_register); 339b987e98eSWoojung Huh 340b987e98eSWoojung Huh void ksz_switch_remove(struct ksz_device *dev) 341b987e98eSWoojung Huh { 342c2e86691STristram Ha dev->dev_ops->exit(dev); 343b987e98eSWoojung Huh dsa_unregister_switch(dev->ds); 344924352c3SMarek Vasut 345924352c3SMarek Vasut if (dev->reset_gpio) 346924352c3SMarek Vasut gpiod_set_value(dev->reset_gpio, 1); 347924352c3SMarek Vasut 348b987e98eSWoojung Huh } 349b987e98eSWoojung Huh EXPORT_SYMBOL(ksz_switch_remove); 350b987e98eSWoojung Huh 351b987e98eSWoojung Huh MODULE_AUTHOR("Woojung Huh <Woojung.Huh@microchip.com>"); 352b987e98eSWoojung Huh MODULE_DESCRIPTION("Microchip KSZ Series Switch DSA Driver"); 353b987e98eSWoojung Huh MODULE_LICENSE("GPL"); 354