| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 1 | /* $Date: 2006/04/28 19:20:06 $ $RCSfile: vsc7326.c,v $ $Revision: 1.19 $ */ | 
 | 2 |  | 
 | 3 | /* Driver for Vitesse VSC7326 (Schaumburg) MAC */ | 
 | 4 |  | 
 | 5 | #include "gmac.h" | 
 | 6 | #include "elmer0.h" | 
 | 7 | #include "vsc7326_reg.h" | 
 | 8 |  | 
 | 9 | /* Update fast changing statistics every 15 seconds */ | 
 | 10 | #define STATS_TICK_SECS 15 | 
 | 11 | /* 30 minutes for full statistics update */ | 
 | 12 | #define MAJOR_UPDATE_TICKS (1800 / STATS_TICK_SECS) | 
 | 13 |  | 
 | 14 | #define MAX_MTU 9600 | 
 | 15 |  | 
 | 16 | /* The egress WM value 0x01a01fff should be used only when the | 
 | 17 |  * interface is down (MAC port disabled). This is a workaround | 
 | 18 |  * for disabling the T2/MAC flow-control. When the interface is | 
 | 19 |  * enabled, the WM value should be set to 0x014a03F0. | 
 | 20 |  */ | 
 | 21 | #define WM_DISABLE	0x01a01fff | 
 | 22 | #define WM_ENABLE	0x014a03F0 | 
 | 23 |  | 
 | 24 | struct init_table { | 
 | 25 | 	u32 addr; | 
 | 26 | 	u32 data; | 
 | 27 | }; | 
 | 28 |  | 
 | 29 | struct _cmac_instance { | 
 | 30 | 	u32 index; | 
 | 31 | 	u32 ticks; | 
 | 32 | }; | 
 | 33 |  | 
 | 34 | #define INITBLOCK_SLEEP	0xffffffff | 
 | 35 |  | 
 | 36 | static void vsc_read(adapter_t *adapter, u32 addr, u32 *val) | 
 | 37 | { | 
 | 38 | 	u32 status, vlo, vhi; | 
 | 39 | 	int i; | 
 | 40 |  | 
 | 41 | 	spin_lock_bh(&adapter->mac_lock); | 
 | 42 | 	t1_tpi_read(adapter, (addr << 2) + 4, &vlo); | 
 | 43 | 	i = 0; | 
 | 44 | 	do { | 
 | 45 | 		t1_tpi_read(adapter, (REG_LOCAL_STATUS << 2) + 4, &vlo); | 
 | 46 | 		t1_tpi_read(adapter, REG_LOCAL_STATUS << 2, &vhi); | 
 | 47 | 		status = (vhi << 16) | vlo; | 
 | 48 | 		i++; | 
 | 49 | 	} while (((status & 1) == 0) && (i < 50)); | 
 | 50 | 	if (i == 50) | 
 | 51 | 		CH_ERR("Invalid tpi read from MAC, breaking loop.\n"); | 
 | 52 |  | 
 | 53 | 	t1_tpi_read(adapter, (REG_LOCAL_DATA << 2) + 4, &vlo); | 
 | 54 | 	t1_tpi_read(adapter, REG_LOCAL_DATA << 2, &vhi); | 
 | 55 |  | 
 | 56 | 	*val = (vhi << 16) | vlo; | 
 | 57 |  | 
 | 58 | 	/* CH_ERR("rd: block: 0x%x  sublock: 0x%x  reg: 0x%x  data: 0x%x\n", | 
 | 59 | 		((addr&0xe000)>>13), ((addr&0x1e00)>>9), | 
 | 60 | 		((addr&0x01fe)>>1), *val); */ | 
 | 61 | 	spin_unlock_bh(&adapter->mac_lock); | 
 | 62 | } | 
 | 63 |  | 
 | 64 | static void vsc_write(adapter_t *adapter, u32 addr, u32 data) | 
 | 65 | { | 
 | 66 | 	spin_lock_bh(&adapter->mac_lock); | 
 | 67 | 	t1_tpi_write(adapter, (addr << 2) + 4, data & 0xFFFF); | 
 | 68 | 	t1_tpi_write(adapter, addr << 2, (data >> 16) & 0xFFFF); | 
 | 69 | 	/* CH_ERR("wr: block: 0x%x  sublock: 0x%x  reg: 0x%x  data: 0x%x\n", | 
 | 70 | 		((addr&0xe000)>>13), ((addr&0x1e00)>>9), | 
 | 71 | 		((addr&0x01fe)>>1), data); */ | 
 | 72 | 	spin_unlock_bh(&adapter->mac_lock); | 
 | 73 | } | 
 | 74 |  | 
 | 75 | /* Hard reset the MAC.  This wipes out *all* configuration. */ | 
 | 76 | static void vsc7326_full_reset(adapter_t* adapter) | 
 | 77 | { | 
 | 78 | 	u32 val; | 
 | 79 | 	u32 result = 0xffff; | 
 | 80 |  | 
 | 81 | 	t1_tpi_read(adapter, A_ELMER0_GPO, &val); | 
 | 82 | 	val &= ~1; | 
 | 83 | 	t1_tpi_write(adapter, A_ELMER0_GPO, val); | 
 | 84 | 	udelay(2); | 
 | 85 | 	val |= 0x1;	/* Enable mac MAC itself */ | 
 | 86 | 	val |= 0x800;	/* Turn off the red LED */ | 
 | 87 | 	t1_tpi_write(adapter, A_ELMER0_GPO, val); | 
 | 88 | 	mdelay(1); | 
 | 89 | 	vsc_write(adapter, REG_SW_RESET, 0x80000001); | 
 | 90 | 	do { | 
 | 91 | 		mdelay(1); | 
 | 92 | 		vsc_read(adapter, REG_SW_RESET, &result); | 
 | 93 | 	} while (result != 0x0); | 
 | 94 | } | 
 | 95 |  | 
 | 96 | static struct init_table vsc7326_reset[] = { | 
 | 97 | 	{      REG_IFACE_MODE, 0x00000000 }, | 
 | 98 | 	{         REG_CRC_CFG, 0x00000020 }, | 
 | 99 | 	{   REG_PLL_CLK_SPEED, 0x00050c00 }, | 
 | 100 | 	{   REG_PLL_CLK_SPEED, 0x00050c00 }, | 
 | 101 | 	{            REG_MSCH, 0x00002f14 }, | 
 | 102 | 	{       REG_SPI4_MISC, 0x00040409 }, | 
 | 103 | 	{     REG_SPI4_DESKEW, 0x00080000 }, | 
 | 104 | 	{ REG_SPI4_ING_SETUP2, 0x08080004 }, | 
 | 105 | 	{ REG_SPI4_ING_SETUP0, 0x04111004 }, | 
 | 106 | 	{ REG_SPI4_EGR_SETUP0, 0x80001a04 }, | 
 | 107 | 	{ REG_SPI4_ING_SETUP1, 0x02010000 }, | 
 | 108 | 	{      REG_AGE_INC(0), 0x00000000 }, | 
 | 109 | 	{      REG_AGE_INC(1), 0x00000000 }, | 
 | 110 | 	{     REG_ING_CONTROL, 0x0a200011 }, | 
 | 111 | 	{     REG_EGR_CONTROL, 0xa0010091 }, | 
 | 112 | }; | 
 | 113 |  | 
 | 114 | static struct init_table vsc7326_portinit[4][22] = { | 
 | 115 | 	{	/* Port 0 */ | 
 | 116 | 			/* FIFO setup */ | 
 | 117 | 		{           REG_DBG(0), 0x000004f0 }, | 
 | 118 | 		{           REG_HDX(0), 0x00073101 }, | 
 | 119 | 		{        REG_TEST(0,0), 0x00000022 }, | 
 | 120 | 		{        REG_TEST(1,0), 0x00000022 }, | 
 | 121 | 		{  REG_TOP_BOTTOM(0,0), 0x003f0000 }, | 
 | 122 | 		{  REG_TOP_BOTTOM(1,0), 0x00120000 }, | 
 | 123 | 		{ REG_HIGH_LOW_WM(0,0), 0x07460757 }, | 
 | 124 | 		{ REG_HIGH_LOW_WM(1,0), WM_DISABLE }, | 
 | 125 | 		{   REG_CT_THRHLD(0,0), 0x00000000 }, | 
 | 126 | 		{   REG_CT_THRHLD(1,0), 0x00000000 }, | 
 | 127 | 		{         REG_BUCKE(0), 0x0002ffff }, | 
 | 128 | 		{         REG_BUCKI(0), 0x0002ffff }, | 
 | 129 | 		{        REG_TEST(0,0), 0x00000020 }, | 
 | 130 | 		{        REG_TEST(1,0), 0x00000020 }, | 
 | 131 | 			/* Port config */ | 
 | 132 | 		{       REG_MAX_LEN(0), 0x00002710 }, | 
 | 133 | 		{     REG_PORT_FAIL(0), 0x00000002 }, | 
 | 134 | 		{    REG_NORMALIZER(0), 0x00000a64 }, | 
 | 135 | 		{        REG_DENORM(0), 0x00000010 }, | 
 | 136 | 		{     REG_STICK_BIT(0), 0x03baa370 }, | 
 | 137 | 		{     REG_DEV_SETUP(0), 0x00000083 }, | 
 | 138 | 		{     REG_DEV_SETUP(0), 0x00000082 }, | 
 | 139 | 		{      REG_MODE_CFG(0), 0x0200259f }, | 
 | 140 | 	}, | 
 | 141 | 	{	/* Port 1 */ | 
 | 142 | 			/* FIFO setup */ | 
 | 143 | 		{           REG_DBG(1), 0x000004f0 }, | 
 | 144 | 		{           REG_HDX(1), 0x00073101 }, | 
 | 145 | 		{        REG_TEST(0,1), 0x00000022 }, | 
 | 146 | 		{        REG_TEST(1,1), 0x00000022 }, | 
 | 147 | 		{  REG_TOP_BOTTOM(0,1), 0x007e003f }, | 
 | 148 | 		{  REG_TOP_BOTTOM(1,1), 0x00240012 }, | 
 | 149 | 		{ REG_HIGH_LOW_WM(0,1), 0x07460757 }, | 
 | 150 | 		{ REG_HIGH_LOW_WM(1,1), WM_DISABLE }, | 
 | 151 | 		{   REG_CT_THRHLD(0,1), 0x00000000 }, | 
 | 152 | 		{   REG_CT_THRHLD(1,1), 0x00000000 }, | 
 | 153 | 		{         REG_BUCKE(1), 0x0002ffff }, | 
 | 154 | 		{         REG_BUCKI(1), 0x0002ffff }, | 
 | 155 | 		{        REG_TEST(0,1), 0x00000020 }, | 
 | 156 | 		{        REG_TEST(1,1), 0x00000020 }, | 
 | 157 | 			/* Port config */ | 
 | 158 | 		{       REG_MAX_LEN(1), 0x00002710 }, | 
 | 159 | 		{     REG_PORT_FAIL(1), 0x00000002 }, | 
 | 160 | 		{    REG_NORMALIZER(1), 0x00000a64 }, | 
 | 161 | 		{        REG_DENORM(1), 0x00000010 }, | 
 | 162 | 		{     REG_STICK_BIT(1), 0x03baa370 }, | 
 | 163 | 		{     REG_DEV_SETUP(1), 0x00000083 }, | 
 | 164 | 		{     REG_DEV_SETUP(1), 0x00000082 }, | 
 | 165 | 		{      REG_MODE_CFG(1), 0x0200259f }, | 
 | 166 | 	}, | 
 | 167 | 	{	/* Port 2 */ | 
 | 168 | 			/* FIFO setup */ | 
 | 169 | 		{           REG_DBG(2), 0x000004f0 }, | 
 | 170 | 		{           REG_HDX(2), 0x00073101 }, | 
 | 171 | 		{        REG_TEST(0,2), 0x00000022 }, | 
 | 172 | 		{        REG_TEST(1,2), 0x00000022 }, | 
 | 173 | 		{  REG_TOP_BOTTOM(0,2), 0x00bd007e }, | 
 | 174 | 		{  REG_TOP_BOTTOM(1,2), 0x00360024 }, | 
 | 175 | 		{ REG_HIGH_LOW_WM(0,2), 0x07460757 }, | 
 | 176 | 		{ REG_HIGH_LOW_WM(1,2), WM_DISABLE }, | 
 | 177 | 		{   REG_CT_THRHLD(0,2), 0x00000000 }, | 
 | 178 | 		{   REG_CT_THRHLD(1,2), 0x00000000 }, | 
 | 179 | 		{         REG_BUCKE(2), 0x0002ffff }, | 
 | 180 | 		{         REG_BUCKI(2), 0x0002ffff }, | 
 | 181 | 		{        REG_TEST(0,2), 0x00000020 }, | 
 | 182 | 		{        REG_TEST(1,2), 0x00000020 }, | 
 | 183 | 			/* Port config */ | 
 | 184 | 		{       REG_MAX_LEN(2), 0x00002710 }, | 
 | 185 | 		{     REG_PORT_FAIL(2), 0x00000002 }, | 
 | 186 | 		{    REG_NORMALIZER(2), 0x00000a64 }, | 
 | 187 | 		{        REG_DENORM(2), 0x00000010 }, | 
 | 188 | 		{     REG_STICK_BIT(2), 0x03baa370 }, | 
 | 189 | 		{     REG_DEV_SETUP(2), 0x00000083 }, | 
 | 190 | 		{     REG_DEV_SETUP(2), 0x00000082 }, | 
 | 191 | 		{      REG_MODE_CFG(2), 0x0200259f }, | 
 | 192 | 	}, | 
 | 193 | 	{	/* Port 3 */ | 
 | 194 | 			/* FIFO setup */ | 
 | 195 | 		{           REG_DBG(3), 0x000004f0 }, | 
 | 196 | 		{           REG_HDX(3), 0x00073101 }, | 
 | 197 | 		{        REG_TEST(0,3), 0x00000022 }, | 
 | 198 | 		{        REG_TEST(1,3), 0x00000022 }, | 
 | 199 | 		{  REG_TOP_BOTTOM(0,3), 0x00fc00bd }, | 
 | 200 | 		{  REG_TOP_BOTTOM(1,3), 0x00480036 }, | 
 | 201 | 		{ REG_HIGH_LOW_WM(0,3), 0x07460757 }, | 
 | 202 | 		{ REG_HIGH_LOW_WM(1,3), WM_DISABLE }, | 
 | 203 | 		{   REG_CT_THRHLD(0,3), 0x00000000 }, | 
 | 204 | 		{   REG_CT_THRHLD(1,3), 0x00000000 }, | 
 | 205 | 		{         REG_BUCKE(3), 0x0002ffff }, | 
 | 206 | 		{         REG_BUCKI(3), 0x0002ffff }, | 
 | 207 | 		{        REG_TEST(0,3), 0x00000020 }, | 
 | 208 | 		{        REG_TEST(1,3), 0x00000020 }, | 
 | 209 | 			/* Port config */ | 
 | 210 | 		{       REG_MAX_LEN(3), 0x00002710 }, | 
 | 211 | 		{     REG_PORT_FAIL(3), 0x00000002 }, | 
 | 212 | 		{    REG_NORMALIZER(3), 0x00000a64 }, | 
 | 213 | 		{        REG_DENORM(3), 0x00000010 }, | 
 | 214 | 		{     REG_STICK_BIT(3), 0x03baa370 }, | 
 | 215 | 		{     REG_DEV_SETUP(3), 0x00000083 }, | 
 | 216 | 		{     REG_DEV_SETUP(3), 0x00000082 }, | 
 | 217 | 		{      REG_MODE_CFG(3), 0x0200259f }, | 
 | 218 | 	}, | 
 | 219 | }; | 
 | 220 |  | 
 | 221 | static void run_table(adapter_t *adapter, struct init_table *ib, int len) | 
 | 222 | { | 
 | 223 | 	int i; | 
 | 224 |  | 
 | 225 | 	for (i = 0; i < len; i++) { | 
 | 226 | 		if (ib[i].addr == INITBLOCK_SLEEP) { | 
 | 227 | 			udelay( ib[i].data ); | 
 | 228 | 			CH_ERR("sleep %d us\n",ib[i].data); | 
| Francois Romieu | d748742 | 2006-12-11 23:49:13 +0100 | [diff] [blame] | 229 | 		} else | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 230 | 			vsc_write( adapter, ib[i].addr, ib[i].data ); | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 231 | 	} | 
 | 232 | } | 
 | 233 |  | 
 | 234 | static int bist_rd(adapter_t *adapter, int moduleid, int address) | 
 | 235 | { | 
| Francois Romieu | 356bd14 | 2006-12-11 23:47:00 +0100 | [diff] [blame] | 236 | 	int data = 0; | 
 | 237 | 	u32 result = 0; | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 238 |  | 
| Francois Romieu | 356bd14 | 2006-12-11 23:47:00 +0100 | [diff] [blame] | 239 | 	if ((address != 0x0) && | 
 | 240 | 	    (address != 0x1) && | 
 | 241 | 	    (address != 0x2) && | 
 | 242 | 	    (address != 0xd) && | 
 | 243 | 	    (address != 0xe)) | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 244 | 			CH_ERR("No bist address: 0x%x\n", address); | 
 | 245 |  | 
 | 246 | 	data = ((0x00 << 24) | ((address & 0xff) << 16) | (0x00 << 8) | | 
 | 247 | 		((moduleid & 0xff) << 0)); | 
 | 248 | 	vsc_write(adapter, REG_RAM_BIST_CMD, data); | 
 | 249 |  | 
 | 250 | 	udelay(10); | 
 | 251 |  | 
 | 252 | 	vsc_read(adapter, REG_RAM_BIST_RESULT, &result); | 
| Francois Romieu | 356bd14 | 2006-12-11 23:47:00 +0100 | [diff] [blame] | 253 | 	if ((result & (1 << 9)) != 0x0) | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 254 | 		CH_ERR("Still in bist read: 0x%x\n", result); | 
| Francois Romieu | 356bd14 | 2006-12-11 23:47:00 +0100 | [diff] [blame] | 255 | 	else if ((result & (1 << 8)) != 0x0) | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 256 | 		CH_ERR("bist read error: 0x%x\n", result); | 
 | 257 |  | 
| Francois Romieu | b7d5839 | 2006-12-11 23:41:36 +0100 | [diff] [blame] | 258 | 	return (result & 0xff); | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 259 | } | 
 | 260 |  | 
 | 261 | static int bist_wr(adapter_t *adapter, int moduleid, int address, int value) | 
 | 262 | { | 
| Francois Romieu | 356bd14 | 2006-12-11 23:47:00 +0100 | [diff] [blame] | 263 | 	int data = 0; | 
 | 264 | 	u32 result = 0; | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 265 |  | 
| Francois Romieu | 356bd14 | 2006-12-11 23:47:00 +0100 | [diff] [blame] | 266 | 	if ((address != 0x0) && | 
 | 267 | 	    (address != 0x1) && | 
 | 268 | 	    (address != 0x2) && | 
 | 269 | 	    (address != 0xd) && | 
 | 270 | 	    (address != 0xe)) | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 271 | 			CH_ERR("No bist address: 0x%x\n", address); | 
 | 272 |  | 
| Francois Romieu | 356bd14 | 2006-12-11 23:47:00 +0100 | [diff] [blame] | 273 | 	if (value > 255) | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 274 | 		CH_ERR("Suspicious write out of range value: 0x%x\n", value); | 
 | 275 |  | 
 | 276 | 	data = ((0x01 << 24) | ((address & 0xff) << 16) | (value << 8) | | 
 | 277 | 		((moduleid & 0xff) << 0)); | 
 | 278 | 	vsc_write(adapter, REG_RAM_BIST_CMD, data); | 
 | 279 |  | 
 | 280 | 	udelay(5); | 
 | 281 |  | 
 | 282 | 	vsc_read(adapter, REG_RAM_BIST_CMD, &result); | 
| Francois Romieu | 356bd14 | 2006-12-11 23:47:00 +0100 | [diff] [blame] | 283 | 	if ((result & (1 << 27)) != 0x0) | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 284 | 		CH_ERR("Still in bist write: 0x%x\n", result); | 
| Francois Romieu | 356bd14 | 2006-12-11 23:47:00 +0100 | [diff] [blame] | 285 | 	else if ((result & (1 << 26)) != 0x0) | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 286 | 		CH_ERR("bist write error: 0x%x\n", result); | 
 | 287 |  | 
| Francois Romieu | b7d5839 | 2006-12-11 23:41:36 +0100 | [diff] [blame] | 288 | 	return 0; | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 289 | } | 
 | 290 |  | 
 | 291 | static int run_bist(adapter_t *adapter, int moduleid) | 
 | 292 | { | 
 | 293 | 	/*run bist*/ | 
 | 294 | 	(void) bist_wr(adapter,moduleid, 0x00, 0x02); | 
 | 295 | 	(void) bist_wr(adapter,moduleid, 0x01, 0x01); | 
 | 296 |  | 
| Francois Romieu | b7d5839 | 2006-12-11 23:41:36 +0100 | [diff] [blame] | 297 | 	return 0; | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 298 | } | 
 | 299 |  | 
 | 300 | static int check_bist(adapter_t *adapter, int moduleid) | 
 | 301 | { | 
 | 302 | 	int result=0; | 
 | 303 | 	int column=0; | 
 | 304 | 	/*check bist*/ | 
 | 305 | 	result = bist_rd(adapter,moduleid, 0x02); | 
 | 306 | 	column = ((bist_rd(adapter,moduleid, 0x0e)<<8) + | 
 | 307 | 			(bist_rd(adapter,moduleid, 0x0d))); | 
 | 308 | 	if ((result & 3) != 0x3) | 
 | 309 | 		CH_ERR("Result: 0x%x  BIST error in ram %d, column: 0x%04x\n", | 
 | 310 | 			result, moduleid, column); | 
| Francois Romieu | b7d5839 | 2006-12-11 23:41:36 +0100 | [diff] [blame] | 311 | 	return 0; | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 312 | } | 
 | 313 |  | 
 | 314 | static int enable_mem(adapter_t *adapter, int moduleid) | 
 | 315 | { | 
 | 316 | 	/*enable mem*/ | 
 | 317 | 	(void) bist_wr(adapter,moduleid, 0x00, 0x00); | 
| Francois Romieu | b7d5839 | 2006-12-11 23:41:36 +0100 | [diff] [blame] | 318 | 	return 0; | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 319 | } | 
 | 320 |  | 
 | 321 | static int run_bist_all(adapter_t *adapter) | 
 | 322 | { | 
| Francois Romieu | 356bd14 | 2006-12-11 23:47:00 +0100 | [diff] [blame] | 323 | 	int port = 0; | 
 | 324 | 	u32 val = 0; | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 325 |  | 
 | 326 | 	vsc_write(adapter, REG_MEM_BIST, 0x5); | 
 | 327 | 	vsc_read(adapter, REG_MEM_BIST, &val); | 
 | 328 |  | 
| Francois Romieu | 356bd14 | 2006-12-11 23:47:00 +0100 | [diff] [blame] | 329 | 	for (port = 0; port < 12; port++) | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 330 | 		vsc_write(adapter, REG_DEV_SETUP(port), 0x0); | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 331 |  | 
 | 332 | 	udelay(300); | 
 | 333 | 	vsc_write(adapter, REG_SPI4_MISC, 0x00040409); | 
 | 334 | 	udelay(300); | 
 | 335 |  | 
 | 336 | 	(void) run_bist(adapter,13); | 
 | 337 | 	(void) run_bist(adapter,14); | 
 | 338 | 	(void) run_bist(adapter,20); | 
 | 339 | 	(void) run_bist(adapter,21); | 
 | 340 | 	mdelay(200); | 
 | 341 | 	(void) check_bist(adapter,13); | 
 | 342 | 	(void) check_bist(adapter,14); | 
 | 343 | 	(void) check_bist(adapter,20); | 
 | 344 | 	(void) check_bist(adapter,21); | 
 | 345 | 	udelay(100); | 
 | 346 | 	(void) enable_mem(adapter,13); | 
 | 347 | 	(void) enable_mem(adapter,14); | 
 | 348 | 	(void) enable_mem(adapter,20); | 
 | 349 | 	(void) enable_mem(adapter,21); | 
 | 350 | 	udelay(300); | 
 | 351 | 	vsc_write(adapter, REG_SPI4_MISC, 0x60040400); | 
 | 352 | 	udelay(300); | 
| Francois Romieu | 356bd14 | 2006-12-11 23:47:00 +0100 | [diff] [blame] | 353 | 	for (port = 0; port < 12; port++) | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 354 | 		vsc_write(adapter, REG_DEV_SETUP(port), 0x1); | 
| Francois Romieu | 356bd14 | 2006-12-11 23:47:00 +0100 | [diff] [blame] | 355 |  | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 356 | 	udelay(300); | 
 | 357 | 	vsc_write(adapter, REG_MEM_BIST, 0x0); | 
 | 358 | 	mdelay(10); | 
| Francois Romieu | b7d5839 | 2006-12-11 23:41:36 +0100 | [diff] [blame] | 359 | 	return 0; | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 360 | } | 
 | 361 |  | 
 | 362 | static int mac_intr_handler(struct cmac *mac) | 
 | 363 | { | 
 | 364 | 	return 0; | 
 | 365 | } | 
 | 366 |  | 
 | 367 | static int mac_intr_enable(struct cmac *mac) | 
 | 368 | { | 
 | 369 | 	return 0; | 
 | 370 | } | 
 | 371 |  | 
 | 372 | static int mac_intr_disable(struct cmac *mac) | 
 | 373 | { | 
 | 374 | 	return 0; | 
 | 375 | } | 
 | 376 |  | 
 | 377 | static int mac_intr_clear(struct cmac *mac) | 
 | 378 | { | 
 | 379 | 	return 0; | 
 | 380 | } | 
 | 381 |  | 
 | 382 | /* Expect MAC address to be in network byte order. */ | 
 | 383 | static int mac_set_address(struct cmac* mac, u8 addr[6]) | 
 | 384 | { | 
 | 385 | 	u32 val; | 
 | 386 | 	int port = mac->instance->index; | 
 | 387 |  | 
 | 388 | 	vsc_write(mac->adapter, REG_MAC_LOW_ADDR(port), | 
 | 389 | 		  (addr[3] << 16) | (addr[4] << 8) | addr[5]); | 
 | 390 | 	vsc_write(mac->adapter, REG_MAC_HIGH_ADDR(port), | 
 | 391 | 		  (addr[0] << 16) | (addr[1] << 8) | addr[2]); | 
 | 392 |  | 
 | 393 | 	vsc_read(mac->adapter, REG_ING_FFILT_UM_EN, &val); | 
 | 394 | 	val &= ~0xf0000000; | 
 | 395 | 	vsc_write(mac->adapter, REG_ING_FFILT_UM_EN, val | (port << 28)); | 
 | 396 |  | 
 | 397 | 	vsc_write(mac->adapter, REG_ING_FFILT_MASK0, | 
 | 398 | 		  0xffff0000 | (addr[4] << 8) | addr[5]); | 
 | 399 | 	vsc_write(mac->adapter, REG_ING_FFILT_MASK1, | 
 | 400 | 		  0xffff0000 | (addr[2] << 8) | addr[3]); | 
 | 401 | 	vsc_write(mac->adapter, REG_ING_FFILT_MASK2, | 
 | 402 | 		  0xffff0000 | (addr[0] << 8) | addr[1]); | 
 | 403 | 	return 0; | 
 | 404 | } | 
 | 405 |  | 
 | 406 | static int mac_get_address(struct cmac *mac, u8 addr[6]) | 
 | 407 | { | 
 | 408 | 	u32 addr_lo, addr_hi; | 
 | 409 | 	int port = mac->instance->index; | 
 | 410 |  | 
 | 411 | 	vsc_read(mac->adapter, REG_MAC_LOW_ADDR(port), &addr_lo); | 
 | 412 | 	vsc_read(mac->adapter, REG_MAC_HIGH_ADDR(port), &addr_hi); | 
 | 413 |  | 
 | 414 | 	addr[0] = (u8) (addr_hi >> 16); | 
 | 415 | 	addr[1] = (u8) (addr_hi >> 8); | 
 | 416 | 	addr[2] = (u8) addr_hi; | 
 | 417 | 	addr[3] = (u8) (addr_lo >> 16); | 
 | 418 | 	addr[4] = (u8) (addr_lo >> 8); | 
 | 419 | 	addr[5] = (u8) addr_lo; | 
 | 420 | 	return 0; | 
 | 421 | } | 
 | 422 |  | 
 | 423 | /* This is intended to reset a port, not the whole MAC */ | 
 | 424 | static int mac_reset(struct cmac *mac) | 
 | 425 | { | 
 | 426 | 	int index = mac->instance->index; | 
 | 427 |  | 
 | 428 | 	run_table(mac->adapter, vsc7326_portinit[index], | 
 | 429 | 		  ARRAY_SIZE(vsc7326_portinit[index])); | 
 | 430 |  | 
 | 431 | 	return 0; | 
 | 432 | } | 
 | 433 |  | 
 | 434 | static int mac_set_rx_mode(struct cmac *mac, struct t1_rx_mode *rm) | 
 | 435 | { | 
 | 436 | 	u32 v; | 
 | 437 | 	int port = mac->instance->index; | 
 | 438 |  | 
 | 439 | 	vsc_read(mac->adapter, REG_ING_FFILT_UM_EN, &v); | 
 | 440 | 	v |= 1 << 12; | 
 | 441 |  | 
 | 442 | 	if (t1_rx_mode_promisc(rm)) | 
 | 443 | 		v &= ~(1 << (port + 16)); | 
 | 444 | 	else | 
 | 445 | 		v |= 1 << (port + 16); | 
 | 446 |  | 
 | 447 | 	vsc_write(mac->adapter, REG_ING_FFILT_UM_EN, v); | 
 | 448 | 	return 0; | 
 | 449 | } | 
 | 450 |  | 
 | 451 | static int mac_set_mtu(struct cmac *mac, int mtu) | 
 | 452 | { | 
 | 453 | 	int port = mac->instance->index; | 
 | 454 |  | 
 | 455 | 	if (mtu > MAX_MTU) | 
 | 456 | 		return -EINVAL; | 
 | 457 |  | 
 | 458 | 	/* max_len includes header and FCS */ | 
 | 459 | 	vsc_write(mac->adapter, REG_MAX_LEN(port), mtu + 14 + 4); | 
 | 460 | 	return 0; | 
 | 461 | } | 
 | 462 |  | 
 | 463 | static int mac_set_speed_duplex_fc(struct cmac *mac, int speed, int duplex, | 
 | 464 | 				   int fc) | 
 | 465 | { | 
 | 466 | 	u32 v; | 
 | 467 | 	int enable, port = mac->instance->index; | 
 | 468 |  | 
 | 469 | 	if (speed >= 0 && speed != SPEED_10 && speed != SPEED_100 && | 
 | 470 | 	    speed != SPEED_1000) | 
 | 471 | 		return -1; | 
 | 472 | 	if (duplex > 0 && duplex != DUPLEX_FULL) | 
 | 473 | 		return -1; | 
 | 474 |  | 
 | 475 | 	if (speed >= 0) { | 
 | 476 | 		vsc_read(mac->adapter, REG_MODE_CFG(port), &v); | 
 | 477 | 		enable = v & 3;             /* save tx/rx enables */ | 
 | 478 | 		v &= ~0xf; | 
 | 479 | 		v |= 4;                     /* full duplex */ | 
 | 480 | 		if (speed == SPEED_1000) | 
 | 481 | 			v |= 8;             /* GigE */ | 
 | 482 | 		enable |= v; | 
 | 483 | 		vsc_write(mac->adapter, REG_MODE_CFG(port), v); | 
 | 484 |  | 
 | 485 | 		if (speed == SPEED_1000) | 
 | 486 | 			v = 0x82; | 
 | 487 | 		else if (speed == SPEED_100) | 
 | 488 | 			v = 0x84; | 
 | 489 | 		else	/* SPEED_10 */ | 
 | 490 | 			v = 0x86; | 
 | 491 | 		vsc_write(mac->adapter, REG_DEV_SETUP(port), v | 1); /* reset */ | 
 | 492 | 		vsc_write(mac->adapter, REG_DEV_SETUP(port), v); | 
 | 493 | 		vsc_read(mac->adapter, REG_DBG(port), &v); | 
 | 494 | 		v &= ~0xff00; | 
 | 495 | 		if (speed == SPEED_1000) | 
 | 496 | 			v |= 0x400; | 
 | 497 | 		else if (speed == SPEED_100) | 
 | 498 | 			v |= 0x2000; | 
 | 499 | 		else	/* SPEED_10 */ | 
 | 500 | 			v |= 0xff00; | 
 | 501 | 		vsc_write(mac->adapter, REG_DBG(port), v); | 
 | 502 |  | 
 | 503 | 		vsc_write(mac->adapter, REG_TX_IFG(port), | 
 | 504 | 			  speed == SPEED_1000 ? 5 : 0x11); | 
 | 505 | 		if (duplex == DUPLEX_HALF) | 
 | 506 | 			enable = 0x0;	/* 100 or 10 */ | 
 | 507 | 		else if (speed == SPEED_1000) | 
 | 508 | 			enable = 0xc; | 
 | 509 | 		else	/* SPEED_100 or 10 */ | 
 | 510 | 			enable = 0x4; | 
 | 511 | 		enable |= 0x9 << 10;	/* IFG1 */ | 
 | 512 | 		enable |= 0x6 << 6;	/* IFG2 */ | 
 | 513 | 		enable |= 0x1 << 4;	/* VLAN */ | 
 | 514 | 		enable |= 0x3;		/* RX/TX EN */ | 
 | 515 | 		vsc_write(mac->adapter, REG_MODE_CFG(port), enable); | 
 | 516 |  | 
 | 517 | 	} | 
 | 518 |  | 
 | 519 | 	vsc_read(mac->adapter, REG_PAUSE_CFG(port), &v); | 
 | 520 | 	v &= 0xfff0ffff; | 
 | 521 | 	v |= 0x20000;      /* xon/xoff */ | 
 | 522 | 	if (fc & PAUSE_RX) | 
 | 523 | 		v |= 0x40000; | 
 | 524 | 	if (fc & PAUSE_TX) | 
 | 525 | 		v |= 0x80000; | 
 | 526 | 	if (fc == (PAUSE_RX | PAUSE_TX)) | 
 | 527 | 		v |= 0x10000; | 
 | 528 | 	vsc_write(mac->adapter, REG_PAUSE_CFG(port), v); | 
 | 529 | 	return 0; | 
 | 530 | } | 
 | 531 |  | 
 | 532 | static int mac_enable(struct cmac *mac, int which) | 
 | 533 | { | 
 | 534 | 	u32 val; | 
 | 535 | 	int port = mac->instance->index; | 
 | 536 |  | 
 | 537 | 	/* Write the correct WM value when the port is enabled. */ | 
 | 538 | 	vsc_write(mac->adapter, REG_HIGH_LOW_WM(1,port), WM_ENABLE); | 
 | 539 |  | 
 | 540 | 	vsc_read(mac->adapter, REG_MODE_CFG(port), &val); | 
 | 541 | 	if (which & MAC_DIRECTION_RX) | 
 | 542 | 		val |= 0x2; | 
 | 543 | 	if (which & MAC_DIRECTION_TX) | 
 | 544 | 		val |= 1; | 
 | 545 | 	vsc_write(mac->adapter, REG_MODE_CFG(port), val); | 
 | 546 | 	return 0; | 
 | 547 | } | 
 | 548 |  | 
 | 549 | static int mac_disable(struct cmac *mac, int which) | 
 | 550 | { | 
 | 551 | 	u32 val; | 
 | 552 | 	int i, port = mac->instance->index; | 
 | 553 |  | 
 | 554 | 	/* Reset the port, this also writes the correct WM value */ | 
 | 555 | 	mac_reset(mac); | 
 | 556 |  | 
 | 557 | 	vsc_read(mac->adapter, REG_MODE_CFG(port), &val); | 
 | 558 | 	if (which & MAC_DIRECTION_RX) | 
 | 559 | 		val &= ~0x2; | 
 | 560 | 	if (which & MAC_DIRECTION_TX) | 
 | 561 | 		val &= ~0x1; | 
 | 562 | 	vsc_write(mac->adapter, REG_MODE_CFG(port), val); | 
 | 563 | 	vsc_read(mac->adapter, REG_MODE_CFG(port), &val); | 
 | 564 |  | 
 | 565 | 	/* Clear stats */ | 
 | 566 | 	for (i = 0; i <= 0x3a; ++i) | 
 | 567 | 		vsc_write(mac->adapter, CRA(4, port, i), 0); | 
 | 568 |  | 
 | 569 | 	/* Clear sofware counters */ | 
 | 570 | 	memset(&mac->stats, 0, sizeof(struct cmac_statistics)); | 
 | 571 |  | 
 | 572 | 	return 0; | 
 | 573 | } | 
 | 574 |  | 
 | 575 | static void rmon_update(struct cmac *mac, unsigned int addr, u64 *stat) | 
 | 576 | { | 
 | 577 | 	u32 v, lo; | 
 | 578 |  | 
 | 579 | 	vsc_read(mac->adapter, addr, &v); | 
 | 580 | 	lo = *stat; | 
 | 581 | 	*stat = *stat - lo + v; | 
 | 582 |  | 
 | 583 | 	if (v == 0) | 
 | 584 | 		return; | 
 | 585 |  | 
 | 586 | 	if (v < lo) | 
 | 587 | 		*stat += (1ULL << 32); | 
 | 588 | } | 
 | 589 |  | 
 | 590 | static void port_stats_update(struct cmac *mac) | 
 | 591 | { | 
| Francois Romieu | 8343246 | 2006-12-12 00:13:48 +0100 | [diff] [blame] | 592 | 	struct { | 
 | 593 | 		unsigned int reg; | 
 | 594 | 		unsigned int offset; | 
 | 595 | 	} hw_stats[] = { | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 596 |  | 
| Francois Romieu | 8343246 | 2006-12-12 00:13:48 +0100 | [diff] [blame] | 597 | #define HW_STAT(reg, stat_name) \ | 
 | 598 | 	{ reg, (&((struct cmac_statistics *)NULL)->stat_name) - (u64 *)NULL } | 
 | 599 |  | 
 | 600 | 		/* Rx stats */ | 
 | 601 | 		HW_STAT(RxUnicast, RxUnicastFramesOK), | 
 | 602 | 		HW_STAT(RxMulticast, RxMulticastFramesOK), | 
 | 603 | 		HW_STAT(RxBroadcast, RxBroadcastFramesOK), | 
 | 604 | 		HW_STAT(Crc, RxFCSErrors), | 
 | 605 | 		HW_STAT(RxAlignment, RxAlignErrors), | 
 | 606 | 		HW_STAT(RxOversize, RxFrameTooLongErrors), | 
 | 607 | 		HW_STAT(RxPause, RxPauseFrames), | 
 | 608 | 		HW_STAT(RxJabbers, RxJabberErrors), | 
 | 609 | 		HW_STAT(RxFragments, RxRuntErrors), | 
 | 610 | 		HW_STAT(RxUndersize, RxRuntErrors), | 
 | 611 | 		HW_STAT(RxSymbolCarrier, RxSymbolErrors), | 
 | 612 | 		HW_STAT(RxSize1519ToMax, RxJumboFramesOK), | 
 | 613 |  | 
 | 614 | 		/* Tx stats (skip collision stats as we are full-duplex only) */ | 
 | 615 | 		HW_STAT(TxUnicast, TxUnicastFramesOK), | 
 | 616 | 		HW_STAT(TxMulticast, TxMulticastFramesOK), | 
 | 617 | 		HW_STAT(TxBroadcast, TxBroadcastFramesOK), | 
 | 618 | 		HW_STAT(TxPause, TxPauseFrames), | 
 | 619 | 		HW_STAT(TxUnderrun, TxUnderrun), | 
 | 620 | 		HW_STAT(TxSize1519ToMax, TxJumboFramesOK), | 
 | 621 | 	}, *p = hw_stats; | 
 | 622 | 	unsigned int port = mac->instance->index; | 
 | 623 | 	u64 *stats = (u64 *)&mac->stats; | 
 | 624 | 	unsigned int i; | 
 | 625 |  | 
 | 626 | 	for (i = 0; i < ARRAY_SIZE(hw_stats); i++) | 
 | 627 | 		rmon_update(mac, CRA(0x4, port, p->reg), stats + p->offset); | 
 | 628 |  | 
 | 629 | 	rmon_update(mac, REG_TX_OK_BYTES(port), &mac->stats.TxOctetsOK); | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 630 | 	rmon_update(mac, REG_RX_OK_BYTES(port), &mac->stats.RxOctetsOK); | 
 | 631 | 	rmon_update(mac, REG_RX_BAD_BYTES(port), &mac->stats.RxOctetsBad); | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 632 | } | 
 | 633 |  | 
 | 634 | /* | 
 | 635 |  * This function is called periodically to accumulate the current values of the | 
 | 636 |  * RMON counters into the port statistics.  Since the counters are only 32 bits | 
 | 637 |  * some of them can overflow in less than a minute at GigE speeds, so this | 
 | 638 |  * function should be called every 30 seconds or so. | 
 | 639 |  * | 
 | 640 |  * To cut down on reading costs we update only the octet counters at each tick | 
 | 641 |  * and do a full update at major ticks, which can be every 30 minutes or more. | 
 | 642 |  */ | 
 | 643 | static const struct cmac_statistics *mac_update_statistics(struct cmac *mac, | 
 | 644 | 							   int flag) | 
 | 645 | { | 
 | 646 | 	if (flag == MAC_STATS_UPDATE_FULL || | 
 | 647 | 	    mac->instance->ticks >= MAJOR_UPDATE_TICKS) { | 
 | 648 | 		port_stats_update(mac); | 
 | 649 | 		mac->instance->ticks = 0; | 
 | 650 | 	} else { | 
 | 651 | 		int port = mac->instance->index; | 
 | 652 |  | 
 | 653 | 		rmon_update(mac, REG_RX_OK_BYTES(port), | 
 | 654 | 			    &mac->stats.RxOctetsOK); | 
 | 655 | 		rmon_update(mac, REG_RX_BAD_BYTES(port), | 
 | 656 | 			    &mac->stats.RxOctetsBad); | 
 | 657 | 		rmon_update(mac, REG_TX_OK_BYTES(port), | 
 | 658 | 			    &mac->stats.TxOctetsOK); | 
 | 659 | 		mac->instance->ticks++; | 
 | 660 | 	} | 
 | 661 | 	return &mac->stats; | 
 | 662 | } | 
 | 663 |  | 
 | 664 | static void mac_destroy(struct cmac *mac) | 
 | 665 | { | 
 | 666 | 	kfree(mac); | 
 | 667 | } | 
 | 668 |  | 
 | 669 | static struct cmac_ops vsc7326_ops = { | 
 | 670 | 	.destroy                  = mac_destroy, | 
 | 671 | 	.reset                    = mac_reset, | 
 | 672 | 	.interrupt_handler        = mac_intr_handler, | 
 | 673 | 	.interrupt_enable         = mac_intr_enable, | 
 | 674 | 	.interrupt_disable        = mac_intr_disable, | 
 | 675 | 	.interrupt_clear          = mac_intr_clear, | 
 | 676 | 	.enable                   = mac_enable, | 
 | 677 | 	.disable                  = mac_disable, | 
 | 678 | 	.set_mtu                  = mac_set_mtu, | 
 | 679 | 	.set_rx_mode              = mac_set_rx_mode, | 
 | 680 | 	.set_speed_duplex_fc      = mac_set_speed_duplex_fc, | 
 | 681 | 	.statistics_update        = mac_update_statistics, | 
 | 682 | 	.macaddress_get           = mac_get_address, | 
 | 683 | 	.macaddress_set           = mac_set_address, | 
 | 684 | }; | 
 | 685 |  | 
 | 686 | static struct cmac *vsc7326_mac_create(adapter_t *adapter, int index) | 
 | 687 | { | 
 | 688 | 	struct cmac *mac; | 
 | 689 | 	u32 val; | 
 | 690 | 	int i; | 
 | 691 |  | 
 | 692 | 	mac = kzalloc(sizeof(*mac) + sizeof(cmac_instance), GFP_KERNEL); | 
| Francois Romieu | c697f83 | 2006-12-05 22:38:00 +0100 | [diff] [blame] | 693 | 	if (!mac) | 
 | 694 | 		return NULL; | 
| Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 695 |  | 
 | 696 | 	mac->ops = &vsc7326_ops; | 
 | 697 | 	mac->instance = (cmac_instance *)(mac + 1); | 
 | 698 | 	mac->adapter  = adapter; | 
 | 699 |  | 
 | 700 | 	mac->instance->index = index; | 
 | 701 | 	mac->instance->ticks = 0; | 
 | 702 |  | 
 | 703 | 	i = 0; | 
 | 704 | 	do { | 
 | 705 | 		u32 vhi, vlo; | 
 | 706 |  | 
 | 707 | 		vhi = vlo = 0; | 
 | 708 | 		t1_tpi_read(adapter, (REG_LOCAL_STATUS << 2) + 4, &vlo); | 
 | 709 | 		udelay(1); | 
 | 710 | 		t1_tpi_read(adapter, REG_LOCAL_STATUS << 2, &vhi); | 
 | 711 | 		udelay(5); | 
 | 712 | 		val = (vhi << 16) | vlo; | 
 | 713 | 	} while ((++i < 10000) && (val == 0xffffffff)); | 
 | 714 |  | 
 | 715 | 	return mac; | 
 | 716 | } | 
 | 717 |  | 
 | 718 | static int vsc7326_mac_reset(adapter_t *adapter) | 
 | 719 | { | 
 | 720 | 	vsc7326_full_reset(adapter); | 
 | 721 | 	(void) run_bist_all(adapter); | 
 | 722 | 	run_table(adapter, vsc7326_reset, ARRAY_SIZE(vsc7326_reset)); | 
 | 723 | 	return 0; | 
 | 724 | } | 
 | 725 |  | 
 | 726 | struct gmac t1_vsc7326_ops = { | 
 | 727 | 	.stats_update_period = STATS_TICK_SECS, | 
 | 728 | 	.create              = vsc7326_mac_create, | 
 | 729 | 	.reset               = vsc7326_mac_reset, | 
 | 730 | }; |