162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright Sunplus Technology Co., Ltd.
362306a36Sopenharmony_ci *       All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/platform_device.h>
762306a36Sopenharmony_ci#include <linux/nvmem-consumer.h>
862306a36Sopenharmony_ci#include <linux/etherdevice.h>
962306a36Sopenharmony_ci#include <linux/netdevice.h>
1062306a36Sopenharmony_ci#include <linux/spinlock.h>
1162306a36Sopenharmony_ci#include <linux/of_net.h>
1262306a36Sopenharmony_ci#include <linux/reset.h>
1362306a36Sopenharmony_ci#include <linux/clk.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "spl2sw_register.h"
1762306a36Sopenharmony_ci#include "spl2sw_define.h"
1862306a36Sopenharmony_ci#include "spl2sw_desc.h"
1962306a36Sopenharmony_ci#include "spl2sw_mdio.h"
2062306a36Sopenharmony_ci#include "spl2sw_phy.h"
2162306a36Sopenharmony_ci#include "spl2sw_int.h"
2262306a36Sopenharmony_ci#include "spl2sw_mac.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* net device operations */
2562306a36Sopenharmony_cistatic int spl2sw_ethernet_open(struct net_device *ndev)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	struct spl2sw_mac *mac = netdev_priv(ndev);
2862306a36Sopenharmony_ci	struct spl2sw_common *comm = mac->comm;
2962306a36Sopenharmony_ci	u32 mask;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	netdev_dbg(ndev, "Open port = %x\n", mac->lan_port);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	comm->enable |= mac->lan_port;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	spl2sw_mac_hw_start(comm);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	/* Enable TX and RX interrupts */
3862306a36Sopenharmony_ci	mask = readl(comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
3962306a36Sopenharmony_ci	mask &= ~(MAC_INT_TX | MAC_INT_RX);
4062306a36Sopenharmony_ci	writel(mask, comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	phy_start(ndev->phydev);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	netif_start_queue(ndev);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	return 0;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic int spl2sw_ethernet_stop(struct net_device *ndev)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct spl2sw_mac *mac = netdev_priv(ndev);
5262306a36Sopenharmony_ci	struct spl2sw_common *comm = mac->comm;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	netif_stop_queue(ndev);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	comm->enable &= ~mac->lan_port;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	phy_stop(ndev->phydev);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	spl2sw_mac_hw_stop(comm);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return 0;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic netdev_tx_t spl2sw_ethernet_start_xmit(struct sk_buff *skb,
6662306a36Sopenharmony_ci					      struct net_device *ndev)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct spl2sw_mac *mac = netdev_priv(ndev);
6962306a36Sopenharmony_ci	struct spl2sw_common *comm = mac->comm;
7062306a36Sopenharmony_ci	struct spl2sw_skb_info *skbinfo;
7162306a36Sopenharmony_ci	struct spl2sw_mac_desc *txdesc;
7262306a36Sopenharmony_ci	unsigned long flags;
7362306a36Sopenharmony_ci	u32 mapping;
7462306a36Sopenharmony_ci	u32 tx_pos;
7562306a36Sopenharmony_ci	u32 cmd1;
7662306a36Sopenharmony_ci	u32 cmd2;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (unlikely(comm->tx_desc_full == 1)) {
7962306a36Sopenharmony_ci		/* No TX descriptors left. Wait for tx interrupt. */
8062306a36Sopenharmony_ci		netdev_dbg(ndev, "TX descriptor queue full when xmit!\n");
8162306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* If skb size is shorter than ETH_ZLEN (60), pad it with 0. */
8562306a36Sopenharmony_ci	if (unlikely(skb->len < ETH_ZLEN)) {
8662306a36Sopenharmony_ci		if (skb_padto(skb, ETH_ZLEN))
8762306a36Sopenharmony_ci			return NETDEV_TX_OK;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci		skb_put(skb, ETH_ZLEN - skb->len);
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	mapping = dma_map_single(&comm->pdev->dev, skb->data,
9362306a36Sopenharmony_ci				 skb->len, DMA_TO_DEVICE);
9462306a36Sopenharmony_ci	if (dma_mapping_error(&comm->pdev->dev, mapping)) {
9562306a36Sopenharmony_ci		ndev->stats.tx_errors++;
9662306a36Sopenharmony_ci		dev_kfree_skb(skb);
9762306a36Sopenharmony_ci		return NETDEV_TX_OK;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	spin_lock_irqsave(&comm->tx_lock, flags);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	tx_pos = comm->tx_pos;
10362306a36Sopenharmony_ci	txdesc = &comm->tx_desc[tx_pos];
10462306a36Sopenharmony_ci	skbinfo = &comm->tx_temp_skb_info[tx_pos];
10562306a36Sopenharmony_ci	skbinfo->mapping = mapping;
10662306a36Sopenharmony_ci	skbinfo->len = skb->len;
10762306a36Sopenharmony_ci	skbinfo->skb = skb;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	/* Set up a TX descriptor */
11062306a36Sopenharmony_ci	cmd1 = TXD_OWN | TXD_SOP | TXD_EOP | (mac->to_vlan << 12) |
11162306a36Sopenharmony_ci	       (skb->len & TXD_PKT_LEN);
11262306a36Sopenharmony_ci	cmd2 = skb->len & TXD_BUF_LEN1;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (tx_pos == (TX_DESC_NUM - 1))
11562306a36Sopenharmony_ci		cmd2 |= TXD_EOR;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	txdesc->addr1 = skbinfo->mapping;
11862306a36Sopenharmony_ci	txdesc->cmd2 = cmd2;
11962306a36Sopenharmony_ci	wmb();	/* Set TXD_OWN after other fields are effective. */
12062306a36Sopenharmony_ci	txdesc->cmd1 = cmd1;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* Move tx_pos to next position */
12362306a36Sopenharmony_ci	tx_pos = ((tx_pos + 1) == TX_DESC_NUM) ? 0 : tx_pos + 1;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (unlikely(tx_pos == comm->tx_done_pos)) {
12662306a36Sopenharmony_ci		netif_stop_queue(ndev);
12762306a36Sopenharmony_ci		comm->tx_desc_full = 1;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci	comm->tx_pos = tx_pos;
13062306a36Sopenharmony_ci	wmb();		/* make sure settings are effective. */
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* Trigger mac to transmit */
13362306a36Sopenharmony_ci	writel(MAC_TRIG_L_SOC0, comm->l2sw_reg_base + L2SW_CPU_TX_TRIG);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	spin_unlock_irqrestore(&comm->tx_lock, flags);
13662306a36Sopenharmony_ci	return NETDEV_TX_OK;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void spl2sw_ethernet_set_rx_mode(struct net_device *ndev)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct spl2sw_mac *mac = netdev_priv(ndev);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	spl2sw_mac_rx_mode_set(mac);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int spl2sw_ethernet_set_mac_address(struct net_device *ndev, void *addr)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct spl2sw_mac *mac = netdev_priv(ndev);
14962306a36Sopenharmony_ci	int err;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	err = eth_mac_addr(ndev, addr);
15262306a36Sopenharmony_ci	if (err)
15362306a36Sopenharmony_ci		return err;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* Delete the old MAC address */
15662306a36Sopenharmony_ci	netdev_dbg(ndev, "Old Ethernet (MAC) address = %pM\n", mac->mac_addr);
15762306a36Sopenharmony_ci	if (is_valid_ether_addr(mac->mac_addr)) {
15862306a36Sopenharmony_ci		err = spl2sw_mac_addr_del(mac);
15962306a36Sopenharmony_ci		if (err)
16062306a36Sopenharmony_ci			return err;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* Set the MAC address */
16462306a36Sopenharmony_ci	ether_addr_copy(mac->mac_addr, ndev->dev_addr);
16562306a36Sopenharmony_ci	return spl2sw_mac_addr_add(mac);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic void spl2sw_ethernet_tx_timeout(struct net_device *ndev, unsigned int txqueue)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct spl2sw_mac *mac = netdev_priv(ndev);
17162306a36Sopenharmony_ci	struct spl2sw_common *comm = mac->comm;
17262306a36Sopenharmony_ci	unsigned long flags;
17362306a36Sopenharmony_ci	int i;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	netdev_err(ndev, "TX timed out!\n");
17662306a36Sopenharmony_ci	ndev->stats.tx_errors++;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	spin_lock_irqsave(&comm->tx_lock, flags);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	for (i = 0; i < MAX_NETDEV_NUM; i++)
18162306a36Sopenharmony_ci		if (comm->ndev[i])
18262306a36Sopenharmony_ci			netif_stop_queue(comm->ndev[i]);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	spl2sw_mac_soft_reset(comm);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* Accept TX packets again. */
18762306a36Sopenharmony_ci	for (i = 0; i < MAX_NETDEV_NUM; i++)
18862306a36Sopenharmony_ci		if (comm->ndev[i]) {
18962306a36Sopenharmony_ci			netif_trans_update(comm->ndev[i]);
19062306a36Sopenharmony_ci			netif_wake_queue(comm->ndev[i]);
19162306a36Sopenharmony_ci		}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	spin_unlock_irqrestore(&comm->tx_lock, flags);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic const struct net_device_ops netdev_ops = {
19762306a36Sopenharmony_ci	.ndo_open = spl2sw_ethernet_open,
19862306a36Sopenharmony_ci	.ndo_stop = spl2sw_ethernet_stop,
19962306a36Sopenharmony_ci	.ndo_start_xmit = spl2sw_ethernet_start_xmit,
20062306a36Sopenharmony_ci	.ndo_set_rx_mode = spl2sw_ethernet_set_rx_mode,
20162306a36Sopenharmony_ci	.ndo_set_mac_address = spl2sw_ethernet_set_mac_address,
20262306a36Sopenharmony_ci	.ndo_do_ioctl = phy_do_ioctl,
20362306a36Sopenharmony_ci	.ndo_tx_timeout = spl2sw_ethernet_tx_timeout,
20462306a36Sopenharmony_ci};
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic void spl2sw_check_mac_vendor_id_and_convert(u8 *mac_addr)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	/* Byte order of MAC address of some samples are reversed.
20962306a36Sopenharmony_ci	 * Check vendor id and convert byte order if it is wrong.
21062306a36Sopenharmony_ci	 * OUI of Sunplus: fc:4b:bc
21162306a36Sopenharmony_ci	 */
21262306a36Sopenharmony_ci	if (mac_addr[5] == 0xfc && mac_addr[4] == 0x4b && mac_addr[3] == 0xbc &&
21362306a36Sopenharmony_ci	    (mac_addr[0] != 0xfc || mac_addr[1] != 0x4b || mac_addr[2] != 0xbc)) {
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		swap(mac_addr[0], mac_addr[5]);
21662306a36Sopenharmony_ci		swap(mac_addr[1], mac_addr[4]);
21762306a36Sopenharmony_ci		swap(mac_addr[2], mac_addr[3]);
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic int spl2sw_nvmem_get_mac_address(struct device *dev, struct device_node *np,
22262306a36Sopenharmony_ci					void *addrbuf)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct nvmem_cell *cell;
22562306a36Sopenharmony_ci	ssize_t len;
22662306a36Sopenharmony_ci	u8 *mac;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* Get nvmem cell of mac-address from dts. */
22962306a36Sopenharmony_ci	cell = of_nvmem_cell_get(np, "mac-address");
23062306a36Sopenharmony_ci	if (IS_ERR(cell))
23162306a36Sopenharmony_ci		return PTR_ERR(cell);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/* Read mac address from nvmem cell. */
23462306a36Sopenharmony_ci	mac = nvmem_cell_read(cell, &len);
23562306a36Sopenharmony_ci	nvmem_cell_put(cell);
23662306a36Sopenharmony_ci	if (IS_ERR(mac))
23762306a36Sopenharmony_ci		return PTR_ERR(mac);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (len != ETH_ALEN) {
24062306a36Sopenharmony_ci		kfree(mac);
24162306a36Sopenharmony_ci		dev_info(dev, "Invalid length of mac address in nvmem!\n");
24262306a36Sopenharmony_ci		return -EINVAL;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* Byte order of some samples are reversed.
24662306a36Sopenharmony_ci	 * Convert byte order here.
24762306a36Sopenharmony_ci	 */
24862306a36Sopenharmony_ci	spl2sw_check_mac_vendor_id_and_convert(mac);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/* Check if mac address is valid */
25162306a36Sopenharmony_ci	if (!is_valid_ether_addr(mac)) {
25262306a36Sopenharmony_ci		dev_info(dev, "Invalid mac address in nvmem (%pM)!\n", mac);
25362306a36Sopenharmony_ci		kfree(mac);
25462306a36Sopenharmony_ci		return -EINVAL;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	ether_addr_copy(addrbuf, mac);
25862306a36Sopenharmony_ci	kfree(mac);
25962306a36Sopenharmony_ci	return 0;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic u32 spl2sw_init_netdev(struct platform_device *pdev, u8 *mac_addr,
26362306a36Sopenharmony_ci			      struct net_device **r_ndev)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct net_device *ndev;
26662306a36Sopenharmony_ci	struct spl2sw_mac *mac;
26762306a36Sopenharmony_ci	int ret;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	/* Allocate the devices, and also allocate spl2sw_mac,
27062306a36Sopenharmony_ci	 * we can get it by netdev_priv().
27162306a36Sopenharmony_ci	 */
27262306a36Sopenharmony_ci	ndev = devm_alloc_etherdev(&pdev->dev, sizeof(*mac));
27362306a36Sopenharmony_ci	if (!ndev) {
27462306a36Sopenharmony_ci		*r_ndev = NULL;
27562306a36Sopenharmony_ci		return -ENOMEM;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci	SET_NETDEV_DEV(ndev, &pdev->dev);
27862306a36Sopenharmony_ci	ndev->netdev_ops = &netdev_ops;
27962306a36Sopenharmony_ci	mac = netdev_priv(ndev);
28062306a36Sopenharmony_ci	mac->ndev = ndev;
28162306a36Sopenharmony_ci	ether_addr_copy(mac->mac_addr, mac_addr);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	eth_hw_addr_set(ndev, mac_addr);
28462306a36Sopenharmony_ci	dev_info(&pdev->dev, "Ethernet (MAC) address = %pM\n", mac_addr);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	ret = register_netdev(ndev);
28762306a36Sopenharmony_ci	if (ret) {
28862306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register net device \"%s\"!\n",
28962306a36Sopenharmony_ci			ndev->name);
29062306a36Sopenharmony_ci		*r_ndev = NULL;
29162306a36Sopenharmony_ci		return ret;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci	netdev_dbg(ndev, "Registered net device \"%s\" successfully.\n", ndev->name);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	*r_ndev = ndev;
29662306a36Sopenharmony_ci	return 0;
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic struct device_node *spl2sw_get_eth_child_node(struct device_node *ether_np, int id)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct device_node *port_np;
30262306a36Sopenharmony_ci	int port_id;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	for_each_child_of_node(ether_np, port_np) {
30562306a36Sopenharmony_ci		/* It is not a 'port' node, continue. */
30662306a36Sopenharmony_ci		if (strcmp(port_np->name, "port"))
30762306a36Sopenharmony_ci			continue;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		if (of_property_read_u32(port_np, "reg", &port_id) < 0)
31062306a36Sopenharmony_ci			continue;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		if (port_id == id)
31362306a36Sopenharmony_ci			return port_np;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* Not found! */
31762306a36Sopenharmony_ci	return NULL;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic int spl2sw_probe(struct platform_device *pdev)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct device_node *eth_ports_np;
32362306a36Sopenharmony_ci	struct device_node *port_np;
32462306a36Sopenharmony_ci	struct spl2sw_common *comm;
32562306a36Sopenharmony_ci	struct device_node *phy_np;
32662306a36Sopenharmony_ci	phy_interface_t phy_mode;
32762306a36Sopenharmony_ci	struct net_device *ndev;
32862306a36Sopenharmony_ci	struct spl2sw_mac *mac;
32962306a36Sopenharmony_ci	u8 mac_addr[ETH_ALEN];
33062306a36Sopenharmony_ci	int irq, i, ret;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (platform_get_drvdata(pdev))
33362306a36Sopenharmony_ci		return -ENODEV;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	/* Allocate memory for 'spl2sw_common' area. */
33662306a36Sopenharmony_ci	comm = devm_kzalloc(&pdev->dev, sizeof(*comm), GFP_KERNEL);
33762306a36Sopenharmony_ci	if (!comm)
33862306a36Sopenharmony_ci		return -ENOMEM;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	comm->pdev = pdev;
34162306a36Sopenharmony_ci	platform_set_drvdata(pdev, comm);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	spin_lock_init(&comm->tx_lock);
34462306a36Sopenharmony_ci	spin_lock_init(&comm->mdio_lock);
34562306a36Sopenharmony_ci	spin_lock_init(&comm->int_mask_lock);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	/* Get memory resource 0 from dts. */
34862306a36Sopenharmony_ci	comm->l2sw_reg_base = devm_platform_ioremap_resource(pdev, 0);
34962306a36Sopenharmony_ci	if (IS_ERR(comm->l2sw_reg_base))
35062306a36Sopenharmony_ci		return PTR_ERR(comm->l2sw_reg_base);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/* Get irq resource from dts. */
35362306a36Sopenharmony_ci	ret = platform_get_irq(pdev, 0);
35462306a36Sopenharmony_ci	if (ret < 0)
35562306a36Sopenharmony_ci		return ret;
35662306a36Sopenharmony_ci	irq = ret;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	/* Get clock controller. */
35962306a36Sopenharmony_ci	comm->clk = devm_clk_get(&pdev->dev, NULL);
36062306a36Sopenharmony_ci	if (IS_ERR(comm->clk)) {
36162306a36Sopenharmony_ci		dev_err_probe(&pdev->dev, PTR_ERR(comm->clk),
36262306a36Sopenharmony_ci			      "Failed to retrieve clock controller!\n");
36362306a36Sopenharmony_ci		return PTR_ERR(comm->clk);
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	/* Get reset controller. */
36762306a36Sopenharmony_ci	comm->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
36862306a36Sopenharmony_ci	if (IS_ERR(comm->rstc)) {
36962306a36Sopenharmony_ci		dev_err_probe(&pdev->dev, PTR_ERR(comm->rstc),
37062306a36Sopenharmony_ci			      "Failed to retrieve reset controller!\n");
37162306a36Sopenharmony_ci		return PTR_ERR(comm->rstc);
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	/* Enable clock. */
37562306a36Sopenharmony_ci	ret = clk_prepare_enable(comm->clk);
37662306a36Sopenharmony_ci	if (ret)
37762306a36Sopenharmony_ci		return ret;
37862306a36Sopenharmony_ci	udelay(1);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/* Reset MAC */
38162306a36Sopenharmony_ci	reset_control_assert(comm->rstc);
38262306a36Sopenharmony_ci	udelay(1);
38362306a36Sopenharmony_ci	reset_control_deassert(comm->rstc);
38462306a36Sopenharmony_ci	usleep_range(1000, 2000);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	/* Request irq. */
38762306a36Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, irq, spl2sw_ethernet_interrupt, 0,
38862306a36Sopenharmony_ci			       dev_name(&pdev->dev), comm);
38962306a36Sopenharmony_ci	if (ret) {
39062306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to request irq #%d!\n", irq);
39162306a36Sopenharmony_ci		goto out_clk_disable;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	/* Initialize TX and RX descriptors. */
39562306a36Sopenharmony_ci	ret = spl2sw_descs_init(comm);
39662306a36Sopenharmony_ci	if (ret) {
39762306a36Sopenharmony_ci		dev_err(&pdev->dev, "Fail to initialize mac descriptors!\n");
39862306a36Sopenharmony_ci		spl2sw_descs_free(comm);
39962306a36Sopenharmony_ci		goto out_clk_disable;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/* Initialize MAC. */
40362306a36Sopenharmony_ci	spl2sw_mac_init(comm);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/* Initialize mdio bus */
40662306a36Sopenharmony_ci	ret = spl2sw_mdio_init(comm);
40762306a36Sopenharmony_ci	if (ret) {
40862306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to initialize mdio bus!\n");
40962306a36Sopenharmony_ci		goto out_clk_disable;
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/* Get child node ethernet-ports. */
41362306a36Sopenharmony_ci	eth_ports_np = of_get_child_by_name(pdev->dev.of_node, "ethernet-ports");
41462306a36Sopenharmony_ci	if (!eth_ports_np) {
41562306a36Sopenharmony_ci		dev_err(&pdev->dev, "No ethernet-ports child node found!\n");
41662306a36Sopenharmony_ci		ret = -ENODEV;
41762306a36Sopenharmony_ci		goto out_free_mdio;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	for (i = 0; i < MAX_NETDEV_NUM; i++) {
42162306a36Sopenharmony_ci		/* Get port@i of node ethernet-ports. */
42262306a36Sopenharmony_ci		port_np = spl2sw_get_eth_child_node(eth_ports_np, i);
42362306a36Sopenharmony_ci		if (!port_np)
42462306a36Sopenharmony_ci			continue;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		/* Get phy-mode. */
42762306a36Sopenharmony_ci		if (of_get_phy_mode(port_np, &phy_mode)) {
42862306a36Sopenharmony_ci			dev_err(&pdev->dev, "Failed to get phy-mode property of port@%d!\n",
42962306a36Sopenharmony_ci				i);
43062306a36Sopenharmony_ci			continue;
43162306a36Sopenharmony_ci		}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci		/* Get phy-handle. */
43462306a36Sopenharmony_ci		phy_np = of_parse_phandle(port_np, "phy-handle", 0);
43562306a36Sopenharmony_ci		if (!phy_np) {
43662306a36Sopenharmony_ci			dev_err(&pdev->dev, "Failed to get phy-handle property of port@%d!\n",
43762306a36Sopenharmony_ci				i);
43862306a36Sopenharmony_ci			continue;
43962306a36Sopenharmony_ci		}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci		/* Get mac-address from nvmem. */
44262306a36Sopenharmony_ci		ret = spl2sw_nvmem_get_mac_address(&pdev->dev, port_np, mac_addr);
44362306a36Sopenharmony_ci		if (ret == -EPROBE_DEFER) {
44462306a36Sopenharmony_ci			goto out_unregister_dev;
44562306a36Sopenharmony_ci		} else if (ret) {
44662306a36Sopenharmony_ci			dev_info(&pdev->dev, "Generate a random mac address!\n");
44762306a36Sopenharmony_ci			eth_random_addr(mac_addr);
44862306a36Sopenharmony_ci		}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		/* Initialize the net device. */
45162306a36Sopenharmony_ci		ret = spl2sw_init_netdev(pdev, mac_addr, &ndev);
45262306a36Sopenharmony_ci		if (ret)
45362306a36Sopenharmony_ci			goto out_unregister_dev;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci		ndev->irq = irq;
45662306a36Sopenharmony_ci		comm->ndev[i] = ndev;
45762306a36Sopenharmony_ci		mac = netdev_priv(ndev);
45862306a36Sopenharmony_ci		mac->phy_node = phy_np;
45962306a36Sopenharmony_ci		mac->phy_mode = phy_mode;
46062306a36Sopenharmony_ci		mac->comm = comm;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci		mac->lan_port = 0x1 << i;	/* forward to port i */
46362306a36Sopenharmony_ci		mac->to_vlan = 0x1 << i;	/* vlan group: i     */
46462306a36Sopenharmony_ci		mac->vlan_id = i;		/* vlan group: i     */
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		/* Set MAC address */
46762306a36Sopenharmony_ci		ret = spl2sw_mac_addr_add(mac);
46862306a36Sopenharmony_ci		if (ret)
46962306a36Sopenharmony_ci			goto out_unregister_dev;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		spl2sw_mac_rx_mode_set(mac);
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/* Find first valid net device. */
47562306a36Sopenharmony_ci	for (i = 0; i < MAX_NETDEV_NUM; i++) {
47662306a36Sopenharmony_ci		if (comm->ndev[i])
47762306a36Sopenharmony_ci			break;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci	if (i >= MAX_NETDEV_NUM) {
48062306a36Sopenharmony_ci		dev_err(&pdev->dev, "No valid ethernet port!\n");
48162306a36Sopenharmony_ci		ret = -ENODEV;
48262306a36Sopenharmony_ci		goto out_free_mdio;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	/* Save first valid net device */
48662306a36Sopenharmony_ci	ndev = comm->ndev[i];
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	ret = spl2sw_phy_connect(comm);
48962306a36Sopenharmony_ci	if (ret) {
49062306a36Sopenharmony_ci		netdev_err(ndev, "Failed to connect phy!\n");
49162306a36Sopenharmony_ci		goto out_unregister_dev;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	/* Add and enable napi. */
49562306a36Sopenharmony_ci	netif_napi_add(ndev, &comm->rx_napi, spl2sw_rx_poll);
49662306a36Sopenharmony_ci	napi_enable(&comm->rx_napi);
49762306a36Sopenharmony_ci	netif_napi_add_tx(ndev, &comm->tx_napi, spl2sw_tx_poll);
49862306a36Sopenharmony_ci	napi_enable(&comm->tx_napi);
49962306a36Sopenharmony_ci	return 0;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ciout_unregister_dev:
50262306a36Sopenharmony_ci	for (i = 0; i < MAX_NETDEV_NUM; i++)
50362306a36Sopenharmony_ci		if (comm->ndev[i])
50462306a36Sopenharmony_ci			unregister_netdev(comm->ndev[i]);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ciout_free_mdio:
50762306a36Sopenharmony_ci	spl2sw_mdio_remove(comm);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ciout_clk_disable:
51062306a36Sopenharmony_ci	clk_disable_unprepare(comm->clk);
51162306a36Sopenharmony_ci	return ret;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic int spl2sw_remove(struct platform_device *pdev)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	struct spl2sw_common *comm;
51762306a36Sopenharmony_ci	int i;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	comm = platform_get_drvdata(pdev);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	spl2sw_phy_remove(comm);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* Unregister and free net device. */
52462306a36Sopenharmony_ci	for (i = 0; i < MAX_NETDEV_NUM; i++)
52562306a36Sopenharmony_ci		if (comm->ndev[i])
52662306a36Sopenharmony_ci			unregister_netdev(comm->ndev[i]);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	comm->enable = 0;
52962306a36Sopenharmony_ci	spl2sw_mac_hw_stop(comm);
53062306a36Sopenharmony_ci	spl2sw_descs_free(comm);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* Disable and delete napi. */
53362306a36Sopenharmony_ci	napi_disable(&comm->rx_napi);
53462306a36Sopenharmony_ci	netif_napi_del(&comm->rx_napi);
53562306a36Sopenharmony_ci	napi_disable(&comm->tx_napi);
53662306a36Sopenharmony_ci	netif_napi_del(&comm->tx_napi);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	spl2sw_mdio_remove(comm);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	clk_disable_unprepare(comm->clk);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	return 0;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic const struct of_device_id spl2sw_of_match[] = {
54662306a36Sopenharmony_ci	{.compatible = "sunplus,sp7021-emac"},
54762306a36Sopenharmony_ci	{ /* sentinel */ }
54862306a36Sopenharmony_ci};
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, spl2sw_of_match);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic struct platform_driver spl2sw_driver = {
55362306a36Sopenharmony_ci	.probe = spl2sw_probe,
55462306a36Sopenharmony_ci	.remove = spl2sw_remove,
55562306a36Sopenharmony_ci	.driver = {
55662306a36Sopenharmony_ci		.name = "sp7021_emac",
55762306a36Sopenharmony_ci		.of_match_table = spl2sw_of_match,
55862306a36Sopenharmony_ci	},
55962306a36Sopenharmony_ci};
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cimodule_platform_driver(spl2sw_driver);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ciMODULE_AUTHOR("Wells Lu <wellslutw@gmail.com>");
56462306a36Sopenharmony_ciMODULE_DESCRIPTION("Sunplus Dual 10M/100M Ethernet driver");
56562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
566