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