espi.c 11.2 KB
Newer Older
1 2 3
/*****************************************************************************
 *                                                                           *
 * File: espi.c                                                              *
S
Scott Bardone 已提交
4 5
 * $Revision: 1.14 $                                                         *
 * $Date: 2005/05/14 00:59:32 $                                              *
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
 * Description:                                                              *
 *  Ethernet SPI functionality.                                              *
 *  part of the Chelsio 10Gb Ethernet Driver.                                *
 *                                                                           *
 * This program is free software; you can redistribute it and/or modify      *
 * it under the terms of the GNU General Public License, version 2, as       *
 * published by the Free Software Foundation.                                *
 *                                                                           *
 * You should have received a copy of the GNU General Public License along   *
 * with this program; if not, write to the Free Software Foundation, Inc.,   *
 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.                 *
 *                                                                           *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED    *
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF      *
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.                     *
 *                                                                           *
 * http://www.chelsio.com                                                    *
 *                                                                           *
 * Copyright (c) 2003 - 2005 Chelsio Communications, Inc.                    *
 * All rights reserved.                                                      *
 *                                                                           *
 * Maintainers: maintainers@chelsio.com                                      *
 *                                                                           *
 * Authors: Dimitrios Michailidis   <dm@chelsio.com>                         *
 *          Tina Yang               <tainay@chelsio.com>                     *
 *          Felix Marti             <felix@chelsio.com>                      *
 *          Scott Bardone           <sbardone@chelsio.com>                   *
 *          Kurt Ottaway            <kottaway@chelsio.com>                   *
 *          Frank DiMambro          <frank@chelsio.com>                      *
 *                                                                           *
 * History:                                                                  *
 *                                                                           *
 ****************************************************************************/

#include "common.h"
#include "regs.h"
#include "espi.h"

struct peespi {
	adapter_t *adapter;
	struct espi_intr_counts intr_cnt;
	u32 misc_ctrl;
	spinlock_t lock;
};

#define ESPI_INTR_MASK (F_DIP4ERR | F_RXDROP | F_TXDROP | F_RXOVERFLOW | \
			F_RAMPARITYERR | F_DIP2PARITYERR)
#define MON_MASK  (V_MONITORED_PORT_NUM(3) | F_MONITORED_DIRECTION \
		   | F_MONITORED_INTERFACE)

#define TRICN_CNFG 14
#define TRICN_CMD_READ  0x11
#define TRICN_CMD_WRITE 0x21
#define TRICN_CMD_ATTEMPTS 10

static int tricn_write(adapter_t *adapter, int bundle_addr, int module_addr,
		       int ch_addr, int reg_offset, u32 wr_data)
{
	int busy, attempts = TRICN_CMD_ATTEMPTS;

S
Scott Bardone 已提交
66 67 68 69 70 71 72
	writel(V_WRITE_DATA(wr_data) |
	       V_REGISTER_OFFSET(reg_offset) |
	       V_CHANNEL_ADDR(ch_addr) | V_MODULE_ADDR(module_addr) |
	       V_BUNDLE_ADDR(bundle_addr) |
	       V_SPI4_COMMAND(TRICN_CMD_WRITE),
	       adapter->regs + A_ESPI_CMD_ADDR);
	writel(0, adapter->regs + A_ESPI_GOSTAT);
73 74

	do {
S
Scott Bardone 已提交
75
		busy = readl(adapter->regs + A_ESPI_GOSTAT) & F_ESPI_CMD_BUSY;
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
	} while (busy && --attempts);

	if (busy)
		CH_ERR("%s: TRICN write timed out\n", adapter->name);

	return busy;
}

/* 1. Deassert rx_reset_core. */
/* 2. Program TRICN_CNFG registers. */
/* 3. Deassert rx_reset_link */
static int tricn_init(adapter_t *adapter)
{
	int     i               = 0;
	int     sme             = 1;
	int     stat            = 0;
	int     timeout         = 0;
	int     is_ready        = 0;
	int     dynamic_deskew  = 0;

	if (dynamic_deskew)
		sme = 0;


	/* 1 */
	timeout=1000;
	do {
S
Scott Bardone 已提交
103
		stat = readl(adapter->regs + A_ESPI_RX_RESET);
104 105 106 107
		is_ready = (stat & 0x4);
		timeout--;
		udelay(5);
	} while (!is_ready || (timeout==0));
S
Scott Bardone 已提交
108
	writel(0x2, adapter->regs + A_ESPI_RX_RESET);
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
	if (timeout==0)
	{
		CH_ERR("ESPI : ERROR : Timeout tricn_init() \n");
		t1_fatal_err(adapter);
	}

	/* 2 */
	if (sme) {
		tricn_write(adapter, 0, 0, 0, TRICN_CNFG, 0x81);
		tricn_write(adapter, 0, 1, 0, TRICN_CNFG, 0x81);
		tricn_write(adapter, 0, 2, 0, TRICN_CNFG, 0x81);
	}
	for (i=1; i<= 8; i++) tricn_write(adapter, 0, 0, i, TRICN_CNFG, 0xf1);
	for (i=1; i<= 2; i++) tricn_write(adapter, 0, 1, i, TRICN_CNFG, 0xf1);
	for (i=1; i<= 3; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xe1);
	for (i=4; i<= 4; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1);
	for (i=5; i<= 5; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xe1);
	for (i=6; i<= 6; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1);
	for (i=7; i<= 7; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0x80);
	for (i=8; i<= 8; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1);

	/* 3 */
S
Scott Bardone 已提交
131
	writel(0x3, adapter->regs + A_ESPI_RX_RESET);
132 133 134 135 136 137

	return 0;
}

void t1_espi_intr_enable(struct peespi *espi)
{
S
Scott Bardone 已提交
138
	u32 enable, pl_intr = readl(espi->adapter->regs + A_PL_ENABLE);
139 140 141 142 143 144 145 146 147

	/*
	 * Cannot enable ESPI interrupts on T1B because HW asserts the
	 * interrupt incorrectly, namely the driver gets ESPI interrupts
	 * but no data is actually dropped (can verify this reading the ESPI
	 * drop registers).  Also, once the ESPI interrupt is asserted it
	 * cannot be cleared (HW bug).
	 */
	enable = t1_is_T1B(espi->adapter) ? 0 : ESPI_INTR_MASK;
S
Scott Bardone 已提交
148 149
	writel(enable, espi->adapter->regs + A_ESPI_INTR_ENABLE);
	writel(pl_intr | F_PL_INTR_ESPI, espi->adapter->regs + A_PL_ENABLE);
150 151 152 153
}

void t1_espi_intr_clear(struct peespi *espi)
{
S
Scott Bardone 已提交
154 155
	writel(0xffffffff, espi->adapter->regs + A_ESPI_INTR_STATUS);
	writel(F_PL_INTR_ESPI, espi->adapter->regs + A_PL_CAUSE);
156 157 158 159
}

void t1_espi_intr_disable(struct peespi *espi)
{
S
Scott Bardone 已提交
160
	u32 pl_intr = readl(espi->adapter->regs + A_PL_ENABLE);
161

S
Scott Bardone 已提交
162 163
	writel(0, espi->adapter->regs + A_ESPI_INTR_ENABLE);
	writel(pl_intr & ~F_PL_INTR_ESPI, espi->adapter->regs + A_PL_ENABLE);
164 165 166 167 168
}

int t1_espi_intr_handler(struct peespi *espi)
{
	u32 cnt;
S
Scott Bardone 已提交
169
	u32 status = readl(espi->adapter->regs + A_ESPI_INTR_STATUS);
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

	if (status & F_DIP4ERR)
		espi->intr_cnt.DIP4_err++;
	if (status & F_RXDROP)
		espi->intr_cnt.rx_drops++;
	if (status & F_TXDROP)
		espi->intr_cnt.tx_drops++;
	if (status & F_RXOVERFLOW)
		espi->intr_cnt.rx_ovflw++;
	if (status & F_RAMPARITYERR)
		espi->intr_cnt.parity_err++;
	if (status & F_DIP2PARITYERR) {
		espi->intr_cnt.DIP2_parity_err++;

		/*
		 * Must read the error count to clear the interrupt
		 * that it causes.
		 */
S
Scott Bardone 已提交
188
		cnt = readl(espi->adapter->regs + A_ESPI_DIP2_ERR_COUNT);
189 190 191 192 193 194 195 196
	}

	/*
	 * For T1B we need to write 1 to clear ESPI interrupts.  For T2+ we
	 * write the status as is.
	 */
	if (status && t1_is_T1B(espi->adapter))
		status = 1;
S
Scott Bardone 已提交
197
	writel(status, espi->adapter->regs + A_ESPI_INTR_STATUS);
198 199 200
	return 0;
}

S
Scott Bardone 已提交
201
const struct espi_intr_counts *t1_espi_get_intr_counts(struct peespi *espi)
202
{
S
Scott Bardone 已提交
203
    return &espi->intr_cnt;
204 205
}

S
Scott Bardone 已提交
206
static void espi_setup_for_pm3393(adapter_t *adapter)
207 208 209
{
	u32 wmark = t1_is_T1B(adapter) ? 0x4000 : 0x3200;

S
Scott Bardone 已提交
210 211 212 213 214 215 216 217 218
	writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN0);
	writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN1);
	writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN2);
	writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN3);
	writel(0x100, adapter->regs + A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK);
	writel(wmark, adapter->regs + A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK);
	writel(3, adapter->regs + A_ESPI_CALENDAR_LENGTH);
	writel(0x08000008, adapter->regs + A_ESPI_TRAIN);
	writel(V_RX_NPORTS(1) | V_TX_NPORTS(1), adapter->regs + A_PORT_CONFIG);
219 220 221 222 223 224 225 226
}

/* T2 Init part --  */
/* 1. Set T_ESPI_MISCCTRL_ADDR */
/* 2. Init ESPI registers. */
/* 3. Init TriCN Hard Macro */
int t1_espi_init(struct peespi *espi, int mac_type, int nports)
{
S
Scott Bardone 已提交
227 228
	u32 cnt;

229 230 231 232 233
	u32 status_enable_extra = 0;
	adapter_t *adapter = espi->adapter;
	u32 status, burstval = 0x800100;

	/* Disable ESPI training.  MACs that can handle it enable it below. */
S
Scott Bardone 已提交
234
	writel(0, adapter->regs + A_ESPI_TRAIN);
235 236

	if (is_T2(adapter)) {
S
Scott Bardone 已提交
237 238 239
		writel(V_OUT_OF_SYNC_COUNT(4) |
		       V_DIP2_PARITY_ERR_THRES(3) |
		       V_DIP4_THRES(1), adapter->regs + A_ESPI_MISC_CONTROL);
240 241 242 243 244
		if (nports == 4) {
			/* T204: maxburst1 = 0x40, maxburst2 = 0x20 */
			burstval = 0x200040;
		}
	}
S
Scott Bardone 已提交
245
	writel(burstval, adapter->regs + A_ESPI_MAXBURST1_MAXBURST2);
246

S
Scott Bardone 已提交
247 248
	switch (mac_type) {
	case CHBT_MAC_PM3393:
249
		espi_setup_for_pm3393(adapter);
S
Scott Bardone 已提交
250 251
		break;
	default:
252
		return -1;
S
Scott Bardone 已提交
253
	}
254 255 256 257 258

	/*
	 * Make sure any pending interrupts from the SPI are
	 * Cleared before enabling the interrupt.
	 */
S
Scott Bardone 已提交
259 260
	writel(ESPI_INTR_MASK, espi->adapter->regs + A_ESPI_INTR_ENABLE);
	status = readl(espi->adapter->regs + A_ESPI_INTR_STATUS);
261
	if (status & F_DIP2PARITYERR) {
S
Scott Bardone 已提交
262
		cnt = readl(espi->adapter->regs + A_ESPI_DIP2_ERR_COUNT);
263 264 265 266 267 268 269 270
	}

	/*
	 * For T1B we need to write 1 to clear ESPI interrupts.  For T2+ we
	 * write the status as is.
	 */
	if (status && t1_is_T1B(espi->adapter))
		status = 1;
S
Scott Bardone 已提交
271
	writel(status, espi->adapter->regs + A_ESPI_INTR_STATUS);
272

S
Scott Bardone 已提交
273 274
	writel(status_enable_extra | F_RXSTATUSENABLE,
	       adapter->regs + A_ESPI_FIFO_STATUS_ENABLE);
275 276 277 278 279 280 281

	if (is_T2(adapter)) {
		tricn_init(adapter);
		/*
		 * Always position the control at the 1st port egress IN
		 * (sop,eop) counter to reduce PIOs for T/N210 workaround.
		 */
S
Scott Bardone 已提交
282
		espi->misc_ctrl = (readl(adapter->regs + A_ESPI_MISC_CONTROL)
283 284
				   & ~MON_MASK) | (F_MONITORED_DIRECTION
				   | F_MONITORED_INTERFACE);
S
Scott Bardone 已提交
285
		writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
		spin_lock_init(&espi->lock);
	}

	return 0;
}

void t1_espi_destroy(struct peespi *espi)
{
	kfree(espi);
}

struct peespi *t1_espi_create(adapter_t *adapter)
{
	struct peespi *espi = kmalloc(sizeof(*espi), GFP_KERNEL);

	memset(espi, 0, sizeof(*espi));

	if (espi)
		espi->adapter = adapter;
	return espi;
}

void t1_espi_set_misc_ctrl(adapter_t *adapter, u32 val)
{
	struct peespi *espi = adapter->espi;

	if (!is_T2(adapter))
		return;
	spin_lock(&espi->lock);
	espi->misc_ctrl = (val & ~MON_MASK) |
			  (espi->misc_ctrl & MON_MASK);
S
Scott Bardone 已提交
317
	writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
318 319 320 321 322 323 324
	spin_unlock(&espi->lock);
}

u32 t1_espi_get_mon(adapter_t *adapter, u32 addr, u8 wait)
{
	u32 sel;

S
Scott Bardone 已提交
325 326
	struct peespi *espi = adapter->espi;

327 328 329 330 331 332 333 334 335 336
	if (!is_T2(adapter))
		return 0;
	sel = V_MONITORED_PORT_NUM((addr & 0x3c) >> 2);
	if (!wait) {
		if (!spin_trylock(&espi->lock))
			return 0;
	}
	else
		spin_lock(&espi->lock);
	if ((sel != (espi->misc_ctrl & MON_MASK))) {
S
Scott Bardone 已提交
337 338 339 340
		writel(((espi->misc_ctrl & ~MON_MASK) | sel),
		       adapter->regs + A_ESPI_MISC_CONTROL);
		sel = readl(adapter->regs + A_ESPI_SCH_TOKEN3);
		writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
341 342
	}
	else
S
Scott Bardone 已提交
343
		sel = readl(adapter->regs + A_ESPI_SCH_TOKEN3);
344 345 346
	spin_unlock(&espi->lock);
	return sel;
}