ahci.c 19 KB
Newer Older
Jin Zhengxiong's avatar
Jin Zhengxiong committed
1
/*
2
 * Copyright (C) Freescale Semiconductor, Inc. 2006.
Jin Zhengxiong's avatar
Jin Zhengxiong committed
3 4 5 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
 * Author: Jason Jin<Jason.jin@freescale.com>
 *         Zhang Wei<wei.zhang@freescale.com>
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * 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
 *
 * with the reference on libata and ahci drvier in kernel
 *
 */
#include <common.h>

#include <command.h>
#include <pci.h>
#include <asm/processor.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <malloc.h>
#include <scsi.h>
#include <ata.h>
#include <linux/ctype.h>
#include <ahci.h>

struct ahci_probe_ent *probe_ent = NULL;
hd_driveid_t *ataid[AHCI_MAX_PORTS];

43 44
#define writel_with_flush(a,b)	do { writel(a,b); readl(b); } while (0)

45 46 47 48 49 50 51 52
/*
 * Some controllers limit number of blocks they can read at once. Contemporary
 * SSD devices work much faster if the read size is aligned to a power of 2.
 * Let's set default to 128 and allowing to be overwritten if needed.
 */
#ifndef MAX_SATA_BLOCKS_READ
#define MAX_SATA_BLOCKS_READ	0x80
#endif
Jin Zhengxiong's avatar
Jin Zhengxiong committed
53 54 55 56 57 58 59 60 61 62 63 64

static inline u32 ahci_port_base(u32 base, u32 port)
{
	return base + 0x100 + (port * 0x80);
}


static void ahci_setup_port(struct ahci_ioports *port, unsigned long base,
			    unsigned int port_idx)
{
	base = ahci_port_base(base, port_idx);

65 66
	port->cmd_addr = base;
	port->scr_addr = base + PORT_SCR;
Jin Zhengxiong's avatar
Jin Zhengxiong committed
67 68 69 70
}


#define msleep(a) udelay(a * 1000)
71 72 73 74

static int waiting_for_cmd_completed(volatile u8 *offset,
				     int timeout_msec,
				     u32 sign)
Jin Zhengxiong's avatar
Jin Zhengxiong committed
75 76 77
{
	int i;
	u32 status;
78 79

	for (i = 0; ((status = readl(offset)) & sign) && i < timeout_msec; i++)
Jin Zhengxiong's avatar
Jin Zhengxiong committed
80 81
		msleep(1);

82
	return (i < timeout_msec) ? 0 : -1;
Jin Zhengxiong's avatar
Jin Zhengxiong committed
83 84 85 86 87
}


static int ahci_host_init(struct ahci_probe_ent *probe_ent)
{
88
#ifndef CONFIG_SCSI_AHCI_PLAT
Jin Zhengxiong's avatar
Jin Zhengxiong committed
89
	pci_dev_t pdev = probe_ent->dev;
90 91 92
	u16 tmp16;
	unsigned short vendor;
#endif
Jin Zhengxiong's avatar
Jin Zhengxiong committed
93 94 95
	volatile u8 *mmio = (volatile u8 *)probe_ent->mmio_base;
	u32 tmp, cap_save;
	int i, j;
96
	volatile u8 *port_mmio;
Jin Zhengxiong's avatar
Jin Zhengxiong committed
97

98 99
	debug("ahci_host_init: start\n");

Jin Zhengxiong's avatar
Jin Zhengxiong committed
100
	cap_save = readl(mmio + HOST_CAP);
101
	cap_save &= ((1 << 28) | (1 << 17));
Jin Zhengxiong's avatar
Jin Zhengxiong committed
102 103 104 105 106 107 108 109 110 111
	cap_save |= (1 << 27);

	/* global controller reset */
	tmp = readl(mmio + HOST_CTL);
	if ((tmp & HOST_RESET) == 0)
		writel_with_flush(tmp | HOST_RESET, mmio + HOST_CTL);

	/* reset must complete within 1 second, or
	 * the hardware should be considered fried.
	 */
112 113 114 115 116 117 118 119 120
	i = 1000;
	do {
		udelay(1000);
		tmp = readl(mmio + HOST_CTL);
		if (!i--) {
			debug("controller reset failed (0x%x)\n", tmp);
			return -1;
		}
	} while (tmp & HOST_RESET);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
121 122 123 124 125

	writel_with_flush(HOST_AHCI_EN, mmio + HOST_CTL);
	writel(cap_save, mmio + HOST_CAP);
	writel_with_flush(0xf, mmio + HOST_PORTS_IMPL);

126
#ifndef CONFIG_SCSI_AHCI_PLAT
Jin Zhengxiong's avatar
Jin Zhengxiong committed
127 128 129 130 131 132 133 134
	pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor);

	if (vendor == PCI_VENDOR_ID_INTEL) {
		u16 tmp16;
		pci_read_config_word(pdev, 0x92, &tmp16);
		tmp16 |= 0xf;
		pci_write_config_word(pdev, 0x92, tmp16);
	}
135
#endif
Jin Zhengxiong's avatar
Jin Zhengxiong committed
136 137 138 139 140
	probe_ent->cap = readl(mmio + HOST_CAP);
	probe_ent->port_map = readl(mmio + HOST_PORTS_IMPL);
	probe_ent->n_ports = (probe_ent->cap & 0x1f) + 1;

	debug("cap 0x%x  port_map 0x%x  n_ports %d\n",
141
	      probe_ent->cap, probe_ent->port_map, probe_ent->n_ports);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
142

143 144 145
	if (probe_ent->n_ports > CONFIG_SYS_SCSI_MAX_SCSI_ID)
		probe_ent->n_ports = CONFIG_SYS_SCSI_MAX_SCSI_ID;

Jin Zhengxiong's avatar
Jin Zhengxiong committed
146
	for (i = 0; i < probe_ent->n_ports; i++) {
147 148 149
		probe_ent->port[i].port_mmio = ahci_port_base((u32) mmio, i);
		port_mmio = (u8 *) probe_ent->port[i].port_mmio;
		ahci_setup_port(&probe_ent->port[i], (unsigned long)mmio, i);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
150 151 152 153 154

		/* make sure port is not active */
		tmp = readl(port_mmio + PORT_CMD);
		if (tmp & (PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
			   PORT_CMD_FIS_RX | PORT_CMD_START)) {
Stefan Reinauer's avatar
Stefan Reinauer committed
155
			debug("Port %d is active. Deactivating.\n", i);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
156 157 158 159 160 161 162 163 164 165
			tmp &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
				 PORT_CMD_FIS_RX | PORT_CMD_START);
			writel_with_flush(tmp, port_mmio + PORT_CMD);

			/* spec says 500 msecs for each bit, so
			 * this is slightly incorrect.
			 */
			msleep(500);
		}

Stefan Reinauer's avatar
Stefan Reinauer committed
166
		debug("Spinning up port %d... ", i);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
167 168 169
		writel(PORT_CMD_SPIN_UP, port_mmio + PORT_CMD);

		j = 0;
170
		while (j < 1000) {
Jin Zhengxiong's avatar
Jin Zhengxiong committed
171 172 173
			tmp = readl(port_mmio + PORT_SCR_STAT);
			if ((tmp & 0xf) == 0x3)
				break;
174
			udelay(1000);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
175 176
			j++;
		}
177 178 179 180
		if (j == 1000)
			debug("timeout.\n");
		else
			debug("ok.\n");
Jin Zhengxiong's avatar
Jin Zhengxiong committed
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196

		tmp = readl(port_mmio + PORT_SCR_ERR);
		debug("PORT_SCR_ERR 0x%x\n", tmp);
		writel(tmp, port_mmio + PORT_SCR_ERR);

		/* ack any pending irq events for this port */
		tmp = readl(port_mmio + PORT_IRQ_STAT);
		debug("PORT_IRQ_STAT 0x%x\n", tmp);
		if (tmp)
			writel(tmp, port_mmio + PORT_IRQ_STAT);

		writel(1 << i, mmio + HOST_IRQ_STAT);

		/* set irq mask (enables interrupts) */
		writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK);

197
		/*register linkup ports */
Jin Zhengxiong's avatar
Jin Zhengxiong committed
198
		tmp = readl(port_mmio + PORT_SCR_STAT);
199 200 201
		debug("Port %d status: 0x%x\n", i, tmp);
		if ((tmp & 0xf) == 0x03)
			probe_ent->link_port_map |= (0x01 << i);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
202 203 204 205 206 207 208
	}

	tmp = readl(mmio + HOST_CTL);
	debug("HOST_CTL 0x%x\n", tmp);
	writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL);
	tmp = readl(mmio + HOST_CTL);
	debug("HOST_CTL 0x%x\n", tmp);
209
#ifndef CONFIG_SCSI_AHCI_PLAT
Jin Zhengxiong's avatar
Jin Zhengxiong committed
210 211 212
	pci_read_config_word(pdev, PCI_COMMAND, &tmp16);
	tmp |= PCI_COMMAND_MASTER;
	pci_write_config_word(pdev, PCI_COMMAND, tmp16);
213
#endif
Jin Zhengxiong's avatar
Jin Zhengxiong committed
214 215 216 217 218 219
	return 0;
}


static void ahci_print_info(struct ahci_probe_ent *probe_ent)
{
220
#ifndef CONFIG_SCSI_AHCI_PLAT
Jin Zhengxiong's avatar
Jin Zhengxiong committed
221
	pci_dev_t pdev = probe_ent->dev;
222 223
	u16 cc;
#endif
224
	volatile u8 *mmio = (volatile u8 *)probe_ent->mmio_base;
Jin Zhengxiong's avatar
Jin Zhengxiong committed
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
	u32 vers, cap, impl, speed;
	const char *speed_s;
	const char *scc_s;

	vers = readl(mmio + HOST_VERSION);
	cap = probe_ent->cap;
	impl = probe_ent->port_map;

	speed = (cap >> 20) & 0xf;
	if (speed == 1)
		speed_s = "1.5";
	else if (speed == 2)
		speed_s = "3";
	else
		speed_s = "?";

241 242 243
#ifdef CONFIG_SCSI_AHCI_PLAT
	scc_s = "SATA";
#else
Jin Zhengxiong's avatar
Jin Zhengxiong committed
244 245 246 247 248 249 250 251 252
	pci_read_config_word(pdev, 0x0a, &cc);
	if (cc == 0x0101)
		scc_s = "IDE";
	else if (cc == 0x0106)
		scc_s = "SATA";
	else if (cc == 0x0104)
		scc_s = "RAID";
	else
		scc_s = "unknown";
253
#endif
254 255 256 257 258 259 260
	printf("AHCI %02x%02x.%02x%02x "
	       "%u slots %u ports %s Gbps 0x%x impl %s mode\n",
	       (vers >> 24) & 0xff,
	       (vers >> 16) & 0xff,
	       (vers >> 8) & 0xff,
	       vers & 0xff,
	       ((cap >> 8) & 0x1f) + 1, (cap & 0x1f) + 1, speed_s, impl, scc_s);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
261 262

	printf("flags: "
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
	       "%s%s%s%s%s%s"
	       "%s%s%s%s%s%s%s\n",
	       cap & (1 << 31) ? "64bit " : "",
	       cap & (1 << 30) ? "ncq " : "",
	       cap & (1 << 28) ? "ilck " : "",
	       cap & (1 << 27) ? "stag " : "",
	       cap & (1 << 26) ? "pm " : "",
	       cap & (1 << 25) ? "led " : "",
	       cap & (1 << 24) ? "clo " : "",
	       cap & (1 << 19) ? "nz " : "",
	       cap & (1 << 18) ? "only " : "",
	       cap & (1 << 17) ? "pmp " : "",
	       cap & (1 << 15) ? "pio " : "",
	       cap & (1 << 14) ? "slum " : "",
	       cap & (1 << 13) ? "part " : "");
Jin Zhengxiong's avatar
Jin Zhengxiong committed
278 279
}

280
#ifndef CONFIG_SCSI_AHCI_PLAT
281
static int ahci_init_one(pci_dev_t pdev)
Jin Zhengxiong's avatar
Jin Zhengxiong committed
282
{
283
	u16 vendor;
Jin Zhengxiong's avatar
Jin Zhengxiong committed
284 285
	int rc;

286
	memset((void *)ataid, 0, sizeof(hd_driveid_t *) * AHCI_MAX_PORTS);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
287

288 289
	probe_ent = malloc(sizeof(struct ahci_probe_ent));
	memset(probe_ent, 0, sizeof(struct ahci_probe_ent));
Jin Zhengxiong's avatar
Jin Zhengxiong committed
290 291
	probe_ent->dev = pdev;

292 293 294 295 296 297 298
	probe_ent->host_flags = ATA_FLAG_SATA
				| ATA_FLAG_NO_LEGACY
				| ATA_FLAG_MMIO
				| ATA_FLAG_PIO_DMA
				| ATA_FLAG_NO_ATAPI;
	probe_ent->pio_mask = 0x1f;
	probe_ent->udma_mask = 0x7f;	/*Fixme,assume to support UDMA6 */
Jin Zhengxiong's avatar
Jin Zhengxiong committed
299

300 301
	pci_read_config_dword(pdev, PCI_BASE_ADDRESS_5, &probe_ent->mmio_base);
	debug("ahci mmio_base=0x%08x\n", probe_ent->mmio_base);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
302 303 304 305 306 307

	/* Take from kernel:
	 * JMicron-specific fixup:
	 * make sure we're in AHCI mode
	 */
	pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor);
308
	if (vendor == 0x197b)
Jin Zhengxiong's avatar
Jin Zhengxiong committed
309 310 311 312 313 314 315 316 317 318 319
		pci_write_config_byte(pdev, 0x41, 0xa1);

	/* initialize adapter */
	rc = ahci_host_init(probe_ent);
	if (rc)
		goto err_out;

	ahci_print_info(probe_ent);

	return 0;

320
      err_out:
Jin Zhengxiong's avatar
Jin Zhengxiong committed
321 322
	return rc;
}
323
#endif
Jin Zhengxiong's avatar
Jin Zhengxiong committed
324 325

#define MAX_DATA_BYTE_COUNT  (4*1024*1024)
326

Jin Zhengxiong's avatar
Jin Zhengxiong committed
327 328 329 330 331 332 333 334
static int ahci_fill_sg(u8 port, unsigned char *buf, int buf_len)
{
	struct ahci_ioports *pp = &(probe_ent->port[port]);
	struct ahci_sg *ahci_sg = pp->cmd_tbl_sg;
	u32 sg_count;
	int i;

	sg_count = ((buf_len - 1) / MAX_DATA_BYTE_COUNT) + 1;
335
	if (sg_count > AHCI_MAX_SG) {
Jin Zhengxiong's avatar
Jin Zhengxiong committed
336 337 338 339
		printf("Error:Too much sg!\n");
		return -1;
	}

340 341 342
	for (i = 0; i < sg_count; i++) {
		ahci_sg->addr =
		    cpu_to_le32((u32) buf + i * MAX_DATA_BYTE_COUNT);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
343
		ahci_sg->addr_hi = 0;
344 345 346 347
		ahci_sg->flags_size = cpu_to_le32(0x3fffff &
					  (buf_len < MAX_DATA_BYTE_COUNT
					   ? (buf_len - 1)
					   : (MAX_DATA_BYTE_COUNT - 1)));
Jin Zhengxiong's avatar
Jin Zhengxiong committed
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
		ahci_sg++;
		buf_len -= MAX_DATA_BYTE_COUNT;
	}

	return sg_count;
}


static void ahci_fill_cmd_slot(struct ahci_ioports *pp, u32 opts)
{
	pp->cmd_slot->opts = cpu_to_le32(opts);
	pp->cmd_slot->status = 0;
	pp->cmd_slot->tbl_addr = cpu_to_le32(pp->cmd_tbl & 0xffffffff);
	pp->cmd_slot->tbl_addr_hi = 0;
}


static void ahci_set_feature(u8 port)
{
	struct ahci_ioports *pp = &(probe_ent->port[port]);
368 369
	volatile u8 *port_mmio = (volatile u8 *)pp->port_mmio;
	u32 cmd_fis_len = 5;	/* five dwords */
Jin Zhengxiong's avatar
Jin Zhengxiong committed
370 371
	u8 fis[20];

372 373
	/*set feature */
	memset(fis, 0, 20);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
374 375 376 377 378 379
	fis[0] = 0x27;
	fis[1] = 1 << 7;
	fis[2] = ATA_CMD_SETF;
	fis[3] = SETFEATURES_XFER;
	fis[12] = __ilog2(probe_ent->udma_mask + 1) + 0x40 - 0x01;

380
	memcpy((unsigned char *)pp->cmd_tbl, fis, 20);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
381 382 383 384
	ahci_fill_cmd_slot(pp, cmd_fis_len);
	writel(1, port_mmio + PORT_CMD_ISSUE);
	readl(port_mmio + PORT_CMD_ISSUE);

385
	if (waiting_for_cmd_completed(port_mmio + PORT_CMD_ISSUE, 150, 0x1)) {
Jin Zhengxiong's avatar
Jin Zhengxiong committed
386 387 388 389 390 391 392 393
		printf("set feature error!\n");
	}
}


static int ahci_port_start(u8 port)
{
	struct ahci_ioports *pp = &(probe_ent->port[port]);
394
	volatile u8 *port_mmio = (volatile u8 *)pp->port_mmio;
Jin Zhengxiong's avatar
Jin Zhengxiong committed
395 396 397
	u32 port_status;
	u32 mem;

398
	debug("Enter start port: %d\n", port);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
399
	port_status = readl(port_mmio + PORT_SCR_STAT);
400 401
	debug("Port %d status: %x\n", port, port_status);
	if ((port_status & 0xf) != 0x03) {
Jin Zhengxiong's avatar
Jin Zhengxiong committed
402 403 404 405
		printf("No Link on this port!\n");
		return -1;
	}

406
	mem = (u32) malloc(AHCI_PORT_PRIV_DMA_SZ + 2048);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
407 408 409 410 411 412
	if (!mem) {
		free(pp);
		printf("No mem for table!\n");
		return -ENOMEM;
	}

413 414
	mem = (mem + 0x800) & (~0x7ff);	/* Aligned to 2048-bytes */
	memset((u8 *) mem, 0, AHCI_PORT_PRIV_DMA_SZ);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
415 416 417 418 419 420

	/*
	 * First item in chunk of DMA memory: 32-slot command table,
	 * 32 bytes each in size
	 */
	pp->cmd_slot = (struct ahci_cmd_hdr *)mem;
421
	debug("cmd_slot = 0x%x\n", (unsigned)pp->cmd_slot);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
422
	mem += (AHCI_CMD_SLOT_SZ + 224);
423

Jin Zhengxiong's avatar
Jin Zhengxiong committed
424 425 426 427 428
	/*
	 * Second item: Received-FIS area
	 */
	pp->rx_fis = mem;
	mem += AHCI_RX_FIS_SZ;
429

Jin Zhengxiong's avatar
Jin Zhengxiong committed
430 431 432 433 434
	/*
	 * Third item: data area for storing a single command
	 * and its scatter-gather table
	 */
	pp->cmd_tbl = mem;
435
	debug("cmd_tbl_dma = 0x%x\n", pp->cmd_tbl);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
436 437 438 439

	mem += AHCI_CMD_TBL_HDR;
	pp->cmd_tbl_sg = (struct ahci_sg *)mem;

440
	writel_with_flush((u32) pp->cmd_slot, port_mmio + PORT_LST_ADDR);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
441 442 443 444

	writel_with_flush(pp->rx_fis, port_mmio + PORT_FIS_ADDR);

	writel_with_flush(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX |
445 446
			  PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP |
			  PORT_CMD_START, port_mmio + PORT_CMD);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
447

448
	debug("Exit start port %d\n", port);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
449 450 451 452 453

	return 0;
}


454 455
static int get_ahci_device_data(u8 port, u8 *fis, int fis_len, u8 *buf,
				int buf_len)
Jin Zhengxiong's avatar
Jin Zhengxiong committed
456 457
{

458 459
	struct ahci_ioports *pp = &(probe_ent->port[port]);
	volatile u8 *port_mmio = (volatile u8 *)pp->port_mmio;
Jin Zhengxiong's avatar
Jin Zhengxiong committed
460 461 462 463
	u32 opts;
	u32 port_status;
	int sg_count;

464
	debug("Enter get_ahci_device_data: for port %d\n", port);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
465

466
	if (port > probe_ent->n_ports) {
Jin Zhengxiong's avatar
Jin Zhengxiong committed
467 468 469 470 471
		printf("Invaild port number %d\n", port);
		return -1;
	}

	port_status = readl(port_mmio + PORT_SCR_STAT);
472 473
	if ((port_status & 0xf) != 0x03) {
		debug("No Link on port %d!\n", port);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
474 475 476 477 478
		return -1;
	}

	memcpy((unsigned char *)pp->cmd_tbl, fis, fis_len);

479 480
	sg_count = ahci_fill_sg(port, buf, buf_len);
	opts = (fis_len >> 2) | (sg_count << 16);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
481 482 483 484 485 486 487 488 489
	ahci_fill_cmd_slot(pp, opts);

	writel_with_flush(1, port_mmio + PORT_CMD_ISSUE);

	if (waiting_for_cmd_completed(port_mmio + PORT_CMD_ISSUE, 150, 0x1)) {
		printf("timeout exit!\n");
		return -1;
	}
	debug("get_ahci_device_data: %d byte transferred.\n",
490
	      pp->cmd_slot->status);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
491 492 493 494 495 496 497 498

	return 0;
}


static char *ata_id_strcpy(u16 *target, u16 *src, int len)
{
	int i;
499
	for (i = 0; i < len / 2; i++)
500
		target[i] = swab16(src[i]);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
	return (char *)target;
}


static void dump_ataid(hd_driveid_t *ataid)
{
	debug("(49)ataid->capability = 0x%x\n", ataid->capability);
	debug("(53)ataid->field_valid =0x%x\n", ataid->field_valid);
	debug("(63)ataid->dma_mword = 0x%x\n", ataid->dma_mword);
	debug("(64)ataid->eide_pio_modes = 0x%x\n", ataid->eide_pio_modes);
	debug("(75)ataid->queue_depth = 0x%x\n", ataid->queue_depth);
	debug("(80)ataid->major_rev_num = 0x%x\n", ataid->major_rev_num);
	debug("(81)ataid->minor_rev_num = 0x%x\n", ataid->minor_rev_num);
	debug("(82)ataid->command_set_1 = 0x%x\n", ataid->command_set_1);
	debug("(83)ataid->command_set_2 = 0x%x\n", ataid->command_set_2);
	debug("(84)ataid->cfsse = 0x%x\n", ataid->cfsse);
	debug("(85)ataid->cfs_enable_1 = 0x%x\n", ataid->cfs_enable_1);
	debug("(86)ataid->cfs_enable_2 = 0x%x\n", ataid->cfs_enable_2);
	debug("(87)ataid->csf_default = 0x%x\n", ataid->csf_default);
	debug("(88)ataid->dma_ultra = 0x%x\n", ataid->dma_ultra);
	debug("(93)ataid->hw_config = 0x%x\n", ataid->hw_config);
}

524

Jin Zhengxiong's avatar
Jin Zhengxiong committed
525 526 527 528 529 530 531 532
/*
 * SCSI INQUIRY command operation.
 */
static int ata_scsiop_inquiry(ccb *pccb)
{
	u8 hdr[] = {
		0,
		0,
533
		0x5,		/* claim SPC-3 version compatibility */
Jin Zhengxiong's avatar
Jin Zhengxiong committed
534 535 536 537 538 539 540 541 542 543 544 545
		2,
		95 - 4,
	};
	u8 fis[20];
	u8 *tmpid;
	u8 port;

	/* Clean ccb data buffer */
	memset(pccb->pdata, 0, pccb->datalen);

	memcpy(pccb->pdata, hdr, sizeof(hdr));

546
	if (pccb->datalen <= 35)
Jin Zhengxiong's avatar
Jin Zhengxiong committed
547 548 549 550
		return 0;

	memset(fis, 0, 20);
	/* Construct the FIS */
551 552 553
	fis[0] = 0x27;		/* Host to device FIS. */
	fis[1] = 1 << 7;	/* Command FIS. */
	fis[2] = ATA_CMD_IDENT;	/* Command byte. */
Jin Zhengxiong's avatar
Jin Zhengxiong committed
554 555 556

	/* Read id from sata */
	port = pccb->target;
557
	if (!(tmpid = malloc(sizeof(hd_driveid_t))))
Jin Zhengxiong's avatar
Jin Zhengxiong committed
558 559
		return -ENOMEM;

560 561
	if (get_ahci_device_data(port, (u8 *) & fis, 20,
				 tmpid, sizeof(hd_driveid_t))) {
Jin Zhengxiong's avatar
Jin Zhengxiong committed
562 563 564 565
		debug("scsi_ahci: SCSI inquiry command failure.\n");
		return -EIO;
	}

566
	if (ataid[port])
Jin Zhengxiong's avatar
Jin Zhengxiong committed
567
		free(ataid[port]);
568
	ataid[port] = (hd_driveid_t *) tmpid;
Jin Zhengxiong's avatar
Jin Zhengxiong committed
569 570

	memcpy(&pccb->pdata[8], "ATA     ", 8);
571 572
	ata_id_strcpy((u16 *) &pccb->pdata[16], (u16 *)ataid[port]->model, 16);
	ata_id_strcpy((u16 *) &pccb->pdata[32], (u16 *)ataid[port]->fw_rev, 4);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
573 574 575 576 577 578 579 580 581

	dump_ataid(ataid[port]);
	return 0;
}


/*
 * SCSI READ10 command operation.
 */
582
static int ata_scsiop_read10(ccb * pccb)
Jin Zhengxiong's avatar
Jin Zhengxiong committed
583
{
584 585
	u32 lba = 0;
	u16 blocks = 0;
Jin Zhengxiong's avatar
Jin Zhengxiong committed
586
	u8 fis[20];
587 588
	u8 *user_buffer = pccb->pdata;
	u32 user_buffer_size = pccb->datalen;
Jin Zhengxiong's avatar
Jin Zhengxiong committed
589

590 591 592
	/* Retrieve the base LBA number from the ccb structure. */
	memcpy(&lba, pccb->cmd + 2, sizeof(lba));
	lba = be32_to_cpu(lba);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
593

594 595 596 597
	/*
	 * And the number of blocks.
	 *
	 * For 10-byte and 16-byte SCSI R/W commands, transfer
Jin Zhengxiong's avatar
Jin Zhengxiong committed
598 599 600 601 602 603
	 * length 0 means transfer 0 block of data.
	 * However, for ATA R/W commands, sector count 0 means
	 * 256 or 65536 sectors, not 0 sectors as in SCSI.
	 *
	 * WARNING: one or two older ATA drives treat 0 as 0...
	 */
604 605 606 607 608 609
	blocks = (((u16)pccb->cmd[7]) << 8) | ((u16) pccb->cmd[8]);

	debug("scsi_ahci: read %d blocks starting from lba 0x%x\n",
	      (unsigned)lba, blocks);

	/* Preset the FIS */
Jin Zhengxiong's avatar
Jin Zhengxiong committed
610
	memset(fis, 0, 20);
611 612 613
	fis[0] = 0x27;		 /* Host to device FIS. */
	fis[1] = 1 << 7;	 /* Command FIS. */
	fis[2] = ATA_CMD_RD_DMA; /* Command byte. */
Jin Zhengxiong's avatar
Jin Zhengxiong committed
614

615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
	while (blocks) {
		u16 now_blocks; /* number of blocks per iteration */
		u32 transfer_size; /* number of bytes per iteration */

		now_blocks = min(MAX_SATA_BLOCKS_READ, blocks);

		transfer_size = ATA_BLOCKSIZE * now_blocks;
		if (transfer_size > user_buffer_size) {
			printf("scsi_ahci: Error: buffer too small.\n");
			return -EIO;
		}

		/* LBA address, only support LBA28 in this driver */
		fis[4] = (lba >> 0) & 0xff;
		fis[5] = (lba >> 8) & 0xff;
		fis[6] = (lba >> 16) & 0xff;
		fis[7] = ((lba >> 24) & 0xf) | 0xe0;

		/* Block (sector) count */
		fis[12] = (now_blocks >> 0) & 0xff;
		fis[13] = (now_blocks >> 8) & 0xff;

		/* Read from ahci */
		if (get_ahci_device_data(pccb->target, (u8 *) &fis, sizeof(fis),
					 user_buffer, user_buffer_size)) {
			debug("scsi_ahci: SCSI READ10 command failure.\n");
			return -EIO;
		}
		user_buffer += transfer_size;
		user_buffer_size -= transfer_size;
		blocks -= now_blocks;
		lba += now_blocks;
Jin Zhengxiong's avatar
Jin Zhengxiong committed
647 648 649 650 651 652 653 654 655 656 657
	}

	return 0;
}


/*
 * SCSI READ CAPACITY10 command operation.
 */
static int ata_scsiop_read_capacity10(ccb *pccb)
{
658
	u32 cap;
Jin Zhengxiong's avatar
Jin Zhengxiong committed
659

660
	if (!ataid[pccb->target]) {
Jin Zhengxiong's avatar
Jin Zhengxiong committed
661
		printf("scsi_ahci: SCSI READ CAPACITY10 command failure. "
662 663
		       "\tNo ATA info!\n"
		       "\tPlease run SCSI commmand INQUIRY firstly!\n");
Jin Zhengxiong's avatar
Jin Zhengxiong committed
664 665 666
		return -EPERM;
	}

667
	cap = be32_to_cpu(ataid[pccb->target]->lba_capacity);
668
	memcpy(pccb->pdata, &cap, sizeof(cap));
Jin Zhengxiong's avatar
Jin Zhengxiong committed
669

670 671 672
	pccb->pdata[4] = pccb->pdata[5] = 0;
	pccb->pdata[6] = 512 >> 8;
	pccb->pdata[7] = 512 & 0xff;
Jin Zhengxiong's avatar
Jin Zhengxiong committed
673 674 675 676 677 678 679 680 681 682 683 684 685

	return 0;
}


/*
 * SCSI TEST UNIT READY command operation.
 */
static int ata_scsiop_test_unit_ready(ccb *pccb)
{
	return (ataid[pccb->target]) ? 0 : -EPERM;
}

686

Jin Zhengxiong's avatar
Jin Zhengxiong committed
687 688 689 690
int scsi_exec(ccb *pccb)
{
	int ret;

691
	switch (pccb->cmd[0]) {
Jin Zhengxiong's avatar
Jin Zhengxiong committed
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
	case SCSI_READ10:
		ret = ata_scsiop_read10(pccb);
		break;
	case SCSI_RD_CAPAC:
		ret = ata_scsiop_read_capacity10(pccb);
		break;
	case SCSI_TST_U_RDY:
		ret = ata_scsiop_test_unit_ready(pccb);
		break;
	case SCSI_INQUIRY:
		ret = ata_scsiop_inquiry(pccb);
		break;
	default:
		printf("Unsupport SCSI command 0x%02x\n", pccb->cmd[0]);
		return FALSE;
	}

709 710
	if (ret) {
		debug("SCSI command 0x%02x ret errno %d\n", pccb->cmd[0], ret);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
711 712 713 714 715 716 717 718 719 720 721 722
		return FALSE;
	}
	return TRUE;

}


void scsi_low_level_init(int busdevfunc)
{
	int i;
	u32 linkmap;

723
#ifndef CONFIG_SCSI_AHCI_PLAT
Jin Zhengxiong's avatar
Jin Zhengxiong committed
724
	ahci_init_one(busdevfunc);
725
#endif
Jin Zhengxiong's avatar
Jin Zhengxiong committed
726 727 728

	linkmap = probe_ent->link_port_map;

729
	for (i = 0; i < CONFIG_SYS_SCSI_MAX_SCSI_ID; i++) {
730 731 732
		if (((linkmap >> i) & 0x01)) {
			if (ahci_port_start((u8) i)) {
				printf("Can not start port %d\n", i);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
733 734
				continue;
			}
735
			ahci_set_feature((u8) i);
Jin Zhengxiong's avatar
Jin Zhengxiong committed
736 737 738 739
		}
	}
}

740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
#ifdef CONFIG_SCSI_AHCI_PLAT
int ahci_init(u32 base)
{
	int i, rc = 0;
	u32 linkmap;

	memset(ataid, 0, sizeof(ataid));

	probe_ent = malloc(sizeof(struct ahci_probe_ent));
	memset(probe_ent, 0, sizeof(struct ahci_probe_ent));

	probe_ent->host_flags = ATA_FLAG_SATA
				| ATA_FLAG_NO_LEGACY
				| ATA_FLAG_MMIO
				| ATA_FLAG_PIO_DMA
				| ATA_FLAG_NO_ATAPI;
	probe_ent->pio_mask = 0x1f;
	probe_ent->udma_mask = 0x7f;	/*Fixme,assume to support UDMA6 */

	probe_ent->mmio_base = base;

	/* initialize adapter */
	rc = ahci_host_init(probe_ent);
	if (rc)
		goto err_out;

	ahci_print_info(probe_ent);

	linkmap = probe_ent->link_port_map;

	for (i = 0; i < CONFIG_SYS_SCSI_MAX_SCSI_ID; i++) {
		if (((linkmap >> i) & 0x01)) {
			if (ahci_port_start((u8) i)) {
				printf("Can not start port %d\n", i);
				continue;
			}
			ahci_set_feature((u8) i);
		}
	}
err_out:
	return rc;
}
#endif
Jin Zhengxiong's avatar
Jin Zhengxiong committed
783 784 785

void scsi_bus_reset(void)
{
786
	/*Not implement*/
Jin Zhengxiong's avatar
Jin Zhengxiong committed
787 788 789
}


790
void scsi_print_error(ccb * pccb)
Jin Zhengxiong's avatar
Jin Zhengxiong committed
791
{
792
	/*The ahci error info can be read in the ahci driver*/
Jin Zhengxiong's avatar
Jin Zhengxiong committed
793
}