Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 1 | /******************************************************************************* |
| 2 | This is the driver for the MAC 10/100 on-chip Ethernet controller |
| 3 | currently tested on all the ST boards based on STb7109 and stx7200 SoCs. |
| 4 | |
| 5 | DWC Ether MAC 10/100 Universal version 4.0 has been used for developing |
| 6 | this code. |
| 7 | |
| 8 | This only implements the mac core functions for this chip. |
| 9 | |
| 10 | Copyright (C) 2007-2009 STMicroelectronics Ltd |
| 11 | |
| 12 | This program is free software; you can redistribute it and/or modify it |
| 13 | under the terms and conditions of the GNU General Public License, |
| 14 | version 2, as published by the Free Software Foundation. |
| 15 | |
| 16 | This program is distributed in the hope it will be useful, but WITHOUT |
| 17 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 18 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 19 | more details. |
| 20 | |
| 21 | You should have received a copy of the GNU General Public License along with |
| 22 | this program; if not, write to the Free Software Foundation, Inc., |
| 23 | 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
| 24 | |
| 25 | The full GNU General Public License is included in this distribution in |
| 26 | the file called "COPYING". |
| 27 | |
| 28 | Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> |
| 29 | *******************************************************************************/ |
| 30 | |
| 31 | #include <linux/crc32.h> |
Alexey Dobriyan | b7f080c | 2011-06-16 11:01:34 +0000 | [diff] [blame] | 32 | #include <asm/io.h> |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 33 | #include "dwmac100.h" |
| 34 | |
Giuseppe CAVALLARO | ad01b7d | 2010-08-23 20:40:42 +0000 | [diff] [blame] | 35 | static void dwmac100_core_init(void __iomem *ioaddr) |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 36 | { |
| 37 | u32 value = readl(ioaddr + MAC_CONTROL); |
| 38 | |
| 39 | writel((value | MAC_CORE_INIT), ioaddr + MAC_CONTROL); |
| 40 | |
| 41 | #ifdef STMMAC_VLAN_TAG_USED |
| 42 | writel(ETH_P_8021Q, ioaddr + MAC_VLAN1); |
| 43 | #endif |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 44 | } |
| 45 | |
Giuseppe CAVALLARO | ebbb293 | 2010-09-17 03:23:40 +0000 | [diff] [blame] | 46 | static int dwmac100_rx_coe_supported(void __iomem *ioaddr) |
| 47 | { |
| 48 | return 0; |
| 49 | } |
| 50 | |
Giuseppe CAVALLARO | ad01b7d | 2010-08-23 20:40:42 +0000 | [diff] [blame] | 51 | static void dwmac100_dump_mac_regs(void __iomem *ioaddr) |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 52 | { |
| 53 | pr_info("\t----------------------------------------------\n" |
David S. Miller | 74af4c7 | 2010-08-31 20:56:48 -0700 | [diff] [blame] | 54 | "\t DWMAC 100 CSR (base addr = 0x%p)\n" |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 55 | "\t----------------------------------------------\n", |
David S. Miller | 74af4c7 | 2010-08-31 20:56:48 -0700 | [diff] [blame] | 56 | ioaddr); |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 57 | pr_info("\tcontrol reg (offset 0x%x): 0x%08x\n", MAC_CONTROL, |
| 58 | readl(ioaddr + MAC_CONTROL)); |
| 59 | pr_info("\taddr HI (offset 0x%x): 0x%08x\n ", MAC_ADDR_HIGH, |
| 60 | readl(ioaddr + MAC_ADDR_HIGH)); |
| 61 | pr_info("\taddr LO (offset 0x%x): 0x%08x\n", MAC_ADDR_LOW, |
| 62 | readl(ioaddr + MAC_ADDR_LOW)); |
| 63 | pr_info("\tmulticast hash HI (offset 0x%x): 0x%08x\n", |
| 64 | MAC_HASH_HIGH, readl(ioaddr + MAC_HASH_HIGH)); |
| 65 | pr_info("\tmulticast hash LO (offset 0x%x): 0x%08x\n", |
| 66 | MAC_HASH_LOW, readl(ioaddr + MAC_HASH_LOW)); |
| 67 | pr_info("\tflow control (offset 0x%x): 0x%08x\n", |
| 68 | MAC_FLOW_CTRL, readl(ioaddr + MAC_FLOW_CTRL)); |
| 69 | pr_info("\tVLAN1 tag (offset 0x%x): 0x%08x\n", MAC_VLAN1, |
| 70 | readl(ioaddr + MAC_VLAN1)); |
| 71 | pr_info("\tVLAN2 tag (offset 0x%x): 0x%08x\n", MAC_VLAN2, |
| 72 | readl(ioaddr + MAC_VLAN2)); |
| 73 | pr_info("\n\tMAC management counter registers\n"); |
| 74 | pr_info("\t MMC crtl (offset 0x%x): 0x%08x\n", |
| 75 | MMC_CONTROL, readl(ioaddr + MMC_CONTROL)); |
| 76 | pr_info("\t MMC High Interrupt (offset 0x%x): 0x%08x\n", |
| 77 | MMC_HIGH_INTR, readl(ioaddr + MMC_HIGH_INTR)); |
| 78 | pr_info("\t MMC Low Interrupt (offset 0x%x): 0x%08x\n", |
| 79 | MMC_LOW_INTR, readl(ioaddr + MMC_LOW_INTR)); |
| 80 | pr_info("\t MMC High Interrupt Mask (offset 0x%x): 0x%08x\n", |
| 81 | MMC_HIGH_INTR_MASK, readl(ioaddr + MMC_HIGH_INTR_MASK)); |
| 82 | pr_info("\t MMC Low Interrupt Mask (offset 0x%x): 0x%08x\n", |
| 83 | MMC_LOW_INTR_MASK, readl(ioaddr + MMC_LOW_INTR_MASK)); |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 84 | } |
| 85 | |
Giuseppe CAVALLARO | ad01b7d | 2010-08-23 20:40:42 +0000 | [diff] [blame] | 86 | static void dwmac100_irq_status(void __iomem *ioaddr) |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 87 | { |
| 88 | return; |
| 89 | } |
| 90 | |
Giuseppe CAVALLARO | ad01b7d | 2010-08-23 20:40:42 +0000 | [diff] [blame] | 91 | static void dwmac100_set_umac_addr(void __iomem *ioaddr, unsigned char *addr, |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 92 | unsigned int reg_n) |
| 93 | { |
| 94 | stmmac_set_mac_addr(ioaddr, addr, MAC_ADDR_HIGH, MAC_ADDR_LOW); |
| 95 | } |
| 96 | |
Giuseppe CAVALLARO | ad01b7d | 2010-08-23 20:40:42 +0000 | [diff] [blame] | 97 | static void dwmac100_get_umac_addr(void __iomem *ioaddr, unsigned char *addr, |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 98 | unsigned int reg_n) |
| 99 | { |
| 100 | stmmac_get_mac_addr(ioaddr, addr, MAC_ADDR_HIGH, MAC_ADDR_LOW); |
| 101 | } |
| 102 | |
| 103 | static void dwmac100_set_filter(struct net_device *dev) |
| 104 | { |
Giuseppe CAVALLARO | ad01b7d | 2010-08-23 20:40:42 +0000 | [diff] [blame] | 105 | void __iomem *ioaddr = (void __iomem *) dev->base_addr; |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 106 | u32 value = readl(ioaddr + MAC_CONTROL); |
| 107 | |
| 108 | if (dev->flags & IFF_PROMISC) { |
| 109 | value |= MAC_CONTROL_PR; |
| 110 | value &= ~(MAC_CONTROL_PM | MAC_CONTROL_IF | MAC_CONTROL_HO | |
| 111 | MAC_CONTROL_HP); |
| 112 | } else if ((netdev_mc_count(dev) > HASH_TABLE_SIZE) |
| 113 | || (dev->flags & IFF_ALLMULTI)) { |
| 114 | value |= MAC_CONTROL_PM; |
| 115 | value &= ~(MAC_CONTROL_PR | MAC_CONTROL_IF | MAC_CONTROL_HO); |
| 116 | writel(0xffffffff, ioaddr + MAC_HASH_HIGH); |
| 117 | writel(0xffffffff, ioaddr + MAC_HASH_LOW); |
| 118 | } else if (netdev_mc_empty(dev)) { /* no multicast */ |
| 119 | value &= ~(MAC_CONTROL_PM | MAC_CONTROL_PR | MAC_CONTROL_IF | |
| 120 | MAC_CONTROL_HO | MAC_CONTROL_HP); |
| 121 | } else { |
| 122 | u32 mc_filter[2]; |
| 123 | struct netdev_hw_addr *ha; |
| 124 | |
| 125 | /* Perfect filter mode for physical address and Hash |
| 126 | filter for multicast */ |
| 127 | value |= MAC_CONTROL_HP; |
| 128 | value &= ~(MAC_CONTROL_PM | MAC_CONTROL_PR | |
| 129 | MAC_CONTROL_IF | MAC_CONTROL_HO); |
| 130 | |
| 131 | memset(mc_filter, 0, sizeof(mc_filter)); |
| 132 | netdev_for_each_mc_addr(ha, dev) { |
| 133 | /* The upper 6 bits of the calculated CRC are used to |
| 134 | * index the contens of the hash table */ |
| 135 | int bit_nr = |
| 136 | ether_crc(ETH_ALEN, ha->addr) >> 26; |
| 137 | /* The most significant bit determines the register to |
| 138 | * use (H/L) while the other 5 bits determine the bit |
| 139 | * within the register. */ |
| 140 | mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); |
| 141 | } |
| 142 | writel(mc_filter[0], ioaddr + MAC_HASH_LOW); |
| 143 | writel(mc_filter[1], ioaddr + MAC_HASH_HIGH); |
| 144 | } |
| 145 | |
| 146 | writel(value, ioaddr + MAC_CONTROL); |
| 147 | |
Giuseppe CAVALLARO | 56b106a | 2010-04-13 20:21:12 +0000 | [diff] [blame] | 148 | CHIP_DBG(KERN_INFO "%s: CTRL reg: 0x%08x Hash regs: " |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 149 | "HI 0x%08x, LO 0x%08x\n", |
| 150 | __func__, readl(ioaddr + MAC_CONTROL), |
| 151 | readl(ioaddr + MAC_HASH_HIGH), readl(ioaddr + MAC_HASH_LOW)); |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 152 | } |
| 153 | |
Giuseppe CAVALLARO | ad01b7d | 2010-08-23 20:40:42 +0000 | [diff] [blame] | 154 | static void dwmac100_flow_ctrl(void __iomem *ioaddr, unsigned int duplex, |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 155 | unsigned int fc, unsigned int pause_time) |
| 156 | { |
| 157 | unsigned int flow = MAC_FLOW_CTRL_ENABLE; |
| 158 | |
| 159 | if (duplex) |
| 160 | flow |= (pause_time << MAC_FLOW_CTRL_PT_SHIFT); |
| 161 | writel(flow, ioaddr + MAC_FLOW_CTRL); |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 162 | } |
| 163 | |
| 164 | /* No PMT module supported for this Ethernet Controller. |
| 165 | * Tested on ST platforms only. |
| 166 | */ |
Giuseppe CAVALLARO | ad01b7d | 2010-08-23 20:40:42 +0000 | [diff] [blame] | 167 | static void dwmac100_pmt(void __iomem *ioaddr, unsigned long mode) |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 168 | { |
| 169 | return; |
| 170 | } |
| 171 | |
stephen hemminger | cadb792 | 2010-10-13 14:51:25 +0000 | [diff] [blame] | 172 | static const struct stmmac_ops dwmac100_ops = { |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 173 | .core_init = dwmac100_core_init, |
Giuseppe CAVALLARO | ebbb293 | 2010-09-17 03:23:40 +0000 | [diff] [blame] | 174 | .rx_coe = dwmac100_rx_coe_supported, |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 175 | .dump_regs = dwmac100_dump_mac_regs, |
| 176 | .host_irq_status = dwmac100_irq_status, |
| 177 | .set_filter = dwmac100_set_filter, |
| 178 | .flow_ctrl = dwmac100_flow_ctrl, |
| 179 | .pmt = dwmac100_pmt, |
| 180 | .set_umac_addr = dwmac100_set_umac_addr, |
| 181 | .get_umac_addr = dwmac100_get_umac_addr, |
| 182 | }; |
| 183 | |
Giuseppe CAVALLARO | ad01b7d | 2010-08-23 20:40:42 +0000 | [diff] [blame] | 184 | struct mac_device_info *dwmac100_setup(void __iomem *ioaddr) |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 185 | { |
| 186 | struct mac_device_info *mac; |
| 187 | |
| 188 | mac = kzalloc(sizeof(const struct mac_device_info), GFP_KERNEL); |
Dan Carpenter | 1ff2190 | 2010-07-22 01:16:48 +0000 | [diff] [blame] | 189 | if (!mac) |
| 190 | return NULL; |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 191 | |
| 192 | pr_info("\tDWMAC100\n"); |
| 193 | |
| 194 | mac->mac = &dwmac100_ops; |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 195 | mac->dma = &dwmac100_dma_ops; |
| 196 | |
Giuseppe CAVALLARO | 3c32be6 | 2010-04-13 20:21:11 +0000 | [diff] [blame] | 197 | mac->link.port = MAC_CONTROL_PS; |
| 198 | mac->link.duplex = MAC_CONTROL_F; |
| 199 | mac->link.speed = 0; |
| 200 | mac->mii.addr = MAC_MII_ADDR; |
| 201 | mac->mii.data = MAC_MII_DATA; |
| 202 | |
| 203 | return mac; |
| 204 | } |