Commit fe78118c authored by Alex Deucher's avatar Alex Deucher

drm/radeon: protect concurrent smc register access with a spinlock

smc registers are access indirectly via the main mmio aperture, so
there may be problems with concurrent access.  This adds a spinlock
to protect access to this register space.
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 136de91e
...@@ -47,10 +47,11 @@ int ci_copy_bytes_to_smc(struct radeon_device *rdev, ...@@ -47,10 +47,11 @@ int ci_copy_bytes_to_smc(struct radeon_device *rdev,
u32 smc_start_address, u32 smc_start_address,
const u8 *src, u32 byte_count, u32 limit) const u8 *src, u32 byte_count, u32 limit)
{ {
unsigned long flags;
u32 data, original_data; u32 data, original_data;
u32 addr; u32 addr;
u32 extra_shift; u32 extra_shift;
int ret; int ret = 0;
if (smc_start_address & 3) if (smc_start_address & 3)
return -EINVAL; return -EINVAL;
...@@ -59,13 +60,14 @@ int ci_copy_bytes_to_smc(struct radeon_device *rdev, ...@@ -59,13 +60,14 @@ int ci_copy_bytes_to_smc(struct radeon_device *rdev,
addr = smc_start_address; addr = smc_start_address;
spin_lock_irqsave(&rdev->smc_idx_lock, flags);
while (byte_count >= 4) { while (byte_count >= 4) {
/* SMC address space is BE */ /* SMC address space is BE */
data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3]; data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
ret = ci_set_smc_sram_address(rdev, addr, limit); ret = ci_set_smc_sram_address(rdev, addr, limit);
if (ret) if (ret)
return ret; goto done;
WREG32(SMC_IND_DATA_0, data); WREG32(SMC_IND_DATA_0, data);
...@@ -80,7 +82,7 @@ int ci_copy_bytes_to_smc(struct radeon_device *rdev, ...@@ -80,7 +82,7 @@ int ci_copy_bytes_to_smc(struct radeon_device *rdev,
ret = ci_set_smc_sram_address(rdev, addr, limit); ret = ci_set_smc_sram_address(rdev, addr, limit);
if (ret) if (ret)
return ret; goto done;
original_data = RREG32(SMC_IND_DATA_0); original_data = RREG32(SMC_IND_DATA_0);
...@@ -97,11 +99,15 @@ int ci_copy_bytes_to_smc(struct radeon_device *rdev, ...@@ -97,11 +99,15 @@ int ci_copy_bytes_to_smc(struct radeon_device *rdev,
ret = ci_set_smc_sram_address(rdev, addr, limit); ret = ci_set_smc_sram_address(rdev, addr, limit);
if (ret) if (ret)
return ret; goto done;
WREG32(SMC_IND_DATA_0, data); WREG32(SMC_IND_DATA_0, data);
} }
return 0;
done:
spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
return ret;
} }
void ci_start_smc(struct radeon_device *rdev) void ci_start_smc(struct radeon_device *rdev)
...@@ -197,6 +203,7 @@ PPSMC_Result ci_wait_for_smc_inactive(struct radeon_device *rdev) ...@@ -197,6 +203,7 @@ PPSMC_Result ci_wait_for_smc_inactive(struct radeon_device *rdev)
int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit) int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit)
{ {
unsigned long flags;
u32 ucode_start_address; u32 ucode_start_address;
u32 ucode_size; u32 ucode_size;
const u8 *src; const u8 *src;
...@@ -219,6 +226,7 @@ int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit) ...@@ -219,6 +226,7 @@ int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit)
return -EINVAL; return -EINVAL;
src = (const u8 *)rdev->smc_fw->data; src = (const u8 *)rdev->smc_fw->data;
spin_lock_irqsave(&rdev->smc_idx_lock, flags);
WREG32(SMC_IND_INDEX_0, ucode_start_address); WREG32(SMC_IND_INDEX_0, ucode_start_address);
WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0); WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0);
while (ucode_size >= 4) { while (ucode_size >= 4) {
...@@ -231,6 +239,7 @@ int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit) ...@@ -231,6 +239,7 @@ int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit)
ucode_size -= 4; ucode_size -= 4;
} }
WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0); WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
return 0; return 0;
} }
...@@ -238,25 +247,29 @@ int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit) ...@@ -238,25 +247,29 @@ int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit)
int ci_read_smc_sram_dword(struct radeon_device *rdev, int ci_read_smc_sram_dword(struct radeon_device *rdev,
u32 smc_address, u32 *value, u32 limit) u32 smc_address, u32 *value, u32 limit)
{ {
unsigned long flags;
int ret; int ret;
spin_lock_irqsave(&rdev->smc_idx_lock, flags);
ret = ci_set_smc_sram_address(rdev, smc_address, limit); ret = ci_set_smc_sram_address(rdev, smc_address, limit);
if (ret) if (ret == 0)
return ret; *value = RREG32(SMC_IND_DATA_0);
spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
*value = RREG32(SMC_IND_DATA_0); return ret;
return 0;
} }
int ci_write_smc_sram_dword(struct radeon_device *rdev, int ci_write_smc_sram_dword(struct radeon_device *rdev,
u32 smc_address, u32 value, u32 limit) u32 smc_address, u32 value, u32 limit)
{ {
unsigned long flags;
int ret; int ret;
spin_lock_irqsave(&rdev->smc_idx_lock, flags);
ret = ci_set_smc_sram_address(rdev, smc_address, limit); ret = ci_set_smc_sram_address(rdev, smc_address, limit);
if (ret) if (ret == 0)
return ret; WREG32(SMC_IND_DATA_0, value);
spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
WREG32(SMC_IND_DATA_0, value); return ret;
return 0;
} }
...@@ -2110,6 +2110,8 @@ struct radeon_device { ...@@ -2110,6 +2110,8 @@ struct radeon_device {
resource_size_t rmmio_size; resource_size_t rmmio_size;
/* protects concurrent MM_INDEX/DATA based register access */ /* protects concurrent MM_INDEX/DATA based register access */
spinlock_t mmio_idx_lock; spinlock_t mmio_idx_lock;
/* protects concurrent SMC based register access */
spinlock_t smc_idx_lock;
void __iomem *rmmio; void __iomem *rmmio;
radeon_rreg_t mc_rreg; radeon_rreg_t mc_rreg;
radeon_wreg_t mc_wreg; radeon_wreg_t mc_wreg;
...@@ -2292,17 +2294,24 @@ static inline void rv370_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uin ...@@ -2292,17 +2294,24 @@ static inline void rv370_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uin
static inline u32 tn_smc_rreg(struct radeon_device *rdev, u32 reg) static inline u32 tn_smc_rreg(struct radeon_device *rdev, u32 reg)
{ {
unsigned long flags;
u32 r; u32 r;
spin_lock_irqsave(&rdev->smc_idx_lock, flags);
WREG32(TN_SMC_IND_INDEX_0, (reg)); WREG32(TN_SMC_IND_INDEX_0, (reg));
r = RREG32(TN_SMC_IND_DATA_0); r = RREG32(TN_SMC_IND_DATA_0);
spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
return r; return r;
} }
static inline void tn_smc_wreg(struct radeon_device *rdev, u32 reg, u32 v) static inline void tn_smc_wreg(struct radeon_device *rdev, u32 reg, u32 v)
{ {
unsigned long flags;
spin_lock_irqsave(&rdev->smc_idx_lock, flags);
WREG32(TN_SMC_IND_INDEX_0, (reg)); WREG32(TN_SMC_IND_INDEX_0, (reg));
WREG32(TN_SMC_IND_DATA_0, (v)); WREG32(TN_SMC_IND_DATA_0, (v));
spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
} }
static inline u32 r600_rcu_rreg(struct radeon_device *rdev, u32 reg) static inline u32 r600_rcu_rreg(struct radeon_device *rdev, u32 reg)
......
...@@ -1249,6 +1249,7 @@ int radeon_device_init(struct radeon_device *rdev, ...@@ -1249,6 +1249,7 @@ int radeon_device_init(struct radeon_device *rdev,
/* Registers mapping */ /* Registers mapping */
/* TODO: block userspace mapping of io register */ /* TODO: block userspace mapping of io register */
spin_lock_init(&rdev->mmio_idx_lock); spin_lock_init(&rdev->mmio_idx_lock);
spin_lock_init(&rdev->smc_idx_lock);
if (rdev->family >= CHIP_BONAIRE) { if (rdev->family >= CHIP_BONAIRE) {
rdev->rmmio_base = pci_resource_start(rdev->pdev, 5); rdev->rmmio_base = pci_resource_start(rdev->pdev, 5);
rdev->rmmio_size = pci_resource_len(rdev->pdev, 5); rdev->rmmio_size = pci_resource_len(rdev->pdev, 5);
......
...@@ -274,8 +274,8 @@ static const u8 cayman_smc_int_vectors[] = ...@@ -274,8 +274,8 @@ static const u8 cayman_smc_int_vectors[] =
0x08, 0x72, 0x08, 0x72 0x08, 0x72, 0x08, 0x72
}; };
int rv770_set_smc_sram_address(struct radeon_device *rdev, static int rv770_set_smc_sram_address(struct radeon_device *rdev,
u16 smc_address, u16 limit) u16 smc_address, u16 limit)
{ {
u32 addr; u32 addr;
...@@ -296,9 +296,10 @@ int rv770_copy_bytes_to_smc(struct radeon_device *rdev, ...@@ -296,9 +296,10 @@ int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
u16 smc_start_address, const u8 *src, u16 smc_start_address, const u8 *src,
u16 byte_count, u16 limit) u16 byte_count, u16 limit)
{ {
unsigned long flags;
u32 data, original_data, extra_shift; u32 data, original_data, extra_shift;
u16 addr; u16 addr;
int ret; int ret = 0;
if (smc_start_address & 3) if (smc_start_address & 3)
return -EINVAL; return -EINVAL;
...@@ -307,13 +308,14 @@ int rv770_copy_bytes_to_smc(struct radeon_device *rdev, ...@@ -307,13 +308,14 @@ int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
addr = smc_start_address; addr = smc_start_address;
spin_lock_irqsave(&rdev->smc_idx_lock, flags);
while (byte_count >= 4) { while (byte_count >= 4) {
/* SMC address space is BE */ /* SMC address space is BE */
data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3]; data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
ret = rv770_set_smc_sram_address(rdev, addr, limit); ret = rv770_set_smc_sram_address(rdev, addr, limit);
if (ret) if (ret)
return ret; goto done;
WREG32(SMC_SRAM_DATA, data); WREG32(SMC_SRAM_DATA, data);
...@@ -328,7 +330,7 @@ int rv770_copy_bytes_to_smc(struct radeon_device *rdev, ...@@ -328,7 +330,7 @@ int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
ret = rv770_set_smc_sram_address(rdev, addr, limit); ret = rv770_set_smc_sram_address(rdev, addr, limit);
if (ret) if (ret)
return ret; goto done;
original_data = RREG32(SMC_SRAM_DATA); original_data = RREG32(SMC_SRAM_DATA);
...@@ -346,12 +348,15 @@ int rv770_copy_bytes_to_smc(struct radeon_device *rdev, ...@@ -346,12 +348,15 @@ int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
ret = rv770_set_smc_sram_address(rdev, addr, limit); ret = rv770_set_smc_sram_address(rdev, addr, limit);
if (ret) if (ret)
return ret; goto done;
WREG32(SMC_SRAM_DATA, data); WREG32(SMC_SRAM_DATA, data);
} }
return 0; done:
spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
return ret;
} }
static int rv770_program_interrupt_vectors(struct radeon_device *rdev, static int rv770_program_interrupt_vectors(struct radeon_device *rdev,
...@@ -461,12 +466,15 @@ PPSMC_Result rv770_wait_for_smc_inactive(struct radeon_device *rdev) ...@@ -461,12 +466,15 @@ PPSMC_Result rv770_wait_for_smc_inactive(struct radeon_device *rdev)
static void rv770_clear_smc_sram(struct radeon_device *rdev, u16 limit) static void rv770_clear_smc_sram(struct radeon_device *rdev, u16 limit)
{ {
unsigned long flags;
u16 i; u16 i;
spin_lock_irqsave(&rdev->smc_idx_lock, flags);
for (i = 0; i < limit; i += 4) { for (i = 0; i < limit; i += 4) {
rv770_set_smc_sram_address(rdev, i, limit); rv770_set_smc_sram_address(rdev, i, limit);
WREG32(SMC_SRAM_DATA, 0); WREG32(SMC_SRAM_DATA, 0);
} }
spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
} }
int rv770_load_smc_ucode(struct radeon_device *rdev, int rv770_load_smc_ucode(struct radeon_device *rdev,
...@@ -595,27 +603,29 @@ int rv770_load_smc_ucode(struct radeon_device *rdev, ...@@ -595,27 +603,29 @@ int rv770_load_smc_ucode(struct radeon_device *rdev,
int rv770_read_smc_sram_dword(struct radeon_device *rdev, int rv770_read_smc_sram_dword(struct radeon_device *rdev,
u16 smc_address, u32 *value, u16 limit) u16 smc_address, u32 *value, u16 limit)
{ {
unsigned long flags;
int ret; int ret;
spin_lock_irqsave(&rdev->smc_idx_lock, flags);
ret = rv770_set_smc_sram_address(rdev, smc_address, limit); ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
if (ret) if (ret == 0)
return ret; *value = RREG32(SMC_SRAM_DATA);
spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
*value = RREG32(SMC_SRAM_DATA);
return 0; return ret;
} }
int rv770_write_smc_sram_dword(struct radeon_device *rdev, int rv770_write_smc_sram_dword(struct radeon_device *rdev,
u16 smc_address, u32 value, u16 limit) u16 smc_address, u32 value, u16 limit)
{ {
unsigned long flags;
int ret; int ret;
spin_lock_irqsave(&rdev->smc_idx_lock, flags);
ret = rv770_set_smc_sram_address(rdev, smc_address, limit); ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
if (ret) if (ret == 0)
return ret; WREG32(SMC_SRAM_DATA, value);
spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
WREG32(SMC_SRAM_DATA, value); return ret;
return 0;
} }
...@@ -187,8 +187,6 @@ typedef struct RV770_SMC_STATETABLE RV770_SMC_STATETABLE; ...@@ -187,8 +187,6 @@ typedef struct RV770_SMC_STATETABLE RV770_SMC_STATETABLE;
#define RV770_SMC_SOFT_REGISTER_uvd_enabled 0x9C #define RV770_SMC_SOFT_REGISTER_uvd_enabled 0x9C
#define RV770_SMC_SOFT_REGISTER_is_asic_lombok 0xA0 #define RV770_SMC_SOFT_REGISTER_is_asic_lombok 0xA0
int rv770_set_smc_sram_address(struct radeon_device *rdev,
u16 smc_address, u16 limit);
int rv770_copy_bytes_to_smc(struct radeon_device *rdev, int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
u16 smc_start_address, const u8 *src, u16 smc_start_address, const u8 *src,
u16 byte_count, u16 limit); u16 byte_count, u16 limit);
......
...@@ -29,8 +29,8 @@ ...@@ -29,8 +29,8 @@
#include "ppsmc.h" #include "ppsmc.h"
#include "radeon_ucode.h" #include "radeon_ucode.h"
int si_set_smc_sram_address(struct radeon_device *rdev, static int si_set_smc_sram_address(struct radeon_device *rdev,
u32 smc_address, u32 limit) u32 smc_address, u32 limit)
{ {
if (smc_address & 3) if (smc_address & 3)
return -EINVAL; return -EINVAL;
...@@ -47,7 +47,8 @@ int si_copy_bytes_to_smc(struct radeon_device *rdev, ...@@ -47,7 +47,8 @@ int si_copy_bytes_to_smc(struct radeon_device *rdev,
u32 smc_start_address, u32 smc_start_address,
const u8 *src, u32 byte_count, u32 limit) const u8 *src, u32 byte_count, u32 limit)
{ {
int ret; unsigned long flags;
int ret = 0;
u32 data, original_data, addr, extra_shift; u32 data, original_data, addr, extra_shift;
if (smc_start_address & 3) if (smc_start_address & 3)
...@@ -57,13 +58,14 @@ int si_copy_bytes_to_smc(struct radeon_device *rdev, ...@@ -57,13 +58,14 @@ int si_copy_bytes_to_smc(struct radeon_device *rdev,
addr = smc_start_address; addr = smc_start_address;
spin_lock_irqsave(&rdev->smc_idx_lock, flags);
while (byte_count >= 4) { while (byte_count >= 4) {
/* SMC address space is BE */ /* SMC address space is BE */
data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3]; data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
ret = si_set_smc_sram_address(rdev, addr, limit); ret = si_set_smc_sram_address(rdev, addr, limit);
if (ret) if (ret)
return ret; goto done;
WREG32(SMC_IND_DATA_0, data); WREG32(SMC_IND_DATA_0, data);
...@@ -78,7 +80,7 @@ int si_copy_bytes_to_smc(struct radeon_device *rdev, ...@@ -78,7 +80,7 @@ int si_copy_bytes_to_smc(struct radeon_device *rdev,
ret = si_set_smc_sram_address(rdev, addr, limit); ret = si_set_smc_sram_address(rdev, addr, limit);
if (ret) if (ret)
return ret; goto done;
original_data = RREG32(SMC_IND_DATA_0); original_data = RREG32(SMC_IND_DATA_0);
...@@ -96,11 +98,15 @@ int si_copy_bytes_to_smc(struct radeon_device *rdev, ...@@ -96,11 +98,15 @@ int si_copy_bytes_to_smc(struct radeon_device *rdev,
ret = si_set_smc_sram_address(rdev, addr, limit); ret = si_set_smc_sram_address(rdev, addr, limit);
if (ret) if (ret)
return ret; goto done;
WREG32(SMC_IND_DATA_0, data); WREG32(SMC_IND_DATA_0, data);
} }
return 0;
done:
spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
return ret;
} }
void si_start_smc(struct radeon_device *rdev) void si_start_smc(struct radeon_device *rdev)
...@@ -203,6 +209,7 @@ PPSMC_Result si_wait_for_smc_inactive(struct radeon_device *rdev) ...@@ -203,6 +209,7 @@ PPSMC_Result si_wait_for_smc_inactive(struct radeon_device *rdev)
int si_load_smc_ucode(struct radeon_device *rdev, u32 limit) int si_load_smc_ucode(struct radeon_device *rdev, u32 limit)
{ {
unsigned long flags;
u32 ucode_start_address; u32 ucode_start_address;
u32 ucode_size; u32 ucode_size;
const u8 *src; const u8 *src;
...@@ -241,6 +248,7 @@ int si_load_smc_ucode(struct radeon_device *rdev, u32 limit) ...@@ -241,6 +248,7 @@ int si_load_smc_ucode(struct radeon_device *rdev, u32 limit)
return -EINVAL; return -EINVAL;
src = (const u8 *)rdev->smc_fw->data; src = (const u8 *)rdev->smc_fw->data;
spin_lock_irqsave(&rdev->smc_idx_lock, flags);
WREG32(SMC_IND_INDEX_0, ucode_start_address); WREG32(SMC_IND_INDEX_0, ucode_start_address);
WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0); WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0);
while (ucode_size >= 4) { while (ucode_size >= 4) {
...@@ -253,6 +261,7 @@ int si_load_smc_ucode(struct radeon_device *rdev, u32 limit) ...@@ -253,6 +261,7 @@ int si_load_smc_ucode(struct radeon_device *rdev, u32 limit)
ucode_size -= 4; ucode_size -= 4;
} }
WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0); WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
return 0; return 0;
} }
...@@ -260,25 +269,29 @@ int si_load_smc_ucode(struct radeon_device *rdev, u32 limit) ...@@ -260,25 +269,29 @@ int si_load_smc_ucode(struct radeon_device *rdev, u32 limit)
int si_read_smc_sram_dword(struct radeon_device *rdev, u32 smc_address, int si_read_smc_sram_dword(struct radeon_device *rdev, u32 smc_address,
u32 *value, u32 limit) u32 *value, u32 limit)
{ {
unsigned long flags;
int ret; int ret;
spin_lock_irqsave(&rdev->smc_idx_lock, flags);
ret = si_set_smc_sram_address(rdev, smc_address, limit); ret = si_set_smc_sram_address(rdev, smc_address, limit);
if (ret) if (ret == 0)
return ret; *value = RREG32(SMC_IND_DATA_0);
spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
*value = RREG32(SMC_IND_DATA_0); return ret;
return 0;
} }
int si_write_smc_sram_dword(struct radeon_device *rdev, u32 smc_address, int si_write_smc_sram_dword(struct radeon_device *rdev, u32 smc_address,
u32 value, u32 limit) u32 value, u32 limit)
{ {
unsigned long flags;
int ret; int ret;
spin_lock_irqsave(&rdev->smc_idx_lock, flags);
ret = si_set_smc_sram_address(rdev, smc_address, limit); ret = si_set_smc_sram_address(rdev, smc_address, limit);
if (ret) if (ret == 0)
return ret; WREG32(SMC_IND_DATA_0, value);
spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
WREG32(SMC_IND_DATA_0, value); return ret;
return 0;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment