diff options
-rw-r--r-- | drivers/net/ethernet/renesas/sh_eth.c | 282 | ||||
-rw-r--r-- | drivers/net/ethernet/renesas/sh_eth.h | 9 |
2 files changed, 286 insertions, 5 deletions
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 8bd0e5897424..99d8ce8379c8 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c | |||
@@ -1604,18 +1604,289 @@ static int sh_eth_do_ioctl(struct net_device *ndev, struct ifreq *rq, | |||
1604 | } | 1604 | } |
1605 | 1605 | ||
1606 | #if defined(SH_ETH_HAS_TSU) | 1606 | #if defined(SH_ETH_HAS_TSU) |
1607 | /* For TSU_POSTn. Please refer to the manual about this (strange) bitfields */ | ||
1608 | static void *sh_eth_tsu_get_post_reg_offset(struct sh_eth_private *mdp, | ||
1609 | int entry) | ||
1610 | { | ||
1611 | return sh_eth_tsu_get_offset(mdp, TSU_POST1) + (entry / 8 * 4); | ||
1612 | } | ||
1613 | |||
1614 | static u32 sh_eth_tsu_get_post_mask(int entry) | ||
1615 | { | ||
1616 | return 0x0f << (28 - ((entry % 8) * 4)); | ||
1617 | } | ||
1618 | |||
1619 | static u32 sh_eth_tsu_get_post_bit(struct sh_eth_private *mdp, int entry) | ||
1620 | { | ||
1621 | return (0x08 >> (mdp->port << 1)) << (28 - ((entry % 8) * 4)); | ||
1622 | } | ||
1623 | |||
1624 | static void sh_eth_tsu_enable_cam_entry_post(struct net_device *ndev, | ||
1625 | int entry) | ||
1626 | { | ||
1627 | struct sh_eth_private *mdp = netdev_priv(ndev); | ||
1628 | u32 tmp; | ||
1629 | void *reg_offset; | ||
1630 | |||
1631 | reg_offset = sh_eth_tsu_get_post_reg_offset(mdp, entry); | ||
1632 | tmp = ioread32(reg_offset); | ||
1633 | iowrite32(tmp | sh_eth_tsu_get_post_bit(mdp, entry), reg_offset); | ||
1634 | } | ||
1635 | |||
1636 | static bool sh_eth_tsu_disable_cam_entry_post(struct net_device *ndev, | ||
1637 | int entry) | ||
1638 | { | ||
1639 | struct sh_eth_private *mdp = netdev_priv(ndev); | ||
1640 | u32 post_mask, ref_mask, tmp; | ||
1641 | void *reg_offset; | ||
1642 | |||
1643 | reg_offset = sh_eth_tsu_get_post_reg_offset(mdp, entry); | ||
1644 | post_mask = sh_eth_tsu_get_post_mask(entry); | ||
1645 | ref_mask = sh_eth_tsu_get_post_bit(mdp, entry) & ~post_mask; | ||
1646 | |||
1647 | tmp = ioread32(reg_offset); | ||
1648 | iowrite32(tmp & ~post_mask, reg_offset); | ||
1649 | |||
1650 | /* If other port enables, the function returns "true" */ | ||
1651 | return tmp & ref_mask; | ||
1652 | } | ||
1653 | |||
1654 | static int sh_eth_tsu_busy(struct net_device *ndev) | ||
1655 | { | ||
1656 | int timeout = SH_ETH_TSU_TIMEOUT_MS * 100; | ||
1657 | struct sh_eth_private *mdp = netdev_priv(ndev); | ||
1658 | |||
1659 | while ((sh_eth_tsu_read(mdp, TSU_ADSBSY) & TSU_ADSBSY_0)) { | ||
1660 | udelay(10); | ||
1661 | timeout--; | ||
1662 | if (timeout <= 0) { | ||
1663 | dev_err(&ndev->dev, "%s: timeout\n", __func__); | ||
1664 | return -ETIMEDOUT; | ||
1665 | } | ||
1666 | } | ||
1667 | |||
1668 | return 0; | ||
1669 | } | ||
1670 | |||
1671 | static int sh_eth_tsu_write_entry(struct net_device *ndev, void *reg, | ||
1672 | const u8 *addr) | ||
1673 | { | ||
1674 | u32 val; | ||
1675 | |||
1676 | val = addr[0] << 24 | addr[1] << 16 | addr[2] << 8 | addr[3]; | ||
1677 | iowrite32(val, reg); | ||
1678 | if (sh_eth_tsu_busy(ndev) < 0) | ||
1679 | return -EBUSY; | ||
1680 | |||
1681 | val = addr[4] << 8 | addr[5]; | ||
1682 | iowrite32(val, reg + 4); | ||
1683 | if (sh_eth_tsu_busy(ndev) < 0) | ||
1684 | return -EBUSY; | ||
1685 | |||
1686 | return 0; | ||
1687 | } | ||
1688 | |||
1689 | static void sh_eth_tsu_read_entry(void *reg, u8 *addr) | ||
1690 | { | ||
1691 | u32 val; | ||
1692 | |||
1693 | val = ioread32(reg); | ||
1694 | addr[0] = (val >> 24) & 0xff; | ||
1695 | addr[1] = (val >> 16) & 0xff; | ||
1696 | addr[2] = (val >> 8) & 0xff; | ||
1697 | addr[3] = val & 0xff; | ||
1698 | val = ioread32(reg + 4); | ||
1699 | addr[4] = (val >> 8) & 0xff; | ||
1700 | addr[5] = val & 0xff; | ||
1701 | } | ||
1702 | |||
1703 | |||
1704 | static int sh_eth_tsu_find_entry(struct net_device *ndev, const u8 *addr) | ||
1705 | { | ||
1706 | struct sh_eth_private *mdp = netdev_priv(ndev); | ||
1707 | void *reg_offset = sh_eth_tsu_get_offset(mdp, TSU_ADRH0); | ||
1708 | int i; | ||
1709 | u8 c_addr[ETH_ALEN]; | ||
1710 | |||
1711 | for (i = 0; i < SH_ETH_TSU_CAM_ENTRIES; i++, reg_offset += 8) { | ||
1712 | sh_eth_tsu_read_entry(reg_offset, c_addr); | ||
1713 | if (memcmp(addr, c_addr, ETH_ALEN) == 0) | ||
1714 | return i; | ||
1715 | } | ||
1716 | |||
1717 | return -ENOENT; | ||
1718 | } | ||
1719 | |||
1720 | static int sh_eth_tsu_find_empty(struct net_device *ndev) | ||
1721 | { | ||
1722 | u8 blank[ETH_ALEN]; | ||
1723 | int entry; | ||
1724 | |||
1725 | memset(blank, 0, sizeof(blank)); | ||
1726 | entry = sh_eth_tsu_find_entry(ndev, blank); | ||
1727 | return (entry < 0) ? -ENOMEM : entry; | ||
1728 | } | ||
1729 | |||
1730 | static int sh_eth_tsu_disable_cam_entry_table(struct net_device *ndev, | ||
1731 | int entry) | ||
1732 | { | ||
1733 | struct sh_eth_private *mdp = netdev_priv(ndev); | ||
1734 | void *reg_offset = sh_eth_tsu_get_offset(mdp, TSU_ADRH0); | ||
1735 | int ret; | ||
1736 | u8 blank[ETH_ALEN]; | ||
1737 | |||
1738 | sh_eth_tsu_write(mdp, sh_eth_tsu_read(mdp, TSU_TEN) & | ||
1739 | ~(1 << (31 - entry)), TSU_TEN); | ||
1740 | |||
1741 | memset(blank, 0, sizeof(blank)); | ||
1742 | ret = sh_eth_tsu_write_entry(ndev, reg_offset + entry * 8, blank); | ||
1743 | if (ret < 0) | ||
1744 | return ret; | ||
1745 | return 0; | ||
1746 | } | ||
1747 | |||
1748 | static int sh_eth_tsu_add_entry(struct net_device *ndev, const u8 *addr) | ||
1749 | { | ||
1750 | struct sh_eth_private *mdp = netdev_priv(ndev); | ||
1751 | void *reg_offset = sh_eth_tsu_get_offset(mdp, TSU_ADRH0); | ||
1752 | int i, ret; | ||
1753 | |||
1754 | if (!mdp->cd->tsu) | ||
1755 | return 0; | ||
1756 | |||
1757 | i = sh_eth_tsu_find_entry(ndev, addr); | ||
1758 | if (i < 0) { | ||
1759 | /* No entry found, create one */ | ||
1760 | i = sh_eth_tsu_find_empty(ndev); | ||
1761 | if (i < 0) | ||
1762 | return -ENOMEM; | ||
1763 | ret = sh_eth_tsu_write_entry(ndev, reg_offset + i * 8, addr); | ||
1764 | if (ret < 0) | ||
1765 | return ret; | ||
1766 | |||
1767 | /* Enable the entry */ | ||
1768 | sh_eth_tsu_write(mdp, sh_eth_tsu_read(mdp, TSU_TEN) | | ||
1769 | (1 << (31 - i)), TSU_TEN); | ||
1770 | } | ||
1771 | |||
1772 | /* Entry found or created, enable POST */ | ||
1773 | sh_eth_tsu_enable_cam_entry_post(ndev, i); | ||
1774 | |||
1775 | return 0; | ||
1776 | } | ||
1777 | |||
1778 | static int sh_eth_tsu_del_entry(struct net_device *ndev, const u8 *addr) | ||
1779 | { | ||
1780 | struct sh_eth_private *mdp = netdev_priv(ndev); | ||
1781 | int i, ret; | ||
1782 | |||
1783 | if (!mdp->cd->tsu) | ||
1784 | return 0; | ||
1785 | |||
1786 | i = sh_eth_tsu_find_entry(ndev, addr); | ||
1787 | if (i) { | ||
1788 | /* Entry found */ | ||
1789 | if (sh_eth_tsu_disable_cam_entry_post(ndev, i)) | ||
1790 | goto done; | ||
1791 | |||
1792 | /* Disable the entry if both ports was disabled */ | ||
1793 | ret = sh_eth_tsu_disable_cam_entry_table(ndev, i); | ||
1794 | if (ret < 0) | ||
1795 | return ret; | ||
1796 | } | ||
1797 | done: | ||
1798 | return 0; | ||
1799 | } | ||
1800 | |||
1801 | static int sh_eth_tsu_purge_all(struct net_device *ndev) | ||
1802 | { | ||
1803 | struct sh_eth_private *mdp = netdev_priv(ndev); | ||
1804 | int i, ret; | ||
1805 | |||
1806 | if (unlikely(!mdp->cd->tsu)) | ||
1807 | return 0; | ||
1808 | |||
1809 | for (i = 0; i < SH_ETH_TSU_CAM_ENTRIES; i++) { | ||
1810 | if (sh_eth_tsu_disable_cam_entry_post(ndev, i)) | ||
1811 | continue; | ||
1812 | |||
1813 | /* Disable the entry if both ports was disabled */ | ||
1814 | ret = sh_eth_tsu_disable_cam_entry_table(ndev, i); | ||
1815 | if (ret < 0) | ||
1816 | return ret; | ||
1817 | } | ||
1818 | |||
1819 | return 0; | ||
1820 | } | ||
1821 | |||
1822 | static void sh_eth_tsu_purge_mcast(struct net_device *ndev) | ||
1823 | { | ||
1824 | struct sh_eth_private *mdp = netdev_priv(ndev); | ||
1825 | u8 addr[ETH_ALEN]; | ||
1826 | void *reg_offset = sh_eth_tsu_get_offset(mdp, TSU_ADRH0); | ||
1827 | int i; | ||
1828 | |||
1829 | if (unlikely(!mdp->cd->tsu)) | ||
1830 | return; | ||
1831 | |||
1832 | for (i = 0; i < SH_ETH_TSU_CAM_ENTRIES; i++, reg_offset += 8) { | ||
1833 | sh_eth_tsu_read_entry(reg_offset, addr); | ||
1834 | if (is_multicast_ether_addr(addr)) | ||
1835 | sh_eth_tsu_del_entry(ndev, addr); | ||
1836 | } | ||
1837 | } | ||
1838 | |||
1607 | /* Multicast reception directions set */ | 1839 | /* Multicast reception directions set */ |
1608 | static void sh_eth_set_multicast_list(struct net_device *ndev) | 1840 | static void sh_eth_set_multicast_list(struct net_device *ndev) |
1609 | { | 1841 | { |
1842 | struct sh_eth_private *mdp = netdev_priv(ndev); | ||
1843 | u32 ecmr_bits; | ||
1844 | int mcast_all = 0; | ||
1845 | unsigned long flags; | ||
1846 | |||
1847 | spin_lock_irqsave(&mdp->lock, flags); | ||
1848 | /* | ||
1849 | * Initial condition is MCT = 1, PRM = 0. | ||
1850 | * Depending on ndev->flags, set PRM or clear MCT | ||
1851 | */ | ||
1852 | ecmr_bits = (sh_eth_read(ndev, ECMR) & ~ECMR_PRM) | ECMR_MCT; | ||
1853 | |||
1854 | if (!(ndev->flags & IFF_MULTICAST)) { | ||
1855 | sh_eth_tsu_purge_mcast(ndev); | ||
1856 | mcast_all = 1; | ||
1857 | } | ||
1858 | if (ndev->flags & IFF_ALLMULTI) { | ||
1859 | sh_eth_tsu_purge_mcast(ndev); | ||
1860 | ecmr_bits &= ~ECMR_MCT; | ||
1861 | mcast_all = 1; | ||
1862 | } | ||
1863 | |||
1610 | if (ndev->flags & IFF_PROMISC) { | 1864 | if (ndev->flags & IFF_PROMISC) { |
1611 | /* Set promiscuous. */ | 1865 | sh_eth_tsu_purge_all(ndev); |
1612 | sh_eth_write(ndev, (sh_eth_read(ndev, ECMR) & ~ECMR_MCT) | | 1866 | ecmr_bits = (ecmr_bits & ~ECMR_MCT) | ECMR_PRM; |
1613 | ECMR_PRM, ECMR); | 1867 | } else if (mdp->cd->tsu) { |
1868 | struct netdev_hw_addr *ha; | ||
1869 | netdev_for_each_mc_addr(ha, ndev) { | ||
1870 | if (mcast_all && is_multicast_ether_addr(ha->addr)) | ||
1871 | continue; | ||
1872 | |||
1873 | if (sh_eth_tsu_add_entry(ndev, ha->addr) < 0) { | ||
1874 | if (!mcast_all) { | ||
1875 | sh_eth_tsu_purge_mcast(ndev); | ||
1876 | ecmr_bits &= ~ECMR_MCT; | ||
1877 | mcast_all = 1; | ||
1878 | } | ||
1879 | } | ||
1880 | } | ||
1614 | } else { | 1881 | } else { |
1615 | /* Normal, unicast/broadcast-only mode. */ | 1882 | /* Normal, unicast/broadcast-only mode. */ |
1616 | sh_eth_write(ndev, (sh_eth_read(ndev, ECMR) & ~ECMR_PRM) | | 1883 | ecmr_bits = (ecmr_bits & ~ECMR_PRM) | ECMR_MCT; |
1617 | ECMR_MCT, ECMR); | ||
1618 | } | 1884 | } |
1885 | |||
1886 | /* update the ethernet mode */ | ||
1887 | sh_eth_write(ndev, ecmr_bits, ECMR); | ||
1888 | |||
1889 | spin_unlock_irqrestore(&mdp->lock, flags); | ||
1619 | } | 1890 | } |
1620 | #endif /* SH_ETH_HAS_TSU */ | 1891 | #endif /* SH_ETH_HAS_TSU */ |
1621 | 1892 | ||
@@ -1869,6 +2140,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev) | |||
1869 | } | 2140 | } |
1870 | mdp->tsu_addr = ioremap(rtsu->start, | 2141 | mdp->tsu_addr = ioremap(rtsu->start, |
1871 | resource_size(rtsu)); | 2142 | resource_size(rtsu)); |
2143 | mdp->port = devno % 2; | ||
1872 | } | 2144 | } |
1873 | 2145 | ||
1874 | /* initialize first or needed device */ | 2146 | /* initialize first or needed device */ |
diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index cb07add8638f..86b392e22c01 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h | |||
@@ -29,6 +29,8 @@ | |||
29 | #define RX_RING_SIZE 64 /* Rx ring size */ | 29 | #define RX_RING_SIZE 64 /* Rx ring size */ |
30 | #define ETHERSMALL 60 | 30 | #define ETHERSMALL 60 |
31 | #define PKT_BUF_SZ 1538 | 31 | #define PKT_BUF_SZ 1538 |
32 | #define SH_ETH_TSU_TIMEOUT_MS 500 | ||
33 | #define SH_ETH_TSU_CAM_ENTRIES 32 | ||
32 | 34 | ||
33 | enum { | 35 | enum { |
34 | /* E-DMAC registers */ | 36 | /* E-DMAC registers */ |
@@ -778,6 +780,7 @@ struct sh_eth_private { | |||
778 | char post_rx; /* POST receive */ | 780 | char post_rx; /* POST receive */ |
779 | char post_fw; /* POST forward */ | 781 | char post_fw; /* POST forward */ |
780 | struct net_device_stats tsu_stats; /* TSU forward status */ | 782 | struct net_device_stats tsu_stats; /* TSU forward status */ |
783 | int port; /* for TSU */ | ||
781 | 784 | ||
782 | unsigned no_ether_link:1; | 785 | unsigned no_ether_link:1; |
783 | unsigned ether_link_active_low:1; | 786 | unsigned ether_link_active_low:1; |
@@ -811,6 +814,12 @@ static inline unsigned long sh_eth_read(struct net_device *ndev, | |||
811 | return ioread32(mdp->addr + mdp->reg_offset[enum_index]); | 814 | return ioread32(mdp->addr + mdp->reg_offset[enum_index]); |
812 | } | 815 | } |
813 | 816 | ||
817 | static inline void *sh_eth_tsu_get_offset(struct sh_eth_private *mdp, | ||
818 | int enum_index) | ||
819 | { | ||
820 | return mdp->tsu_addr + mdp->reg_offset[enum_index]; | ||
821 | } | ||
822 | |||
814 | static inline void sh_eth_tsu_write(struct sh_eth_private *mdp, | 823 | static inline void sh_eth_tsu_write(struct sh_eth_private *mdp, |
815 | unsigned long data, int enum_index) | 824 | unsigned long data, int enum_index) |
816 | { | 825 | { |