xref: /rk3399_rockchip-uboot/drivers/scsi/scsi.c (revision 55431405b4eba26a316bedf9426a2adb2ed49e06)
10fcd48feSSimon Glass /*
20fcd48feSSimon Glass  * (C) Copyright 2001
30fcd48feSSimon Glass  * Denis Peter, MPL AG Switzerland
40fcd48feSSimon Glass  *
50fcd48feSSimon Glass  * SPDX-License-Identifier:	GPL-2.0+
60fcd48feSSimon Glass  */
70fcd48feSSimon Glass 
80fcd48feSSimon Glass #include <common.h>
90fcd48feSSimon Glass #include <dm.h>
100fcd48feSSimon Glass #include <inttypes.h>
1163a7b5a0SYifeng Zhao #include <memalign.h>
120fcd48feSSimon Glass #include <pci.h>
130fcd48feSSimon Glass #include <scsi.h>
140fcd48feSSimon Glass #include <dm/device-internal.h>
150fcd48feSSimon Glass #include <dm/uclass-internal.h>
160fcd48feSSimon Glass 
170fcd48feSSimon Glass #if !defined(CONFIG_DM_SCSI)
180fcd48feSSimon Glass # ifdef CONFIG_SCSI_DEV_LIST
190fcd48feSSimon Glass #  define SCSI_DEV_LIST CONFIG_SCSI_DEV_LIST
200fcd48feSSimon Glass # else
210fcd48feSSimon Glass #  ifdef CONFIG_SATA_ULI5288
220fcd48feSSimon Glass 
230fcd48feSSimon Glass #   define SCSI_VEND_ID 0x10b9
240fcd48feSSimon Glass #   define SCSI_DEV_ID  0x5288
250fcd48feSSimon Glass 
260fcd48feSSimon Glass #  elif !defined(CONFIG_SCSI_AHCI_PLAT)
270fcd48feSSimon Glass #   error no scsi device defined
280fcd48feSSimon Glass #  endif
290fcd48feSSimon Glass # define SCSI_DEV_LIST {SCSI_VEND_ID, SCSI_DEV_ID}
300fcd48feSSimon Glass # endif
310fcd48feSSimon Glass #endif
320fcd48feSSimon Glass 
337337fcd8SSimon Glass #if defined(CONFIG_PCI) && !defined(CONFIG_SCSI_AHCI_PLAT) && \
347337fcd8SSimon Glass 	!defined(CONFIG_DM_SCSI)
350fcd48feSSimon Glass const struct pci_device_id scsi_device_list[] = { SCSI_DEV_LIST };
360fcd48feSSimon Glass #endif
37b9560ad6SSimon Glass static struct scsi_cmd tempccb;	/* temporary scsi command buffer */
380fcd48feSSimon Glass 
390fcd48feSSimon Glass static unsigned char tempbuff[512]; /* temporary data buffer */
400fcd48feSSimon Glass 
410fcd48feSSimon Glass #if !defined(CONFIG_DM_SCSI)
420fcd48feSSimon Glass static int scsi_max_devs; /* number of highest available scsi device */
430fcd48feSSimon Glass 
440fcd48feSSimon Glass static int scsi_curr_dev; /* current device */
450fcd48feSSimon Glass 
460fcd48feSSimon Glass static struct blk_desc scsi_dev_desc[CONFIG_SYS_SCSI_MAX_DEVICE];
470fcd48feSSimon Glass #endif
480fcd48feSSimon Glass 
490fcd48feSSimon Glass /* almost the maximum amount of the scsi_ext command.. */
500fcd48feSSimon Glass #define SCSI_MAX_READ_BLK 0xFFFF
51*55431405SYifeng Zhao #define SCSI_UFS_MAX_READ_BLK 0x2000	/* max read size 32MB for UFS */
520fcd48feSSimon Glass #define SCSI_LBA48_READ	0xFFFFFFF
530fcd48feSSimon Glass 
scsi_print_error(struct scsi_cmd * pccb)54b9560ad6SSimon Glass static void scsi_print_error(struct scsi_cmd *pccb)
550fcd48feSSimon Glass {
560fcd48feSSimon Glass 	/* Dummy function that could print an error for debugging */
570fcd48feSSimon Glass }
580fcd48feSSimon Glass 
590fcd48feSSimon Glass #ifdef CONFIG_SYS_64BIT_LBA
scsi_setup_read16(struct scsi_cmd * pccb,lbaint_t start,unsigned long blocks)60b9560ad6SSimon Glass void scsi_setup_read16(struct scsi_cmd *pccb, lbaint_t start,
61b9560ad6SSimon Glass 		       unsigned long blocks)
620fcd48feSSimon Glass {
630fcd48feSSimon Glass 	pccb->cmd[0] = SCSI_READ16;
640fcd48feSSimon Glass 	pccb->cmd[1] = pccb->lun << 5;
650fcd48feSSimon Glass 	pccb->cmd[2] = (unsigned char)(start >> 56) & 0xff;
660fcd48feSSimon Glass 	pccb->cmd[3] = (unsigned char)(start >> 48) & 0xff;
670fcd48feSSimon Glass 	pccb->cmd[4] = (unsigned char)(start >> 40) & 0xff;
680fcd48feSSimon Glass 	pccb->cmd[5] = (unsigned char)(start >> 32) & 0xff;
690fcd48feSSimon Glass 	pccb->cmd[6] = (unsigned char)(start >> 24) & 0xff;
700fcd48feSSimon Glass 	pccb->cmd[7] = (unsigned char)(start >> 16) & 0xff;
710fcd48feSSimon Glass 	pccb->cmd[8] = (unsigned char)(start >> 8) & 0xff;
720fcd48feSSimon Glass 	pccb->cmd[9] = (unsigned char)start & 0xff;
730fcd48feSSimon Glass 	pccb->cmd[10] = 0;
740fcd48feSSimon Glass 	pccb->cmd[11] = (unsigned char)(blocks >> 24) & 0xff;
750fcd48feSSimon Glass 	pccb->cmd[12] = (unsigned char)(blocks >> 16) & 0xff;
760fcd48feSSimon Glass 	pccb->cmd[13] = (unsigned char)(blocks >> 8) & 0xff;
770fcd48feSSimon Glass 	pccb->cmd[14] = (unsigned char)blocks & 0xff;
780fcd48feSSimon Glass 	pccb->cmd[15] = 0;
790fcd48feSSimon Glass 	pccb->cmdlen = 16;
800fcd48feSSimon Glass 	pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */
810fcd48feSSimon Glass 	debug("scsi_setup_read16: cmd: %02X %02X startblk %02X%02X%02X%02X%02X%02X%02X%02X blccnt %02X%02X%02X%02X\n",
820fcd48feSSimon Glass 	      pccb->cmd[0], pccb->cmd[1],
830fcd48feSSimon Glass 	      pccb->cmd[2], pccb->cmd[3], pccb->cmd[4], pccb->cmd[5],
840fcd48feSSimon Glass 	      pccb->cmd[6], pccb->cmd[7], pccb->cmd[8], pccb->cmd[9],
850fcd48feSSimon Glass 	      pccb->cmd[11], pccb->cmd[12], pccb->cmd[13], pccb->cmd[14]);
860fcd48feSSimon Glass }
870fcd48feSSimon Glass #endif
880fcd48feSSimon Glass 
scsi_setup_read_ext(struct scsi_cmd * pccb,lbaint_t start,unsigned short blocks)89b9560ad6SSimon Glass static void scsi_setup_read_ext(struct scsi_cmd *pccb, lbaint_t start,
900fcd48feSSimon Glass 				unsigned short blocks)
910fcd48feSSimon Glass {
920fcd48feSSimon Glass 	pccb->cmd[0] = SCSI_READ10;
930fcd48feSSimon Glass 	pccb->cmd[1] = pccb->lun << 5;
940fcd48feSSimon Glass 	pccb->cmd[2] = (unsigned char)(start >> 24) & 0xff;
950fcd48feSSimon Glass 	pccb->cmd[3] = (unsigned char)(start >> 16) & 0xff;
960fcd48feSSimon Glass 	pccb->cmd[4] = (unsigned char)(start >> 8) & 0xff;
970fcd48feSSimon Glass 	pccb->cmd[5] = (unsigned char)start & 0xff;
980fcd48feSSimon Glass 	pccb->cmd[6] = 0;
990fcd48feSSimon Glass 	pccb->cmd[7] = (unsigned char)(blocks >> 8) & 0xff;
1000fcd48feSSimon Glass 	pccb->cmd[8] = (unsigned char)blocks & 0xff;
1010fcd48feSSimon Glass 	pccb->cmd[6] = 0;
1020fcd48feSSimon Glass 	pccb->cmdlen = 10;
1030fcd48feSSimon Glass 	pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */
1040fcd48feSSimon Glass 	debug("scsi_setup_read_ext: cmd: %02X %02X startblk %02X%02X%02X%02X blccnt %02X%02X\n",
1050fcd48feSSimon Glass 	      pccb->cmd[0], pccb->cmd[1],
1060fcd48feSSimon Glass 	      pccb->cmd[2], pccb->cmd[3], pccb->cmd[4], pccb->cmd[5],
1070fcd48feSSimon Glass 	      pccb->cmd[7], pccb->cmd[8]);
1080fcd48feSSimon Glass }
1090fcd48feSSimon Glass 
scsi_setup_write_ext(struct scsi_cmd * pccb,lbaint_t start,unsigned short blocks)110b9560ad6SSimon Glass static void scsi_setup_write_ext(struct scsi_cmd *pccb, lbaint_t start,
1110fcd48feSSimon Glass 				 unsigned short blocks)
1120fcd48feSSimon Glass {
1130fcd48feSSimon Glass 	pccb->cmd[0] = SCSI_WRITE10;
11452f5f55eSYifeng Zhao 	pccb->cmd[1] = pccb->lun << 5 | 0x8;
1150fcd48feSSimon Glass 	pccb->cmd[2] = (unsigned char)(start >> 24) & 0xff;
1160fcd48feSSimon Glass 	pccb->cmd[3] = (unsigned char)(start >> 16) & 0xff;
1170fcd48feSSimon Glass 	pccb->cmd[4] = (unsigned char)(start >> 8) & 0xff;
1180fcd48feSSimon Glass 	pccb->cmd[5] = (unsigned char)start & 0xff;
1190fcd48feSSimon Glass 	pccb->cmd[6] = 0;
1200fcd48feSSimon Glass 	pccb->cmd[7] = ((unsigned char)(blocks >> 8)) & 0xff;
1210fcd48feSSimon Glass 	pccb->cmd[8] = (unsigned char)blocks & 0xff;
1220fcd48feSSimon Glass 	pccb->cmd[9] = 0;
1230fcd48feSSimon Glass 	pccb->cmdlen = 10;
1240fcd48feSSimon Glass 	pccb->msgout[0] = SCSI_IDENTIFY;  /* NOT USED */
1250fcd48feSSimon Glass 	debug("%s: cmd: %02X %02X startblk %02X%02X%02X%02X blccnt %02X%02X\n",
1260fcd48feSSimon Glass 	      __func__,
1270fcd48feSSimon Glass 	      pccb->cmd[0], pccb->cmd[1],
1280fcd48feSSimon Glass 	      pccb->cmd[2], pccb->cmd[3], pccb->cmd[4], pccb->cmd[5],
1290fcd48feSSimon Glass 	      pccb->cmd[7], pccb->cmd[8]);
1300fcd48feSSimon Glass }
1310fcd48feSSimon Glass 
scsi_setup_inquiry(struct scsi_cmd * pccb)132b9560ad6SSimon Glass static void scsi_setup_inquiry(struct scsi_cmd *pccb)
1330fcd48feSSimon Glass {
1340fcd48feSSimon Glass 	pccb->cmd[0] = SCSI_INQUIRY;
1350fcd48feSSimon Glass 	pccb->cmd[1] = pccb->lun << 5;
1360fcd48feSSimon Glass 	pccb->cmd[2] = 0;
1370fcd48feSSimon Glass 	pccb->cmd[3] = 0;
13855d11a71SYifeng Zhao 	if (pccb->datalen > SCSI_MAX_INQUIRY_BYTES)
13955d11a71SYifeng Zhao 		pccb->cmd[4] = SCSI_MAX_INQUIRY_BYTES;
1400fcd48feSSimon Glass 	else
1410fcd48feSSimon Glass 		pccb->cmd[4] = (unsigned char)pccb->datalen;
1420fcd48feSSimon Glass 	pccb->cmd[5] = 0;
1430fcd48feSSimon Glass 	pccb->cmdlen = 6;
1440fcd48feSSimon Glass 	pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */
1450fcd48feSSimon Glass }
1460fcd48feSSimon Glass 
14763a7b5a0SYifeng Zhao /*
14863a7b5a0SYifeng Zhao  * Some setup (fill-in) routines
14963a7b5a0SYifeng Zhao  */
scsi_setup_test_unit_ready(struct scsi_cmd * pccb)15063a7b5a0SYifeng Zhao static void scsi_setup_test_unit_ready(struct scsi_cmd *pccb)
15163a7b5a0SYifeng Zhao {
15263a7b5a0SYifeng Zhao 	pccb->cmd[0] = SCSI_TST_U_RDY;
15363a7b5a0SYifeng Zhao 	pccb->cmd[1] = pccb->lun << 5;
15463a7b5a0SYifeng Zhao 	pccb->cmd[2] = 0;
15563a7b5a0SYifeng Zhao 	pccb->cmd[3] = 0;
15663a7b5a0SYifeng Zhao 	pccb->cmd[4] = 0;
15763a7b5a0SYifeng Zhao 	pccb->cmd[5] = 0;
15863a7b5a0SYifeng Zhao 	pccb->cmdlen = 6;
15963a7b5a0SYifeng Zhao 	pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */
16063a7b5a0SYifeng Zhao }
16163a7b5a0SYifeng Zhao 
1620fcd48feSSimon Glass #ifdef CONFIG_BLK
_scsi_read(struct udevice * dev,lbaint_t blknr,lbaint_t blkcnt,void * buffer)16352f5f55eSYifeng Zhao static ulong _scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
1640fcd48feSSimon Glass 		       void *buffer)
1650fcd48feSSimon Glass #else
16652f5f55eSYifeng Zhao static ulong _scsi_read(struct blk_desc *block_dev, lbaint_t blknr,
1670fcd48feSSimon Glass 		       lbaint_t blkcnt, void *buffer)
1680fcd48feSSimon Glass #endif
1690fcd48feSSimon Glass {
1700fcd48feSSimon Glass #ifdef CONFIG_BLK
1710fcd48feSSimon Glass 	struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
1724682c8a1SSimon Glass 	struct udevice *bdev = dev->parent;
1734682c8a1SSimon Glass #else
1744682c8a1SSimon Glass 	struct udevice *bdev = NULL;
1750fcd48feSSimon Glass #endif
176*55431405SYifeng Zhao 	lbaint_t start, blks, max_read_blks;
1770fcd48feSSimon Glass 	uintptr_t buf_addr;
1780fcd48feSSimon Glass 	unsigned short smallblks = 0;
179b9560ad6SSimon Glass 	struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb;
1800fcd48feSSimon Glass 
181*55431405SYifeng Zhao 	if (block_dev->rawblksz == 512)
182*55431405SYifeng Zhao 		max_read_blks = SCSI_MAX_READ_BLK;
183*55431405SYifeng Zhao 	else
184*55431405SYifeng Zhao 		max_read_blks = SCSI_UFS_MAX_READ_BLK;
1850fcd48feSSimon Glass 	/* Setup device */
1860fcd48feSSimon Glass 	pccb->target = block_dev->target;
1870fcd48feSSimon Glass 	pccb->lun = block_dev->lun;
1880fcd48feSSimon Glass 	buf_addr = (unsigned long)buffer;
1890fcd48feSSimon Glass 	start = blknr;
1900fcd48feSSimon Glass 	blks = blkcnt;
1910fcd48feSSimon Glass 	debug("\nscsi_read: dev %d startblk " LBAF
1920fcd48feSSimon Glass 	      ", blccnt " LBAF " buffer %lx\n",
1930fcd48feSSimon Glass 	      block_dev->devnum, start, blks, (unsigned long)buffer);
1940fcd48feSSimon Glass 	do {
1950fcd48feSSimon Glass 		pccb->pdata = (unsigned char *)buf_addr;
1968f7de514SShawn Lin 		pccb->dma_dir = DMA_FROM_DEVICE;
1970fcd48feSSimon Glass #ifdef CONFIG_SYS_64BIT_LBA
1980fcd48feSSimon Glass 		if (start > SCSI_LBA48_READ) {
1990fcd48feSSimon Glass 			unsigned long blocks;
200*55431405SYifeng Zhao 			blocks = min_t(lbaint_t, blks, max_read_blks);
20152f5f55eSYifeng Zhao 			pccb->datalen = block_dev->rawblksz * blocks;
2020fcd48feSSimon Glass 			scsi_setup_read16(pccb, start, blocks);
2030fcd48feSSimon Glass 			start += blocks;
2040fcd48feSSimon Glass 			blks -= blocks;
2050fcd48feSSimon Glass 		} else
2060fcd48feSSimon Glass #endif
207*55431405SYifeng Zhao 		if (blks > max_read_blks) {
208*55431405SYifeng Zhao 			pccb->datalen = block_dev->rawblksz * max_read_blks;
209*55431405SYifeng Zhao 			smallblks = max_read_blks;
2100fcd48feSSimon Glass 			scsi_setup_read_ext(pccb, start, smallblks);
211*55431405SYifeng Zhao 			start += max_read_blks;
212*55431405SYifeng Zhao 			blks -= max_read_blks;
2130fcd48feSSimon Glass 		} else {
21452f5f55eSYifeng Zhao 			pccb->datalen = block_dev->rawblksz * blks;
2150fcd48feSSimon Glass 			smallblks = (unsigned short)blks;
2160fcd48feSSimon Glass 			scsi_setup_read_ext(pccb, start, smallblks);
2170fcd48feSSimon Glass 			start += blks;
2180fcd48feSSimon Glass 			blks = 0;
2190fcd48feSSimon Glass 		}
2200fcd48feSSimon Glass 		debug("scsi_read_ext: startblk " LBAF
2210fcd48feSSimon Glass 		      ", blccnt %x buffer %" PRIXPTR "\n",
2220fcd48feSSimon Glass 		      start, smallblks, buf_addr);
2234682c8a1SSimon Glass 		if (scsi_exec(bdev, pccb)) {
2240fcd48feSSimon Glass 			scsi_print_error(pccb);
2250fcd48feSSimon Glass 			blkcnt -= blks;
2260fcd48feSSimon Glass 			break;
2270fcd48feSSimon Glass 		}
2280fcd48feSSimon Glass 		buf_addr += pccb->datalen;
2290fcd48feSSimon Glass 	} while (blks != 0);
2300fcd48feSSimon Glass 	debug("scsi_read_ext: end startblk " LBAF
2310fcd48feSSimon Glass 	      ", blccnt %x buffer %" PRIXPTR "\n", start, smallblks, buf_addr);
2320fcd48feSSimon Glass 	return blkcnt;
2330fcd48feSSimon Glass }
2340fcd48feSSimon Glass 
23552f5f55eSYifeng Zhao #ifdef CONFIG_BLK
scsi_read(struct udevice * dev,lbaint_t blknr,lbaint_t blkcnt,void * buffer)23652f5f55eSYifeng Zhao static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
23752f5f55eSYifeng Zhao 		       void *buffer)
23852f5f55eSYifeng Zhao #else
23952f5f55eSYifeng Zhao static ulong scsi_read(struct blk_desc *block_dev, lbaint_t blknr,
24052f5f55eSYifeng Zhao 		       lbaint_t blkcnt, void *buffer)
24152f5f55eSYifeng Zhao #endif
24252f5f55eSYifeng Zhao {
24352f5f55eSYifeng Zhao #ifdef CONFIG_BLK
24452f5f55eSYifeng Zhao 	struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
24552f5f55eSYifeng Zhao 	uint32_t rawsectsz = block_dev->rawblksz / 512;
24652f5f55eSYifeng Zhao 	long ret = blkcnt;
24752f5f55eSYifeng Zhao 
24852f5f55eSYifeng Zhao 	if (rawsectsz == 8) {
24952f5f55eSYifeng Zhao 		if ((blknr & (rawsectsz - 1)) || (blkcnt & (rawsectsz - 1))) {
25052f5f55eSYifeng Zhao 			uint32_t offset, n_sec, num_lpa;
25152f5f55eSYifeng Zhao 			long lpa;
25252f5f55eSYifeng Zhao 
25352f5f55eSYifeng Zhao 			while (blkcnt) {
25452f5f55eSYifeng Zhao 				lpa = blknr / rawsectsz;
25552f5f55eSYifeng Zhao 				offset = blknr & (rawsectsz - 1);
25652f5f55eSYifeng Zhao 				n_sec = rawsectsz - offset;
25752f5f55eSYifeng Zhao 				if (n_sec > blkcnt)
25852f5f55eSYifeng Zhao 					n_sec = blkcnt;
25952f5f55eSYifeng Zhao 
26052f5f55eSYifeng Zhao 				if (offset || n_sec < rawsectsz) {
26152f5f55eSYifeng Zhao 					_scsi_read(dev, lpa, 1, block_dev->align_sector_buf);
26252f5f55eSYifeng Zhao 					memcpy(buffer, block_dev->align_sector_buf + offset * 512, n_sec * 512);
26352f5f55eSYifeng Zhao 				} else {
26452f5f55eSYifeng Zhao 					num_lpa = blkcnt / rawsectsz;
26552f5f55eSYifeng Zhao 					n_sec = num_lpa * rawsectsz;
26652f5f55eSYifeng Zhao 					_scsi_read(dev, lpa, num_lpa, buffer);
26752f5f55eSYifeng Zhao 				}
26852f5f55eSYifeng Zhao 				blkcnt -= n_sec;
26952f5f55eSYifeng Zhao 				blknr += n_sec;
27052f5f55eSYifeng Zhao 				buffer += 512 * n_sec;
27152f5f55eSYifeng Zhao 			}
27252f5f55eSYifeng Zhao 
27352f5f55eSYifeng Zhao 			return ret;
27452f5f55eSYifeng Zhao 		}
27552f5f55eSYifeng Zhao 		blknr /= rawsectsz;
27652f5f55eSYifeng Zhao 		blkcnt /= rawsectsz;
27752f5f55eSYifeng Zhao 		_scsi_read(dev, blknr, blkcnt, buffer);
27852f5f55eSYifeng Zhao 
27952f5f55eSYifeng Zhao 		return ret;
28052f5f55eSYifeng Zhao 	}
28152f5f55eSYifeng Zhao 
28252f5f55eSYifeng Zhao 	return _scsi_read(dev, blknr, blkcnt, buffer);
28352f5f55eSYifeng Zhao #else
28452f5f55eSYifeng Zhao 	return _scsi_read(block_dev, blknr, blkcnt, buffer);
28552f5f55eSYifeng Zhao #endif
28652f5f55eSYifeng Zhao }
2870fcd48feSSimon Glass /*******************************************************************************
2880fcd48feSSimon Glass  * scsi_write
2890fcd48feSSimon Glass  */
2900fcd48feSSimon Glass 
2910fcd48feSSimon Glass /* Almost the maximum amount of the scsi_ext command.. */
2920fcd48feSSimon Glass #define SCSI_MAX_WRITE_BLK 0xFFFF
293*55431405SYifeng Zhao #define SCSI_UFS_MAX_WRITE_BLK 0x2000 /* max read size 32MB for UFS */
2940fcd48feSSimon Glass 
2950fcd48feSSimon Glass #ifdef CONFIG_BLK
_scsi_write(struct udevice * dev,lbaint_t blknr,lbaint_t blkcnt,const void * buffer)29652f5f55eSYifeng Zhao static ulong _scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
2970fcd48feSSimon Glass 			const void *buffer)
2980fcd48feSSimon Glass #else
29952f5f55eSYifeng Zhao static ulong _scsi_write(struct blk_desc *block_dev, lbaint_t blknr,
3000fcd48feSSimon Glass 			lbaint_t blkcnt, const void *buffer)
3010fcd48feSSimon Glass #endif
3020fcd48feSSimon Glass {
3030fcd48feSSimon Glass #ifdef CONFIG_BLK
3040fcd48feSSimon Glass 	struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
3054682c8a1SSimon Glass 	struct udevice *bdev = dev->parent;
3064682c8a1SSimon Glass #else
3074682c8a1SSimon Glass 	struct udevice *bdev = NULL;
3080fcd48feSSimon Glass #endif
309*55431405SYifeng Zhao 	lbaint_t start, blks, max_write_blks;
3100fcd48feSSimon Glass 	uintptr_t buf_addr;
3110fcd48feSSimon Glass 	unsigned short smallblks;
312b9560ad6SSimon Glass 	struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb;
3130fcd48feSSimon Glass 
314*55431405SYifeng Zhao 	if (block_dev->rawblksz == 512)
315*55431405SYifeng Zhao 		max_write_blks = SCSI_MAX_WRITE_BLK;
316*55431405SYifeng Zhao 	else
317*55431405SYifeng Zhao 		max_write_blks = SCSI_UFS_MAX_WRITE_BLK;
318*55431405SYifeng Zhao 
3190fcd48feSSimon Glass 	/* Setup device */
3200fcd48feSSimon Glass 	pccb->target = block_dev->target;
3210fcd48feSSimon Glass 	pccb->lun = block_dev->lun;
3220fcd48feSSimon Glass 	buf_addr = (unsigned long)buffer;
3230fcd48feSSimon Glass 	start = blknr;
3240fcd48feSSimon Glass 	blks = blkcnt;
3250fcd48feSSimon Glass 	debug("\n%s: dev %d startblk " LBAF ", blccnt " LBAF " buffer %lx\n",
3260fcd48feSSimon Glass 	      __func__, block_dev->devnum, start, blks, (unsigned long)buffer);
3270fcd48feSSimon Glass 	do {
3280fcd48feSSimon Glass 		pccb->pdata = (unsigned char *)buf_addr;
3298f7de514SShawn Lin 		pccb->dma_dir = DMA_TO_DEVICE;
330*55431405SYifeng Zhao 		if (blks > max_write_blks) {
331*55431405SYifeng Zhao 			pccb->datalen = (block_dev->rawblksz * max_write_blks);
332*55431405SYifeng Zhao 			smallblks = max_write_blks;
3330fcd48feSSimon Glass 			scsi_setup_write_ext(pccb, start, smallblks);
334*55431405SYifeng Zhao 			start += max_write_blks;
335*55431405SYifeng Zhao 			blks -= max_write_blks;
3360fcd48feSSimon Glass 		} else {
33752f5f55eSYifeng Zhao 			pccb->datalen = block_dev->rawblksz * blks;
3380fcd48feSSimon Glass 			smallblks = (unsigned short)blks;
3390fcd48feSSimon Glass 			scsi_setup_write_ext(pccb, start, smallblks);
3400fcd48feSSimon Glass 			start += blks;
3410fcd48feSSimon Glass 			blks = 0;
3420fcd48feSSimon Glass 		}
3430fcd48feSSimon Glass 		debug("%s: startblk " LBAF ", blccnt %x buffer %" PRIXPTR "\n",
3440fcd48feSSimon Glass 		      __func__, start, smallblks, buf_addr);
3454682c8a1SSimon Glass 		if (scsi_exec(bdev, pccb)) {
3460fcd48feSSimon Glass 			scsi_print_error(pccb);
3470fcd48feSSimon Glass 			blkcnt -= blks;
3480fcd48feSSimon Glass 			break;
3490fcd48feSSimon Glass 		}
3500fcd48feSSimon Glass 		buf_addr += pccb->datalen;
3510fcd48feSSimon Glass 	} while (blks != 0);
3520fcd48feSSimon Glass 	debug("%s: end startblk " LBAF ", blccnt %x buffer %" PRIXPTR "\n",
3530fcd48feSSimon Glass 	      __func__, start, smallblks, buf_addr);
3540fcd48feSSimon Glass 	return blkcnt;
3550fcd48feSSimon Glass }
3560fcd48feSSimon Glass 
35752f5f55eSYifeng Zhao #ifdef CONFIG_BLK
scsi_write(struct udevice * dev,lbaint_t blknr,lbaint_t blkcnt,const void * buffer)35852f5f55eSYifeng Zhao static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
35952f5f55eSYifeng Zhao 			const void *buffer)
36052f5f55eSYifeng Zhao #else
36152f5f55eSYifeng Zhao static ulong scsi_write(struct blk_desc *block_dev, lbaint_t blknr,
36252f5f55eSYifeng Zhao 			lbaint_t blkcnt, const void *buffer)
36352f5f55eSYifeng Zhao #endif
36452f5f55eSYifeng Zhao {
36552f5f55eSYifeng Zhao #ifdef CONFIG_BLK
36652f5f55eSYifeng Zhao 	struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
36752f5f55eSYifeng Zhao 	uint32_t rawsectsz = block_dev->rawblksz / 512;
36852f5f55eSYifeng Zhao 	long ret = blkcnt;
36952f5f55eSYifeng Zhao 
37052f5f55eSYifeng Zhao 	if (rawsectsz == 8) {
37152f5f55eSYifeng Zhao 		if ((blknr & (rawsectsz - 1)) || (blkcnt & (rawsectsz - 1))) {
37252f5f55eSYifeng Zhao 			uint32_t num_lpa, offset, n_sec;
37352f5f55eSYifeng Zhao 			long lpa;
37452f5f55eSYifeng Zhao 
37552f5f55eSYifeng Zhao 			while (blkcnt) {
37652f5f55eSYifeng Zhao 				lpa = blknr / rawsectsz;
37752f5f55eSYifeng Zhao 				offset = blknr & (rawsectsz - 1);
37852f5f55eSYifeng Zhao 				n_sec = rawsectsz - offset;
37952f5f55eSYifeng Zhao 				if (n_sec > blkcnt)
38052f5f55eSYifeng Zhao 					n_sec = blkcnt;
38152f5f55eSYifeng Zhao 				if (offset || n_sec < rawsectsz) {
38252f5f55eSYifeng Zhao 					 _scsi_read(dev, lpa, 1, block_dev->align_sector_buf);
38352f5f55eSYifeng Zhao 					memcpy(block_dev->align_sector_buf + offset * 512, buffer, n_sec * 512);
38452f5f55eSYifeng Zhao 					_scsi_write(dev, lpa, 1, block_dev->align_sector_buf);
38552f5f55eSYifeng Zhao 				} else {
38652f5f55eSYifeng Zhao 					num_lpa = blkcnt / rawsectsz;
38752f5f55eSYifeng Zhao 					n_sec = num_lpa * rawsectsz;
38852f5f55eSYifeng Zhao 					_scsi_write(dev, lpa, num_lpa, buffer);
38952f5f55eSYifeng Zhao 				}
39052f5f55eSYifeng Zhao 				blkcnt -= n_sec;
39152f5f55eSYifeng Zhao 				blknr += n_sec;
39252f5f55eSYifeng Zhao 				buffer += 512 * n_sec;
39352f5f55eSYifeng Zhao 			}
39452f5f55eSYifeng Zhao 
39552f5f55eSYifeng Zhao 			return ret;
39652f5f55eSYifeng Zhao 		}
39752f5f55eSYifeng Zhao 		blknr /= rawsectsz;
39852f5f55eSYifeng Zhao 		blkcnt /= rawsectsz;
39952f5f55eSYifeng Zhao 		_scsi_write(dev, blknr, blkcnt, buffer);
40052f5f55eSYifeng Zhao 
40152f5f55eSYifeng Zhao 		return ret;
40252f5f55eSYifeng Zhao 	}
40352f5f55eSYifeng Zhao 
40452f5f55eSYifeng Zhao 	return _scsi_write(dev, blknr, blkcnt, buffer);
40552f5f55eSYifeng Zhao #else
40652f5f55eSYifeng Zhao 	return _scsi_write(block_dev, blknr, blkcnt, buffer);
40752f5f55eSYifeng Zhao #endif
40852f5f55eSYifeng Zhao 
40952f5f55eSYifeng Zhao }
41032d06e10SYifeng Zhao 
41163a7b5a0SYifeng Zhao #ifdef CONFIG_BLK
scsi_erase(struct udevice * dev,lbaint_t blknr,lbaint_t blkcnt)41263a7b5a0SYifeng Zhao static ulong scsi_erase(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt)
41363a7b5a0SYifeng Zhao {
41463a7b5a0SYifeng Zhao 	ALLOC_CACHE_ALIGN_BUFFER_PAD(struct unmap_para_list, um_list, 1, ARCH_DMA_MINALIGN);
41563a7b5a0SYifeng Zhao 	struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
41663a7b5a0SYifeng Zhao 	struct udevice *bdev = dev->parent;
41763a7b5a0SYifeng Zhao 	struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb;
41863a7b5a0SYifeng Zhao 	uint32_t rawsectsz = block_dev->rawblksz / block_dev->blksz;
41963a7b5a0SYifeng Zhao 
420e36090fdSYifeng Zhao 	if (rawsectsz == 1) /* The sata devices not support data erase yet. */
421e36090fdSYifeng Zhao 		return blkcnt;
422e36090fdSYifeng Zhao 
42363a7b5a0SYifeng Zhao 	pccb->target = block_dev->target;
42463a7b5a0SYifeng Zhao 	pccb->lun = block_dev->lun;
42563a7b5a0SYifeng Zhao 	pccb->datalen = 0;
42663a7b5a0SYifeng Zhao 	scsi_setup_test_unit_ready(pccb);
42763a7b5a0SYifeng Zhao 	if (scsi_exec(bdev, pccb)) {
42863a7b5a0SYifeng Zhao 		printf("TEST UNIT READY fail!Can not erase UFS device\n");
42963a7b5a0SYifeng Zhao 		return 0;
43063a7b5a0SYifeng Zhao 	}
43163a7b5a0SYifeng Zhao 
43263a7b5a0SYifeng Zhao 	if (blknr % rawsectsz != 0 || blkcnt % rawsectsz != 0)
43363a7b5a0SYifeng Zhao 		printf("UFS erase area not aligned to %d, blknr = %lx, blkcnt = %lx\n", rawsectsz, blknr, blkcnt);
43463a7b5a0SYifeng Zhao 
43563a7b5a0SYifeng Zhao 	um_list->um_data_len = cpu_to_be16(sizeof(struct unmap_para_list) - 2);
43663a7b5a0SYifeng Zhao 	um_list->um_block_desc_len = cpu_to_be16(sizeof(struct um_block_descriptor));
43763a7b5a0SYifeng Zhao 	if (8 == sizeof(lbaint_t))
43863a7b5a0SYifeng Zhao 		um_list->ub_desc.um_block_addr = cpu_to_be64(blknr / rawsectsz);
43963a7b5a0SYifeng Zhao 	else
44063a7b5a0SYifeng Zhao 		um_list->ub_desc.um_block_addr = cpu_to_be64((uint64_t)blknr / rawsectsz);
44163a7b5a0SYifeng Zhao 	um_list->ub_desc.um_block_sz = cpu_to_be32((uint32_t)blkcnt / rawsectsz);
44263a7b5a0SYifeng Zhao 
44363a7b5a0SYifeng Zhao 	pccb->pdata = (void *)um_list;
44463a7b5a0SYifeng Zhao 	pccb->datalen = 24;
44563a7b5a0SYifeng Zhao 	pccb->dma_dir = DMA_TO_DEVICE;
44663a7b5a0SYifeng Zhao 	memset(pccb->cmd, 0, 10);
44763a7b5a0SYifeng Zhao 	pccb->cmd[0] = SCSI_UNMAP;
44863a7b5a0SYifeng Zhao 	pccb->cmd[8] = 24;
44963a7b5a0SYifeng Zhao 	pccb->cmdlen = 10;
45063a7b5a0SYifeng Zhao 
45163a7b5a0SYifeng Zhao 	if (scsi_exec(bdev, pccb)) {
45263a7b5a0SYifeng Zhao 		printf("erase UFS device error.\n");
45363a7b5a0SYifeng Zhao 		return 0;
45463a7b5a0SYifeng Zhao 	}
45563a7b5a0SYifeng Zhao 
45663a7b5a0SYifeng Zhao 	return blkcnt;
45763a7b5a0SYifeng Zhao }
45863a7b5a0SYifeng Zhao #endif
45952f5f55eSYifeng Zhao 
4607337fcd8SSimon Glass #if defined(CONFIG_PCI) && !defined(CONFIG_SCSI_AHCI_PLAT) && \
4617337fcd8SSimon Glass 	!defined(CONFIG_DM_SCSI)
scsi_init(void)4620fcd48feSSimon Glass void scsi_init(void)
4630fcd48feSSimon Glass {
4640fcd48feSSimon Glass 	int busdevfunc = -1;
4650fcd48feSSimon Glass 	int i;
4660fcd48feSSimon Glass 	/*
4670fcd48feSSimon Glass 	 * Find a device from the list, this driver will support a single
4680fcd48feSSimon Glass 	 * controller.
4690fcd48feSSimon Glass 	 */
4700fcd48feSSimon Glass 	for (i = 0; i < ARRAY_SIZE(scsi_device_list); i++) {
4710fcd48feSSimon Glass 		/* get PCI Device ID */
4720fcd48feSSimon Glass #ifdef CONFIG_DM_PCI
4730fcd48feSSimon Glass 		struct udevice *dev;
4740fcd48feSSimon Glass 		int ret;
4750fcd48feSSimon Glass 
4760fcd48feSSimon Glass 		ret = dm_pci_find_device(scsi_device_list[i].vendor,
4770fcd48feSSimon Glass 					 scsi_device_list[i].device, 0, &dev);
4780fcd48feSSimon Glass 		if (!ret) {
4790fcd48feSSimon Glass 			busdevfunc = dm_pci_get_bdf(dev);
4800fcd48feSSimon Glass 			break;
4810fcd48feSSimon Glass 		}
4820fcd48feSSimon Glass #else
4830fcd48feSSimon Glass 		busdevfunc = pci_find_device(scsi_device_list[i].vendor,
4840fcd48feSSimon Glass 					     scsi_device_list[i].device,
4850fcd48feSSimon Glass 					     0);
4860fcd48feSSimon Glass #endif
4870fcd48feSSimon Glass 		if (busdevfunc != -1)
4880fcd48feSSimon Glass 			break;
4890fcd48feSSimon Glass 	}
4900fcd48feSSimon Glass 
4910fcd48feSSimon Glass 	if (busdevfunc == -1) {
4920fcd48feSSimon Glass 		printf("Error: SCSI Controller(s) ");
4930fcd48feSSimon Glass 		for (i = 0; i < ARRAY_SIZE(scsi_device_list); i++) {
4940fcd48feSSimon Glass 			printf("%04X:%04X ",
4950fcd48feSSimon Glass 			       scsi_device_list[i].vendor,
4960fcd48feSSimon Glass 			       scsi_device_list[i].device);
4970fcd48feSSimon Glass 		}
4980fcd48feSSimon Glass 		printf("not found\n");
4990fcd48feSSimon Glass 		return;
5000fcd48feSSimon Glass 	}
5010fcd48feSSimon Glass #ifdef DEBUG
5020fcd48feSSimon Glass 	else {
5030fcd48feSSimon Glass 		printf("SCSI Controller (%04X,%04X) found (%d:%d:%d)\n",
5040fcd48feSSimon Glass 		       scsi_device_list[i].vendor,
5050fcd48feSSimon Glass 		       scsi_device_list[i].device,
5060fcd48feSSimon Glass 		       (busdevfunc >> 16) & 0xFF,
5070fcd48feSSimon Glass 		       (busdevfunc >> 11) & 0x1F,
5080fcd48feSSimon Glass 		       (busdevfunc >> 8) & 0x7);
5090fcd48feSSimon Glass 	}
5100fcd48feSSimon Glass #endif
5110fcd48feSSimon Glass 	bootstage_start(BOOTSTAGE_ID_ACCUM_SCSI, "ahci");
5120fcd48feSSimon Glass 	scsi_low_level_init(busdevfunc);
5138eab1a58SSimon Glass 	scsi_scan(true);
5140fcd48feSSimon Glass 	bootstage_accum(BOOTSTAGE_ID_ACCUM_SCSI);
5150fcd48feSSimon Glass }
5160fcd48feSSimon Glass #endif
5170fcd48feSSimon Glass 
5180fcd48feSSimon Glass /* copy src to dest, skipping leading and trailing blanks
5190fcd48feSSimon Glass  * and null terminate the string
5200fcd48feSSimon Glass  */
scsi_ident_cpy(unsigned char * dest,unsigned char * src,unsigned int len)5210fcd48feSSimon Glass static void scsi_ident_cpy(unsigned char *dest, unsigned char *src,
5220fcd48feSSimon Glass 			   unsigned int len)
5230fcd48feSSimon Glass {
5240fcd48feSSimon Glass 	int start, end;
5250fcd48feSSimon Glass 
5260fcd48feSSimon Glass 	start = 0;
5270fcd48feSSimon Glass 	while (start < len) {
5280fcd48feSSimon Glass 		if (src[start] != ' ')
5290fcd48feSSimon Glass 			break;
5300fcd48feSSimon Glass 		start++;
5310fcd48feSSimon Glass 	}
5320fcd48feSSimon Glass 	end = len-1;
5330fcd48feSSimon Glass 	while (end > start) {
5340fcd48feSSimon Glass 		if (src[end] != ' ')
5350fcd48feSSimon Glass 			break;
5360fcd48feSSimon Glass 		end--;
5370fcd48feSSimon Glass 	}
5380fcd48feSSimon Glass 	for (; start <= end; start++)
5390fcd48feSSimon Glass 		*dest ++= src[start];
5400fcd48feSSimon Glass 	*dest = '\0';
5410fcd48feSSimon Glass }
5420fcd48feSSimon Glass 
scsi_read_capacity(struct udevice * dev,struct scsi_cmd * pccb,lbaint_t * capacity,unsigned long * blksz)5434682c8a1SSimon Glass static int scsi_read_capacity(struct udevice *dev, struct scsi_cmd *pccb,
5444682c8a1SSimon Glass 			      lbaint_t *capacity, unsigned long *blksz)
5450fcd48feSSimon Glass {
5460fcd48feSSimon Glass 	*capacity = 0;
5470fcd48feSSimon Glass 
5480fcd48feSSimon Glass 	memset(pccb->cmd, '\0', sizeof(pccb->cmd));
5490fcd48feSSimon Glass 	pccb->cmd[0] = SCSI_RD_CAPAC10;
5500fcd48feSSimon Glass 	pccb->cmd[1] = pccb->lun << 5;
5510fcd48feSSimon Glass 	pccb->cmdlen = 10;
5520fcd48feSSimon Glass 	pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */
5530fcd48feSSimon Glass 
5540fcd48feSSimon Glass 	pccb->datalen = 8;
555f6580ef3SSimon Glass 	if (scsi_exec(dev, pccb))
5560fcd48feSSimon Glass 		return 1;
5570fcd48feSSimon Glass 
5580fcd48feSSimon Glass 	*capacity = ((lbaint_t)pccb->pdata[0] << 24) |
5590fcd48feSSimon Glass 		    ((lbaint_t)pccb->pdata[1] << 16) |
5600fcd48feSSimon Glass 		    ((lbaint_t)pccb->pdata[2] << 8)  |
5610fcd48feSSimon Glass 		    ((lbaint_t)pccb->pdata[3]);
5620fcd48feSSimon Glass 
5630fcd48feSSimon Glass 	if (*capacity != 0xffffffff) {
5640fcd48feSSimon Glass 		/* Read capacity (10) was sufficient for this drive. */
5650fcd48feSSimon Glass 		*blksz = ((unsigned long)pccb->pdata[4] << 24) |
5660fcd48feSSimon Glass 			 ((unsigned long)pccb->pdata[5] << 16) |
5670fcd48feSSimon Glass 			 ((unsigned long)pccb->pdata[6] << 8)  |
5680fcd48feSSimon Glass 			 ((unsigned long)pccb->pdata[7]);
5690fcd48feSSimon Glass 		return 0;
5700fcd48feSSimon Glass 	}
5710fcd48feSSimon Glass 
5720fcd48feSSimon Glass 	/* Read capacity (10) was insufficient. Use read capacity (16). */
5730fcd48feSSimon Glass 	memset(pccb->cmd, '\0', sizeof(pccb->cmd));
5740fcd48feSSimon Glass 	pccb->cmd[0] = SCSI_RD_CAPAC16;
5750fcd48feSSimon Glass 	pccb->cmd[1] = 0x10;
5760fcd48feSSimon Glass 	pccb->cmdlen = 16;
5770fcd48feSSimon Glass 	pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */
5780fcd48feSSimon Glass 
5790fcd48feSSimon Glass 	pccb->datalen = 16;
5808f7de514SShawn Lin 	pccb->dma_dir = DMA_FROM_DEVICE;
581f6580ef3SSimon Glass 	if (scsi_exec(dev, pccb))
5820fcd48feSSimon Glass 		return 1;
5830fcd48feSSimon Glass 
5840fcd48feSSimon Glass 	*capacity = ((uint64_t)pccb->pdata[0] << 56) |
5850fcd48feSSimon Glass 		    ((uint64_t)pccb->pdata[1] << 48) |
5860fcd48feSSimon Glass 		    ((uint64_t)pccb->pdata[2] << 40) |
5870fcd48feSSimon Glass 		    ((uint64_t)pccb->pdata[3] << 32) |
5880fcd48feSSimon Glass 		    ((uint64_t)pccb->pdata[4] << 24) |
5890fcd48feSSimon Glass 		    ((uint64_t)pccb->pdata[5] << 16) |
5900fcd48feSSimon Glass 		    ((uint64_t)pccb->pdata[6] << 8)  |
5910fcd48feSSimon Glass 		    ((uint64_t)pccb->pdata[7]);
5920fcd48feSSimon Glass 
5930fcd48feSSimon Glass 	*blksz = ((uint64_t)pccb->pdata[8]  << 56) |
5940fcd48feSSimon Glass 		 ((uint64_t)pccb->pdata[9]  << 48) |
5950fcd48feSSimon Glass 		 ((uint64_t)pccb->pdata[10] << 40) |
5960fcd48feSSimon Glass 		 ((uint64_t)pccb->pdata[11] << 32) |
5970fcd48feSSimon Glass 		 ((uint64_t)pccb->pdata[12] << 24) |
5980fcd48feSSimon Glass 		 ((uint64_t)pccb->pdata[13] << 16) |
5990fcd48feSSimon Glass 		 ((uint64_t)pccb->pdata[14] << 8)  |
6000fcd48feSSimon Glass 		 ((uint64_t)pccb->pdata[15]);
6010fcd48feSSimon Glass 
6020fcd48feSSimon Glass 	return 0;
6030fcd48feSSimon Glass }
6040fcd48feSSimon Glass 
6050fcd48feSSimon Glass /**
6060fcd48feSSimon Glass  * scsi_init_dev_desc_priv - initialize only SCSI specific blk_desc properties
6070fcd48feSSimon Glass  *
6080fcd48feSSimon Glass  * @dev_desc: Block device description pointer
6090fcd48feSSimon Glass  */
scsi_init_dev_desc_priv(struct blk_desc * dev_desc)6100fcd48feSSimon Glass static void scsi_init_dev_desc_priv(struct blk_desc *dev_desc)
6110fcd48feSSimon Glass {
6120fcd48feSSimon Glass 	dev_desc->target = 0xff;
6130fcd48feSSimon Glass 	dev_desc->lun = 0xff;
6140fcd48feSSimon Glass 	dev_desc->log2blksz =
6150fcd48feSSimon Glass 		LOG2_INVALID(typeof(dev_desc->log2blksz));
6160fcd48feSSimon Glass 	dev_desc->type = DEV_TYPE_UNKNOWN;
6170fcd48feSSimon Glass 	dev_desc->vendor[0] = 0;
6180fcd48feSSimon Glass 	dev_desc->product[0] = 0;
6190fcd48feSSimon Glass 	dev_desc->revision[0] = 0;
6200fcd48feSSimon Glass 	dev_desc->removable = false;
621c4d660d4SSimon Glass #if !CONFIG_IS_ENABLED(BLK)
6220fcd48feSSimon Glass 	dev_desc->block_read = scsi_read;
6230fcd48feSSimon Glass 	dev_desc->block_write = scsi_write;
6240fcd48feSSimon Glass #endif
6250fcd48feSSimon Glass }
6260fcd48feSSimon Glass 
6270fcd48feSSimon Glass #if !defined(CONFIG_DM_SCSI)
6280fcd48feSSimon Glass /**
6290fcd48feSSimon Glass  * scsi_init_dev_desc - initialize all SCSI specific blk_desc properties
6300fcd48feSSimon Glass  *
6310fcd48feSSimon Glass  * @dev_desc: Block device description pointer
6320fcd48feSSimon Glass  * @devnum: Device number
6330fcd48feSSimon Glass  */
scsi_init_dev_desc(struct blk_desc * dev_desc,int devnum)6340fcd48feSSimon Glass static void scsi_init_dev_desc(struct blk_desc *dev_desc, int devnum)
6350fcd48feSSimon Glass {
6360fcd48feSSimon Glass 	dev_desc->lba = 0;
6370fcd48feSSimon Glass 	dev_desc->blksz = 0;
6380fcd48feSSimon Glass 	dev_desc->if_type = IF_TYPE_SCSI;
6390fcd48feSSimon Glass 	dev_desc->devnum = devnum;
6400fcd48feSSimon Glass 	dev_desc->part_type = PART_TYPE_UNKNOWN;
6410fcd48feSSimon Glass 
6420fcd48feSSimon Glass 	scsi_init_dev_desc_priv(dev_desc);
6430fcd48feSSimon Glass }
6440fcd48feSSimon Glass #endif
6450fcd48feSSimon Glass 
6460fcd48feSSimon Glass /**
6470fcd48feSSimon Glass  * scsi_detect_dev - Detect scsi device
6480fcd48feSSimon Glass  *
6490fcd48feSSimon Glass  * @target: target id
6500fcd48feSSimon Glass  * @lun: target lun
6510fcd48feSSimon Glass  * @dev_desc: block device description
6520fcd48feSSimon Glass  *
6530fcd48feSSimon Glass  * The scsi_detect_dev detects and fills a dev_desc structure when the device is
6540fcd48feSSimon Glass  * detected.
6550fcd48feSSimon Glass  *
6560fcd48feSSimon Glass  * Return: 0 on success, error value otherwise
6570fcd48feSSimon Glass  */
scsi_detect_dev(struct udevice * dev,int target,int lun,struct blk_desc * dev_desc)6584682c8a1SSimon Glass static int scsi_detect_dev(struct udevice *dev, int target, int lun,
6594682c8a1SSimon Glass 			   struct blk_desc *dev_desc)
6600fcd48feSSimon Glass {
6610fcd48feSSimon Glass 	unsigned char perq, modi;
6620fcd48feSSimon Glass 	lbaint_t capacity;
6630fcd48feSSimon Glass 	unsigned long blksz;
664b9560ad6SSimon Glass 	struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb;
6650fcd48feSSimon Glass 
6660fcd48feSSimon Glass 	pccb->target = target;
6670fcd48feSSimon Glass 	pccb->lun = lun;
6680fcd48feSSimon Glass 	pccb->pdata = (unsigned char *)&tempbuff;
66955d11a71SYifeng Zhao 	pccb->datalen = SCSI_STD_INQUIRY_BYTES;
6708f7de514SShawn Lin 	pccb->dma_dir = DMA_FROM_DEVICE;
6710fcd48feSSimon Glass 	scsi_setup_inquiry(pccb);
672f6580ef3SSimon Glass 	if (scsi_exec(dev, pccb)) {
6730fcd48feSSimon Glass 		if (pccb->contr_stat == SCSI_SEL_TIME_OUT) {
6740fcd48feSSimon Glass 			/*
6750fcd48feSSimon Glass 			  * selection timeout => assuming no
6760fcd48feSSimon Glass 			  * device present
6770fcd48feSSimon Glass 			  */
6780fcd48feSSimon Glass 			debug("Selection timeout ID %d\n",
6790fcd48feSSimon Glass 			      pccb->target);
6800fcd48feSSimon Glass 			return -ETIMEDOUT;
6810fcd48feSSimon Glass 		}
6820fcd48feSSimon Glass 		scsi_print_error(pccb);
6830fcd48feSSimon Glass 		return -ENODEV;
6840fcd48feSSimon Glass 	}
6850fcd48feSSimon Glass 	perq = tempbuff[0];
6860fcd48feSSimon Glass 	modi = tempbuff[1];
6870fcd48feSSimon Glass 	if ((perq & 0x1f) == 0x1f)
6880fcd48feSSimon Glass 		return -ENODEV; /* skip unknown devices */
6890fcd48feSSimon Glass 	if ((modi & 0x80) == 0x80) /* drive is removable */
6900fcd48feSSimon Glass 		dev_desc->removable = true;
6910fcd48feSSimon Glass 	/* get info for this device */
6920fcd48feSSimon Glass 	scsi_ident_cpy((unsigned char *)dev_desc->vendor,
6930fcd48feSSimon Glass 		       &tempbuff[8], 8);
6940fcd48feSSimon Glass 	scsi_ident_cpy((unsigned char *)dev_desc->product,
6950fcd48feSSimon Glass 		       &tempbuff[16], 16);
6960fcd48feSSimon Glass 	scsi_ident_cpy((unsigned char *)dev_desc->revision,
6970fcd48feSSimon Glass 		       &tempbuff[32], 4);
6980fcd48feSSimon Glass 	dev_desc->target = pccb->target;
6990fcd48feSSimon Glass 	dev_desc->lun = pccb->lun;
7000fcd48feSSimon Glass 
7010fcd48feSSimon Glass 	pccb->datalen = 0;
7020fcd48feSSimon Glass 	scsi_setup_test_unit_ready(pccb);
703f6580ef3SSimon Glass 	if (scsi_exec(dev, pccb)) {
7040fcd48feSSimon Glass 		if (dev_desc->removable) {
7050fcd48feSSimon Glass 			dev_desc->type = perq;
7060fcd48feSSimon Glass 			goto removable;
7070fcd48feSSimon Glass 		}
7080fcd48feSSimon Glass 		scsi_print_error(pccb);
7090fcd48feSSimon Glass 		return -EINVAL;
7100fcd48feSSimon Glass 	}
7114682c8a1SSimon Glass 	if (scsi_read_capacity(dev, pccb, &capacity, &blksz)) {
7120fcd48feSSimon Glass 		scsi_print_error(pccb);
7130fcd48feSSimon Glass 		return -EINVAL;
7140fcd48feSSimon Glass 	}
71552f5f55eSYifeng Zhao 
7160fcd48feSSimon Glass 	dev_desc->lba = capacity;
7170fcd48feSSimon Glass 	dev_desc->blksz = blksz;
7180fcd48feSSimon Glass 	dev_desc->log2blksz = LOG2(dev_desc->blksz);
7190fcd48feSSimon Glass 	dev_desc->type = perq;
7200fcd48feSSimon Glass removable:
7210fcd48feSSimon Glass 	return 0;
7220fcd48feSSimon Glass }
7230fcd48feSSimon Glass 
7240fcd48feSSimon Glass /*
7250fcd48feSSimon Glass  * (re)-scan the scsi bus and reports scsi device info
7260fcd48feSSimon Glass  * to the user if mode = 1
7270fcd48feSSimon Glass  */
7280fcd48feSSimon Glass #if defined(CONFIG_DM_SCSI)
do_scsi_scan_one(struct udevice * dev,int id,int lun,bool verbose)7298eab1a58SSimon Glass static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose)
7300fcd48feSSimon Glass {
7310fcd48feSSimon Glass 	int ret;
7320fcd48feSSimon Glass 	struct udevice *bdev;
7330fcd48feSSimon Glass 	struct blk_desc bd;
7340fcd48feSSimon Glass 	struct blk_desc *bdesc;
7350fcd48feSSimon Glass 	char str[10];
7360fcd48feSSimon Glass 
7370fcd48feSSimon Glass 	/*
7380fcd48feSSimon Glass 	 * detect the scsi driver to get information about its geometry (block
7390fcd48feSSimon Glass 	 * size, number of blocks) and other parameters (ids, type, ...)
7400fcd48feSSimon Glass 	 */
7410fcd48feSSimon Glass 	scsi_init_dev_desc_priv(&bd);
7424682c8a1SSimon Glass 	if (scsi_detect_dev(dev, id, lun, &bd))
7430fcd48feSSimon Glass 		return -ENODEV;
7440fcd48feSSimon Glass 
7450fcd48feSSimon Glass 	/*
7460fcd48feSSimon Glass 	* Create only one block device and do detection
7470fcd48feSSimon Glass 	* to make sure that there won't be a lot of
7480fcd48feSSimon Glass 	* block devices created
7490fcd48feSSimon Glass 	*/
7500fcd48feSSimon Glass 	snprintf(str, sizeof(str), "id%dlun%d", id, lun);
7510fcd48feSSimon Glass 	ret = blk_create_devicef(dev, "scsi_blk", str, IF_TYPE_SCSI, -1,
7520fcd48feSSimon Glass 			bd.blksz, bd.blksz * bd.lba, &bdev);
7530fcd48feSSimon Glass 	if (ret) {
7540fcd48feSSimon Glass 		debug("Can't create device\n");
7550fcd48feSSimon Glass 		return ret;
7560fcd48feSSimon Glass 	}
7570fcd48feSSimon Glass 
7580fcd48feSSimon Glass 	bdesc = dev_get_uclass_platdata(bdev);
7590fcd48feSSimon Glass 	bdesc->target = id;
7600fcd48feSSimon Glass 	bdesc->lun = lun;
7610fcd48feSSimon Glass 	bdesc->removable = bd.removable;
7620fcd48feSSimon Glass 	bdesc->type = bd.type;
76352f5f55eSYifeng Zhao 
76452f5f55eSYifeng Zhao 	if (bdesc->rawblksz == 4096) {
76552f5f55eSYifeng Zhao 		bdesc->blksz = 512;
76632d06e10SYifeng Zhao 		bdesc->rawlba++; /* add 1 sector for ufs */
76752f5f55eSYifeng Zhao 		bdesc->lba = bdesc->rawlba * 8;
76852f5f55eSYifeng Zhao 		bdesc->align_sector_buf = memalign(CONFIG_SYS_CACHELINE_SIZE, bdesc->rawblksz);
76952f5f55eSYifeng Zhao 	}
77052f5f55eSYifeng Zhao 
7710fcd48feSSimon Glass 	memcpy(&bdesc->vendor, &bd.vendor, sizeof(bd.vendor));
7720fcd48feSSimon Glass 	memcpy(&bdesc->product, &bd.product, sizeof(bd.product));
7730fcd48feSSimon Glass 	memcpy(&bdesc->revision, &bd.revision,	sizeof(bd.revision));
7740fcd48feSSimon Glass 	part_init(bdesc);
7750fcd48feSSimon Glass 
7768eab1a58SSimon Glass 	if (verbose) {
7770fcd48feSSimon Glass 		printf("  Device %d: ", 0);
7780fcd48feSSimon Glass 		dev_print(bdesc);
7790fcd48feSSimon Glass 	}
7800fcd48feSSimon Glass 	return 0;
7810fcd48feSSimon Glass }
7820fcd48feSSimon Glass 
scsi_scan_dev(struct udevice * dev,bool verbose)7835c561763SSimon Glass int scsi_scan_dev(struct udevice *dev, bool verbose)
7845c561763SSimon Glass {
7855c561763SSimon Glass 	struct scsi_platdata *uc_plat; /* scsi controller platdata */
7865c561763SSimon Glass 	int ret;
7875c561763SSimon Glass 	int i;
7885c561763SSimon Glass 	int lun;
7895c561763SSimon Glass 
7905c561763SSimon Glass 	/* probe SCSI controller driver */
7915c561763SSimon Glass 	ret = device_probe(dev);
7925c561763SSimon Glass 	if (ret)
7935c561763SSimon Glass 		return ret;
7945c561763SSimon Glass 
7955c561763SSimon Glass 	/* Get controller platdata */
7965c561763SSimon Glass 	uc_plat = dev_get_uclass_platdata(dev);
7975c561763SSimon Glass 
7985c561763SSimon Glass 	for (i = 0; i < uc_plat->max_id; i++)
7995c561763SSimon Glass 		for (lun = 0; lun < uc_plat->max_lun; lun++)
8005c561763SSimon Glass 			do_scsi_scan_one(dev, i, lun, verbose);
8015c561763SSimon Glass 
8025c561763SSimon Glass 	return 0;
8035c561763SSimon Glass }
8045c561763SSimon Glass 
scsi_scan(bool verbose)8058eab1a58SSimon Glass int scsi_scan(bool verbose)
8060fcd48feSSimon Glass {
8070fcd48feSSimon Glass 	struct uclass *uc;
8080fcd48feSSimon Glass 	struct udevice *dev; /* SCSI controller */
8090fcd48feSSimon Glass 	int ret;
8100fcd48feSSimon Glass 
8118eab1a58SSimon Glass 	if (verbose)
8120fcd48feSSimon Glass 		printf("scanning bus for devices...\n");
8130fcd48feSSimon Glass 
8140fcd48feSSimon Glass 	blk_unbind_all(IF_TYPE_SCSI);
8150fcd48feSSimon Glass 
8160fcd48feSSimon Glass 	ret = uclass_get(UCLASS_SCSI, &uc);
8170fcd48feSSimon Glass 	if (ret)
8180fcd48feSSimon Glass 		return ret;
8190fcd48feSSimon Glass 
8200fcd48feSSimon Glass 	uclass_foreach_dev(dev, uc) {
8215c561763SSimon Glass 		ret = scsi_scan_dev(dev, verbose);
8220fcd48feSSimon Glass 		if (ret)
8230fcd48feSSimon Glass 			return ret;
8240fcd48feSSimon Glass 	}
8250fcd48feSSimon Glass 
8260fcd48feSSimon Glass 	return 0;
8270fcd48feSSimon Glass }
8280fcd48feSSimon Glass #else
scsi_scan(bool verbose)8298eab1a58SSimon Glass int scsi_scan(bool verbose)
8300fcd48feSSimon Glass {
8310fcd48feSSimon Glass 	unsigned char i, lun;
8320fcd48feSSimon Glass 	int ret;
8330fcd48feSSimon Glass 
8348eab1a58SSimon Glass 	if (verbose)
8350fcd48feSSimon Glass 		printf("scanning bus for devices...\n");
8360fcd48feSSimon Glass 	for (i = 0; i < CONFIG_SYS_SCSI_MAX_DEVICE; i++)
8370fcd48feSSimon Glass 		scsi_init_dev_desc(&scsi_dev_desc[i], i);
8380fcd48feSSimon Glass 
8390fcd48feSSimon Glass 	scsi_max_devs = 0;
8400fcd48feSSimon Glass 	for (i = 0; i < CONFIG_SYS_SCSI_MAX_SCSI_ID; i++) {
8410fcd48feSSimon Glass 		for (lun = 0; lun < CONFIG_SYS_SCSI_MAX_LUN; lun++) {
8424682c8a1SSimon Glass 			ret = scsi_detect_dev(NULL, i, lun,
8430fcd48feSSimon Glass 					      &scsi_dev_desc[scsi_max_devs]);
8440fcd48feSSimon Glass 			if (ret)
8450fcd48feSSimon Glass 				continue;
8460fcd48feSSimon Glass 			part_init(&scsi_dev_desc[scsi_max_devs]);
8470fcd48feSSimon Glass 
8488eab1a58SSimon Glass 			if (verbose) {
8490fcd48feSSimon Glass 				printf("  Device %d: ", 0);
8500fcd48feSSimon Glass 				dev_print(&scsi_dev_desc[scsi_max_devs]);
8518eab1a58SSimon Glass 			}
8520fcd48feSSimon Glass 			scsi_max_devs++;
8530fcd48feSSimon Glass 		} /* next LUN */
8540fcd48feSSimon Glass 	}
8550fcd48feSSimon Glass 	if (scsi_max_devs > 0)
8560fcd48feSSimon Glass 		scsi_curr_dev = 0;
8570fcd48feSSimon Glass 	else
8580fcd48feSSimon Glass 		scsi_curr_dev = -1;
8590fcd48feSSimon Glass 
8600fcd48feSSimon Glass 	printf("Found %d device(s).\n", scsi_max_devs);
8610fcd48feSSimon Glass #ifndef CONFIG_SPL_BUILD
862018f5303SSimon Glass 	env_set_ulong("scsidevs", scsi_max_devs);
8630fcd48feSSimon Glass #endif
8640fcd48feSSimon Glass 	return 0;
8650fcd48feSSimon Glass }
8660fcd48feSSimon Glass #endif
8670fcd48feSSimon Glass 
8680fcd48feSSimon Glass #ifdef CONFIG_BLK
8690fcd48feSSimon Glass static const struct blk_ops scsi_blk_ops = {
8700fcd48feSSimon Glass 	.read	= scsi_read,
8710fcd48feSSimon Glass 	.write	= scsi_write,
87263a7b5a0SYifeng Zhao 	.erase	= scsi_erase,
8730fcd48feSSimon Glass };
8740fcd48feSSimon Glass 
8750fcd48feSSimon Glass U_BOOT_DRIVER(scsi_blk) = {
8760fcd48feSSimon Glass 	.name		= "scsi_blk",
8770fcd48feSSimon Glass 	.id		= UCLASS_BLK,
8780fcd48feSSimon Glass 	.ops		= &scsi_blk_ops,
8790fcd48feSSimon Glass };
8800fcd48feSSimon Glass #else
8810fcd48feSSimon Glass U_BOOT_LEGACY_BLK(scsi) = {
8820fcd48feSSimon Glass 	.if_typename	= "scsi",
8830fcd48feSSimon Glass 	.if_type	= IF_TYPE_SCSI,
8840fcd48feSSimon Glass 	.max_devs	= CONFIG_SYS_SCSI_MAX_DEVICE,
8850fcd48feSSimon Glass 	.desc		= scsi_dev_desc,
8860fcd48feSSimon Glass };
8870fcd48feSSimon Glass #endif
888