mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-21 21:29:49 +07:00
c942fddf87
Based on 3 normalized pattern(s): 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 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 [author] [kishon] [vijay] [abraham] [i] [kishon]@[ti] [com] 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 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 [author] [graeme] [gregory] [gg]@[slimlogic] [co] [uk] [author] [kishon] [vijay] [abraham] [i] [kishon]@[ti] [com] [based] [on] [twl6030]_[usb] [c] [author] [hema] [hk] [hemahk]@[ti] [com] 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 extracted by the scancode license scanner the SPDX license identifier GPL-2.0-or-later has been chosen to replace the boilerplate/reference in 1105 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Reviewed-by: Richard Fontana <rfontana@redhat.com> Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190527070033.202006027@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
318 lines
8.1 KiB
C
318 lines
8.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
|
|
*
|
|
* Original author:
|
|
* Ben Collins <bcollins@ubuntu.com>
|
|
*
|
|
* Additional work by:
|
|
* John Brooks <john.brooks@bluecherry.net>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "solo6x10.h"
|
|
|
|
static int multi_p2m;
|
|
module_param(multi_p2m, uint, 0644);
|
|
MODULE_PARM_DESC(multi_p2m,
|
|
"Use multiple P2M DMA channels (default: no, 6010-only)");
|
|
|
|
static int desc_mode;
|
|
module_param(desc_mode, uint, 0644);
|
|
MODULE_PARM_DESC(desc_mode,
|
|
"Allow use of descriptor mode DMA (default: no, 6010-only)");
|
|
|
|
int solo_p2m_dma(struct solo_dev *solo_dev, int wr,
|
|
void *sys_addr, u32 ext_addr, u32 size,
|
|
int repeat, u32 ext_size)
|
|
{
|
|
dma_addr_t dma_addr;
|
|
int ret;
|
|
|
|
if (WARN_ON_ONCE((unsigned long)sys_addr & 0x03))
|
|
return -EINVAL;
|
|
if (WARN_ON_ONCE(!size))
|
|
return -EINVAL;
|
|
|
|
dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size,
|
|
wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
|
|
if (pci_dma_mapping_error(solo_dev->pdev, dma_addr))
|
|
return -ENOMEM;
|
|
|
|
ret = solo_p2m_dma_t(solo_dev, wr, dma_addr, ext_addr, size,
|
|
repeat, ext_size);
|
|
|
|
pci_unmap_single(solo_dev->pdev, dma_addr, size,
|
|
wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Mutex must be held for p2m_id before calling this!! */
|
|
int solo_p2m_dma_desc(struct solo_dev *solo_dev,
|
|
struct solo_p2m_desc *desc, dma_addr_t desc_dma,
|
|
int desc_cnt)
|
|
{
|
|
struct solo_p2m_dev *p2m_dev;
|
|
unsigned int timeout;
|
|
unsigned int config = 0;
|
|
int ret = 0;
|
|
unsigned int p2m_id = 0;
|
|
|
|
/* Get next ID. According to Softlogic, 6110 has problems on !=0 P2M */
|
|
if (solo_dev->type != SOLO_DEV_6110 && multi_p2m)
|
|
p2m_id = atomic_inc_return(&solo_dev->p2m_count) % SOLO_NR_P2M;
|
|
|
|
p2m_dev = &solo_dev->p2m_dev[p2m_id];
|
|
|
|
if (mutex_lock_interruptible(&p2m_dev->mutex))
|
|
return -EINTR;
|
|
|
|
reinit_completion(&p2m_dev->completion);
|
|
p2m_dev->error = 0;
|
|
|
|
if (desc_cnt > 1 && solo_dev->type != SOLO_DEV_6110 && desc_mode) {
|
|
/* For 6010 with more than one desc, we can do a one-shot */
|
|
p2m_dev->desc_count = p2m_dev->desc_idx = 0;
|
|
config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(p2m_id));
|
|
|
|
solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(p2m_id), desc_dma);
|
|
solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(p2m_id), desc_cnt);
|
|
solo_reg_write(solo_dev, SOLO_P2M_CONFIG(p2m_id), config |
|
|
SOLO_P2M_DESC_MODE);
|
|
} else {
|
|
/* For single descriptors and 6110, we need to run each desc */
|
|
p2m_dev->desc_count = desc_cnt;
|
|
p2m_dev->desc_idx = 1;
|
|
p2m_dev->descs = desc;
|
|
|
|
solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(p2m_id),
|
|
desc[1].dma_addr);
|
|
solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(p2m_id),
|
|
desc[1].ext_addr);
|
|
solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(p2m_id),
|
|
desc[1].cfg);
|
|
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(p2m_id),
|
|
desc[1].ctrl);
|
|
}
|
|
|
|
timeout = wait_for_completion_timeout(&p2m_dev->completion,
|
|
solo_dev->p2m_jiffies);
|
|
|
|
if (WARN_ON_ONCE(p2m_dev->error))
|
|
ret = -EIO;
|
|
else if (timeout == 0) {
|
|
solo_dev->p2m_timeouts++;
|
|
ret = -EAGAIN;
|
|
}
|
|
|
|
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(p2m_id), 0);
|
|
|
|
/* Don't write here for the no_desc_mode case, because config is 0.
|
|
* We can't test no_desc_mode again, it might race. */
|
|
if (desc_cnt > 1 && solo_dev->type != SOLO_DEV_6110 && config)
|
|
solo_reg_write(solo_dev, SOLO_P2M_CONFIG(p2m_id), config);
|
|
|
|
mutex_unlock(&p2m_dev->mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void solo_p2m_fill_desc(struct solo_p2m_desc *desc, int wr,
|
|
dma_addr_t dma_addr, u32 ext_addr, u32 size,
|
|
int repeat, u32 ext_size)
|
|
{
|
|
WARN_ON_ONCE(dma_addr & 0x03);
|
|
WARN_ON_ONCE(!size);
|
|
|
|
desc->cfg = SOLO_P2M_COPY_SIZE(size >> 2);
|
|
desc->ctrl = SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) |
|
|
(wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON;
|
|
|
|
if (repeat) {
|
|
desc->cfg |= SOLO_P2M_EXT_INC(ext_size >> 2);
|
|
desc->ctrl |= SOLO_P2M_PCI_INC(size >> 2) |
|
|
SOLO_P2M_REPEAT(repeat);
|
|
}
|
|
|
|
desc->dma_addr = dma_addr;
|
|
desc->ext_addr = ext_addr;
|
|
}
|
|
|
|
int solo_p2m_dma_t(struct solo_dev *solo_dev, int wr,
|
|
dma_addr_t dma_addr, u32 ext_addr, u32 size,
|
|
int repeat, u32 ext_size)
|
|
{
|
|
struct solo_p2m_desc desc[2];
|
|
|
|
solo_p2m_fill_desc(&desc[1], wr, dma_addr, ext_addr, size, repeat,
|
|
ext_size);
|
|
|
|
/* No need for desc_dma since we know it is a single-shot */
|
|
return solo_p2m_dma_desc(solo_dev, desc, 0, 1);
|
|
}
|
|
|
|
void solo_p2m_isr(struct solo_dev *solo_dev, int id)
|
|
{
|
|
struct solo_p2m_dev *p2m_dev = &solo_dev->p2m_dev[id];
|
|
struct solo_p2m_desc *desc;
|
|
|
|
if (p2m_dev->desc_count <= p2m_dev->desc_idx) {
|
|
complete(&p2m_dev->completion);
|
|
return;
|
|
}
|
|
|
|
/* Setup next descriptor */
|
|
p2m_dev->desc_idx++;
|
|
desc = &p2m_dev->descs[p2m_dev->desc_idx];
|
|
|
|
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0);
|
|
solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(id), desc->dma_addr);
|
|
solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(id), desc->ext_addr);
|
|
solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(id), desc->cfg);
|
|
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), desc->ctrl);
|
|
}
|
|
|
|
void solo_p2m_error_isr(struct solo_dev *solo_dev)
|
|
{
|
|
unsigned int err = solo_reg_read(solo_dev, SOLO_PCI_ERR);
|
|
struct solo_p2m_dev *p2m_dev;
|
|
int i;
|
|
|
|
if (!(err & (SOLO_PCI_ERR_P2M | SOLO_PCI_ERR_P2M_DESC)))
|
|
return;
|
|
|
|
for (i = 0; i < SOLO_NR_P2M; i++) {
|
|
p2m_dev = &solo_dev->p2m_dev[i];
|
|
p2m_dev->error = 1;
|
|
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0);
|
|
complete(&p2m_dev->completion);
|
|
}
|
|
}
|
|
|
|
void solo_p2m_exit(struct solo_dev *solo_dev)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < SOLO_NR_P2M; i++)
|
|
solo_irq_off(solo_dev, SOLO_IRQ_P2M(i));
|
|
}
|
|
|
|
static int solo_p2m_test(struct solo_dev *solo_dev, int base, int size)
|
|
{
|
|
u32 *wr_buf;
|
|
u32 *rd_buf;
|
|
int i;
|
|
int ret = -EIO;
|
|
int order = get_order(size);
|
|
|
|
wr_buf = (u32 *)__get_free_pages(GFP_KERNEL, order);
|
|
if (wr_buf == NULL)
|
|
return -1;
|
|
|
|
rd_buf = (u32 *)__get_free_pages(GFP_KERNEL, order);
|
|
if (rd_buf == NULL) {
|
|
free_pages((unsigned long)wr_buf, order);
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < (size >> 3); i++)
|
|
*(wr_buf + i) = (i << 16) | (i + 1);
|
|
|
|
for (i = (size >> 3); i < (size >> 2); i++)
|
|
*(wr_buf + i) = ~((i << 16) | (i + 1));
|
|
|
|
memset(rd_buf, 0x55, size);
|
|
|
|
if (solo_p2m_dma(solo_dev, 1, wr_buf, base, size, 0, 0))
|
|
goto test_fail;
|
|
|
|
if (solo_p2m_dma(solo_dev, 0, rd_buf, base, size, 0, 0))
|
|
goto test_fail;
|
|
|
|
for (i = 0; i < (size >> 2); i++) {
|
|
if (*(wr_buf + i) != *(rd_buf + i))
|
|
goto test_fail;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
test_fail:
|
|
free_pages((unsigned long)wr_buf, order);
|
|
free_pages((unsigned long)rd_buf, order);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int solo_p2m_init(struct solo_dev *solo_dev)
|
|
{
|
|
struct solo_p2m_dev *p2m_dev;
|
|
int i;
|
|
|
|
for (i = 0; i < SOLO_NR_P2M; i++) {
|
|
p2m_dev = &solo_dev->p2m_dev[i];
|
|
|
|
mutex_init(&p2m_dev->mutex);
|
|
init_completion(&p2m_dev->completion);
|
|
|
|
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0);
|
|
solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i),
|
|
SOLO_P2M_CSC_16BIT_565 |
|
|
SOLO_P2M_DESC_INTR_OPT |
|
|
SOLO_P2M_DMA_INTERVAL(0) |
|
|
SOLO_P2M_PCI_MASTER_MODE);
|
|
solo_irq_on(solo_dev, SOLO_IRQ_P2M(i));
|
|
}
|
|
|
|
/* Find correct SDRAM size */
|
|
for (solo_dev->sdram_size = 0, i = 2; i >= 0; i--) {
|
|
solo_reg_write(solo_dev, SOLO_DMA_CTRL,
|
|
SOLO_DMA_CTRL_REFRESH_CYCLE(1) |
|
|
SOLO_DMA_CTRL_SDRAM_SIZE(i) |
|
|
SOLO_DMA_CTRL_SDRAM_CLK_INVERT |
|
|
SOLO_DMA_CTRL_READ_CLK_SELECT |
|
|
SOLO_DMA_CTRL_LATENCY(1));
|
|
|
|
solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config |
|
|
SOLO_SYS_CFG_RESET);
|
|
solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config);
|
|
|
|
switch (i) {
|
|
case 2:
|
|
if (solo_p2m_test(solo_dev, 0x07ff0000, 0x00010000) ||
|
|
solo_p2m_test(solo_dev, 0x05ff0000, 0x00010000))
|
|
continue;
|
|
break;
|
|
|
|
case 1:
|
|
if (solo_p2m_test(solo_dev, 0x03ff0000, 0x00010000))
|
|
continue;
|
|
break;
|
|
|
|
default:
|
|
if (solo_p2m_test(solo_dev, 0x01ff0000, 0x00010000))
|
|
continue;
|
|
}
|
|
|
|
solo_dev->sdram_size = (32 << 20) << i;
|
|
break;
|
|
}
|
|
|
|
if (!solo_dev->sdram_size) {
|
|
dev_err(&solo_dev->pdev->dev, "Error detecting SDRAM size\n");
|
|
return -EIO;
|
|
}
|
|
|
|
if (SOLO_SDRAM_END(solo_dev) > solo_dev->sdram_size) {
|
|
dev_err(&solo_dev->pdev->dev,
|
|
"SDRAM is not large enough (%u < %u)\n",
|
|
solo_dev->sdram_size, SOLO_SDRAM_END(solo_dev));
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|