| /* | 
|  * Faraday FTPCI100 PCI Bridge Controller Device Driver Implementation | 
|  * | 
|  * Copyright (C) 2011 Andes Technology Corporation | 
|  * Gavin Guo, Andes Technology Corporation <gavinguo@andestech.com> | 
|  * Macpaul Lin, Andes Technology Corporation <macpaul@andestech.com> | 
|  * | 
|  * SPDX-License-Identifier:    GPL-2.0+ | 
|  */ | 
| #include <common.h> | 
| #include <malloc.h> | 
| #include <pci.h> | 
|   | 
| #include <faraday/ftpci100.h> | 
|   | 
| #include <asm/io.h> | 
| #include <asm/types.h> /* u32, u16.... used by pci.h */ | 
|   | 
| struct ftpci100_data { | 
|     unsigned int reg_base; | 
|     unsigned int io_base; | 
|     unsigned int mem_base; | 
|     unsigned int mmio_base; | 
|     unsigned int ndevs; | 
| }; | 
|   | 
| static struct pci_config devs[FTPCI100_MAX_FUNCTIONS]; | 
| static struct pci_controller local_hose; | 
|   | 
| static void setup_pci_bar(unsigned int bus, unsigned int dev, unsigned func, | 
|         unsigned char header, struct ftpci100_data *priv) | 
| { | 
|     struct pci_controller *hose = (struct pci_controller *)&local_hose; | 
|     unsigned int i, tmp32, bar_no, iovsmem = 1; | 
|     pci_dev_t dev_nu; | 
|   | 
|     /* A device is present, add an entry to the array */ | 
|     devs[priv->ndevs].bus = bus; | 
|     devs[priv->ndevs].dev = dev; | 
|     devs[priv->ndevs].func = func; | 
|   | 
|     dev_nu = PCI_BDF(bus, dev, func); | 
|   | 
|     if ((header & 0x7f) == 0x01) | 
|         /* PCI-PCI Bridge */ | 
|         bar_no = 2; | 
|     else | 
|         bar_no = 6; | 
|   | 
|     /* Allocate address spaces by configuring BARs */ | 
|     for (i = 0; i < bar_no; i++) { | 
|         pci_hose_write_config_dword(hose, dev_nu, | 
|                     PCI_BASE_ADDRESS_0 + i * 4, 0xffffffff); | 
|         pci_hose_read_config_dword(hose, dev_nu, | 
|                     PCI_BASE_ADDRESS_0 + i * 4, &tmp32); | 
|   | 
|         if (tmp32 == 0x0) | 
|             continue; | 
|   | 
|         /* IO space */ | 
|         if (tmp32 & 0x1) { | 
|             iovsmem = 0; | 
|             unsigned int size_mask = ~(tmp32 & 0xfffffffc); | 
|   | 
|             if (priv->io_base & size_mask) | 
|                 priv->io_base = (priv->io_base & ~size_mask) + \ | 
|                          size_mask + 1; | 
|   | 
|             devs[priv->ndevs].bar[i].addr = priv->io_base; | 
|             devs[priv->ndevs].bar[i].size = size_mask + 1; | 
|   | 
|             pci_hose_write_config_dword(hose, dev_nu, | 
|                     PCI_BASE_ADDRESS_0 + i * 4, | 
|                     priv->io_base); | 
|   | 
|             debug("Allocated IO address 0x%X-" \ | 
|                 "0x%X for Bus %d, Device %d, Function %d\n", | 
|                 priv->io_base, | 
|                 priv->io_base + size_mask, bus, dev, func); | 
|   | 
|             priv->io_base += size_mask + 1; | 
|         } else { | 
|             /* Memory space */ | 
|             unsigned int is_64bit = ((tmp32 & 0x6) == 0x4); | 
|             unsigned int is_pref = tmp32 & 0x8; | 
|             unsigned int size_mask = ~(tmp32 & 0xfffffff0); | 
|             unsigned int alloc_base; | 
|             unsigned int *addr_mem_base; | 
|   | 
|             if (is_pref) | 
|                 addr_mem_base = &priv->mem_base; | 
|             else | 
|                 addr_mem_base = &priv->mmio_base; | 
|   | 
|             alloc_base = *addr_mem_base; | 
|   | 
|             if (alloc_base & size_mask) | 
|                 alloc_base = (alloc_base & ~size_mask) \ | 
|                         + size_mask + 1; | 
|   | 
|             pci_hose_write_config_dword(hose, dev_nu, | 
|                     PCI_BASE_ADDRESS_0 + i * 4, alloc_base); | 
|   | 
|             debug("Allocated %s address 0x%X-" \ | 
|                 "0x%X for Bus %d, Device %d, Function %d\n", | 
|                 is_pref ? "MEM" : "MMIO", alloc_base, | 
|                 alloc_base + size_mask, bus, dev, func); | 
|   | 
|             devs[priv->ndevs].bar[i].addr = alloc_base; | 
|             devs[priv->ndevs].bar[i].size = size_mask + 1; | 
|   | 
|             debug("BAR address  BAR size\n"); | 
|             debug("%010x  %08d\n", | 
|                 devs[priv->ndevs].bar[0].addr, | 
|                 devs[priv->ndevs].bar[0].size); | 
|   | 
|             alloc_base += size_mask + 1; | 
|             *addr_mem_base = alloc_base; | 
|   | 
|             if (is_64bit) { | 
|                 i++; | 
|                 pci_hose_write_config_dword(hose, dev_nu, | 
|                     PCI_BASE_ADDRESS_0 + i * 4, 0x0); | 
|             } | 
|         } | 
|     } | 
|   | 
|     /* Enable Bus Master, Memory Space, and IO Space */ | 
|     pci_hose_read_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, &tmp32); | 
|     pci_hose_write_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, 0x08); | 
|     pci_hose_read_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, &tmp32); | 
|   | 
|     pci_hose_read_config_dword(hose, dev_nu, PCI_COMMAND, &tmp32); | 
|   | 
|     tmp32 &= 0xffff; | 
|   | 
|     if (iovsmem == 0) | 
|         tmp32 |= 0x5; | 
|     else | 
|         tmp32 |= 0x6; | 
|   | 
|     pci_hose_write_config_dword(hose, dev_nu, PCI_COMMAND, tmp32); | 
| } | 
|   | 
| static void pci_bus_scan(struct ftpci100_data *priv) | 
| { | 
|     struct pci_controller *hose = (struct pci_controller *)&local_hose; | 
|     unsigned int bus, dev, func; | 
|     pci_dev_t dev_nu; | 
|     unsigned int data32; | 
|     unsigned int tmp; | 
|     unsigned char header; | 
|     unsigned char int_pin; | 
|     unsigned int niobars; | 
|     unsigned int nmbars; | 
|   | 
|     priv->ndevs = 1; | 
|   | 
|     nmbars = 0; | 
|     niobars = 0; | 
|   | 
|     for (bus = 0; bus < MAX_BUS_NUM; bus++) | 
|         for (dev = 0; dev < MAX_DEV_NUM; dev++) | 
|             for (func = 0; func < MAX_FUN_NUM; func++) { | 
|                 dev_nu = PCI_BDF(bus, dev, func); | 
|                 pci_hose_read_config_dword(hose, dev_nu, | 
|                             PCI_VENDOR_ID, &data32); | 
|   | 
|                 /* | 
|                  * some broken boards return 0 or ~0, | 
|                  * if a slot is empty. | 
|                  */ | 
|                 if (data32 == 0xffffffff || | 
|                     data32 == 0x00000000 || | 
|                     data32 == 0x0000ffff || | 
|                     data32 == 0xffff0000) | 
|                     continue; | 
|   | 
|                 pci_hose_read_config_dword(hose, dev_nu, | 
|                             PCI_HEADER_TYPE, &tmp); | 
|                 header = (unsigned char)tmp; | 
|                 setup_pci_bar(bus, dev, func, header, priv); | 
|   | 
|                 devs[priv->ndevs].v_id = (u16)(data32 & \ | 
|                                 0x0000ffff); | 
|   | 
|                 devs[priv->ndevs].d_id = (u16)((data32 & \ | 
|                             0xffff0000) >> 16); | 
|   | 
|                 /* Figure out what INTX# line the card uses */ | 
|                 pci_hose_read_config_byte(hose, dev_nu, | 
|                         PCI_INTERRUPT_PIN, &int_pin); | 
|   | 
|                 /* assign the appropriate irq line */ | 
|                 if (int_pin > PCI_IRQ_LINES) { | 
|                     printf("more irq lines than expect\n"); | 
|                 } else if (int_pin != 0) { | 
|                     /* This device uses an interrupt line */ | 
|                     devs[priv->ndevs].pin = int_pin; | 
|                 } | 
|   | 
|                 pci_hose_read_config_dword(hose, dev_nu, | 
|                         PCI_CLASS_DEVICE, &data32); | 
|   | 
|                 debug("%06d  %03d  %03d  " \ | 
|                     "%04d  %08x  %08x  " \ | 
|                     "%03d  %08x  %06d  %08x\n", | 
|                     priv->ndevs, devs[priv->ndevs].bus, | 
|                     devs[priv->ndevs].dev, | 
|                     devs[priv->ndevs].func, | 
|                     devs[priv->ndevs].d_id, | 
|                     devs[priv->ndevs].v_id, | 
|                     devs[priv->ndevs].pin, | 
|                     devs[priv->ndevs].bar[0].addr, | 
|                     devs[priv->ndevs].bar[0].size, | 
|                     data32 >> 8); | 
|   | 
|                 priv->ndevs++; | 
|             } | 
| } | 
|   | 
| static void ftpci_preinit(struct ftpci100_data *priv) | 
| { | 
|     struct ftpci100_ahbc *ftpci100; | 
|     struct pci_controller *hose = (struct pci_controller *)&local_hose; | 
|     u32 pci_config_addr; | 
|     u32 pci_config_data; | 
|   | 
|     priv->reg_base = CONFIG_FTPCI100_BASE; | 
|     priv->io_base = CONFIG_FTPCI100_BASE + CONFIG_FTPCI100_IO_SIZE; | 
|     priv->mmio_base = CONFIG_FTPCI100_MEM_BASE; | 
|     priv->mem_base = CONFIG_FTPCI100_MEM_BASE + CONFIG_FTPCI100_MEM_SIZE; | 
|   | 
|     ftpci100 = (struct ftpci100_ahbc *)priv->reg_base; | 
|   | 
|     pci_config_addr = (u32) &ftpci100->conf; | 
|     pci_config_data = (u32) &ftpci100->data; | 
|   | 
|     /* print device name */ | 
|     printf("FTPCI100\n"); | 
|   | 
|     /* dump basic configuration */ | 
|     debug("%s: Config addr is %08X, data port is %08X\n", | 
|         __func__, pci_config_addr, pci_config_data); | 
|   | 
|     /* PCI memory space */ | 
|     pci_set_region(hose->regions + 0, | 
|         CONFIG_PCI_MEM_BUS, | 
|         CONFIG_PCI_MEM_PHYS, | 
|         CONFIG_PCI_MEM_SIZE, | 
|         PCI_REGION_MEM); | 
|     hose->region_count++; | 
|   | 
|     /* PCI IO space */ | 
|     pci_set_region(hose->regions + 1, | 
|         CONFIG_PCI_IO_BUS, | 
|         CONFIG_PCI_IO_PHYS, | 
|         CONFIG_PCI_IO_SIZE, | 
|         PCI_REGION_IO); | 
|     hose->region_count++; | 
|   | 
| #if defined(CONFIG_PCI_SYS_BUS) | 
|     /* PCI System Memory space */ | 
|     pci_set_region(hose->regions + 2, | 
|         CONFIG_PCI_SYS_BUS, | 
|         CONFIG_PCI_SYS_PHYS, | 
|         CONFIG_PCI_SYS_SIZE, | 
|         PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); | 
|     hose->region_count++; | 
| #endif | 
|   | 
|     /* setup indirect read/write function */ | 
|     pci_setup_indirect(hose, pci_config_addr, pci_config_data); | 
|   | 
|     /* register hose */ | 
|     pci_register_hose(hose); | 
| } | 
|   | 
| void pci_ftpci_init(void) | 
| { | 
|     struct ftpci100_data *priv = NULL; | 
|     struct pci_controller *hose = (struct pci_controller *)&local_hose; | 
|     pci_dev_t bridge_num; | 
|   | 
|     struct pci_device_id bridge_ids[] = { | 
|         {FTPCI100_BRIDGE_VENDORID, FTPCI100_BRIDGE_DEVICEID}, | 
|         {0, 0} | 
|     }; | 
|   | 
|     priv = malloc(sizeof(struct ftpci100_data)); | 
|   | 
|     if (!priv) { | 
|         printf("%s(): failed to malloc priv\n", __func__); | 
|         return; | 
|     } | 
|   | 
|     memset(priv, 0, sizeof(struct ftpci100_data)); | 
|   | 
|     ftpci_preinit(priv); | 
|   | 
|     debug("Device  bus  dev  func  deviceID  vendorID  pin  address" \ | 
|         "   size    class\n"); | 
|   | 
|     pci_bus_scan(priv); | 
|   | 
|     /* | 
|      * Setup the PCI Bridge Window to 1GB, | 
|      * it will cause USB OHCI Host controller Unrecoverable Error | 
|      * if it is not set. | 
|      */ | 
|     bridge_num = pci_find_devices(bridge_ids, 0); | 
|     if (bridge_num == -1) { | 
|         printf("PCI Bridge not found\n"); | 
|         return; | 
|     } | 
|     pci_hose_write_config_dword(hose, bridge_num, PCI_MEM_BASE_SIZE1, | 
|                     FTPCI100_BASE_ADR_SIZE(1024)); | 
| } |