code: plan9front

Download patch

ref: 548a48d1561dd77dbc082dddf0f9a2776ee91914
parent: e39d9249076e9a95e97b33313be1ab2e23095f1d
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Jul 3 07:36:50 EDT 2022

imx8: pcie and nvme support

--- a/sys/src/9/imx8/ccm.c
+++ b/sys/src/9/imx8/ccm.c
@@ -1075,6 +1075,90 @@
 	}
 }
 
+enum {
+	CCM_ANALOG_PLLOUT_MONITOR_CFG	= 0x74/4,
+		PLLOUT_MONITOR_CLK_CKE	= 1<<4,
+	CCM_ANALOG_FRAC_PLLOUT_DIV_CFG	= 0x78/4,
+	CCM_ANALOG_SCCG_PLLOUT_DIV_CFG	= 0x7C/4,
+};
+
+static struct {
+	uchar	input;
+	uchar	reg;		/* divider register */
+	uchar	shift;		/* divider shift */
+} anapllout_input[16] = {
+[0]	OSC_25M_REF_CLK,
+[1]	OSC_27M_REF_CLK,
+/* [2]	HDMI_PHY_27M_CLK */
+/* [3]	CLK1_P_N */
+[4]	OSC_32K_REF_CLK,
+[5]	AUDIO_PLL1_CLK,		CCM_ANALOG_FRAC_PLLOUT_DIV_CFG,	0,
+[6]	AUDIO_PLL2_CLK,		CCM_ANALOG_FRAC_PLLOUT_DIV_CFG, 4,
+[7]	GPU_PLL_CLK,		CCM_ANALOG_FRAC_PLLOUT_DIV_CFG,	12,
+[8]	VPU_PLL_CLK,		CCM_ANALOG_FRAC_PLLOUT_DIV_CFG, 16,
+[9]	VIDEO_PLL1_CLK,		CCM_ANALOG_FRAC_PLLOUT_DIV_CFG, 8,
+[10]	ARM_PLL_CLK,		CCM_ANALOG_FRAC_PLLOUT_DIV_CFG, 20,
+[11]	SYSTEM_PLL1_CLK,	CCM_ANALOG_SCCG_PLLOUT_DIV_CFG, 0,
+[12]	SYSTEM_PLL2_CLK,	CCM_ANALOG_SCCG_PLLOUT_DIV_CFG, 4,
+[13]	SYSTEM_PLL3_CLK,	CCM_ANALOG_SCCG_PLLOUT_DIV_CFG, 8,
+[14]	VIDEO_PLL2_CLK,		CCM_ANALOG_SCCG_PLLOUT_DIV_CFG, 16,
+[15]	DRAM_PLL1_CLK,		CCM_ANALOG_SCCG_PLLOUT_DIV_CFG, 12,
+};
+
+static void
+setanapllout(int input, int freq)
+{
+	int mux, div, reg;
+
+	for(mux = 0; mux < nelem(anapllout_input); mux++)
+		if(anapllout_input[mux].input == input)
+			goto Muxok;
+	panic("setanapllout: bad input clock\n");
+	return;
+Muxok:
+	anatop[CCM_ANALOG_PLLOUT_MONITOR_CFG] = mux;
+	if(freq <= 0)
+		return;
+	div = input_clk_freq[input] / freq;
+	if(div < 1 || div > 8){
+		panic("setanapllout: divider out of range\n");
+		return;
+	}
+	enablepll(input);
+	reg = anapllout_input[mux].reg;
+	if(reg){
+		int shift = anapllout_input[mux].shift;
+		anatop[reg] = (anatop[reg] & ~(7<<shift)) | ((div-1)<<shift);
+	} else if(div != 1){
+		panic("setanapllout: bad frequency\n");
+		return;
+	}
+	anatop[CCM_ANALOG_PLLOUT_MONITOR_CFG] |= PLLOUT_MONITOR_CLK_CKE;
+}
+
+static int
+getanapllout(void)
+{
+	int mux, input, freq, reg, div;
+	u32int cfg = anatop[CCM_ANALOG_PLLOUT_MONITOR_CFG];
+
+	mux = cfg & 0xF;
+	input = anapllout_input[mux].input;
+	if(input == 0)
+		return 0;
+	freq = input_clk_freq[input];
+	if((cfg & PLLOUT_MONITOR_CLK_CKE) == 0)
+		freq = -freq;
+	reg = anapllout_input[mux].reg;
+	if(reg){
+		int shift = anapllout_input[mux].shift;
+		div = ((anatop[reg] >> shift) & 7)+1;
+	} else {
+		div = 1;
+	}
+	return freq / div;
+}
+
 static u32int
 clkgate(Clock *gate, u32int val)
 {
@@ -1330,6 +1414,11 @@
 {
 	int root, input;
 
+	if(cistrcmp(name, "ccm_analog_pllout") == 0){
+		setanapllout(lookinputclk(source), freq);
+		return;
+	}
+
 	if((root = lookrootclk(name)) < 0)
 		panic("setclkrate: clock %s not defined", name);
 	if(source == nil)
@@ -1345,6 +1434,9 @@
 getclkrate(char *name)
 {
 	int root, input;
+
+	if(cistrcmp(name, "ccm_analog_pllout") == 0)
+		return getanapllout();
 
 	if((root = lookrootclk(name)) >= 0)
 		return rootclkgetcfg(root, &input);
--- a/sys/src/9/imx8/fns.h
+++ b/sys/src/9/imx8/fns.h
@@ -159,3 +159,10 @@
 #define GPIO_PIN(n, m)	((n)<<5 | (m))
 extern void gpioout(uint pin, int set);
 extern int gpioin(uint pin);
+
+/* pciimx */
+extern int pcicfgrw8(int tbdf, int rno, int data, int read);
+extern int pcicfgrw16(int tbdf, int rno, int data, int read);
+extern int pcicfgrw32(int tbdf, int rno, int data, int read);
+extern void pciintrenable(int tbdf, void (*f)(Ureg*, void*), void *a);
+extern void pciintrdisable(int tbdf, void (*f)(Ureg*, void*), void *a);
--- a/sys/src/9/imx8/gic.c
+++ b/sys/src/9/imx8/gic.c
@@ -254,6 +254,11 @@
 	u32int intid;
 	int cpu, prio;
 
+	if(BUSTYPE(tbdf) == BusPCI){
+		pciintrenable(tbdf, f, a);
+		return;
+	}
+
 	if(tbdf != BUSUNKNOWN)
 		return;
 
@@ -306,6 +311,10 @@
 }
 
 void
-intrdisable(int, void (*)(Ureg*, void*), void *, int, char*)
+intrdisable(int tbdf, void (*f)(Ureg*, void*), void *a, int, char*)
 {
+	if(BUSTYPE(tbdf) == BusPCI){
+		pciintrdisable(tbdf, f, a);
+		return;
+	}
 }
--- a/sys/src/9/imx8/io.h
+++ b/sys/src/9/imx8/io.h
@@ -28,7 +28,12 @@
 	IRQsctr0	= SPI+47,
 	IRQsctr1	= SPI+48,
 
+	IRQpci2		= SPI+74,
+
 	IRQenet1	= SPI+118,
+
+	IRQpci1		= SPI+122,
 };
 
 #define BUSUNKNOWN (-1)
+#define	PCIWADDR(x)	PADDR(x)
--- /dev/null
+++ b/sys/src/9/imx8/pciimx.c
@@ -1,0 +1,497 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/pci.h"
+
+typedef struct Intvec Intvec;
+struct Intvec
+{
+	Pcidev *p;
+	void (*f)(Ureg*, void*);
+	void *a;
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr
+{
+	uvlong	mem_base;
+	uvlong	mem_size;
+	uvlong	cfg_base;
+	uvlong	cfg_size;
+	uvlong	io_base;
+	uvlong	io_size;
+
+	int	bno, ubn;
+	int	irq;
+
+	u32int	*dbi;
+	u32int	*cfg;
+	Pcidev	*bridge;
+
+	Lock;
+	Intvec	vec[32];
+};
+
+static Ctlr ctlrs[2] = {
+	{
+		0x18000000, 0x7f00000,
+		0x1ff00000, 0x80000,
+		0x1ff80000, 0x10000,
+		0, 127, IRQpci1,
+		(u32int*)(VIRTIO + 0x3800000),
+	},
+	{
+		0x20000000, 0x7f00000,
+		0x27f00000, 0x80000,
+		0x27f80000, 0x10000,
+		128, 255, IRQpci2,
+		(u32int*)(VIRTIO + 0x3c00000),
+	},
+};
+
+enum {
+	IATU_MAX		= 8,
+	IATU_INBOUND		= 1<<0,
+	IATU_INDEX_SHIFT	= 1,
+
+	IATU_OFFSET		= 0x300000/4,
+	IATU_STRIDE		= 0x100/4,
+
+	IATU_REGION_CTRL_1	= 0x00/4,
+		CTRL_1_FUNC_NUM_SHIFT		= 20,
+		CTRL_1_FUNC_NUM_MASK		= 7<<CTRL_1_FUNC_NUM_SHIFT,
+
+		CTRL_1_INCREASE_REGION_SIZ	= 1<<13,
+
+		CTRL_1_ATTR_SHIFT		= 9,
+		CTRL_1_ATTR_MASK		= 3<<CTRL_1_ATTR_SHIFT,
+
+		CTRL_1_TD			= 1<<8,
+	
+		CTRL_1_TC_SHIFT			= 5,
+		CTRL_1_TC_MASK			= 7<<CTRL_1_TC_SHIFT,
+
+		CTRL_1_TYPE_SHIFT		= 0,
+		CTRL_1_TYPE_MASK		= 0x1F<<CTRL_1_TYPE_SHIFT,
+		CTRL_1_TYPE_MEM			= 0x0<<CTRL_1_TYPE_SHIFT,
+		CTRL_1_TYPE_IO			= 0x2<<CTRL_1_TYPE_SHIFT,
+		CTRL_1_TYPE_CFG0		= 0x4<<CTRL_1_TYPE_SHIFT,
+		CTRL_1_TYPE_CFG1		= 0x5<<CTRL_1_TYPE_SHIFT,
+
+	IATU_REGION_CTRL_2	= 0x04/4,
+		CTRL_2_REGION_EN		= 1<<31,
+		CTRL_2_INVERT_MODE		= 1<<29,
+		CTRL_2_CFG_SHIFT_MODE		= 1<<28,
+		CTRL_2_DMA_BYPASS		= 1<<27,
+		CTRL_2_HEADER_SUBSITUTE_EN	= 1<<23,
+		CTRL_2_INHIBIT_PAYLOAD		= 1<<22,
+		CTRL_2_SNP			= 1<<20,
+		CTRL_2_FUNC_BYPASS		= 1<<19,
+		CTRL_2_TAG_SUBSTITUTE_EN 	= 1<<16,
+
+	IATU_LWR_BSAE_ADDR	= 0x08/4,
+	IATU_UPPER_BASE_ADDR	= 0x0C/4,
+	IATU_LWR_LIMIT_ADDR	= 0x10/4,
+	IATU_LWR_TARGET_ADDR	= 0x14/4,
+	IATU_UPPER_TARGET_ADDR	= 0x18/4,
+	IATU_UPPER_LIMIT_ADDR	= 0x20/4,	/* undocumented */
+};
+
+/* disable all iATU's */
+static void
+iatuinit(Ctlr *ctlr)
+{
+	u32int *reg;
+	int index;
+
+	for(index=0; index < IATU_MAX; index++){
+		reg = &ctlr->dbi[IATU_OFFSET + IATU_STRIDE*index];
+		reg[IATU_REGION_CTRL_2] &= ~CTRL_2_REGION_EN;
+	}
+}
+
+static void
+iatucfg(Ctlr *ctlr, int index, u32int type, uvlong target, uvlong base, uvlong size)
+{
+	uvlong limit = base + size - 1;
+	u32int *reg;
+
+	assert(size > 0);
+	assert(index < IATU_MAX);
+	assert((index & IATU_INBOUND) == 0);
+
+	reg = &ctlr->dbi[IATU_OFFSET + IATU_STRIDE*index];
+	reg[IATU_REGION_CTRL_2] &= ~CTRL_2_REGION_EN;
+
+	reg[IATU_LWR_BSAE_ADDR] = base;
+	reg[IATU_UPPER_BASE_ADDR] = base >> 32;
+	reg[IATU_LWR_LIMIT_ADDR] = limit;
+	reg[IATU_UPPER_LIMIT_ADDR] = limit >> 32;
+	reg[IATU_LWR_TARGET_ADDR] = target;
+	reg[IATU_UPPER_TARGET_ADDR] = target >> 32;
+
+	type &= CTRL_1_TYPE_MASK;
+	if(((size-1)>>32) != 0)
+		type |= CTRL_1_INCREASE_REGION_SIZ;
+
+	reg[IATU_REGION_CTRL_1] = type;
+	reg[IATU_REGION_CTRL_2] = CTRL_2_REGION_EN;
+
+	while((reg[IATU_REGION_CTRL_2] & CTRL_2_REGION_EN) == 0)
+		microdelay(10);
+}
+
+static Ctlr*
+bus2ctlr(int bno)
+{
+	Ctlr *ctlr;
+
+	for(ctlr = ctlrs; ctlr < &ctlrs[nelem(ctlrs)]; ctlr++)
+		if(bno >= ctlr->bno && bno <= ctlr->ubn)
+			return ctlr;
+	return nil;
+}
+
+static void*
+cfgaddr(int tbdf, int rno)
+{
+	Ctlr *ctlr;
+
+	ctlr = bus2ctlr(BUSBNO(tbdf));
+	if(ctlr == nil)
+		return nil;
+
+	if(pciparentdev == nil){
+		if(BUSDNO(tbdf) != 0 || BUSFNO(tbdf) != 0)
+			return nil;
+		return (uchar*)ctlr->dbi + rno;
+	}
+
+	iatucfg(ctlr, 0<<IATU_INDEX_SHIFT,
+		pciparentdev->parent==nil? CTRL_1_TYPE_CFG0: CTRL_1_TYPE_CFG1,
+		BUSBNO(tbdf)<<24 | BUSDNO(tbdf)<<19 | BUSFNO(tbdf)<<16,
+		ctlr->cfg_base, ctlr->cfg_size);
+
+	return (uchar*)ctlr->cfg + rno;
+}
+
+int
+pcicfgrw32(int tbdf, int rno, int data, int read)
+{
+	u32int *p;
+
+	if((p = cfgaddr(tbdf, rno & ~3)) != nil){
+		if(read)
+			data = *p;
+		else
+			*p = data;
+	} else {
+		data = -1;
+	}
+	return data;
+}
+
+int
+pcicfgrw16(int tbdf, int rno, int data, int read)
+{
+	u16int *p;
+
+	if((p = cfgaddr(tbdf, rno & ~1)) != nil){
+		if(read)
+			data = *p;
+		else
+			*p = data;
+	} else {
+		data = -1;
+	}
+	return data;
+}
+
+int
+pcicfgrw8(int tbdf, int rno, int data, int read)
+{
+	u8int *p;
+
+	if((p = cfgaddr(tbdf, rno)) != nil){
+		if(read)
+			data = *p;
+		else
+			*p = data;
+	} else {
+		data = -1;
+	}
+	return data;
+}
+
+static u16int msimsg;
+#define MSI_TARGET_ADDR		PADDR(&msimsg)
+
+enum {
+	MSI_CAP_ID		= 0x50/4,
+		PCI_MSI_ENABLE	= 1<<16,
+
+	MSI_CTRL_ADDR		= 0x820/4,
+	MSI_CTRL_UPPER_ADDR	= 0x824/4,
+	MSI_CTRL_INT_0_EN	= 0x828/4,
+	MSI_CTRL_INT_0_MASK	= 0x82C/4,
+	MSI_CTRL_INT_0_STATUS	= 0x830/4,
+
+	MISC_CONTROL_1		= 0x8BC/4,
+		DBI_RO_WR_EN 	= 1<<0,
+};
+
+static void
+pciinterrupt(Ureg *ureg, void *arg)
+{
+	Ctlr *ctlr = arg;
+	Intvec *vec;
+	u32int status, mask;
+
+	status = ctlr->dbi[MSI_CTRL_INT_0_STATUS];
+	if(status == 0)
+		return;
+	ctlr->dbi[MSI_CTRL_INT_0_STATUS] = status;
+
+	ilock(ctlr);
+	for(vec = ctlr->vec, mask = 1; vec < &ctlr->vec[nelem(ctlr->vec)]; vec++, mask <<= 1){
+		if((status & mask) != 0 && vec->f != nil)
+			(*vec->f)(ureg, vec->a);
+	}
+	iunlock(ctlr);
+}
+
+static void
+pciintrinit(Ctlr *ctlr)
+{
+	ctlr->dbi[MSI_CTRL_INT_0_EN] = 0;
+	ctlr->dbi[MSI_CTRL_INT_0_MASK] = 0;
+	ctlr->dbi[MSI_CTRL_INT_0_STATUS] = -1;
+	ctlr->dbi[MSI_CTRL_ADDR] = MSI_TARGET_ADDR;
+	ctlr->dbi[MSI_CTRL_UPPER_ADDR] = MSI_TARGET_ADDR >> 32;
+
+	intrenable(ctlr->irq+0, pciinterrupt, ctlr, BUSUNKNOWN, "pci");
+	intrenable(ctlr->irq+1, pciinterrupt, ctlr, BUSUNKNOWN, "pci");
+	intrenable(ctlr->irq+2, pciinterrupt, ctlr, BUSUNKNOWN, "pci");
+	intrenable(ctlr->irq+3, pciinterrupt, ctlr, BUSUNKNOWN, "pci");
+
+	ctlr->dbi[MSI_CAP_ID] |= PCI_MSI_ENABLE;
+}
+
+void
+pciintrenable(int tbdf, void (*f)(Ureg*, void*), void *a)
+{
+	Ctlr *ctlr;
+	Intvec *vec;
+	Pcidev *p;
+
+	ctlr = bus2ctlr(BUSBNO(tbdf));
+	if(ctlr == nil){
+		print("pciintrenable: %T: unknown controller\n", tbdf);
+		return;
+	}
+
+	if((p = pcimatchtbdf(tbdf)) == nil){
+		print("pciintrenable: %T: unknown device\n", tbdf);
+		return;
+	}
+	if(pcimsidisable(p) < 0){
+		print("pciintrenable: %T: device doesnt support vec\n", tbdf);
+		return;
+	}
+
+	ilock(ctlr);
+	for(vec = ctlr->vec; vec < &ctlr->vec[nelem(ctlr->vec)]; vec++){
+		if(vec->p == p){
+			ctlr->dbi[MSI_CTRL_INT_0_EN] &= ~(1 << (vec - ctlr->vec));
+			vec->p = nil;
+			break;
+		}
+	}
+	for(vec = ctlr->vec; vec < &ctlr->vec[nelem(ctlr->vec)]; vec++){
+		if(vec->p == nil){
+			vec->p = p;
+			vec->a = a;
+			vec->f = f;
+			break;
+		}
+	}
+	iunlock(ctlr);
+
+	if(vec >= &ctlr->vec[nelem(ctlr->vec)]){
+		print("pciintrenable: %T: out of isr slots\n", tbdf);
+		return;
+	}
+	ctlr->dbi[MSI_CTRL_INT_0_EN] |= (1 << (vec - ctlr->vec));
+	pcimsienable(p, MSI_TARGET_ADDR, vec - ctlr->vec);
+}
+
+void
+pciintrdisable(int tbdf, void (*f)(Ureg*, void*), void *a)
+{
+	Ctlr *ctlr;
+	Intvec *vec;
+
+	ctlr = bus2ctlr(BUSBNO(tbdf));
+	if(ctlr == nil){
+		print("pciintrenable: %T: unknown controller\n", tbdf);
+		return;
+	}
+
+	ilock(ctlr);
+	for(vec = ctlr->vec; vec < &ctlr->vec[nelem(ctlr->vec)]; vec++){
+		if(vec->p == nil)
+			continue;
+		if(vec->p->tbdf == tbdf && vec->f == f && vec->a == a){
+			ctlr->dbi[MSI_CTRL_INT_0_EN] &= ~(1 << (vec - ctlr->vec));
+			vec->f = nil;
+			vec->a = nil;
+			vec->p = nil;
+			break;
+		}
+	}
+	iunlock(ctlr);
+}
+
+static void
+rootinit(Ctlr *ctlr)
+{
+	uvlong base;
+	ulong ioa;
+
+	iatuinit(ctlr);
+
+	ctlr->cfg = vmap(ctlr->cfg_base, ctlr->cfg_size);
+	if(ctlr->cfg == nil)
+		return;
+
+	ctlr->dbi[MISC_CONTROL_1] |= DBI_RO_WR_EN;
+
+	/* bus number */
+	ctlr->dbi[PciPBN/4] &= ~0xFFFFFF;
+	ctlr->dbi[PciPBN/4] |= ctlr->bno | (ctlr->bno+1)<<8 | ctlr->ubn<<16;
+
+	/* command */
+	ctlr->dbi[PciPCR/4] &= ~0xFFFF;
+	ctlr->dbi[PciPCR/4] |= IOen | MEMen | MASen | SErrEn;
+
+	/* device class/subclass */
+	ctlr->dbi[PciRID/4] &= ~0xFFFF0000;
+	ctlr->dbi[PciRID/4] |=  0x06040000;
+
+	ctlr->dbi[PciBAR0/4] = 0;
+	ctlr->dbi[PciBAR1/4] = 0;
+
+	ctlr->dbi[MISC_CONTROL_1] &= ~DBI_RO_WR_EN;
+
+	ctlr->ubn = pciscan(ctlr->bno, &ctlr->bridge, nil);
+	if(ctlr->bridge == nil || ctlr->bridge->bridge == nil)
+		return;
+
+	pciintrinit(ctlr);
+
+	iatucfg(ctlr, 1<<IATU_INDEX_SHIFT, CTRL_1_TYPE_IO, ctlr->io_base, ctlr->io_base, ctlr->io_size);
+	iatucfg(ctlr, 2<<IATU_INDEX_SHIFT, CTRL_1_TYPE_MEM, ctlr->mem_base, ctlr->mem_base, ctlr->mem_size);
+
+	ioa = ctlr->io_base;
+	base = ctlr->mem_base;
+	pcibusmap(ctlr->bridge, &base, &ioa, 1);
+
+	pcihinv(ctlr->bridge);
+}
+
+static void
+pcicfginit(void)
+{
+	fmtinstall('T', tbdffmt);
+	rootinit(&ctlrs[0]);
+	rootinit(&ctlrs[1]);
+}
+
+enum {
+	SRC_PCIEPHY_RCR		= 0x2C/4,
+	SRC_PCIE2_RCR		= 0x48/4,
+		PCIE_CTRL_APP_XFER_PENDING	= 1<<16,
+		PCIE_CTRL_APP_UNLOCK_MSG	= 1<<15,
+		PCIE_CTRL_SYS_INT		= 1<<14,
+		PCIE_CTRL_CFG_L1_AUX		= 1<<12,
+		PCIE_CTRL_APPS_TURNOFF		= 1<<11,
+		PCIE_CTRL_APPS_PME		= 1<<10,
+		PCIE_CTRL_APPS_EXIT		= 1<<9,
+		PCIE_CTRL_APPS_ENTER		= 1<<8,
+		PCIE_CTRL_APPS_READY		= 1<<7,
+		PCIE_CTRL_APPS_EN		= 1<<6,
+		PCIE_CTRL_APPS_RST		= 1<<5,
+		PCIE_CTRL_APPS_CLK_REQ		= 1<<4,
+		PCIE_PERST			= 1<<3,
+		PCIE_BTN			= 1<<2,
+		PCIE_G_RST			= 1<<1,
+		PCIE_PHY_POWER_ON_RESET_N	= 1<<0,
+};
+
+static u32int *resetc = (u32int*)(VIRTIO + 0x390000);
+
+void
+pciimxlink(void)
+{
+	resetc[SRC_PCIEPHY_RCR] |= PCIE_BTN | PCIE_G_RST;
+	resetc[SRC_PCIE2_RCR] |= PCIE_BTN | PCIE_G_RST;
+
+	resetc[SRC_PCIEPHY_RCR] |= PCIE_CTRL_APPS_EN;
+	resetc[SRC_PCIE2_RCR] |= PCIE_CTRL_APPS_EN;
+
+	setclkgate("pcie_clk_rst.auxclk", 0);
+	setclkgate("pcie2_clk_rst.auxclk", 0);
+
+	iomuxpad("pad_ecspi1_mosi", "gpio5_io07", "~LVTTL ~HYS ~PUE ~ODE FAST 45_OHM");
+	iomuxpad("pad_sai5_rxd2", "gpio3_io23", "~LVTTL ~HYS ~PUE ~ODE FAST 45_OHM");
+
+	gpioout(GPIO_PIN(5, 7), 0);
+	gpioout(GPIO_PIN(3, 23), 0);
+
+	powerup("pcie");
+	powerup("pcie2");
+
+	/* configure monitor CLK2 output internal reference clock for PCIE1 */
+	setclkrate("ccm_analog_pllout", "system_pll1_clk", 100*Mhz);
+	delay(10);
+
+	/* PCIE1_REF_USE_PAD=0 */
+	iomuxgpr(14, 0<<9, 1<<9);
+
+	/* PCIE2_REF_USE_PAD=1 */
+	iomuxgpr(16, 1<<9, 1<<9);
+
+	/* PCIE1_CTRL_DEVICE_TYPE=ROOT, PCIE2_CTRL_DEVICE_TYPE=ROOT */
+	iomuxgpr(12, 4<<12 | 4<<8, 0xF<<12 | 0xF<<8);
+
+	setclkrate("ccm_pcie1_ctrl_clk_root", "system_pll2_div4", 250*Mhz);
+	setclkrate("ccm_pcie2_ctrl_clk_root", "system_pll2_div4", 250*Mhz);
+
+	setclkrate("pcie_clk_rst.auxclk", "system_pll2_div10", 100*Mhz);
+	setclkrate("pcie2_clk_rst.auxclk", "system_pll2_div10", 100*Mhz);
+
+	setclkrate("pcie_phy.ref_alt_clk_p", "system_pll2_div10", 100*Mhz);
+	setclkrate("pcie2_phy.ref_alt_clk_p", "system_pll2_div10", 100*Mhz);
+
+	setclkgate("pcie_clk_rst.auxclk", 1);
+	setclkgate("pcie2_clk_rst.auxclk", 1);
+
+	/* PCIE1_CLKREQ_B_OVERRIDE=0 PCIE1_CLKREQ_B_OVERRIDE_EN=1 */
+	iomuxgpr(14, 1<<10, 3<<10);
+
+	/* PCIE2_CLKREQ_B_OVERRIDE=0 PCIE2_CLKREQ_B_OVERRIDE_EN=1 */
+	iomuxgpr(16, 1<<10, 3<<10);
+
+	delay(100);
+	gpioout(GPIO_PIN(5, 7), 1);
+	gpioout(GPIO_PIN(3, 23), 1);
+	delay(1);
+
+	resetc[SRC_PCIEPHY_RCR] &= ~(PCIE_BTN | PCIE_G_RST);
+	resetc[SRC_PCIE2_RCR] &= ~(PCIE_BTN | PCIE_G_RST);
+
+	pcicfginit();
+}
--- a/sys/src/9/imx8/reform
+++ b/sys/src/9/imx8/reform
@@ -27,6 +27,7 @@
 	ethermedium
 	loopbackmedium
 	i2cimx		devi2c
+	pciimx		pci
 ip
 	tcp
 	udp
@@ -44,6 +45,7 @@
 	uartimx
 	iomux
 	sdmmc	usdhc
+	sdnvme	pci
 port
 	int cpuserver = 0;
 bootdir
--- a/sys/src/9/port/pci.c
+++ b/sys/src/9/port/pci.c
@@ -16,6 +16,7 @@
 };
 
 int pcimaxdno;
+Pcidev *pciparentdev;
 
 static Lock pcicfglock;
 static Pcidev *pcilist, **pcitail;
@@ -113,6 +114,8 @@
 	int data;
 
 	ilock(&pcicfglock);
+	pciparentdev = p->parent;
+
 	data = pcicfgrw8(p->tbdf, rno, 0, 1);
 	iunlock(&pcicfglock);
 
@@ -122,6 +125,8 @@
 pcicfgw8(Pcidev* p, int rno, int data)
 {
 	ilock(&pcicfglock);
+	pciparentdev = p->parent;
+
 	pcicfgrw8(p->tbdf, rno, data, 0);
 	iunlock(&pcicfglock);
 }
@@ -131,6 +136,8 @@
 	int data;
 
 	ilock(&pcicfglock);
+	pciparentdev = p->parent;
+
 	data = pcicfgrw16(p->tbdf, rno, 0, 1);
 	iunlock(&pcicfglock);
 
@@ -140,6 +147,8 @@
 pcicfgw16(Pcidev* p, int rno, int data)
 {
 	ilock(&pcicfglock);
+	pciparentdev = p->parent;
+
 	pcicfgrw16(p->tbdf, rno, data, 0);
 	iunlock(&pcicfglock);
 }
@@ -149,6 +158,8 @@
 	int data;
 
 	ilock(&pcicfglock);
+	pciparentdev = p->parent;
+
 	data = pcicfgrw32(p->tbdf, rno, 0, 1);
 	iunlock(&pcicfglock);
 
@@ -158,6 +169,8 @@
 pcicfgw32(Pcidev* p, int rno, int data)
 {
 	ilock(&pcicfglock);
+	pciparentdev = p->parent;
+
 	pcicfgrw32(p->tbdf, rno, data, 0);
 	iunlock(&pcicfglock);
 }
@@ -169,6 +182,7 @@
 	int v;
 
 	ilock(&pcicfglock);
+	pciparentdev = p->parent;
 
 	v = pcicfgrw32(p->tbdf, rno, 0, 1);
 	pcicfgrw32(p->tbdf, rno, -1, 0);
@@ -206,6 +220,8 @@
 pcisetbar(Pcidev *p, int rno, uvlong bar)
 {
 	ilock(&pcicfglock);
+	pciparentdev = p->parent;
+
 	pcicfgrw32(p->tbdf, rno, bar, 0);
 	if((bar&7) == 4 && rno >= PciBAR0 && rno < PciBAR0+4*(nelem(p->mem)-1))
 		pcicfgrw32(p->tbdf, rno+4, bar>>32, 0);
@@ -216,6 +232,8 @@
 pcisetwin(Pcidev *p, uvlong base, uvlong limit)
 {
 	ilock(&pcicfglock);
+	pciparentdev = p->parent;
+
 	if(base & 1){
 		pcicfgrw16(p->tbdf, PciIBR, (limit & 0xF000)|((base & 0xF000)>>8), 0);
 		pcicfgrw32(p->tbdf, PciIUBR, (limit & 0xFFFF0000)|(base>>16), 0);
@@ -534,6 +552,8 @@
 			tbdf = MKBUS(BusPCI, bno, dno, fno);
 
 			lock(&pcicfglock);
+			pciparentdev = parent;
+
 			l = pcicfgrw32(tbdf, PciVID, 0, 1);
 			unlock(&pcicfglock);
 
@@ -540,6 +560,7 @@
 			if(l == 0xFFFFFFFF || l == 0)
 				continue;
 			p = pcidevalloc();
+			p->parent = parent;
 			p->tbdf = tbdf;
 			p->vid = l;
 			p->did = l>>16;
@@ -622,7 +643,6 @@
 				break;
 			}
 
-			p->parent = parent;
 			if(head != nil)
 				*tail = p;
 			else
--- a/sys/src/9/port/pci.h
+++ b/sys/src/9/port/pci.h
@@ -223,6 +223,7 @@
 };
 
 extern int pcimaxdno;
+extern Pcidev *pciparentdev;
 
 extern void pcidevfree(Pcidev* pcidev);