| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Microchip's LAN865x 10BASE-T1S MAC-PHY driver
- *
- * Author: Parthiban Veerasooran <parthiban.veerasooran@microchip.com>
- */
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/phy.h>
- #include <linux/oa_tc6.h>
- #define DRV_NAME "lan8650"
- /* MAC Network Control Register */
- #define LAN865X_REG_MAC_NET_CTL 0x00010000
- #define MAC_NET_CTL_TXEN BIT(3) /* Transmit Enable */
- #define MAC_NET_CTL_RXEN BIT(2) /* Receive Enable */
- /* MAC Network Configuration Reg */
- #define LAN865X_REG_MAC_NET_CFG 0x00010001
- #define MAC_NET_CFG_PROMISCUOUS_MODE BIT(4)
- #define MAC_NET_CFG_MULTICAST_MODE BIT(6)
- #define MAC_NET_CFG_UNICAST_MODE BIT(7)
- /* MAC Hash Register Bottom */
- #define LAN865X_REG_MAC_L_HASH 0x00010020
- /* MAC Hash Register Top */
- #define LAN865X_REG_MAC_H_HASH 0x00010021
- /* MAC Specific Addr 1 Bottom Reg */
- #define LAN865X_REG_MAC_L_SADDR1 0x00010022
- /* MAC Specific Addr 1 Top Reg */
- #define LAN865X_REG_MAC_H_SADDR1 0x00010023
- /* MAC TSU Timer Increment Register */
- #define LAN865X_REG_MAC_TSU_TIMER_INCR 0x00010077
- #define MAC_TSU_TIMER_INCR_COUNT_NANOSECONDS 0x0028
- struct lan865x_priv {
- struct work_struct multicast_work;
- struct net_device *netdev;
- struct spi_device *spi;
- struct oa_tc6 *tc6;
- };
- static int lan865x_set_hw_macaddr_low_bytes(struct oa_tc6 *tc6, const u8 *mac)
- {
- u32 regval;
- regval = (mac[3] << 24) | (mac[2] << 16) | (mac[1] << 8) | mac[0];
- return oa_tc6_write_register(tc6, LAN865X_REG_MAC_L_SADDR1, regval);
- }
- static int lan865x_set_hw_macaddr(struct lan865x_priv *priv, const u8 *mac)
- {
- int restore_ret;
- u32 regval;
- int ret;
- /* Configure MAC address low bytes */
- ret = lan865x_set_hw_macaddr_low_bytes(priv->tc6, mac);
- if (ret)
- return ret;
- /* Prepare and configure MAC address high bytes */
- regval = (mac[5] << 8) | mac[4];
- ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_H_SADDR1,
- regval);
- if (!ret)
- return 0;
- /* Restore the old MAC address low bytes from netdev if the new MAC
- * address high bytes setting failed.
- */
- restore_ret = lan865x_set_hw_macaddr_low_bytes(priv->tc6,
- priv->netdev->dev_addr);
- if (restore_ret)
- return restore_ret;
- return ret;
- }
- static const struct ethtool_ops lan865x_ethtool_ops = {
- .get_link_ksettings = phy_ethtool_get_link_ksettings,
- .set_link_ksettings = phy_ethtool_set_link_ksettings,
- };
- static int lan865x_set_mac_address(struct net_device *netdev, void *addr)
- {
- struct lan865x_priv *priv = netdev_priv(netdev);
- struct sockaddr *address = addr;
- int ret;
- ret = eth_prepare_mac_addr_change(netdev, addr);
- if (ret < 0)
- return ret;
- if (ether_addr_equal(address->sa_data, netdev->dev_addr))
- return 0;
- ret = lan865x_set_hw_macaddr(priv, address->sa_data);
- if (ret)
- return ret;
- eth_commit_mac_addr_change(netdev, addr);
- return 0;
- }
- static u32 get_address_bit(u8 addr[ETH_ALEN], u32 bit)
- {
- return ((addr[bit / 8]) >> (bit % 8)) & 1;
- }
- static u32 lan865x_hash(u8 addr[ETH_ALEN])
- {
- u32 hash_index = 0;
- for (int i = 0; i < 6; i++) {
- u32 hash = 0;
- for (int j = 0; j < 8; j++)
- hash ^= get_address_bit(addr, (j * 6) + i);
- hash_index |= (hash << i);
- }
- return hash_index;
- }
- static int lan865x_set_specific_multicast_addr(struct lan865x_priv *priv)
- {
- struct netdev_hw_addr *ha;
- u32 hash_lo = 0;
- u32 hash_hi = 0;
- int ret;
- netdev_for_each_mc_addr(ha, priv->netdev) {
- u32 bit_num = lan865x_hash(ha->addr);
- if (bit_num >= BIT(5))
- hash_hi |= (1 << (bit_num - BIT(5)));
- else
- hash_lo |= (1 << bit_num);
- }
- /* Enabling specific multicast addresses */
- ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_H_HASH, hash_hi);
- if (ret) {
- netdev_err(priv->netdev, "Failed to write reg_hashh: %d\n",
- ret);
- return ret;
- }
- ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_L_HASH, hash_lo);
- if (ret)
- netdev_err(priv->netdev, "Failed to write reg_hashl: %d\n",
- ret);
- return ret;
- }
- static int lan865x_set_all_multicast_addr(struct lan865x_priv *priv)
- {
- int ret;
- /* Enabling all multicast addresses */
- ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_H_HASH,
- 0xffffffff);
- if (ret) {
- netdev_err(priv->netdev, "Failed to write reg_hashh: %d\n",
- ret);
- return ret;
- }
- ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_L_HASH,
- 0xffffffff);
- if (ret)
- netdev_err(priv->netdev, "Failed to write reg_hashl: %d\n",
- ret);
- return ret;
- }
- static int lan865x_clear_all_multicast_addr(struct lan865x_priv *priv)
- {
- int ret;
- ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_H_HASH, 0);
- if (ret) {
- netdev_err(priv->netdev, "Failed to write reg_hashh: %d\n",
- ret);
- return ret;
- }
- ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_L_HASH, 0);
- if (ret)
- netdev_err(priv->netdev, "Failed to write reg_hashl: %d\n",
- ret);
- return ret;
- }
- static void lan865x_multicast_work_handler(struct work_struct *work)
- {
- struct lan865x_priv *priv = container_of(work, struct lan865x_priv,
- multicast_work);
- u32 regval = 0;
- int ret;
- if (priv->netdev->flags & IFF_PROMISC) {
- /* Enabling promiscuous mode */
- regval |= MAC_NET_CFG_PROMISCUOUS_MODE;
- regval &= (~MAC_NET_CFG_MULTICAST_MODE);
- regval &= (~MAC_NET_CFG_UNICAST_MODE);
- } else if (priv->netdev->flags & IFF_ALLMULTI) {
- /* Enabling all multicast mode */
- if (lan865x_set_all_multicast_addr(priv))
- return;
- regval &= (~MAC_NET_CFG_PROMISCUOUS_MODE);
- regval |= MAC_NET_CFG_MULTICAST_MODE;
- regval &= (~MAC_NET_CFG_UNICAST_MODE);
- } else if (!netdev_mc_empty(priv->netdev)) {
- /* Enabling specific multicast mode */
- if (lan865x_set_specific_multicast_addr(priv))
- return;
- regval &= (~MAC_NET_CFG_PROMISCUOUS_MODE);
- regval |= MAC_NET_CFG_MULTICAST_MODE;
- regval &= (~MAC_NET_CFG_UNICAST_MODE);
- } else {
- /* Enabling local mac address only */
- if (lan865x_clear_all_multicast_addr(priv))
- return;
- }
- ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_NET_CFG, regval);
- if (ret)
- netdev_err(priv->netdev, "Failed to enable promiscuous/multicast/normal mode: %d\n",
- ret);
- }
- static void lan865x_set_multicast_list(struct net_device *netdev)
- {
- struct lan865x_priv *priv = netdev_priv(netdev);
- schedule_work(&priv->multicast_work);
- }
- static netdev_tx_t lan865x_send_packet(struct sk_buff *skb,
- struct net_device *netdev)
- {
- struct lan865x_priv *priv = netdev_priv(netdev);
- return oa_tc6_start_xmit(priv->tc6, skb);
- }
- static int lan865x_hw_disable(struct lan865x_priv *priv)
- {
- u32 regval;
- if (oa_tc6_read_register(priv->tc6, LAN865X_REG_MAC_NET_CTL, ®val))
- return -ENODEV;
- regval &= ~(MAC_NET_CTL_TXEN | MAC_NET_CTL_RXEN);
- if (oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_NET_CTL, regval))
- return -ENODEV;
- return 0;
- }
- static int lan865x_net_close(struct net_device *netdev)
- {
- struct lan865x_priv *priv = netdev_priv(netdev);
- int ret;
- netif_stop_queue(netdev);
- phy_stop(netdev->phydev);
- ret = lan865x_hw_disable(priv);
- if (ret) {
- netdev_err(netdev, "Failed to disable the hardware: %d\n", ret);
- return ret;
- }
- return 0;
- }
- static int lan865x_hw_enable(struct lan865x_priv *priv)
- {
- u32 regval;
- if (oa_tc6_read_register(priv->tc6, LAN865X_REG_MAC_NET_CTL, ®val))
- return -ENODEV;
- regval |= MAC_NET_CTL_TXEN | MAC_NET_CTL_RXEN;
- if (oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_NET_CTL, regval))
- return -ENODEV;
- return 0;
- }
- static int lan865x_net_open(struct net_device *netdev)
- {
- struct lan865x_priv *priv = netdev_priv(netdev);
- int ret;
- ret = lan865x_hw_enable(priv);
- if (ret) {
- netdev_err(netdev, "Failed to enable hardware: %d\n", ret);
- return ret;
- }
- phy_start(netdev->phydev);
- netif_start_queue(netdev);
- return 0;
- }
- static const struct net_device_ops lan865x_netdev_ops = {
- .ndo_open = lan865x_net_open,
- .ndo_stop = lan865x_net_close,
- .ndo_start_xmit = lan865x_send_packet,
- .ndo_set_rx_mode = lan865x_set_multicast_list,
- .ndo_set_mac_address = lan865x_set_mac_address,
- };
- static int lan865x_probe(struct spi_device *spi)
- {
- struct net_device *netdev;
- struct lan865x_priv *priv;
- int ret;
- netdev = alloc_etherdev(sizeof(struct lan865x_priv));
- if (!netdev)
- return -ENOMEM;
- priv = netdev_priv(netdev);
- priv->netdev = netdev;
- priv->spi = spi;
- spi_set_drvdata(spi, priv);
- INIT_WORK(&priv->multicast_work, lan865x_multicast_work_handler);
- priv->tc6 = oa_tc6_init(spi, netdev);
- if (!priv->tc6) {
- ret = -ENODEV;
- goto free_netdev;
- }
- /* LAN865x Rev.B0/B1 configuration parameters from AN1760
- * As per the Configuration Application Note AN1760 published in the
- * link, https://www.microchip.com/en-us/application-notes/an1760
- * Revision F (DS60001760G - June 2024), configure the MAC to set time
- * stamping at the end of the Start of Frame Delimiter (SFD) and set the
- * Timer Increment reg to 40 ns to be used as a 25 MHz internal clock.
- */
- ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_TSU_TIMER_INCR,
- MAC_TSU_TIMER_INCR_COUNT_NANOSECONDS);
- if (ret) {
- dev_err(&spi->dev, "Failed to config TSU Timer Incr reg: %d\n",
- ret);
- goto oa_tc6_exit;
- }
- /* As per the point s3 in the below errata, SPI receive Ethernet frame
- * transfer may halt when starting the next frame in the same data block
- * (chunk) as the end of a previous frame. The RFA field should be
- * configured to 01b or 10b for proper operation. In these modes, only
- * one receive Ethernet frame will be placed in a single data block.
- * When the RFA field is written to 01b, received frames will be forced
- * to only start in the first word of the data block payload (SWO=0). As
- * recommended, enable zero align receive frame feature for proper
- * operation.
- *
- * https://ww1.microchip.com/downloads/aemDocuments/documents/AIS/ProductDocuments/Errata/LAN8650-1-Errata-80001075.pdf
- */
- ret = oa_tc6_zero_align_receive_frame_enable(priv->tc6);
- if (ret) {
- dev_err(&spi->dev, "Failed to set ZARFE: %d\n", ret);
- goto oa_tc6_exit;
- }
- /* Get the MAC address from the SPI device tree node */
- if (device_get_ethdev_address(&spi->dev, netdev))
- eth_hw_addr_random(netdev);
- ret = lan865x_set_hw_macaddr(priv, netdev->dev_addr);
- if (ret) {
- dev_err(&spi->dev, "Failed to configure MAC: %d\n", ret);
- goto oa_tc6_exit;
- }
- netdev->if_port = IF_PORT_10BASET;
- netdev->irq = spi->irq;
- netdev->netdev_ops = &lan865x_netdev_ops;
- netdev->ethtool_ops = &lan865x_ethtool_ops;
- ret = register_netdev(netdev);
- if (ret) {
- dev_err(&spi->dev, "Register netdev failed (ret = %d)", ret);
- goto oa_tc6_exit;
- }
- return 0;
- oa_tc6_exit:
- oa_tc6_exit(priv->tc6);
- free_netdev:
- free_netdev(priv->netdev);
- return ret;
- }
- static void lan865x_remove(struct spi_device *spi)
- {
- struct lan865x_priv *priv = spi_get_drvdata(spi);
- cancel_work_sync(&priv->multicast_work);
- unregister_netdev(priv->netdev);
- oa_tc6_exit(priv->tc6);
- free_netdev(priv->netdev);
- }
- static const struct spi_device_id lan865x_ids[] = {
- { .name = "lan8650" },
- { .name = "lan8651" },
- {},
- };
- MODULE_DEVICE_TABLE(spi, lan865x_ids);
- static const struct of_device_id lan865x_dt_ids[] = {
- { .compatible = "microchip,lan8650" },
- { .compatible = "microchip,lan8651" },
- { /* Sentinel */ }
- };
- MODULE_DEVICE_TABLE(of, lan865x_dt_ids);
- static struct spi_driver lan865x_driver = {
- .driver = {
- .name = DRV_NAME,
- .of_match_table = lan865x_dt_ids,
- },
- .probe = lan865x_probe,
- .remove = lan865x_remove,
- .id_table = lan865x_ids,
- };
- module_spi_driver(lan865x_driver);
- MODULE_DESCRIPTION(DRV_NAME " 10Base-T1S MACPHY Ethernet Driver");
- MODULE_AUTHOR("Parthiban Veerasooran <parthiban.veerasooran@microchip.com>");
- MODULE_LICENSE("GPL");
|