hw/misc/mps2-scc: Implement CFG_REG5 and CFG_REG6 for MPS3 AN524
The AN524 version of the SCC interface has different behaviour for some of the CFG registers; implement it. Each board in this family can have minor differences in the meaning of the CFG registers, so rather than trying to specify all the possible semantics via individual device properties, we make the behaviour conditional on the part-number field of the SCC_ID register which the board code already passes us. For the AN524, the differences are: * CFG3 is reserved rather than being board switches * CFG5 is a new register ("ACLK Frequency in Hz") * CFG6 is a new register ("Clock divider for BRAM") We implement both of the new registers as reads-as-written. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20210215115138.20465-11-peter.maydell@linaro.org
This commit is contained in:
parent
11e1d41265
commit
8e4b4c1ca6
|
@ -31,8 +31,11 @@
|
||||||
|
|
||||||
REG32(CFG0, 0)
|
REG32(CFG0, 0)
|
||||||
REG32(CFG1, 4)
|
REG32(CFG1, 4)
|
||||||
|
REG32(CFG2, 8)
|
||||||
REG32(CFG3, 0xc)
|
REG32(CFG3, 0xc)
|
||||||
REG32(CFG4, 0x10)
|
REG32(CFG4, 0x10)
|
||||||
|
REG32(CFG5, 0x14)
|
||||||
|
REG32(CFG6, 0x18)
|
||||||
REG32(CFGDATA_RTN, 0xa0)
|
REG32(CFGDATA_RTN, 0xa0)
|
||||||
REG32(CFGDATA_OUT, 0xa4)
|
REG32(CFGDATA_OUT, 0xa4)
|
||||||
REG32(CFGCTRL, 0xa8)
|
REG32(CFGCTRL, 0xa8)
|
||||||
|
@ -49,6 +52,12 @@ REG32(DLL, 0x100)
|
||||||
REG32(AID, 0xFF8)
|
REG32(AID, 0xFF8)
|
||||||
REG32(ID, 0xFFC)
|
REG32(ID, 0xFFC)
|
||||||
|
|
||||||
|
static int scc_partno(MPS2SCC *s)
|
||||||
|
{
|
||||||
|
/* Return the partno field of the SCC_ID (0x524, 0x511, etc) */
|
||||||
|
return extract32(s->id, 4, 8);
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle a write via the SYS_CFG channel to the specified function/device.
|
/* Handle a write via the SYS_CFG channel to the specified function/device.
|
||||||
* Return false on error (reported to guest via SYS_CFGCTRL ERROR bit).
|
* Return false on error (reported to guest via SYS_CFGCTRL ERROR bit).
|
||||||
*/
|
*/
|
||||||
|
@ -100,7 +109,18 @@ static uint64_t mps2_scc_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
case A_CFG1:
|
case A_CFG1:
|
||||||
r = s->cfg1;
|
r = s->cfg1;
|
||||||
break;
|
break;
|
||||||
|
case A_CFG2:
|
||||||
|
if (scc_partno(s) != 0x524) {
|
||||||
|
/* CFG2 reserved on other boards */
|
||||||
|
goto bad_offset;
|
||||||
|
}
|
||||||
|
r = s->cfg2;
|
||||||
|
break;
|
||||||
case A_CFG3:
|
case A_CFG3:
|
||||||
|
if (scc_partno(s) == 0x524) {
|
||||||
|
/* CFG3 reserved on AN524 */
|
||||||
|
goto bad_offset;
|
||||||
|
}
|
||||||
/* These are user-settable DIP switches on the board. We don't
|
/* These are user-settable DIP switches on the board. We don't
|
||||||
* model that, so just return zeroes.
|
* model that, so just return zeroes.
|
||||||
*/
|
*/
|
||||||
|
@ -109,6 +129,20 @@ static uint64_t mps2_scc_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
case A_CFG4:
|
case A_CFG4:
|
||||||
r = s->cfg4;
|
r = s->cfg4;
|
||||||
break;
|
break;
|
||||||
|
case A_CFG5:
|
||||||
|
if (scc_partno(s) != 0x524) {
|
||||||
|
/* CFG5 reserved on other boards */
|
||||||
|
goto bad_offset;
|
||||||
|
}
|
||||||
|
r = s->cfg5;
|
||||||
|
break;
|
||||||
|
case A_CFG6:
|
||||||
|
if (scc_partno(s) != 0x524) {
|
||||||
|
/* CFG6 reserved on other boards */
|
||||||
|
goto bad_offset;
|
||||||
|
}
|
||||||
|
r = s->cfg6;
|
||||||
|
break;
|
||||||
case A_CFGDATA_RTN:
|
case A_CFGDATA_RTN:
|
||||||
r = s->cfgdata_rtn;
|
r = s->cfgdata_rtn;
|
||||||
break;
|
break;
|
||||||
|
@ -131,6 +165,7 @@ static uint64_t mps2_scc_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
r = s->id;
|
r = s->id;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
bad_offset:
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
"MPS2 SCC read: bad offset %x\n", (int) offset);
|
"MPS2 SCC read: bad offset %x\n", (int) offset);
|
||||||
r = 0;
|
r = 0;
|
||||||
|
@ -159,6 +194,30 @@ static void mps2_scc_write(void *opaque, hwaddr offset, uint64_t value,
|
||||||
led_set_state(s->led[i], extract32(value, i, 1));
|
led_set_state(s->led[i], extract32(value, i, 1));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case A_CFG2:
|
||||||
|
if (scc_partno(s) != 0x524) {
|
||||||
|
/* CFG2 reserved on other boards */
|
||||||
|
goto bad_offset;
|
||||||
|
}
|
||||||
|
/* AN524: QSPI Select signal */
|
||||||
|
s->cfg2 = value;
|
||||||
|
break;
|
||||||
|
case A_CFG5:
|
||||||
|
if (scc_partno(s) != 0x524) {
|
||||||
|
/* CFG5 reserved on other boards */
|
||||||
|
goto bad_offset;
|
||||||
|
}
|
||||||
|
/* AN524: ACLK frequency in Hz */
|
||||||
|
s->cfg5 = value;
|
||||||
|
break;
|
||||||
|
case A_CFG6:
|
||||||
|
if (scc_partno(s) != 0x524) {
|
||||||
|
/* CFG6 reserved on other boards */
|
||||||
|
goto bad_offset;
|
||||||
|
}
|
||||||
|
/* AN524: Clock divider for BRAM */
|
||||||
|
s->cfg6 = value;
|
||||||
|
break;
|
||||||
case A_CFGDATA_OUT:
|
case A_CFGDATA_OUT:
|
||||||
s->cfgdata_out = value;
|
s->cfgdata_out = value;
|
||||||
break;
|
break;
|
||||||
|
@ -202,6 +261,7 @@ static void mps2_scc_write(void *opaque, hwaddr offset, uint64_t value,
|
||||||
s->dll = deposit32(s->dll, 24, 8, extract32(value, 24, 8));
|
s->dll = deposit32(s->dll, 24, 8, extract32(value, 24, 8));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
bad_offset:
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
"MPS2 SCC write: bad offset 0x%x\n", (int) offset);
|
"MPS2 SCC write: bad offset 0x%x\n", (int) offset);
|
||||||
break;
|
break;
|
||||||
|
@ -222,6 +282,9 @@ static void mps2_scc_reset(DeviceState *dev)
|
||||||
trace_mps2_scc_reset();
|
trace_mps2_scc_reset();
|
||||||
s->cfg0 = 0;
|
s->cfg0 = 0;
|
||||||
s->cfg1 = 0;
|
s->cfg1 = 0;
|
||||||
|
s->cfg2 = 0;
|
||||||
|
s->cfg5 = 0;
|
||||||
|
s->cfg6 = 0;
|
||||||
s->cfgdata_rtn = 0;
|
s->cfgdata_rtn = 0;
|
||||||
s->cfgdata_out = 0;
|
s->cfgdata_out = 0;
|
||||||
s->cfgctrl = 0x100000;
|
s->cfgctrl = 0x100000;
|
||||||
|
@ -260,11 +323,15 @@ static void mps2_scc_realize(DeviceState *dev, Error **errp)
|
||||||
|
|
||||||
static const VMStateDescription mps2_scc_vmstate = {
|
static const VMStateDescription mps2_scc_vmstate = {
|
||||||
.name = "mps2-scc",
|
.name = "mps2-scc",
|
||||||
.version_id = 2,
|
.version_id = 3,
|
||||||
.minimum_version_id = 2,
|
.minimum_version_id = 3,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_UINT32(cfg0, MPS2SCC),
|
VMSTATE_UINT32(cfg0, MPS2SCC),
|
||||||
VMSTATE_UINT32(cfg1, MPS2SCC),
|
VMSTATE_UINT32(cfg1, MPS2SCC),
|
||||||
|
VMSTATE_UINT32(cfg2, MPS2SCC),
|
||||||
|
/* cfg3, cfg4 are read-only so need not be migrated */
|
||||||
|
VMSTATE_UINT32(cfg5, MPS2SCC),
|
||||||
|
VMSTATE_UINT32(cfg6, MPS2SCC),
|
||||||
VMSTATE_UINT32(cfgdata_rtn, MPS2SCC),
|
VMSTATE_UINT32(cfgdata_rtn, MPS2SCC),
|
||||||
VMSTATE_UINT32(cfgdata_out, MPS2SCC),
|
VMSTATE_UINT32(cfgdata_out, MPS2SCC),
|
||||||
VMSTATE_UINT32(cfgctrl, MPS2SCC),
|
VMSTATE_UINT32(cfgctrl, MPS2SCC),
|
||||||
|
|
|
@ -29,7 +29,10 @@ struct MPS2SCC {
|
||||||
|
|
||||||
uint32_t cfg0;
|
uint32_t cfg0;
|
||||||
uint32_t cfg1;
|
uint32_t cfg1;
|
||||||
|
uint32_t cfg2;
|
||||||
uint32_t cfg4;
|
uint32_t cfg4;
|
||||||
|
uint32_t cfg5;
|
||||||
|
uint32_t cfg6;
|
||||||
uint32_t cfgdata_rtn;
|
uint32_t cfgdata_rtn;
|
||||||
uint32_t cfgdata_out;
|
uint32_t cfgdata_out;
|
||||||
uint32_t cfgctrl;
|
uint32_t cfgctrl;
|
||||||
|
|
Loading…
Reference in a new issue