changelog shortlog tags branches changeset files file revisions raw help

Mercurial > hg > plan9front / annotate sys/src/9/pc/sdiahci.c

changeset 7411: 82cc8a9cd294
parent: 9df9ef969856
child: 42c10e6101f2
author: cinap_lenrek@felloff.net
date: Tue, 08 Oct 2019 13:53:57 +0200
permissions: -rw-r--r--
description: sdiahci: force Hudson SATA Controller to AHCI mode
taruti@0 1
 /*
taruti@0 2
  * intel/amd ahci sata controller
cinap_lenrek@605 3
  * copyright © 2007-10 coraid, inc.
taruti@0 4
  */
taruti@0 5
 
taruti@0 6
 #include "u.h"
taruti@0 7
 #include "../port/lib.h"
taruti@0 8
 #include "mem.h"
taruti@0 9
 #include "dat.h"
taruti@0 10
 #include "fns.h"
taruti@0 11
 #include "io.h"
taruti@0 12
 #include "../port/error.h"
taruti@0 13
 #include "../port/sd.h"
cinap_lenrek@605 14
 #include <fis.h>
taruti@0 15
 #include "ahci.h"
cinap_lenrek@605 16
 #include "../port/led.h"
taruti@0 17
 
cinap_lenrek@605 18
 #pragma	varargck	type	"T"	int
cinap_lenrek@2224 19
 #define	dprint(...)	if(debug)	print(__VA_ARGS__); else USED(debug)
cinap_lenrek@2223 20
 #define	idprint(...)	if(prid)	print(__VA_ARGS__); else USED(prid)
cinap_lenrek@605 21
 #define	aprint(...)	if(datapi)	print(__VA_ARGS__); else USED(datapi)
cinap_lenrek@2223 22
 #define	ledprint(...)	if(dled)	print(__VA_ARGS__); else USED(dled)
cinap_lenrek@605 23
 #define	Pciwaddrh(a)	0
taruti@0 24
 #define Tname(c)	tname[(c)->type]
cinap_lenrek@605 25
 #define	Ticks		MACHP(0)->ticks
cinap_lenrek@605 26
 #define	MS2TK(t)	(((ulong)(t)*HZ)/1000)
taruti@0 27
 
taruti@0 28
 enum {
taruti@0 29
 	NCtlr	= 4,
taruti@0 30
 	NCtlrdrv= 32,
taruti@0 31
 	NDrive	= NCtlr*NCtlrdrv,
taruti@0 32
 
cinap_lenrek@605 33
 	Fahdrs	= 4,
cinap_lenrek@605 34
 
taruti@0 35
 	Read	= 0,
taruti@0 36
 	Write,
cinap_lenrek@605 37
 
cinap_lenrek@605 38
 	Eesb	= 1<<0,	/* must have (Eesb & Emtype) == 0 */
taruti@0 39
 };
taruti@0 40
 
taruti@0 41
 /* pci space configuration */
taruti@0 42
 enum {
taruti@0 43
 	Pmap	= 0x90,
taruti@0 44
 	Ppcs	= 0x91,
taruti@0 45
 	Prev	= 0xa8,
taruti@0 46
 };
taruti@0 47
 
taruti@0 48
 enum {
taruti@0 49
 	Tesb,
taruti@0 50
 	Tich,
taruti@0 51
 	Tsb600,
cinap_lenrek@605 52
 	Tjmicron,
cinap_lenrek@605 53
 	Tahci,
taruti@0 54
 };
taruti@0 55
 
taruti@0 56
 static char *tname[] = {
taruti@0 57
 	"63xxesb",
taruti@0 58
 	"ich",
taruti@0 59
 	"sb600",
cinap_lenrek@605 60
 	"jmicron",
cinap_lenrek@605 61
 	"ahci",
taruti@0 62
 };
taruti@0 63
 
taruti@0 64
 enum {
taruti@0 65
 	Dnull,
taruti@0 66
 	Dmissing,
taruti@0 67
 	Dnew,
taruti@0 68
 	Dready,
taruti@0 69
 	Derror,
taruti@0 70
 	Dreset,
taruti@0 71
 	Doffline,
taruti@0 72
 	Dportreset,
taruti@0 73
 	Dlast,
taruti@0 74
 };
taruti@0 75
 
taruti@0 76
 static char *diskstates[Dlast] = {
taruti@0 77
 	"null",
taruti@0 78
 	"missing",
taruti@0 79
 	"new",
taruti@0 80
 	"ready",
taruti@0 81
 	"error",
taruti@0 82
 	"reset",
taruti@0 83
 	"offline",
taruti@0 84
 	"portreset",
taruti@0 85
 };
taruti@0 86
 
taruti@0 87
 extern SDifc sdiahciifc;
taruti@0 88
 typedef struct Ctlr Ctlr;
taruti@0 89
 
taruti@0 90
 enum {
taruti@0 91
 	DMautoneg,
taruti@0 92
 	DMsatai,
taruti@0 93
 	DMsataii,
cinap_lenrek@605 94
 	DMsataiii,
cinap_lenrek@605 95
 	DMlast,
taruti@0 96
 };
taruti@0 97
 
cinap_lenrek@605 98
 static char *modes[DMlast] = {
taruti@0 99
 	"auto",
taruti@0 100
 	"satai",
taruti@0 101
 	"sataii",
cinap_lenrek@605 102
 	"sataiii",
taruti@0 103
 };
taruti@0 104
 
cinap_lenrek@605 105
 typedef struct Htab Htab;
cinap_lenrek@605 106
 struct Htab {
cinap_lenrek@605 107
 	ulong	bit;
cinap_lenrek@605 108
 	char	*name;
taruti@0 109
 };
taruti@0 110
 
taruti@0 111
 typedef struct {
taruti@0 112
 	Lock;
taruti@0 113
 
taruti@0 114
 	Ctlr	*ctlr;
taruti@0 115
 	SDunit	*unit;
taruti@0 116
 	char	name[10];
taruti@0 117
 	Aport	*port;
taruti@0 118
 	Aportm	portm;
cinap_lenrek@605 119
 	Aportc	portc;	/* redundant ptr to port and portm. */
cinap_lenrek@605 120
 	Ledport;
taruti@0 121
 
cinap_lenrek@605 122
 	uchar	drivechange;
cinap_lenrek@2230 123
 	uchar	nodma;
taruti@0 124
 	uchar	state;
taruti@0 125
 
taruti@0 126
 	uvlong	sectors;
cinap_lenrek@605 127
 	uint	secsize;
cinap_lenrek@605 128
 	ulong	totick;
taruti@0 129
 	ulong	lastseen;
cinap_lenrek@605 130
 	uint	wait;
cinap_lenrek@605 131
 	uchar	mode;
taruti@0 132
 	uchar	active;
taruti@0 133
 
taruti@0 134
 	char	serial[20+1];
taruti@0 135
 	char	firmware[8+1];
taruti@0 136
 	char	model[40+1];
cinap_lenrek@605 137
 	uvlong	wwn;
taruti@0 138
 
taruti@0 139
 	ushort	info[0x200];
taruti@0 140
 
cinap_lenrek@605 141
 	/*
cinap_lenrek@605 142
 	 * ahci allows non-sequential ports.
cinap_lenrek@605 143
 	 * to avoid this hassle, we let
cinap_lenrek@605 144
 	 * driveno	ctlr*NCtlrdrv + unit
cinap_lenrek@605 145
 	 * portno	nth available port
cinap_lenrek@605 146
 	 */
cinap_lenrek@605 147
 	uint	driveno;
cinap_lenrek@605 148
 	uint	portno;
taruti@0 149
 } Drive;
taruti@0 150
 
taruti@0 151
 struct Ctlr {
taruti@0 152
 	Lock;
taruti@0 153
 
taruti@0 154
 	int	type;
taruti@0 155
 	int	enabled;
taruti@0 156
 	SDev	*sdev;
taruti@0 157
 	Pcidev	*pci;
taruti@0 158
 
taruti@0 159
 	uchar	*mmio;
taruti@0 160
 	ulong	*lmmio;
taruti@0 161
 	Ahba	*hba;
cinap_lenrek@605 162
 	Aenc;
cinap_lenrek@605 163
 	uint	enctype;
taruti@0 164
 
taruti@0 165
 	Drive	rawdrive[NCtlrdrv];
taruti@0 166
 	Drive*	drive[NCtlrdrv];
taruti@0 167
 	int	ndrive;
cinap_lenrek@2223 168
 
cinap_lenrek@2223 169
 	uint	missirq;
taruti@0 170
 };
taruti@0 171
 
taruti@0 172
 static	Ctlr	iactlr[NCtlr];
taruti@0 173
 static	SDev	sdevs[NCtlr];
taruti@0 174
 static	int	niactlr;
taruti@0 175
 
taruti@0 176
 static	Drive	*iadrive[NDrive];
taruti@0 177
 static	int	niadrive;
taruti@0 178
 
taruti@0 179
 static	int	debug;
taruti@0 180
 static	int	prid = 1;
taruti@0 181
 static	int	datapi;
cinap_lenrek@605 182
 static	int	dled;
taruti@0 183
 
taruti@0 184
 static char stab[] = {
taruti@0 185
 [0]	'i', 'm',
taruti@0 186
 [8]	't', 'c', 'p', 'e',
taruti@0 187
 [16]	'N', 'I', 'W', 'B', 'D', 'C', 'H', 'S', 'T', 'F', 'X'
taruti@0 188
 };
taruti@0 189
 
taruti@0 190
 static void
taruti@0 191
 serrstr(ulong r, char *s, char *e)
taruti@0 192
 {
taruti@0 193
 	int i;
taruti@0 194
 
taruti@0 195
 	e -= 3;
taruti@0 196
 	for(i = 0; i < nelem(stab) && s < e; i++)
taruti@0 197
 		if(r & (1<<i) && stab[i]){
taruti@0 198
 			*s++ = stab[i];
taruti@0 199
 			if(SerrBad & (1<<i))
taruti@0 200
 				*s++ = '*';
taruti@0 201
 		}
taruti@0 202
 	*s = 0;
taruti@0 203
 }
taruti@0 204
 
taruti@0 205
 static char ntab[] = "0123456789abcdef";
taruti@0 206
 
taruti@0 207
 static void
taruti@0 208
 preg(uchar *reg, int n)
taruti@0 209
 {
cinap_lenrek@605 210
 	char buf[25*3+1], *e;
taruti@0 211
 	int i;
taruti@0 212
 
taruti@0 213
 	e = buf;
taruti@0 214
 	for(i = 0; i < n; i++){
cinap_lenrek@605 215
 		*e++ = ntab[reg[i] >> 4];
cinap_lenrek@605 216
 		*e++ = ntab[reg[i] & 0xf];
taruti@0 217
 		*e++ = ' ';
taruti@0 218
 	}
taruti@0 219
 	*e++ = '\n';
taruti@0 220
 	*e = 0;
taruti@0 221
 	dprint(buf);
taruti@0 222
 }
taruti@0 223
 
taruti@0 224
 static void
taruti@0 225
 dreg(char *s, Aport *p)
taruti@0 226
 {
cinap_lenrek@605 227
 	dprint("%stask=%lux; cmd=%lux; ci=%lux; is=%lux\n",
taruti@0 228
 		s, p->task, p->cmd, p->ci, p->isr);
taruti@0 229
 }
taruti@0 230
 
taruti@0 231
 static void
taruti@0 232
 esleep(int ms)
taruti@0 233
 {
taruti@0 234
 	if(waserror())
taruti@0 235
 		return;
taruti@0 236
 	tsleep(&up->sleep, return0, 0, ms);
taruti@0 237
 	poperror();
taruti@0 238
 }
taruti@0 239
 
taruti@0 240
 typedef struct {
taruti@0 241
 	Aport	*p;
taruti@0 242
 	int	i;
cinap_lenrek@605 243
 } Asleep;
taruti@0 244
 
taruti@0 245
 static int
taruti@0 246
 ahciclear(void *v)
taruti@0 247
 {
taruti@0 248
 	Asleep *s;
taruti@0 249
 
taruti@0 250
 	s = v;
taruti@0 251
 	return (s->p->ci & s->i) == 0;
taruti@0 252
 }
taruti@0 253
 
taruti@0 254
 static void
taruti@0 255
 aesleep(Aportm *m, Asleep *a, int ms)
taruti@0 256
 {
taruti@0 257
 	if(waserror())
taruti@0 258
 		return;
taruti@0 259
 	tsleep(m, ahciclear, a, ms);
taruti@0 260
 	poperror();
taruti@0 261
 }
taruti@0 262
 
taruti@0 263
 static int
taruti@0 264
 ahciwait(Aportc *c, int ms)
taruti@0 265
 {
cinap_lenrek@605 266
 	Aport *p;
taruti@0 267
 	Asleep as;
taruti@0 268
 
taruti@0 269
 	p = c->p;
taruti@0 270
 	p->ci = 1;
taruti@0 271
 	as.p = p;
taruti@0 272
 	as.i = 1;
taruti@0 273
 	aesleep(c->m, &as, ms);
cinap_lenrek@605 274
 	if((p->task & 1) == 0 && p->ci == 0)
taruti@0 275
 		return 0;
cinap_lenrek@605 276
 	dreg("ahciwait fail/timeout ", c->p);
taruti@0 277
 	return -1;
taruti@0 278
 }
taruti@0 279
 
cinap_lenrek@2230 280
 static Alist*
cinap_lenrek@605 281
 mkalist(Aportm *m, uint flags, uchar *data, int len)
cinap_lenrek@605 282
 {
cinap_lenrek@605 283
 	Actab *t;
cinap_lenrek@605 284
 	Alist *l;
cinap_lenrek@605 285
 	Aprdt *p;
cinap_lenrek@605 286
 
cinap_lenrek@605 287
 	t = m->ctab;
cinap_lenrek@2230 288
 	if(data && len > 0){
cinap_lenrek@2230 289
 		p = &t->prdt;
cinap_lenrek@2230 290
 		p->dba = PCIWADDR(data);
cinap_lenrek@2230 291
 		p->dbahi = Pciwaddrh(data);
cinap_lenrek@2230 292
 		p->count = 1<<31 | len - 2 | 1;
cinap_lenrek@2230 293
 		flags |= 1<<16;
cinap_lenrek@2230 294
 	}
cinap_lenrek@605 295
 	l = m->list;
cinap_lenrek@605 296
 	l->flags = flags | 0x5;
cinap_lenrek@605 297
 	l->len = 0;
cinap_lenrek@605 298
 	l->ctab = PCIWADDR(t);
cinap_lenrek@605 299
 	l->ctabhi = Pciwaddrh(t);
cinap_lenrek@2230 300
 	return l;
cinap_lenrek@605 301
 }
cinap_lenrek@605 302
 
taruti@0 303
 static int
taruti@0 304
 nop(Aportc *pc)
taruti@0 305
 {
taruti@0 306
 	uchar *c;
taruti@0 307
 
taruti@0 308
 	if((pc->m->feat & Dnop) == 0)
taruti@0 309
 		return -1;
cinap_lenrek@605 310
 	c = pc->m->ctab->cfis;
cinap_lenrek@605 311
 	nopfis(pc->m, c, 0);
cinap_lenrek@605 312
 	mkalist(pc->m, Lwrite, 0, 0);
taruti@0 313
 	return ahciwait(pc, 3*1000);
taruti@0 314
 }
taruti@0 315
 
taruti@0 316
 static int
cinap_lenrek@605 317
 setfeatures(Aportc *pc, uchar f, uint w)
taruti@0 318
 {
taruti@0 319
 	uchar *c;
taruti@0 320
 
cinap_lenrek@605 321
 	c = pc->m->ctab->cfis;
cinap_lenrek@605 322
 	featfis(pc->m, c, f);
cinap_lenrek@605 323
 	mkalist(pc->m, Lwrite, 0, 0);
cinap_lenrek@605 324
 	return ahciwait(pc, w);
taruti@0 325
 }
taruti@0 326
 
cinap_lenrek@605 327
 /*
cinap_lenrek@605 328
  * ata 7, required for sata, requires that all devices "support"
cinap_lenrek@605 329
  * udma mode 5,   however sata:pata bridges allow older devices
cinap_lenrek@605 330
  * which may not.  the innodisk satadom, for example allows
cinap_lenrek@605 331
  * only udma mode 2.  on the assumption that actual udma is
cinap_lenrek@605 332
  * taking place on these bridges, we set the highest udma mode
cinap_lenrek@605 333
  * available, or pio if there is no udma mode available.
cinap_lenrek@605 334
  */
taruti@0 335
 static int
cinap_lenrek@605 336
 settxmode(Aportc *pc, uchar f)
taruti@0 337
 {
taruti@0 338
 	uchar *c;
taruti@0 339
 
cinap_lenrek@605 340
 	c = pc->m->ctab->cfis;
cinap_lenrek@605 341
 	if(txmodefis(pc->m, c, f) == -1)
taruti@0 342
 		return 0;
cinap_lenrek@605 343
 	mkalist(pc->m, Lwrite, 0, 0);
taruti@0 344
 	return ahciwait(pc, 3*1000);
taruti@0 345
 }
taruti@0 346
 
taruti@0 347
 static void
taruti@0 348
 asleep(int ms)
taruti@0 349
 {
cinap_lenrek@2223 350
 	if(up == nil || !islo())
taruti@0 351
 		delay(ms);
taruti@0 352
 	else
taruti@0 353
 		esleep(ms);
taruti@0 354
 }
taruti@0 355
 
cinap_lenrek@6149 356
 static void
cinap_lenrek@605 357
 ahciportreset(Aportc *c, uint mode)
taruti@0 358
 {
taruti@0 359
 	ulong *cmd, i;
taruti@0 360
 	Aport *p;
taruti@0 361
 
taruti@0 362
 	p = c->p;
taruti@0 363
 	cmd = &p->cmd;
taruti@0 364
 	*cmd &= ~(Afre|Ast);
taruti@0 365
 	for(i = 0; i < 500; i += 25){
cinap_lenrek@605 366
 		if((*cmd & Acr) == 0)
taruti@0 367
 			break;
taruti@0 368
 		asleep(25);
taruti@0 369
 	}
cinap_lenrek@2223 370
 	if((*cmd & Apwr) != Apwr)
cinap_lenrek@2223 371
 		*cmd |= Apwr;
cinap_lenrek@605 372
 	p->sctl = 3*Aipm | 0*Aspd | Adet;
taruti@0 373
 	delay(1);
cinap_lenrek@605 374
 	p->sctl = 3*Aipm | mode*Aspd;
taruti@0 375
 }
taruti@0 376
 
taruti@0 377
 static int
taruti@0 378
 ahciflushcache(Aportc *pc)
taruti@0 379
 {
cinap_lenrek@605 380
 	uchar *c;
taruti@0 381
 
cinap_lenrek@605 382
 	c = pc->m->ctab->cfis;
cinap_lenrek@605 383
 	flushcachefis(pc->m, c);
cinap_lenrek@605 384
 	mkalist(pc->m, Lwrite, 0, 0);
taruti@0 385
 
taruti@0 386
 	if(ahciwait(pc, 60000) == -1 || pc->p->task & (1|32)){
cinap_lenrek@2223 387
 		dprint("ahciflushcache fail [task %lux]\n", pc->p->task);
cinap_lenrek@605 388
 //		preg(pc->m->fis.r, 20);
taruti@0 389
 		return -1;
taruti@0 390
 	}
taruti@0 391
 	return 0;
taruti@0 392
 }
taruti@0 393
 
taruti@0 394
 static int
cinap_lenrek@605 395
 ahciidentify0(Aportc *pc, void *id)
taruti@0 396
 {
taruti@0 397
 	uchar *c;
taruti@0 398
 	Actab *t;
taruti@0 399
 
taruti@0 400
 	t = pc->m->ctab;
taruti@0 401
 	c = t->cfis;
cinap_lenrek@605 402
 	memset(id, 0, 0x200);
cinap_lenrek@605 403
 	identifyfis(pc->m, c);
cinap_lenrek@605 404
 	mkalist(pc->m, 0, id, 0x200);
taruti@0 405
 	return ahciwait(pc, 3*1000);
taruti@0 406
 }
taruti@0 407
 
taruti@0 408
 static vlong
cinap_lenrek@605 409
 ahciidentify(Aportc *pc, ushort *id, uint *ss, char *d)
taruti@0 410
 {
cinap_lenrek@605 411
 	int i, n;
taruti@0 412
 	vlong s;
taruti@0 413
 	Aportm *m;
taruti@0 414
 
taruti@0 415
 	m = pc->m;
cinap_lenrek@605 416
 	for(i = 0;; i++){
cinap_lenrek@605 417
 		if(i > 5 || ahciidentify0(pc, id) != 0)
cinap_lenrek@605 418
 			return -1;
cinap_lenrek@605 419
 		n = idpuis(id);
cinap_lenrek@605 420
 		if(n & Pspinup && setfeatures(pc, 7, 20*1000) == -1)
cinap_lenrek@605 421
 			print("%s: puis spinup fail\n", d);
cinap_lenrek@605 422
 		if(n & Pidready)
cinap_lenrek@605 423
 			break;
cinap_lenrek@605 424
 		print("%s: puis waiting\n", d);
taruti@0 425
 	}
cinap_lenrek@605 426
 	s = idfeat(m, id);
cinap_lenrek@605 427
 	*ss = idss(m, id);
cinap_lenrek@605 428
 	if(s == -1 || (m->feat&Dlba) == 0){
cinap_lenrek@605 429
 		if((m->feat&Dlba) == 0)
cinap_lenrek@605 430
 			dprint("%s: no lba support\n", d);
taruti@0 431
 		return -1;
taruti@0 432
 	}
taruti@0 433
 	return s;
taruti@0 434
 }
taruti@0 435
 
taruti@0 436
 static int
taruti@0 437
 ahciquiet(Aport *a)
taruti@0 438
 {
taruti@0 439
 	ulong *p, i;
taruti@0 440
 
taruti@0 441
 	p = &a->cmd;
taruti@0 442
 	*p &= ~Ast;
taruti@0 443
 	for(i = 0; i < 500; i += 50){
taruti@0 444
 		if((*p & Acr) == 0)
taruti@0 445
 			goto stop;
taruti@0 446
 		asleep(50);
taruti@0 447
 	}
taruti@0 448
 	return -1;
taruti@0 449
 stop:
taruti@0 450
 	if((a->task & (ASdrq|ASbsy)) == 0){
taruti@0 451
 		*p |= Ast;
taruti@0 452
 		return 0;
taruti@0 453
 	}
taruti@0 454
 
taruti@0 455
 	*p |= Aclo;
taruti@0 456
 	for(i = 0; i < 500; i += 50){
taruti@0 457
 		if((*p & Aclo) == 0)
taruti@0 458
 			goto stop1;
taruti@0 459
 		asleep(50);
taruti@0 460
 	}
taruti@0 461
 	return -1;
taruti@0 462
 stop1:
taruti@0 463
 	/* extra check */
cinap_lenrek@2223 464
 	dprint("ahci: clo clear [task %lux]\n", a->task);
taruti@0 465
 	if(a->task & ASbsy)
taruti@0 466
 		return -1;
cinap_lenrek@605 467
 	*p |= Afre | Ast;
taruti@0 468
 	return 0;
taruti@0 469
 }
taruti@0 470
 
taruti@0 471
 static int
taruti@0 472
 ahcicomreset(Aportc *pc)
taruti@0 473
 {
taruti@0 474
 	uchar *c;
taruti@0 475
 
cinap_lenrek@605 476
 	dreg("comreset ", pc->p);
taruti@0 477
 	if(ahciquiet(pc->p) == -1){
cinap_lenrek@605 478
 		dprint("ahci: ahciquiet failed\n");
taruti@0 479
 		return -1;
taruti@0 480
 	}
taruti@0 481
 	dreg("comreset ", pc->p);
taruti@0 482
 
cinap_lenrek@605 483
 	c = pc->m->ctab->cfis;
cinap_lenrek@605 484
 	nopfis(pc->m, c, 1);
cinap_lenrek@605 485
 	mkalist(pc->m, Lclear | Lreset, 0, 0);
taruti@0 486
 	if(ahciwait(pc, 500) == -1){
cinap_lenrek@605 487
 		dprint("ahci: comreset1 failed\n");
taruti@0 488
 		return -1;
taruti@0 489
 	}
taruti@0 490
 	microdelay(250);
taruti@0 491
 	dreg("comreset ", pc->p);
taruti@0 492
 
cinap_lenrek@605 493
 	nopfis(pc->m, c, 0);
cinap_lenrek@605 494
 	mkalist(pc->m, Lwrite, 0, 0);
taruti@0 495
 	if(ahciwait(pc, 150) == -1){
cinap_lenrek@605 496
 		dprint("ahci: comreset2 failed\n");
taruti@0 497
 		return -1;
taruti@0 498
 	}
taruti@0 499
 	dreg("comreset ", pc->p);
taruti@0 500
 	return 0;
taruti@0 501
 }
taruti@0 502
 
taruti@0 503
 static int
taruti@0 504
 ahciidle(Aport *port)
taruti@0 505
 {
taruti@0 506
 	ulong *p, i, r;
taruti@0 507
 
taruti@0 508
 	p = &port->cmd;
taruti@0 509
 	if((*p & Arun) == 0)
taruti@0 510
 		return 0;
taruti@0 511
 	*p &= ~Ast;
taruti@0 512
 	r = 0;
taruti@0 513
 	for(i = 0; i < 500; i += 25){
taruti@0 514
 		if((*p & Acr) == 0)
taruti@0 515
 			goto stop;
taruti@0 516
 		asleep(25);
taruti@0 517
 	}
taruti@0 518
 	r = -1;
taruti@0 519
 stop:
taruti@0 520
 	if((*p & Afre) == 0)
taruti@0 521
 		return r;
taruti@0 522
 	*p &= ~Afre;
taruti@0 523
 	for(i = 0; i < 500; i += 25){
taruti@0 524
 		if((*p & Afre) == 0)
taruti@0 525
 			return 0;
taruti@0 526
 		asleep(25);
taruti@0 527
 	}
taruti@0 528
 	return -1;
taruti@0 529
 }
taruti@0 530
 
taruti@0 531
 /*
cinap_lenrek@605 532
  * §6.2.2.1  first part; comreset handled by reset disk.
taruti@0 533
  *	- remainder is handled by configdisk.
taruti@0 534
  *	- ahcirecover is a quick recovery from a failed command.
taruti@0 535
  */
taruti@0 536
 static int
taruti@0 537
 ahciswreset(Aportc *pc)
taruti@0 538
 {
taruti@0 539
 	int i;
taruti@0 540
 
taruti@0 541
 	i = ahciidle(pc->p);
taruti@0 542
 	pc->p->cmd |= Afre;
taruti@0 543
 	if(i == -1)
taruti@0 544
 		return -1;
taruti@0 545
 	if(pc->p->task & (ASdrq|ASbsy))
taruti@0 546
 		return -1;
taruti@0 547
 	return 0;
taruti@0 548
 }
taruti@0 549
 
taruti@0 550
 static int
taruti@0 551
 ahcirecover(Aportc *pc)
taruti@0 552
 {
taruti@0 553
 	ahciswreset(pc);
taruti@0 554
 	pc->p->cmd |= Ast;
cinap_lenrek@605 555
 	if(settxmode(pc, pc->m->udma) == -1)
taruti@0 556
 		return -1;
taruti@0 557
 	return 0;
taruti@0 558
 }
taruti@0 559
 
taruti@0 560
 static void*
taruti@0 561
 malign(int size, int align)
taruti@0 562
 {
taruti@0 563
 	void *v;
taruti@0 564
 
taruti@0 565
 	v = xspanalloc(size, align, 0);
taruti@0 566
 	memset(v, 0, size);
taruti@0 567
 	return v;
taruti@0 568
 }
taruti@0 569
 
taruti@0 570
 static void
taruti@0 571
 setupfis(Afis *f)
taruti@0 572
 {
taruti@0 573
 	f->base = malign(0x100, 0x100);
taruti@0 574
 	f->d = f->base + 0;
taruti@0 575
 	f->p = f->base + 0x20;
taruti@0 576
 	f->r = f->base + 0x40;
taruti@0 577
 	f->u = f->base + 0x60;
taruti@0 578
 	f->devicebits = (ulong*)(f->base + 0x58);
taruti@0 579
 }
taruti@0 580
 
taruti@0 581
 static void
cinap_lenrek@605 582
 ahciwakeup(Aportc *c, uint mode)
taruti@0 583
 {
taruti@0 584
 	ushort s;
cinap_lenrek@2223 585
 	Aport *p;
taruti@0 586
 
cinap_lenrek@2223 587
 	p = c->p;
cinap_lenrek@2223 588
 	s = p->sstatus;
cinap_lenrek@2419 589
 	if((s & Isleepy) == 0)
taruti@0 590
 		return;
cinap_lenrek@605 591
 	if((s & Smask) != Spresent){
cinap_lenrek@2223 592
 		dprint("ahci: slumbering drive missing [ss %.3ux]\n", s);
taruti@0 593
 		return;
taruti@0 594
 	}
cinap_lenrek@605 595
 	ahciportreset(c, mode);
cinap_lenrek@2223 596
 	dprint("ahci: wake %.3ux -> %.3lux\n", s, c->p->sstatus);
taruti@0 597
 }
taruti@0 598
 
taruti@0 599
 static int
taruti@0 600
 ahciconfigdrive(Ahba *h, Aportc *c, int mode)
taruti@0 601
 {
taruti@0 602
 	Aportm *m;
taruti@0 603
 	Aport *p;
cinap_lenrek@2223 604
 	int i;
taruti@0 605
 
taruti@0 606
 	p = c->p;
taruti@0 607
 	m = c->m;
taruti@0 608
 
taruti@0 609
 	if(m->list == 0){
taruti@0 610
 		setupfis(&m->fis);
taruti@0 611
 		m->list = malign(sizeof *m->list, 1024);
taruti@0 612
 		m->ctab = malign(sizeof *m->ctab, 128);
taruti@0 613
 	}
taruti@0 614
 
cinap_lenrek@2223 615
 	if(ahciidle(p) == -1){
cinap_lenrek@2223 616
 		dprint("ahci: port not idle\n");
cinap_lenrek@2223 617
 		return -1;
cinap_lenrek@2223 618
 	}
cinap_lenrek@2223 619
 
cinap_lenrek@605 620
 	p->list = PCIWADDR(m->list);
cinap_lenrek@605 621
 	p->listhi = Pciwaddrh(m->list);
cinap_lenrek@605 622
 	p->fis = PCIWADDR(m->fis.base);
cinap_lenrek@605 623
 	p->fishi = Pciwaddrh(m->fis.base);
cinap_lenrek@605 624
 
cinap_lenrek@605 625
 	p->cmd |= Afre;
cinap_lenrek@605 626
 
cinap_lenrek@2145 627
 	if((p->cmd & Apwr) != Apwr)
cinap_lenrek@605 628
 		p->cmd |= Apwr;
cinap_lenrek@2145 629
 
cinap_lenrek@2145 630
 	if((h->cap & Hss) != 0){
cinap_lenrek@2144 631
 		dprint("ahci: spin up ... [%.3lux]\n", p->sstatus);
cinap_lenrek@2223 632
 		for(i = 0; i < 1400; i += 50){
cinap_lenrek@2145 633
 			if((p->sstatus & Sbist) != 0)
cinap_lenrek@2145 634
 				break;
cinap_lenrek@2230 635
 			if((p->sstatus & Smask) == Sphylink)
cinap_lenrek@605 636
 				break;
cinap_lenrek@605 637
 			asleep(50);
cinap_lenrek@605 638
 		}
taruti@0 639
 	}
taruti@0 640
 
cinap_lenrek@605 641
 	if((p->sstatus & SSmask) == (Isleepy | Spresent))
cinap_lenrek@605 642
 		ahciwakeup(c, mode);
cinap_lenrek@2144 643
 
cinap_lenrek@2223 644
 	p->serror = SerrAll;
cinap_lenrek@2223 645
 	p->ie = IEM;
cinap_lenrek@2223 646
 
cinap_lenrek@2223 647
 	/* we will get called again once phylink has been established */
cinap_lenrek@2230 648
 	if((p->sstatus & Smask) != Sphylink)
cinap_lenrek@2223 649
 		return 0;
cinap_lenrek@2223 650
 
taruti@0 651
 	/* disable power managment sequence from book. */
cinap_lenrek@605 652
 	p->sctl = 3*Aipm | mode*Aspd | 0*Adet;
cinap_lenrek@2145 653
 	p->cmd &= ~Aalpe;
taruti@0 654
 
cinap_lenrek@2223 655
 	p->cmd |= Afre | Ast;
taruti@0 656
 
taruti@0 657
 	return 0;
taruti@0 658
 }
taruti@0 659
 
taruti@0 660
 static int
taruti@0 661
 ahcienable(Ahba *h)
taruti@0 662
 {
taruti@0 663
 	h->ghc |= Hie;
taruti@0 664
 	return 0;
taruti@0 665
 }
taruti@0 666
 
taruti@0 667
 static int
taruti@0 668
 ahcidisable(Ahba *h)
taruti@0 669
 {
taruti@0 670
 	h->ghc &= ~Hie;
taruti@0 671
 	return 0;
taruti@0 672
 }
taruti@0 673
 
taruti@0 674
 static int
taruti@0 675
 countbits(ulong u)
taruti@0 676
 {
taruti@0 677
 	int i, n;
taruti@0 678
 
taruti@0 679
 	n = 0;
taruti@0 680
 	for(i = 0; i < 32; i++)
taruti@0 681
 		if(u & (1<<i))
taruti@0 682
 			n++;
taruti@0 683
 	return n;
taruti@0 684
 }
taruti@0 685
 
taruti@0 686
 static int
cinap_lenrek@605 687
 ahciconf(Ctlr *c)
taruti@0 688
 {
cinap_lenrek@605 689
 	uint u;
taruti@0 690
 	Ahba *h;
taruti@0 691
 
cinap_lenrek@605 692
 	h = c->hba = (Ahba*)c->mmio;
taruti@0 693
 	u = h->cap;
taruti@0 694
 
cinap_lenrek@605 695
 	if((u & Ham) == 0)
taruti@0 696
 		h->ghc |= Hae;
taruti@0 697
 
taruti@0 698
 	return countbits(h->pi);
taruti@0 699
 }
taruti@0 700
 
taruti@0 701
 static int
cinap_lenrek@2223 702
 ahcihandoff(Ahba *h)
cinap_lenrek@2223 703
 {
cinap_lenrek@2223 704
 	int wait;
cinap_lenrek@2223 705
 
cinap_lenrek@2223 706
 	if((h->cap2 & Boh) == 0)
cinap_lenrek@2223 707
 		return 0;
cinap_lenrek@2223 708
 	h->bios |= Oos;
cinap_lenrek@2223 709
 	for(wait = 0; wait < 2000; wait += 100){
cinap_lenrek@2223 710
 		if((h->bios & Bos) == 0)
cinap_lenrek@2223 711
 			return 0;
cinap_lenrek@2223 712
 		delay(100);
cinap_lenrek@2223 713
 	}
cinap_lenrek@2223 714
 	iprint("ahci: bios handoff timed out\n");
cinap_lenrek@2223 715
 	return -1;
cinap_lenrek@2223 716
 }
cinap_lenrek@2223 717
 
cinap_lenrek@2223 718
 static int
taruti@0 719
 ahcihbareset(Ahba *h)
taruti@0 720
 {
taruti@0 721
 	int wait;
taruti@0 722
 
cinap_lenrek@605 723
 	h->ghc |= Hhr;
taruti@0 724
 	for(wait = 0; wait < 1000; wait += 100){
cinap_lenrek@3623 725
 		if((h->ghc & Hhr) == 0)
taruti@0 726
 			return 0;
taruti@0 727
 		delay(100);
taruti@0 728
 	}
taruti@0 729
 	return -1;
taruti@0 730
 }
taruti@0 731
 
cinap_lenrek@605 732
 static char*
cinap_lenrek@605 733
 dnam(Drive *d)
taruti@0 734
 {
cinap_lenrek@605 735
 	char *s;
taruti@0 736
 
cinap_lenrek@605 737
 	s = d->name;
cinap_lenrek@605 738
 	if(d->unit && d->unit->name)
cinap_lenrek@605 739
 		s = d->unit->name;
cinap_lenrek@605 740
 	return s;
taruti@0 741
 }
taruti@0 742
 
taruti@0 743
 static int
taruti@0 744
 identify(Drive *d)
taruti@0 745
 {
cinap_lenrek@605 746
 	uchar oserial[21];
taruti@0 747
 	ushort *id;
taruti@0 748
 	vlong osectors, s;
taruti@0 749
 	SDunit *u;
taruti@0 750
 
taruti@0 751
 	id = d->info;
cinap_lenrek@605 752
 	s = ahciidentify(&d->portc, id, &d->secsize, dnam(d));
cinap_lenrek@2223 753
 	if(s == -1)
taruti@0 754
 		return -1;
taruti@0 755
 	osectors = d->sectors;
taruti@0 756
 	memmove(oserial, d->serial, sizeof d->serial);
taruti@0 757
 
taruti@0 758
 	d->sectors = s;
taruti@0 759
 
taruti@0 760
 	idmove(d->serial, id+10, 20);
taruti@0 761
 	idmove(d->firmware, id+23, 8);
taruti@0 762
 	idmove(d->model, id+27, 40);
cinap_lenrek@605 763
 	d->wwn = idwwn(d->portc.m, id);
taruti@0 764
 
cinap_lenrek@605 765
 	u = d->unit;
taruti@0 766
 	memset(u->inquiry, 0, sizeof u->inquiry);
taruti@0 767
 	u->inquiry[2] = 2;
taruti@0 768
 	u->inquiry[3] = 2;
taruti@0 769
 	u->inquiry[4] = sizeof u->inquiry - 4;
taruti@0 770
 	memmove(u->inquiry+8, d->model, 40);
taruti@0 771
 
cinap_lenrek@605 772
 	if(osectors != s || memcmp(oserial, d->serial, sizeof oserial)){
cinap_lenrek@605 773
 		d->drivechange = 1;
cinap_lenrek@2230 774
 		d->nodma = 0;
taruti@0 775
 		u->sectors = 0;
taruti@0 776
 	}
taruti@0 777
 	return 0;
taruti@0 778
 }
taruti@0 779
 
taruti@0 780
 static void
taruti@0 781
 clearci(Aport *p)
taruti@0 782
 {
cinap_lenrek@605 783
 	if(p->cmd & Ast){
taruti@0 784
 		p->cmd &= ~Ast;
taruti@0 785
 		p->cmd |=  Ast;
taruti@0 786
 	}
taruti@0 787
 }
taruti@0 788
 
cinap_lenrek@605 789
 static int
cinap_lenrek@605 790
 ignoreahdrs(Drive *d)
cinap_lenrek@605 791
 {
cinap_lenrek@605 792
 	return d->portm.feat & Datapi && d->ctlr->type == Tsb600;
cinap_lenrek@605 793
 }
cinap_lenrek@605 794
 
taruti@0 795
 static void
taruti@0 796
 updatedrive(Drive *d)
taruti@0 797
 {
cinap_lenrek@605 798
 	ulong f, cause, serr, s0, pr, ewake;
taruti@0 799
 	Aport *p;
taruti@0 800
 	static ulong last;
taruti@0 801
 
taruti@0 802
 	pr = 1;
taruti@0 803
 	ewake = 0;
cinap_lenrek@605 804
 	f = 0;
taruti@0 805
 	p = d->port;
taruti@0 806
 	cause = p->isr;
cinap_lenrek@605 807
 	if(d->ctlr->type == Tjmicron)
cinap_lenrek@605 808
 		cause &= ~Aifs;
taruti@0 809
 	serr = p->serror;
taruti@0 810
 	p->isr = cause;
taruti@0 811
 
taruti@0 812
 	if(p->ci == 0){
cinap_lenrek@605 813
 		f |= Fdone;
taruti@0 814
 		pr = 0;
cinap_lenrek@2223 815
 	}else if(cause & Adps){
taruti@0 816
 		pr = 0;
cinap_lenrek@2223 817
 	}else if(cause & Atfes){
cinap_lenrek@2223 818
 		f |= Ferror;
cinap_lenrek@2223 819
 		ewake = 1;
cinap_lenrek@2223 820
 		pr = 0;
cinap_lenrek@2223 821
 	}
taruti@0 822
 	if(cause & Ifatal){
taruti@0 823
 		ewake = 1;
cinap_lenrek@605 824
 		dprint("%s: fatal\n", dnam(d));
taruti@0 825
 	}
taruti@0 826
 	if(cause & Adhrs){
cinap_lenrek@605 827
 		if(p->task & 33){
cinap_lenrek@605 828
 			if(ignoreahdrs(d) && serr & ErrE)
cinap_lenrek@605 829
 				f |= Fahdrs;
cinap_lenrek@605 830
 			dprint("%s: Adhrs cause %lux serr %lux task %lux\n",
cinap_lenrek@605 831
 				dnam(d), cause, serr, p->task);
cinap_lenrek@605 832
 			f |= Ferror;
taruti@0 833
 			ewake = 1;
taruti@0 834
 		}
taruti@0 835
 		pr = 0;
taruti@0 836
 	}
taruti@0 837
 	if(p->task & 1 && last != cause)
cinap_lenrek@605 838
 		dprint("%s: err ca %lux serr %lux task %lux sstat %.3lux\n",
cinap_lenrek@605 839
 			dnam(d), cause, serr, p->task, p->sstatus);
taruti@0 840
 	if(pr)
cinap_lenrek@605 841
 		dprint("%s: upd %lux ta %lux\n", dnam(d), cause, p->task);
taruti@0 842
 
taruti@0 843
 	if(cause & (Aprcs|Aifs)){
taruti@0 844
 		s0 = d->state;
cinap_lenrek@605 845
 		switch(p->sstatus & Smask){
cinap_lenrek@605 846
 		case Smissing:
taruti@0 847
 			d->state = Dmissing;
taruti@0 848
 			break;
cinap_lenrek@605 849
 		case Spresent:
cinap_lenrek@605 850
 			if((p->sstatus & Imask) == Islumber)
cinap_lenrek@605 851
 				d->state = Dnew;
taruti@0 852
 			else
taruti@0 853
 				d->state = Derror;
taruti@0 854
 			break;
cinap_lenrek@605 855
 		case Sphylink:
cinap_lenrek@605 856
 			/* power mgnt crap for suprise removal */
taruti@0 857
 			p->ie |= Aprcs|Apcs;	/* is this required? */
taruti@0 858
 			d->state = Dreset;
taruti@0 859
 			break;
cinap_lenrek@605 860
 		case Sbist:
taruti@0 861
 			d->state = Doffline;
taruti@0 862
 			break;
taruti@0 863
 		}
cinap_lenrek@2223 864
 		dprint("%s: updatedrive: %s → %s [ss %.3lux]\n",
cinap_lenrek@2223 865
 			dnam(d), diskstates[s0], diskstates[d->state], p->sstatus);
taruti@0 866
 		if(s0 == Dready && d->state != Dready)
cinap_lenrek@2223 867
 			dprint("%s: pulled\n", dnam(d));
taruti@0 868
 		if(d->state != Dready)
cinap_lenrek@605 869
 			f |= Ferror;
cinap_lenrek@605 870
 		if(d->state != Dready || p->ci)
cinap_lenrek@605 871
 			ewake = 1;
taruti@0 872
 	}
taruti@0 873
 	p->serror = serr;
cinap_lenrek@605 874
 	if(ewake)
taruti@0 875
 		clearci(p);
cinap_lenrek@605 876
 	if(f){
cinap_lenrek@605 877
 		d->portm.flag = f;
taruti@0 878
 		wakeup(&d->portm);
taruti@0 879
 	}
taruti@0 880
 	last = cause;
taruti@0 881
 }
taruti@0 882
 
taruti@0 883
 static void
cinap_lenrek@2223 884
 dstatus(Drive *d, int s)
taruti@0 885
 {
cinap_lenrek@2224 886
 	dprint("%s: dstatus: %s → %s from pc=%p\n", dnam(d), 
cinap_lenrek@2224 887
 		diskstates[d->state], diskstates[s], getcallerpc(&d));
cinap_lenrek@2224 888
 
cinap_lenrek@2223 889
 	ilock(d);
cinap_lenrek@2223 890
 	d->state = s;
cinap_lenrek@2223 891
 	iunlock(d);
cinap_lenrek@2223 892
 }
cinap_lenrek@2223 893
 
cinap_lenrek@2223 894
 static void
cinap_lenrek@2223 895
 configdrive(Drive *d)
cinap_lenrek@2223 896
 {
cinap_lenrek@2232 897
 	if(ahciconfigdrive(d->ctlr->hba, &d->portc, d->mode) == -1){
cinap_lenrek@2223 898
 		dstatus(d, Dportreset);
cinap_lenrek@2232 899
 		return;
cinap_lenrek@2232 900
 	}
cinap_lenrek@2223 901
 
cinap_lenrek@2223 902
 	ilock(d);
cinap_lenrek@2223 903
 	switch(d->port->sstatus & Smask){
cinap_lenrek@2423 904
 	default:
cinap_lenrek@605 905
 	case Smissing:
taruti@0 906
 		d->state = Dmissing;
taruti@0 907
 		break;
cinap_lenrek@605 908
 	case Spresent:
cinap_lenrek@2250 909
 		if(d->state == Dnull)
cinap_lenrek@2250 910
 			d->state = Dportreset;
taruti@0 911
 		break;
cinap_lenrek@605 912
 	case Sphylink:
cinap_lenrek@2223 913
 		if(d->state == Dready)
cinap_lenrek@2223 914
 			break;
taruti@0 915
 		d->wait = 0;
taruti@0 916
 		d->state = Dnew;
taruti@0 917
 		break;
cinap_lenrek@605 918
 	case Sbist:
taruti@0 919
 		d->state = Doffline;
taruti@0 920
 		break;
taruti@0 921
 	}
taruti@0 922
 	iunlock(d);
cinap_lenrek@2224 923
 
cinap_lenrek@2224 924
 	dprint("%s: configdrive: %s\n", dnam(d), diskstates[d->state]);
taruti@0 925
 }
taruti@0 926
 
taruti@0 927
 static void
taruti@0 928
 resetdisk(Drive *d)
taruti@0 929
 {
taruti@0 930
 	uint state, det, stat;
taruti@0 931
 	Aport *p;
taruti@0 932
 
taruti@0 933
 	p = d->port;
taruti@0 934
 	det = p->sctl & 7;
cinap_lenrek@605 935
 	stat = p->sstatus & Smask;
taruti@0 936
 	state = (p->cmd>>28) & 0xf;
cinap_lenrek@2223 937
 	dprint("%s: resetdisk [icc %ux; det %.3ux; sdet %.3ux]\n", dnam(d), state, det, stat);
taruti@0 938
 
taruti@0 939
 	ilock(d);
cinap_lenrek@2223 940
 	if(d->state != Dready && d->state != Dnew)
taruti@0 941
 		d->portm.flag |= Ferror;
cinap_lenrek@2223 942
 	if(stat != Sphylink)
cinap_lenrek@2223 943
 		d->state = Dportreset;
cinap_lenrek@2223 944
 	else
cinap_lenrek@2223 945
 		d->state = Dreset;
taruti@0 946
 	clearci(p);			/* satisfy sleep condition. */
taruti@0 947
 	wakeup(&d->portm);
cinap_lenrek@605 948
 	iunlock(d);
cinap_lenrek@605 949
 
cinap_lenrek@2223 950
 	if(stat != Sphylink)
taruti@0 951
 		return;
taruti@0 952
 
taruti@0 953
 	qlock(&d->portm);
cinap_lenrek@2223 954
 	if(p->cmd&Ast && ahciswreset(&d->portc) == -1)
cinap_lenrek@2223 955
 		dstatus(d, Dportreset);	/* get a bigger stick. */
cinap_lenrek@2223 956
 	else
taruti@0 957
 		configdrive(d);
taruti@0 958
 	qunlock(&d->portm);
taruti@0 959
 }
taruti@0 960
 
taruti@0 961
 static int
taruti@0 962
 newdrive(Drive *d)
taruti@0 963
 {
cinap_lenrek@605 964
 	char *s;
taruti@0 965
 	Aportc *c;
taruti@0 966
 	Aportm *m;
taruti@0 967
 
taruti@0 968
 	c = &d->portc;
taruti@0 969
 	m = &d->portm;
taruti@0 970
 
taruti@0 971
 	qlock(c->m);
cinap_lenrek@605 972
 	setfissig(m, c->p->sig);
cinap_lenrek@605 973
 	if(identify(d) == -1){
cinap_lenrek@605 974
 		dprint("%s: identify failure\n", dnam(d));
taruti@0 975
 		goto lose;
taruti@0 976
 	}
cinap_lenrek@605 977
 	if(settxmode(c, m->udma) == -1){
cinap_lenrek@605 978
 		dprint("%s: can't set udma mode\n", dnam(d));
taruti@0 979
 		goto lose;
taruti@0 980
 	}
cinap_lenrek@605 981
 	if(m->feat & Dpower && setfeatures(c, 0x85, 3*1000) == -1){
cinap_lenrek@2223 982
 		dprint("%s: can't disable apm\n", dnam(d));
taruti@0 983
 		m->feat &= ~Dpower;
taruti@0 984
 		if(ahcirecover(c) == -1)
taruti@0 985
 			goto lose;
taruti@0 986
 	}
cinap_lenrek@2223 987
 	dstatus(d, Dready);
taruti@0 988
 	qunlock(c->m);
taruti@0 989
 
cinap_lenrek@605 990
 	s = "";
cinap_lenrek@605 991
 	if(m->feat & Dllba)
cinap_lenrek@605 992
 		s = "L";
cinap_lenrek@605 993
 	idprint("%s: %sLBA %,lld sectors\n", dnam(d), s, d->sectors);
cinap_lenrek@605 994
 	idprint("  %s %s %s %s\n", d->model, d->firmware, d->serial,
cinap_lenrek@605 995
 		d->drivechange? "[newdrive]": "");
taruti@0 996
 	return 0;
taruti@0 997
 
taruti@0 998
 lose:
cinap_lenrek@605 999
 	idprint("%s: can't be initialized\n", dnam(d));
cinap_lenrek@2223 1000
 	dstatus(d, Dnull);
taruti@0 1001
 	qunlock(c->m);
taruti@0 1002
 	return -1;
taruti@0 1003
 }
taruti@0 1004
 
taruti@0 1005
 enum {
taruti@0 1006
 	Nms		= 256,
cinap_lenrek@6149 1007
 	Mcomrwait	=  1*1024/Nms - 1,
taruti@0 1008
 	Mphywait	=  2*1024/Nms - 1,
taruti@0 1009
 	Midwait		= 16*1024/Nms - 1,
taruti@0 1010
 };
taruti@0 1011
 
taruti@0 1012
 static void
cinap_lenrek@605 1013
 hangck(Drive *d)
taruti@0 1014
 {
cinap_lenrek@2224 1015
 	if(d->active && d->totick != 0 && (long)(Ticks - d->totick) > 0){
cinap_lenrek@2230 1016
 		dprint("%s: drive hung [task %lux; ci %lux; serr %lux]%s\n",
cinap_lenrek@2230 1017
 			dnam(d), d->port->task, d->port->ci, d->port->serror,
cinap_lenrek@2230 1018
 			d->nodma == 0 ? "; disabling dma" : "");
cinap_lenrek@2230 1019
 		d->nodma = 1;
taruti@0 1020
 		d->state = Dreset;
taruti@0 1021
 	}
taruti@0 1022
 }
taruti@0 1023
 
taruti@0 1024
 static ushort olds[NCtlr*NCtlrdrv];
taruti@0 1025
 
cinap_lenrek@2223 1026
 static void
taruti@0 1027
 doportreset(Drive *d)
taruti@0 1028
 {
cinap_lenrek@2223 1029
 	qlock(&d->portm);
cinap_lenrek@2223 1030
 	ahciportreset(&d->portc, d->mode);
cinap_lenrek@2223 1031
 	qunlock(&d->portm);
taruti@0 1032
 
cinap_lenrek@2223 1033
 	dprint("ahci: portreset: %s [task %lux; ss %.3lux]\n",
cinap_lenrek@605 1034
 		diskstates[d->state], d->port->task, d->port->sstatus);
taruti@0 1035
 }
taruti@0 1036
 
taruti@0 1037
 /* drive must be locked */
taruti@0 1038
 static void
taruti@0 1039
 statechange(Drive *d)
taruti@0 1040
 {
taruti@0 1041
 	switch(d->state){
taruti@0 1042
 	case Dnull:
taruti@0 1043
 	case Doffline:
cinap_lenrek@605 1044
 		if(d->unit)
taruti@0 1045
 		if(d->unit->sectors != 0){
taruti@0 1046
 			d->sectors = 0;
cinap_lenrek@605 1047
 			d->drivechange = 1;
taruti@0 1048
 		}
taruti@0 1049
 	case Dready:
taruti@0 1050
 		d->wait = 0;
taruti@0 1051
 	}
taruti@0 1052
 }
taruti@0 1053
 
cinap_lenrek@605 1054
 static uint
cinap_lenrek@605 1055
 maxmode(Ctlr *c)
cinap_lenrek@605 1056
 {
cinap_lenrek@605 1057
 	return (c->hba->cap & 0xf*Hiss)/Hiss;
cinap_lenrek@605 1058
 }
cinap_lenrek@605 1059
 
cinap_lenrek@2223 1060
 static void iainterrupt(Ureg*, void *);
cinap_lenrek@2223 1061
 
taruti@0 1062
 static void
taruti@0 1063
 checkdrive(Drive *d, int i)
taruti@0 1064
 {
cinap_lenrek@605 1065
 	ushort s, sig;
taruti@0 1066
 
cinap_lenrek@2223 1067
 	if(d->ctlr->enabled == 0)
cinap_lenrek@2223 1068
 		return;
cinap_lenrek@2223 1069
 	if(d->driveno == 0)
cinap_lenrek@2223 1070
 		iainterrupt(0, d->ctlr);	/* check for missed irq's */
cinap_lenrek@2223 1071
 
taruti@0 1072
 	ilock(d);
taruti@0 1073
 	s = d->port->sstatus;
taruti@0 1074
 	if(s)
cinap_lenrek@605 1075
 		d->lastseen = Ticks;
taruti@0 1076
 	if(s != olds[i]){
cinap_lenrek@605 1077
 		dprint("%s: status: %.3ux -> %.3ux: %s\n",
cinap_lenrek@605 1078
 			dnam(d), olds[i], s, diskstates[d->state]);
taruti@0 1079
 		olds[i] = s;
taruti@0 1080
 		d->wait = 0;
taruti@0 1081
 	}
cinap_lenrek@605 1082
 	hangck(d);
taruti@0 1083
 	switch(d->state){
taruti@0 1084
 	case Dnull:
taruti@0 1085
 	case Dready:
taruti@0 1086
 		break;
taruti@0 1087
 	case Dmissing:
taruti@0 1088
 	case Dnew:
cinap_lenrek@605 1089
 		switch(s & (Iactive|Smask)){
cinap_lenrek@605 1090
 		case Spresent:
cinap_lenrek@605 1091
 			ahciwakeup(&d->portc, d->mode);
cinap_lenrek@605 1092
 		case Smissing:
taruti@0 1093
 			break;
taruti@0 1094
 		default:
cinap_lenrek@605 1095
 			dprint("%s: unknown status %.3ux\n", dnam(d), s);
taruti@0 1096
 			/* fall through */
cinap_lenrek@605 1097
 		case Iactive:		/* active, no device */
taruti@0 1098
 			if(++d->wait&Mphywait)
taruti@0 1099
 				break;
taruti@0 1100
 reset:
cinap_lenrek@605 1101
 			if(d->mode == 0)
cinap_lenrek@605 1102
 				d->mode = maxmode(d->ctlr);
cinap_lenrek@605 1103
 			else
cinap_lenrek@605 1104
 				d->mode--;
cinap_lenrek@605 1105
 			if(d->mode == DMautoneg){
cinap_lenrek@6149 1106
 				d->wait = 0;
taruti@0 1107
 				d->state = Dportreset;
taruti@0 1108
 				goto portreset;
taruti@0 1109
 			}
cinap_lenrek@605 1110
 			dprint("%s: reset; new mode %s\n", dnam(d),
cinap_lenrek@605 1111
 				modes[d->mode]);
taruti@0 1112
 			iunlock(d);
taruti@0 1113
 			resetdisk(d);
taruti@0 1114
 			ilock(d);
taruti@0 1115
 			break;
cinap_lenrek@605 1116
 		case Iactive | Sphylink:
cinap_lenrek@605 1117
 			if(d->unit == nil)
cinap_lenrek@605 1118
 				break;
taruti@0 1119
 			if((++d->wait&Midwait) == 0){
cinap_lenrek@2223 1120
 				dprint("%s: slow reset [task %lux; ss %.3ux; wait %d]\n",
cinap_lenrek@2223 1121
 					dnam(d), d->port->task, s, d->wait);
taruti@0 1122
 				goto reset;
taruti@0 1123
 			}
taruti@0 1124
 			s = (uchar)d->port->task;
cinap_lenrek@605 1125
 			sig = d->port->sig >> 16;
cinap_lenrek@605 1126
 			if(s == 0x7f || s&ASbsy ||
cinap_lenrek@605 1127
 			    (sig != 0xeb14 && (s & ASdrdy) == 0))
taruti@0 1128
 				break;
taruti@0 1129
 			iunlock(d);
taruti@0 1130
 			newdrive(d);
taruti@0 1131
 			ilock(d);
taruti@0 1132
 			break;
taruti@0 1133
 		}
taruti@0 1134
 		break;
taruti@0 1135
 	case Doffline:
taruti@0 1136
 		if(d->wait++ & Mcomrwait)
taruti@0 1137
 			break;
taruti@0 1138
 	case Derror:
taruti@0 1139
 	case Dreset:
cinap_lenrek@605 1140
 		dprint("%s: reset [%s]: mode %d; status %.3ux\n",
cinap_lenrek@605 1141
 			dnam(d), diskstates[d->state], d->mode, s);
taruti@0 1142
 		iunlock(d);
taruti@0 1143
 		resetdisk(d);
taruti@0 1144
 		ilock(d);
taruti@0 1145
 		break;
taruti@0 1146
 	case Dportreset:
taruti@0 1147
 portreset:
cinap_lenrek@6149 1148
 		if(d->wait++ & Mcomrwait)
taruti@0 1149
 			break;
cinap_lenrek@6149 1150
 		if(d->wait > Mcomrwait && (s & Iactive) == 0){
cinap_lenrek@6149 1151
 			d->state = Dnull;	/* stuck in portreset */
cinap_lenrek@6149 1152
 			break;
cinap_lenrek@6149 1153
 		}
cinap_lenrek@605 1154
 		dprint("%s: portreset [%s]: mode %d; status %.3ux\n",
cinap_lenrek@605 1155
 			dnam(d), diskstates[d->state], d->mode, s);
taruti@0 1156
 		d->portm.flag |= Ferror;
taruti@0 1157
 		clearci(d->port);
taruti@0 1158
 		wakeup(&d->portm);
cinap_lenrek@2223 1159
 		if((s & Smask) == Smissing){
taruti@0 1160
 			d->state = Dmissing;
taruti@0 1161
 			break;
taruti@0 1162
 		}
taruti@0 1163
 		iunlock(d);
taruti@0 1164
 		doportreset(d);
taruti@0 1165
 		ilock(d);
taruti@0 1166
 		break;
taruti@0 1167
 	}
taruti@0 1168
 	statechange(d);
taruti@0 1169
 	iunlock(d);
taruti@0 1170
 }
taruti@0 1171
 
taruti@0 1172
 static void
taruti@0 1173
 satakproc(void*)
taruti@0 1174
 {
taruti@0 1175
 	int i;
taruti@0 1176
 
cinap_lenrek@3110 1177
 	while(waserror())
cinap_lenrek@3110 1178
 		;
taruti@0 1179
 	for(;;){
taruti@0 1180
 		tsleep(&up->sleep, return0, 0, Nms);
taruti@0 1181
 		for(i = 0; i < niadrive; i++)
taruti@0 1182
 			checkdrive(iadrive[i], i);
taruti@0 1183
 	}
taruti@0 1184
 }
taruti@0 1185
 
taruti@0 1186
 static void
cinap_lenrek@2223 1187
 iainterrupt(Ureg *u, void *a)
taruti@0 1188
 {
taruti@0 1189
 	int i;
taruti@0 1190
 	ulong cause, m;
taruti@0 1191
 	Ctlr *c;
taruti@0 1192
 	Drive *d;
taruti@0 1193
 
taruti@0 1194
 	c = a;
taruti@0 1195
 	ilock(c);
taruti@0 1196
 	cause = c->hba->isr;
cinap_lenrek@605 1197
 	for(i = 0; cause; i++){
taruti@0 1198
 		m = 1 << i;
taruti@0 1199
 		if((cause & m) == 0)
taruti@0 1200
 			continue;
cinap_lenrek@605 1201
 		cause &= ~m;
taruti@0 1202
 		d = c->rawdrive + i;
taruti@0 1203
 		ilock(d);
cinap_lenrek@2418 1204
 		if(d->port != nil && d->port->isr && c->hba->pi & m)
taruti@0 1205
 			updatedrive(d);
taruti@0 1206
 		c->hba->isr = m;
taruti@0 1207
 		iunlock(d);
taruti@0 1208
 	}
cinap_lenrek@2223 1209
 	if(u == 0 && i > 0)
cinap_lenrek@2223 1210
 		c->missirq++;
taruti@0 1211
 	iunlock(c);
taruti@0 1212
 }
taruti@0 1213
 
taruti@0 1214
 static int
cinap_lenrek@605 1215
 ahciencreset(Ctlr *c)
cinap_lenrek@605 1216
 {
cinap_lenrek@605 1217
 	Ahba *h;
cinap_lenrek@605 1218
 
cinap_lenrek@605 1219
 	if(c->enctype == Eesb)
cinap_lenrek@605 1220
 		return 0;
cinap_lenrek@605 1221
 	h = c->hba;
cinap_lenrek@605 1222
 	h->emctl |= Emrst;
cinap_lenrek@605 1223
 	while(h->emctl & Emrst)
cinap_lenrek@605 1224
 		delay(1);
cinap_lenrek@605 1225
 	return 0;
cinap_lenrek@605 1226
 }
cinap_lenrek@605 1227
 
cinap_lenrek@605 1228
 /*
cinap_lenrek@605 1229
  * from the standard: (http://en.wikipedia.org/wiki/IBPI)
cinap_lenrek@605 1230
  * rebuild is preferred as locate+fail; alternate 1hz fail
cinap_lenrek@605 1231
  * we're going to assume no locate led.
cinap_lenrek@605 1232
  */
cinap_lenrek@605 1233
 
cinap_lenrek@605 1234
 enum {
cinap_lenrek@605 1235
 	Ledsleep	= 125,		/* 8hz */
cinap_lenrek@605 1236
 
cinap_lenrek@605 1237
 	N0	= Ledon*Aled,
cinap_lenrek@605 1238
 	L0	= Ledon*Aled | Ledon*Locled,
cinap_lenrek@605 1239
 	L1	= Ledon*Aled | Ledoff*Locled,
cinap_lenrek@605 1240
 	R0	= Ledon*Aled | Ledon*Locled |	Ledon*Errled,
cinap_lenrek@605 1241
 	R1	= Ledon*Aled | 			Ledoff*Errled,
cinap_lenrek@605 1242
 	S0	= Ledon*Aled |  Ledon*Locled /*|	Ledon*Errled*/,	/* botch */
cinap_lenrek@605 1243
 	S1	= Ledon*Aled | 			Ledoff*Errled,
cinap_lenrek@605 1244
 	P0	= Ledon*Aled | 			Ledon*Errled,
cinap_lenrek@605 1245
 	P1	= Ledon*Aled | 			Ledoff*Errled,
cinap_lenrek@605 1246
 	F0	= Ledon*Aled | 			Ledon*Errled,
cinap_lenrek@605 1247
 	C0	= Ledon*Aled | Ledon*Locled,
cinap_lenrek@605 1248
 	C1	= Ledon*Aled | Ledoff*Locled,
cinap_lenrek@605 1249
 
cinap_lenrek@605 1250
 };
cinap_lenrek@605 1251
 
cinap_lenrek@605 1252
 //static ushort led3[Ibpilast*8] = {
cinap_lenrek@605 1253
 //[Ibpinone*8]	0,	0,	0,	0,	0,	0,	0,	0,
cinap_lenrek@605 1254
 //[Ibpinormal*8]	N0,	N0,	N0,	N0,	N0,	N0,	N0,	N0,
cinap_lenrek@605 1255
 //[Ibpirebuild*8]	R0,	R0,	R0,	R0,	R1,	R1,	R1,	R1,
cinap_lenrek@605 1256
 //[Ibpilocate*8]	L0,	L1,	L0,	L1,	L0,	L1,	L0,	L1,
cinap_lenrek@605 1257
 //[Ibpispare*8]	S0,	S1,	S0,	S1,	S1,	S1,	S1,	S1,
cinap_lenrek@605 1258
 //[Ibpipfa*8]	P0,	P1,	P0,	P1,	P1,	P1,	P1,	P1,	/* first 1 sec */
cinap_lenrek@605 1259
 //[Ibpifail*8]	F0,	F0,	F0,	F0,	F0,	F0,	F0,	F0,
cinap_lenrek@605 1260
 //[Ibpicritarray*8]	C0,	C0,	C0,	C0,	C1,	C1,	C1,	C1,
cinap_lenrek@605 1261
 //[Ibpifailarray*8]	C0,	C1,	C0,	C1,	C0,	C1,	C0,	C1,
cinap_lenrek@605 1262
 //};
cinap_lenrek@605 1263
 
cinap_lenrek@605 1264
 static ushort led2[Ibpilast*8] = {
cinap_lenrek@605 1265
 [Ibpinone*8]	0,	0,	0,	0,	0,	0,	0,	0,
cinap_lenrek@605 1266
 [Ibpinormal*8]	N0,	N0,	N0,	N0,	N0,	N0,	N0,	N0,
cinap_lenrek@605 1267
 [Ibpirebuild*8]	R0,	R0,	R0,	R0,	R1,	R1,	R1,	R1,
cinap_lenrek@605 1268
 [Ibpilocate*8]	L0,	L0,	L0,	L0,	L0,	L0,	L0,	L0,
cinap_lenrek@605 1269
 [Ibpispare*8]	S0,	S0,	S0,	S0,	S1,	S1,	S1,	S1,
cinap_lenrek@605 1270
 [Ibpipfa*8]	P0,	P1,	P0,	P1,	P1,	P1,	P1,	P1,	/* first 1 sec */
cinap_lenrek@605 1271
 [Ibpifail*8]	F0,	F0,	F0,	F0,	F0,	F0,	F0,	F0,
cinap_lenrek@605 1272
 [Ibpicritarray*8]	C0,	C0,	C0,	C0,	C1,	C1,	C1,	C1,
cinap_lenrek@605 1273
 [Ibpifailarray*8]	C0,	C1,	C0,	C1,	C0,	C1,	C0,	C1,
cinap_lenrek@605 1274
 };
cinap_lenrek@605 1275
 
cinap_lenrek@605 1276
 static int
cinap_lenrek@605 1277
 ledstate(Ledport *p, uint seq)
cinap_lenrek@605 1278
 {
cinap_lenrek@605 1279
 	ushort i;
cinap_lenrek@605 1280
 
cinap_lenrek@605 1281
 	if(p->led == Ibpipfa && seq%32 >= 8)
cinap_lenrek@605 1282
 		i = P1;
cinap_lenrek@605 1283
 	else
cinap_lenrek@605 1284
 		i = led2[8*p->led + seq%8];
cinap_lenrek@605 1285
 	if(i != p->ledbits){
cinap_lenrek@605 1286
 		p->ledbits = i;
cinap_lenrek@605 1287
 		ledprint("ledstate %,.011ub %ud\n", p->ledbits, seq);
cinap_lenrek@605 1288
 		return 1;
cinap_lenrek@605 1289
 	}
cinap_lenrek@605 1290
 	return 0;
cinap_lenrek@605 1291
 }
cinap_lenrek@605 1292
 
cinap_lenrek@605 1293
 static int
cinap_lenrek@605 1294
 blink(Drive *d, ulong t)
cinap_lenrek@605 1295
 {
cinap_lenrek@605 1296
 	Ahba *h;
cinap_lenrek@605 1297
 	Ctlr *c;
cinap_lenrek@605 1298
 	Aledmsg msg;
cinap_lenrek@605 1299
 
cinap_lenrek@605 1300
 	if(ledstate(d, t) == 0)
cinap_lenrek@605 1301
 		return 0;
cinap_lenrek@605 1302
 	c = d->ctlr;
cinap_lenrek@605 1303
 	h = c->hba;
cinap_lenrek@605 1304
 	/* ensure last message has been transmitted */
cinap_lenrek@605 1305
 	while(h->emctl & Tmsg)
cinap_lenrek@605 1306
 		microdelay(1);
cinap_lenrek@605 1307
 	switch(c->enctype){
cinap_lenrek@605 1308
 	default:
cinap_lenrek@1484 1309
 		panic("%s: bad led type %d", dnam(d), c->enctype);
cinap_lenrek@605 1310
 	case Elmt:
cinap_lenrek@605 1311
 		memset(&msg, 0, sizeof msg);
cinap_lenrek@605 1312
 		msg.type = Mled;
cinap_lenrek@605 1313
 		msg.dsize = 0;
cinap_lenrek@605 1314
 		msg.msize = sizeof msg - 4;
cinap_lenrek@605 1315
 		msg.led[0] = d->ledbits;
cinap_lenrek@605 1316
 		msg.led[1] = d->ledbits>>8;
cinap_lenrek@605 1317
 		msg.pm = 0;
cinap_lenrek@605 1318
 		msg.hba = d->driveno;
cinap_lenrek@605 1319
 		memmove(c->enctx, &msg, sizeof msg);
cinap_lenrek@605 1320
 		break;
cinap_lenrek@605 1321
 	}
cinap_lenrek@605 1322
 	h->emctl |= Tmsg;
cinap_lenrek@605 1323
 	return 1;
cinap_lenrek@605 1324
 }
cinap_lenrek@605 1325
 
cinap_lenrek@605 1326
 enum {
cinap_lenrek@605 1327
 	Esbdrv0	= 4,		/* start pos in bits */
cinap_lenrek@605 1328
 	Esbiota	= 3,		/* shift in bits */
cinap_lenrek@605 1329
 	Esbact	= 1,
cinap_lenrek@605 1330
 	Esbloc	= 2,
cinap_lenrek@605 1331
 	Esberr	= 4,
cinap_lenrek@605 1332
 };
cinap_lenrek@605 1333
 
cinap_lenrek@605 1334
 uint
cinap_lenrek@605 1335
 esbbits(uint s)
cinap_lenrek@605 1336
 {
cinap_lenrek@605 1337
 	uint i, e;				/* except after c */
cinap_lenrek@605 1338
 
cinap_lenrek@605 1339
 	e = 0;
cinap_lenrek@605 1340
 	for(i = 0; i < 3; i++)
cinap_lenrek@605 1341
 		e |= ((s>>3*i & 7) != 0)<<i;
cinap_lenrek@605 1342
 	return e;
cinap_lenrek@605 1343
 }
cinap_lenrek@605 1344
 
cinap_lenrek@605 1345
 static int
cinap_lenrek@605 1346
 blinkesb(Ctlr *c, ulong t)
cinap_lenrek@605 1347
 {
cinap_lenrek@605 1348
 	uint i, s, u[32/4];
cinap_lenrek@605 1349
 	uvlong v;
cinap_lenrek@605 1350
 	Drive *d;
cinap_lenrek@605 1351
 
cinap_lenrek@605 1352
 	s = 0;
cinap_lenrek@605 1353
 	for(i = 0; i < c->ndrive; i++){
cinap_lenrek@605 1354
 		d = c->drive[i];
cinap_lenrek@605 1355
 		s |= ledstate(d, t);		/* no port mapping */
cinap_lenrek@605 1356
 	}
cinap_lenrek@605 1357
 	if(s == 0)
cinap_lenrek@605 1358
 		return 0;
cinap_lenrek@605 1359
 	memset(u, 0, sizeof u);
cinap_lenrek@605 1360
 	for(i = 0; i < c->ndrive; i++){
cinap_lenrek@605 1361
 		d = c->drive[i];
cinap_lenrek@605 1362
 		s = Esbdrv0 + Esbiota*i;
cinap_lenrek@605 1363
 		v = esbbits(d->ledbits) * (1ull << s%32);
cinap_lenrek@605 1364
 		u[s/32 + 0] |= v;
cinap_lenrek@605 1365
 		u[s/32 + 1] |= v>>32;
cinap_lenrek@605 1366
 	}
cinap_lenrek@605 1367
 	for(i = 0; i < c->encsz; i++)
cinap_lenrek@605 1368
 		c->enctx[i] = u[i];
cinap_lenrek@605 1369
 	return 1;
cinap_lenrek@605 1370
 }
cinap_lenrek@605 1371
 
cinap_lenrek@605 1372
 static long
cinap_lenrek@605 1373
 ahciledr(SDunit *u, Chan *ch, void *a, long n, vlong off)
cinap_lenrek@605 1374
 {
cinap_lenrek@605 1375
 	Ctlr *c;
cinap_lenrek@605 1376
 	Drive *d;
cinap_lenrek@605 1377
 
cinap_lenrek@605 1378
 	c = u->dev->ctlr;
cinap_lenrek@605 1379
 	d = c->drive[u->subno];
cinap_lenrek@605 1380
 	return ledr(d, ch, a, n, off);
cinap_lenrek@605 1381
 }
cinap_lenrek@605 1382
 
cinap_lenrek@605 1383
 static long
cinap_lenrek@605 1384
 ahciledw(SDunit *u, Chan *ch, void *a, long n, vlong off)
cinap_lenrek@605 1385
 {
cinap_lenrek@605 1386
 	Ctlr *c;
cinap_lenrek@605 1387
 	Drive *d;
cinap_lenrek@605 1388
 
cinap_lenrek@605 1389
 	c = u->dev->ctlr;
cinap_lenrek@605 1390
 	d = c->drive[u->subno];
cinap_lenrek@605 1391
 	return ledw(d, ch, a, n, off);
cinap_lenrek@605 1392
 }
cinap_lenrek@605 1393
 
cinap_lenrek@605 1394
 static void
cinap_lenrek@605 1395
 ledkproc(void*)
cinap_lenrek@605 1396
 {
cinap_lenrek@605 1397
 	uchar map[NCtlr];
cinap_lenrek@605 1398
 	uint i, j, t0, t1;
cinap_lenrek@605 1399
 	Ctlr *c;
cinap_lenrek@605 1400
 	Drive *d;
cinap_lenrek@605 1401
 
cinap_lenrek@605 1402
 	j = 0;
cinap_lenrek@605 1403
 	memset(map, 0, sizeof map);
cinap_lenrek@605 1404
 	for(i = 0; i < niactlr; i++)
cinap_lenrek@605 1405
 		if(iactlr[i].enctype != 0){
cinap_lenrek@605 1406
 			ahciencreset(iactlr + i);
cinap_lenrek@605 1407
 			map[i] = 1;
cinap_lenrek@605 1408
 			j++;
cinap_lenrek@605 1409
 		}
cinap_lenrek@605 1410
 	if(j == 0)
cinap_lenrek@605 1411
 		pexit("no work", 1);
cinap_lenrek@605 1412
 	for(i = 0; i < niadrive; i++){
cinap_lenrek@605 1413
 		iadrive[i]->nled = 3;		/* hardcoded */
cinap_lenrek@605 1414
 		if(iadrive[i]->ctlr->enctype == Eesb)
cinap_lenrek@605 1415
 			iadrive[i]->nled = 3;
cinap_lenrek@605 1416
 		iadrive[i]->ledbits = -1;
cinap_lenrek@605 1417
 	}
cinap_lenrek@605 1418
 	for(i = 0; ; i++){
cinap_lenrek@605 1419
 		t0 = Ticks;
cinap_lenrek@605 1420
 		for(j = 0; j < niadrive; ){
cinap_lenrek@605 1421
 			c = iadrive[j]->ctlr;
cinap_lenrek@605 1422
 			if(map[j] == 0)
cinap_lenrek@605 1423
 				j += c->enctype;
cinap_lenrek@605 1424
 			else if(c->enctype == Eesb){
cinap_lenrek@605 1425
 				blinkesb(c, i);
cinap_lenrek@605 1426
 				j += c->ndrive;
cinap_lenrek@605 1427
 			}else{
cinap_lenrek@605 1428
 				d = iadrive[j++];
cinap_lenrek@605 1429
 				blink(d, i);
cinap_lenrek@605 1430
 			}
cinap_lenrek@605 1431
 		}
cinap_lenrek@605 1432
 		t1 = Ticks;
cinap_lenrek@605 1433
 		esleep(Ledsleep - TK2MS(t1 - t0));
cinap_lenrek@605 1434
 	}
cinap_lenrek@605 1435
 }
cinap_lenrek@605 1436
 
cinap_lenrek@605 1437
 static int
cinap_lenrek@2223 1438
 waitready(Drive *d)
cinap_lenrek@2223 1439
 {
cinap_lenrek@2223 1440
 	ulong s, i, δ;
cinap_lenrek@2223 1441
 
cinap_lenrek@2230 1442
 	for(i = 0;; i += 250){
cinap_lenrek@2223 1443
 		if(d->state == Dreset || d->state == Dportreset || d->state == Dnew)
cinap_lenrek@2223 1444
 			return 1;
cinap_lenrek@2230 1445
 		ilock(d);
cinap_lenrek@2230 1446
 		s = d->port->sstatus;
cinap_lenrek@2230 1447
 		if(d->state == Dready && (s & Smask) == Sphylink){
cinap_lenrek@2230 1448
 			iunlock(d);
cinap_lenrek@2230 1449
 			return 0;
cinap_lenrek@2230 1450
 		}
cinap_lenrek@2223 1451
 		δ = Ticks - d->lastseen;
cinap_lenrek@2223 1452
 		if(d->state == Dnull || δ > 10*1000)
cinap_lenrek@2230 1453
 			break;
cinap_lenrek@2223 1454
 		if((s & Imask) == 0 && δ > 1500)
cinap_lenrek@2230 1455
 			break;
cinap_lenrek@2230 1456
 		if(i >= 15*1000){
cinap_lenrek@2230 1457
 			d->state = Doffline;
cinap_lenrek@2230 1458
 			iunlock(d);
cinap_lenrek@2230 1459
 			print("%s: not responding; offline\n", dnam(d));
cinap_lenrek@2223 1460
 			return -1;
cinap_lenrek@2230 1461
 		}
cinap_lenrek@2230 1462
 		iunlock(d);
cinap_lenrek@2223 1463
 		esleep(250);
cinap_lenrek@2223 1464
 	}
cinap_lenrek@2230 1465
 	iunlock(d);
cinap_lenrek@2223 1466
 	return -1;
cinap_lenrek@2223 1467
 }
cinap_lenrek@2223 1468
 
cinap_lenrek@2223 1469
 static int
taruti@0 1470
 iaverify(SDunit *u)
taruti@0 1471
 {
taruti@0 1472
 	Ctlr *c;
taruti@0 1473
 	Drive *d;
taruti@0 1474
 
taruti@0 1475
 	c = u->dev->ctlr;
taruti@0 1476
 	d = c->drive[u->subno];
taruti@0 1477
 	ilock(c);
taruti@0 1478
 	ilock(d);
cinap_lenrek@605 1479
 	if(d->unit == nil){
cinap_lenrek@605 1480
 		d->unit = u;
cinap_lenrek@605 1481
 		if(c->enctype != 0)
cinap_lenrek@605 1482
 			sdaddfile(u, "led", 0644, eve, ahciledr, ahciledw);
cinap_lenrek@605 1483
 	}
taruti@0 1484
 	iunlock(d);
taruti@0 1485
 	iunlock(c);
taruti@0 1486
 	checkdrive(d, d->driveno);		/* c->d0 + d->driveno */
taruti@0 1487
 	return 1;
taruti@0 1488
 }
taruti@0 1489
 
taruti@0 1490
 static int
cinap_lenrek@2223 1491
 iaonline(SDunit *u)
cinap_lenrek@2223 1492
 {
cinap_lenrek@2223 1493
 	int r;
cinap_lenrek@2223 1494
 	Ctlr *c;
cinap_lenrek@2223 1495
 	Drive *d;
cinap_lenrek@2223 1496
 
cinap_lenrek@2223 1497
 	c = u->dev->ctlr;
cinap_lenrek@2223 1498
 	d = c->drive[u->subno];
cinap_lenrek@2223 1499
 
cinap_lenrek@2423 1500
 	while(d->state != Dmissing && waitready(d) == 1)
cinap_lenrek@2223 1501
 		esleep(1);
cinap_lenrek@2223 1502
 
cinap_lenrek@2224 1503
 	dprint("%s: iaonline: %s\n", dnam(d), diskstates[d->state]);
cinap_lenrek@2230 1504
 
cinap_lenrek@2223 1505
 	ilock(d);
cinap_lenrek@2223 1506
 	if(d->portm.feat & Datapi){
cinap_lenrek@2230 1507
 		r = d->drivechange;
cinap_lenrek@2223 1508
 		d->drivechange = 0;
cinap_lenrek@2223 1509
 		iunlock(d);
cinap_lenrek@2230 1510
 		if(r != 0)
cinap_lenrek@2230 1511
 			scsiverify(u);
cinap_lenrek@2223 1512
 		return scsionline(u);
cinap_lenrek@2223 1513
 	}
cinap_lenrek@2223 1514
 	r = 0;
cinap_lenrek@2223 1515
 	if(d->drivechange){
cinap_lenrek@2223 1516
 		d->drivechange = 0;
cinap_lenrek@2223 1517
 		r = 2;
cinap_lenrek@2223 1518
 	}else if(d->state == Dready)
cinap_lenrek@2223 1519
 		r = 1;
cinap_lenrek@2223 1520
 	if(r){
cinap_lenrek@2223 1521
 		u->sectors = d->sectors;
cinap_lenrek@2223 1522
 		u->secsize = d->secsize;
cinap_lenrek@2223 1523
 	}
cinap_lenrek@2223 1524
 	iunlock(d);
cinap_lenrek@2223 1525
 
cinap_lenrek@2223 1526
 	return r;
cinap_lenrek@2223 1527
 }
cinap_lenrek@2223 1528
 
cinap_lenrek@2223 1529
 static int
taruti@0 1530
 iaenable(SDev *s)
taruti@0 1531
 {
taruti@0 1532
 	char name[32];
taruti@0 1533
 	Ctlr *c;
taruti@0 1534
 	static int once;
taruti@0 1535
 
taruti@0 1536
 	c = s->ctlr;
taruti@0 1537
 	ilock(c);
cinap_lenrek@605 1538
 	if(!c->enabled){
cinap_lenrek@605 1539
 		if(once == 0)
taruti@0 1540
 			kproc("iasata", satakproc, 0);
taruti@0 1541
 		if(c->ndrive == 0)
taruti@0 1542
 			panic("iaenable: zero s->ctlr->ndrive");
taruti@0 1543
 		pcisetbme(c->pci);
taruti@0 1544
 		snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
taruti@0 1545
 		intrenable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
taruti@0 1546
 		/* supposed to squelch leftover interrupts here. */
taruti@0 1547
 		ahcienable(c->hba);
taruti@0 1548
 		c->enabled = 1;
cinap_lenrek@605 1549
 		if(++once == niactlr)
cinap_lenrek@605 1550
 			kproc("ialed", ledkproc, 0);
taruti@0 1551
 	}
taruti@0 1552
 	iunlock(c);
taruti@0 1553
 	return 1;
taruti@0 1554
 }
taruti@0 1555
 
taruti@0 1556
 static int
taruti@0 1557
 iadisable(SDev *s)
taruti@0 1558
 {
taruti@0 1559
 	char name[32];
taruti@0 1560
 	Ctlr *c;
taruti@0 1561
 
taruti@0 1562
 	c = s->ctlr;
taruti@0 1563
 	ilock(c);
taruti@0 1564
 	ahcidisable(c->hba);
taruti@0 1565
 	snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
taruti@0 1566
 	intrdisable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
taruti@0 1567
 	c->enabled = 0;
taruti@0 1568
 	iunlock(c);
taruti@0 1569
 	return 1;
taruti@0 1570
 }
taruti@0 1571
 
taruti@0 1572
 static Alist*
cinap_lenrek@3231 1573
 ahcibuild(Drive *d, int rw, void *data, int nsect, vlong lba)
taruti@0 1574
 {
cinap_lenrek@605 1575
 	uchar *c;
cinap_lenrek@605 1576
 	uint flags;
cinap_lenrek@2230 1577
 	Aportm *m;
taruti@0 1578
 
cinap_lenrek@2230 1579
 	m = &d->portm;
cinap_lenrek@605 1580
 	c = m->ctab->cfis;
cinap_lenrek@3231 1581
 	rwfis(m, c, rw, nsect, lba);
cinap_lenrek@605 1582
 	flags = Lpref;
cinap_lenrek@605 1583
 	if(rw == SDwrite)
cinap_lenrek@605 1584
 		flags |= Lwrite;
cinap_lenrek@3231 1585
 	return mkalist(m, flags, data, nsect * d->secsize);
taruti@0 1586
 }
taruti@0 1587
 
taruti@0 1588
 static Alist*
cinap_lenrek@2230 1589
 ahcibuildpkt(Drive *d, SDreq *r, void *data, int n)
cinap_lenrek@2230 1590
 {
cinap_lenrek@2230 1591
 	uint flags;
cinap_lenrek@2230 1592
 	Aportm *m;
cinap_lenrek@2230 1593
 	uchar *c;
cinap_lenrek@2230 1594
 	Actab *t;
cinap_lenrek@2230 1595
 
cinap_lenrek@2230 1596
 	m = &d->portm;
cinap_lenrek@2230 1597
 	t = m->ctab;
cinap_lenrek@2230 1598
 	c = t->cfis;
cinap_lenrek@2230 1599
 
cinap_lenrek@2230 1600
 	atapirwfis(m, c, r->cmd, r->clen, 0x2000);
cinap_lenrek@2230 1601
 	if((n & 15) != 0 || d->nodma)
cinap_lenrek@2230 1602
 		c[Ffeat] &= ~1;	/* use pio */
cinap_lenrek@2230 1603
 	else if(c[Ffeat] & 1 && d->info[62] & (1<<15))	/* dma direction */
cinap_lenrek@2230 1604
 		c[Ffeat] = (c[Ffeat] & ~(1<<2)) | ((r->write == 0) << 2);
cinap_lenrek@2230 1605
 	flags = Lpref | Latapi;
cinap_lenrek@2230 1606
 	if(r->write != 0 && data)
cinap_lenrek@2230 1607
 		flags |= Lwrite;
cinap_lenrek@2230 1608
 	return mkalist(m, flags, data, n);
cinap_lenrek@2230 1609
 }
cinap_lenrek@2230 1610
 
cinap_lenrek@2230 1611
 static Alist*
cinap_lenrek@2230 1612
 ahcibuildfis(Drive *d, SDreq *r, void *data, uint n)
taruti@0 1613
 {
cinap_lenrek@605 1614
 	uint flags;
taruti@0 1615
 	uchar *c;
cinap_lenrek@2230 1616
 	Aportm *m;
taruti@0 1617
 
cinap_lenrek@2230 1618
 	if((r->ataproto & Pprotom) == Ppkt)
cinap_lenrek@2230 1619
 		return ahcibuildpkt(d, r, data, n);
taruti@0 1620
 
cinap_lenrek@2230 1621
 	m = &d->portm;
cinap_lenrek@605 1622
 	c = m->ctab->cfis;
cinap_lenrek@2230 1623
 	memmove(c, r->cmd, r->clen);
cinap_lenrek@2230 1624
 	flags = Lpref;
cinap_lenrek@2230 1625
 	if(r->write || n == 0)
cinap_lenrek@2230 1626
 		flags |= Lwrite;
cinap_lenrek@2230 1627
 	return mkalist(m, flags, data, n);
taruti@0 1628
 }
taruti@0 1629
 
taruti@0 1630
 static int
taruti@0 1631
 lockready(Drive *d)
taruti@0 1632
 {
taruti@0 1633
 	int i;
taruti@0 1634
 
taruti@0 1635
 	qlock(&d->portm);
taruti@0 1636
 	while ((i = waitready(d)) == 1) {
taruti@0 1637
 		qunlock(&d->portm);
taruti@0 1638
 		esleep(1);
taruti@0 1639
 		qlock(&d->portm);
taruti@0 1640
 	}
taruti@0 1641
 	return i;
taruti@0 1642
 }
taruti@0 1643
 
taruti@0 1644
 static int
taruti@0 1645
 flushcache(Drive *d)
taruti@0 1646
 {
taruti@0 1647
 	int i;
taruti@0 1648
 
taruti@0 1649
 	i = -1;
taruti@0 1650
 	if(lockready(d) == 0)
taruti@0 1651
 		i = ahciflushcache(&d->portc);
taruti@0 1652
 	qunlock(&d->portm);
taruti@0 1653
 	return i;
taruti@0 1654
 }
taruti@0 1655
 
taruti@0 1656
 static int
cinap_lenrek@605 1657
 io(Drive *d, uint proto, int to, int interrupt)
taruti@0 1658
 {
cinap_lenrek@2230 1659
 	uint task, flag, stat, rv;
taruti@0 1660
 	Aport *p;
taruti@0 1661
 	Asleep as;
taruti@0 1662
 
taruti@0 1663
 	switch(waitready(d)){
taruti@0 1664
 	case -1:
taruti@0 1665
 		return SDeio;
taruti@0 1666
 	case 1:
cinap_lenrek@605 1667
 		return SDretry;
taruti@0 1668
 	}
taruti@0 1669
 
taruti@0 1670
 	ilock(d);
taruti@0 1671
 	d->portm.flag = 0;
taruti@0 1672
 	iunlock(d);
cinap_lenrek@605 1673
 	p = d->port;
taruti@0 1674
 	p->ci = 1;
taruti@0 1675
 
taruti@0 1676
 	as.p = p;
taruti@0 1677
 	as.i = 1;
cinap_lenrek@605 1678
 	d->totick = 0;
cinap_lenrek@605 1679
 	if(to > 0)
cinap_lenrek@605 1680
 		d->totick = Ticks + MS2TK(to) | 1;	/* fix fencepost */
taruti@0 1681
 	d->active++;
taruti@0 1682
 
taruti@0 1683
 	while(waserror())
cinap_lenrek@605 1684
 		if(interrupt){
cinap_lenrek@605 1685
 			d->active--;
cinap_lenrek@605 1686
 			d->port->ci = 0;
cinap_lenrek@2223 1687
 			if(ahcicomreset(&d->portc) == -1)
cinap_lenrek@2223 1688
 				dstatus(d, Dreset);
cinap_lenrek@605 1689
 			return SDtimeout;
cinap_lenrek@605 1690
 		}
cinap_lenrek@2223 1691
 	
taruti@0 1692
 	sleep(&d->portm, ahciclear, &as);
taruti@0 1693
 	poperror();
taruti@0 1694
 
taruti@0 1695
 	d->active--;
taruti@0 1696
 	ilock(d);
cinap_lenrek@2230 1697
 	stat = d->state;
taruti@0 1698
 	flag = d->portm.flag;
taruti@0 1699
 	task = d->port->task;
taruti@0 1700
 	iunlock(d);
taruti@0 1701
 
cinap_lenrek@605 1702
 	rv = SDok;
cinap_lenrek@2230 1703
 	if(proto & Ppkt && stat == Dready){
cinap_lenrek@605 1704
 		rv = task >> 8 + 4 & 0xf;
cinap_lenrek@605 1705
 		flag &= ~Fahdrs;
cinap_lenrek@605 1706
 		flag |= Fdone;
cinap_lenrek@2230 1707
 	}else if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && stat == Dready){
taruti@0 1708
 		d->port->ci = 0;
taruti@0 1709
 		ahcirecover(&d->portc);
taruti@0 1710
 		task = d->port->task;
taruti@0 1711
 		flag &= ~Fdone;		/* either an error or do-over */
taruti@0 1712
 	}
taruti@0 1713
 	if(flag == 0){
cinap_lenrek@605 1714
 		print("%s: retry\n", dnam(d));
cinap_lenrek@605 1715
 		return SDretry;
taruti@0 1716
 	}
cinap_lenrek@605 1717
 	if(flag & (Fahdrs | Ferror)){
cinap_lenrek@605 1718
 		if((task & Eidnf) == 0)
cinap_lenrek@605 1719
 			print("%s: i/o error %ux\n", dnam(d), task);
taruti@0 1720
 		return SDcheck;
taruti@0 1721
 	}
cinap_lenrek@605 1722
 	return rv;
cinap_lenrek@605 1723
 }
taruti@0 1724
 
cinap_lenrek@605 1725
 static int
cinap_lenrek@605 1726
 iariopkt(SDreq *r, Drive *d)
cinap_lenrek@605 1727
 {
cinap_lenrek@2230 1728
 	int try, to;
cinap_lenrek@605 1729
 	uchar *cmd;
cinap_lenrek@2230 1730
 	Alist *l;
cinap_lenrek@605 1731
 
cinap_lenrek@605 1732
 	cmd = r->cmd;
cinap_lenrek@605 1733
 	aprint("%s: %.2ux %.2ux %c %d %p\n", dnam(d), cmd[0], cmd[2],
cinap_lenrek@605 1734
 		"rw"[r->write], r->dlen, r->data);
cinap_lenrek@2230 1735
 
cinap_lenrek@605 1736
 	r->rlen = 0;
cinap_lenrek@605 1737
 
cinap_lenrek@2224 1738
 	/*
cinap_lenrek@2230 1739
 	 * prevent iaonline() to hang forever by timing out
cinap_lenrek@2230 1740
 	 * inquiry and capacity commands after 5 seconds.
cinap_lenrek@2224 1741
 	 */
cinap_lenrek@2230 1742
 	to = 30*1000;
cinap_lenrek@2224 1743
 	switch(cmd[0]){
cinap_lenrek@2224 1744
 	case 0x9e: if(cmd[1] != 0x10) break;
cinap_lenrek@2224 1745
 	case 0x25:
cinap_lenrek@2224 1746
 	case 0x12:
cinap_lenrek@2224 1747
 		to = 5*1000;
cinap_lenrek@2224 1748
 		break;
cinap_lenrek@2224 1749
 	}
cinap_lenrek@2224 1750
 
cinap_lenrek@605 1751
 	for(try = 0; try < 10; try++){
cinap_lenrek@605 1752
 		qlock(&d->portm);
cinap_lenrek@2230 1753
 		l = ahcibuildpkt(d, r, r->data, r->dlen);
cinap_lenrek@2224 1754
 		r->status = io(d, Ppkt, to, 0);
cinap_lenrek@605 1755
 		switch(r->status){
cinap_lenrek@605 1756
 		case SDeio:
cinap_lenrek@2230 1757
 			qunlock(&d->portm);
cinap_lenrek@605 1758
 			return SDeio;
cinap_lenrek@605 1759
 		case SDretry:
cinap_lenrek@2230 1760
 			qunlock(&d->portm);
cinap_lenrek@605 1761
 			continue;
cinap_lenrek@605 1762
 		}
cinap_lenrek@2230 1763
 		r->rlen = l->len;
cinap_lenrek@2230 1764
 		qunlock(&d->portm);
cinap_lenrek@605 1765
 		return SDok;
cinap_lenrek@605 1766
 	}
cinap_lenrek@605 1767
 	print("%s: bad disk\n", dnam(d));
cinap_lenrek@605 1768
 	return r->status = SDcheck;
cinap_lenrek@605 1769
 }
taruti@0 1770
 
cinap_lenrek@605 1771
 static long
cinap_lenrek@605 1772
 ahcibio(SDunit *u, int lun, int write, void *a, long count, uvlong lba)
cinap_lenrek@605 1773
 {
cinap_lenrek@605 1774
 	int n, rw, try, status, max;
cinap_lenrek@605 1775
 	uchar *data;
cinap_lenrek@605 1776
 	Ctlr *c;
cinap_lenrek@605 1777
 	Drive *d;
cinap_lenrek@605 1778
 
cinap_lenrek@605 1779
 	c = u->dev->ctlr;
cinap_lenrek@605 1780
 	d = c->drive[u->subno];
cinap_lenrek@605 1781
 	if(d->portm.feat & Datapi)
cinap_lenrek@605 1782
 		return scsibio(u, lun, write, a, count, lba);
cinap_lenrek@605 1783
 
cinap_lenrek@605 1784
 	max = 128;
cinap_lenrek@605 1785
 	if(d->portm.feat & Dllba){
cinap_lenrek@605 1786
 		max = 8192;		/* ahci maximum */
cinap_lenrek@605 1787
 		if(c->type == Tsb600)
cinap_lenrek@605 1788
 			max = 255;	/* errata */
cinap_lenrek@605 1789
 	}
cinap_lenrek@605 1790
 	rw = write? SDwrite: SDread;
cinap_lenrek@605 1791
 	data = a;
cinap_lenrek@2230 1792
 	dprint("%s: bio: %llud %c %lud %p\n",
cinap_lenrek@2230 1793
 		dnam(d), lba, "rw"[rw], count, data);
cinap_lenrek@2230 1794
 
cinap_lenrek@605 1795
 	for(try = 0; try < 10;){
cinap_lenrek@605 1796
 		n = count;
cinap_lenrek@605 1797
 		if(n > max)
cinap_lenrek@605 1798
 			n = max;
cinap_lenrek@605 1799
 		qlock(&d->portm);
cinap_lenrek@2230 1800
 		ahcibuild(d, rw, data, n, lba);
cinap_lenrek@605 1801
 		status = io(d, Pdma, 5000, 0);
cinap_lenrek@605 1802
 		qunlock(&d->portm);
cinap_lenrek@605 1803
 		switch(status){
cinap_lenrek@605 1804
 		case SDeio:
cinap_lenrek@605 1805
 			return -1;
cinap_lenrek@605 1806
 		case SDretry:
cinap_lenrek@605 1807
 			try++;
cinap_lenrek@605 1808
 			continue;
cinap_lenrek@605 1809
 		}
cinap_lenrek@605 1810
 		try = 0;
cinap_lenrek@605 1811
 		count -= n;
cinap_lenrek@3231 1812
 		lba += n;
cinap_lenrek@605 1813
 		data += n * u->secsize;
cinap_lenrek@605 1814
 		if(count == 0)
cinap_lenrek@605 1815
 			return data - (uchar*)a;
cinap_lenrek@605 1816
 	}
cinap_lenrek@605 1817
 	print("%s: bad disk\n", dnam(d));
cinap_lenrek@605 1818
 	return -1;
taruti@0 1819
 }
taruti@0 1820
 
taruti@0 1821
 static int
taruti@0 1822
 iario(SDreq *r)
taruti@0 1823
 {
cinap_lenrek@605 1824
 	int i, n, count, rw;
cinap_lenrek@605 1825
 	uchar *cmd;
cinap_lenrek@605 1826
 	uvlong lba;
taruti@0 1827
 	Ctlr *c;
taruti@0 1828
 	Drive *d;
cinap_lenrek@2144 1829
 	SDunit *u;
taruti@0 1830
 
cinap_lenrek@2144 1831
 	u = r->unit;
cinap_lenrek@2144 1832
 	c = u->dev->ctlr;
cinap_lenrek@2144 1833
 	d = c->drive[u->subno];
taruti@0 1834
 	if(d->portm.feat & Datapi)
taruti@0 1835
 		return iariopkt(r, d);
taruti@0 1836
 	cmd = r->cmd;
taruti@0 1837
 
cinap_lenrek@605 1838
 	if(cmd[0] == 0x35 || cmd[0] == 0x91){
taruti@0 1839
 		if(flushcache(d) == 0)
taruti@0 1840
 			return sdsetsense(r, SDok, 0, 0, 0);
taruti@0 1841
 		return sdsetsense(r, SDcheck, 3, 0xc, 2);
taruti@0 1842
 	}
taruti@0 1843
 
cinap_lenrek@605 1844
 	if((i = sdfakescsi(r)) != SDnostatus){
taruti@0 1845
 		r->status = i;
taruti@0 1846
 		return i;
taruti@0 1847
 	}
taruti@0 1848
 
cinap_lenrek@605 1849
 	if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
cinap_lenrek@605 1850
 		return i;
cinap_lenrek@2144 1851
 	n = ahcibio(u, r->lun, r->write, r->data, count, lba);
cinap_lenrek@605 1852
 	if(n == -1)
cinap_lenrek@605 1853
 		return SDeio;
cinap_lenrek@605 1854
 	r->rlen = n;
cinap_lenrek@605 1855
 	return SDok;
cinap_lenrek@605 1856
 }
taruti@0 1857
 
cinap_lenrek@605 1858
 static uchar bogusrfis[16] = {
cinap_lenrek@605 1859
 [Ftype]		0x34,
cinap_lenrek@605 1860
 [Fioport]	0x40,
cinap_lenrek@2223 1861
 [Fstatus]	0x50,
cinap_lenrek@605 1862
 [Fdev]		0xa0,
cinap_lenrek@605 1863
 };
cinap_lenrek@605 1864
 
cinap_lenrek@605 1865
 static void
cinap_lenrek@605 1866
 sdr0(Drive *d)
cinap_lenrek@605 1867
 {
cinap_lenrek@605 1868
 	uchar *c;
taruti@0 1869
 
cinap_lenrek@605 1870
 	c = d->portm.fis.r;
cinap_lenrek@605 1871
 	memmove(c, bogusrfis, sizeof bogusrfis);
cinap_lenrek@605 1872
 	coherence();
cinap_lenrek@605 1873
 }
cinap_lenrek@605 1874
 
cinap_lenrek@605 1875
 static int
cinap_lenrek@605 1876
 sdr(SDreq *r, Drive *d, int st)
cinap_lenrek@605 1877
 {
cinap_lenrek@605 1878
 	uchar *c;
cinap_lenrek@605 1879
 	uint t;
taruti@0 1880
 
cinap_lenrek@605 1881
 	if((r->ataproto & Pprotom) == Ppkt){
cinap_lenrek@605 1882
 		t = d->port->task;
cinap_lenrek@605 1883
 		if(t & ASerr)
cinap_lenrek@605 1884
 			st = t >> 8 + 4 & 0xf;
cinap_lenrek@605 1885
 	}
cinap_lenrek@605 1886
 	c = d->portm.fis.r;
cinap_lenrek@605 1887
 	memmove(r->cmd, c, 16);
cinap_lenrek@605 1888
 	r->status = st;
cinap_lenrek@605 1889
 	if(st == SDcheck)
cinap_lenrek@605 1890
 		st = SDok;
cinap_lenrek@605 1891
 	return st;
cinap_lenrek@605 1892
 }
taruti@0 1893
 
cinap_lenrek@605 1894
 static int
cinap_lenrek@605 1895
 fisreqchk(Sfis *f, SDreq *r)
cinap_lenrek@605 1896
 {
cinap_lenrek@605 1897
 	if((r->ataproto & Pprotom) == Ppkt)
cinap_lenrek@605 1898
 		return SDnostatus;
cinap_lenrek@605 1899
 	/*
cinap_lenrek@605 1900
 	 * handle oob requests;
cinap_lenrek@605 1901
 	 *    restrict & sanitize commands
cinap_lenrek@605 1902
 	 */
cinap_lenrek@605 1903
 	if(r->clen != 16)
cinap_lenrek@605 1904
 		error(Eio);
cinap_lenrek@605 1905
 	if(r->cmd[0] == 0xf0){
cinap_lenrek@605 1906
 		sigtofis(f, r->cmd);
cinap_lenrek@605 1907
 		r->status = SDok;
cinap_lenrek@605 1908
 		return SDok;
cinap_lenrek@605 1909
 	}
cinap_lenrek@605 1910
 	r->cmd[0] = 0x27;
cinap_lenrek@605 1911
 	r->cmd[1] = 0x80;
cinap_lenrek@605 1912
 	r->cmd[7] |= 0xa0;
cinap_lenrek@605 1913
 	return SDnostatus;
cinap_lenrek@605 1914
 }
taruti@0 1915
 
cinap_lenrek@605 1916
 static int
cinap_lenrek@605 1917
 iaataio(SDreq *r)
cinap_lenrek@605 1918
 {
cinap_lenrek@605 1919
 	int try;
cinap_lenrek@605 1920
 	Ctlr *c;
cinap_lenrek@605 1921
 	Drive *d;
cinap_lenrek@605 1922
 	SDunit *u;
cinap_lenrek@2230 1923
 	Alist *l;
taruti@0 1924
 
cinap_lenrek@605 1925
 	u = r->unit;
cinap_lenrek@605 1926
 	c = u->dev->ctlr;
cinap_lenrek@605 1927
 	d = c->drive[u->subno];
cinap_lenrek@605 1928
 
cinap_lenrek@605 1929
 	if((r->status = fisreqchk(&d->portm, r)) != SDnostatus)
cinap_lenrek@605 1930
 		return r->status;
cinap_lenrek@605 1931
 	r->rlen = 0;
cinap_lenrek@605 1932
 	sdr0(d);
cinap_lenrek@605 1933
 	for(try = 0; try < 10; try++){
cinap_lenrek@605 1934
 		qlock(&d->portm);
cinap_lenrek@2230 1935
 		l = ahcibuildfis(d, r, r->data, r->dlen);
cinap_lenrek@605 1936
 		r->status = io(d, r->ataproto & Pprotom, -1, 1);
cinap_lenrek@605 1937
 		switch(r->status){
cinap_lenrek@605 1938
 		case SDtimeout:
cinap_lenrek@2230 1939
 			qunlock(&d->portm);
cinap_lenrek@605 1940
 			return sdsetsense(r, SDcheck, 11, 0, 6);
cinap_lenrek@605 1941
 		case SDeio:
cinap_lenrek@2230 1942
 			qunlock(&d->portm);
cinap_lenrek@605 1943
 			return SDeio;
cinap_lenrek@605 1944
 		case SDretry:
cinap_lenrek@2230 1945
 			qunlock(&d->portm);
cinap_lenrek@605 1946
 			continue;
taruti@0 1947
 		}
cinap_lenrek@2230 1948
 		r->rlen = (r->ataproto & Pprotom) == Ppkt ? l->len : r->dlen;
cinap_lenrek@2230 1949
 		try = sdr(r, d, r->status);
cinap_lenrek@2230 1950
 		qunlock(&d->portm);
cinap_lenrek@2230 1951
 		return try;
taruti@0 1952
 	}
cinap_lenrek@605 1953
 	print("%s: bad disk\n", dnam(d));
cinap_lenrek@2223 1954
 	return r->status = SDeio;
taruti@0 1955
 }
taruti@0 1956
 
cinap_lenrek@605 1957
 enum{
cinap_lenrek@605 1958
 	Ghc	= 0x04/4,	/* global host control */
cinap_lenrek@605 1959
 	Pi	= 0x0c/4,	/* ports implemented */
cinap_lenrek@605 1960
 	Cmddec	= 1<<15,	/* enable command block decode */
cinap_lenrek@605 1961
 
cinap_lenrek@605 1962
 	/* Ghc bits */
cinap_lenrek@605 1963
 	Ahcien	= 1<<31,	/* ahci enable */
cinap_lenrek@605 1964
 };
cinap_lenrek@605 1965
 
taruti@0 1966
 static void
taruti@0 1967
 iasetupahci(Ctlr *c)
taruti@0 1968
 {
cinap_lenrek@6149 1969
 	if(c->type != Tich)
cinap_lenrek@6149 1970
 		return;
cinap_lenrek@6149 1971
 
cinap_lenrek@605 1972
 	pcicfgw16(c->pci, 0x40, pcicfgr16(c->pci, 0x40) & ~Cmddec);
cinap_lenrek@605 1973
 	pcicfgw16(c->pci, 0x42, pcicfgr16(c->pci, 0x42) & ~Cmddec);
taruti@0 1974
 
cinap_lenrek@605 1975
 	c->lmmio[Ghc] |= Ahcien;
cinap_lenrek@6149 1976
 	c->lmmio[Pi] = (1 << 6) - 1;	/* 6 ports (supposedly ro pi reg) */
taruti@0 1977
 
taruti@0 1978
 	/* enable ahci mode; from ich9 datasheet */
taruti@0 1979
 	pcicfgw16(c->pci, 0x90, 1<<6 | 1<<5);
cinap_lenrek@6149 1980
 
cinap_lenrek@6149 1981
 	/* configure drives 0-5 as ahci sata  (c.f. errata) */
cinap_lenrek@6149 1982
 	pcicfgw16(c->pci, 0x92, pcicfgr16(c->pci, 0x92) | 0xf);
taruti@0 1983
 }
taruti@0 1984
 
cinap_lenrek@605 1985
 static void
cinap_lenrek@605 1986
 sbsetupahci(Pcidev *p)
cinap_lenrek@605 1987
 {
cinap_lenrek@605 1988
 	print("sbsetupahci: tweaking %.4ux ccru %.2ux ccrp %.2ux\n",
cinap_lenrek@605 1989
 		p->did, p->ccru, p->ccrp);
cinap_lenrek@605 1990
 	pcicfgw8(p, 0x40, pcicfgr8(p, 0x40) | 1);
cinap_lenrek@605 1991
 	pcicfgw8(p, PciCCRu, 6);
cinap_lenrek@605 1992
 	pcicfgw8(p, PciCCRp, 1);
cinap_lenrek@605 1993
 	p->ccru = 6;
cinap_lenrek@605 1994
 	p->ccrp = 1;
cinap_lenrek@605 1995
 }
cinap_lenrek@605 1996
 
cinap_lenrek@605 1997
 static int
cinap_lenrek@605 1998
 esbenc(Ctlr *c)
cinap_lenrek@605 1999
 {
cinap_lenrek@605 2000
 	c->encsz = 1;
cinap_lenrek@605 2001
 	c->enctx = (ulong*)(c->mmio + 0xa0);
cinap_lenrek@605 2002
 	c->enctype = Eesb;
cinap_lenrek@605 2003
 	c->enctx[0] = 0;
cinap_lenrek@605 2004
 	return 0;
cinap_lenrek@605 2005
 }
cinap_lenrek@605 2006
 
cinap_lenrek@605 2007
 static int
cinap_lenrek@605 2008
 ahciencinit(Ctlr *c)
cinap_lenrek@605 2009
 {
cinap_lenrek@605 2010
 	ulong type, sz, o, *bar;
cinap_lenrek@605 2011
 	Ahba *h;
cinap_lenrek@605 2012
 
cinap_lenrek@605 2013
 	h = c->hba;
cinap_lenrek@605 2014
 	if(c->type == Tesb)
cinap_lenrek@605 2015
 		return esbenc(c);
cinap_lenrek@605 2016
 	if((h->cap & Hems) == 0)
cinap_lenrek@605 2017
 		return -1;
cinap_lenrek@605 2018
 	type = h->emctl & Emtype;
cinap_lenrek@605 2019
 	switch(type){
cinap_lenrek@605 2020
 	case Esgpio:
cinap_lenrek@605 2021
 	case Eses2:
cinap_lenrek@605 2022
 	case Esafte:
cinap_lenrek@605 2023
 		return -1;
cinap_lenrek@605 2024
 	case Elmt:
cinap_lenrek@605 2025
 		break;
cinap_lenrek@605 2026
 	default:
cinap_lenrek@605 2027
 		return -1;
cinap_lenrek@605 2028
 	}
cinap_lenrek@605 2029
 
cinap_lenrek@605 2030
 	sz = h->emloc & 0xffff;
cinap_lenrek@605 2031
 	o = h->emloc>>16;
cinap_lenrek@605 2032
 	if(sz == 0 || o == 0)
cinap_lenrek@605 2033
 		return -1;
cinap_lenrek@605 2034
 	bar = c->lmmio;
cinap_lenrek@605 2035
 	dprint("size = %.4lux; loc = %.4lux*4\n", sz, o);
cinap_lenrek@605 2036
 	c->encsz = sz;
cinap_lenrek@605 2037
 	c->enctx = bar + o;
cinap_lenrek@605 2038
 	if((h->emctl & Xonly) == 0){
cinap_lenrek@605 2039
 		if(h->emctl & Smb)
cinap_lenrek@605 2040
 			c->encrx = bar + o;
cinap_lenrek@605 2041
 		else
cinap_lenrek@605 2042
 			c->encrx = bar + o*2;
cinap_lenrek@605 2043
 	}
cinap_lenrek@605 2044
 	c->enctype = type;
cinap_lenrek@605 2045
 	return 0;
cinap_lenrek@605 2046
 }
cinap_lenrek@605 2047
 
taruti@0 2048
 static int
taruti@0 2049
 didtype(Pcidev *p)
taruti@0 2050
 {
cinap_lenrek@605 2051
 	int type;
cinap_lenrek@605 2052
 
cinap_lenrek@605 2053
 	type = Tahci;
taruti@0 2054
 	switch(p->vid){
cinap_lenrek@605 2055
 	default:
cinap_lenrek@605 2056
 		return -1;
taruti@0 2057
 	case 0x8086:
cinap_lenrek@2504 2058
 		if((p->did & 0xffff) == 0x1e02)
cinap_lenrek@2504 2059
 			return Tich;		/* c210 */
cinap_lenrek@3757 2060
 		if((p->did & 0xffff) == 0x8c02)
cinap_lenrek@3757 2061
 			return Tich;		/* c220 */
cinap_lenrek@2504 2062
 		if((p->did & 0xffff) == 0x24d1)
cinap_lenrek@2504 2063
 			return Tich;		/* 82801eb/er */
cinap_lenrek@2235 2064
 		if((p->did & 0xffff) == 0x2653)
cinap_lenrek@2235 2065
 			return Tich;		/* 82801fbm */
taruti@0 2066
 		if((p->did & 0xfffc) == 0x2680)
taruti@0 2067
 			return Tesb;
cinap_lenrek@605 2068
 		if((p->did & 0xfffb) == 0x27c1)
cinap_lenrek@605 2069
 			return Tich;		/* 82801g[bh]m */
cinap_lenrek@1118 2070
 		if((p->did & 0xffff) == 0x2822)
cinap_lenrek@1118 2071
 			return Tich;		/* 82801 SATA RAID */
cinap_lenrek@605 2072
 		if((p->did & 0xffff) == 0x2821)
cinap_lenrek@605 2073
 			return Tich;		/* 82801h[roh] */
cinap_lenrek@605 2074
 		if((p->did & 0xfffe) == 0x2824)
cinap_lenrek@605 2075
 			return Tich;		/* 82801h[b] */
cinap_lenrek@605 2076
 		if((p->did & 0xfeff) == 0x2829)
cinap_lenrek@605 2077
 			return Tich;		/* ich8 */
cinap_lenrek@605 2078
 		if((p->did & 0xfffe) == 0x2922)
cinap_lenrek@605 2079
 			return Tich;		/* ich9 */
cinap_lenrek@2504 2080
 		if((p->did & 0xffff) == 0x3a02)
cinap_lenrek@605 2081
 			return Tich;		/* 82801jd/do */
cinap_lenrek@2504 2082
 		if((p->did & 0xfefe) == 0x3a22)
cinap_lenrek@605 2083
 			return Tich;		/* ich10, pch */
cinap_lenrek@2504 2084
 		if((p->did & 0xfff7) == 0x3b28)
cinap_lenrek@605 2085
 			return Tich;		/* pchm */
cinap_lenrek@605 2086
 		if((p->did & 0xfffe) == 0x3b22)
cinap_lenrek@605 2087
 			return Tich;		/* pch */
taruti@0 2088
 		break;
taruti@0 2089
 	case 0x1002:
cinap_lenrek@605 2090
 		if(p->ccru == 1 || p->ccrp != 1)
cinap_lenrek@605 2091
 		if(p->did == 0x4380 || p->did == 0x4390)
cinap_lenrek@605 2092
 			sbsetupahci(p);
cinap_lenrek@605 2093
 		type = Tsb600;
cinap_lenrek@605 2094
 		break;
cinap_lenrek@605 2095
 	case 0x1106:
cinap_lenrek@605 2096
 		/*
cinap_lenrek@605 2097
 		 * unconfirmed report that the programming
cinap_lenrek@605 2098
 		 * interface is set incorrectly.
cinap_lenrek@605 2099
 		 */
cinap_lenrek@605 2100
 		if(p->did == 0x3349)
cinap_lenrek@605 2101
 			return Tahci;
cinap_lenrek@605 2102
 		break;
cinap_lenrek@1191 2103
 	case 0x1022:
cinap_lenrek@1191 2104
 		/* Hudson SATA Controller [AHCI mode] */
cinap_lenrek@7411 2105
 		if((p->did & 0xfffe) == 0x7800){
cinap_lenrek@7411 2106
 			sbsetupahci(p);
cinap_lenrek@1191 2107
 			return Tahci;
cinap_lenrek@7411 2108
 		}
cinap_lenrek@1191 2109
 		break;
cinap_lenrek@605 2110
 	case 0x10de:
cinap_lenrek@605 2111
 	case 0x1039:
cinap_lenrek@605 2112
 	case 0x1b4b:
cinap_lenrek@605 2113
 	case 0x11ab:
cinap_lenrek@605 2114
 		break;
cinap_lenrek@605 2115
 	case 0x197b:
cinap_lenrek@605 2116
 	case 0x10b9:
cinap_lenrek@605 2117
 		type = Tjmicron;
taruti@0 2118
 		break;
taruti@0 2119
 	}
taruti@0 2120
 	if(p->ccrb == Pcibcstore && p->ccru == 6 && p->ccrp == 1)
cinap_lenrek@605 2121
 		return type;
taruti@0 2122
 	return -1;
taruti@0 2123
 }
taruti@0 2124
 
taruti@0 2125
 static SDev*
taruti@0 2126
 iapnp(void)
taruti@0 2127
 {
taruti@0 2128
 	int i, n, nunit, type;
cinap_lenrek@3255 2129
 	uintptr io;
taruti@0 2130
 	Ctlr *c;
taruti@0 2131
 	Drive *d;
taruti@0 2132
 	Pcidev *p;
cinap_lenrek@605 2133
 	SDev *s;
taruti@0 2134
 	static int done;
taruti@0 2135
 
cinap_lenrek@605 2136
 	if(done)
taruti@0 2137
 		return nil;
cinap_lenrek@605 2138
 	done = 1;
cinap_lenrek@2144 2139
 
cinap_lenrek@2423 2140
 	if(getconf("*noahci") != nil)
cinap_lenrek@2423 2141
 		return nil;
cinap_lenrek@2423 2142
 
cinap_lenrek@2144 2143
 	if(getconf("*ahcidebug") != nil){
cinap_lenrek@2144 2144
 		debug = 1;
cinap_lenrek@2144 2145
 		datapi = 1;
cinap_lenrek@2144 2146
 	}
cinap_lenrek@2144 2147
 
taruti@0 2148
 	memset(olds, 0xff, sizeof olds);
taruti@0 2149
 	p = nil;
taruti@0 2150
 	while((p = pcimatch(p, 0, 0)) != nil){
cinap_lenrek@605 2151
 		if((type = didtype(p)) == -1)
cinap_lenrek@605 2152
 			continue;
cinap_lenrek@4175 2153
 		io = p->mem[Abar].bar;
cinap_lenrek@4175 2154
 		if(io == 0 || (io & 1) != 0 || p->mem[Abar].size < 0x180)
taruti@0 2155
 			continue;
cinap_lenrek@4175 2156
 		io &= ~0xf;
taruti@0 2157
 		if(niactlr == NCtlr){
cinap_lenrek@605 2158
 			print("iapnp: %s: too many controllers\n", tname[type]);
taruti@0 2159
 			break;
taruti@0 2160
 		}
taruti@0 2161
 		c = iactlr + niactlr;
cinap_lenrek@605 2162
 		s = sdevs + niactlr;
taruti@0 2163
 		memset(c, 0, sizeof *c);
taruti@0 2164
 		memset(s, 0, sizeof *s);
taruti@0 2165
 		c->mmio = vmap(io, p->mem[Abar].size);
taruti@0 2166
 		if(c->mmio == 0){
cinap_lenrek@605 2167
 			print("%s: address %#p in use did %.4ux\n",
taruti@0 2168
 				Tname(c), io, p->did);
taruti@0 2169
 			continue;
taruti@0 2170
 		}
taruti@0 2171
 		c->lmmio = (ulong*)c->mmio;
taruti@0 2172
 		c->pci = p;
taruti@0 2173
 		c->type = type;
taruti@0 2174
 
taruti@0 2175
 		s->ifc = &sdiahciifc;
cinap_lenrek@605 2176
 		s->idno = 'E';
taruti@0 2177
 		s->ctlr = c;
taruti@0 2178
 		c->sdev = s;
taruti@0 2179
 
cinap_lenrek@6785 2180
 		pcienable(p);
cinap_lenrek@2223 2181
 		ahcihandoff((Ahba*)c->mmio);
cinap_lenrek@6149 2182
 		if(p->vid == 0x8086)
taruti@0 2183
 			iasetupahci(c);
cinap_lenrek@605 2184
 		nunit = ahciconf(c);
cinap_lenrek@6149 2185
 		if(nunit < 1){
taruti@0 2186
 			vunmap(c->mmio, p->mem[Abar].size);
cinap_lenrek@6785 2187
 			pcidisable(p);
taruti@0 2188
 			continue;
taruti@0 2189
 		}
taruti@0 2190
 		c->ndrive = s->nunit = nunit;
taruti@0 2191
 
taruti@0 2192
 		/* map the drives -- they don't all need to be enabled. */
taruti@0 2193
 		memset(c->rawdrive, 0, sizeof c->rawdrive);
taruti@0 2194
 		n = 0;
cinap_lenrek@605 2195
 		for(i = 0; i < NCtlrdrv; i++){
taruti@0 2196
 			d = c->rawdrive + i;
taruti@0 2197
 			d->portno = i;
taruti@0 2198
 			d->driveno = -1;
taruti@0 2199
 			d->sectors = 0;
taruti@0 2200
 			d->serial[0] = ' ';
taruti@0 2201
 			d->ctlr = c;
cinap_lenrek@605 2202
 			if((c->hba->pi & 1<<i) == 0)
taruti@0 2203
 				continue;
cinap_lenrek@4175 2204
 			io = 0x100 + 0x80*i;
cinap_lenrek@4175 2205
 			if((io + 0x80) > p->mem[Abar].size)
cinap_lenrek@4175 2206
 				continue;
cinap_lenrek@4175 2207
 			d->port = (Aport*)(c->mmio + io);
taruti@0 2208
 			d->portc.p = d->port;
taruti@0 2209
 			d->portc.m = &d->portm;
taruti@0 2210
 			d->driveno = n++;
cinap_lenrek@4175 2211
 			snprint(d->name, sizeof d->name, "iahci%d.%d", niactlr, i);
taruti@0 2212
 			c->drive[d->driveno] = d;
taruti@0 2213
 			iadrive[niadrive + d->driveno] = d;
taruti@0 2214
 		}
taruti@0 2215
 		for(i = 0; i < n; i++){
cinap_lenrek@605 2216
 			c->drive[i]->mode = DMautoneg;
taruti@0 2217
 			configdrive(c->drive[i]);
taruti@0 2218
 		}
cinap_lenrek@605 2219
 		ahciencinit(c);
taruti@0 2220
 
taruti@0 2221
 		niadrive += n;
taruti@0 2222
 		niactlr++;
cinap_lenrek@605 2223
 		sdadddevs(s);
cinap_lenrek@605 2224
 		i = (c->hba->cap >> 21) & 1;
cinap_lenrek@605 2225
 		print("#S/%s: %s: sata-%s with %d ports\n", s->name,
cinap_lenrek@605 2226
 			Tname(c), "I\0II" + i*2, nunit);
taruti@0 2227
 	}
cinap_lenrek@605 2228
 	return nil;
taruti@0 2229
 }
taruti@0 2230
 
cinap_lenrek@605 2231
 static Htab ctab[] = {
cinap_lenrek@605 2232
 	Aasp,	"asp",
cinap_lenrek@605 2233
 	Aalpe ,	"alpe ",
cinap_lenrek@605 2234
 	Adlae,	"dlae",
cinap_lenrek@605 2235
 	Aatapi,	"atapi",
cinap_lenrek@605 2236
 	Apste,	"pste",
cinap_lenrek@605 2237
 	Afbsc,	"fbsc",
cinap_lenrek@605 2238
 	Aesp,	"esp",
cinap_lenrek@605 2239
 	Acpd,	"cpd",
cinap_lenrek@605 2240
 	Ampsp,	"mpsp",
cinap_lenrek@605 2241
 	Ahpcp,	"hpcp",
cinap_lenrek@605 2242
 	Apma,	"pma",
cinap_lenrek@605 2243
 	Acps,	"cps",
cinap_lenrek@605 2244
 	Acr,	"cr",
cinap_lenrek@605 2245
 	Afr,	"fr",
cinap_lenrek@605 2246
 	Ampss,	"mpss",
cinap_lenrek@605 2247
 	Apod,	"pod",
cinap_lenrek@605 2248
 	Asud,	"sud",
cinap_lenrek@605 2249
 	Ast,	"st",
taruti@0 2250
 };
taruti@0 2251
 
cinap_lenrek@605 2252
 static char*
cinap_lenrek@605 2253
 capfmt(char *p, char *e, Htab *t, int n, ulong cap)
taruti@0 2254
 {
cinap_lenrek@605 2255
 	uint i;
taruti@0 2256
 
cinap_lenrek@605 2257
 	*p = 0;
cinap_lenrek@605 2258
 	for(i = 0; i < n; i++)
cinap_lenrek@605 2259
 		if(cap & t[i].bit)
cinap_lenrek@605 2260
 			p = seprint(p, e, "%s ", t[i].name);
cinap_lenrek@605 2261
 	return p;
taruti@0 2262
 }
taruti@0 2263
 
taruti@0 2264
 static int
taruti@0 2265
 iarctl(SDunit *u, char *p, int l)
taruti@0 2266
 {
cinap_lenrek@605 2267
 	char buf[32], *e, *op;
taruti@0 2268
 	Aport *o;
taruti@0 2269
 	Ctlr *c;
taruti@0 2270
 	Drive *d;
taruti@0 2271
 
cinap_lenrek@605 2272
 	if((c = u->dev->ctlr) == nil)
taruti@0 2273
 		return 0;
taruti@0 2274
 	d = c->drive[u->subno];
taruti@0 2275
 	o = d->port;
taruti@0 2276
 
taruti@0 2277
 	e = p+l;
taruti@0 2278
 	op = p;
taruti@0 2279
 	if(d->state == Dready){
taruti@0 2280
 		p = seprint(p, e, "model\t%s\n", d->model);
taruti@0 2281
 		p = seprint(p, e, "serial\t%s\n", d->serial);
taruti@0 2282
 		p = seprint(p, e, "firm\t%s\n", d->firmware);
cinap_lenrek@605 2283
 		if(d->wwn != 0)
cinap_lenrek@605 2284
 			p = seprint(p, e, "wwn\t%ullx\n", d->wwn);
taruti@0 2285
 		p = seprint(p, e, "flag\t");
cinap_lenrek@605 2286
 		p = pflag(p, e, &d->portm);
cinap_lenrek@605 2287
 		p = seprint(p, e, "udma\t%d\n", d->portm.udma);
taruti@0 2288
 	}else
taruti@0 2289
 		p = seprint(p, e, "no disk present [%s]\n", diskstates[d->state]);
taruti@0 2290
 	serrstr(o->serror, buf, buf + sizeof buf - 1);
cinap_lenrek@605 2291
 	p = seprint(p, e, "reg\ttask %lux cmd %lux serr %lux %s ci %lux is %lux "
cinap_lenrek@605 2292
 		"sig %lux sstatus %.3lux\n", o->task, o->cmd, o->serror, buf,
taruti@0 2293
 		o->ci, o->isr, o->sig, o->sstatus);
cinap_lenrek@605 2294
 	p = seprint(p, e, "cmd\t");
cinap_lenrek@605 2295
 	p = capfmt(p, e, ctab, nelem(ctab), o->cmd);
cinap_lenrek@605 2296
 	p = seprint(p, e, "\n");
cinap_lenrek@605 2297
 	p = seprint(p, e, "mode\t%s %s\n", modes[d->mode], modes[maxmode(c)]);
cinap_lenrek@3231 2298
 	p = seprint(p, e, "geometry %llud %d\n", d->sectors, d->secsize);
google@1920 2299
 	p = seprint(p, e, "alignment %d %d\n", 
google@1920 2300
 		d->secsize<<d->portm.physshift, d->portm.physalign);
cinap_lenrek@2223 2301
 	p = seprint(p, e, "missirq\t%ud\n", c->missirq);
taruti@0 2302
 	return p - op;
taruti@0 2303
 }
taruti@0 2304
 
taruti@0 2305
 static void
taruti@0 2306
 forcemode(Drive *d, char *mode)
taruti@0 2307
 {
taruti@0 2308
 	int i;
taruti@0 2309
 
cinap_lenrek@605 2310
 	for(i = 0; i < nelem(modes); i++)
cinap_lenrek@605 2311
 		if(strcmp(mode, modes[i]) == 0)
taruti@0 2312
 			break;
cinap_lenrek@605 2313
 	if(i == nelem(modes))
taruti@0 2314
 		i = 0;
taruti@0 2315
 	ilock(d);
taruti@0 2316
 	d->mode = i;
taruti@0 2317
 	iunlock(d);
taruti@0 2318
 }
taruti@0 2319
 
taruti@0 2320
 static void
taruti@0 2321
 forcestate(Drive *d, char *state)
taruti@0 2322
 {
taruti@0 2323
 	int i;
taruti@0 2324
 
taruti@0 2325
 	for(i = 0; i < nelem(diskstates); i++)
taruti@0 2326
 		if(strcmp(state, diskstates[i]) == 0)
taruti@0 2327
 			break;
taruti@0 2328
 	if(i == nelem(diskstates))
taruti@0 2329
 		error(Ebadctl);
cinap_lenrek@2223 2330
 	dstatus(d, i);
taruti@0 2331
 }
taruti@0 2332
 
cinap_lenrek@605 2333
 static int
cinap_lenrek@605 2334
 runsettxmode(Drive *d, char *s)
cinap_lenrek@605 2335
 {
cinap_lenrek@605 2336
 	int i;
cinap_lenrek@605 2337
 	Aportc *c;
cinap_lenrek@605 2338
 	Aportm *m;
cinap_lenrek@605 2339
 
cinap_lenrek@605 2340
 	c = &d->portc;
cinap_lenrek@605 2341
 	m = &d->portm;
cinap_lenrek@605 2342
 
cinap_lenrek@605 2343
 	i = 1;
cinap_lenrek@605 2344
 	if(lockready(d) == 0){
cinap_lenrek@605 2345
 		m->udma = atoi(s);
cinap_lenrek@605 2346
 		if(settxmode(c, m->udma) == 0)
cinap_lenrek@605 2347
 			i = 0;
cinap_lenrek@605 2348
 	}
cinap_lenrek@605 2349
 	qunlock(m);
cinap_lenrek@605 2350
 	return i;
cinap_lenrek@605 2351
 }
cinap_lenrek@605 2352
 
taruti@0 2353
 
taruti@0 2354
 static int
taruti@0 2355
 iawctl(SDunit *u, Cmdbuf *cmd)
taruti@0 2356
 {
taruti@0 2357
 	char **f;
taruti@0 2358
 	Ctlr *c;
taruti@0 2359
 	Drive *d;
taruti@0 2360
 
taruti@0 2361
 	c = u->dev->ctlr;
taruti@0 2362
 	d = c->drive[u->subno];
taruti@0 2363
 	f = cmd->f;
taruti@0 2364
 
cinap_lenrek@605 2365
 	if(strcmp(f[0], "mode") == 0)
taruti@0 2366
 		forcemode(d, f[1]? f[1]: "satai");
taruti@0 2367
 	else if(strcmp(f[0], "state") == 0)
taruti@0 2368
 		forcestate(d, f[1]? f[1]: "null");
cinap_lenrek@605 2369
 	else if(strcmp(f[0], "txmode") == 0){
cinap_lenrek@605 2370
 		if(runsettxmode(d, f[1]? f[1]: "0"))
cinap_lenrek@605 2371
 			cmderror(cmd, "bad txmode / stuck port");
cinap_lenrek@605 2372
 	}else
taruti@0 2373
 		cmderror(cmd, Ebadctl);
taruti@0 2374
 	return 0;
taruti@0 2375
 }
taruti@0 2376
 
taruti@0 2377
 static char *
taruti@0 2378
 portr(char *p, char *e, uint x)
taruti@0 2379
 {
taruti@0 2380
 	int i, a;
taruti@0 2381
 
taruti@0 2382
 	p[0] = 0;
taruti@0 2383
 	a = -1;
taruti@0 2384
 	for(i = 0; i < 32; i++){
taruti@0 2385
 		if((x & (1<<i)) == 0){
taruti@0 2386
 			if(a != -1 && i - 1 != a)
taruti@0 2387
 				p = seprint(p, e, "-%d", i - 1);
taruti@0 2388
 			a = -1;
taruti@0 2389
 			continue;
taruti@0 2390
 		}
taruti@0 2391
 		if(a == -1){
taruti@0 2392
 			if(i > 0)
taruti@0 2393
 				p = seprint(p, e, ", ");
taruti@0 2394
 			p = seprint(p, e, "%d", a = i);
taruti@0 2395
 		}
taruti@0 2396
 	}
taruti@0 2397
 	if(a != -1 && i - 1 != a)
taruti@0 2398
 		p = seprint(p, e, "-%d", i - 1);
taruti@0 2399
 	return p;
taruti@0 2400
 }
taruti@0 2401
 
cinap_lenrek@605 2402
 static Htab htab[] = {
cinap_lenrek@605 2403
 	H64a,	"64a",
cinap_lenrek@605 2404
 	Hncq,	"ncq",
cinap_lenrek@605 2405
 	Hsntf,	"ntf",
cinap_lenrek@605 2406
 	Hmps,	"mps",
cinap_lenrek@605 2407
 	Hss,	"ss",
cinap_lenrek@605 2408
 	Halp,	"alp",
cinap_lenrek@605 2409
 	Hal,	"led",
cinap_lenrek@605 2410
 	Hclo,	"clo",
cinap_lenrek@605 2411
 	Ham,	"am",
cinap_lenrek@605 2412
 	Hpm,	"pm",
cinap_lenrek@605 2413
 	Hfbs,	"fbs",
cinap_lenrek@605 2414
 	Hpmb,	"pmb",
cinap_lenrek@605 2415
 	Hssc,	"slum",
cinap_lenrek@605 2416
 	Hpsc,	"pslum",
cinap_lenrek@605 2417
 	Hcccs,	"coal",
cinap_lenrek@605 2418
 	Hems,	"ems",
cinap_lenrek@605 2419
 	Hxs,	"xs",
cinap_lenrek@605 2420
 };
taruti@0 2421
 
cinap_lenrek@605 2422
 static Htab htab2[] = {
cinap_lenrek@605 2423
 	Apts,	"apts",
cinap_lenrek@605 2424
 	Nvmp,	"nvmp",
cinap_lenrek@605 2425
 	Boh,	"boh",
cinap_lenrek@605 2426
 };
taruti@0 2427
 
cinap_lenrek@605 2428
 static Htab emtab[] = {
cinap_lenrek@605 2429
 	Pm,	"pm",
cinap_lenrek@605 2430
 	Alhd,	"alhd",
cinap_lenrek@605 2431
 	Xonly,	"xonly",
cinap_lenrek@605 2432
 	Smb,	"smb",
cinap_lenrek@605 2433
 	Esgpio,	"esgpio",
cinap_lenrek@605 2434
 	Eses2,	"eses2",
cinap_lenrek@605 2435
 	Esafte,	"esafte",
cinap_lenrek@605 2436
 	Elmt,	"elmt",
cinap_lenrek@605 2437
 };
cinap_lenrek@605 2438
 
cinap_lenrek@605 2439
 static char*
cinap_lenrek@605 2440
 iartopctl(SDev *s, char *p, char *e)
cinap_lenrek@605 2441
 {
cinap_lenrek@605 2442
 	char pr[25];
cinap_lenrek@605 2443
 	ulong cap;
cinap_lenrek@605 2444
 	Ahba *h;
cinap_lenrek@605 2445
 	Ctlr *c;
cinap_lenrek@605 2446
 
cinap_lenrek@605 2447
 	c = s->ctlr;
cinap_lenrek@605 2448
 	h = c->hba;
cinap_lenrek@605 2449
 	cap = h->cap;
cinap_lenrek@605 2450
 	p = seprint(p, e, "sd%c ahci %s port %#p: ", s->idno, Tname(c), h);
cinap_lenrek@605 2451
 	p = capfmt(p, e, htab, nelem(htab), cap);
cinap_lenrek@605 2452
 	p = capfmt(p, e, htab2, nelem(htab2), h->cap2);
cinap_lenrek@605 2453
 	p = capfmt(p, e, emtab, nelem(emtab), h->emctl);
cinap_lenrek@605 2454
 	portr(pr, pr + sizeof pr, h->pi);
taruti@0 2455
 	return seprint(p, e,
cinap_lenrek@605 2456
 		"iss %ld ncs %ld np %ld ghc %lux isr %lux pi %lux %s ver %lux\n",
taruti@0 2457
 		(cap>>20) & 0xf, (cap>>8) & 0x1f, 1 + (cap & 0x1f),
cinap_lenrek@605 2458
 		h->ghc, h->isr, h->pi, pr, h->ver);
taruti@0 2459
 }
taruti@0 2460
 
taruti@0 2461
 static int
taruti@0 2462
 iawtopctl(SDev *, Cmdbuf *cmd)
taruti@0 2463
 {
taruti@0 2464
 	int *v;
taruti@0 2465
 	char **f;
taruti@0 2466
 
taruti@0 2467
 	f = cmd->f;
taruti@0 2468
 	v = 0;
taruti@0 2469
 
taruti@0 2470
 	if(strcmp(f[0], "debug") == 0)
taruti@0 2471
 		v = &debug;
taruti@0 2472
 	else if(strcmp(f[0], "idprint") == 0)
taruti@0 2473
 		v = &prid;
taruti@0 2474
 	else if(strcmp(f[0], "aprint") == 0)
taruti@0 2475
 		v = &datapi;
cinap_lenrek@605 2476
 	else if(strcmp(f[0], "ledprint") == 0)
cinap_lenrek@605 2477
 		v = &dled;
taruti@0 2478
 	else
taruti@0 2479
 		cmderror(cmd, Ebadctl);
taruti@0 2480
 
taruti@0 2481
 	switch(cmd->nf){
taruti@0 2482
 	default:
taruti@0 2483
 		cmderror(cmd, Ebadarg);
taruti@0 2484
 	case 1:
taruti@0 2485
 		*v ^= 1;
taruti@0 2486
 		break;
taruti@0 2487
 	case 2:
taruti@0 2488
 		if(f[1])
taruti@0 2489
 			*v = strcmp(f[1], "on") == 0;
taruti@0 2490
 		else
taruti@0 2491
 			*v ^= 1;
taruti@0 2492
 		break;
taruti@0 2493
 	}
taruti@0 2494
 	return 0;
taruti@0 2495
 }
taruti@0 2496
 
taruti@0 2497
 SDifc sdiahciifc = {
cinap_lenrek@605 2498
 	"ahci",
taruti@0 2499
 
taruti@0 2500
 	iapnp,
taruti@0 2501
 	nil,		/* legacy */
taruti@0 2502
 	iaenable,
taruti@0 2503
 	iadisable,
taruti@0 2504
 
taruti@0 2505
 	iaverify,
taruti@0 2506
 	iaonline,
taruti@0 2507
 	iario,
taruti@0 2508
 	iarctl,
taruti@0 2509
 	iawctl,
taruti@0 2510
 
cinap_lenrek@605 2511
 	ahcibio,
taruti@0 2512
 	nil,		/* probe */
taruti@0 2513
 	nil,		/* clear */
taruti@0 2514
 	iartopctl,
taruti@0 2515
 	iawtopctl,
cinap_lenrek@605 2516
 	iaataio,
taruti@0 2517
 };