+}
+
+static int
+dpauxio(Igfx *igfx, Dp *dp, uchar buf[20], int len)
+{
+ int t, i;
+ u32int w;
+
+ if(dp->auxctl.a == 0){
+ werrstr("not present");
+ return -1;
+ }
+
+ t = 0;
+ while(rr(igfx, dp->auxctl.a) & (1<<31)){
+ if(++t >= 10){
+ werrstr("busy");
+ return -1;
+ }
+ sleep(5);
+ }
+
+ /* clear sticky bits */
+ wr(igfx, dp->auxctl.a, (1<<28) | (1<<25) | (1<<30));
+
+ for(i=0; i<nelem(dp->auxdat); i++){
+ w = buf[i*4+0]<<24;
+ w |= buf[i*4+1]<<16;
+ w |= buf[i*4+2]<<8;
+ w |= buf[i*4+3];
+ wr(igfx, dp->auxdat[i].a, w);
+ }
+
+ /* 2X Bit Clock divider */
+ w = ((dp == &igfx->dp[0]) ? igfx->cdclk : (igfx->rawclkfreq.v & 0x3ff)) >> 1;
+ if(w < 1 || w > 0x3fd){
+ werrstr("bad clock");
+ return -1;
+ }
+
+ /* hack: slow down a bit */
+ w += 2;
+
+ w |= 1<<31; /* SendBusy */
+ w |= 1<<29; /* interrupt disabled */
+ w |= 3<<26; /* timeout 1600µs */
+ w |= len<<20; /* send bytes */
+ w |= 5<<16; /* precharge time (5*2 = 10µs) */
+ wr(igfx, dp->auxctl.a, w);
+
+ t = 0;
+ for(;;){
+ w = rr(igfx, dp->auxctl.a);
+ if((w & (1<<30)) != 0)
+ break;
+ if(++t >= 10){
+ werrstr("busy");
+ return -1;
+ }
+ sleep(5);
+ }
+ if(w & (1<<28)){
+ werrstr("receive timeout");
+ return -1;
+ }
+ if(w & (1<<25)){
+ werrstr("receive error");
+ return -1;
+ }
+
+ len = (w >> 20) & 0x1f;
+ for(i=0; i<nelem(dp->auxdat); i++){
+ w = rr(igfx, dp->auxdat[i].a);
+ buf[i*4+0] = w>>24;
+ buf[i*4+1] = w>>16;
+ buf[i*4+2] = w>>8;
+ buf[i*4+3] = w;
+ }
+
+ return len;
+}
+
+enum {
+ CmdNative = 8,
+ CmdMot = 4,
+ CmdRead = 1,
+ CmdWrite = 0,
+};
+
+static int
+dpauxtra(Igfx *igfx, Dp *dp, int cmd, int addr, uchar *data, int len)
+{
+ uchar buf[20];
+ int r;
+
+ assert(len <= 16);
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (cmd << 4) | ((addr >> 16) & 0xF);
+ buf[1] = addr >> 8;
+ buf[2] = addr;
+ buf[3] = len-1;
+ r = 3;
+ if(data != nil && len > 0){
+ if((cmd & CmdRead) == 0)
+ memmove(buf+4, data, len);
+ r = 4;
+ if((cmd & CmdRead) == 0)
+ r += len;
+ }
+ if((r = dpauxio(igfx, dp, buf, r)) < 0){
+ trace("%s: dpauxio: dp %c, cmd %x, addr %x, len %d: %r\n",
+ igfx->ctlr->name, 'a'+(int)(dp - &igfx->dp[0]), cmd, addr, len);
+ return -1;
+ }
+ if(r == 0 || data == nil || len == 0)
+ return 0;
+ if((cmd & CmdRead) != 0){
+ if(--r < len)
+ len = r;
+ memmove(data, buf+1, len);
+ }
+ return len;
+}
+
+static int
+rdpaux(Igfx *igfx, Dp *dp, int addr)
+{
+ uchar buf[1];
+ if(dpauxtra(igfx, dp, CmdNative|CmdRead, addr, buf, 1) != 1)
+ return -1;
+ return buf[0];
+}
+static int
+wdpaux(Igfx *igfx, Dp *dp, int addr, uchar val)
+{
+ if(dpauxtra(igfx, dp, CmdNative|CmdWrite, addr, &val, 1) != 1)
+ return -1;
+ return 0;
+}
+
+static int
+enabledp(Igfx *igfx, Dp *dp)
+{
+ int try, r;
+ u32int w;
+
+ if(dp->ctl.a == 0)
+ return 0;
+ if((dp->ctl.v & (1<<31)) == 0)
+ return 0;
+
+ /* FIXME: always times out */
+ if(igfx->type == TypeHSW && dp == &igfx->dp[0])
+ goto Skip;
+
+ /* Link configuration */
+ wdpaux(igfx, dp, 0x100, (270*MHz) / 27000000);
+ w = dp->ctl.v >> (igfx->type == TypeHSW ? 1 : 19) & 7;
+ wdpaux(igfx, dp, 0x101, w+1);
+
+ r = 0;
+
+ /* Link training pattern 1 */
+ dp->ctl.v &= ~(7<<8);
+ loadreg(igfx, dp->ctl);
+ for(try = 0;;try++){
+ if(try > 5)
+ goto Fail;
+ /* Link training pattern 1 */
+ wdpaux(igfx, dp, 0x102, 0x01);
+ sleep(100);
+ if((r = rdpaux(igfx, dp, 0x202)) < 0)
+ goto Fail;
+ if(r & 1) /* LANE0_CR_DONE */
+ break;
+ }
+ trace("pattern1 finished: %x\n", r);
+
+ /* Link training pattern 2 */
+ dp->ctl.v &= ~(7<<8);
+ dp->ctl.v |= 1<<8;
+ loadreg(igfx, dp->ctl);
+ for(try = 0;;try++){
+ if(try > 5)
+ goto Fail;
+ /* Link training pattern 2 */
+ wdpaux(igfx, dp, 0x102, 0x02);
+ sleep(100);
+ if((r = rdpaux(igfx, dp, 0x202)) < 0)
+ goto Fail;
+ if((r & 7) == 7)
+ break;
+ }
+ trace("pattern2 finished: %x\n", r);
+
+ if(igfx->type == TypeHSW){
+ /* set link training to idle pattern and wait for 5 idle
+ * patterns */
+ dp->ctl.v &= ~(7<<8);
+ dp->ctl.v |= 2<<8;
+ loadreg(igfx, dp->ctl);
+ for(try=0; try<10; try++){
+ sleep(10);
+ if(rr(igfx, dp->stat.a) & (1<<25))
+ break;
+ }
+ }
+Skip:
+ /* stop training */
+ dp->ctl.v &= ~(7<<8);
+ dp->ctl.v |= 3<<8;
+ loadreg(igfx, dp->ctl);
+ wdpaux(igfx, dp, 0x102, 0x00);
+ return 1;
+
+Fail:
+ trace("training failed: %x\n", r);
+
+ /* disable port */
+ dp->ctl.v &= ~(1<<31);
+ loadreg(igfx, dp->ctl);
+ wdpaux(igfx, dp, 0x102, 0x00);
+ return -1;
+}