blob: 55a6f1cac156df49fc50fe86cab5bf23ffdad9ed [file] [log] [blame]
/****************************************************************************
*
* The MIT License (MIT)
*
* Copyright (c) 2014 - 2020 Vivante Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*****************************************************************************
*
* The GPL License (GPL)
*
* Copyright (C) 2014 - 2020 Vivante Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*****************************************************************************
*
* Note: This software is released under dual MIT and GPL licenses. A
* recipient may use this file under the terms of either the MIT license or
* GPL License. If you wish to use only one license not the other, you can
* indicate your decision by deleting one of the above license notices in your
* version of this file.
*
*****************************************************************************/
#include "gc_hal_kernel_linux.h"
#include "gc_hal_kernel_platform.h"
/* Disable MSI for internal FPGA build except PPC */
#if gcdFPGA_BUILD && !defined(CONFIG_PPC)
#define USE_MSI 0
#else
#define USE_MSI 1
#endif
gceSTATUS
_AdjustParam(
IN gcsPLATFORM *Platform,
OUT gcsMODULE_PARAMETERS *Args
);
gceSTATUS
_GetGPUPhysical(
IN gcsPLATFORM * Platform,
IN gctPHYS_ADDR_T CPUPhysical,
OUT gctPHYS_ADDR_T *GPUPhysical
);
static struct _gcsPLATFORM_OPERATIONS default_ops =
{
.adjustParam = _AdjustParam,
.getGPUPhysical = _GetGPUPhysical,
};
#if USE_LINUX_PCIE
#define MAX_PCIE_DEVICE 4
#define MAX_PCIE_BAR 6
typedef struct _gcsBARINFO
{
gctPHYS_ADDR_T base;
gctSIZE_T size;
gctPOINTER logical;
}
gcsBARINFO, *gckBARINFO;
struct _gcsPCIEInfo
{
gcsBARINFO bar[MAX_PCIE_BAR];
struct pci_dev *pdev;
gctPHYS_ADDR_T sram_bases[gcvSRAM_EXT_COUNT];
gctPHYS_ADDR_T sram_gpu_bases[gcvSRAM_EXT_COUNT];
uint32_t sram_sizes[gcvSRAM_EXT_COUNT];
int sram_bars[gcvSRAM_EXT_COUNT];
int sram_offsets[gcvSRAM_EXT_COUNT];
};
struct _gcsPLATFORM_PCIE
{
struct _gcsPLATFORM base;
struct _gcsPCIEInfo pcie_info[MAX_PCIE_DEVICE];
unsigned int device_number;
};
struct _gcsPLATFORM_PCIE default_platform =
{
.base =
{
.name = __FILE__,
.ops = &default_ops,
},
};
void
_QueryBarInfo(
struct pci_dev *Pdev,
gctPHYS_ADDR_T *BarAddr,
gctSIZE_T *BarSize,
gctUINT BarNum
)
{
gctUINT addr;
gctUINT size;
/* Read the bar address */
if (pci_read_config_dword(Pdev, PCI_BASE_ADDRESS_0 + BarNum * 0x4, &addr) < 0)
{
return;
}
/* Read the bar size */
if (pci_write_config_dword(Pdev, PCI_BASE_ADDRESS_0 + BarNum * 0x4, 0xffffffff) < 0)
{
return;
}
if (pci_read_config_dword(Pdev, PCI_BASE_ADDRESS_0 + BarNum * 0x4, &size) < 0)
{
return;
}
size &= 0xfffffff0;
size = ~size;
size += 1;
/* Write back the bar address */
if (pci_write_config_dword(Pdev, PCI_BASE_ADDRESS_0 + BarNum * 0x4, addr) < 0)
{
return;
}
gcmkPRINT("Bar%d addr=0x%x size=0x%x", BarNum, addr, size);
*BarAddr = addr;
*BarSize = size;
}
#else
static struct _gcsPLATFORM default_platform =
{
.name = __FILE__,
.ops = &default_ops,
};
#endif
gceSTATUS
_AdjustParam(
IN gcsPLATFORM *Platform,
OUT gcsMODULE_PARAMETERS *Args
)
{
#if USE_LINUX_PCIE
struct _gcsPLATFORM_PCIE *pcie_platform = (struct _gcsPLATFORM_PCIE *)Platform;
struct pci_dev *pdev = pcie_platform->pcie_info[0].pdev;
int irqline = pdev->irq;
unsigned int i;
unsigned int dev_index, core_index = 0;
int sram_bar, sram_offset;
if (Args->irqs[gcvCORE_2D] != -1)
{
Args->irqs[gcvCORE_2D] = irqline;
}
if (Args->irqs[gcvCORE_MAJOR] != -1)
{
Args->irqs[gcvCORE_MAJOR] = irqline;
}
for (dev_index = 0; dev_index < pcie_platform->device_number; dev_index++)
{
struct pci_dev * pcieDev = pcie_platform->pcie_info[dev_index].pdev;
for (i = 0; i < MAX_PCIE_BAR; i++)
{
_QueryBarInfo(
pcieDev,
&pcie_platform->pcie_info[dev_index].bar[i].base,
&pcie_platform->pcie_info[dev_index].bar[i].size,
i
);
}
for (i = 0; i < gcvCORE_COUNT; i++)
{
if (Args->bars[i] != -1)
{
Args->irqs[core_index] = pcieDev->irq;
/* VIV bitfile: Merge last 4 cores to last one bar to support 8 cores. */
if (Args->bars[i] == 5)
{
Args->registerBasesMapped[4] =
pcie_platform->pcie_info[dev_index].bar[i].logical =
(gctPOINTER)pci_iomap(pcieDev, Args->bars[i], 0x500000);
Args->registerBasesMapped[5] = Args->registerBasesMapped[4] + 0x100000;
Args->registerBasesMapped[6] = Args->registerBasesMapped[5] + 0x100000;
Args->registerBasesMapped[7] = Args->registerBasesMapped[6] + 0x100000;
Args->irqs[5] =
Args->irqs[6] =
Args->irqs[7] = Args->irqs[4];
continue;
}
if (Args->regOffsets[i])
{
gcmkASSERT(Args->regOffsets[i] + Args->registerSizes[core_index]
< pcie_platform->pcie_info[dev_index].bar[Args->bars[i]].size);
}
Args->registerBasesMapped[core_index] =
pcie_platform->pcie_info[dev_index].bar[i].logical =
(gctPOINTER)pci_iomap(pcieDev, Args->bars[i], Args->registerSizes[core_index] + Args->regOffsets[i]) + Args->regOffsets[i];
core_index++;
}
}
for (i = 0; i < gcvSRAM_EXT_COUNT; i++)
{
pcie_platform->pcie_info[dev_index].sram_bases[i] =
pcie_platform->pcie_info[dev_index].sram_gpu_bases[i] = Args->extSRAMBases[i];
pcie_platform->pcie_info[dev_index].sram_sizes[i] = Args->extSRAMSizes[i];
pcie_platform->pcie_info[dev_index].sram_bars[i] = sram_bar = Args->sRAMBars[i];
pcie_platform->pcie_info[dev_index].sram_offsets[i] = sram_offset = Args->sRAMOffsets[i];
/* Get CPU view SRAM base address from bar address and bar inside offset. */
if (sram_bar != -1 && sram_offset != -1)
{
pcie_platform->pcie_info[dev_index].sram_bases[i] = Args->extSRAMBases[i]
= pcie_platform->pcie_info[dev_index].bar[sram_bar].base
+ sram_offset;
}
}
}
Args->contiguousRequested = gcvTRUE;
#endif
return gcvSTATUS_OK;
}
gceSTATUS
_GetGPUPhysical(
IN gcsPLATFORM * Platform,
IN gctPHYS_ADDR_T CPUPhysical,
OUT gctPHYS_ADDR_T *GPUPhysical
)
{
#if USE_LINUX_PCIE
struct _gcsPLATFORM_PCIE *pcie_platform = (struct _gcsPLATFORM_PCIE *)Platform;
/* Only support 1 external shared SRAM currently. */
gctPHYS_ADDR_T sram_base = pcie_platform->pcie_info[0].sram_bases[0];
gctPHYS_ADDR_T sram_gpu_base = pcie_platform->pcie_info[0].sram_gpu_bases[0];
uint32_t sram_size = pcie_platform->pcie_info[0].sram_sizes[0];
/* TODO: We should always set axi sram size by insmod parameters, never from feature database. */
if (!sram_size && Platform->dev && Platform->dev->extSRAMSizes[0])
{
sram_size = Platform->dev->extSRAMSizes[0];
}
if (sram_base != gcvINVALID_PHYSICAL_ADDRESS && sram_gpu_base != gcvINVALID_PHYSICAL_ADDRESS && sram_size)
{
if ((CPUPhysical >= sram_base) && (CPUPhysical < (sram_base + sram_size)))
{
*GPUPhysical = CPUPhysical - sram_base + sram_gpu_base;
}
else
{
*GPUPhysical = CPUPhysical;
}
}
else
#endif
{
*GPUPhysical = CPUPhysical;
}
return gcvSTATUS_OK;
}
#if USE_LINUX_PCIE
static const struct pci_device_id vivpci_ids[] = {
{
.class = 0x000000,
.class_mask = 0x000000,
.vendor = 0x10ee,
.device = 0x7012,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 0
}, { /* End: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, vivpci_ids);
static int gpu_sub_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
static u64 dma_mask = DMA_BIT_MASK(40);
#else
static u64 dma_mask = DMA_40BIT_MASK;
#endif
gcmkPRINT("PCIE DRIVER PROBED");
if (pci_enable_device(pdev)) {
printk(KERN_ERR "galcore: pci_enable_device() failed.\n");
}
if (pci_set_dma_mask(pdev, dma_mask)) {
printk(KERN_ERR "galcore: Failed to set DMA mask.\n");
}
pci_set_master(pdev);
if (pci_request_regions(pdev, "galcore")) {
printk(KERN_ERR "galcore: Failed to get ownership of BAR region.\n");
}
#if USE_MSI
if (pci_enable_msi(pdev)) {
printk(KERN_ERR "galcore: Failed to enable MSI.\n");
}
#endif
default_platform.pcie_info[default_platform.device_number++].pdev = pdev;
return 0;
}
static void gpu_sub_remove(struct pci_dev *pdev)
{
pci_set_drvdata(pdev, NULL);
#if USE_MSI
pci_disable_msi(pdev);
#endif
pci_clear_master(pdev);
pci_release_regions(pdev);
pci_disable_device(pdev);
return;
}
static struct pci_driver gpu_pci_subdriver = {
.name = DEVICE_NAME,
.id_table = vivpci_ids,
.probe = gpu_sub_probe,
.remove = gpu_sub_remove
};
#endif
static struct platform_device *default_dev;
int gckPLATFORM_Init(struct platform_driver *pdrv,
struct _gcsPLATFORM **platform)
{
int ret;
default_dev = platform_device_alloc(pdrv->driver.name, -1);
if (!default_dev) {
printk(KERN_ERR "galcore: platform_device_alloc failed.\n");
return -ENOMEM;
}
/* Add device */
ret = platform_device_add(default_dev);
if (ret) {
printk(KERN_ERR "galcore: platform_device_add failed.\n");
goto put_dev;
}
*platform = (gcsPLATFORM *)&default_platform;
#if USE_LINUX_PCIE
ret = pci_register_driver(&gpu_pci_subdriver);
#endif
return 0;
put_dev:
platform_device_put(default_dev);
return ret;
}
int gckPLATFORM_Terminate(struct _gcsPLATFORM *platform)
{
if (default_dev) {
platform_device_unregister(default_dev);
default_dev = NULL;
}
#if USE_LINUX_PCIE
{
unsigned int dev_index;
struct _gcsPLATFORM_PCIE *pcie_platform = (struct _gcsPLATFORM_PCIE *)platform;
for (dev_index = 0; dev_index < pcie_platform->device_number; dev_index++)
{
unsigned int i;
for (i = 0; i < MAX_PCIE_BAR; i++)
{
if (pcie_platform->pcie_info[dev_index].bar[i].logical != 0)
{
pci_iounmap(pcie_platform->pcie_info[dev_index].pdev, pcie_platform->pcie_info[dev_index].bar[i].logical);
}
}
}
pci_unregister_driver(&gpu_pci_subdriver);
}
#endif
return 0;
}