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> 110fcd48feSSimon Glass #include <pci.h> 120fcd48feSSimon Glass #include <scsi.h> 130fcd48feSSimon Glass #include <dm/device-internal.h> 140fcd48feSSimon Glass #include <dm/uclass-internal.h> 150fcd48feSSimon Glass 160fcd48feSSimon Glass #if !defined(CONFIG_DM_SCSI) 170fcd48feSSimon Glass # ifdef CONFIG_SCSI_DEV_LIST 180fcd48feSSimon Glass # define SCSI_DEV_LIST CONFIG_SCSI_DEV_LIST 190fcd48feSSimon Glass # else 200fcd48feSSimon Glass # ifdef CONFIG_SATA_ULI5288 210fcd48feSSimon Glass 220fcd48feSSimon Glass # define SCSI_VEND_ID 0x10b9 230fcd48feSSimon Glass # define SCSI_DEV_ID 0x5288 240fcd48feSSimon Glass 250fcd48feSSimon Glass # elif !defined(CONFIG_SCSI_AHCI_PLAT) 260fcd48feSSimon Glass # error no scsi device defined 270fcd48feSSimon Glass # endif 280fcd48feSSimon Glass # define SCSI_DEV_LIST {SCSI_VEND_ID, SCSI_DEV_ID} 290fcd48feSSimon Glass # endif 300fcd48feSSimon Glass #endif 310fcd48feSSimon Glass 327337fcd8SSimon Glass #if defined(CONFIG_PCI) && !defined(CONFIG_SCSI_AHCI_PLAT) && \ 337337fcd8SSimon Glass !defined(CONFIG_DM_SCSI) 340fcd48feSSimon Glass const struct pci_device_id scsi_device_list[] = { SCSI_DEV_LIST }; 350fcd48feSSimon Glass #endif 36b9560ad6SSimon Glass static struct scsi_cmd tempccb; /* temporary scsi command buffer */ 370fcd48feSSimon Glass 380fcd48feSSimon Glass static unsigned char tempbuff[512]; /* temporary data buffer */ 390fcd48feSSimon Glass 400fcd48feSSimon Glass #if !defined(CONFIG_DM_SCSI) 410fcd48feSSimon Glass static int scsi_max_devs; /* number of highest available scsi device */ 420fcd48feSSimon Glass 430fcd48feSSimon Glass static int scsi_curr_dev; /* current device */ 440fcd48feSSimon Glass 450fcd48feSSimon Glass static struct blk_desc scsi_dev_desc[CONFIG_SYS_SCSI_MAX_DEVICE]; 460fcd48feSSimon Glass #endif 470fcd48feSSimon Glass 480fcd48feSSimon Glass /* almost the maximum amount of the scsi_ext command.. */ 490fcd48feSSimon Glass #define SCSI_MAX_READ_BLK 0xFFFF 500fcd48feSSimon Glass #define SCSI_LBA48_READ 0xFFFFFFF 510fcd48feSSimon Glass 52b9560ad6SSimon Glass static void scsi_print_error(struct scsi_cmd *pccb) 530fcd48feSSimon Glass { 540fcd48feSSimon Glass /* Dummy function that could print an error for debugging */ 550fcd48feSSimon Glass } 560fcd48feSSimon Glass 570fcd48feSSimon Glass #ifdef CONFIG_SYS_64BIT_LBA 58b9560ad6SSimon Glass void scsi_setup_read16(struct scsi_cmd *pccb, lbaint_t start, 59b9560ad6SSimon Glass unsigned long blocks) 600fcd48feSSimon Glass { 610fcd48feSSimon Glass pccb->cmd[0] = SCSI_READ16; 620fcd48feSSimon Glass pccb->cmd[1] = pccb->lun << 5; 630fcd48feSSimon Glass pccb->cmd[2] = (unsigned char)(start >> 56) & 0xff; 640fcd48feSSimon Glass pccb->cmd[3] = (unsigned char)(start >> 48) & 0xff; 650fcd48feSSimon Glass pccb->cmd[4] = (unsigned char)(start >> 40) & 0xff; 660fcd48feSSimon Glass pccb->cmd[5] = (unsigned char)(start >> 32) & 0xff; 670fcd48feSSimon Glass pccb->cmd[6] = (unsigned char)(start >> 24) & 0xff; 680fcd48feSSimon Glass pccb->cmd[7] = (unsigned char)(start >> 16) & 0xff; 690fcd48feSSimon Glass pccb->cmd[8] = (unsigned char)(start >> 8) & 0xff; 700fcd48feSSimon Glass pccb->cmd[9] = (unsigned char)start & 0xff; 710fcd48feSSimon Glass pccb->cmd[10] = 0; 720fcd48feSSimon Glass pccb->cmd[11] = (unsigned char)(blocks >> 24) & 0xff; 730fcd48feSSimon Glass pccb->cmd[12] = (unsigned char)(blocks >> 16) & 0xff; 740fcd48feSSimon Glass pccb->cmd[13] = (unsigned char)(blocks >> 8) & 0xff; 750fcd48feSSimon Glass pccb->cmd[14] = (unsigned char)blocks & 0xff; 760fcd48feSSimon Glass pccb->cmd[15] = 0; 770fcd48feSSimon Glass pccb->cmdlen = 16; 780fcd48feSSimon Glass pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ 790fcd48feSSimon Glass debug("scsi_setup_read16: cmd: %02X %02X startblk %02X%02X%02X%02X%02X%02X%02X%02X blccnt %02X%02X%02X%02X\n", 800fcd48feSSimon Glass pccb->cmd[0], pccb->cmd[1], 810fcd48feSSimon Glass pccb->cmd[2], pccb->cmd[3], pccb->cmd[4], pccb->cmd[5], 820fcd48feSSimon Glass pccb->cmd[6], pccb->cmd[7], pccb->cmd[8], pccb->cmd[9], 830fcd48feSSimon Glass pccb->cmd[11], pccb->cmd[12], pccb->cmd[13], pccb->cmd[14]); 840fcd48feSSimon Glass } 850fcd48feSSimon Glass #endif 860fcd48feSSimon Glass 87b9560ad6SSimon Glass static void scsi_setup_read_ext(struct scsi_cmd *pccb, lbaint_t start, 880fcd48feSSimon Glass unsigned short blocks) 890fcd48feSSimon Glass { 900fcd48feSSimon Glass pccb->cmd[0] = SCSI_READ10; 910fcd48feSSimon Glass pccb->cmd[1] = pccb->lun << 5; 920fcd48feSSimon Glass pccb->cmd[2] = (unsigned char)(start >> 24) & 0xff; 930fcd48feSSimon Glass pccb->cmd[3] = (unsigned char)(start >> 16) & 0xff; 940fcd48feSSimon Glass pccb->cmd[4] = (unsigned char)(start >> 8) & 0xff; 950fcd48feSSimon Glass pccb->cmd[5] = (unsigned char)start & 0xff; 960fcd48feSSimon Glass pccb->cmd[6] = 0; 970fcd48feSSimon Glass pccb->cmd[7] = (unsigned char)(blocks >> 8) & 0xff; 980fcd48feSSimon Glass pccb->cmd[8] = (unsigned char)blocks & 0xff; 990fcd48feSSimon Glass pccb->cmd[6] = 0; 1000fcd48feSSimon Glass pccb->cmdlen = 10; 1010fcd48feSSimon Glass pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ 1020fcd48feSSimon Glass debug("scsi_setup_read_ext: cmd: %02X %02X startblk %02X%02X%02X%02X blccnt %02X%02X\n", 1030fcd48feSSimon Glass pccb->cmd[0], pccb->cmd[1], 1040fcd48feSSimon Glass pccb->cmd[2], pccb->cmd[3], pccb->cmd[4], pccb->cmd[5], 1050fcd48feSSimon Glass pccb->cmd[7], pccb->cmd[8]); 1060fcd48feSSimon Glass } 1070fcd48feSSimon Glass 108b9560ad6SSimon Glass static void scsi_setup_write_ext(struct scsi_cmd *pccb, lbaint_t start, 1090fcd48feSSimon Glass unsigned short blocks) 1100fcd48feSSimon Glass { 1110fcd48feSSimon Glass pccb->cmd[0] = SCSI_WRITE10; 1120fcd48feSSimon Glass pccb->cmd[1] = pccb->lun << 5; 1130fcd48feSSimon Glass pccb->cmd[2] = (unsigned char)(start >> 24) & 0xff; 1140fcd48feSSimon Glass pccb->cmd[3] = (unsigned char)(start >> 16) & 0xff; 1150fcd48feSSimon Glass pccb->cmd[4] = (unsigned char)(start >> 8) & 0xff; 1160fcd48feSSimon Glass pccb->cmd[5] = (unsigned char)start & 0xff; 1170fcd48feSSimon Glass pccb->cmd[6] = 0; 1180fcd48feSSimon Glass pccb->cmd[7] = ((unsigned char)(blocks >> 8)) & 0xff; 1190fcd48feSSimon Glass pccb->cmd[8] = (unsigned char)blocks & 0xff; 1200fcd48feSSimon Glass pccb->cmd[9] = 0; 1210fcd48feSSimon Glass pccb->cmdlen = 10; 1220fcd48feSSimon Glass pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ 1230fcd48feSSimon Glass debug("%s: cmd: %02X %02X startblk %02X%02X%02X%02X blccnt %02X%02X\n", 1240fcd48feSSimon Glass __func__, 1250fcd48feSSimon Glass pccb->cmd[0], pccb->cmd[1], 1260fcd48feSSimon Glass pccb->cmd[2], pccb->cmd[3], pccb->cmd[4], pccb->cmd[5], 1270fcd48feSSimon Glass pccb->cmd[7], pccb->cmd[8]); 1280fcd48feSSimon Glass } 1290fcd48feSSimon Glass 130b9560ad6SSimon Glass static void scsi_setup_inquiry(struct scsi_cmd *pccb) 1310fcd48feSSimon Glass { 1320fcd48feSSimon Glass pccb->cmd[0] = SCSI_INQUIRY; 1330fcd48feSSimon Glass pccb->cmd[1] = pccb->lun << 5; 1340fcd48feSSimon Glass pccb->cmd[2] = 0; 1350fcd48feSSimon Glass pccb->cmd[3] = 0; 1360fcd48feSSimon Glass if (pccb->datalen > 255) 1370fcd48feSSimon Glass pccb->cmd[4] = 255; 1380fcd48feSSimon Glass else 1390fcd48feSSimon Glass pccb->cmd[4] = (unsigned char)pccb->datalen; 1400fcd48feSSimon Glass pccb->cmd[5] = 0; 1410fcd48feSSimon Glass pccb->cmdlen = 6; 1420fcd48feSSimon Glass pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ 1430fcd48feSSimon Glass } 1440fcd48feSSimon Glass 1450fcd48feSSimon Glass #ifdef CONFIG_BLK 1460fcd48feSSimon Glass static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, 1470fcd48feSSimon Glass void *buffer) 1480fcd48feSSimon Glass #else 1490fcd48feSSimon Glass static ulong scsi_read(struct blk_desc *block_dev, lbaint_t blknr, 1500fcd48feSSimon Glass lbaint_t blkcnt, void *buffer) 1510fcd48feSSimon Glass #endif 1520fcd48feSSimon Glass { 1530fcd48feSSimon Glass #ifdef CONFIG_BLK 1540fcd48feSSimon Glass struct blk_desc *block_dev = dev_get_uclass_platdata(dev); 1554682c8a1SSimon Glass struct udevice *bdev = dev->parent; 1564682c8a1SSimon Glass #else 1574682c8a1SSimon Glass struct udevice *bdev = NULL; 1580fcd48feSSimon Glass #endif 1590fcd48feSSimon Glass lbaint_t start, blks; 1600fcd48feSSimon Glass uintptr_t buf_addr; 1610fcd48feSSimon Glass unsigned short smallblks = 0; 162b9560ad6SSimon Glass struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb; 1630fcd48feSSimon Glass 1640fcd48feSSimon Glass /* Setup device */ 1650fcd48feSSimon Glass pccb->target = block_dev->target; 1660fcd48feSSimon Glass pccb->lun = block_dev->lun; 1670fcd48feSSimon Glass buf_addr = (unsigned long)buffer; 1680fcd48feSSimon Glass start = blknr; 1690fcd48feSSimon Glass blks = blkcnt; 1700fcd48feSSimon Glass debug("\nscsi_read: dev %d startblk " LBAF 1710fcd48feSSimon Glass ", blccnt " LBAF " buffer %lx\n", 1720fcd48feSSimon Glass block_dev->devnum, start, blks, (unsigned long)buffer); 1730fcd48feSSimon Glass do { 1740fcd48feSSimon Glass pccb->pdata = (unsigned char *)buf_addr; 175*8f7de514SShawn Lin pccb->dma_dir = DMA_FROM_DEVICE; 1760fcd48feSSimon Glass #ifdef CONFIG_SYS_64BIT_LBA 1770fcd48feSSimon Glass if (start > SCSI_LBA48_READ) { 1780fcd48feSSimon Glass unsigned long blocks; 1790fcd48feSSimon Glass blocks = min_t(lbaint_t, blks, SCSI_MAX_READ_BLK); 1800fcd48feSSimon Glass pccb->datalen = block_dev->blksz * blocks; 1810fcd48feSSimon Glass scsi_setup_read16(pccb, start, blocks); 1820fcd48feSSimon Glass start += blocks; 1830fcd48feSSimon Glass blks -= blocks; 1840fcd48feSSimon Glass } else 1850fcd48feSSimon Glass #endif 1860fcd48feSSimon Glass if (blks > SCSI_MAX_READ_BLK) { 1870fcd48feSSimon Glass pccb->datalen = block_dev->blksz * 1880fcd48feSSimon Glass SCSI_MAX_READ_BLK; 1890fcd48feSSimon Glass smallblks = SCSI_MAX_READ_BLK; 1900fcd48feSSimon Glass scsi_setup_read_ext(pccb, start, smallblks); 1910fcd48feSSimon Glass start += SCSI_MAX_READ_BLK; 1920fcd48feSSimon Glass blks -= SCSI_MAX_READ_BLK; 1930fcd48feSSimon Glass } else { 1940fcd48feSSimon Glass pccb->datalen = block_dev->blksz * blks; 1950fcd48feSSimon Glass smallblks = (unsigned short)blks; 1960fcd48feSSimon Glass scsi_setup_read_ext(pccb, start, smallblks); 1970fcd48feSSimon Glass start += blks; 1980fcd48feSSimon Glass blks = 0; 1990fcd48feSSimon Glass } 2000fcd48feSSimon Glass debug("scsi_read_ext: startblk " LBAF 2010fcd48feSSimon Glass ", blccnt %x buffer %" PRIXPTR "\n", 2020fcd48feSSimon Glass start, smallblks, buf_addr); 2034682c8a1SSimon Glass if (scsi_exec(bdev, pccb)) { 2040fcd48feSSimon Glass scsi_print_error(pccb); 2050fcd48feSSimon Glass blkcnt -= blks; 2060fcd48feSSimon Glass break; 2070fcd48feSSimon Glass } 2080fcd48feSSimon Glass buf_addr += pccb->datalen; 2090fcd48feSSimon Glass } while (blks != 0); 2100fcd48feSSimon Glass debug("scsi_read_ext: end startblk " LBAF 2110fcd48feSSimon Glass ", blccnt %x buffer %" PRIXPTR "\n", start, smallblks, buf_addr); 2120fcd48feSSimon Glass return blkcnt; 2130fcd48feSSimon Glass } 2140fcd48feSSimon Glass 2150fcd48feSSimon Glass /******************************************************************************* 2160fcd48feSSimon Glass * scsi_write 2170fcd48feSSimon Glass */ 2180fcd48feSSimon Glass 2190fcd48feSSimon Glass /* Almost the maximum amount of the scsi_ext command.. */ 2200fcd48feSSimon Glass #define SCSI_MAX_WRITE_BLK 0xFFFF 2210fcd48feSSimon Glass 2220fcd48feSSimon Glass #ifdef CONFIG_BLK 2230fcd48feSSimon Glass static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, 2240fcd48feSSimon Glass const void *buffer) 2250fcd48feSSimon Glass #else 2260fcd48feSSimon Glass static ulong scsi_write(struct blk_desc *block_dev, lbaint_t blknr, 2270fcd48feSSimon Glass lbaint_t blkcnt, const void *buffer) 2280fcd48feSSimon Glass #endif 2290fcd48feSSimon Glass { 2300fcd48feSSimon Glass #ifdef CONFIG_BLK 2310fcd48feSSimon Glass struct blk_desc *block_dev = dev_get_uclass_platdata(dev); 2324682c8a1SSimon Glass struct udevice *bdev = dev->parent; 2334682c8a1SSimon Glass #else 2344682c8a1SSimon Glass struct udevice *bdev = NULL; 2350fcd48feSSimon Glass #endif 2360fcd48feSSimon Glass lbaint_t start, blks; 2370fcd48feSSimon Glass uintptr_t buf_addr; 2380fcd48feSSimon Glass unsigned short smallblks; 239b9560ad6SSimon Glass struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb; 2400fcd48feSSimon Glass 2410fcd48feSSimon Glass /* Setup device */ 2420fcd48feSSimon Glass pccb->target = block_dev->target; 2430fcd48feSSimon Glass pccb->lun = block_dev->lun; 2440fcd48feSSimon Glass buf_addr = (unsigned long)buffer; 2450fcd48feSSimon Glass start = blknr; 2460fcd48feSSimon Glass blks = blkcnt; 2470fcd48feSSimon Glass debug("\n%s: dev %d startblk " LBAF ", blccnt " LBAF " buffer %lx\n", 2480fcd48feSSimon Glass __func__, block_dev->devnum, start, blks, (unsigned long)buffer); 2490fcd48feSSimon Glass do { 2500fcd48feSSimon Glass pccb->pdata = (unsigned char *)buf_addr; 251*8f7de514SShawn Lin pccb->dma_dir = DMA_TO_DEVICE; 2520fcd48feSSimon Glass if (blks > SCSI_MAX_WRITE_BLK) { 2530fcd48feSSimon Glass pccb->datalen = (block_dev->blksz * 2540fcd48feSSimon Glass SCSI_MAX_WRITE_BLK); 2550fcd48feSSimon Glass smallblks = SCSI_MAX_WRITE_BLK; 2560fcd48feSSimon Glass scsi_setup_write_ext(pccb, start, smallblks); 2570fcd48feSSimon Glass start += SCSI_MAX_WRITE_BLK; 2580fcd48feSSimon Glass blks -= SCSI_MAX_WRITE_BLK; 2590fcd48feSSimon Glass } else { 2600fcd48feSSimon Glass pccb->datalen = block_dev->blksz * blks; 2610fcd48feSSimon Glass smallblks = (unsigned short)blks; 2620fcd48feSSimon Glass scsi_setup_write_ext(pccb, start, smallblks); 2630fcd48feSSimon Glass start += blks; 2640fcd48feSSimon Glass blks = 0; 2650fcd48feSSimon Glass } 2660fcd48feSSimon Glass debug("%s: startblk " LBAF ", blccnt %x buffer %" PRIXPTR "\n", 2670fcd48feSSimon Glass __func__, start, smallblks, buf_addr); 2684682c8a1SSimon Glass if (scsi_exec(bdev, pccb)) { 2690fcd48feSSimon Glass scsi_print_error(pccb); 2700fcd48feSSimon Glass blkcnt -= blks; 2710fcd48feSSimon Glass break; 2720fcd48feSSimon Glass } 2730fcd48feSSimon Glass buf_addr += pccb->datalen; 2740fcd48feSSimon Glass } while (blks != 0); 2750fcd48feSSimon Glass debug("%s: end startblk " LBAF ", blccnt %x buffer %" PRIXPTR "\n", 2760fcd48feSSimon Glass __func__, start, smallblks, buf_addr); 2770fcd48feSSimon Glass return blkcnt; 2780fcd48feSSimon Glass } 2790fcd48feSSimon Glass 2807337fcd8SSimon Glass #if defined(CONFIG_PCI) && !defined(CONFIG_SCSI_AHCI_PLAT) && \ 2817337fcd8SSimon Glass !defined(CONFIG_DM_SCSI) 2820fcd48feSSimon Glass void scsi_init(void) 2830fcd48feSSimon Glass { 2840fcd48feSSimon Glass int busdevfunc = -1; 2850fcd48feSSimon Glass int i; 2860fcd48feSSimon Glass /* 2870fcd48feSSimon Glass * Find a device from the list, this driver will support a single 2880fcd48feSSimon Glass * controller. 2890fcd48feSSimon Glass */ 2900fcd48feSSimon Glass for (i = 0; i < ARRAY_SIZE(scsi_device_list); i++) { 2910fcd48feSSimon Glass /* get PCI Device ID */ 2920fcd48feSSimon Glass #ifdef CONFIG_DM_PCI 2930fcd48feSSimon Glass struct udevice *dev; 2940fcd48feSSimon Glass int ret; 2950fcd48feSSimon Glass 2960fcd48feSSimon Glass ret = dm_pci_find_device(scsi_device_list[i].vendor, 2970fcd48feSSimon Glass scsi_device_list[i].device, 0, &dev); 2980fcd48feSSimon Glass if (!ret) { 2990fcd48feSSimon Glass busdevfunc = dm_pci_get_bdf(dev); 3000fcd48feSSimon Glass break; 3010fcd48feSSimon Glass } 3020fcd48feSSimon Glass #else 3030fcd48feSSimon Glass busdevfunc = pci_find_device(scsi_device_list[i].vendor, 3040fcd48feSSimon Glass scsi_device_list[i].device, 3050fcd48feSSimon Glass 0); 3060fcd48feSSimon Glass #endif 3070fcd48feSSimon Glass if (busdevfunc != -1) 3080fcd48feSSimon Glass break; 3090fcd48feSSimon Glass } 3100fcd48feSSimon Glass 3110fcd48feSSimon Glass if (busdevfunc == -1) { 3120fcd48feSSimon Glass printf("Error: SCSI Controller(s) "); 3130fcd48feSSimon Glass for (i = 0; i < ARRAY_SIZE(scsi_device_list); i++) { 3140fcd48feSSimon Glass printf("%04X:%04X ", 3150fcd48feSSimon Glass scsi_device_list[i].vendor, 3160fcd48feSSimon Glass scsi_device_list[i].device); 3170fcd48feSSimon Glass } 3180fcd48feSSimon Glass printf("not found\n"); 3190fcd48feSSimon Glass return; 3200fcd48feSSimon Glass } 3210fcd48feSSimon Glass #ifdef DEBUG 3220fcd48feSSimon Glass else { 3230fcd48feSSimon Glass printf("SCSI Controller (%04X,%04X) found (%d:%d:%d)\n", 3240fcd48feSSimon Glass scsi_device_list[i].vendor, 3250fcd48feSSimon Glass scsi_device_list[i].device, 3260fcd48feSSimon Glass (busdevfunc >> 16) & 0xFF, 3270fcd48feSSimon Glass (busdevfunc >> 11) & 0x1F, 3280fcd48feSSimon Glass (busdevfunc >> 8) & 0x7); 3290fcd48feSSimon Glass } 3300fcd48feSSimon Glass #endif 3310fcd48feSSimon Glass bootstage_start(BOOTSTAGE_ID_ACCUM_SCSI, "ahci"); 3320fcd48feSSimon Glass scsi_low_level_init(busdevfunc); 3338eab1a58SSimon Glass scsi_scan(true); 3340fcd48feSSimon Glass bootstage_accum(BOOTSTAGE_ID_ACCUM_SCSI); 3350fcd48feSSimon Glass } 3360fcd48feSSimon Glass #endif 3370fcd48feSSimon Glass 3380fcd48feSSimon Glass /* copy src to dest, skipping leading and trailing blanks 3390fcd48feSSimon Glass * and null terminate the string 3400fcd48feSSimon Glass */ 3410fcd48feSSimon Glass static void scsi_ident_cpy(unsigned char *dest, unsigned char *src, 3420fcd48feSSimon Glass unsigned int len) 3430fcd48feSSimon Glass { 3440fcd48feSSimon Glass int start, end; 3450fcd48feSSimon Glass 3460fcd48feSSimon Glass start = 0; 3470fcd48feSSimon Glass while (start < len) { 3480fcd48feSSimon Glass if (src[start] != ' ') 3490fcd48feSSimon Glass break; 3500fcd48feSSimon Glass start++; 3510fcd48feSSimon Glass } 3520fcd48feSSimon Glass end = len-1; 3530fcd48feSSimon Glass while (end > start) { 3540fcd48feSSimon Glass if (src[end] != ' ') 3550fcd48feSSimon Glass break; 3560fcd48feSSimon Glass end--; 3570fcd48feSSimon Glass } 3580fcd48feSSimon Glass for (; start <= end; start++) 3590fcd48feSSimon Glass *dest ++= src[start]; 3600fcd48feSSimon Glass *dest = '\0'; 3610fcd48feSSimon Glass } 3620fcd48feSSimon Glass 3634682c8a1SSimon Glass static int scsi_read_capacity(struct udevice *dev, struct scsi_cmd *pccb, 3644682c8a1SSimon Glass lbaint_t *capacity, unsigned long *blksz) 3650fcd48feSSimon Glass { 3660fcd48feSSimon Glass *capacity = 0; 3670fcd48feSSimon Glass 3680fcd48feSSimon Glass memset(pccb->cmd, '\0', sizeof(pccb->cmd)); 3690fcd48feSSimon Glass pccb->cmd[0] = SCSI_RD_CAPAC10; 3700fcd48feSSimon Glass pccb->cmd[1] = pccb->lun << 5; 3710fcd48feSSimon Glass pccb->cmdlen = 10; 3720fcd48feSSimon Glass pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ 3730fcd48feSSimon Glass 3740fcd48feSSimon Glass pccb->datalen = 8; 375f6580ef3SSimon Glass if (scsi_exec(dev, pccb)) 3760fcd48feSSimon Glass return 1; 3770fcd48feSSimon Glass 3780fcd48feSSimon Glass *capacity = ((lbaint_t)pccb->pdata[0] << 24) | 3790fcd48feSSimon Glass ((lbaint_t)pccb->pdata[1] << 16) | 3800fcd48feSSimon Glass ((lbaint_t)pccb->pdata[2] << 8) | 3810fcd48feSSimon Glass ((lbaint_t)pccb->pdata[3]); 3820fcd48feSSimon Glass 3830fcd48feSSimon Glass if (*capacity != 0xffffffff) { 3840fcd48feSSimon Glass /* Read capacity (10) was sufficient for this drive. */ 3850fcd48feSSimon Glass *blksz = ((unsigned long)pccb->pdata[4] << 24) | 3860fcd48feSSimon Glass ((unsigned long)pccb->pdata[5] << 16) | 3870fcd48feSSimon Glass ((unsigned long)pccb->pdata[6] << 8) | 3880fcd48feSSimon Glass ((unsigned long)pccb->pdata[7]); 3890fcd48feSSimon Glass return 0; 3900fcd48feSSimon Glass } 3910fcd48feSSimon Glass 3920fcd48feSSimon Glass /* Read capacity (10) was insufficient. Use read capacity (16). */ 3930fcd48feSSimon Glass memset(pccb->cmd, '\0', sizeof(pccb->cmd)); 3940fcd48feSSimon Glass pccb->cmd[0] = SCSI_RD_CAPAC16; 3950fcd48feSSimon Glass pccb->cmd[1] = 0x10; 3960fcd48feSSimon Glass pccb->cmdlen = 16; 3970fcd48feSSimon Glass pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ 3980fcd48feSSimon Glass 3990fcd48feSSimon Glass pccb->datalen = 16; 400*8f7de514SShawn Lin pccb->dma_dir = DMA_FROM_DEVICE; 401f6580ef3SSimon Glass if (scsi_exec(dev, pccb)) 4020fcd48feSSimon Glass return 1; 4030fcd48feSSimon Glass 4040fcd48feSSimon Glass *capacity = ((uint64_t)pccb->pdata[0] << 56) | 4050fcd48feSSimon Glass ((uint64_t)pccb->pdata[1] << 48) | 4060fcd48feSSimon Glass ((uint64_t)pccb->pdata[2] << 40) | 4070fcd48feSSimon Glass ((uint64_t)pccb->pdata[3] << 32) | 4080fcd48feSSimon Glass ((uint64_t)pccb->pdata[4] << 24) | 4090fcd48feSSimon Glass ((uint64_t)pccb->pdata[5] << 16) | 4100fcd48feSSimon Glass ((uint64_t)pccb->pdata[6] << 8) | 4110fcd48feSSimon Glass ((uint64_t)pccb->pdata[7]); 4120fcd48feSSimon Glass 4130fcd48feSSimon Glass *blksz = ((uint64_t)pccb->pdata[8] << 56) | 4140fcd48feSSimon Glass ((uint64_t)pccb->pdata[9] << 48) | 4150fcd48feSSimon Glass ((uint64_t)pccb->pdata[10] << 40) | 4160fcd48feSSimon Glass ((uint64_t)pccb->pdata[11] << 32) | 4170fcd48feSSimon Glass ((uint64_t)pccb->pdata[12] << 24) | 4180fcd48feSSimon Glass ((uint64_t)pccb->pdata[13] << 16) | 4190fcd48feSSimon Glass ((uint64_t)pccb->pdata[14] << 8) | 4200fcd48feSSimon Glass ((uint64_t)pccb->pdata[15]); 4210fcd48feSSimon Glass 4220fcd48feSSimon Glass return 0; 4230fcd48feSSimon Glass } 4240fcd48feSSimon Glass 4250fcd48feSSimon Glass 4260fcd48feSSimon Glass /* 4270fcd48feSSimon Glass * Some setup (fill-in) routines 4280fcd48feSSimon Glass */ 429b9560ad6SSimon Glass static void scsi_setup_test_unit_ready(struct scsi_cmd *pccb) 4300fcd48feSSimon Glass { 4310fcd48feSSimon Glass pccb->cmd[0] = SCSI_TST_U_RDY; 4320fcd48feSSimon Glass pccb->cmd[1] = pccb->lun << 5; 4330fcd48feSSimon Glass pccb->cmd[2] = 0; 4340fcd48feSSimon Glass pccb->cmd[3] = 0; 4350fcd48feSSimon Glass pccb->cmd[4] = 0; 4360fcd48feSSimon Glass pccb->cmd[5] = 0; 4370fcd48feSSimon Glass pccb->cmdlen = 6; 4380fcd48feSSimon Glass pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ 4390fcd48feSSimon Glass } 4400fcd48feSSimon Glass 4410fcd48feSSimon Glass /** 4420fcd48feSSimon Glass * scsi_init_dev_desc_priv - initialize only SCSI specific blk_desc properties 4430fcd48feSSimon Glass * 4440fcd48feSSimon Glass * @dev_desc: Block device description pointer 4450fcd48feSSimon Glass */ 4460fcd48feSSimon Glass static void scsi_init_dev_desc_priv(struct blk_desc *dev_desc) 4470fcd48feSSimon Glass { 4480fcd48feSSimon Glass dev_desc->target = 0xff; 4490fcd48feSSimon Glass dev_desc->lun = 0xff; 4500fcd48feSSimon Glass dev_desc->log2blksz = 4510fcd48feSSimon Glass LOG2_INVALID(typeof(dev_desc->log2blksz)); 4520fcd48feSSimon Glass dev_desc->type = DEV_TYPE_UNKNOWN; 4530fcd48feSSimon Glass dev_desc->vendor[0] = 0; 4540fcd48feSSimon Glass dev_desc->product[0] = 0; 4550fcd48feSSimon Glass dev_desc->revision[0] = 0; 4560fcd48feSSimon Glass dev_desc->removable = false; 457c4d660d4SSimon Glass #if !CONFIG_IS_ENABLED(BLK) 4580fcd48feSSimon Glass dev_desc->block_read = scsi_read; 4590fcd48feSSimon Glass dev_desc->block_write = scsi_write; 4600fcd48feSSimon Glass #endif 4610fcd48feSSimon Glass } 4620fcd48feSSimon Glass 4630fcd48feSSimon Glass #if !defined(CONFIG_DM_SCSI) 4640fcd48feSSimon Glass /** 4650fcd48feSSimon Glass * scsi_init_dev_desc - initialize all SCSI specific blk_desc properties 4660fcd48feSSimon Glass * 4670fcd48feSSimon Glass * @dev_desc: Block device description pointer 4680fcd48feSSimon Glass * @devnum: Device number 4690fcd48feSSimon Glass */ 4700fcd48feSSimon Glass static void scsi_init_dev_desc(struct blk_desc *dev_desc, int devnum) 4710fcd48feSSimon Glass { 4720fcd48feSSimon Glass dev_desc->lba = 0; 4730fcd48feSSimon Glass dev_desc->blksz = 0; 4740fcd48feSSimon Glass dev_desc->if_type = IF_TYPE_SCSI; 4750fcd48feSSimon Glass dev_desc->devnum = devnum; 4760fcd48feSSimon Glass dev_desc->part_type = PART_TYPE_UNKNOWN; 4770fcd48feSSimon Glass 4780fcd48feSSimon Glass scsi_init_dev_desc_priv(dev_desc); 4790fcd48feSSimon Glass } 4800fcd48feSSimon Glass #endif 4810fcd48feSSimon Glass 4820fcd48feSSimon Glass /** 4830fcd48feSSimon Glass * scsi_detect_dev - Detect scsi device 4840fcd48feSSimon Glass * 4850fcd48feSSimon Glass * @target: target id 4860fcd48feSSimon Glass * @lun: target lun 4870fcd48feSSimon Glass * @dev_desc: block device description 4880fcd48feSSimon Glass * 4890fcd48feSSimon Glass * The scsi_detect_dev detects and fills a dev_desc structure when the device is 4900fcd48feSSimon Glass * detected. 4910fcd48feSSimon Glass * 4920fcd48feSSimon Glass * Return: 0 on success, error value otherwise 4930fcd48feSSimon Glass */ 4944682c8a1SSimon Glass static int scsi_detect_dev(struct udevice *dev, int target, int lun, 4954682c8a1SSimon Glass struct blk_desc *dev_desc) 4960fcd48feSSimon Glass { 4970fcd48feSSimon Glass unsigned char perq, modi; 4980fcd48feSSimon Glass lbaint_t capacity; 4990fcd48feSSimon Glass unsigned long blksz; 500b9560ad6SSimon Glass struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb; 5010fcd48feSSimon Glass 5020fcd48feSSimon Glass pccb->target = target; 5030fcd48feSSimon Glass pccb->lun = lun; 5040fcd48feSSimon Glass pccb->pdata = (unsigned char *)&tempbuff; 5050fcd48feSSimon Glass pccb->datalen = 512; 506*8f7de514SShawn Lin pccb->dma_dir = DMA_FROM_DEVICE; 5070fcd48feSSimon Glass scsi_setup_inquiry(pccb); 508f6580ef3SSimon Glass if (scsi_exec(dev, pccb)) { 5090fcd48feSSimon Glass if (pccb->contr_stat == SCSI_SEL_TIME_OUT) { 5100fcd48feSSimon Glass /* 5110fcd48feSSimon Glass * selection timeout => assuming no 5120fcd48feSSimon Glass * device present 5130fcd48feSSimon Glass */ 5140fcd48feSSimon Glass debug("Selection timeout ID %d\n", 5150fcd48feSSimon Glass pccb->target); 5160fcd48feSSimon Glass return -ETIMEDOUT; 5170fcd48feSSimon Glass } 5180fcd48feSSimon Glass scsi_print_error(pccb); 5190fcd48feSSimon Glass return -ENODEV; 5200fcd48feSSimon Glass } 5210fcd48feSSimon Glass perq = tempbuff[0]; 5220fcd48feSSimon Glass modi = tempbuff[1]; 5230fcd48feSSimon Glass if ((perq & 0x1f) == 0x1f) 5240fcd48feSSimon Glass return -ENODEV; /* skip unknown devices */ 5250fcd48feSSimon Glass if ((modi & 0x80) == 0x80) /* drive is removable */ 5260fcd48feSSimon Glass dev_desc->removable = true; 5270fcd48feSSimon Glass /* get info for this device */ 5280fcd48feSSimon Glass scsi_ident_cpy((unsigned char *)dev_desc->vendor, 5290fcd48feSSimon Glass &tempbuff[8], 8); 5300fcd48feSSimon Glass scsi_ident_cpy((unsigned char *)dev_desc->product, 5310fcd48feSSimon Glass &tempbuff[16], 16); 5320fcd48feSSimon Glass scsi_ident_cpy((unsigned char *)dev_desc->revision, 5330fcd48feSSimon Glass &tempbuff[32], 4); 5340fcd48feSSimon Glass dev_desc->target = pccb->target; 5350fcd48feSSimon Glass dev_desc->lun = pccb->lun; 5360fcd48feSSimon Glass 5370fcd48feSSimon Glass pccb->datalen = 0; 5380fcd48feSSimon Glass scsi_setup_test_unit_ready(pccb); 539f6580ef3SSimon Glass if (scsi_exec(dev, pccb)) { 5400fcd48feSSimon Glass if (dev_desc->removable) { 5410fcd48feSSimon Glass dev_desc->type = perq; 5420fcd48feSSimon Glass goto removable; 5430fcd48feSSimon Glass } 5440fcd48feSSimon Glass scsi_print_error(pccb); 5450fcd48feSSimon Glass return -EINVAL; 5460fcd48feSSimon Glass } 5474682c8a1SSimon Glass if (scsi_read_capacity(dev, pccb, &capacity, &blksz)) { 5480fcd48feSSimon Glass scsi_print_error(pccb); 5490fcd48feSSimon Glass return -EINVAL; 5500fcd48feSSimon Glass } 5510fcd48feSSimon Glass dev_desc->lba = capacity; 5520fcd48feSSimon Glass dev_desc->blksz = blksz; 5530fcd48feSSimon Glass dev_desc->log2blksz = LOG2(dev_desc->blksz); 5540fcd48feSSimon Glass dev_desc->type = perq; 5550fcd48feSSimon Glass removable: 5560fcd48feSSimon Glass return 0; 5570fcd48feSSimon Glass } 5580fcd48feSSimon Glass 5590fcd48feSSimon Glass /* 5600fcd48feSSimon Glass * (re)-scan the scsi bus and reports scsi device info 5610fcd48feSSimon Glass * to the user if mode = 1 5620fcd48feSSimon Glass */ 5630fcd48feSSimon Glass #if defined(CONFIG_DM_SCSI) 5648eab1a58SSimon Glass static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose) 5650fcd48feSSimon Glass { 5660fcd48feSSimon Glass int ret; 5670fcd48feSSimon Glass struct udevice *bdev; 5680fcd48feSSimon Glass struct blk_desc bd; 5690fcd48feSSimon Glass struct blk_desc *bdesc; 5700fcd48feSSimon Glass char str[10]; 5710fcd48feSSimon Glass 5720fcd48feSSimon Glass /* 5730fcd48feSSimon Glass * detect the scsi driver to get information about its geometry (block 5740fcd48feSSimon Glass * size, number of blocks) and other parameters (ids, type, ...) 5750fcd48feSSimon Glass */ 5760fcd48feSSimon Glass scsi_init_dev_desc_priv(&bd); 5774682c8a1SSimon Glass if (scsi_detect_dev(dev, id, lun, &bd)) 5780fcd48feSSimon Glass return -ENODEV; 5790fcd48feSSimon Glass 5800fcd48feSSimon Glass /* 5810fcd48feSSimon Glass * Create only one block device and do detection 5820fcd48feSSimon Glass * to make sure that there won't be a lot of 5830fcd48feSSimon Glass * block devices created 5840fcd48feSSimon Glass */ 5850fcd48feSSimon Glass snprintf(str, sizeof(str), "id%dlun%d", id, lun); 5860fcd48feSSimon Glass ret = blk_create_devicef(dev, "scsi_blk", str, IF_TYPE_SCSI, -1, 5870fcd48feSSimon Glass bd.blksz, bd.blksz * bd.lba, &bdev); 5880fcd48feSSimon Glass if (ret) { 5890fcd48feSSimon Glass debug("Can't create device\n"); 5900fcd48feSSimon Glass return ret; 5910fcd48feSSimon Glass } 5920fcd48feSSimon Glass 5930fcd48feSSimon Glass bdesc = dev_get_uclass_platdata(bdev); 5940fcd48feSSimon Glass bdesc->target = id; 5950fcd48feSSimon Glass bdesc->lun = lun; 5960fcd48feSSimon Glass bdesc->removable = bd.removable; 5970fcd48feSSimon Glass bdesc->type = bd.type; 5980fcd48feSSimon Glass memcpy(&bdesc->vendor, &bd.vendor, sizeof(bd.vendor)); 5990fcd48feSSimon Glass memcpy(&bdesc->product, &bd.product, sizeof(bd.product)); 6000fcd48feSSimon Glass memcpy(&bdesc->revision, &bd.revision, sizeof(bd.revision)); 6010fcd48feSSimon Glass part_init(bdesc); 6020fcd48feSSimon Glass 6038eab1a58SSimon Glass if (verbose) { 6040fcd48feSSimon Glass printf(" Device %d: ", 0); 6050fcd48feSSimon Glass dev_print(bdesc); 6060fcd48feSSimon Glass } 6070fcd48feSSimon Glass return 0; 6080fcd48feSSimon Glass } 6090fcd48feSSimon Glass 6105c561763SSimon Glass int scsi_scan_dev(struct udevice *dev, bool verbose) 6115c561763SSimon Glass { 6125c561763SSimon Glass struct scsi_platdata *uc_plat; /* scsi controller platdata */ 6135c561763SSimon Glass int ret; 6145c561763SSimon Glass int i; 6155c561763SSimon Glass int lun; 6165c561763SSimon Glass 6175c561763SSimon Glass /* probe SCSI controller driver */ 6185c561763SSimon Glass ret = device_probe(dev); 6195c561763SSimon Glass if (ret) 6205c561763SSimon Glass return ret; 6215c561763SSimon Glass 6225c561763SSimon Glass /* Get controller platdata */ 6235c561763SSimon Glass uc_plat = dev_get_uclass_platdata(dev); 6245c561763SSimon Glass 6255c561763SSimon Glass for (i = 0; i < uc_plat->max_id; i++) 6265c561763SSimon Glass for (lun = 0; lun < uc_plat->max_lun; lun++) 6275c561763SSimon Glass do_scsi_scan_one(dev, i, lun, verbose); 6285c561763SSimon Glass 6295c561763SSimon Glass return 0; 6305c561763SSimon Glass } 6315c561763SSimon Glass 6328eab1a58SSimon Glass int scsi_scan(bool verbose) 6330fcd48feSSimon Glass { 6340fcd48feSSimon Glass struct uclass *uc; 6350fcd48feSSimon Glass struct udevice *dev; /* SCSI controller */ 6360fcd48feSSimon Glass int ret; 6370fcd48feSSimon Glass 6388eab1a58SSimon Glass if (verbose) 6390fcd48feSSimon Glass printf("scanning bus for devices...\n"); 6400fcd48feSSimon Glass 6410fcd48feSSimon Glass blk_unbind_all(IF_TYPE_SCSI); 6420fcd48feSSimon Glass 6430fcd48feSSimon Glass ret = uclass_get(UCLASS_SCSI, &uc); 6440fcd48feSSimon Glass if (ret) 6450fcd48feSSimon Glass return ret; 6460fcd48feSSimon Glass 6470fcd48feSSimon Glass uclass_foreach_dev(dev, uc) { 6485c561763SSimon Glass ret = scsi_scan_dev(dev, verbose); 6490fcd48feSSimon Glass if (ret) 6500fcd48feSSimon Glass return ret; 6510fcd48feSSimon Glass } 6520fcd48feSSimon Glass 6530fcd48feSSimon Glass return 0; 6540fcd48feSSimon Glass } 6550fcd48feSSimon Glass #else 6568eab1a58SSimon Glass int scsi_scan(bool verbose) 6570fcd48feSSimon Glass { 6580fcd48feSSimon Glass unsigned char i, lun; 6590fcd48feSSimon Glass int ret; 6600fcd48feSSimon Glass 6618eab1a58SSimon Glass if (verbose) 6620fcd48feSSimon Glass printf("scanning bus for devices...\n"); 6630fcd48feSSimon Glass for (i = 0; i < CONFIG_SYS_SCSI_MAX_DEVICE; i++) 6640fcd48feSSimon Glass scsi_init_dev_desc(&scsi_dev_desc[i], i); 6650fcd48feSSimon Glass 6660fcd48feSSimon Glass scsi_max_devs = 0; 6670fcd48feSSimon Glass for (i = 0; i < CONFIG_SYS_SCSI_MAX_SCSI_ID; i++) { 6680fcd48feSSimon Glass for (lun = 0; lun < CONFIG_SYS_SCSI_MAX_LUN; lun++) { 6694682c8a1SSimon Glass ret = scsi_detect_dev(NULL, i, lun, 6700fcd48feSSimon Glass &scsi_dev_desc[scsi_max_devs]); 6710fcd48feSSimon Glass if (ret) 6720fcd48feSSimon Glass continue; 6730fcd48feSSimon Glass part_init(&scsi_dev_desc[scsi_max_devs]); 6740fcd48feSSimon Glass 6758eab1a58SSimon Glass if (verbose) { 6760fcd48feSSimon Glass printf(" Device %d: ", 0); 6770fcd48feSSimon Glass dev_print(&scsi_dev_desc[scsi_max_devs]); 6788eab1a58SSimon Glass } 6790fcd48feSSimon Glass scsi_max_devs++; 6800fcd48feSSimon Glass } /* next LUN */ 6810fcd48feSSimon Glass } 6820fcd48feSSimon Glass if (scsi_max_devs > 0) 6830fcd48feSSimon Glass scsi_curr_dev = 0; 6840fcd48feSSimon Glass else 6850fcd48feSSimon Glass scsi_curr_dev = -1; 6860fcd48feSSimon Glass 6870fcd48feSSimon Glass printf("Found %d device(s).\n", scsi_max_devs); 6880fcd48feSSimon Glass #ifndef CONFIG_SPL_BUILD 689018f5303SSimon Glass env_set_ulong("scsidevs", scsi_max_devs); 6900fcd48feSSimon Glass #endif 6910fcd48feSSimon Glass return 0; 6920fcd48feSSimon Glass } 6930fcd48feSSimon Glass #endif 6940fcd48feSSimon Glass 6950fcd48feSSimon Glass #ifdef CONFIG_BLK 6960fcd48feSSimon Glass static const struct blk_ops scsi_blk_ops = { 6970fcd48feSSimon Glass .read = scsi_read, 6980fcd48feSSimon Glass .write = scsi_write, 6990fcd48feSSimon Glass }; 7000fcd48feSSimon Glass 7010fcd48feSSimon Glass U_BOOT_DRIVER(scsi_blk) = { 7020fcd48feSSimon Glass .name = "scsi_blk", 7030fcd48feSSimon Glass .id = UCLASS_BLK, 7040fcd48feSSimon Glass .ops = &scsi_blk_ops, 7050fcd48feSSimon Glass }; 7060fcd48feSSimon Glass #else 7070fcd48feSSimon Glass U_BOOT_LEGACY_BLK(scsi) = { 7080fcd48feSSimon Glass .if_typename = "scsi", 7090fcd48feSSimon Glass .if_type = IF_TYPE_SCSI, 7100fcd48feSSimon Glass .max_devs = CONFIG_SYS_SCSI_MAX_DEVICE, 7110fcd48feSSimon Glass .desc = scsi_dev_desc, 7120fcd48feSSimon Glass }; 7130fcd48feSSimon Glass #endif 714