18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci pd.c (c) 1997-8 Grant R. Guenther <grant@torque.net> 38c2ecf20Sopenharmony_ci Under the terms of the GNU General Public License. 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci This is the high-level driver for parallel port IDE hard 68c2ecf20Sopenharmony_ci drives based on chips supported by the paride module. 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci By default, the driver will autoprobe for a single parallel 98c2ecf20Sopenharmony_ci port IDE drive, but if their individual parameters are 108c2ecf20Sopenharmony_ci specified, the driver can handle up to 4 drives. 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci The behaviour of the pd driver can be altered by setting 138c2ecf20Sopenharmony_ci some parameters from the insmod command line. The following 148c2ecf20Sopenharmony_ci parameters are adjustable: 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci drive0 These four arguments can be arrays of 178c2ecf20Sopenharmony_ci drive1 1-8 integers as follows: 188c2ecf20Sopenharmony_ci drive2 198c2ecf20Sopenharmony_ci drive3 <prt>,<pro>,<uni>,<mod>,<geo>,<sby>,<dly>,<slv> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci Where, 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci <prt> is the base of the parallel port address for 248c2ecf20Sopenharmony_ci the corresponding drive. (required) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci <pro> is the protocol number for the adapter that 278c2ecf20Sopenharmony_ci supports this drive. These numbers are 288c2ecf20Sopenharmony_ci logged by 'paride' when the protocol modules 298c2ecf20Sopenharmony_ci are initialised. (0 if not given) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci <uni> for those adapters that support chained 328c2ecf20Sopenharmony_ci devices, this is the unit selector for the 338c2ecf20Sopenharmony_ci chain of devices on the given port. It should 348c2ecf20Sopenharmony_ci be zero for devices that don't support chaining. 358c2ecf20Sopenharmony_ci (0 if not given) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci <mod> this can be -1 to choose the best mode, or one 388c2ecf20Sopenharmony_ci of the mode numbers supported by the adapter. 398c2ecf20Sopenharmony_ci (-1 if not given) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci <geo> this defaults to 0 to indicate that the driver 428c2ecf20Sopenharmony_ci should use the CHS geometry provided by the drive 438c2ecf20Sopenharmony_ci itself. If set to 1, the driver will provide 448c2ecf20Sopenharmony_ci a logical geometry with 64 heads and 32 sectors 458c2ecf20Sopenharmony_ci per track, to be consistent with most SCSI 468c2ecf20Sopenharmony_ci drivers. (0 if not given) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci <sby> set this to zero to disable the power saving 498c2ecf20Sopenharmony_ci standby mode, if needed. (1 if not given) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci <dly> some parallel ports require the driver to 528c2ecf20Sopenharmony_ci go more slowly. -1 sets a default value that 538c2ecf20Sopenharmony_ci should work with the chosen protocol. Otherwise, 548c2ecf20Sopenharmony_ci set this to a small integer, the larger it is 558c2ecf20Sopenharmony_ci the slower the port i/o. In some cases, setting 568c2ecf20Sopenharmony_ci this to zero will speed up the device. (default -1) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci <slv> IDE disks can be jumpered to master or slave. 598c2ecf20Sopenharmony_ci Set this to 0 to choose the master drive, 1 to 608c2ecf20Sopenharmony_ci choose the slave, -1 (the default) to choose the 618c2ecf20Sopenharmony_ci first drive found. 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci major You may use this parameter to override the 658c2ecf20Sopenharmony_ci default major number (45) that this driver 668c2ecf20Sopenharmony_ci will use. Be sure to change the device 678c2ecf20Sopenharmony_ci name as well. 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci name This parameter is a character string that 708c2ecf20Sopenharmony_ci contains the name the kernel will use for this 718c2ecf20Sopenharmony_ci device (in /proc output, for instance). 728c2ecf20Sopenharmony_ci (default "pd") 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci cluster The driver will attempt to aggregate requests 758c2ecf20Sopenharmony_ci for adjacent blocks into larger multi-block 768c2ecf20Sopenharmony_ci clusters. The maximum cluster size (in 512 778c2ecf20Sopenharmony_ci byte sectors) is set with this parameter. 788c2ecf20Sopenharmony_ci (default 64) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci verbose This parameter controls the amount of logging 818c2ecf20Sopenharmony_ci that the driver will do. Set it to 0 for 828c2ecf20Sopenharmony_ci normal operation, 1 to see autoprobe progress 838c2ecf20Sopenharmony_ci messages, or 2 to see additional debugging 848c2ecf20Sopenharmony_ci output. (default 0) 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci nice This parameter controls the driver's use of 878c2ecf20Sopenharmony_ci idle CPU time, at the expense of some speed. 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci If this driver is built into the kernel, you can use kernel 908c2ecf20Sopenharmony_ci the following command line parameters, with the same values 918c2ecf20Sopenharmony_ci as the corresponding module parameters listed above: 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci pd.drive0 948c2ecf20Sopenharmony_ci pd.drive1 958c2ecf20Sopenharmony_ci pd.drive2 968c2ecf20Sopenharmony_ci pd.drive3 978c2ecf20Sopenharmony_ci pd.cluster 988c2ecf20Sopenharmony_ci pd.nice 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci In addition, you can use the parameter pd.disable to disable 1018c2ecf20Sopenharmony_ci the driver entirely. 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci*/ 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* Changes: 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci 1.01 GRG 1997.01.24 Restored pd_reset() 1088c2ecf20Sopenharmony_ci Added eject ioctl 1098c2ecf20Sopenharmony_ci 1.02 GRG 1998.05.06 SMP spinlock changes, 1108c2ecf20Sopenharmony_ci Added slave support 1118c2ecf20Sopenharmony_ci 1.03 GRG 1998.06.16 Eliminate an Ugh. 1128c2ecf20Sopenharmony_ci 1.04 GRG 1998.08.15 Extra debugging, use HZ in loop timing 1138c2ecf20Sopenharmony_ci 1.05 GRG 1998.09.24 Added jumbo support 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci*/ 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#define PD_VERSION "1.05" 1188c2ecf20Sopenharmony_ci#define PD_MAJOR 45 1198c2ecf20Sopenharmony_ci#define PD_NAME "pd" 1208c2ecf20Sopenharmony_ci#define PD_UNITS 4 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* Here are things one can override from the insmod command. 1238c2ecf20Sopenharmony_ci Most are autoprobed by paride unless set here. Verbose is off 1248c2ecf20Sopenharmony_ci by default. 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci*/ 1278c2ecf20Sopenharmony_ci#include <linux/types.h> 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int verbose = 0; 1308c2ecf20Sopenharmony_cistatic int major = PD_MAJOR; 1318c2ecf20Sopenharmony_cistatic char *name = PD_NAME; 1328c2ecf20Sopenharmony_cistatic int cluster = 64; 1338c2ecf20Sopenharmony_cistatic int nice = 0; 1348c2ecf20Sopenharmony_cistatic int disable = 0; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int drive0[8] = { 0, 0, 0, -1, 0, 1, -1, -1 }; 1378c2ecf20Sopenharmony_cistatic int drive1[8] = { 0, 0, 0, -1, 0, 1, -1, -1 }; 1388c2ecf20Sopenharmony_cistatic int drive2[8] = { 0, 0, 0, -1, 0, 1, -1, -1 }; 1398c2ecf20Sopenharmony_cistatic int drive3[8] = { 0, 0, 0, -1, 0, 1, -1, -1 }; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int (*drives[4])[8] = {&drive0, &drive1, &drive2, &drive3}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cienum {D_PRT, D_PRO, D_UNI, D_MOD, D_GEO, D_SBY, D_DLY, D_SLV}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* end of parameters */ 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci#include <linux/init.h> 1488c2ecf20Sopenharmony_ci#include <linux/module.h> 1498c2ecf20Sopenharmony_ci#include <linux/gfp.h> 1508c2ecf20Sopenharmony_ci#include <linux/fs.h> 1518c2ecf20Sopenharmony_ci#include <linux/delay.h> 1528c2ecf20Sopenharmony_ci#include <linux/hdreg.h> 1538c2ecf20Sopenharmony_ci#include <linux/cdrom.h> /* for the eject ioctl */ 1548c2ecf20Sopenharmony_ci#include <linux/blk-mq.h> 1558c2ecf20Sopenharmony_ci#include <linux/blkpg.h> 1568c2ecf20Sopenharmony_ci#include <linux/kernel.h> 1578c2ecf20Sopenharmony_ci#include <linux/mutex.h> 1588c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 1598c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pd_mutex); 1628c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(pd_lock); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cimodule_param(verbose, int, 0); 1658c2ecf20Sopenharmony_cimodule_param(major, int, 0); 1668c2ecf20Sopenharmony_cimodule_param(name, charp, 0); 1678c2ecf20Sopenharmony_cimodule_param(cluster, int, 0); 1688c2ecf20Sopenharmony_cimodule_param(nice, int, 0); 1698c2ecf20Sopenharmony_cimodule_param_array(drive0, int, NULL, 0); 1708c2ecf20Sopenharmony_cimodule_param_array(drive1, int, NULL, 0); 1718c2ecf20Sopenharmony_cimodule_param_array(drive2, int, NULL, 0); 1728c2ecf20Sopenharmony_cimodule_param_array(drive3, int, NULL, 0); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci#include "paride.h" 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci#define PD_BITS 4 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* numbers for "SCSI" geometry */ 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci#define PD_LOG_HEADS 64 1818c2ecf20Sopenharmony_ci#define PD_LOG_SECTS 32 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci#define PD_ID_OFF 54 1848c2ecf20Sopenharmony_ci#define PD_ID_LEN 14 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci#define PD_MAX_RETRIES 5 1878c2ecf20Sopenharmony_ci#define PD_TMO 800 /* interrupt timeout in jiffies */ 1888c2ecf20Sopenharmony_ci#define PD_SPIN_DEL 50 /* spin delay in micro-seconds */ 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci#define PD_SPIN (1000000*PD_TMO)/(HZ*PD_SPIN_DEL) 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci#define STAT_ERR 0x00001 1938c2ecf20Sopenharmony_ci#define STAT_INDEX 0x00002 1948c2ecf20Sopenharmony_ci#define STAT_ECC 0x00004 1958c2ecf20Sopenharmony_ci#define STAT_DRQ 0x00008 1968c2ecf20Sopenharmony_ci#define STAT_SEEK 0x00010 1978c2ecf20Sopenharmony_ci#define STAT_WRERR 0x00020 1988c2ecf20Sopenharmony_ci#define STAT_READY 0x00040 1998c2ecf20Sopenharmony_ci#define STAT_BUSY 0x00080 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci#define ERR_AMNF 0x00100 2028c2ecf20Sopenharmony_ci#define ERR_TK0NF 0x00200 2038c2ecf20Sopenharmony_ci#define ERR_ABRT 0x00400 2048c2ecf20Sopenharmony_ci#define ERR_MCR 0x00800 2058c2ecf20Sopenharmony_ci#define ERR_IDNF 0x01000 2068c2ecf20Sopenharmony_ci#define ERR_MC 0x02000 2078c2ecf20Sopenharmony_ci#define ERR_UNC 0x04000 2088c2ecf20Sopenharmony_ci#define ERR_TMO 0x10000 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci#define IDE_READ 0x20 2118c2ecf20Sopenharmony_ci#define IDE_WRITE 0x30 2128c2ecf20Sopenharmony_ci#define IDE_READ_VRFY 0x40 2138c2ecf20Sopenharmony_ci#define IDE_INIT_DEV_PARMS 0x91 2148c2ecf20Sopenharmony_ci#define IDE_STANDBY 0x96 2158c2ecf20Sopenharmony_ci#define IDE_ACKCHANGE 0xdb 2168c2ecf20Sopenharmony_ci#define IDE_DOORLOCK 0xde 2178c2ecf20Sopenharmony_ci#define IDE_DOORUNLOCK 0xdf 2188c2ecf20Sopenharmony_ci#define IDE_IDENTIFY 0xec 2198c2ecf20Sopenharmony_ci#define IDE_EJECT 0xed 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci#define PD_NAMELEN 8 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistruct pd_unit { 2248c2ecf20Sopenharmony_ci struct pi_adapter pia; /* interface to paride layer */ 2258c2ecf20Sopenharmony_ci struct pi_adapter *pi; 2268c2ecf20Sopenharmony_ci int access; /* count of active opens ... */ 2278c2ecf20Sopenharmony_ci int capacity; /* Size of this volume in sectors */ 2288c2ecf20Sopenharmony_ci int heads; /* physical geometry */ 2298c2ecf20Sopenharmony_ci int sectors; 2308c2ecf20Sopenharmony_ci int cylinders; 2318c2ecf20Sopenharmony_ci int can_lba; 2328c2ecf20Sopenharmony_ci int drive; /* master=0 slave=1 */ 2338c2ecf20Sopenharmony_ci int changed; /* Have we seen a disk change ? */ 2348c2ecf20Sopenharmony_ci int removable; /* removable media device ? */ 2358c2ecf20Sopenharmony_ci int standby; 2368c2ecf20Sopenharmony_ci int alt_geom; 2378c2ecf20Sopenharmony_ci char name[PD_NAMELEN]; /* pda, pdb, etc ... */ 2388c2ecf20Sopenharmony_ci struct gendisk *gd; 2398c2ecf20Sopenharmony_ci struct blk_mq_tag_set tag_set; 2408c2ecf20Sopenharmony_ci struct list_head rq_list; 2418c2ecf20Sopenharmony_ci}; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic struct pd_unit pd[PD_UNITS]; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistruct pd_req { 2468c2ecf20Sopenharmony_ci /* for REQ_OP_DRV_IN: */ 2478c2ecf20Sopenharmony_ci enum action (*func)(struct pd_unit *disk); 2488c2ecf20Sopenharmony_ci}; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic char pd_scratch[512]; /* scratch block buffer */ 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic char *pd_errs[17] = { "ERR", "INDEX", "ECC", "DRQ", "SEEK", "WRERR", 2538c2ecf20Sopenharmony_ci "READY", "BUSY", "AMNF", "TK0NF", "ABRT", "MCR", 2548c2ecf20Sopenharmony_ci "IDNF", "MC", "UNC", "???", "TMO" 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic void *par_drv; /* reference of parport driver */ 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic inline int status_reg(struct pd_unit *disk) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci return pi_read_regr(disk->pi, 1, 6); 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic inline int read_reg(struct pd_unit *disk, int reg) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci return pi_read_regr(disk->pi, 0, reg); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic inline void write_status(struct pd_unit *disk, int val) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci pi_write_regr(disk->pi, 1, 6, val); 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic inline void write_reg(struct pd_unit *disk, int reg, int val) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci pi_write_regr(disk->pi, 0, reg, val); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic inline u8 DRIVE(struct pd_unit *disk) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci return 0xa0+0x10*disk->drive; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/* ide command interface */ 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic void pd_print_error(struct pd_unit *disk, char *msg, int status) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci int i; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci printk("%s: %s: status = 0x%x =", disk->name, msg, status); 2918c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pd_errs); i++) 2928c2ecf20Sopenharmony_ci if (status & (1 << i)) 2938c2ecf20Sopenharmony_ci printk(" %s", pd_errs[i]); 2948c2ecf20Sopenharmony_ci printk("\n"); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic void pd_reset(struct pd_unit *disk) 2988c2ecf20Sopenharmony_ci{ /* called only for MASTER drive */ 2998c2ecf20Sopenharmony_ci write_status(disk, 4); 3008c2ecf20Sopenharmony_ci udelay(50); 3018c2ecf20Sopenharmony_ci write_status(disk, 0); 3028c2ecf20Sopenharmony_ci udelay(250); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci#define DBMSG(msg) ((verbose>1)?(msg):NULL) 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int pd_wait_for(struct pd_unit *disk, int w, char *msg) 3088c2ecf20Sopenharmony_ci{ /* polled wait */ 3098c2ecf20Sopenharmony_ci int k, r, e; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci k = 0; 3128c2ecf20Sopenharmony_ci while (k < PD_SPIN) { 3138c2ecf20Sopenharmony_ci r = status_reg(disk); 3148c2ecf20Sopenharmony_ci k++; 3158c2ecf20Sopenharmony_ci if (((r & w) == w) && !(r & STAT_BUSY)) 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci udelay(PD_SPIN_DEL); 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci e = (read_reg(disk, 1) << 8) + read_reg(disk, 7); 3208c2ecf20Sopenharmony_ci if (k >= PD_SPIN) 3218c2ecf20Sopenharmony_ci e |= ERR_TMO; 3228c2ecf20Sopenharmony_ci if ((e & (STAT_ERR | ERR_TMO)) && (msg != NULL)) 3238c2ecf20Sopenharmony_ci pd_print_error(disk, msg, e); 3248c2ecf20Sopenharmony_ci return e; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic void pd_send_command(struct pd_unit *disk, int n, int s, int h, int c0, int c1, int func) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci write_reg(disk, 6, DRIVE(disk) + h); 3308c2ecf20Sopenharmony_ci write_reg(disk, 1, 0); /* the IDE task file */ 3318c2ecf20Sopenharmony_ci write_reg(disk, 2, n); 3328c2ecf20Sopenharmony_ci write_reg(disk, 3, s); 3338c2ecf20Sopenharmony_ci write_reg(disk, 4, c0); 3348c2ecf20Sopenharmony_ci write_reg(disk, 5, c1); 3358c2ecf20Sopenharmony_ci write_reg(disk, 7, func); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci udelay(1); 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic void pd_ide_command(struct pd_unit *disk, int func, int block, int count) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci int c1, c0, h, s; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (disk->can_lba) { 3458c2ecf20Sopenharmony_ci s = block & 255; 3468c2ecf20Sopenharmony_ci c0 = (block >>= 8) & 255; 3478c2ecf20Sopenharmony_ci c1 = (block >>= 8) & 255; 3488c2ecf20Sopenharmony_ci h = ((block >>= 8) & 15) + 0x40; 3498c2ecf20Sopenharmony_ci } else { 3508c2ecf20Sopenharmony_ci s = (block % disk->sectors) + 1; 3518c2ecf20Sopenharmony_ci h = (block /= disk->sectors) % disk->heads; 3528c2ecf20Sopenharmony_ci c0 = (block /= disk->heads) % 256; 3538c2ecf20Sopenharmony_ci c1 = (block >>= 8); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci pd_send_command(disk, count, s, h, c0, c1, func); 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci/* The i/o request engine */ 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cienum action {Fail = 0, Ok = 1, Hold, Wait}; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic struct request *pd_req; /* current request */ 3638c2ecf20Sopenharmony_cistatic enum action (*phase)(void); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic void run_fsm(void); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic void ps_tq_int(struct work_struct *work); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic DECLARE_DELAYED_WORK(fsm_tq, ps_tq_int); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic void schedule_fsm(void) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci if (!nice) 3748c2ecf20Sopenharmony_ci schedule_delayed_work(&fsm_tq, 0); 3758c2ecf20Sopenharmony_ci else 3768c2ecf20Sopenharmony_ci schedule_delayed_work(&fsm_tq, nice-1); 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic void ps_tq_int(struct work_struct *work) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci run_fsm(); 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic enum action do_pd_io_start(void); 3858c2ecf20Sopenharmony_cistatic enum action pd_special(void); 3868c2ecf20Sopenharmony_cistatic enum action do_pd_read_start(void); 3878c2ecf20Sopenharmony_cistatic enum action do_pd_write_start(void); 3888c2ecf20Sopenharmony_cistatic enum action do_pd_read_drq(void); 3898c2ecf20Sopenharmony_cistatic enum action do_pd_write_done(void); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic int pd_queue; 3928c2ecf20Sopenharmony_cistatic int pd_claimed; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic struct pd_unit *pd_current; /* current request's drive */ 3958c2ecf20Sopenharmony_cistatic PIA *pi_current; /* current request's PIA */ 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int set_next_request(void) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct gendisk *disk; 4008c2ecf20Sopenharmony_ci struct request_queue *q; 4018c2ecf20Sopenharmony_ci int old_pos = pd_queue; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci do { 4048c2ecf20Sopenharmony_ci disk = pd[pd_queue].gd; 4058c2ecf20Sopenharmony_ci q = disk ? disk->queue : NULL; 4068c2ecf20Sopenharmony_ci if (++pd_queue == PD_UNITS) 4078c2ecf20Sopenharmony_ci pd_queue = 0; 4088c2ecf20Sopenharmony_ci if (q) { 4098c2ecf20Sopenharmony_ci struct pd_unit *disk = q->queuedata; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (list_empty(&disk->rq_list)) 4128c2ecf20Sopenharmony_ci continue; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci pd_req = list_first_entry(&disk->rq_list, 4158c2ecf20Sopenharmony_ci struct request, 4168c2ecf20Sopenharmony_ci queuelist); 4178c2ecf20Sopenharmony_ci list_del_init(&pd_req->queuelist); 4188c2ecf20Sopenharmony_ci blk_mq_start_request(pd_req); 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci } while (pd_queue != old_pos); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return pd_req != NULL; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic void run_fsm(void) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci while (1) { 4298c2ecf20Sopenharmony_ci enum action res; 4308c2ecf20Sopenharmony_ci int stop = 0; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (!phase) { 4338c2ecf20Sopenharmony_ci pd_current = pd_req->rq_disk->private_data; 4348c2ecf20Sopenharmony_ci pi_current = pd_current->pi; 4358c2ecf20Sopenharmony_ci phase = do_pd_io_start; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci switch (pd_claimed) { 4398c2ecf20Sopenharmony_ci case 0: 4408c2ecf20Sopenharmony_ci pd_claimed = 1; 4418c2ecf20Sopenharmony_ci if (!pi_schedule_claimed(pi_current, run_fsm)) 4428c2ecf20Sopenharmony_ci return; 4438c2ecf20Sopenharmony_ci fallthrough; 4448c2ecf20Sopenharmony_ci case 1: 4458c2ecf20Sopenharmony_ci pd_claimed = 2; 4468c2ecf20Sopenharmony_ci pi_current->proto->connect(pi_current); 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci switch(res = phase()) { 4508c2ecf20Sopenharmony_ci case Ok: case Fail: { 4518c2ecf20Sopenharmony_ci blk_status_t err; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci err = res == Ok ? 0 : BLK_STS_IOERR; 4548c2ecf20Sopenharmony_ci pi_disconnect(pi_current); 4558c2ecf20Sopenharmony_ci pd_claimed = 0; 4568c2ecf20Sopenharmony_ci phase = NULL; 4578c2ecf20Sopenharmony_ci spin_lock_irq(&pd_lock); 4588c2ecf20Sopenharmony_ci if (!blk_update_request(pd_req, err, 4598c2ecf20Sopenharmony_ci blk_rq_cur_bytes(pd_req))) { 4608c2ecf20Sopenharmony_ci __blk_mq_end_request(pd_req, err); 4618c2ecf20Sopenharmony_ci pd_req = NULL; 4628c2ecf20Sopenharmony_ci stop = !set_next_request(); 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci spin_unlock_irq(&pd_lock); 4658c2ecf20Sopenharmony_ci if (stop) 4668c2ecf20Sopenharmony_ci return; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci fallthrough; 4698c2ecf20Sopenharmony_ci case Hold: 4708c2ecf20Sopenharmony_ci schedule_fsm(); 4718c2ecf20Sopenharmony_ci return; 4728c2ecf20Sopenharmony_ci case Wait: 4738c2ecf20Sopenharmony_ci pi_disconnect(pi_current); 4748c2ecf20Sopenharmony_ci pd_claimed = 0; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic int pd_retries = 0; /* i/o error retry count */ 4808c2ecf20Sopenharmony_cistatic int pd_block; /* address of next requested block */ 4818c2ecf20Sopenharmony_cistatic int pd_count; /* number of blocks still to do */ 4828c2ecf20Sopenharmony_cistatic int pd_run; /* sectors in current cluster */ 4838c2ecf20Sopenharmony_cistatic char *pd_buf; /* buffer for request in progress */ 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic enum action do_pd_io_start(void) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci switch (req_op(pd_req)) { 4888c2ecf20Sopenharmony_ci case REQ_OP_DRV_IN: 4898c2ecf20Sopenharmony_ci phase = pd_special; 4908c2ecf20Sopenharmony_ci return pd_special(); 4918c2ecf20Sopenharmony_ci case REQ_OP_READ: 4928c2ecf20Sopenharmony_ci case REQ_OP_WRITE: 4938c2ecf20Sopenharmony_ci pd_block = blk_rq_pos(pd_req); 4948c2ecf20Sopenharmony_ci pd_count = blk_rq_cur_sectors(pd_req); 4958c2ecf20Sopenharmony_ci if (pd_block + pd_count > get_capacity(pd_req->rq_disk)) 4968c2ecf20Sopenharmony_ci return Fail; 4978c2ecf20Sopenharmony_ci pd_run = blk_rq_sectors(pd_req); 4988c2ecf20Sopenharmony_ci pd_buf = bio_data(pd_req->bio); 4998c2ecf20Sopenharmony_ci pd_retries = 0; 5008c2ecf20Sopenharmony_ci if (req_op(pd_req) == REQ_OP_READ) 5018c2ecf20Sopenharmony_ci return do_pd_read_start(); 5028c2ecf20Sopenharmony_ci else 5038c2ecf20Sopenharmony_ci return do_pd_write_start(); 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci return Fail; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic enum action pd_special(void) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci struct pd_req *req = blk_mq_rq_to_pdu(pd_req); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci return req->func(pd_current); 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic int pd_next_buf(void) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci unsigned long saved_flags; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci pd_count--; 5208c2ecf20Sopenharmony_ci pd_run--; 5218c2ecf20Sopenharmony_ci pd_buf += 512; 5228c2ecf20Sopenharmony_ci pd_block++; 5238c2ecf20Sopenharmony_ci if (!pd_run) 5248c2ecf20Sopenharmony_ci return 1; 5258c2ecf20Sopenharmony_ci if (pd_count) 5268c2ecf20Sopenharmony_ci return 0; 5278c2ecf20Sopenharmony_ci spin_lock_irqsave(&pd_lock, saved_flags); 5288c2ecf20Sopenharmony_ci if (!blk_update_request(pd_req, 0, blk_rq_cur_bytes(pd_req))) { 5298c2ecf20Sopenharmony_ci __blk_mq_end_request(pd_req, 0); 5308c2ecf20Sopenharmony_ci pd_req = NULL; 5318c2ecf20Sopenharmony_ci pd_count = 0; 5328c2ecf20Sopenharmony_ci pd_buf = NULL; 5338c2ecf20Sopenharmony_ci } else { 5348c2ecf20Sopenharmony_ci pd_count = blk_rq_cur_sectors(pd_req); 5358c2ecf20Sopenharmony_ci pd_buf = bio_data(pd_req->bio); 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pd_lock, saved_flags); 5388c2ecf20Sopenharmony_ci return !pd_count; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic unsigned long pd_timeout; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic enum action do_pd_read_start(void) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci if (pd_wait_for(pd_current, STAT_READY, "do_pd_read") & STAT_ERR) { 5468c2ecf20Sopenharmony_ci if (pd_retries < PD_MAX_RETRIES) { 5478c2ecf20Sopenharmony_ci pd_retries++; 5488c2ecf20Sopenharmony_ci return Wait; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci return Fail; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci pd_ide_command(pd_current, IDE_READ, pd_block, pd_run); 5538c2ecf20Sopenharmony_ci phase = do_pd_read_drq; 5548c2ecf20Sopenharmony_ci pd_timeout = jiffies + PD_TMO; 5558c2ecf20Sopenharmony_ci return Hold; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic enum action do_pd_write_start(void) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci if (pd_wait_for(pd_current, STAT_READY, "do_pd_write") & STAT_ERR) { 5618c2ecf20Sopenharmony_ci if (pd_retries < PD_MAX_RETRIES) { 5628c2ecf20Sopenharmony_ci pd_retries++; 5638c2ecf20Sopenharmony_ci return Wait; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci return Fail; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci pd_ide_command(pd_current, IDE_WRITE, pd_block, pd_run); 5688c2ecf20Sopenharmony_ci while (1) { 5698c2ecf20Sopenharmony_ci if (pd_wait_for(pd_current, STAT_DRQ, "do_pd_write_drq") & STAT_ERR) { 5708c2ecf20Sopenharmony_ci if (pd_retries < PD_MAX_RETRIES) { 5718c2ecf20Sopenharmony_ci pd_retries++; 5728c2ecf20Sopenharmony_ci return Wait; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci return Fail; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci pi_write_block(pd_current->pi, pd_buf, 512); 5778c2ecf20Sopenharmony_ci if (pd_next_buf()) 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci phase = do_pd_write_done; 5818c2ecf20Sopenharmony_ci pd_timeout = jiffies + PD_TMO; 5828c2ecf20Sopenharmony_ci return Hold; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic inline int pd_ready(void) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci return !(status_reg(pd_current) & STAT_BUSY); 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic enum action do_pd_read_drq(void) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci if (!pd_ready() && !time_after_eq(jiffies, pd_timeout)) 5938c2ecf20Sopenharmony_ci return Hold; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci while (1) { 5968c2ecf20Sopenharmony_ci if (pd_wait_for(pd_current, STAT_DRQ, "do_pd_read_drq") & STAT_ERR) { 5978c2ecf20Sopenharmony_ci if (pd_retries < PD_MAX_RETRIES) { 5988c2ecf20Sopenharmony_ci pd_retries++; 5998c2ecf20Sopenharmony_ci phase = do_pd_read_start; 6008c2ecf20Sopenharmony_ci return Wait; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci return Fail; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci pi_read_block(pd_current->pi, pd_buf, 512); 6058c2ecf20Sopenharmony_ci if (pd_next_buf()) 6068c2ecf20Sopenharmony_ci break; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci return Ok; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic enum action do_pd_write_done(void) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci if (!pd_ready() && !time_after_eq(jiffies, pd_timeout)) 6148c2ecf20Sopenharmony_ci return Hold; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (pd_wait_for(pd_current, STAT_READY, "do_pd_write_done") & STAT_ERR) { 6178c2ecf20Sopenharmony_ci if (pd_retries < PD_MAX_RETRIES) { 6188c2ecf20Sopenharmony_ci pd_retries++; 6198c2ecf20Sopenharmony_ci phase = do_pd_write_start; 6208c2ecf20Sopenharmony_ci return Wait; 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci return Fail; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci return Ok; 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci/* special io requests */ 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci/* According to the ATA standard, the default CHS geometry should be 6308c2ecf20Sopenharmony_ci available following a reset. Some Western Digital drives come up 6318c2ecf20Sopenharmony_ci in a mode where only LBA addresses are accepted until the device 6328c2ecf20Sopenharmony_ci parameters are initialised. 6338c2ecf20Sopenharmony_ci*/ 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic void pd_init_dev_parms(struct pd_unit *disk) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci pd_wait_for(disk, 0, DBMSG("before init_dev_parms")); 6388c2ecf20Sopenharmony_ci pd_send_command(disk, disk->sectors, 0, disk->heads - 1, 0, 0, 6398c2ecf20Sopenharmony_ci IDE_INIT_DEV_PARMS); 6408c2ecf20Sopenharmony_ci udelay(300); 6418c2ecf20Sopenharmony_ci pd_wait_for(disk, 0, "Initialise device parameters"); 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic enum action pd_door_lock(struct pd_unit *disk) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci if (!(pd_wait_for(disk, STAT_READY, "Lock") & STAT_ERR)) { 6478c2ecf20Sopenharmony_ci pd_send_command(disk, 1, 0, 0, 0, 0, IDE_DOORLOCK); 6488c2ecf20Sopenharmony_ci pd_wait_for(disk, STAT_READY, "Lock done"); 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci return Ok; 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic enum action pd_door_unlock(struct pd_unit *disk) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci if (!(pd_wait_for(disk, STAT_READY, "Lock") & STAT_ERR)) { 6568c2ecf20Sopenharmony_ci pd_send_command(disk, 1, 0, 0, 0, 0, IDE_DOORUNLOCK); 6578c2ecf20Sopenharmony_ci pd_wait_for(disk, STAT_READY, "Lock done"); 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci return Ok; 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cistatic enum action pd_eject(struct pd_unit *disk) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci pd_wait_for(disk, 0, DBMSG("before unlock on eject")); 6658c2ecf20Sopenharmony_ci pd_send_command(disk, 1, 0, 0, 0, 0, IDE_DOORUNLOCK); 6668c2ecf20Sopenharmony_ci pd_wait_for(disk, 0, DBMSG("after unlock on eject")); 6678c2ecf20Sopenharmony_ci pd_wait_for(disk, 0, DBMSG("before eject")); 6688c2ecf20Sopenharmony_ci pd_send_command(disk, 0, 0, 0, 0, 0, IDE_EJECT); 6698c2ecf20Sopenharmony_ci pd_wait_for(disk, 0, DBMSG("after eject")); 6708c2ecf20Sopenharmony_ci return Ok; 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cistatic enum action pd_media_check(struct pd_unit *disk) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci int r = pd_wait_for(disk, STAT_READY, DBMSG("before media_check")); 6768c2ecf20Sopenharmony_ci if (!(r & STAT_ERR)) { 6778c2ecf20Sopenharmony_ci pd_send_command(disk, 1, 1, 0, 0, 0, IDE_READ_VRFY); 6788c2ecf20Sopenharmony_ci r = pd_wait_for(disk, STAT_READY, DBMSG("RDY after READ_VRFY")); 6798c2ecf20Sopenharmony_ci } else 6808c2ecf20Sopenharmony_ci disk->changed = 1; /* say changed if other error */ 6818c2ecf20Sopenharmony_ci if (r & ERR_MC) { 6828c2ecf20Sopenharmony_ci disk->changed = 1; 6838c2ecf20Sopenharmony_ci pd_send_command(disk, 1, 0, 0, 0, 0, IDE_ACKCHANGE); 6848c2ecf20Sopenharmony_ci pd_wait_for(disk, STAT_READY, DBMSG("RDY after ACKCHANGE")); 6858c2ecf20Sopenharmony_ci pd_send_command(disk, 1, 1, 0, 0, 0, IDE_READ_VRFY); 6868c2ecf20Sopenharmony_ci r = pd_wait_for(disk, STAT_READY, DBMSG("RDY after VRFY")); 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci return Ok; 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic void pd_standby_off(struct pd_unit *disk) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci pd_wait_for(disk, 0, DBMSG("before STANDBY")); 6948c2ecf20Sopenharmony_ci pd_send_command(disk, 0, 0, 0, 0, 0, IDE_STANDBY); 6958c2ecf20Sopenharmony_ci pd_wait_for(disk, 0, DBMSG("after STANDBY")); 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cistatic enum action pd_identify(struct pd_unit *disk) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci int j; 7018c2ecf20Sopenharmony_ci char id[PD_ID_LEN + 1]; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci/* WARNING: here there may be dragons. reset() applies to both drives, 7048c2ecf20Sopenharmony_ci but we call it only on probing the MASTER. This should allow most 7058c2ecf20Sopenharmony_ci common configurations to work, but be warned that a reset can clear 7068c2ecf20Sopenharmony_ci settings on the SLAVE drive. 7078c2ecf20Sopenharmony_ci*/ 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (disk->drive == 0) 7108c2ecf20Sopenharmony_ci pd_reset(disk); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci write_reg(disk, 6, DRIVE(disk)); 7138c2ecf20Sopenharmony_ci pd_wait_for(disk, 0, DBMSG("before IDENT")); 7148c2ecf20Sopenharmony_ci pd_send_command(disk, 1, 0, 0, 0, 0, IDE_IDENTIFY); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (pd_wait_for(disk, STAT_DRQ, DBMSG("IDENT DRQ")) & STAT_ERR) 7178c2ecf20Sopenharmony_ci return Fail; 7188c2ecf20Sopenharmony_ci pi_read_block(disk->pi, pd_scratch, 512); 7198c2ecf20Sopenharmony_ci disk->can_lba = pd_scratch[99] & 2; 7208c2ecf20Sopenharmony_ci disk->sectors = le16_to_cpu(*(__le16 *) (pd_scratch + 12)); 7218c2ecf20Sopenharmony_ci disk->heads = le16_to_cpu(*(__le16 *) (pd_scratch + 6)); 7228c2ecf20Sopenharmony_ci disk->cylinders = le16_to_cpu(*(__le16 *) (pd_scratch + 2)); 7238c2ecf20Sopenharmony_ci if (disk->can_lba) 7248c2ecf20Sopenharmony_ci disk->capacity = le32_to_cpu(*(__le32 *) (pd_scratch + 120)); 7258c2ecf20Sopenharmony_ci else 7268c2ecf20Sopenharmony_ci disk->capacity = disk->sectors * disk->heads * disk->cylinders; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci for (j = 0; j < PD_ID_LEN; j++) 7298c2ecf20Sopenharmony_ci id[j ^ 1] = pd_scratch[j + PD_ID_OFF]; 7308c2ecf20Sopenharmony_ci j = PD_ID_LEN - 1; 7318c2ecf20Sopenharmony_ci while ((j >= 0) && (id[j] <= 0x20)) 7328c2ecf20Sopenharmony_ci j--; 7338c2ecf20Sopenharmony_ci j++; 7348c2ecf20Sopenharmony_ci id[j] = 0; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci disk->removable = pd_scratch[0] & 0x80; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci printk("%s: %s, %s, %d blocks [%dM], (%d/%d/%d), %s media\n", 7398c2ecf20Sopenharmony_ci disk->name, id, 7408c2ecf20Sopenharmony_ci disk->drive ? "slave" : "master", 7418c2ecf20Sopenharmony_ci disk->capacity, disk->capacity / 2048, 7428c2ecf20Sopenharmony_ci disk->cylinders, disk->heads, disk->sectors, 7438c2ecf20Sopenharmony_ci disk->removable ? "removable" : "fixed"); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci if (disk->capacity) 7468c2ecf20Sopenharmony_ci pd_init_dev_parms(disk); 7478c2ecf20Sopenharmony_ci if (!disk->standby) 7488c2ecf20Sopenharmony_ci pd_standby_off(disk); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci return Ok; 7518c2ecf20Sopenharmony_ci} 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci/* end of io request engine */ 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_cistatic blk_status_t pd_queue_rq(struct blk_mq_hw_ctx *hctx, 7568c2ecf20Sopenharmony_ci const struct blk_mq_queue_data *bd) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci struct pd_unit *disk = hctx->queue->queuedata; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci spin_lock_irq(&pd_lock); 7618c2ecf20Sopenharmony_ci if (!pd_req) { 7628c2ecf20Sopenharmony_ci pd_req = bd->rq; 7638c2ecf20Sopenharmony_ci blk_mq_start_request(pd_req); 7648c2ecf20Sopenharmony_ci } else 7658c2ecf20Sopenharmony_ci list_add_tail(&bd->rq->queuelist, &disk->rq_list); 7668c2ecf20Sopenharmony_ci spin_unlock_irq(&pd_lock); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci run_fsm(); 7698c2ecf20Sopenharmony_ci return BLK_STS_OK; 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic int pd_special_command(struct pd_unit *disk, 7738c2ecf20Sopenharmony_ci enum action (*func)(struct pd_unit *disk)) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci struct request *rq; 7768c2ecf20Sopenharmony_ci struct pd_req *req; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci rq = blk_get_request(disk->gd->queue, REQ_OP_DRV_IN, 0); 7798c2ecf20Sopenharmony_ci if (IS_ERR(rq)) 7808c2ecf20Sopenharmony_ci return PTR_ERR(rq); 7818c2ecf20Sopenharmony_ci req = blk_mq_rq_to_pdu(rq); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci req->func = func; 7848c2ecf20Sopenharmony_ci blk_execute_rq(disk->gd->queue, disk->gd, rq, 0); 7858c2ecf20Sopenharmony_ci blk_put_request(rq); 7868c2ecf20Sopenharmony_ci return 0; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci/* kernel glue structures */ 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_cistatic int pd_open(struct block_device *bdev, fmode_t mode) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci struct pd_unit *disk = bdev->bd_disk->private_data; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci mutex_lock(&pd_mutex); 7968c2ecf20Sopenharmony_ci disk->access++; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (disk->removable) { 7998c2ecf20Sopenharmony_ci pd_special_command(disk, pd_media_check); 8008c2ecf20Sopenharmony_ci pd_special_command(disk, pd_door_lock); 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci mutex_unlock(&pd_mutex); 8038c2ecf20Sopenharmony_ci return 0; 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cistatic int pd_getgeo(struct block_device *bdev, struct hd_geometry *geo) 8078c2ecf20Sopenharmony_ci{ 8088c2ecf20Sopenharmony_ci struct pd_unit *disk = bdev->bd_disk->private_data; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci if (disk->alt_geom) { 8118c2ecf20Sopenharmony_ci geo->heads = PD_LOG_HEADS; 8128c2ecf20Sopenharmony_ci geo->sectors = PD_LOG_SECTS; 8138c2ecf20Sopenharmony_ci geo->cylinders = disk->capacity / (geo->heads * geo->sectors); 8148c2ecf20Sopenharmony_ci } else { 8158c2ecf20Sopenharmony_ci geo->heads = disk->heads; 8168c2ecf20Sopenharmony_ci geo->sectors = disk->sectors; 8178c2ecf20Sopenharmony_ci geo->cylinders = disk->cylinders; 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci return 0; 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic int pd_ioctl(struct block_device *bdev, fmode_t mode, 8248c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci struct pd_unit *disk = bdev->bd_disk->private_data; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci switch (cmd) { 8298c2ecf20Sopenharmony_ci case CDROMEJECT: 8308c2ecf20Sopenharmony_ci mutex_lock(&pd_mutex); 8318c2ecf20Sopenharmony_ci if (disk->access == 1) 8328c2ecf20Sopenharmony_ci pd_special_command(disk, pd_eject); 8338c2ecf20Sopenharmony_ci mutex_unlock(&pd_mutex); 8348c2ecf20Sopenharmony_ci return 0; 8358c2ecf20Sopenharmony_ci default: 8368c2ecf20Sopenharmony_ci return -EINVAL; 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistatic void pd_release(struct gendisk *p, fmode_t mode) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci struct pd_unit *disk = p->private_data; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci mutex_lock(&pd_mutex); 8458c2ecf20Sopenharmony_ci if (!--disk->access && disk->removable) 8468c2ecf20Sopenharmony_ci pd_special_command(disk, pd_door_unlock); 8478c2ecf20Sopenharmony_ci mutex_unlock(&pd_mutex); 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic unsigned int pd_check_events(struct gendisk *p, unsigned int clearing) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci struct pd_unit *disk = p->private_data; 8538c2ecf20Sopenharmony_ci int r; 8548c2ecf20Sopenharmony_ci if (!disk->removable) 8558c2ecf20Sopenharmony_ci return 0; 8568c2ecf20Sopenharmony_ci pd_special_command(disk, pd_media_check); 8578c2ecf20Sopenharmony_ci r = disk->changed; 8588c2ecf20Sopenharmony_ci disk->changed = 0; 8598c2ecf20Sopenharmony_ci return r ? DISK_EVENT_MEDIA_CHANGE : 0; 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cistatic int pd_revalidate(struct gendisk *p) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci struct pd_unit *disk = p->private_data; 8658c2ecf20Sopenharmony_ci if (pd_special_command(disk, pd_identify) == 0) 8668c2ecf20Sopenharmony_ci set_capacity(p, disk->capacity); 8678c2ecf20Sopenharmony_ci else 8688c2ecf20Sopenharmony_ci set_capacity(p, 0); 8698c2ecf20Sopenharmony_ci return 0; 8708c2ecf20Sopenharmony_ci} 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cistatic const struct block_device_operations pd_fops = { 8738c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 8748c2ecf20Sopenharmony_ci .open = pd_open, 8758c2ecf20Sopenharmony_ci .release = pd_release, 8768c2ecf20Sopenharmony_ci .ioctl = pd_ioctl, 8778c2ecf20Sopenharmony_ci .compat_ioctl = pd_ioctl, 8788c2ecf20Sopenharmony_ci .getgeo = pd_getgeo, 8798c2ecf20Sopenharmony_ci .check_events = pd_check_events, 8808c2ecf20Sopenharmony_ci .revalidate_disk= pd_revalidate 8818c2ecf20Sopenharmony_ci}; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci/* probing */ 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cistatic const struct blk_mq_ops pd_mq_ops = { 8868c2ecf20Sopenharmony_ci .queue_rq = pd_queue_rq, 8878c2ecf20Sopenharmony_ci}; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_cistatic void pd_probe_drive(struct pd_unit *disk) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci struct gendisk *p; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci p = alloc_disk(1 << PD_BITS); 8948c2ecf20Sopenharmony_ci if (!p) 8958c2ecf20Sopenharmony_ci return; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci strcpy(p->disk_name, disk->name); 8988c2ecf20Sopenharmony_ci p->fops = &pd_fops; 8998c2ecf20Sopenharmony_ci p->major = major; 9008c2ecf20Sopenharmony_ci p->first_minor = (disk - pd) << PD_BITS; 9018c2ecf20Sopenharmony_ci p->events = DISK_EVENT_MEDIA_CHANGE; 9028c2ecf20Sopenharmony_ci disk->gd = p; 9038c2ecf20Sopenharmony_ci p->private_data = disk; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci memset(&disk->tag_set, 0, sizeof(disk->tag_set)); 9068c2ecf20Sopenharmony_ci disk->tag_set.ops = &pd_mq_ops; 9078c2ecf20Sopenharmony_ci disk->tag_set.cmd_size = sizeof(struct pd_req); 9088c2ecf20Sopenharmony_ci disk->tag_set.nr_hw_queues = 1; 9098c2ecf20Sopenharmony_ci disk->tag_set.nr_maps = 1; 9108c2ecf20Sopenharmony_ci disk->tag_set.queue_depth = 2; 9118c2ecf20Sopenharmony_ci disk->tag_set.numa_node = NUMA_NO_NODE; 9128c2ecf20Sopenharmony_ci disk->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_BLOCKING; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (blk_mq_alloc_tag_set(&disk->tag_set)) 9158c2ecf20Sopenharmony_ci return; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci p->queue = blk_mq_init_queue(&disk->tag_set); 9188c2ecf20Sopenharmony_ci if (IS_ERR(p->queue)) { 9198c2ecf20Sopenharmony_ci blk_mq_free_tag_set(&disk->tag_set); 9208c2ecf20Sopenharmony_ci p->queue = NULL; 9218c2ecf20Sopenharmony_ci return; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci p->queue->queuedata = disk; 9258c2ecf20Sopenharmony_ci blk_queue_max_hw_sectors(p->queue, cluster); 9268c2ecf20Sopenharmony_ci blk_queue_bounce_limit(p->queue, BLK_BOUNCE_HIGH); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (disk->drive == -1) { 9298c2ecf20Sopenharmony_ci for (disk->drive = 0; disk->drive <= 1; disk->drive++) 9308c2ecf20Sopenharmony_ci if (pd_special_command(disk, pd_identify) == 0) 9318c2ecf20Sopenharmony_ci return; 9328c2ecf20Sopenharmony_ci } else if (pd_special_command(disk, pd_identify) == 0) 9338c2ecf20Sopenharmony_ci return; 9348c2ecf20Sopenharmony_ci disk->gd = NULL; 9358c2ecf20Sopenharmony_ci put_disk(p); 9368c2ecf20Sopenharmony_ci} 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_cistatic int pd_detect(void) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci int found = 0, unit, pd_drive_count = 0; 9418c2ecf20Sopenharmony_ci struct pd_unit *disk; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci for (unit = 0; unit < PD_UNITS; unit++) { 9448c2ecf20Sopenharmony_ci int *parm = *drives[unit]; 9458c2ecf20Sopenharmony_ci struct pd_unit *disk = pd + unit; 9468c2ecf20Sopenharmony_ci disk->pi = &disk->pia; 9478c2ecf20Sopenharmony_ci disk->access = 0; 9488c2ecf20Sopenharmony_ci disk->changed = 1; 9498c2ecf20Sopenharmony_ci disk->capacity = 0; 9508c2ecf20Sopenharmony_ci disk->drive = parm[D_SLV]; 9518c2ecf20Sopenharmony_ci snprintf(disk->name, PD_NAMELEN, "%s%c", name, 'a'+unit); 9528c2ecf20Sopenharmony_ci disk->alt_geom = parm[D_GEO]; 9538c2ecf20Sopenharmony_ci disk->standby = parm[D_SBY]; 9548c2ecf20Sopenharmony_ci if (parm[D_PRT]) 9558c2ecf20Sopenharmony_ci pd_drive_count++; 9568c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&disk->rq_list); 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci par_drv = pi_register_driver(name); 9608c2ecf20Sopenharmony_ci if (!par_drv) { 9618c2ecf20Sopenharmony_ci pr_err("failed to register %s driver\n", name); 9628c2ecf20Sopenharmony_ci return -1; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci if (pd_drive_count == 0) { /* nothing spec'd - so autoprobe for 1 */ 9668c2ecf20Sopenharmony_ci disk = pd; 9678c2ecf20Sopenharmony_ci if (pi_init(disk->pi, 1, -1, -1, -1, -1, -1, pd_scratch, 9688c2ecf20Sopenharmony_ci PI_PD, verbose, disk->name)) { 9698c2ecf20Sopenharmony_ci pd_probe_drive(disk); 9708c2ecf20Sopenharmony_ci if (!disk->gd) 9718c2ecf20Sopenharmony_ci pi_release(disk->pi); 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci } else { 9758c2ecf20Sopenharmony_ci for (unit = 0, disk = pd; unit < PD_UNITS; unit++, disk++) { 9768c2ecf20Sopenharmony_ci int *parm = *drives[unit]; 9778c2ecf20Sopenharmony_ci if (!parm[D_PRT]) 9788c2ecf20Sopenharmony_ci continue; 9798c2ecf20Sopenharmony_ci if (pi_init(disk->pi, 0, parm[D_PRT], parm[D_MOD], 9808c2ecf20Sopenharmony_ci parm[D_UNI], parm[D_PRO], parm[D_DLY], 9818c2ecf20Sopenharmony_ci pd_scratch, PI_PD, verbose, disk->name)) { 9828c2ecf20Sopenharmony_ci pd_probe_drive(disk); 9838c2ecf20Sopenharmony_ci if (!disk->gd) 9848c2ecf20Sopenharmony_ci pi_release(disk->pi); 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci for (unit = 0, disk = pd; unit < PD_UNITS; unit++, disk++) { 9898c2ecf20Sopenharmony_ci if (disk->gd) { 9908c2ecf20Sopenharmony_ci set_capacity(disk->gd, disk->capacity); 9918c2ecf20Sopenharmony_ci add_disk(disk->gd); 9928c2ecf20Sopenharmony_ci found = 1; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci if (!found) { 9968c2ecf20Sopenharmony_ci printk("%s: no valid drive found\n", name); 9978c2ecf20Sopenharmony_ci pi_unregister_driver(par_drv); 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci return found; 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistatic int __init pd_init(void) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci if (disable) 10058c2ecf20Sopenharmony_ci goto out1; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci if (register_blkdev(major, name)) 10088c2ecf20Sopenharmony_ci goto out1; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci printk("%s: %s version %s, major %d, cluster %d, nice %d\n", 10118c2ecf20Sopenharmony_ci name, name, PD_VERSION, major, cluster, nice); 10128c2ecf20Sopenharmony_ci if (!pd_detect()) 10138c2ecf20Sopenharmony_ci goto out2; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci return 0; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ciout2: 10188c2ecf20Sopenharmony_ci unregister_blkdev(major, name); 10198c2ecf20Sopenharmony_ciout1: 10208c2ecf20Sopenharmony_ci return -ENODEV; 10218c2ecf20Sopenharmony_ci} 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_cistatic void __exit pd_exit(void) 10248c2ecf20Sopenharmony_ci{ 10258c2ecf20Sopenharmony_ci struct pd_unit *disk; 10268c2ecf20Sopenharmony_ci int unit; 10278c2ecf20Sopenharmony_ci unregister_blkdev(major, name); 10288c2ecf20Sopenharmony_ci for (unit = 0, disk = pd; unit < PD_UNITS; unit++, disk++) { 10298c2ecf20Sopenharmony_ci struct gendisk *p = disk->gd; 10308c2ecf20Sopenharmony_ci if (p) { 10318c2ecf20Sopenharmony_ci disk->gd = NULL; 10328c2ecf20Sopenharmony_ci del_gendisk(p); 10338c2ecf20Sopenharmony_ci blk_cleanup_queue(p->queue); 10348c2ecf20Sopenharmony_ci blk_mq_free_tag_set(&disk->tag_set); 10358c2ecf20Sopenharmony_ci put_disk(p); 10368c2ecf20Sopenharmony_ci pi_release(disk->pi); 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci} 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 10428c2ecf20Sopenharmony_cimodule_init(pd_init) 10438c2ecf20Sopenharmony_cimodule_exit(pd_exit) 1044