diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2022-07-03 11:36:50 +0000 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2022-07-03 11:36:50 +0000 |
commit | 548a48d1561dd77dbc082dddf0f9a2776ee91914 (patch) | |
tree | 5d8df94bd49aa1d186b2a2e2cf97f79f757e148f /sys/src/9/imx8/pciimx.c | |
parent | e39d9249076e9a95e97b33313be1ab2e23095f1d (diff) |
imx8: pcie and nvme support
Diffstat (limited to 'sys/src/9/imx8/pciimx.c')
-rw-r--r-- | sys/src/9/imx8/pciimx.c | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/sys/src/9/imx8/pciimx.c b/sys/src/9/imx8/pciimx.c new file mode 100644 index 000000000..a18b07c9b --- /dev/null +++ b/sys/src/9/imx8/pciimx.c @@ -0,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(); +} |