xref: /freebsd/sys/cam/ata/ata_da.c (revision 6fb5c84ea229af35e1df446cf09daaeb407e6d15)
152c9ce25SScott Long /*-
252c9ce25SScott Long  * Copyright (c) 2009 Alexander Motin <mav@FreeBSD.org>
352c9ce25SScott Long  * All rights reserved.
452c9ce25SScott Long  *
552c9ce25SScott Long  * Redistribution and use in source and binary forms, with or without
652c9ce25SScott Long  * modification, are permitted provided that the following conditions
752c9ce25SScott Long  * are met:
852c9ce25SScott Long  * 1. Redistributions of source code must retain the above copyright
952c9ce25SScott Long  *    notice, this list of conditions and the following disclaimer,
1052c9ce25SScott Long  *    without modification, immediately at the beginning of the file.
1152c9ce25SScott Long  * 2. Redistributions in binary form must reproduce the above copyright
1252c9ce25SScott Long  *    notice, this list of conditions and the following disclaimer in the
1352c9ce25SScott Long  *    documentation and/or other materials provided with the distribution.
1452c9ce25SScott Long  *
1552c9ce25SScott Long  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1652c9ce25SScott Long  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1752c9ce25SScott Long  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1852c9ce25SScott Long  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1952c9ce25SScott Long  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2052c9ce25SScott Long  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2152c9ce25SScott Long  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2252c9ce25SScott Long  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2352c9ce25SScott Long  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2452c9ce25SScott Long  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2552c9ce25SScott Long  */
2652c9ce25SScott Long 
2752c9ce25SScott Long #include <sys/cdefs.h>
2852c9ce25SScott Long __FBSDID("$FreeBSD$");
2952c9ce25SScott Long 
30e3a6d3a4SAlexander Motin #include "opt_ada.h"
31e3a6d3a4SAlexander Motin 
3252c9ce25SScott Long #include <sys/param.h>
3352c9ce25SScott Long 
3452c9ce25SScott Long #ifdef _KERNEL
3552c9ce25SScott Long #include <sys/systm.h>
3652c9ce25SScott Long #include <sys/kernel.h>
3752c9ce25SScott Long #include <sys/bio.h>
3852c9ce25SScott Long #include <sys/sysctl.h>
3952c9ce25SScott Long #include <sys/taskqueue.h>
4052c9ce25SScott Long #include <sys/lock.h>
4152c9ce25SScott Long #include <sys/mutex.h>
4252c9ce25SScott Long #include <sys/conf.h>
4352c9ce25SScott Long #include <sys/devicestat.h>
4452c9ce25SScott Long #include <sys/eventhandler.h>
4552c9ce25SScott Long #include <sys/malloc.h>
4652c9ce25SScott Long #include <sys/cons.h>
47fd104c15SRebecca Cran #include <sys/reboot.h>
4852c9ce25SScott Long #include <geom/geom_disk.h>
4952c9ce25SScott Long #endif /* _KERNEL */
5052c9ce25SScott Long 
5152c9ce25SScott Long #ifndef _KERNEL
5252c9ce25SScott Long #include <stdio.h>
5352c9ce25SScott Long #include <string.h>
5452c9ce25SScott Long #endif /* _KERNEL */
5552c9ce25SScott Long 
5652c9ce25SScott Long #include <cam/cam.h>
5752c9ce25SScott Long #include <cam/cam_ccb.h>
5852c9ce25SScott Long #include <cam/cam_periph.h>
5952c9ce25SScott Long #include <cam/cam_xpt_periph.h>
6052c9ce25SScott Long #include <cam/cam_sim.h>
6152c9ce25SScott Long 
6252c9ce25SScott Long #include <cam/ata/ata_all.h>
6352c9ce25SScott Long 
644461491bSMarius Strobl #include <machine/md_var.h>	/* geometry translation */
654461491bSMarius Strobl 
6652c9ce25SScott Long #ifdef _KERNEL
6752c9ce25SScott Long 
6852c9ce25SScott Long #define ATA_MAX_28BIT_LBA               268435455UL
6952c9ce25SScott Long 
7052c9ce25SScott Long typedef enum {
711ed6aaf9SAlexander Motin 	ADA_STATE_RAHEAD,
72f513d14cSAlexander Motin 	ADA_STATE_WCACHE,
731e637ba6SAlexander Motin 	ADA_STATE_NORMAL
7452c9ce25SScott Long } ada_state;
7552c9ce25SScott Long 
7652c9ce25SScott Long typedef enum {
772e1eb332SMarius Strobl 	ADA_FLAG_CAN_48BIT	= 0x0002,
782e1eb332SMarius Strobl 	ADA_FLAG_CAN_FLUSHCACHE	= 0x0004,
792e1eb332SMarius Strobl 	ADA_FLAG_CAN_NCQ	= 0x0008,
802e1eb332SMarius Strobl 	ADA_FLAG_CAN_DMA	= 0x0010,
812e1eb332SMarius Strobl 	ADA_FLAG_NEED_OTAG	= 0x0020,
822e1eb332SMarius Strobl 	ADA_FLAG_WENT_IDLE	= 0x0040,
832e1eb332SMarius Strobl 	ADA_FLAG_CAN_TRIM	= 0x0080,
842e1eb332SMarius Strobl 	ADA_FLAG_OPEN		= 0x0100,
852e1eb332SMarius Strobl 	ADA_FLAG_SCTX_INIT	= 0x0200,
862e1eb332SMarius Strobl 	ADA_FLAG_CAN_CFA        = 0x0400,
872e1eb332SMarius Strobl 	ADA_FLAG_CAN_POWERMGT   = 0x0800,
882e1eb332SMarius Strobl 	ADA_FLAG_CAN_DMA48	= 0x1000
8952c9ce25SScott Long } ada_flags;
9052c9ce25SScott Long 
9152c9ce25SScott Long typedef enum {
92d3a460d3SAlexander Motin 	ADA_Q_NONE		= 0x00,
93d3a460d3SAlexander Motin 	ADA_Q_4K		= 0x01,
9452c9ce25SScott Long } ada_quirks;
9552c9ce25SScott Long 
96*6fb5c84eSSteven Hartland #define ADA_Q_BIT_STRING	\
97*6fb5c84eSSteven Hartland 	"\020"			\
98*6fb5c84eSSteven Hartland 	"\0014K"
99*6fb5c84eSSteven Hartland 
10052c9ce25SScott Long typedef enum {
1011ed6aaf9SAlexander Motin 	ADA_CCB_RAHEAD		= 0x01,
1021ed6aaf9SAlexander Motin 	ADA_CCB_WCACHE		= 0x02,
10352c9ce25SScott Long 	ADA_CCB_BUFFER_IO	= 0x03,
10452c9ce25SScott Long 	ADA_CCB_WAITING		= 0x04,
10552c9ce25SScott Long 	ADA_CCB_DUMP		= 0x05,
1061c80ec0aSAlexander Motin 	ADA_CCB_TRIM		= 0x06,
10752c9ce25SScott Long 	ADA_CCB_TYPE_MASK	= 0x0F,
10852c9ce25SScott Long } ada_ccb_state;
10952c9ce25SScott Long 
11052c9ce25SScott Long /* Offsets into our private area for storing information */
11152c9ce25SScott Long #define ccb_state	ppriv_field0
11252c9ce25SScott Long #define ccb_bp		ppriv_ptr1
11352c9ce25SScott Long 
11452c9ce25SScott Long struct disk_params {
11552c9ce25SScott Long 	u_int8_t  heads;
11652c9ce25SScott Long 	u_int8_t  secs_per_track;
117c1bd46c2SAlexander Motin 	u_int32_t cylinders;
118c1bd46c2SAlexander Motin 	u_int32_t secsize;	/* Number of bytes/logical sector */
119c1bd46c2SAlexander Motin 	u_int64_t sectors;	/* Total number sectors */
12052c9ce25SScott Long };
12152c9ce25SScott Long 
1221524677aSAlexander Motin #define TRIM_MAX_BLOCKS	8
123c213c551SSteven Hartland #define TRIM_MAX_RANGES	(TRIM_MAX_BLOCKS * ATA_DSM_BLK_RANGES)
1241524677aSAlexander Motin #define TRIM_MAX_BIOS	(TRIM_MAX_RANGES * 4)
1251c80ec0aSAlexander Motin struct trim_request {
126c213c551SSteven Hartland 	uint8_t		data[TRIM_MAX_RANGES * ATA_DSM_RANGE_SIZE];
12737ddbd16SAlexander Motin 	struct bio	*bps[TRIM_MAX_BIOS];
1281c80ec0aSAlexander Motin };
1291c80ec0aSAlexander Motin 
13052c9ce25SScott Long struct ada_softc {
13152c9ce25SScott Long 	struct	 bio_queue_head bio_queue;
1321c80ec0aSAlexander Motin 	struct	 bio_queue_head trim_queue;
13352c9ce25SScott Long 	ada_state state;
13452c9ce25SScott Long 	ada_flags flags;
13552c9ce25SScott Long 	ada_quirks quirks;
1365f83aee5SSteven Hartland 	int	 sort_io_queue;
13752c9ce25SScott Long 	int	 ordered_tag_count;
13852c9ce25SScott Long 	int	 outstanding_cmds;
1391c80ec0aSAlexander Motin 	int	 trim_max_ranges;
1401c80ec0aSAlexander Motin 	int	 trim_running;
1411ed6aaf9SAlexander Motin 	int	 read_ahead;
142e3a6d3a4SAlexander Motin 	int	 write_cache;
143e3a6d3a4SAlexander Motin #ifdef ADA_TEST_FAILURE
144e3a6d3a4SAlexander Motin 	int      force_read_error;
145e3a6d3a4SAlexander Motin 	int      force_write_error;
146e3a6d3a4SAlexander Motin 	int      periodic_read_error;
147e3a6d3a4SAlexander Motin 	int      periodic_read_count;
148e3a6d3a4SAlexander Motin #endif
14952c9ce25SScott Long 	struct	 disk_params params;
15052c9ce25SScott Long 	struct	 disk *disk;
15152c9ce25SScott Long 	struct task		sysctl_task;
15252c9ce25SScott Long 	struct sysctl_ctx_list	sysctl_ctx;
15352c9ce25SScott Long 	struct sysctl_oid	*sysctl_tree;
15452c9ce25SScott Long 	struct callout		sendordered_c;
1551c80ec0aSAlexander Motin 	struct trim_request	trim_req;
15652c9ce25SScott Long };
15752c9ce25SScott Long 
15852c9ce25SScott Long struct ada_quirk_entry {
15952c9ce25SScott Long 	struct scsi_inquiry_pattern inq_pat;
16052c9ce25SScott Long 	ada_quirks quirks;
16152c9ce25SScott Long };
16252c9ce25SScott Long 
16330a4094fSAlexander Motin static struct ada_quirk_entry ada_quirk_table[] =
16430a4094fSAlexander Motin {
16530a4094fSAlexander Motin 	{
166d3a460d3SAlexander Motin 		/* Hitachi Advanced Format (4k) drives */
167d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Hitachi H??????????E3*", "*" },
168d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
169d3a460d3SAlexander Motin 	},
170d3a460d3SAlexander Motin 	{
171d3a460d3SAlexander Motin 		/* Samsung Advanced Format (4k) drives */
172643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG HD155UI*", "*" },
173643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
174643d1826SAlexander Motin 	},
175643d1826SAlexander Motin 	{
176643d1826SAlexander Motin 		/* Samsung Advanced Format (4k) drives */
177d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG HD204UI*", "*" },
178d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
179d3a460d3SAlexander Motin 	},
180d3a460d3SAlexander Motin 	{
181d3a460d3SAlexander Motin 		/* Seagate Barracuda Green Advanced Format (4k) drives */
182d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST????DL*", "*" },
183d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
184d3a460d3SAlexander Motin 	},
185d3a460d3SAlexander Motin 	{
186643d1826SAlexander Motin 		/* Seagate Barracuda Advanced Format (4k) drives */
187643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST???DM*", "*" },
188643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
189643d1826SAlexander Motin 	},
190643d1826SAlexander Motin 	{
191643d1826SAlexander Motin 		/* Seagate Barracuda Advanced Format (4k) drives */
192643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST????DM*", "*" },
193643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
194643d1826SAlexander Motin 	},
195643d1826SAlexander Motin 	{
196d3a460d3SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
197d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9500423AS*", "*" },
198d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
199d3a460d3SAlexander Motin 	},
200d3a460d3SAlexander Motin 	{
201d3a460d3SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
202d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9500424AS*", "*" },
203d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
204d3a460d3SAlexander Motin 	},
205d3a460d3SAlexander Motin 	{
206d3a460d3SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
207643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9640423AS*", "*" },
208643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
209643d1826SAlexander Motin 	},
210643d1826SAlexander Motin 	{
211643d1826SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
212643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9640424AS*", "*" },
213643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
214643d1826SAlexander Motin 	},
215643d1826SAlexander Motin 	{
216643d1826SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
217d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9750420AS*", "*" },
218d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
219d3a460d3SAlexander Motin 	},
220d3a460d3SAlexander Motin 	{
221d3a460d3SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
222d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9750422AS*", "*" },
223d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
224d3a460d3SAlexander Motin 	},
225d3a460d3SAlexander Motin 	{
226643d1826SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
227643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9750423AS*", "*" },
228643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
229643d1826SAlexander Motin 	},
230643d1826SAlexander Motin 	{
231d3a460d3SAlexander Motin 		/* Seagate Momentus Thin Advanced Format (4k) drives */
232d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST???LT*", "*" },
233d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
234d3a460d3SAlexander Motin 	},
235d3a460d3SAlexander Motin 	{
236d3a460d3SAlexander Motin 		/* WDC Caviar Green Advanced Format (4k) drives */
237d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD????RS*", "*" },
238d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
239d3a460d3SAlexander Motin 	},
240d3a460d3SAlexander Motin 	{
241d3a460d3SAlexander Motin 		/* WDC Caviar Green Advanced Format (4k) drives */
242d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD????RX*", "*" },
243d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
244d3a460d3SAlexander Motin 	},
245d3a460d3SAlexander Motin 	{
246d3a460d3SAlexander Motin 		/* WDC Caviar Green Advanced Format (4k) drives */
247d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD??????RS*", "*" },
248d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
249d3a460d3SAlexander Motin 	},
250d3a460d3SAlexander Motin 	{
251d3a460d3SAlexander Motin 		/* WDC Caviar Green Advanced Format (4k) drives */
252d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD??????RX*", "*" },
253d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
254d3a460d3SAlexander Motin 	},
255d3a460d3SAlexander Motin 	{
256d3a460d3SAlexander Motin 		/* WDC Scorpio Black Advanced Format (4k) drives */
257d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD???PKT*", "*" },
258d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
259d3a460d3SAlexander Motin 	},
260d3a460d3SAlexander Motin 	{
261d3a460d3SAlexander Motin 		/* WDC Scorpio Black Advanced Format (4k) drives */
262d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD?????PKT*", "*" },
263d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
264d3a460d3SAlexander Motin 	},
265d3a460d3SAlexander Motin 	{
266d3a460d3SAlexander Motin 		/* WDC Scorpio Blue Advanced Format (4k) drives */
267d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD???PVT*", "*" },
268d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
269d3a460d3SAlexander Motin 	},
270d3a460d3SAlexander Motin 	{
271d3a460d3SAlexander Motin 		/* WDC Scorpio Blue Advanced Format (4k) drives */
272d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD?????PVT*", "*" },
273d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
274d3a460d3SAlexander Motin 	},
275d3a460d3SAlexander Motin 	{
2769d3334e1SEitan Adler 		/*
2779d3334e1SEitan Adler 		 * Corsair Force 2 SSDs
2789d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
2799d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
2809d3334e1SEitan Adler 		 * PR: 169974
2819d3334e1SEitan Adler 		 */
2829d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair CSSD-F*", "*" },
2839d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
2849d3334e1SEitan Adler 	},
2859d3334e1SEitan Adler 	{
2869d3334e1SEitan Adler 		/*
2879d3334e1SEitan Adler 		 * Corsair Force 3 SSDs
2889d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
2899d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
2909d3334e1SEitan Adler 		 * PR: 169974
2919d3334e1SEitan Adler 		 */
2929d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair Force 3*", "*" },
2939d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
2949d3334e1SEitan Adler 	},
2959d3334e1SEitan Adler 	{
2969d3334e1SEitan Adler 		/*
2979d3334e1SEitan Adler 		 * OCZ Agility 3 SSDs
2989d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
2999d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3009d3334e1SEitan Adler 		 * PR: 169974
3019d3334e1SEitan Adler 		 */
3029d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-AGILITY3*", "*" },
3039d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3049d3334e1SEitan Adler 	},
3059d3334e1SEitan Adler 	{
3069d3334e1SEitan Adler 		/*
3079d3334e1SEitan Adler 		 * OCZ Vertex 2 SSDs (inc pro series)
3089d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3099d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3109d3334e1SEitan Adler 		 * PR: 169974
3119d3334e1SEitan Adler 		 */
3129d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ?VERTEX2*", "*" },
3139d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3149d3334e1SEitan Adler 	},
3159d3334e1SEitan Adler 	{
3169d3334e1SEitan Adler 		/*
3179d3334e1SEitan Adler 		 * OCZ Vertex 3 SSDs
3189d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3199d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3209d3334e1SEitan Adler 		 * PR: 169974
3219d3334e1SEitan Adler 		 */
3229d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-VERTEX3*", "*" },
3239d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3249d3334e1SEitan Adler 	},
3259d3334e1SEitan Adler 	{
3269d3334e1SEitan Adler 		/*
3279d3334e1SEitan Adler 		 * SuperTalent TeraDrive CT SSDs
3289d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3299d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3309d3334e1SEitan Adler 		 * PR: 169974
3319d3334e1SEitan Adler 		 */
3329d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "FTM??CT25H*", "*" },
3339d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3349d3334e1SEitan Adler 	},
3359d3334e1SEitan Adler 	{
3369d3334e1SEitan Adler 		/*
3379d3334e1SEitan Adler 		 * Crucial RealSSD C300 SSDs
3389d3334e1SEitan Adler 		 * 4k optimised
3399d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3409d3334e1SEitan Adler 		 * PR: 169974
3419d3334e1SEitan Adler 		 */
3429d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "C300-CTFDDAC???MAG*",
3439d3334e1SEitan Adler 		"*" }, /*quirks*/ADA_Q_4K
3449d3334e1SEitan Adler 	},
3459d3334e1SEitan Adler 	{
3469d3334e1SEitan Adler 		/*
3479d3334e1SEitan Adler 		 * XceedIOPS SATA SSDs
3489d3334e1SEitan Adler 		 * 4k optimised
3499d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3509d3334e1SEitan Adler 		 * PR: 169974
3519d3334e1SEitan Adler 		 */
3529d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SG9XCS2D*", "*" },
3539d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3549d3334e1SEitan Adler 	},
3559d3334e1SEitan Adler 	{
3569d3334e1SEitan Adler 		/*
357883db1c1SEitan Adler 		 * Intel 320 Series SSDs
358883db1c1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
359883db1c1SEitan Adler 		 */
360883db1c1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSA2CW*", "*" },
361883db1c1SEitan Adler 		/*quirks*/ADA_Q_4K
362883db1c1SEitan Adler 	},
363883db1c1SEitan Adler 	{
364883db1c1SEitan Adler 		/*
3659d3334e1SEitan Adler 		 * Intel 330 Series SSDs
3669d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3679d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3689d3334e1SEitan Adler 		 * PR: 169974
3699d3334e1SEitan Adler 		 */
3709d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSC2ct*", "*" },
3719d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3729d3334e1SEitan Adler 	},
3739d3334e1SEitan Adler 	{
3749d3334e1SEitan Adler 		/*
375883db1c1SEitan Adler 		 * Intel 510 Series SSDs
376883db1c1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
377883db1c1SEitan Adler 		 */
378883db1c1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSC2MH*", "*" },
379883db1c1SEitan Adler 		/*quirks*/ADA_Q_4K
380883db1c1SEitan Adler 	},
381883db1c1SEitan Adler 	{
382883db1c1SEitan Adler 		/*
3839d3334e1SEitan Adler 		 * OCZ Deneva R Series SSDs
3849d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3859d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3869d3334e1SEitan Adler 		 * PR: 169974
3879d3334e1SEitan Adler 		 */
3889d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "DENRSTE251M45*", "*" },
3899d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3909d3334e1SEitan Adler 	},
3919d3334e1SEitan Adler 	{
3929d3334e1SEitan Adler 		/*
3939d3334e1SEitan Adler 		 * Kingston HyperX 3k SSDs
3949d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3959d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3969d3334e1SEitan Adler 		 * PR: 169974
3979d3334e1SEitan Adler 		 */
3989d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "KINGSTON SH103S3*", "*" },
3999d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
4009d3334e1SEitan Adler 	},
4019d3334e1SEitan Adler 	{
40230a4094fSAlexander Motin 		/* Default */
40330a4094fSAlexander Motin 		{
40430a4094fSAlexander Motin 		  T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED,
40530a4094fSAlexander Motin 		  /*vendor*/"*", /*product*/"*", /*revision*/"*"
40630a4094fSAlexander Motin 		},
40730a4094fSAlexander Motin 		/*quirks*/0
40830a4094fSAlexander Motin 	},
40930a4094fSAlexander Motin };
41052c9ce25SScott Long 
41152c9ce25SScott Long static	disk_strategy_t	adastrategy;
41252c9ce25SScott Long static	dumper_t	adadump;
41352c9ce25SScott Long static	periph_init_t	adainit;
41452c9ce25SScott Long static	void		adaasync(void *callback_arg, u_int32_t code,
41552c9ce25SScott Long 				struct cam_path *path, void *arg);
41652c9ce25SScott Long static	void		adasysctlinit(void *context, int pending);
41752c9ce25SScott Long static	periph_ctor_t	adaregister;
41852c9ce25SScott Long static	periph_dtor_t	adacleanup;
41952c9ce25SScott Long static	periph_start_t	adastart;
42052c9ce25SScott Long static	periph_oninv_t	adaoninvalidate;
42152c9ce25SScott Long static	void		adadone(struct cam_periph *periph,
42252c9ce25SScott Long 			       union ccb *done_ccb);
42352c9ce25SScott Long static  int		adaerror(union ccb *ccb, u_int32_t cam_flags,
42452c9ce25SScott Long 				u_int32_t sense_flags);
425c1bd46c2SAlexander Motin static void		adagetparams(struct cam_periph *periph,
42652c9ce25SScott Long 				struct ccb_getdev *cgd);
42752c9ce25SScott Long static timeout_t	adasendorderedtag;
42852c9ce25SScott Long static void		adashutdown(void *arg, int howto);
429c3d0d168SAlexander Motin static void		adasuspend(void *arg);
430c3d0d168SAlexander Motin static void		adaresume(void *arg);
43152c9ce25SScott Long 
4320d307e09SAlexander Motin #ifndef	ADA_DEFAULT_LEGACY_ALIASES
4330d307e09SAlexander Motin #define	ADA_DEFAULT_LEGACY_ALIASES	1
4340d307e09SAlexander Motin #endif
4350d307e09SAlexander Motin 
43652c9ce25SScott Long #ifndef ADA_DEFAULT_TIMEOUT
43752c9ce25SScott Long #define ADA_DEFAULT_TIMEOUT 30	/* Timeout in seconds */
43852c9ce25SScott Long #endif
43952c9ce25SScott Long 
44052c9ce25SScott Long #ifndef	ADA_DEFAULT_RETRY
44152c9ce25SScott Long #define	ADA_DEFAULT_RETRY	4
44252c9ce25SScott Long #endif
44352c9ce25SScott Long 
44452c9ce25SScott Long #ifndef	ADA_DEFAULT_SEND_ORDERED
44552c9ce25SScott Long #define	ADA_DEFAULT_SEND_ORDERED	1
44652c9ce25SScott Long #endif
44752c9ce25SScott Long 
448fd104c15SRebecca Cran #ifndef	ADA_DEFAULT_SPINDOWN_SHUTDOWN
449fd104c15SRebecca Cran #define	ADA_DEFAULT_SPINDOWN_SHUTDOWN	1
450fd104c15SRebecca Cran #endif
451fd104c15SRebecca Cran 
452c3d0d168SAlexander Motin #ifndef	ADA_DEFAULT_SPINDOWN_SUSPEND
453c3d0d168SAlexander Motin #define	ADA_DEFAULT_SPINDOWN_SUSPEND	1
454c3d0d168SAlexander Motin #endif
455c3d0d168SAlexander Motin 
4561ed6aaf9SAlexander Motin #ifndef	ADA_DEFAULT_READ_AHEAD
4571ed6aaf9SAlexander Motin #define	ADA_DEFAULT_READ_AHEAD	1
4581ed6aaf9SAlexander Motin #endif
4591ed6aaf9SAlexander Motin 
460f513d14cSAlexander Motin #ifndef	ADA_DEFAULT_WRITE_CACHE
461f513d14cSAlexander Motin #define	ADA_DEFAULT_WRITE_CACHE	1
462f513d14cSAlexander Motin #endif
463f513d14cSAlexander Motin 
4641ed6aaf9SAlexander Motin #define	ADA_RA	(softc->read_ahead >= 0 ? \
4651ed6aaf9SAlexander Motin 		 softc->read_ahead : ada_read_ahead)
4661ed6aaf9SAlexander Motin #define	ADA_WC	(softc->write_cache >= 0 ? \
4671ed6aaf9SAlexander Motin 		 softc->write_cache : ada_write_cache)
4685f83aee5SSteven Hartland #define	ADA_SIO	(softc->sort_io_queue >= 0 ? \
4695f83aee5SSteven Hartland 		 softc->sort_io_queue : cam_sort_io_queues)
4701ed6aaf9SAlexander Motin 
4714461491bSMarius Strobl /*
4724461491bSMarius Strobl  * Most platforms map firmware geometry to actual, but some don't.  If
4734461491bSMarius Strobl  * not overridden, default to nothing.
4744461491bSMarius Strobl  */
4754461491bSMarius Strobl #ifndef ata_disk_firmware_geom_adjust
4764461491bSMarius Strobl #define	ata_disk_firmware_geom_adjust(disk)
4774461491bSMarius Strobl #endif
47852c9ce25SScott Long 
4790d307e09SAlexander Motin static int ada_legacy_aliases = ADA_DEFAULT_LEGACY_ALIASES;
48052c9ce25SScott Long static int ada_retry_count = ADA_DEFAULT_RETRY;
48152c9ce25SScott Long static int ada_default_timeout = ADA_DEFAULT_TIMEOUT;
48252c9ce25SScott Long static int ada_send_ordered = ADA_DEFAULT_SEND_ORDERED;
483fd104c15SRebecca Cran static int ada_spindown_shutdown = ADA_DEFAULT_SPINDOWN_SHUTDOWN;
484c3d0d168SAlexander Motin static int ada_spindown_suspend = ADA_DEFAULT_SPINDOWN_SUSPEND;
4851ed6aaf9SAlexander Motin static int ada_read_ahead = ADA_DEFAULT_READ_AHEAD;
486f513d14cSAlexander Motin static int ada_write_cache = ADA_DEFAULT_WRITE_CACHE;
48752c9ce25SScott Long 
4886472ac3dSEd Schouten static SYSCTL_NODE(_kern_cam, OID_AUTO, ada, CTLFLAG_RD, 0,
48952c9ce25SScott Long             "CAM Direct Access Disk driver");
4900d307e09SAlexander Motin SYSCTL_INT(_kern_cam_ada, OID_AUTO, legacy_aliases, CTLFLAG_RW,
4910d307e09SAlexander Motin            &ada_legacy_aliases, 0, "Create legacy-like device aliases");
4920d307e09SAlexander Motin TUNABLE_INT("kern.cam.ada.legacy_aliases", &ada_legacy_aliases);
49352c9ce25SScott Long SYSCTL_INT(_kern_cam_ada, OID_AUTO, retry_count, CTLFLAG_RW,
49452c9ce25SScott Long            &ada_retry_count, 0, "Normal I/O retry count");
49552c9ce25SScott Long TUNABLE_INT("kern.cam.ada.retry_count", &ada_retry_count);
49652c9ce25SScott Long SYSCTL_INT(_kern_cam_ada, OID_AUTO, default_timeout, CTLFLAG_RW,
49752c9ce25SScott Long            &ada_default_timeout, 0, "Normal I/O timeout (in seconds)");
49852c9ce25SScott Long TUNABLE_INT("kern.cam.ada.default_timeout", &ada_default_timeout);
499af010905SChristian Brueffer SYSCTL_INT(_kern_cam_ada, OID_AUTO, send_ordered, CTLFLAG_RW,
50052c9ce25SScott Long            &ada_send_ordered, 0, "Send Ordered Tags");
501af010905SChristian Brueffer TUNABLE_INT("kern.cam.ada.send_ordered", &ada_send_ordered);
502fd104c15SRebecca Cran SYSCTL_INT(_kern_cam_ada, OID_AUTO, spindown_shutdown, CTLFLAG_RW,
503fd104c15SRebecca Cran            &ada_spindown_shutdown, 0, "Spin down upon shutdown");
504fd104c15SRebecca Cran TUNABLE_INT("kern.cam.ada.spindown_shutdown", &ada_spindown_shutdown);
505c3d0d168SAlexander Motin SYSCTL_INT(_kern_cam_ada, OID_AUTO, spindown_suspend, CTLFLAG_RW,
506c3d0d168SAlexander Motin            &ada_spindown_suspend, 0, "Spin down upon suspend");
507c3d0d168SAlexander Motin TUNABLE_INT("kern.cam.ada.spindown_suspend", &ada_spindown_suspend);
5081ed6aaf9SAlexander Motin SYSCTL_INT(_kern_cam_ada, OID_AUTO, read_ahead, CTLFLAG_RW,
5091ed6aaf9SAlexander Motin            &ada_read_ahead, 0, "Enable disk read-ahead");
5101ed6aaf9SAlexander Motin TUNABLE_INT("kern.cam.ada.read_ahead", &ada_read_ahead);
511f513d14cSAlexander Motin SYSCTL_INT(_kern_cam_ada, OID_AUTO, write_cache, CTLFLAG_RW,
512f513d14cSAlexander Motin            &ada_write_cache, 0, "Enable disk write cache");
513f513d14cSAlexander Motin TUNABLE_INT("kern.cam.ada.write_cache", &ada_write_cache);
51452c9ce25SScott Long 
51552c9ce25SScott Long /*
51652c9ce25SScott Long  * ADA_ORDEREDTAG_INTERVAL determines how often, relative
51752c9ce25SScott Long  * to the default timeout, we check to see whether an ordered
51852c9ce25SScott Long  * tagged transaction is appropriate to prevent simple tag
51952c9ce25SScott Long  * starvation.  Since we'd like to ensure that there is at least
52052c9ce25SScott Long  * 1/2 of the timeout length left for a starved transaction to
52152c9ce25SScott Long  * complete after we've sent an ordered tag, we must poll at least
52252c9ce25SScott Long  * four times in every timeout period.  This takes care of the worst
52352c9ce25SScott Long  * case where a starved transaction starts during an interval that
52452c9ce25SScott Long  * meets the requirement "don't send an ordered tag" test so it takes
52552c9ce25SScott Long  * us two intervals to determine that a tag must be sent.
52652c9ce25SScott Long  */
52752c9ce25SScott Long #ifndef ADA_ORDEREDTAG_INTERVAL
52852c9ce25SScott Long #define ADA_ORDEREDTAG_INTERVAL 4
52952c9ce25SScott Long #endif
53052c9ce25SScott Long 
53152c9ce25SScott Long static struct periph_driver adadriver =
53252c9ce25SScott Long {
53352c9ce25SScott Long 	adainit, "ada",
53452c9ce25SScott Long 	TAILQ_HEAD_INITIALIZER(adadriver.units), /* generation */ 0
53552c9ce25SScott Long };
53652c9ce25SScott Long 
53752c9ce25SScott Long PERIPHDRIVER_DECLARE(ada, adadriver);
53852c9ce25SScott Long 
539d745c852SEd Schouten static MALLOC_DEFINE(M_ATADA, "ata_da", "ata_da buffers");
54052c9ce25SScott Long 
54152c9ce25SScott Long static int
54252c9ce25SScott Long adaopen(struct disk *dp)
54352c9ce25SScott Long {
54452c9ce25SScott Long 	struct cam_periph *periph;
54552c9ce25SScott Long 	struct ada_softc *softc;
54652c9ce25SScott Long 	int error;
54752c9ce25SScott Long 
54852c9ce25SScott Long 	periph = (struct cam_periph *)dp->d_drv1;
54952c9ce25SScott Long 	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
55052c9ce25SScott Long 		return(ENXIO);
55152c9ce25SScott Long 	}
55252c9ce25SScott Long 
55352c9ce25SScott Long 	cam_periph_lock(periph);
55452c9ce25SScott Long 	if ((error = cam_periph_hold(periph, PRIBIO|PCATCH)) != 0) {
55552c9ce25SScott Long 		cam_periph_unlock(periph);
55652c9ce25SScott Long 		cam_periph_release(periph);
55752c9ce25SScott Long 		return (error);
55852c9ce25SScott Long 	}
55952c9ce25SScott Long 
560fddde2b8SAlexander Motin 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH,
561fddde2b8SAlexander Motin 	    ("adaopen\n"));
56252c9ce25SScott Long 
5637338ef1aSAlexander Motin 	softc = (struct ada_softc *)periph->softc;
5647338ef1aSAlexander Motin 	softc->flags |= ADA_FLAG_OPEN;
56552c9ce25SScott Long 
56652c9ce25SScott Long 	cam_periph_unhold(periph);
56752c9ce25SScott Long 	cam_periph_unlock(periph);
56852c9ce25SScott Long 	return (0);
56952c9ce25SScott Long }
57052c9ce25SScott Long 
57152c9ce25SScott Long static int
57252c9ce25SScott Long adaclose(struct disk *dp)
57352c9ce25SScott Long {
57452c9ce25SScott Long 	struct	cam_periph *periph;
57552c9ce25SScott Long 	struct	ada_softc *softc;
57652c9ce25SScott Long 	union ccb *ccb;
57752c9ce25SScott Long 
57852c9ce25SScott Long 	periph = (struct cam_periph *)dp->d_drv1;
57952c9ce25SScott Long 	cam_periph_lock(periph);
5806f487924SAlexander Motin 	if (cam_periph_hold(periph, PRIBIO) != 0) {
58152c9ce25SScott Long 		cam_periph_unlock(periph);
58252c9ce25SScott Long 		cam_periph_release(periph);
5836f487924SAlexander Motin 		return (0);
58452c9ce25SScott Long 	}
58552c9ce25SScott Long 
58652c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
587fddde2b8SAlexander Motin 
588fddde2b8SAlexander Motin 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH,
589fddde2b8SAlexander Motin 	    ("adaclose\n"));
590fddde2b8SAlexander Motin 
59152c9ce25SScott Long 	/* We only sync the cache if the drive is capable of it. */
592c36bb43cSAlexander Motin 	if ((softc->flags & ADA_FLAG_CAN_FLUSHCACHE) != 0 &&
5937338ef1aSAlexander Motin 	    (periph->flags & CAM_PERIPH_INVALID) == 0) {
59452c9ce25SScott Long 
595bbfa4aa1SAlexander Motin 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
59652c9ce25SScott Long 		cam_fill_ataio(&ccb->ataio,
59752c9ce25SScott Long 				    1,
59852c9ce25SScott Long 				    adadone,
59952c9ce25SScott Long 				    CAM_DIR_NONE,
60052c9ce25SScott Long 				    0,
60152c9ce25SScott Long 				    NULL,
60252c9ce25SScott Long 				    0,
60352c9ce25SScott Long 				    ada_default_timeout*1000);
60452c9ce25SScott Long 
60552c9ce25SScott Long 		if (softc->flags & ADA_FLAG_CAN_48BIT)
60652c9ce25SScott Long 			ata_48bit_cmd(&ccb->ataio, ATA_FLUSHCACHE48, 0, 0, 0);
60752c9ce25SScott Long 		else
6087606b445SAlexander Motin 			ata_28bit_cmd(&ccb->ataio, ATA_FLUSHCACHE, 0, 0, 0);
6097642883fSAlexander Motin 		cam_periph_runccb(ccb, adaerror, /*cam_flags*/0,
61046f118feSAlexander Motin 		    /*sense_flags*/0, softc->disk->d_devstat);
61152c9ce25SScott Long 
61252c9ce25SScott Long 		if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
61352c9ce25SScott Long 			xpt_print(periph->path, "Synchronize cache failed\n");
61452c9ce25SScott Long 		xpt_release_ccb(ccb);
61552c9ce25SScott Long 	}
61652c9ce25SScott Long 
61752c9ce25SScott Long 	softc->flags &= ~ADA_FLAG_OPEN;
61852c9ce25SScott Long 	cam_periph_unhold(periph);
61952c9ce25SScott Long 	cam_periph_unlock(periph);
62052c9ce25SScott Long 	cam_periph_release(periph);
62152c9ce25SScott Long 	return (0);
62252c9ce25SScott Long }
62352c9ce25SScott Long 
6241c80ec0aSAlexander Motin static void
6251c80ec0aSAlexander Motin adaschedule(struct cam_periph *periph)
6261c80ec0aSAlexander Motin {
6271c80ec0aSAlexander Motin 	struct ada_softc *softc = (struct ada_softc *)periph->softc;
62810a6c358SAlexander Motin 	uint32_t prio;
6291c80ec0aSAlexander Motin 
6306bf435dcSAlexander Motin 	if (softc->state != ADA_STATE_NORMAL)
6316bf435dcSAlexander Motin 		return;
6326bf435dcSAlexander Motin 
63310a6c358SAlexander Motin 	/* Check if cam_periph_getccb() was called. */
63410a6c358SAlexander Motin 	prio = periph->immediate_priority;
63510a6c358SAlexander Motin 
63610a6c358SAlexander Motin 	/* Check if we have more work to do. */
6371c80ec0aSAlexander Motin 	if (bioq_first(&softc->bio_queue) ||
6381c80ec0aSAlexander Motin 	    (!softc->trim_running && bioq_first(&softc->trim_queue))) {
63910a6c358SAlexander Motin 		prio = CAM_PRIORITY_NORMAL;
6401c80ec0aSAlexander Motin 	}
64110a6c358SAlexander Motin 
64210a6c358SAlexander Motin 	/* Schedule CCB if any of above is true. */
64310a6c358SAlexander Motin 	if (prio != CAM_PRIORITY_NONE)
64410a6c358SAlexander Motin 		xpt_schedule(periph, prio);
6451c80ec0aSAlexander Motin }
6461c80ec0aSAlexander Motin 
64752c9ce25SScott Long /*
64852c9ce25SScott Long  * Actually translate the requested transfer into one the physical driver
64952c9ce25SScott Long  * can understand.  The transfer is described by a buf and will include
65052c9ce25SScott Long  * only one physical transfer.
65152c9ce25SScott Long  */
65252c9ce25SScott Long static void
65352c9ce25SScott Long adastrategy(struct bio *bp)
65452c9ce25SScott Long {
65552c9ce25SScott Long 	struct cam_periph *periph;
65652c9ce25SScott Long 	struct ada_softc *softc;
65752c9ce25SScott Long 
65852c9ce25SScott Long 	periph = (struct cam_periph *)bp->bio_disk->d_drv1;
65952c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
66052c9ce25SScott Long 
66152c9ce25SScott Long 	cam_periph_lock(periph);
66252c9ce25SScott Long 
663fddde2b8SAlexander Motin 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("adastrategy(%p)\n", bp));
664fddde2b8SAlexander Motin 
66552c9ce25SScott Long 	/*
66652c9ce25SScott Long 	 * If the device has been made invalid, error out
66752c9ce25SScott Long 	 */
6687338ef1aSAlexander Motin 	if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
66952c9ce25SScott Long 		cam_periph_unlock(periph);
67052c9ce25SScott Long 		biofinish(bp, NULL, ENXIO);
67152c9ce25SScott Long 		return;
67252c9ce25SScott Long 	}
67352c9ce25SScott Long 
67452c9ce25SScott Long 	/*
67552c9ce25SScott Long 	 * Place it in the queue of disk activities for this disk
67652c9ce25SScott Long 	 */
6771c80ec0aSAlexander Motin 	if (bp->bio_cmd == BIO_DELETE &&
6785f83aee5SSteven Hartland 	    (softc->flags & ADA_FLAG_CAN_TRIM)) {
6795f83aee5SSteven Hartland 		if (ADA_SIO)
6801c80ec0aSAlexander Motin 		    bioq_disksort(&softc->trim_queue, bp);
6811c80ec0aSAlexander Motin 		else
6825f83aee5SSteven Hartland 		    bioq_insert_tail(&softc->trim_queue, bp);
6835f83aee5SSteven Hartland 	} else {
6845f83aee5SSteven Hartland 		if (ADA_SIO)
68552c9ce25SScott Long 		    bioq_disksort(&softc->bio_queue, bp);
6865f83aee5SSteven Hartland 		else
6875f83aee5SSteven Hartland 		    bioq_insert_tail(&softc->bio_queue, bp);
6885f83aee5SSteven Hartland 	}
68952c9ce25SScott Long 
69052c9ce25SScott Long 	/*
69152c9ce25SScott Long 	 * Schedule ourselves for performing the work.
69252c9ce25SScott Long 	 */
6931c80ec0aSAlexander Motin 	adaschedule(periph);
69452c9ce25SScott Long 	cam_periph_unlock(periph);
69552c9ce25SScott Long 
69652c9ce25SScott Long 	return;
69752c9ce25SScott Long }
69852c9ce25SScott Long 
69952c9ce25SScott Long static int
70052c9ce25SScott Long adadump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length)
70152c9ce25SScott Long {
70252c9ce25SScott Long 	struct	    cam_periph *periph;
70352c9ce25SScott Long 	struct	    ada_softc *softc;
70452c9ce25SScott Long 	u_int	    secsize;
70552c9ce25SScott Long 	union	    ccb ccb;
70652c9ce25SScott Long 	struct	    disk *dp;
70752c9ce25SScott Long 	uint64_t    lba;
70852c9ce25SScott Long 	uint16_t    count;
7090191d9b3SAlexander Motin 	int	    error = 0;
71052c9ce25SScott Long 
71152c9ce25SScott Long 	dp = arg;
71252c9ce25SScott Long 	periph = dp->d_drv1;
71352c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
71452c9ce25SScott Long 	cam_periph_lock(periph);
71552c9ce25SScott Long 	secsize = softc->params.secsize;
71652c9ce25SScott Long 	lba = offset / secsize;
71752c9ce25SScott Long 	count = length / secsize;
71852c9ce25SScott Long 
7197338ef1aSAlexander Motin 	if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
72052c9ce25SScott Long 		cam_periph_unlock(periph);
72152c9ce25SScott Long 		return (ENXIO);
72252c9ce25SScott Long 	}
72352c9ce25SScott Long 
72452c9ce25SScott Long 	if (length > 0) {
725bbfa4aa1SAlexander Motin 		xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
72652c9ce25SScott Long 		ccb.ccb_h.ccb_state = ADA_CCB_DUMP;
72752c9ce25SScott Long 		cam_fill_ataio(&ccb.ataio,
72852c9ce25SScott Long 		    0,
72952c9ce25SScott Long 		    adadone,
73052c9ce25SScott Long 		    CAM_DIR_OUT,
73152c9ce25SScott Long 		    0,
73252c9ce25SScott Long 		    (u_int8_t *) virtual,
73352c9ce25SScott Long 		    length,
73452c9ce25SScott Long 		    ada_default_timeout*1000);
73552c9ce25SScott Long 		if ((softc->flags & ADA_FLAG_CAN_48BIT) &&
73652c9ce25SScott Long 		    (lba + count >= ATA_MAX_28BIT_LBA ||
73752c9ce25SScott Long 		    count >= 256)) {
73852c9ce25SScott Long 			ata_48bit_cmd(&ccb.ataio, ATA_WRITE_DMA48,
73952c9ce25SScott Long 			    0, lba, count);
74052c9ce25SScott Long 		} else {
7417606b445SAlexander Motin 			ata_28bit_cmd(&ccb.ataio, ATA_WRITE_DMA,
74252c9ce25SScott Long 			    0, lba, count);
74352c9ce25SScott Long 		}
74452c9ce25SScott Long 		xpt_polled_action(&ccb);
74552c9ce25SScott Long 
7460191d9b3SAlexander Motin 		error = cam_periph_error(&ccb,
7470191d9b3SAlexander Motin 		    0, SF_NO_RECOVERY | SF_NO_RETRY, NULL);
7480191d9b3SAlexander Motin 		if ((ccb.ccb_h.status & CAM_DEV_QFRZN) != 0)
7490191d9b3SAlexander Motin 			cam_release_devq(ccb.ccb_h.path, /*relsim_flags*/0,
7500191d9b3SAlexander Motin 			    /*reduction*/0, /*timeout*/0, /*getcount_only*/0);
7510191d9b3SAlexander Motin 		if (error != 0)
75252c9ce25SScott Long 			printf("Aborting dump due to I/O error.\n");
7530191d9b3SAlexander Motin 
75452c9ce25SScott Long 		cam_periph_unlock(periph);
7550191d9b3SAlexander Motin 		return (error);
75652c9ce25SScott Long 	}
75752c9ce25SScott Long 
75852c9ce25SScott Long 	if (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) {
759bbfa4aa1SAlexander Motin 		xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
76052c9ce25SScott Long 
76152c9ce25SScott Long 		ccb.ccb_h.ccb_state = ADA_CCB_DUMP;
76252c9ce25SScott Long 		cam_fill_ataio(&ccb.ataio,
7630191d9b3SAlexander Motin 				    0,
76452c9ce25SScott Long 				    adadone,
76552c9ce25SScott Long 				    CAM_DIR_NONE,
76652c9ce25SScott Long 				    0,
76752c9ce25SScott Long 				    NULL,
76852c9ce25SScott Long 				    0,
76952c9ce25SScott Long 				    ada_default_timeout*1000);
77052c9ce25SScott Long 
77152c9ce25SScott Long 		if (softc->flags & ADA_FLAG_CAN_48BIT)
77252c9ce25SScott Long 			ata_48bit_cmd(&ccb.ataio, ATA_FLUSHCACHE48, 0, 0, 0);
77352c9ce25SScott Long 		else
7747606b445SAlexander Motin 			ata_28bit_cmd(&ccb.ataio, ATA_FLUSHCACHE, 0, 0, 0);
77552c9ce25SScott Long 		xpt_polled_action(&ccb);
77652c9ce25SScott Long 
7770191d9b3SAlexander Motin 		error = cam_periph_error(&ccb,
7780191d9b3SAlexander Motin 		    0, SF_NO_RECOVERY | SF_NO_RETRY, NULL);
77952c9ce25SScott Long 		if ((ccb.ccb_h.status & CAM_DEV_QFRZN) != 0)
7800191d9b3SAlexander Motin 			cam_release_devq(ccb.ccb_h.path, /*relsim_flags*/0,
7810191d9b3SAlexander Motin 			    /*reduction*/0, /*timeout*/0, /*getcount_only*/0);
7820191d9b3SAlexander Motin 		if (error != 0)
7830191d9b3SAlexander Motin 			xpt_print(periph->path, "Synchronize cache failed\n");
78452c9ce25SScott Long 	}
78552c9ce25SScott Long 	cam_periph_unlock(periph);
7860191d9b3SAlexander Motin 	return (error);
78752c9ce25SScott Long }
78852c9ce25SScott Long 
78952c9ce25SScott Long static void
79052c9ce25SScott Long adainit(void)
79152c9ce25SScott Long {
79252c9ce25SScott Long 	cam_status status;
79352c9ce25SScott Long 
79452c9ce25SScott Long 	/*
79552c9ce25SScott Long 	 * Install a global async callback.  This callback will
79652c9ce25SScott Long 	 * receive async callbacks like "new device found".
79752c9ce25SScott Long 	 */
79852c9ce25SScott Long 	status = xpt_register_async(AC_FOUND_DEVICE, adaasync, NULL, NULL);
79952c9ce25SScott Long 
80052c9ce25SScott Long 	if (status != CAM_REQ_CMP) {
80152c9ce25SScott Long 		printf("ada: Failed to attach master async callback "
80252c9ce25SScott Long 		       "due to status 0x%x!\n", status);
80352c9ce25SScott Long 	} else if (ada_send_ordered) {
80452c9ce25SScott Long 
805c3d0d168SAlexander Motin 		/* Register our event handlers */
806c3d0d168SAlexander Motin 		if ((EVENTHANDLER_REGISTER(power_suspend, adasuspend,
807c3d0d168SAlexander Motin 					   NULL, EVENTHANDLER_PRI_LAST)) == NULL)
808c3d0d168SAlexander Motin 		    printf("adainit: power event registration failed!\n");
809c3d0d168SAlexander Motin 		if ((EVENTHANDLER_REGISTER(power_resume, adaresume,
810c3d0d168SAlexander Motin 					   NULL, EVENTHANDLER_PRI_LAST)) == NULL)
811c3d0d168SAlexander Motin 		    printf("adainit: power event registration failed!\n");
81252c9ce25SScott Long 		if ((EVENTHANDLER_REGISTER(shutdown_post_sync, adashutdown,
81352c9ce25SScott Long 					   NULL, SHUTDOWN_PRI_DEFAULT)) == NULL)
81452c9ce25SScott Long 		    printf("adainit: shutdown event registration failed!\n");
81552c9ce25SScott Long 	}
81652c9ce25SScott Long }
81752c9ce25SScott Long 
8180ba1e4d0SKenneth D. Merry /*
8190ba1e4d0SKenneth D. Merry  * Callback from GEOM, called when it has finished cleaning up its
8200ba1e4d0SKenneth D. Merry  * resources.
8210ba1e4d0SKenneth D. Merry  */
8220ba1e4d0SKenneth D. Merry static void
8230ba1e4d0SKenneth D. Merry adadiskgonecb(struct disk *dp)
8240ba1e4d0SKenneth D. Merry {
8250ba1e4d0SKenneth D. Merry 	struct cam_periph *periph;
8260ba1e4d0SKenneth D. Merry 
8270ba1e4d0SKenneth D. Merry 	periph = (struct cam_periph *)dp->d_drv1;
8280ba1e4d0SKenneth D. Merry 
8290ba1e4d0SKenneth D. Merry 	cam_periph_release(periph);
8300ba1e4d0SKenneth D. Merry }
8310ba1e4d0SKenneth D. Merry 
83252c9ce25SScott Long static void
83352c9ce25SScott Long adaoninvalidate(struct cam_periph *periph)
83452c9ce25SScott Long {
83552c9ce25SScott Long 	struct ada_softc *softc;
83652c9ce25SScott Long 
83752c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
83852c9ce25SScott Long 
83952c9ce25SScott Long 	/*
84052c9ce25SScott Long 	 * De-register any async callbacks.
84152c9ce25SScott Long 	 */
84252c9ce25SScott Long 	xpt_register_async(0, adaasync, periph, periph->path);
84352c9ce25SScott Long 
84452c9ce25SScott Long 	/*
84552c9ce25SScott Long 	 * Return all queued I/O with ENXIO.
84652c9ce25SScott Long 	 * XXX Handle any transactions queued to the card
84752c9ce25SScott Long 	 *     with XPT_ABORT_CCB.
84852c9ce25SScott Long 	 */
84952c9ce25SScott Long 	bioq_flush(&softc->bio_queue, NULL, ENXIO);
8501c80ec0aSAlexander Motin 	bioq_flush(&softc->trim_queue, NULL, ENXIO);
85152c9ce25SScott Long 
85252c9ce25SScott Long 	disk_gone(softc->disk);
85352c9ce25SScott Long 	xpt_print(periph->path, "lost device\n");
85452c9ce25SScott Long }
85552c9ce25SScott Long 
85652c9ce25SScott Long static void
85752c9ce25SScott Long adacleanup(struct cam_periph *periph)
85852c9ce25SScott Long {
85952c9ce25SScott Long 	struct ada_softc *softc;
86052c9ce25SScott Long 
86152c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
86252c9ce25SScott Long 
86352c9ce25SScott Long 	xpt_print(periph->path, "removing device entry\n");
86452c9ce25SScott Long 	cam_periph_unlock(periph);
86552c9ce25SScott Long 
86652c9ce25SScott Long 	/*
86752c9ce25SScott Long 	 * If we can't free the sysctl tree, oh well...
86852c9ce25SScott Long 	 */
86952c9ce25SScott Long 	if ((softc->flags & ADA_FLAG_SCTX_INIT) != 0
87052c9ce25SScott Long 	    && sysctl_ctx_free(&softc->sysctl_ctx) != 0) {
87152c9ce25SScott Long 		xpt_print(periph->path, "can't remove sysctl context\n");
87252c9ce25SScott Long 	}
87352c9ce25SScott Long 
87452c9ce25SScott Long 	disk_destroy(softc->disk);
87552c9ce25SScott Long 	callout_drain(&softc->sendordered_c);
87652c9ce25SScott Long 	free(softc, M_DEVBUF);
87752c9ce25SScott Long 	cam_periph_lock(periph);
87852c9ce25SScott Long }
87952c9ce25SScott Long 
88052c9ce25SScott Long static void
88152c9ce25SScott Long adaasync(void *callback_arg, u_int32_t code,
88252c9ce25SScott Long 	struct cam_path *path, void *arg)
88352c9ce25SScott Long {
884581b2e78SAlexander Motin 	struct ccb_getdev cgd;
88552c9ce25SScott Long 	struct cam_periph *periph;
886f513d14cSAlexander Motin 	struct ada_softc *softc;
88752c9ce25SScott Long 
88852c9ce25SScott Long 	periph = (struct cam_periph *)callback_arg;
88952c9ce25SScott Long 	switch (code) {
89052c9ce25SScott Long 	case AC_FOUND_DEVICE:
89152c9ce25SScott Long 	{
89252c9ce25SScott Long 		struct ccb_getdev *cgd;
89352c9ce25SScott Long 		cam_status status;
89452c9ce25SScott Long 
89552c9ce25SScott Long 		cgd = (struct ccb_getdev *)arg;
89652c9ce25SScott Long 		if (cgd == NULL)
89752c9ce25SScott Long 			break;
89852c9ce25SScott Long 
89952c9ce25SScott Long 		if (cgd->protocol != PROTO_ATA)
90052c9ce25SScott Long 			break;
90152c9ce25SScott Long 
90252c9ce25SScott Long 		/*
90352c9ce25SScott Long 		 * Allocate a peripheral instance for
90452c9ce25SScott Long 		 * this device and start the probe
90552c9ce25SScott Long 		 * process.
90652c9ce25SScott Long 		 */
90752c9ce25SScott Long 		status = cam_periph_alloc(adaregister, adaoninvalidate,
90852c9ce25SScott Long 					  adacleanup, adastart,
90952c9ce25SScott Long 					  "ada", CAM_PERIPH_BIO,
91052c9ce25SScott Long 					  cgd->ccb_h.path, adaasync,
91152c9ce25SScott Long 					  AC_FOUND_DEVICE, cgd);
91252c9ce25SScott Long 
91352c9ce25SScott Long 		if (status != CAM_REQ_CMP
91452c9ce25SScott Long 		 && status != CAM_REQ_INPROG)
91552c9ce25SScott Long 			printf("adaasync: Unable to attach to new device "
91652c9ce25SScott Long 				"due to status 0x%x\n", status);
91752c9ce25SScott Long 		break;
91852c9ce25SScott Long 	}
919581b2e78SAlexander Motin 	case AC_GETDEV_CHANGED:
920581b2e78SAlexander Motin 	{
921581b2e78SAlexander Motin 		softc = (struct ada_softc *)periph->softc;
922581b2e78SAlexander Motin 		xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
923581b2e78SAlexander Motin 		cgd.ccb_h.func_code = XPT_GDEV_TYPE;
924581b2e78SAlexander Motin 		xpt_action((union ccb *)&cgd);
925581b2e78SAlexander Motin 
926581b2e78SAlexander Motin 		if ((cgd.ident_data.capabilities1 & ATA_SUPPORT_DMA) &&
927581b2e78SAlexander Motin 		    (cgd.inq_flags & SID_DMA))
928581b2e78SAlexander Motin 			softc->flags |= ADA_FLAG_CAN_DMA;
929581b2e78SAlexander Motin 		else
930581b2e78SAlexander Motin 			softc->flags &= ~ADA_FLAG_CAN_DMA;
9312e1eb332SMarius Strobl 		if (cgd.ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) {
9322e1eb332SMarius Strobl 			softc->flags |= ADA_FLAG_CAN_48BIT;
9332e1eb332SMarius Strobl 			if (cgd.inq_flags & SID_DMA48)
9342e1eb332SMarius Strobl 				softc->flags |= ADA_FLAG_CAN_DMA48;
9352e1eb332SMarius Strobl 			else
9362e1eb332SMarius Strobl 				softc->flags &= ~ADA_FLAG_CAN_DMA48;
9372e1eb332SMarius Strobl 		} else
9382e1eb332SMarius Strobl 			softc->flags &= ~(ADA_FLAG_CAN_48BIT |
9392e1eb332SMarius Strobl 			    ADA_FLAG_CAN_DMA48);
940581b2e78SAlexander Motin 		if ((cgd.ident_data.satacapabilities & ATA_SUPPORT_NCQ) &&
941581b2e78SAlexander Motin 		    (cgd.inq_flags & SID_DMA) && (cgd.inq_flags & SID_CmdQue))
942581b2e78SAlexander Motin 			softc->flags |= ADA_FLAG_CAN_NCQ;
943581b2e78SAlexander Motin 		else
944581b2e78SAlexander Motin 			softc->flags &= ~ADA_FLAG_CAN_NCQ;
945581b2e78SAlexander Motin 		if ((cgd.ident_data.support_dsm & ATA_SUPPORT_DSM_TRIM) &&
946581b2e78SAlexander Motin 		    (cgd.inq_flags & SID_DMA))
947581b2e78SAlexander Motin 			softc->flags |= ADA_FLAG_CAN_TRIM;
948581b2e78SAlexander Motin 		else
949581b2e78SAlexander Motin 			softc->flags &= ~ADA_FLAG_CAN_TRIM;
950581b2e78SAlexander Motin 
951581b2e78SAlexander Motin 		cam_periph_async(periph, code, path, arg);
952581b2e78SAlexander Motin 		break;
953581b2e78SAlexander Motin 	}
9543089bb2eSAlexander Motin 	case AC_ADVINFO_CHANGED:
9553089bb2eSAlexander Motin 	{
9563089bb2eSAlexander Motin 		uintptr_t buftype;
9573089bb2eSAlexander Motin 
9583089bb2eSAlexander Motin 		buftype = (uintptr_t)arg;
9593089bb2eSAlexander Motin 		if (buftype == CDAI_TYPE_PHYS_PATH) {
9603089bb2eSAlexander Motin 			struct ada_softc *softc;
9613089bb2eSAlexander Motin 
9623089bb2eSAlexander Motin 			softc = periph->softc;
9633089bb2eSAlexander Motin 			disk_attr_changed(softc->disk, "GEOM::physpath",
9643089bb2eSAlexander Motin 					  M_NOWAIT);
9653089bb2eSAlexander Motin 		}
9663089bb2eSAlexander Motin 		break;
9673089bb2eSAlexander Motin 	}
968f513d14cSAlexander Motin 	case AC_SENT_BDR:
969f513d14cSAlexander Motin 	case AC_BUS_RESET:
970f513d14cSAlexander Motin 	{
971f513d14cSAlexander Motin 		softc = (struct ada_softc *)periph->softc;
972f513d14cSAlexander Motin 		cam_periph_async(periph, code, path, arg);
973f513d14cSAlexander Motin 		if (softc->state != ADA_STATE_NORMAL)
974f513d14cSAlexander Motin 			break;
975e3a6d3a4SAlexander Motin 		xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
976f513d14cSAlexander Motin 		cgd.ccb_h.func_code = XPT_GDEV_TYPE;
977f513d14cSAlexander Motin 		xpt_action((union ccb *)&cgd);
9781ed6aaf9SAlexander Motin 		if (ADA_RA >= 0 &&
9791ed6aaf9SAlexander Motin 		    cgd.ident_data.support.command1 & ATA_SUPPORT_LOOKAHEAD)
9801ed6aaf9SAlexander Motin 			softc->state = ADA_STATE_RAHEAD;
9811ed6aaf9SAlexander Motin 		else if (ADA_WC >= 0 &&
9821ed6aaf9SAlexander Motin 		    cgd.ident_data.support.command1 & ATA_SUPPORT_WRITECACHE)
983f513d14cSAlexander Motin 			softc->state = ADA_STATE_WCACHE;
9841ed6aaf9SAlexander Motin 		else
9851ed6aaf9SAlexander Motin 		    break;
986f513d14cSAlexander Motin 		cam_periph_acquire(periph);
987f513d14cSAlexander Motin 		xpt_schedule(periph, CAM_PRIORITY_DEV);
988f513d14cSAlexander Motin 	}
98952c9ce25SScott Long 	default:
99052c9ce25SScott Long 		cam_periph_async(periph, code, path, arg);
99152c9ce25SScott Long 		break;
99252c9ce25SScott Long 	}
99352c9ce25SScott Long }
99452c9ce25SScott Long 
99552c9ce25SScott Long static void
99652c9ce25SScott Long adasysctlinit(void *context, int pending)
99752c9ce25SScott Long {
99852c9ce25SScott Long 	struct cam_periph *periph;
99952c9ce25SScott Long 	struct ada_softc *softc;
100052c9ce25SScott Long 	char tmpstr[80], tmpstr2[80];
100152c9ce25SScott Long 
100252c9ce25SScott Long 	periph = (struct cam_periph *)context;
1003e3a6d3a4SAlexander Motin 
1004e3a6d3a4SAlexander Motin 	/* periph was held for us when this task was enqueued */
10057338ef1aSAlexander Motin 	if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
1006e3a6d3a4SAlexander Motin 		cam_periph_release(periph);
100752c9ce25SScott Long 		return;
1008e3a6d3a4SAlexander Motin 	}
100952c9ce25SScott Long 
101052c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
101152c9ce25SScott Long 	snprintf(tmpstr, sizeof(tmpstr), "CAM ADA unit %d", periph->unit_number);
101252c9ce25SScott Long 	snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number);
101352c9ce25SScott Long 
101452c9ce25SScott Long 	sysctl_ctx_init(&softc->sysctl_ctx);
101552c9ce25SScott Long 	softc->flags |= ADA_FLAG_SCTX_INIT;
101652c9ce25SScott Long 	softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx,
101752c9ce25SScott Long 		SYSCTL_STATIC_CHILDREN(_kern_cam_ada), OID_AUTO, tmpstr2,
101852c9ce25SScott Long 		CTLFLAG_RD, 0, tmpstr);
101952c9ce25SScott Long 	if (softc->sysctl_tree == NULL) {
102052c9ce25SScott Long 		printf("adasysctlinit: unable to allocate sysctl tree\n");
102152c9ce25SScott Long 		cam_periph_release(periph);
102252c9ce25SScott Long 		return;
102352c9ce25SScott Long 	}
102452c9ce25SScott Long 
1025e3a6d3a4SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
10261ed6aaf9SAlexander Motin 		OID_AUTO, "read_ahead", CTLFLAG_RW | CTLFLAG_MPSAFE,
10271ed6aaf9SAlexander Motin 		&softc->read_ahead, 0, "Enable disk read ahead.");
10281ed6aaf9SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1029e3a6d3a4SAlexander Motin 		OID_AUTO, "write_cache", CTLFLAG_RW | CTLFLAG_MPSAFE,
1030e3a6d3a4SAlexander Motin 		&softc->write_cache, 0, "Enable disk write cache.");
10315f83aee5SSteven Hartland 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
10325f83aee5SSteven Hartland 		OID_AUTO, "sort_io_queue", CTLFLAG_RW | CTLFLAG_MPSAFE,
10335f83aee5SSteven Hartland 		&softc->sort_io_queue, 0,
10345f83aee5SSteven Hartland 		"Sort IO queue to try and optimise disk access patterns");
1035e3a6d3a4SAlexander Motin #ifdef ADA_TEST_FAILURE
1036e3a6d3a4SAlexander Motin 	/*
1037e3a6d3a4SAlexander Motin 	 * Add a 'door bell' sysctl which allows one to set it from userland
1038e3a6d3a4SAlexander Motin 	 * and cause something bad to happen.  For the moment, we only allow
1039e3a6d3a4SAlexander Motin 	 * whacking the next read or write.
1040e3a6d3a4SAlexander Motin 	 */
1041e3a6d3a4SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1042e3a6d3a4SAlexander Motin 		OID_AUTO, "force_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE,
1043e3a6d3a4SAlexander Motin 		&softc->force_read_error, 0,
1044e3a6d3a4SAlexander Motin 		"Force a read error for the next N reads.");
1045e3a6d3a4SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1046e3a6d3a4SAlexander Motin 		OID_AUTO, "force_write_error", CTLFLAG_RW | CTLFLAG_MPSAFE,
1047e3a6d3a4SAlexander Motin 		&softc->force_write_error, 0,
1048e3a6d3a4SAlexander Motin 		"Force a write error for the next N writes.");
1049e3a6d3a4SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1050e3a6d3a4SAlexander Motin 		OID_AUTO, "periodic_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE,
1051e3a6d3a4SAlexander Motin 		&softc->periodic_read_error, 0,
1052e3a6d3a4SAlexander Motin 		"Force a read error every N reads (don't set too low).");
1053e3a6d3a4SAlexander Motin #endif
105452c9ce25SScott Long 	cam_periph_release(periph);
105552c9ce25SScott Long }
105652c9ce25SScott Long 
1057416494d7SJustin T. Gibbs static int
1058416494d7SJustin T. Gibbs adagetattr(struct bio *bp)
1059416494d7SJustin T. Gibbs {
10606884b662SAlexander Motin 	int ret;
1061416494d7SJustin T. Gibbs 	struct cam_periph *periph;
1062416494d7SJustin T. Gibbs 
1063416494d7SJustin T. Gibbs 	periph = (struct cam_periph *)bp->bio_disk->d_drv1;
10646884b662SAlexander Motin 	cam_periph_lock(periph);
1065416494d7SJustin T. Gibbs 	ret = xpt_getattr(bp->bio_data, bp->bio_length, bp->bio_attribute,
1066416494d7SJustin T. Gibbs 	    periph->path);
10676884b662SAlexander Motin 	cam_periph_unlock(periph);
1068416494d7SJustin T. Gibbs 	if (ret == 0)
1069416494d7SJustin T. Gibbs 		bp->bio_completed = bp->bio_length;
1070416494d7SJustin T. Gibbs 	return ret;
1071416494d7SJustin T. Gibbs }
1072416494d7SJustin T. Gibbs 
107352c9ce25SScott Long static cam_status
107452c9ce25SScott Long adaregister(struct cam_periph *periph, void *arg)
107552c9ce25SScott Long {
107652c9ce25SScott Long 	struct ada_softc *softc;
107752c9ce25SScott Long 	struct ccb_pathinq cpi;
107852c9ce25SScott Long 	struct ccb_getdev *cgd;
10790d307e09SAlexander Motin 	char   announce_buf[80], buf1[32];
108052c9ce25SScott Long 	struct disk_params *dp;
108152c9ce25SScott Long 	caddr_t match;
108252c9ce25SScott Long 	u_int maxio;
1083d3a460d3SAlexander Motin 	int legacy_id, quirks;
108452c9ce25SScott Long 
108552c9ce25SScott Long 	cgd = (struct ccb_getdev *)arg;
108652c9ce25SScott Long 	if (cgd == NULL) {
108752c9ce25SScott Long 		printf("adaregister: no getdev CCB, can't register device\n");
108852c9ce25SScott Long 		return(CAM_REQ_CMP_ERR);
108952c9ce25SScott Long 	}
109052c9ce25SScott Long 
109152c9ce25SScott Long 	softc = (struct ada_softc *)malloc(sizeof(*softc), M_DEVBUF,
109252c9ce25SScott Long 	    M_NOWAIT|M_ZERO);
109352c9ce25SScott Long 
109452c9ce25SScott Long 	if (softc == NULL) {
109552c9ce25SScott Long 		printf("adaregister: Unable to probe new device. "
109652c9ce25SScott Long 		    "Unable to allocate softc\n");
109752c9ce25SScott Long 		return(CAM_REQ_CMP_ERR);
109852c9ce25SScott Long 	}
109952c9ce25SScott Long 
110052c9ce25SScott Long 	bioq_init(&softc->bio_queue);
11011c80ec0aSAlexander Motin 	bioq_init(&softc->trim_queue);
110252c9ce25SScott Long 
1103581b2e78SAlexander Motin 	if ((cgd->ident_data.capabilities1 & ATA_SUPPORT_DMA) &&
1104cf2b9a5fSAlexander Motin 	    (cgd->inq_flags & SID_DMA))
110546f118feSAlexander Motin 		softc->flags |= ADA_FLAG_CAN_DMA;
11062e1eb332SMarius Strobl 	if (cgd->ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) {
110752c9ce25SScott Long 		softc->flags |= ADA_FLAG_CAN_48BIT;
11082e1eb332SMarius Strobl 		if (cgd->inq_flags & SID_DMA48)
11092e1eb332SMarius Strobl 			softc->flags |= ADA_FLAG_CAN_DMA48;
11102e1eb332SMarius Strobl 	}
111152c9ce25SScott Long 	if (cgd->ident_data.support.command2 & ATA_SUPPORT_FLUSHCACHE)
111252c9ce25SScott Long 		softc->flags |= ADA_FLAG_CAN_FLUSHCACHE;
1113fd104c15SRebecca Cran 	if (cgd->ident_data.support.command1 & ATA_SUPPORT_POWERMGT)
1114fd104c15SRebecca Cran 		softc->flags |= ADA_FLAG_CAN_POWERMGT;
1115581b2e78SAlexander Motin 	if ((cgd->ident_data.satacapabilities & ATA_SUPPORT_NCQ) &&
1116cf2b9a5fSAlexander Motin 	    (cgd->inq_flags & SID_DMA) && (cgd->inq_flags & SID_CmdQue))
111752c9ce25SScott Long 		softc->flags |= ADA_FLAG_CAN_NCQ;
1118581b2e78SAlexander Motin 	if ((cgd->ident_data.support_dsm & ATA_SUPPORT_DSM_TRIM) &&
1119581b2e78SAlexander Motin 	    (cgd->inq_flags & SID_DMA)) {
11201c80ec0aSAlexander Motin 		softc->flags |= ADA_FLAG_CAN_TRIM;
11211c80ec0aSAlexander Motin 		softc->trim_max_ranges = TRIM_MAX_RANGES;
11221c80ec0aSAlexander Motin 		if (cgd->ident_data.max_dsm_blocks != 0) {
11231c80ec0aSAlexander Motin 			softc->trim_max_ranges =
1124c213c551SSteven Hartland 			    min(cgd->ident_data.max_dsm_blocks *
1125c213c551SSteven Hartland 				ATA_DSM_BLK_RANGES, softc->trim_max_ranges);
11261c80ec0aSAlexander Motin 		}
11271c80ec0aSAlexander Motin 	}
11281c80ec0aSAlexander Motin 	if (cgd->ident_data.support.command2 & ATA_SUPPORT_CFA)
11291c80ec0aSAlexander Motin 		softc->flags |= ADA_FLAG_CAN_CFA;
113052c9ce25SScott Long 
113152c9ce25SScott Long 	periph->softc = softc;
113252c9ce25SScott Long 
113352c9ce25SScott Long 	/*
113452c9ce25SScott Long 	 * See if this device has any quirks.
113552c9ce25SScott Long 	 */
113630a4094fSAlexander Motin 	match = cam_quirkmatch((caddr_t)&cgd->ident_data,
113730a4094fSAlexander Motin 			       (caddr_t)ada_quirk_table,
113830a4094fSAlexander Motin 			       sizeof(ada_quirk_table)/sizeof(*ada_quirk_table),
113930a4094fSAlexander Motin 			       sizeof(*ada_quirk_table), ata_identify_match);
114052c9ce25SScott Long 	if (match != NULL)
114152c9ce25SScott Long 		softc->quirks = ((struct ada_quirk_entry *)match)->quirks;
114252c9ce25SScott Long 	else
114352c9ce25SScott Long 		softc->quirks = ADA_Q_NONE;
114452c9ce25SScott Long 
114552c9ce25SScott Long 	bzero(&cpi, sizeof(cpi));
114683c5d981SAlexander Motin 	xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NONE);
114752c9ce25SScott Long 	cpi.ccb_h.func_code = XPT_PATH_INQ;
114852c9ce25SScott Long 	xpt_action((union ccb *)&cpi);
114952c9ce25SScott Long 
115052c9ce25SScott Long 	TASK_INIT(&softc->sysctl_task, 0, adasysctlinit, periph);
115152c9ce25SScott Long 
115252c9ce25SScott Long 	/*
115352c9ce25SScott Long 	 * Register this media as a disk
115452c9ce25SScott Long 	 */
1155781338b6SAlexander Motin 	(void)cam_periph_hold(periph, PRIBIO);
1156edec59d9SAlexander Motin 	cam_periph_unlock(periph);
1157d3a460d3SAlexander Motin 	snprintf(announce_buf, sizeof(announce_buf),
1158d3a460d3SAlexander Motin 	    "kern.cam.ada.%d.quirks", periph->unit_number);
1159d3a460d3SAlexander Motin 	quirks = softc->quirks;
1160d3a460d3SAlexander Motin 	TUNABLE_INT_FETCH(announce_buf, &quirks);
1161d3a460d3SAlexander Motin 	softc->quirks = quirks;
11621ed6aaf9SAlexander Motin 	softc->read_ahead = -1;
11631ed6aaf9SAlexander Motin 	snprintf(announce_buf, sizeof(announce_buf),
11641ed6aaf9SAlexander Motin 	    "kern.cam.ada.%d.read_ahead", periph->unit_number);
11651ed6aaf9SAlexander Motin 	TUNABLE_INT_FETCH(announce_buf, &softc->read_ahead);
1166781338b6SAlexander Motin 	softc->write_cache = -1;
1167781338b6SAlexander Motin 	snprintf(announce_buf, sizeof(announce_buf),
1168781338b6SAlexander Motin 	    "kern.cam.ada.%d.write_cache", periph->unit_number);
1169781338b6SAlexander Motin 	TUNABLE_INT_FETCH(announce_buf, &softc->write_cache);
117062cc3a63SSteven Hartland 	/* Disable queue sorting for non-rotational media by default. */
117190edda31SSteven Hartland 	if (cgd->ident_data.media_rotation_rate == 1)
117290edda31SSteven Hartland 		softc->sort_io_queue = 0;
117390edda31SSteven Hartland 	else
11745f83aee5SSteven Hartland 		softc->sort_io_queue = -1;
1175c1bd46c2SAlexander Motin 	adagetparams(periph, cgd);
117652c9ce25SScott Long 	softc->disk = disk_alloc();
1177b8b6b5d3SAlexander Motin 	softc->disk->d_devstat = devstat_new_entry(periph->periph_name,
1178b8b6b5d3SAlexander Motin 			  periph->unit_number, softc->params.secsize,
1179b8b6b5d3SAlexander Motin 			  DEVSTAT_ALL_SUPPORTED,
1180b8b6b5d3SAlexander Motin 			  DEVSTAT_TYPE_DIRECT |
1181b8b6b5d3SAlexander Motin 			  XPORT_DEVSTAT_TYPE(cpi.transport),
1182b8b6b5d3SAlexander Motin 			  DEVSTAT_PRIORITY_DISK);
118352c9ce25SScott Long 	softc->disk->d_open = adaopen;
118452c9ce25SScott Long 	softc->disk->d_close = adaclose;
118552c9ce25SScott Long 	softc->disk->d_strategy = adastrategy;
1186416494d7SJustin T. Gibbs 	softc->disk->d_getattr = adagetattr;
118752c9ce25SScott Long 	softc->disk->d_dump = adadump;
11880ba1e4d0SKenneth D. Merry 	softc->disk->d_gone = adadiskgonecb;
118952c9ce25SScott Long 	softc->disk->d_name = "ada";
119052c9ce25SScott Long 	softc->disk->d_drv1 = periph;
119152c9ce25SScott Long 	maxio = cpi.maxio;		/* Honor max I/O size of SIM */
119252c9ce25SScott Long 	if (maxio == 0)
119352c9ce25SScott Long 		maxio = DFLTPHYS;	/* traditional default */
119452c9ce25SScott Long 	else if (maxio > MAXPHYS)
119552c9ce25SScott Long 		maxio = MAXPHYS;	/* for safety */
11961c80ec0aSAlexander Motin 	if (softc->flags & ADA_FLAG_CAN_48BIT)
1197c1bd46c2SAlexander Motin 		maxio = min(maxio, 65536 * softc->params.secsize);
119852c9ce25SScott Long 	else					/* 28bit ATA command limit */
1199c1bd46c2SAlexander Motin 		maxio = min(maxio, 256 * softc->params.secsize);
120052c9ce25SScott Long 	softc->disk->d_maxsize = maxio;
120152c9ce25SScott Long 	softc->disk->d_unit = periph->unit_number;
120252c9ce25SScott Long 	softc->disk->d_flags = 0;
120352c9ce25SScott Long 	if (softc->flags & ADA_FLAG_CAN_FLUSHCACHE)
120452c9ce25SScott Long 		softc->disk->d_flags |= DISKFLAG_CANFLUSHCACHE;
1205c213c551SSteven Hartland 	if (softc->flags & ADA_FLAG_CAN_TRIM) {
12061c80ec0aSAlexander Motin 		softc->disk->d_flags |= DISKFLAG_CANDELETE;
12079fe9ba5bSSteven Hartland 		softc->disk->d_delmaxsize = softc->params.secsize *
12089fe9ba5bSSteven Hartland 					    ATA_DSM_RANGE_MAX *
12099fe9ba5bSSteven Hartland 					    softc->trim_max_ranges;
1210c213c551SSteven Hartland 	} else if ((softc->flags & ADA_FLAG_CAN_CFA) &&
1211c213c551SSteven Hartland 	    !(softc->flags & ADA_FLAG_CAN_48BIT)) {
1212c213c551SSteven Hartland 		softc->disk->d_flags |= DISKFLAG_CANDELETE;
12139fe9ba5bSSteven Hartland 		softc->disk->d_delmaxsize = 256 * softc->params.secsize;
12149fe9ba5bSSteven Hartland 	} else
12159fe9ba5bSSteven Hartland 		softc->disk->d_delmaxsize = maxio;
1216abc1e60eSKonstantin Belousov 	if ((cpi.hba_misc & PIM_UNMAPPED) != 0)
1217abc1e60eSKonstantin Belousov 		softc->disk->d_flags |= DISKFLAG_UNMAPPED_BIO;
121865cb6238SNathan Whitehorn 	strlcpy(softc->disk->d_descr, cgd->ident_data.model,
121965cb6238SNathan Whitehorn 	    MIN(sizeof(softc->disk->d_descr), sizeof(cgd->ident_data.model)));
1220d50aaa6dSAndriy Gapon 	strlcpy(softc->disk->d_ident, cgd->ident_data.serial,
1221d50aaa6dSAndriy Gapon 	    MIN(sizeof(softc->disk->d_ident), sizeof(cgd->ident_data.serial)));
12228edcf694SAlexander Motin 	softc->disk->d_hba_vendor = cpi.hba_vendor;
12238edcf694SAlexander Motin 	softc->disk->d_hba_device = cpi.hba_device;
12248edcf694SAlexander Motin 	softc->disk->d_hba_subvendor = cpi.hba_subvendor;
12258edcf694SAlexander Motin 	softc->disk->d_hba_subdevice = cpi.hba_subdevice;
122652c9ce25SScott Long 
122752c9ce25SScott Long 	softc->disk->d_sectorsize = softc->params.secsize;
1228c1bd46c2SAlexander Motin 	softc->disk->d_mediasize = (off_t)softc->params.sectors *
1229c1bd46c2SAlexander Motin 	    softc->params.secsize;
1230ce8332d4SAlexander Motin 	if (ata_physical_sector_size(&cgd->ident_data) !=
1231ce8332d4SAlexander Motin 	    softc->params.secsize) {
1232ce8332d4SAlexander Motin 		softc->disk->d_stripesize =
1233ce8332d4SAlexander Motin 		    ata_physical_sector_size(&cgd->ident_data);
1234ce8332d4SAlexander Motin 		softc->disk->d_stripeoffset = (softc->disk->d_stripesize -
1235ce8332d4SAlexander Motin 		    ata_logical_sector_offset(&cgd->ident_data)) %
1236ce8332d4SAlexander Motin 		    softc->disk->d_stripesize;
1237d3a460d3SAlexander Motin 	} else if (softc->quirks & ADA_Q_4K) {
1238d3a460d3SAlexander Motin 		softc->disk->d_stripesize = 4096;
1239d3a460d3SAlexander Motin 		softc->disk->d_stripeoffset = 0;
1240ce8332d4SAlexander Motin 	}
124152c9ce25SScott Long 	softc->disk->d_fwsectors = softc->params.secs_per_track;
124252c9ce25SScott Long 	softc->disk->d_fwheads = softc->params.heads;
12434461491bSMarius Strobl 	ata_disk_firmware_geom_adjust(softc->disk);
124452c9ce25SScott Long 
12450d307e09SAlexander Motin 	if (ada_legacy_aliases) {
12460d307e09SAlexander Motin #ifdef ATA_STATIC_ID
12470d307e09SAlexander Motin 		legacy_id = xpt_path_legacy_ata_id(periph->path);
12480d307e09SAlexander Motin #else
12490d307e09SAlexander Motin 		legacy_id = softc->disk->d_unit;
12500d307e09SAlexander Motin #endif
12510d307e09SAlexander Motin 		if (legacy_id >= 0) {
12520d307e09SAlexander Motin 			snprintf(announce_buf, sizeof(announce_buf),
12530d307e09SAlexander Motin 			    "kern.devalias.%s%d",
12540d307e09SAlexander Motin 			    softc->disk->d_name, softc->disk->d_unit);
12550d307e09SAlexander Motin 			snprintf(buf1, sizeof(buf1),
12560d307e09SAlexander Motin 			    "ad%d", legacy_id);
12570d307e09SAlexander Motin 			setenv(announce_buf, buf1);
12580d307e09SAlexander Motin 		}
12590d307e09SAlexander Motin 	} else
12600d307e09SAlexander Motin 		legacy_id = -1;
12610ba1e4d0SKenneth D. Merry 	/*
12620ba1e4d0SKenneth D. Merry 	 * Acquire a reference to the periph before we register with GEOM.
12630ba1e4d0SKenneth D. Merry 	 * We'll release this reference once GEOM calls us back (via
12640ba1e4d0SKenneth D. Merry 	 * adadiskgonecb()) telling us that our provider has been freed.
12650ba1e4d0SKenneth D. Merry 	 */
12660ba1e4d0SKenneth D. Merry 	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
12670ba1e4d0SKenneth D. Merry 		xpt_print(periph->path, "%s: lost periph during "
12680ba1e4d0SKenneth D. Merry 			  "registration!\n", __func__);
12690ba1e4d0SKenneth D. Merry 		cam_periph_lock(periph);
12700ba1e4d0SKenneth D. Merry 		return (CAM_REQ_CMP_ERR);
12710ba1e4d0SKenneth D. Merry 	}
127252c9ce25SScott Long 	disk_create(softc->disk, DISK_VERSION);
1273edec59d9SAlexander Motin 	cam_periph_lock(periph);
1274781338b6SAlexander Motin 	cam_periph_unhold(periph);
127552c9ce25SScott Long 
127652c9ce25SScott Long 	dp = &softc->params;
127752c9ce25SScott Long 	snprintf(announce_buf, sizeof(announce_buf),
127852c9ce25SScott Long 		"%juMB (%ju %u byte sectors: %dH %dS/T %dC)",
127952c9ce25SScott Long 		(uintmax_t)(((uintmax_t)dp->secsize *
128052c9ce25SScott Long 		dp->sectors) / (1024*1024)),
128152c9ce25SScott Long 		(uintmax_t)dp->sectors,
128252c9ce25SScott Long 		dp->secsize, dp->heads,
128352c9ce25SScott Long 		dp->secs_per_track, dp->cylinders);
128452c9ce25SScott Long 	xpt_announce_periph(periph, announce_buf);
1285*6fb5c84eSSteven Hartland 	xpt_announce_quirks(periph, softc->quirks, ADA_Q_BIT_STRING);
12860d307e09SAlexander Motin 	if (legacy_id >= 0)
12870d307e09SAlexander Motin 		printf("%s%d: Previously was known as ad%d\n",
12880d307e09SAlexander Motin 		       periph->periph_name, periph->unit_number, legacy_id);
1289e3a6d3a4SAlexander Motin 
1290e3a6d3a4SAlexander Motin 	/*
1291e3a6d3a4SAlexander Motin 	 * Create our sysctl variables, now that we know
1292e3a6d3a4SAlexander Motin 	 * we have successfully attached.
1293e3a6d3a4SAlexander Motin 	 */
1294e3a6d3a4SAlexander Motin 	cam_periph_acquire(periph);
1295e3a6d3a4SAlexander Motin 	taskqueue_enqueue(taskqueue_thread, &softc->sysctl_task);
1296e3a6d3a4SAlexander Motin 
129752c9ce25SScott Long 	/*
129852c9ce25SScott Long 	 * Add async callbacks for bus reset and
129952c9ce25SScott Long 	 * bus device reset calls.  I don't bother
130052c9ce25SScott Long 	 * checking if this fails as, in most cases,
130152c9ce25SScott Long 	 * the system will function just fine without
130252c9ce25SScott Long 	 * them and the only alternative would be to
130352c9ce25SScott Long 	 * not attach the device on failure.
130452c9ce25SScott Long 	 */
13053089bb2eSAlexander Motin 	xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE |
1306581b2e78SAlexander Motin 	    AC_GETDEV_CHANGED | AC_ADVINFO_CHANGED,
1307581b2e78SAlexander Motin 	    adaasync, periph, periph->path);
130852c9ce25SScott Long 
130952c9ce25SScott Long 	/*
131052c9ce25SScott Long 	 * Schedule a periodic event to occasionally send an
131152c9ce25SScott Long 	 * ordered tag to a device.
131252c9ce25SScott Long 	 */
131352c9ce25SScott Long 	callout_init_mtx(&softc->sendordered_c, periph->sim->mtx, 0);
131452c9ce25SScott Long 	callout_reset(&softc->sendordered_c,
131547bb9643SAlexander Motin 	    (ada_default_timeout * hz) / ADA_ORDEREDTAG_INTERVAL,
131652c9ce25SScott Long 	    adasendorderedtag, softc);
131752c9ce25SScott Long 
13181ed6aaf9SAlexander Motin 	if (ADA_RA >= 0 &&
13191ed6aaf9SAlexander Motin 	    cgd->ident_data.support.command1 & ATA_SUPPORT_LOOKAHEAD) {
13201ed6aaf9SAlexander Motin 		softc->state = ADA_STATE_RAHEAD;
13211ed6aaf9SAlexander Motin 		cam_periph_acquire(periph);
13221ed6aaf9SAlexander Motin 		xpt_schedule(periph, CAM_PRIORITY_DEV);
13231ed6aaf9SAlexander Motin 	} else if (ADA_WC >= 0 &&
1324f513d14cSAlexander Motin 	    cgd->ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) {
1325f513d14cSAlexander Motin 		softc->state = ADA_STATE_WCACHE;
1326f513d14cSAlexander Motin 		cam_periph_acquire(periph);
1327f513d14cSAlexander Motin 		xpt_schedule(periph, CAM_PRIORITY_DEV);
1328f513d14cSAlexander Motin 	} else
1329f513d14cSAlexander Motin 		softc->state = ADA_STATE_NORMAL;
1330f513d14cSAlexander Motin 
133152c9ce25SScott Long 	return(CAM_REQ_CMP);
133252c9ce25SScott Long }
133352c9ce25SScott Long 
133452c9ce25SScott Long static void
133552c9ce25SScott Long adastart(struct cam_periph *periph, union ccb *start_ccb)
133652c9ce25SScott Long {
133746f118feSAlexander Motin 	struct ada_softc *softc = (struct ada_softc *)periph->softc;
133846f118feSAlexander Motin 	struct ccb_ataio *ataio = &start_ccb->ataio;
133952c9ce25SScott Long 
1340fddde2b8SAlexander Motin 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("adastart\n"));
1341fddde2b8SAlexander Motin 
134252c9ce25SScott Long 	switch (softc->state) {
134352c9ce25SScott Long 	case ADA_STATE_NORMAL:
134452c9ce25SScott Long 	{
134552c9ce25SScott Long 		struct bio *bp;
13461c80ec0aSAlexander Motin 		u_int8_t tag_code;
134752c9ce25SScott Long 
13481c80ec0aSAlexander Motin 		/* Execute immediate CCB if waiting. */
134952c9ce25SScott Long 		if (periph->immediate_priority <= periph->pinfo.priority) {
1350fddde2b8SAlexander Motin 			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
135152c9ce25SScott Long 					("queuing for immediate ccb\n"));
135252c9ce25SScott Long 			start_ccb->ccb_h.ccb_state = ADA_CCB_WAITING;
135352c9ce25SScott Long 			SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
135452c9ce25SScott Long 					  periph_links.sle);
135552c9ce25SScott Long 			periph->immediate_priority = CAM_PRIORITY_NONE;
135652c9ce25SScott Long 			wakeup(&periph->ccb_list);
13571c80ec0aSAlexander Motin 			/* Have more work to do, so ensure we stay scheduled */
13581c80ec0aSAlexander Motin 			adaschedule(periph);
13591c80ec0aSAlexander Motin 			break;
13601c80ec0aSAlexander Motin 		}
13611c80ec0aSAlexander Motin 		/* Run TRIM if not running yet. */
13621c80ec0aSAlexander Motin 		if (!softc->trim_running &&
13631c80ec0aSAlexander Motin 		    (bp = bioq_first(&softc->trim_queue)) != 0) {
13641c80ec0aSAlexander Motin 			struct trim_request *req = &softc->trim_req;
13651c80ec0aSAlexander Motin 			struct bio *bp1;
136637ddbd16SAlexander Motin 			uint64_t lastlba = (uint64_t)-1;
136737ddbd16SAlexander Motin 			int bps = 0, c, lastcount = 0, off, ranges = 0;
136852c9ce25SScott Long 
13691c80ec0aSAlexander Motin 			softc->trim_running = 1;
13701c80ec0aSAlexander Motin 			bzero(req, sizeof(*req));
13711c80ec0aSAlexander Motin 			bp1 = bp;
13721c80ec0aSAlexander Motin 			do {
13731c80ec0aSAlexander Motin 				uint64_t lba = bp1->bio_pblkno;
13741c80ec0aSAlexander Motin 				int count = bp1->bio_bcount /
13751c80ec0aSAlexander Motin 				    softc->params.secsize;
13761c80ec0aSAlexander Motin 
13771c80ec0aSAlexander Motin 				bioq_remove(&softc->trim_queue, bp1);
13781c80ec0aSAlexander Motin 
137937ddbd16SAlexander Motin 				/* Try to extend the previous range. */
138037ddbd16SAlexander Motin 				if (lba == lastlba) {
1381c213c551SSteven Hartland 					c = min(count, ATA_DSM_RANGE_MAX - lastcount);
138237ddbd16SAlexander Motin 					lastcount += c;
1383c213c551SSteven Hartland 					off = (ranges - 1) * ATA_DSM_RANGE_SIZE;
138437ddbd16SAlexander Motin 					req->data[off + 6] = lastcount & 0xff;
138537ddbd16SAlexander Motin 					req->data[off + 7] =
138637ddbd16SAlexander Motin 					    (lastcount >> 8) & 0xff;
138737ddbd16SAlexander Motin 					count -= c;
138837ddbd16SAlexander Motin 					lba += c;
138937ddbd16SAlexander Motin 				}
139037ddbd16SAlexander Motin 
139137ddbd16SAlexander Motin 				while (count > 0) {
1392c213c551SSteven Hartland 					c = min(count, ATA_DSM_RANGE_MAX);
1393c213c551SSteven Hartland 					off = ranges * ATA_DSM_RANGE_SIZE;
13941c80ec0aSAlexander Motin 					req->data[off + 0] = lba & 0xff;
13951c80ec0aSAlexander Motin 					req->data[off + 1] = (lba >> 8) & 0xff;
13961c80ec0aSAlexander Motin 					req->data[off + 2] = (lba >> 16) & 0xff;
13971c80ec0aSAlexander Motin 					req->data[off + 3] = (lba >> 24) & 0xff;
13981c80ec0aSAlexander Motin 					req->data[off + 4] = (lba >> 32) & 0xff;
13991c80ec0aSAlexander Motin 					req->data[off + 5] = (lba >> 40) & 0xff;
14001c80ec0aSAlexander Motin 					req->data[off + 6] = c & 0xff;
14011c80ec0aSAlexander Motin 					req->data[off + 7] = (c >> 8) & 0xff;
14021c80ec0aSAlexander Motin 					lba += c;
14031c80ec0aSAlexander Motin 					count -= c;
140437ddbd16SAlexander Motin 					lastcount = c;
14051c80ec0aSAlexander Motin 					ranges++;
1406c213c551SSteven Hartland 					/*
1407c213c551SSteven Hartland 					 * Its the caller's responsibility to ensure the
1408c213c551SSteven Hartland 					 * request will fit so we don't need to check for
1409c213c551SSteven Hartland 					 * overrun here
1410c213c551SSteven Hartland 					 */
14111c80ec0aSAlexander Motin 				}
141237ddbd16SAlexander Motin 				lastlba = lba;
14131c80ec0aSAlexander Motin 				req->bps[bps++] = bp1;
14141c80ec0aSAlexander Motin 				bp1 = bioq_first(&softc->trim_queue);
141537ddbd16SAlexander Motin 				if (bps >= TRIM_MAX_BIOS ||
141637ddbd16SAlexander Motin 				    bp1 == NULL ||
14171c80ec0aSAlexander Motin 				    bp1->bio_bcount / softc->params.secsize >
1418c213c551SSteven Hartland 				    (softc->trim_max_ranges - ranges) *
1419c213c551SSteven Hartland 				    ATA_DSM_RANGE_MAX)
14201c80ec0aSAlexander Motin 					break;
14211c80ec0aSAlexander Motin 			} while (1);
14221c80ec0aSAlexander Motin 			cam_fill_ataio(ataio,
14231c80ec0aSAlexander Motin 			    ada_retry_count,
14241c80ec0aSAlexander Motin 			    adadone,
14251c80ec0aSAlexander Motin 			    CAM_DIR_OUT,
14261c80ec0aSAlexander Motin 			    0,
14271c80ec0aSAlexander Motin 			    req->data,
1428c213c551SSteven Hartland 			    ((ranges + ATA_DSM_BLK_RANGES - 1) /
1429c213c551SSteven Hartland 			        ATA_DSM_BLK_RANGES) * ATA_DSM_BLK_SIZE,
14301c80ec0aSAlexander Motin 			    ada_default_timeout * 1000);
14311c80ec0aSAlexander Motin 			ata_48bit_cmd(ataio, ATA_DATA_SET_MANAGEMENT,
1432c213c551SSteven Hartland 			    ATA_DSM_TRIM, 0, (ranges + ATA_DSM_BLK_RANGES -
1433c213c551SSteven Hartland 			    1) / ATA_DSM_BLK_RANGES);
14341c80ec0aSAlexander Motin 			start_ccb->ccb_h.ccb_state = ADA_CCB_TRIM;
14351c80ec0aSAlexander Motin 			goto out;
14361c80ec0aSAlexander Motin 		}
14371c80ec0aSAlexander Motin 		/* Run regular command. */
14381c80ec0aSAlexander Motin 		bp = bioq_first(&softc->bio_queue);
14391c80ec0aSAlexander Motin 		if (bp == NULL) {
14401c80ec0aSAlexander Motin 			xpt_release_ccb(start_ccb);
14411c80ec0aSAlexander Motin 			break;
14421c80ec0aSAlexander Motin 		}
144352c9ce25SScott Long 		bioq_remove(&softc->bio_queue, bp);
144452c9ce25SScott Long 
1445f03f7a0cSJustin T. Gibbs 		if ((bp->bio_flags & BIO_ORDERED) != 0
1446f03f7a0cSJustin T. Gibbs 		 || (softc->flags & ADA_FLAG_NEED_OTAG) != 0) {
144752c9ce25SScott Long 			softc->flags &= ~ADA_FLAG_NEED_OTAG;
144852c9ce25SScott Long 			softc->ordered_tag_count++;
144946f118feSAlexander Motin 			tag_code = 0;
145052c9ce25SScott Long 		} else {
145146f118feSAlexander Motin 			tag_code = 1;
145252c9ce25SScott Long 		}
145352c9ce25SScott Long 		switch (bp->bio_cmd) {
145452c9ce25SScott Long 		case BIO_READ:
145552c9ce25SScott Long 		case BIO_WRITE:
145652c9ce25SScott Long 		{
145752c9ce25SScott Long 			uint64_t lba = bp->bio_pblkno;
145852c9ce25SScott Long 			uint16_t count = bp->bio_bcount / softc->params.secsize;
1459e3a6d3a4SAlexander Motin #ifdef ADA_TEST_FAILURE
1460e3a6d3a4SAlexander Motin 			int fail = 0;
146152c9ce25SScott Long 
1462e3a6d3a4SAlexander Motin 			/*
1463e3a6d3a4SAlexander Motin 			 * Support the failure ioctls.  If the command is a
1464e3a6d3a4SAlexander Motin 			 * read, and there are pending forced read errors, or
1465e3a6d3a4SAlexander Motin 			 * if a write and pending write errors, then fail this
1466e3a6d3a4SAlexander Motin 			 * operation with EIO.  This is useful for testing
1467e3a6d3a4SAlexander Motin 			 * purposes.  Also, support having every Nth read fail.
1468e3a6d3a4SAlexander Motin 			 *
1469e3a6d3a4SAlexander Motin 			 * This is a rather blunt tool.
1470e3a6d3a4SAlexander Motin 			 */
1471e3a6d3a4SAlexander Motin 			if (bp->bio_cmd == BIO_READ) {
1472e3a6d3a4SAlexander Motin 				if (softc->force_read_error) {
1473e3a6d3a4SAlexander Motin 					softc->force_read_error--;
1474e3a6d3a4SAlexander Motin 					fail = 1;
1475e3a6d3a4SAlexander Motin 				}
1476e3a6d3a4SAlexander Motin 				if (softc->periodic_read_error > 0) {
1477e3a6d3a4SAlexander Motin 					if (++softc->periodic_read_count >=
1478e3a6d3a4SAlexander Motin 					    softc->periodic_read_error) {
1479e3a6d3a4SAlexander Motin 						softc->periodic_read_count = 0;
1480e3a6d3a4SAlexander Motin 						fail = 1;
1481e3a6d3a4SAlexander Motin 					}
1482e3a6d3a4SAlexander Motin 				}
1483e3a6d3a4SAlexander Motin 			} else {
1484e3a6d3a4SAlexander Motin 				if (softc->force_write_error) {
1485e3a6d3a4SAlexander Motin 					softc->force_write_error--;
1486e3a6d3a4SAlexander Motin 					fail = 1;
1487e3a6d3a4SAlexander Motin 				}
1488e3a6d3a4SAlexander Motin 			}
1489e3a6d3a4SAlexander Motin 			if (fail) {
1490e3a6d3a4SAlexander Motin 				bp->bio_error = EIO;
1491e3a6d3a4SAlexander Motin 				bp->bio_flags |= BIO_ERROR;
1492e3a6d3a4SAlexander Motin 				biodone(bp);
1493e3a6d3a4SAlexander Motin 				xpt_release_ccb(start_ccb);
1494e3a6d3a4SAlexander Motin 				adaschedule(periph);
1495e3a6d3a4SAlexander Motin 				return;
1496e3a6d3a4SAlexander Motin 			}
1497e3a6d3a4SAlexander Motin #endif
1498abc1e60eSKonstantin Belousov 			KASSERT((bp->bio_flags & BIO_UNMAPPED) == 0 ||
1499abc1e60eSKonstantin Belousov 			    round_page(bp->bio_bcount + bp->bio_ma_offset) /
1500abc1e60eSKonstantin Belousov 			    PAGE_SIZE == bp->bio_ma_n,
1501abc1e60eSKonstantin Belousov 			    ("Short bio %p", bp));
150252c9ce25SScott Long 			cam_fill_ataio(ataio,
150352c9ce25SScott Long 			    ada_retry_count,
150452c9ce25SScott Long 			    adadone,
1505abc1e60eSKonstantin Belousov 			    (bp->bio_cmd == BIO_READ ? CAM_DIR_IN :
1506abc1e60eSKonstantin Belousov 				CAM_DIR_OUT) | ((bp->bio_flags & BIO_UNMAPPED)
1507abc1e60eSKonstantin Belousov 				!= 0 ? CAM_DATA_BIO : 0),
150852c9ce25SScott Long 			    tag_code,
1509abc1e60eSKonstantin Belousov 			    ((bp->bio_flags & BIO_UNMAPPED) != 0) ? (void *)bp :
151052c9ce25SScott Long 				bp->bio_data,
151152c9ce25SScott Long 			    bp->bio_bcount,
151252c9ce25SScott Long 			    ada_default_timeout*1000);
151352c9ce25SScott Long 
151446f118feSAlexander Motin 			if ((softc->flags & ADA_FLAG_CAN_NCQ) && tag_code) {
151552c9ce25SScott Long 				if (bp->bio_cmd == BIO_READ) {
151652c9ce25SScott Long 					ata_ncq_cmd(ataio, ATA_READ_FPDMA_QUEUED,
151752c9ce25SScott Long 					    lba, count);
151852c9ce25SScott Long 				} else {
151952c9ce25SScott Long 					ata_ncq_cmd(ataio, ATA_WRITE_FPDMA_QUEUED,
152052c9ce25SScott Long 					    lba, count);
152152c9ce25SScott Long 				}
152252c9ce25SScott Long 			} else if ((softc->flags & ADA_FLAG_CAN_48BIT) &&
152352c9ce25SScott Long 			    (lba + count >= ATA_MAX_28BIT_LBA ||
152446f118feSAlexander Motin 			    count > 256)) {
15252e1eb332SMarius Strobl 				if (softc->flags & ADA_FLAG_CAN_DMA48) {
152652c9ce25SScott Long 					if (bp->bio_cmd == BIO_READ) {
152752c9ce25SScott Long 						ata_48bit_cmd(ataio, ATA_READ_DMA48,
152852c9ce25SScott Long 						    0, lba, count);
152952c9ce25SScott Long 					} else {
153052c9ce25SScott Long 						ata_48bit_cmd(ataio, ATA_WRITE_DMA48,
153152c9ce25SScott Long 						    0, lba, count);
153252c9ce25SScott Long 					}
153352c9ce25SScott Long 				} else {
153452c9ce25SScott Long 					if (bp->bio_cmd == BIO_READ) {
153546f118feSAlexander Motin 						ata_48bit_cmd(ataio, ATA_READ_MUL48,
153646f118feSAlexander Motin 						    0, lba, count);
153746f118feSAlexander Motin 					} else {
153846f118feSAlexander Motin 						ata_48bit_cmd(ataio, ATA_WRITE_MUL48,
153946f118feSAlexander Motin 						    0, lba, count);
154046f118feSAlexander Motin 					}
154146f118feSAlexander Motin 				}
154246f118feSAlexander Motin 			} else {
154346f118feSAlexander Motin 				if (count == 256)
154446f118feSAlexander Motin 					count = 0;
154546f118feSAlexander Motin 				if (softc->flags & ADA_FLAG_CAN_DMA) {
154646f118feSAlexander Motin 					if (bp->bio_cmd == BIO_READ) {
15477606b445SAlexander Motin 						ata_28bit_cmd(ataio, ATA_READ_DMA,
154852c9ce25SScott Long 						    0, lba, count);
154952c9ce25SScott Long 					} else {
15507606b445SAlexander Motin 						ata_28bit_cmd(ataio, ATA_WRITE_DMA,
155152c9ce25SScott Long 						    0, lba, count);
155252c9ce25SScott Long 					}
155346f118feSAlexander Motin 				} else {
155446f118feSAlexander Motin 					if (bp->bio_cmd == BIO_READ) {
155546f118feSAlexander Motin 						ata_28bit_cmd(ataio, ATA_READ_MUL,
155646f118feSAlexander Motin 						    0, lba, count);
155746f118feSAlexander Motin 					} else {
155846f118feSAlexander Motin 						ata_28bit_cmd(ataio, ATA_WRITE_MUL,
155946f118feSAlexander Motin 						    0, lba, count);
156046f118feSAlexander Motin 					}
156146f118feSAlexander Motin 				}
156252c9ce25SScott Long 			}
156352c9ce25SScott Long 			break;
15641c80ec0aSAlexander Motin 		}
15651c80ec0aSAlexander Motin 		case BIO_DELETE:
15661c80ec0aSAlexander Motin 		{
15671c80ec0aSAlexander Motin 			uint64_t lba = bp->bio_pblkno;
15681c80ec0aSAlexander Motin 			uint16_t count = bp->bio_bcount / softc->params.secsize;
15691c80ec0aSAlexander Motin 
15701c80ec0aSAlexander Motin 			cam_fill_ataio(ataio,
15711c80ec0aSAlexander Motin 			    ada_retry_count,
15721c80ec0aSAlexander Motin 			    adadone,
15731c80ec0aSAlexander Motin 			    CAM_DIR_NONE,
15741c80ec0aSAlexander Motin 			    0,
15751c80ec0aSAlexander Motin 			    NULL,
15761c80ec0aSAlexander Motin 			    0,
15771c80ec0aSAlexander Motin 			    ada_default_timeout*1000);
15781c80ec0aSAlexander Motin 
15791c80ec0aSAlexander Motin 			if (count >= 256)
15801c80ec0aSAlexander Motin 				count = 0;
15811c80ec0aSAlexander Motin 			ata_28bit_cmd(ataio, ATA_CFA_ERASE, 0, lba, count);
15821c80ec0aSAlexander Motin 			break;
15831c80ec0aSAlexander Motin 		}
158452c9ce25SScott Long 		case BIO_FLUSH:
158552c9ce25SScott Long 			cam_fill_ataio(ataio,
158652c9ce25SScott Long 			    1,
158752c9ce25SScott Long 			    adadone,
158852c9ce25SScott Long 			    CAM_DIR_NONE,
158946f118feSAlexander Motin 			    0,
159052c9ce25SScott Long 			    NULL,
159152c9ce25SScott Long 			    0,
159252c9ce25SScott Long 			    ada_default_timeout*1000);
159352c9ce25SScott Long 
159452c9ce25SScott Long 			if (softc->flags & ADA_FLAG_CAN_48BIT)
159552c9ce25SScott Long 				ata_48bit_cmd(ataio, ATA_FLUSHCACHE48, 0, 0, 0);
159652c9ce25SScott Long 			else
15977606b445SAlexander Motin 				ata_28bit_cmd(ataio, ATA_FLUSHCACHE, 0, 0, 0);
159852c9ce25SScott Long 			break;
159952c9ce25SScott Long 		}
160052c9ce25SScott Long 		start_ccb->ccb_h.ccb_state = ADA_CCB_BUFFER_IO;
16011c80ec0aSAlexander Motin out:
160252c9ce25SScott Long 		start_ccb->ccb_h.ccb_bp = bp;
1603c1bd46c2SAlexander Motin 		softc->outstanding_cmds++;
160452c9ce25SScott Long 		xpt_action(start_ccb);
160552c9ce25SScott Long 
16061c80ec0aSAlexander Motin 		/* May have more work to do, so ensure we stay scheduled */
16071c80ec0aSAlexander Motin 		adaschedule(periph);
160852c9ce25SScott Long 		break;
160952c9ce25SScott Long 	}
16101ed6aaf9SAlexander Motin 	case ADA_STATE_RAHEAD:
1611f513d14cSAlexander Motin 	case ADA_STATE_WCACHE:
1612f513d14cSAlexander Motin 	{
16137338ef1aSAlexander Motin 		if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
16141ed6aaf9SAlexander Motin 			softc->state = ADA_STATE_NORMAL;
16151ed6aaf9SAlexander Motin 			xpt_release_ccb(start_ccb);
16161ed6aaf9SAlexander Motin 			cam_periph_release_locked(periph);
16171ed6aaf9SAlexander Motin 			return;
16181ed6aaf9SAlexander Motin 		}
16191ed6aaf9SAlexander Motin 
1620f513d14cSAlexander Motin 		cam_fill_ataio(ataio,
1621f513d14cSAlexander Motin 		    1,
1622f513d14cSAlexander Motin 		    adadone,
1623f513d14cSAlexander Motin 		    CAM_DIR_NONE,
1624f513d14cSAlexander Motin 		    0,
1625f513d14cSAlexander Motin 		    NULL,
1626f513d14cSAlexander Motin 		    0,
1627f513d14cSAlexander Motin 		    ada_default_timeout*1000);
1628f513d14cSAlexander Motin 
16291ed6aaf9SAlexander Motin 		if (softc->state == ADA_STATE_RAHEAD) {
16301ed6aaf9SAlexander Motin 			ata_28bit_cmd(ataio, ATA_SETFEATURES, ADA_RA ?
16311ed6aaf9SAlexander Motin 			    ATA_SF_ENAB_RCACHE : ATA_SF_DIS_RCACHE, 0, 0);
16321ed6aaf9SAlexander Motin 			start_ccb->ccb_h.ccb_state = ADA_CCB_RAHEAD;
16331ed6aaf9SAlexander Motin 		} else {
16341ed6aaf9SAlexander Motin 			ata_28bit_cmd(ataio, ATA_SETFEATURES, ADA_WC ?
1635f513d14cSAlexander Motin 			    ATA_SF_ENAB_WCACHE : ATA_SF_DIS_WCACHE, 0, 0);
1636f513d14cSAlexander Motin 			start_ccb->ccb_h.ccb_state = ADA_CCB_WCACHE;
16371ed6aaf9SAlexander Motin 		}
1638cccf4220SAlexander Motin 		start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE;
1639f513d14cSAlexander Motin 		xpt_action(start_ccb);
1640f513d14cSAlexander Motin 		break;
1641f513d14cSAlexander Motin 	}
164252c9ce25SScott Long 	}
164352c9ce25SScott Long }
164452c9ce25SScott Long 
164552c9ce25SScott Long static void
164652c9ce25SScott Long adadone(struct cam_periph *periph, union ccb *done_ccb)
164752c9ce25SScott Long {
164852c9ce25SScott Long 	struct ada_softc *softc;
164952c9ce25SScott Long 	struct ccb_ataio *ataio;
16501ed6aaf9SAlexander Motin 	struct ccb_getdev *cgd;
1651cccf4220SAlexander Motin 	struct cam_path *path;
165252c9ce25SScott Long 
165352c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
165452c9ce25SScott Long 	ataio = &done_ccb->ataio;
1655cccf4220SAlexander Motin 	path = done_ccb->ccb_h.path;
1656fddde2b8SAlexander Motin 
1657cccf4220SAlexander Motin 	CAM_DEBUG(path, CAM_DEBUG_TRACE, ("adadone\n"));
1658fddde2b8SAlexander Motin 
165952c9ce25SScott Long 	switch (ataio->ccb_h.ccb_state & ADA_CCB_TYPE_MASK) {
166052c9ce25SScott Long 	case ADA_CCB_BUFFER_IO:
16611c80ec0aSAlexander Motin 	case ADA_CCB_TRIM:
166252c9ce25SScott Long 	{
166352c9ce25SScott Long 		struct bio *bp;
166452c9ce25SScott Long 
166552c9ce25SScott Long 		bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
166652c9ce25SScott Long 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
166752c9ce25SScott Long 			int error;
166852c9ce25SScott Long 
166946f118feSAlexander Motin 			error = adaerror(done_ccb, 0, 0);
167052c9ce25SScott Long 			if (error == ERESTART) {
167146f118feSAlexander Motin 				/* A retry was scheduled, so just return. */
167252c9ce25SScott Long 				return;
167352c9ce25SScott Long 			}
167452c9ce25SScott Long 			if (error != 0) {
167552c9ce25SScott Long 				bp->bio_error = error;
167652c9ce25SScott Long 				bp->bio_resid = bp->bio_bcount;
167752c9ce25SScott Long 				bp->bio_flags |= BIO_ERROR;
167852c9ce25SScott Long 			} else {
167952c9ce25SScott Long 				bp->bio_resid = ataio->resid;
168052c9ce25SScott Long 				bp->bio_error = 0;
168152c9ce25SScott Long 				if (bp->bio_resid != 0)
168252c9ce25SScott Long 					bp->bio_flags |= BIO_ERROR;
168352c9ce25SScott Long 			}
168452c9ce25SScott Long 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
1685cccf4220SAlexander Motin 				cam_release_devq(path,
168652c9ce25SScott Long 						 /*relsim_flags*/0,
168752c9ce25SScott Long 						 /*reduction*/0,
168852c9ce25SScott Long 						 /*timeout*/0,
168952c9ce25SScott Long 						 /*getcount_only*/0);
169052c9ce25SScott Long 		} else {
169152c9ce25SScott Long 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
169252c9ce25SScott Long 				panic("REQ_CMP with QFRZN");
169352c9ce25SScott Long 			bp->bio_resid = ataio->resid;
169452c9ce25SScott Long 			if (ataio->resid > 0)
169552c9ce25SScott Long 				bp->bio_flags |= BIO_ERROR;
169652c9ce25SScott Long 		}
169752c9ce25SScott Long 		softc->outstanding_cmds--;
169852c9ce25SScott Long 		if (softc->outstanding_cmds == 0)
169952c9ce25SScott Long 			softc->flags |= ADA_FLAG_WENT_IDLE;
17001c80ec0aSAlexander Motin 		if ((ataio->ccb_h.ccb_state & ADA_CCB_TYPE_MASK) ==
17011c80ec0aSAlexander Motin 		    ADA_CCB_TRIM) {
17021c80ec0aSAlexander Motin 			struct trim_request *req =
17031c80ec0aSAlexander Motin 			    (struct trim_request *)ataio->data_ptr;
17041c80ec0aSAlexander Motin 			int i;
170552c9ce25SScott Long 
170637ddbd16SAlexander Motin 			for (i = 1; i < TRIM_MAX_BIOS && req->bps[i]; i++) {
17071c80ec0aSAlexander Motin 				struct bio *bp1 = req->bps[i];
17081c80ec0aSAlexander Motin 
17091c80ec0aSAlexander Motin 				bp1->bio_resid = bp->bio_resid;
17101c80ec0aSAlexander Motin 				bp1->bio_error = bp->bio_error;
17111c80ec0aSAlexander Motin 				if (bp->bio_flags & BIO_ERROR)
17121c80ec0aSAlexander Motin 					bp1->bio_flags |= BIO_ERROR;
17131c80ec0aSAlexander Motin 				biodone(bp1);
17141c80ec0aSAlexander Motin 			}
17151c80ec0aSAlexander Motin 			softc->trim_running = 0;
17161c80ec0aSAlexander Motin 			biodone(bp);
17171c80ec0aSAlexander Motin 			adaschedule(periph);
17181c80ec0aSAlexander Motin 		} else
171952c9ce25SScott Long 			biodone(bp);
172052c9ce25SScott Long 		break;
172152c9ce25SScott Long 	}
17221ed6aaf9SAlexander Motin 	case ADA_CCB_RAHEAD:
17231ed6aaf9SAlexander Motin 	{
17241ed6aaf9SAlexander Motin 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
17251ed6aaf9SAlexander Motin 			if (adaerror(done_ccb, 0, 0) == ERESTART) {
1726cccf4220SAlexander Motin out:
1727cccf4220SAlexander Motin 				/* Drop freeze taken due to CAM_DEV_QFREEZE */
1728cccf4220SAlexander Motin 				cam_release_devq(path, 0, 0, 0, FALSE);
17291ed6aaf9SAlexander Motin 				return;
17301ed6aaf9SAlexander Motin 			} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
1731cccf4220SAlexander Motin 				cam_release_devq(path,
17321ed6aaf9SAlexander Motin 				    /*relsim_flags*/0,
17331ed6aaf9SAlexander Motin 				    /*reduction*/0,
17341ed6aaf9SAlexander Motin 				    /*timeout*/0,
17351ed6aaf9SAlexander Motin 				    /*getcount_only*/0);
17361ed6aaf9SAlexander Motin 			}
17371ed6aaf9SAlexander Motin 		}
17381ed6aaf9SAlexander Motin 
17391ed6aaf9SAlexander Motin 		/*
17401ed6aaf9SAlexander Motin 		 * Since our peripheral may be invalidated by an error
17411ed6aaf9SAlexander Motin 		 * above or an external event, we must release our CCB
17421ed6aaf9SAlexander Motin 		 * before releasing the reference on the peripheral.
17431ed6aaf9SAlexander Motin 		 * The peripheral will only go away once the last reference
17441ed6aaf9SAlexander Motin 		 * is removed, and we need it around for the CCB release
17451ed6aaf9SAlexander Motin 		 * operation.
17461ed6aaf9SAlexander Motin 		 */
17471ed6aaf9SAlexander Motin 		cgd = (struct ccb_getdev *)done_ccb;
1748cccf4220SAlexander Motin 		xpt_setup_ccb(&cgd->ccb_h, path, CAM_PRIORITY_NORMAL);
17491ed6aaf9SAlexander Motin 		cgd->ccb_h.func_code = XPT_GDEV_TYPE;
17501ed6aaf9SAlexander Motin 		xpt_action((union ccb *)cgd);
17511ed6aaf9SAlexander Motin 		if (ADA_WC >= 0 &&
17521ed6aaf9SAlexander Motin 		    cgd->ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) {
17531ed6aaf9SAlexander Motin 			softc->state = ADA_STATE_WCACHE;
17541ed6aaf9SAlexander Motin 			xpt_release_ccb(done_ccb);
17551ed6aaf9SAlexander Motin 			xpt_schedule(periph, CAM_PRIORITY_DEV);
1756cccf4220SAlexander Motin 			goto out;
17571ed6aaf9SAlexander Motin 		}
17581ed6aaf9SAlexander Motin 		softc->state = ADA_STATE_NORMAL;
17591ed6aaf9SAlexander Motin 		xpt_release_ccb(done_ccb);
1760cccf4220SAlexander Motin 		/* Drop freeze taken due to CAM_DEV_QFREEZE */
1761cccf4220SAlexander Motin 		cam_release_devq(path, 0, 0, 0, FALSE);
17621ed6aaf9SAlexander Motin 		adaschedule(periph);
17631ed6aaf9SAlexander Motin 		cam_periph_release_locked(periph);
17641ed6aaf9SAlexander Motin 		return;
17651ed6aaf9SAlexander Motin 	}
1766f513d14cSAlexander Motin 	case ADA_CCB_WCACHE:
1767f513d14cSAlexander Motin 	{
1768f513d14cSAlexander Motin 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
1769f513d14cSAlexander Motin 			if (adaerror(done_ccb, 0, 0) == ERESTART) {
1770cccf4220SAlexander Motin 				goto out;
1771f513d14cSAlexander Motin 			} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
1772cccf4220SAlexander Motin 				cam_release_devq(path,
1773f513d14cSAlexander Motin 				    /*relsim_flags*/0,
1774f513d14cSAlexander Motin 				    /*reduction*/0,
1775f513d14cSAlexander Motin 				    /*timeout*/0,
1776f513d14cSAlexander Motin 				    /*getcount_only*/0);
1777f513d14cSAlexander Motin 			}
1778f513d14cSAlexander Motin 		}
1779f513d14cSAlexander Motin 
1780f513d14cSAlexander Motin 		softc->state = ADA_STATE_NORMAL;
1781f513d14cSAlexander Motin 		/*
1782f513d14cSAlexander Motin 		 * Since our peripheral may be invalidated by an error
1783f513d14cSAlexander Motin 		 * above or an external event, we must release our CCB
1784f513d14cSAlexander Motin 		 * before releasing the reference on the peripheral.
1785f513d14cSAlexander Motin 		 * The peripheral will only go away once the last reference
1786f513d14cSAlexander Motin 		 * is removed, and we need it around for the CCB release
1787f513d14cSAlexander Motin 		 * operation.
1788f513d14cSAlexander Motin 		 */
1789f513d14cSAlexander Motin 		xpt_release_ccb(done_ccb);
1790cccf4220SAlexander Motin 		/* Drop freeze taken due to CAM_DEV_QFREEZE */
1791cccf4220SAlexander Motin 		cam_release_devq(path, 0, 0, 0, FALSE);
1792f513d14cSAlexander Motin 		adaschedule(periph);
1793f513d14cSAlexander Motin 		cam_periph_release_locked(periph);
1794f513d14cSAlexander Motin 		return;
1795f513d14cSAlexander Motin 	}
179652c9ce25SScott Long 	case ADA_CCB_WAITING:
179752c9ce25SScott Long 	{
179852c9ce25SScott Long 		/* Caller will release the CCB */
179952c9ce25SScott Long 		wakeup(&done_ccb->ccb_h.cbfcnp);
180052c9ce25SScott Long 		return;
180152c9ce25SScott Long 	}
180252c9ce25SScott Long 	case ADA_CCB_DUMP:
180352c9ce25SScott Long 		/* No-op.  We're polling */
180452c9ce25SScott Long 		return;
180552c9ce25SScott Long 	default:
180652c9ce25SScott Long 		break;
180752c9ce25SScott Long 	}
180852c9ce25SScott Long 	xpt_release_ccb(done_ccb);
180952c9ce25SScott Long }
181052c9ce25SScott Long 
181152c9ce25SScott Long static int
181252c9ce25SScott Long adaerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
181352c9ce25SScott Long {
181452c9ce25SScott Long 
1815a9b8edb1SAlexander Motin 	return(cam_periph_error(ccb, cam_flags, sense_flags, NULL));
181652c9ce25SScott Long }
181752c9ce25SScott Long 
181852c9ce25SScott Long static void
1819c1bd46c2SAlexander Motin adagetparams(struct cam_periph *periph, struct ccb_getdev *cgd)
182052c9ce25SScott Long {
182152c9ce25SScott Long 	struct ada_softc *softc = (struct ada_softc *)periph->softc;
182252c9ce25SScott Long 	struct disk_params *dp = &softc->params;
182352c9ce25SScott Long 	u_int64_t lbasize48;
182452c9ce25SScott Long 	u_int32_t lbasize;
182552c9ce25SScott Long 
1826c1bd46c2SAlexander Motin 	dp->secsize = ata_logical_sector_size(&cgd->ident_data);
182752c9ce25SScott Long 	if ((cgd->ident_data.atavalid & ATA_FLAG_54_58) &&
182852c9ce25SScott Long 		cgd->ident_data.current_heads && cgd->ident_data.current_sectors) {
182952c9ce25SScott Long 		dp->heads = cgd->ident_data.current_heads;
183052c9ce25SScott Long 		dp->secs_per_track = cgd->ident_data.current_sectors;
183152c9ce25SScott Long 		dp->cylinders = cgd->ident_data.cylinders;
183252c9ce25SScott Long 		dp->sectors = (u_int32_t)cgd->ident_data.current_size_1 |
183352c9ce25SScott Long 			  ((u_int32_t)cgd->ident_data.current_size_2 << 16);
183452c9ce25SScott Long 	} else {
183552c9ce25SScott Long 		dp->heads = cgd->ident_data.heads;
183652c9ce25SScott Long 		dp->secs_per_track = cgd->ident_data.sectors;
183752c9ce25SScott Long 		dp->cylinders = cgd->ident_data.cylinders;
183852c9ce25SScott Long 		dp->sectors = cgd->ident_data.cylinders * dp->heads * dp->secs_per_track;
183952c9ce25SScott Long 	}
184052c9ce25SScott Long 	lbasize = (u_int32_t)cgd->ident_data.lba_size_1 |
184152c9ce25SScott Long 		  ((u_int32_t)cgd->ident_data.lba_size_2 << 16);
184252c9ce25SScott Long 
184352c9ce25SScott Long 	/* use the 28bit LBA size if valid or bigger than the CHS mapping */
184452c9ce25SScott Long 	if (cgd->ident_data.cylinders == 16383 || dp->sectors < lbasize)
184552c9ce25SScott Long 		dp->sectors = lbasize;
184652c9ce25SScott Long 
184752c9ce25SScott Long 	/* use the 48bit LBA size if valid */
184852c9ce25SScott Long 	lbasize48 = ((u_int64_t)cgd->ident_data.lba_size48_1) |
184952c9ce25SScott Long 		    ((u_int64_t)cgd->ident_data.lba_size48_2 << 16) |
185052c9ce25SScott Long 		    ((u_int64_t)cgd->ident_data.lba_size48_3 << 32) |
185152c9ce25SScott Long 		    ((u_int64_t)cgd->ident_data.lba_size48_4 << 48);
185252c9ce25SScott Long 	if ((cgd->ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) &&
185352c9ce25SScott Long 	    lbasize48 > ATA_MAX_28BIT_LBA)
185452c9ce25SScott Long 		dp->sectors = lbasize48;
185552c9ce25SScott Long }
185652c9ce25SScott Long 
185752c9ce25SScott Long static void
185852c9ce25SScott Long adasendorderedtag(void *arg)
185952c9ce25SScott Long {
186052c9ce25SScott Long 	struct ada_softc *softc = arg;
186152c9ce25SScott Long 
186252c9ce25SScott Long 	if (ada_send_ordered) {
186352c9ce25SScott Long 		if ((softc->ordered_tag_count == 0)
186452c9ce25SScott Long 		 && ((softc->flags & ADA_FLAG_WENT_IDLE) == 0)) {
186552c9ce25SScott Long 			softc->flags |= ADA_FLAG_NEED_OTAG;
186652c9ce25SScott Long 		}
186752c9ce25SScott Long 		if (softc->outstanding_cmds > 0)
186852c9ce25SScott Long 			softc->flags &= ~ADA_FLAG_WENT_IDLE;
186952c9ce25SScott Long 
187052c9ce25SScott Long 		softc->ordered_tag_count = 0;
187152c9ce25SScott Long 	}
187252c9ce25SScott Long 	/* Queue us up again */
187352c9ce25SScott Long 	callout_reset(&softc->sendordered_c,
187447bb9643SAlexander Motin 	    (ada_default_timeout * hz) / ADA_ORDEREDTAG_INTERVAL,
187552c9ce25SScott Long 	    adasendorderedtag, softc);
187652c9ce25SScott Long }
187752c9ce25SScott Long 
187852c9ce25SScott Long /*
187952c9ce25SScott Long  * Step through all ADA peripheral drivers, and if the device is still open,
188052c9ce25SScott Long  * sync the disk cache to physical media.
188152c9ce25SScott Long  */
188252c9ce25SScott Long static void
1883c3d0d168SAlexander Motin adaflush(void)
188452c9ce25SScott Long {
188552c9ce25SScott Long 	struct cam_periph *periph;
188652c9ce25SScott Long 	struct ada_softc *softc;
188709cfadbeSAlexander Motin 	union ccb *ccb;
18880191d9b3SAlexander Motin 	int error;
188952c9ce25SScott Long 
1890f371c9e2SAlexander Motin 	CAM_PERIPH_FOREACH(periph, &adadriver) {
189121cc8587SAlexander Motin 		/* If we paniced with lock held - not recurse here. */
189221cc8587SAlexander Motin 		if (cam_periph_owned(periph))
189321cc8587SAlexander Motin 			continue;
189452c9ce25SScott Long 		cam_periph_lock(periph);
189552c9ce25SScott Long 		softc = (struct ada_softc *)periph->softc;
189652c9ce25SScott Long 		/*
189752c9ce25SScott Long 		 * We only sync the cache if the drive is still open, and
189852c9ce25SScott Long 		 * if the drive is capable of it..
189952c9ce25SScott Long 		 */
190052c9ce25SScott Long 		if (((softc->flags & ADA_FLAG_OPEN) == 0) ||
190152c9ce25SScott Long 		    (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) == 0) {
190252c9ce25SScott Long 			cam_periph_unlock(periph);
190352c9ce25SScott Long 			continue;
190452c9ce25SScott Long 		}
190552c9ce25SScott Long 
190609cfadbeSAlexander Motin 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
190709cfadbeSAlexander Motin 		cam_fill_ataio(&ccb->ataio,
19080191d9b3SAlexander Motin 				    0,
190952c9ce25SScott Long 				    adadone,
191052c9ce25SScott Long 				    CAM_DIR_NONE,
191152c9ce25SScott Long 				    0,
191252c9ce25SScott Long 				    NULL,
191352c9ce25SScott Long 				    0,
191452c9ce25SScott Long 				    ada_default_timeout*1000);
191552c9ce25SScott Long 		if (softc->flags & ADA_FLAG_CAN_48BIT)
191609cfadbeSAlexander Motin 			ata_48bit_cmd(&ccb->ataio, ATA_FLUSHCACHE48, 0, 0, 0);
191752c9ce25SScott Long 		else
191809cfadbeSAlexander Motin 			ata_28bit_cmd(&ccb->ataio, ATA_FLUSHCACHE, 0, 0, 0);
191952c9ce25SScott Long 
192009cfadbeSAlexander Motin 		error = cam_periph_runccb(ccb, adaerror, /*cam_flags*/0,
192109cfadbeSAlexander Motin 		    /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY,
192209cfadbeSAlexander Motin 		    softc->disk->d_devstat);
19230191d9b3SAlexander Motin 		if (error != 0)
19240191d9b3SAlexander Motin 			xpt_print(periph->path, "Synchronize cache failed\n");
1925d6794b70SAlexander Motin 		xpt_release_ccb(ccb);
192652c9ce25SScott Long 		cam_periph_unlock(periph);
192752c9ce25SScott Long 	}
1928c3d0d168SAlexander Motin }
1929fd104c15SRebecca Cran 
1930c3d0d168SAlexander Motin static void
1931c3d0d168SAlexander Motin adaspindown(uint8_t cmd, int flags)
1932c3d0d168SAlexander Motin {
1933c3d0d168SAlexander Motin 	struct cam_periph *periph;
1934c3d0d168SAlexander Motin 	struct ada_softc *softc;
193509cfadbeSAlexander Motin 	union ccb *ccb;
19360191d9b3SAlexander Motin 	int error;
1937fd104c15SRebecca Cran 
1938f371c9e2SAlexander Motin 	CAM_PERIPH_FOREACH(periph, &adadriver) {
1939fd104c15SRebecca Cran 		/* If we paniced with lock held - not recurse here. */
1940fd104c15SRebecca Cran 		if (cam_periph_owned(periph))
1941fd104c15SRebecca Cran 			continue;
1942fd104c15SRebecca Cran 		cam_periph_lock(periph);
1943fd104c15SRebecca Cran 		softc = (struct ada_softc *)periph->softc;
1944fd104c15SRebecca Cran 		/*
1945fd104c15SRebecca Cran 		 * We only spin-down the drive if it is capable of it..
1946fd104c15SRebecca Cran 		 */
1947fd104c15SRebecca Cran 		if ((softc->flags & ADA_FLAG_CAN_POWERMGT) == 0) {
1948fd104c15SRebecca Cran 			cam_periph_unlock(periph);
1949fd104c15SRebecca Cran 			continue;
1950fd104c15SRebecca Cran 		}
1951fd104c15SRebecca Cran 
1952fd104c15SRebecca Cran 		if (bootverbose)
1953fd104c15SRebecca Cran 			xpt_print(periph->path, "spin-down\n");
1954fd104c15SRebecca Cran 
195509cfadbeSAlexander Motin 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
195609cfadbeSAlexander Motin 		cam_fill_ataio(&ccb->ataio,
19570191d9b3SAlexander Motin 				    0,
1958fd104c15SRebecca Cran 				    adadone,
1959c3d0d168SAlexander Motin 				    CAM_DIR_NONE | flags,
1960fd104c15SRebecca Cran 				    0,
1961fd104c15SRebecca Cran 				    NULL,
1962fd104c15SRebecca Cran 				    0,
1963fd104c15SRebecca Cran 				    ada_default_timeout*1000);
196409cfadbeSAlexander Motin 		ata_28bit_cmd(&ccb->ataio, cmd, 0, 0, 0);
1965fd104c15SRebecca Cran 
196609cfadbeSAlexander Motin 		error = cam_periph_runccb(ccb, adaerror, /*cam_flags*/0,
196709cfadbeSAlexander Motin 		    /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY,
196809cfadbeSAlexander Motin 		    softc->disk->d_devstat);
19690191d9b3SAlexander Motin 		if (error != 0)
19700191d9b3SAlexander Motin 			xpt_print(periph->path, "Spin-down disk failed\n");
1971d6794b70SAlexander Motin 		xpt_release_ccb(ccb);
1972fd104c15SRebecca Cran 		cam_periph_unlock(periph);
1973fd104c15SRebecca Cran 	}
197452c9ce25SScott Long }
197552c9ce25SScott Long 
1976c3d0d168SAlexander Motin static void
1977c3d0d168SAlexander Motin adashutdown(void *arg, int howto)
1978c3d0d168SAlexander Motin {
1979c3d0d168SAlexander Motin 
1980c3d0d168SAlexander Motin 	adaflush();
1981c3d0d168SAlexander Motin 	if (ada_spindown_shutdown != 0 &&
1982c3d0d168SAlexander Motin 	    (howto & (RB_HALT | RB_POWEROFF)) != 0)
1983c3d0d168SAlexander Motin 		adaspindown(ATA_STANDBY_IMMEDIATE, 0);
1984c3d0d168SAlexander Motin }
1985c3d0d168SAlexander Motin 
1986c3d0d168SAlexander Motin static void
1987c3d0d168SAlexander Motin adasuspend(void *arg)
1988c3d0d168SAlexander Motin {
1989c3d0d168SAlexander Motin 
1990c3d0d168SAlexander Motin 	adaflush();
1991c3d0d168SAlexander Motin 	if (ada_spindown_suspend != 0)
1992c3d0d168SAlexander Motin 		adaspindown(ATA_SLEEP, CAM_DEV_QFREEZE);
1993c3d0d168SAlexander Motin }
1994c3d0d168SAlexander Motin 
1995c3d0d168SAlexander Motin static void
1996c3d0d168SAlexander Motin adaresume(void *arg)
1997c3d0d168SAlexander Motin {
1998c3d0d168SAlexander Motin 	struct cam_periph *periph;
1999c3d0d168SAlexander Motin 	struct ada_softc *softc;
2000c3d0d168SAlexander Motin 
2001c3d0d168SAlexander Motin 	if (ada_spindown_suspend == 0)
2002c3d0d168SAlexander Motin 		return;
2003c3d0d168SAlexander Motin 
2004f371c9e2SAlexander Motin 	CAM_PERIPH_FOREACH(periph, &adadriver) {
2005c3d0d168SAlexander Motin 		cam_periph_lock(periph);
2006c3d0d168SAlexander Motin 		softc = (struct ada_softc *)periph->softc;
2007c3d0d168SAlexander Motin 		/*
2008c3d0d168SAlexander Motin 		 * We only spin-down the drive if it is capable of it..
2009c3d0d168SAlexander Motin 		 */
2010c3d0d168SAlexander Motin 		if ((softc->flags & ADA_FLAG_CAN_POWERMGT) == 0) {
2011c3d0d168SAlexander Motin 			cam_periph_unlock(periph);
2012c3d0d168SAlexander Motin 			continue;
2013c3d0d168SAlexander Motin 		}
2014c3d0d168SAlexander Motin 
2015c3d0d168SAlexander Motin 		if (bootverbose)
2016c3d0d168SAlexander Motin 			xpt_print(periph->path, "resume\n");
2017c3d0d168SAlexander Motin 
2018c3d0d168SAlexander Motin 		/*
2019c3d0d168SAlexander Motin 		 * Drop freeze taken due to CAM_DEV_QFREEZE flag set on
2020c3d0d168SAlexander Motin 		 * sleep request.
2021c3d0d168SAlexander Motin 		 */
2022c3d0d168SAlexander Motin 		cam_release_devq(periph->path,
2023c3d0d168SAlexander Motin 			 /*relsim_flags*/0,
2024c3d0d168SAlexander Motin 			 /*openings*/0,
2025c3d0d168SAlexander Motin 			 /*timeout*/0,
2026c3d0d168SAlexander Motin 			 /*getcount_only*/0);
2027c3d0d168SAlexander Motin 
2028c3d0d168SAlexander Motin 		cam_periph_unlock(periph);
2029c3d0d168SAlexander Motin 	}
2030c3d0d168SAlexander Motin }
2031c3d0d168SAlexander Motin 
203252c9ce25SScott Long #endif /* _KERNEL */
2033