* [Qemu-devel] [PATCH v3 01/13] m25p80: Add support for continuous read out of RDSR and READ_FSR
2017-10-24 19:51 [Qemu-devel] [PATCH v3 00/13] Add support for the ZynqMP Generic QSPI Francisco Iglesias
@ 2017-10-24 19:51 ` Francisco Iglesias
2017-10-25 18:03 ` mar.krzeminski
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 02/13] m25p80: Add support for SST READ ID 0x90/0xAB commands Francisco Iglesias
` (11 subsequent siblings)
12 siblings, 1 reply; 19+ messages in thread
From: Francisco Iglesias @ 2017-10-24 19:51 UTC (permalink / raw)
To: qemu-devel; +Cc: edgari, alistai, francisco.iglesias
Add support for continuous read out of the RDSR and READ_FSR status
registers until the chip select is deasserted. This feature is supported
by amongst others 1 or more flashtypes manufactured by Numonyx (Micron),
Windbond, SST, Gigadevice, Eon and Macronix.
Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
---
hw/block/m25p80.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index a2438b9..2971519 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -423,6 +423,7 @@ typedef struct Flash {
uint8_t data[M25P80_INTERNAL_DATA_BUFFER_SZ];
uint32_t len;
uint32_t pos;
+ bool data_read_loop;
uint8_t needed_bytes;
uint8_t cmd_in_progress;
uint32_t cur_addr;
@@ -983,6 +984,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
}
s->pos = 0;
s->len = 1;
+ s->data_read_loop = true;
s->state = STATE_READING_DATA;
break;
@@ -993,6 +995,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
}
s->pos = 0;
s->len = 1;
+ s->data_read_loop = true;
s->state = STATE_READING_DATA;
break;
@@ -1133,6 +1136,7 @@ static int m25p80_cs(SSISlave *ss, bool select)
s->pos = 0;
s->state = STATE_IDLE;
flash_sync_dirty(s, -1);
+ s->data_read_loop = false;
}
DB_PRINT_L(0, "%sselect\n", select ? "de" : "");
@@ -1198,7 +1202,9 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
s->pos++;
if (s->pos == s->len) {
s->pos = 0;
- s->state = STATE_IDLE;
+ if (!s->data_read_loop) {
+ s->state = STATE_IDLE;
+ }
}
break;
--
2.9.3
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [Qemu-devel] [PATCH v3 01/13] m25p80: Add support for continuous read out of RDSR and READ_FSR
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 01/13] m25p80: Add support for continuous read out of RDSR and READ_FSR Francisco Iglesias
@ 2017-10-25 18:03 ` mar.krzeminski
0 siblings, 0 replies; 19+ messages in thread
From: mar.krzeminski @ 2017-10-25 18:03 UTC (permalink / raw)
To: Francisco Iglesias, qemu-devel; +Cc: edgari, alistai, francisco.iglesias
W dniu 24.10.2017 o 21:51, Francisco Iglesias pisze:
> Add support for continuous read out of the RDSR and READ_FSR status
> registers until the chip select is deasserted. This feature is supported
> by amongst others 1 or more flashtypes manufactured by Numonyx (Micron),
> Windbond, SST, Gigadevice, Eon and Macronix.
>
> Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
Acked-by: Marcin Krzemiński <mar.krzeminski@gmail.com>
> ---
> hw/block/m25p80.c | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
> index a2438b9..2971519 100644
> --- a/hw/block/m25p80.c
> +++ b/hw/block/m25p80.c
> @@ -423,6 +423,7 @@ typedef struct Flash {
> uint8_t data[M25P80_INTERNAL_DATA_BUFFER_SZ];
> uint32_t len;
> uint32_t pos;
> + bool data_read_loop;
> uint8_t needed_bytes;
> uint8_t cmd_in_progress;
> uint32_t cur_addr;
> @@ -983,6 +984,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
> }
> s->pos = 0;
> s->len = 1;
> + s->data_read_loop = true;
> s->state = STATE_READING_DATA;
> break;
>
> @@ -993,6 +995,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
> }
> s->pos = 0;
> s->len = 1;
> + s->data_read_loop = true;
> s->state = STATE_READING_DATA;
> break;
>
> @@ -1133,6 +1136,7 @@ static int m25p80_cs(SSISlave *ss, bool select)
> s->pos = 0;
> s->state = STATE_IDLE;
> flash_sync_dirty(s, -1);
> + s->data_read_loop = false;
> }
>
> DB_PRINT_L(0, "%sselect\n", select ? "de" : "");
> @@ -1198,7 +1202,9 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
> s->pos++;
> if (s->pos == s->len) {
> s->pos = 0;
> - s->state = STATE_IDLE;
> + if (!s->data_read_loop) {
> + s->state = STATE_IDLE;
> + }
> }
> break;
>
^ permalink raw reply [flat|nested] 19+ messages in thread
* [Qemu-devel] [PATCH v3 02/13] m25p80: Add support for SST READ ID 0x90/0xAB commands
2017-10-24 19:51 [Qemu-devel] [PATCH v3 00/13] Add support for the ZynqMP Generic QSPI Francisco Iglesias
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 01/13] m25p80: Add support for continuous read out of RDSR and READ_FSR Francisco Iglesias
@ 2017-10-24 19:51 ` Francisco Iglesias
2017-10-25 18:12 ` mar.krzeminski
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 03/13] m25p80: Add support for BRRD/BRWR and BULK_ERASE (0x60) Francisco Iglesias
` (10 subsequent siblings)
12 siblings, 1 reply; 19+ messages in thread
From: Francisco Iglesias @ 2017-10-24 19:51 UTC (permalink / raw)
To: qemu-devel; +Cc: edgari, alistai, francisco.iglesias
Add support for SST READ ID 0x90/0xAB commands for reading out the flash
manufacuter ID and device ID.
Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
---
hw/block/m25p80.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 2971519..c85e8fa 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -355,6 +355,8 @@ typedef enum {
DPP = 0xa2,
QPP = 0x32,
QPP_4 = 0x34,
+ RDID_90 = 0x90,
+ RDID_AB = 0xab,
ERASE_4K = 0x20,
ERASE4_4K = 0x21,
@@ -405,6 +407,7 @@ typedef enum {
MAN_MACRONIX,
MAN_NUMONYX,
MAN_WINBOND,
+ MAN_SST,
MAN_GENERIC,
} Manufacturer;
@@ -476,6 +479,8 @@ static inline Manufacturer get_man(Flash *s)
return MAN_SPANSION;
case 0xC2:
return MAN_MACRONIX;
+ case 0xBF:
+ return MAN_SST;
default:
return MAN_GENERIC;
}
@@ -1018,6 +1023,21 @@ static void decode_new_cmd(Flash *s, uint32_t value)
s->state = STATE_READING_DATA;
break;
+ case RDID_90:
+ case RDID_AB:
+ DB_PRINT_L(0, "populated manf/dev ID\n");
+ if (get_man(s) == MAN_SST) {
+ s->data[0] = s->pi->id[0];
+ s->data[1] = s->pi->id[2];
+ s->pos = 0;
+ s->len = 2;
+ s->data_read_loop = true;
+ s->state = STATE_READING_DATA;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value);
+ }
+ break;
+
case BULK_ERASE:
if (s->write_enable) {
DB_PRINT_L(0, "chip erase\n");
--
2.9.3
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [Qemu-devel] [PATCH v3 02/13] m25p80: Add support for SST READ ID 0x90/0xAB commands
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 02/13] m25p80: Add support for SST READ ID 0x90/0xAB commands Francisco Iglesias
@ 2017-10-25 18:12 ` mar.krzeminski
2017-10-25 21:03 ` francisco iglesias
0 siblings, 1 reply; 19+ messages in thread
From: mar.krzeminski @ 2017-10-25 18:12 UTC (permalink / raw)
To: Francisco Iglesias, qemu-devel; +Cc: edgari, alistai, francisco.iglesias
W dniu 24.10.2017 o 21:51, Francisco Iglesias pisze:
> Add support for SST READ ID 0x90/0xAB commands for reading out the flash
> manufacuter ID and device ID.
>
> Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
> ---
> hw/block/m25p80.c | 20 ++++++++++++++++++++
> 1 file changed, 20 insertions(+)
>
> diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
> index 2971519..c85e8fa 100644
> --- a/hw/block/m25p80.c
> +++ b/hw/block/m25p80.c
> @@ -355,6 +355,8 @@ typedef enum {
> DPP = 0xa2,
> QPP = 0x32,
> QPP_4 = 0x34,
> + RDID_90 = 0x90,
> + RDID_AB = 0xab,
>
> ERASE_4K = 0x20,
> ERASE4_4K = 0x21,
> @@ -405,6 +407,7 @@ typedef enum {
> MAN_MACRONIX,
> MAN_NUMONYX,
> MAN_WINBOND,
> + MAN_SST,
> MAN_GENERIC,
> } Manufacturer;
>
> @@ -476,6 +479,8 @@ static inline Manufacturer get_man(Flash *s)
> return MAN_SPANSION;
> case 0xC2:
> return MAN_MACRONIX;
> + case 0xBF:
> + return MAN_SST;
> default:
> return MAN_GENERIC;
> }
> @@ -1018,6 +1023,21 @@ static void decode_new_cmd(Flash *s, uint32_t value)
> s->state = STATE_READING_DATA;
> break;
>
> + case RDID_90:
> + case RDID_AB:
> + DB_PRINT_L(0, "populated manf/dev ID\n");
> + if (get_man(s) == MAN_SST) {
> + s->data[0] = s->pi->id[0];
> + s->data[1] = s->pi->id[2];
> + s->pos = 0;
> + s->len = 2;
> + s->data_read_loop = true;
> + s->state = STATE_READING_DATA;
I am not sure about this. I am looking at SST25WF080 datasheet, and it
seem that those commands
also expect address, and based on that returns manufacturer id or device id.
> + } else {
> + qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value);
> + }
> + break;
> +
> case BULK_ERASE:
> if (s->write_enable) {
> DB_PRINT_L(0, "chip erase\n");
Regard,
Marcin
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Qemu-devel] [PATCH v3 02/13] m25p80: Add support for SST READ ID 0x90/0xAB commands
2017-10-25 18:12 ` mar.krzeminski
@ 2017-10-25 21:03 ` francisco iglesias
0 siblings, 0 replies; 19+ messages in thread
From: francisco iglesias @ 2017-10-25 21:03 UTC (permalink / raw)
To: mar.krzeminski; +Cc: qemu-devel, edgari, alistai, Francisco Iglesias
Good day Marcin,
Thank you for your excellent review comments! I will correct the patches in
the next version of the patch series (v4).
Best regards,
Francisco Iglesias
On 25 October 2017 at 20:12, mar.krzeminski <mar.krzeminski@gmail.com>
wrote:
>
>
> W dniu 24.10.2017 o 21:51, Francisco Iglesias pisze:
>
> Add support for SST READ ID 0x90/0xAB commands for reading out the flash
>> manufacuter ID and device ID.
>>
>> Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
>> ---
>> hw/block/m25p80.c | 20 ++++++++++++++++++++
>> 1 file changed, 20 insertions(+)
>>
>> diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
>> index 2971519..c85e8fa 100644
>> --- a/hw/block/m25p80.c
>> +++ b/hw/block/m25p80.c
>> @@ -355,6 +355,8 @@ typedef enum {
>> DPP = 0xa2,
>> QPP = 0x32,
>> QPP_4 = 0x34,
>> + RDID_90 = 0x90,
>> + RDID_AB = 0xab,
>> ERASE_4K = 0x20,
>> ERASE4_4K = 0x21,
>> @@ -405,6 +407,7 @@ typedef enum {
>> MAN_MACRONIX,
>> MAN_NUMONYX,
>> MAN_WINBOND,
>> + MAN_SST,
>> MAN_GENERIC,
>> } Manufacturer;
>> @@ -476,6 +479,8 @@ static inline Manufacturer get_man(Flash *s)
>> return MAN_SPANSION;
>> case 0xC2:
>> return MAN_MACRONIX;
>> + case 0xBF:
>> + return MAN_SST;
>> default:
>> return MAN_GENERIC;
>> }
>> @@ -1018,6 +1023,21 @@ static void decode_new_cmd(Flash *s, uint32_t
>> value)
>> s->state = STATE_READING_DATA;
>> break;
>> + case RDID_90:
>> + case RDID_AB:
>> + DB_PRINT_L(0, "populated manf/dev ID\n");
>> + if (get_man(s) == MAN_SST) {
>> + s->data[0] = s->pi->id[0];
>> + s->data[1] = s->pi->id[2];
>> + s->pos = 0;
>> + s->len = 2;
>> + s->data_read_loop = true;
>> + s->state = STATE_READING_DATA;
>>
> I am not sure about this. I am looking at SST25WF080 datasheet, and it
> seem that those commands
> also expect address, and based on that returns manufacturer id or device
> id.
>
>> + } else {
>> + qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n",
>> value);
>> + }
>> + break;
>> +
>> case BULK_ERASE:
>> if (s->write_enable) {
>> DB_PRINT_L(0, "chip erase\n");
>>
> Regard,
> Marcin
>
^ permalink raw reply [flat|nested] 19+ messages in thread
* [Qemu-devel] [PATCH v3 03/13] m25p80: Add support for BRRD/BRWR and BULK_ERASE (0x60)
2017-10-24 19:51 [Qemu-devel] [PATCH v3 00/13] Add support for the ZynqMP Generic QSPI Francisco Iglesias
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 01/13] m25p80: Add support for continuous read out of RDSR and READ_FSR Francisco Iglesias
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 02/13] m25p80: Add support for SST READ ID 0x90/0xAB commands Francisco Iglesias
@ 2017-10-24 19:51 ` Francisco Iglesias
2017-10-25 14:55 ` mar.krzeminski
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 04/13] m25p80: Add support for n25q512a11 and n25q512a13 Francisco Iglesias
` (9 subsequent siblings)
12 siblings, 1 reply; 19+ messages in thread
From: Francisco Iglesias @ 2017-10-24 19:51 UTC (permalink / raw)
To: qemu-devel; +Cc: edgari, alistai, francisco.iglesias
Add support for the bank address register access commands (BRRD/BRWR) and
the BULK_ERASE (0x60) command.
Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
---
hw/block/m25p80.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index c85e8fa..3d2975c 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -331,6 +331,8 @@ typedef enum {
WRDI = 0x4,
RDSR = 0x5,
WREN = 0x6,
+ BRRD = 0x16,
+ BRWR = 0x17,
JEDEC_READ = 0x9f,
BULK_ERASE = 0xc7,
READ_FSR = 0x70,
@@ -368,6 +370,8 @@ typedef enum {
EN_4BYTE_ADDR = 0xB7,
EX_4BYTE_ADDR = 0xE9,
+ BULK_ERASE_60 = 0x60,
+
EXTEND_ADDR_READ = 0xC8,
EXTEND_ADDR_WRITE = 0xC5,
@@ -975,6 +979,15 @@ static void decode_new_cmd(Flash *s, uint32_t value)
}
break;
+ case BRWR:
+ if (s->write_enable) {
+ s->needed_bytes = 1;
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+ }
+ break;
+
case WRDI:
s->write_enable = false;
break;
@@ -1004,6 +1017,12 @@ static void decode_new_cmd(Flash *s, uint32_t value)
s->state = STATE_READING_DATA;
break;
+ case BRRD:
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ break;
+
case JEDEC_READ:
DB_PRINT_L(0, "populated jedec code\n");
for (i = 0; i < s->pi->id_len; i++) {
@@ -1038,6 +1057,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
}
break;
+ case BULK_ERASE_60:
case BULK_ERASE:
if (s->write_enable) {
DB_PRINT_L(0, "chip erase\n");
--
2.9.3
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [Qemu-devel] [PATCH v3 03/13] m25p80: Add support for BRRD/BRWR and BULK_ERASE (0x60)
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 03/13] m25p80: Add support for BRRD/BRWR and BULK_ERASE (0x60) Francisco Iglesias
@ 2017-10-25 14:55 ` mar.krzeminski
0 siblings, 0 replies; 19+ messages in thread
From: mar.krzeminski @ 2017-10-25 14:55 UTC (permalink / raw)
To: Francisco Iglesias, qemu-devel; +Cc: edgari, alistai, francisco.iglesias
W dniu 24.10.2017 o 21:51, Francisco Iglesias pisze:
> Add support for the bank address register access commands (BRRD/BRWR) and
> the BULK_ERASE (0x60) command.
>
> Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
> ---
> hw/block/m25p80.c | 20 ++++++++++++++++++++
> 1 file changed, 20 insertions(+)
>
> diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
> index c85e8fa..3d2975c 100644
> --- a/hw/block/m25p80.c
> +++ b/hw/block/m25p80.c
> @@ -331,6 +331,8 @@ typedef enum {
> WRDI = 0x4,
> RDSR = 0x5,
> WREN = 0x6,
> + BRRD = 0x16,
> + BRWR = 0x17,
Above commands look to be equivalent to
EXTEND_ADDR_READ/EXTEND_ADDR_WRITE from Micron.
IMO both could fall in the same case block.
> JEDEC_READ = 0x9f,
> BULK_ERASE = 0xc7,
> READ_FSR = 0x70,
> @@ -368,6 +370,8 @@ typedef enum {
> EN_4BYTE_ADDR = 0xB7,
> EX_4BYTE_ADDR = 0xE9,
>
> + BULK_ERASE_60 = 0x60,
> +
> EXTEND_ADDR_READ = 0xC8,
> EXTEND_ADDR_WRITE = 0xC5,
>
> @@ -975,6 +979,15 @@ static void decode_new_cmd(Flash *s, uint32_t value)
> }
> break;
>
> + case BRWR:
> + if (s->write_enable) {
> + s->needed_bytes = 1;
> + s->pos = 0;
> + s->len = 0;
> + s->state = STATE_COLLECTING_DATA;
> + }
> + break;
> +
> case WRDI:
> s->write_enable = false;
> break;
> @@ -1004,6 +1017,12 @@ static void decode_new_cmd(Flash *s, uint32_t value)
> s->state = STATE_READING_DATA;
> break;
>
> + case BRRD:
> + s->pos = 0;
> + s->len = 1;
> + s->state = STATE_READING_DATA;
> + break;
> +
I can not see any code where you are actually filling the register
value. s->ear could be used for that as well.
> case JEDEC_READ:
> DB_PRINT_L(0, "populated jedec code\n");
> for (i = 0; i < s->pi->id_len; i++) {
> @@ -1038,6 +1057,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
> }
> break;
>
> + case BULK_ERASE_60:
> case BULK_ERASE:
> if (s->write_enable) {
> DB_PRINT_L(0, "chip erase\n");
Regards,
Marcin
^ permalink raw reply [flat|nested] 19+ messages in thread
* [Qemu-devel] [PATCH v3 04/13] m25p80: Add support for n25q512a11 and n25q512a13
2017-10-24 19:51 [Qemu-devel] [PATCH v3 00/13] Add support for the ZynqMP Generic QSPI Francisco Iglesias
` (2 preceding siblings ...)
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 03/13] m25p80: Add support for BRRD/BRWR and BULK_ERASE (0x60) Francisco Iglesias
@ 2017-10-24 19:51 ` Francisco Iglesias
2017-10-25 18:02 ` mar.krzeminski
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 05/13] xilinx_spips: Move FlashCMD, XilinxQSPIPS and XilinxSPIPSClass Francisco Iglesias
` (8 subsequent siblings)
12 siblings, 1 reply; 19+ messages in thread
From: Francisco Iglesias @ 2017-10-24 19:51 UTC (permalink / raw)
To: qemu-devel; +Cc: edgari, alistai, francisco.iglesias
Add support for Micron (Numonyx) n25q512a11 and n25q512a13 flashes.
Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
---
hw/block/m25p80.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 3d2975c..7f3fcc4 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -240,6 +240,8 @@ static const FlashPartInfo known_devices[] = {
{ INFO("n25q128a13", 0x20ba18, 0, 64 << 10, 256, ER_4K) },
{ INFO("n25q256a11", 0x20bb19, 0, 64 << 10, 512, ER_4K) },
{ INFO("n25q256a13", 0x20ba19, 0, 64 << 10, 512, ER_4K) },
+ { INFO("n25q512a11", 0x20bb20, 0, 64 << 10, 1024, ER_4K) },
+ { INFO("n25q512a13", 0x20ba20, 0, 64 << 10, 1024, ER_4K) },
{ INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) },
{ INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, ER_4K) },
{ INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) },
--
2.9.3
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [Qemu-devel] [PATCH v3 04/13] m25p80: Add support for n25q512a11 and n25q512a13
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 04/13] m25p80: Add support for n25q512a11 and n25q512a13 Francisco Iglesias
@ 2017-10-25 18:02 ` mar.krzeminski
0 siblings, 0 replies; 19+ messages in thread
From: mar.krzeminski @ 2017-10-25 18:02 UTC (permalink / raw)
To: Francisco Iglesias, qemu-devel; +Cc: edgari, alistai, francisco.iglesias
W dniu 24.10.2017 o 21:51, Francisco Iglesias pisze:
> Add support for Micron (Numonyx) n25q512a11 and n25q512a13 flashes.
>
> Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
Acked-by: Marcin Krzemiński <mar.krzeminski@gmail.com>
> ---
> hw/block/m25p80.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
> index 3d2975c..7f3fcc4 100644
> --- a/hw/block/m25p80.c
> +++ b/hw/block/m25p80.c
> @@ -240,6 +240,8 @@ static const FlashPartInfo known_devices[] = {
> { INFO("n25q128a13", 0x20ba18, 0, 64 << 10, 256, ER_4K) },
> { INFO("n25q256a11", 0x20bb19, 0, 64 << 10, 512, ER_4K) },
> { INFO("n25q256a13", 0x20ba19, 0, 64 << 10, 512, ER_4K) },
> + { INFO("n25q512a11", 0x20bb20, 0, 64 << 10, 1024, ER_4K) },
> + { INFO("n25q512a13", 0x20ba20, 0, 64 << 10, 1024, ER_4K) },
> { INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) },
> { INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, ER_4K) },
> { INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) },
^ permalink raw reply [flat|nested] 19+ messages in thread
* [Qemu-devel] [PATCH v3 05/13] xilinx_spips: Move FlashCMD, XilinxQSPIPS and XilinxSPIPSClass
2017-10-24 19:51 [Qemu-devel] [PATCH v3 00/13] Add support for the ZynqMP Generic QSPI Francisco Iglesias
` (3 preceding siblings ...)
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 04/13] m25p80: Add support for n25q512a11 and n25q512a13 Francisco Iglesias
@ 2017-10-24 19:51 ` Francisco Iglesias
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 06/13] xilinx_spips: Update striping to be big-endian bit order Francisco Iglesias
` (7 subsequent siblings)
12 siblings, 0 replies; 19+ messages in thread
From: Francisco Iglesias @ 2017-10-24 19:51 UTC (permalink / raw)
To: qemu-devel; +Cc: edgari, alistai, francisco.iglesias
Move the FlashCMD enum, XilinxQSPIPS and XilinxSPIPSClass structures to the
header for consistency. Also move out a define and remove two dubbel included
headers (while touching the code). Finally, add 4 byte address commands to the
FlashCMD enum.
Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
---
hw/ssi/xilinx_spips.c | 35 -----------------------------------
include/hw/ssi/xilinx_spips.h | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 34 insertions(+), 35 deletions(-)
diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index ef56d35..559fa79 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -27,8 +27,6 @@
#include "sysemu/sysemu.h"
#include "hw/ptimer.h"
#include "qemu/log.h"
-#include "qemu/fifo8.h"
-#include "hw/ssi/ssi.h"
#include "qemu/bitops.h"
#include "hw/ssi/xilinx_spips.h"
#include "qapi/error.h"
@@ -116,44 +114,11 @@
/* 16MB per linear region */
#define LQSPI_ADDRESS_BITS 24
-/* Bite off 4k chunks at a time */
-#define LQSPI_CACHE_SIZE 1024
#define SNOOP_CHECKING 0xFF
#define SNOOP_NONE 0xFE
#define SNOOP_STRIPING 0
-typedef enum {
- READ = 0x3,
- FAST_READ = 0xb,
- DOR = 0x3b,
- QOR = 0x6b,
- DIOR = 0xbb,
- QIOR = 0xeb,
-
- PP = 0x2,
- DPP = 0xa2,
- QPP = 0x32,
-} FlashCMD;
-
-typedef struct {
- XilinxSPIPS parent_obj;
-
- uint8_t lqspi_buf[LQSPI_CACHE_SIZE];
- hwaddr lqspi_cached_addr;
- Error *migration_blocker;
- bool mmio_execution_enabled;
-} XilinxQSPIPS;
-
-typedef struct XilinxSPIPSClass {
- SysBusDeviceClass parent_class;
-
- const MemoryRegionOps *reg_ops;
-
- uint32_t rx_fifo_size;
- uint32_t tx_fifo_size;
-} XilinxSPIPSClass;
-
static inline int num_effective_busses(XilinxSPIPS *s)
{
return (s->regs[R_LQSPI_CFG] & LQSPI_CFG_SEP_BUS &&
diff --git a/include/hw/ssi/xilinx_spips.h b/include/hw/ssi/xilinx_spips.h
index 06aa096..7f9e2fc 100644
--- a/include/hw/ssi/xilinx_spips.h
+++ b/include/hw/ssi/xilinx_spips.h
@@ -32,6 +32,22 @@ typedef struct XilinxSPIPS XilinxSPIPS;
#define XLNX_SPIPS_R_MAX (0x100 / 4)
+/* Bite off 4k chunks at a time */
+#define LQSPI_CACHE_SIZE 1024
+
+typedef enum {
+ READ = 0x3, READ_4 = 0x13,
+ FAST_READ = 0xb, FAST_READ_4 = 0x0c,
+ DOR = 0x3b, DOR_4 = 0x3c,
+ QOR = 0x6b, QOR_4 = 0x6c,
+ DIOR = 0xbb, DIOR_4 = 0xbc,
+ QIOR = 0xeb, QIOR_4 = 0xec,
+
+ PP = 0x2, PP_4 = 0x12,
+ DPP = 0xa2,
+ QPP = 0x32, QPP_4 = 0x34,
+} FlashCMD;
+
struct XilinxSPIPS {
SysBusDevice parent_obj;
@@ -56,6 +72,24 @@ struct XilinxSPIPS {
uint32_t regs[XLNX_SPIPS_R_MAX];
};
+typedef struct {
+ XilinxSPIPS parent_obj;
+
+ uint8_t lqspi_buf[LQSPI_CACHE_SIZE];
+ hwaddr lqspi_cached_addr;
+ Error *migration_blocker;
+ bool mmio_execution_enabled;
+} XilinxQSPIPS;
+
+typedef struct XilinxSPIPSClass {
+ SysBusDeviceClass parent_class;
+
+ const MemoryRegionOps *reg_ops;
+
+ uint32_t rx_fifo_size;
+ uint32_t tx_fifo_size;
+} XilinxSPIPSClass;
+
#define TYPE_XILINX_SPIPS "xlnx.ps7-spi"
#define TYPE_XILINX_QSPIPS "xlnx.ps7-qspi"
--
2.9.3
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [Qemu-devel] [PATCH v3 06/13] xilinx_spips: Update striping to be big-endian bit order
2017-10-24 19:51 [Qemu-devel] [PATCH v3 00/13] Add support for the ZynqMP Generic QSPI Francisco Iglesias
` (4 preceding siblings ...)
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 05/13] xilinx_spips: Move FlashCMD, XilinxQSPIPS and XilinxSPIPSClass Francisco Iglesias
@ 2017-10-24 19:51 ` Francisco Iglesias
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 08/13] xilinx_spips: Make tx/rx_data_bytes more generic and reusable Francisco Iglesias
` (6 subsequent siblings)
12 siblings, 0 replies; 19+ messages in thread
From: Francisco Iglesias @ 2017-10-24 19:51 UTC (permalink / raw)
To: qemu-devel; +Cc: edgari, alistai, francisco.iglesias
Update striping functionality to be big-endian bit order and output even
bits into flash memory connected to the lower QSPI bus and odd bits into
the flash memory connected to the upper QSPI bus.
Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
---
hw/ssi/xilinx_spips.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index 559fa79..7accf5d 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -208,14 +208,14 @@ static void xilinx_spips_reset(DeviceState *d)
xilinx_spips_update_cs_lines(s);
}
-/* N way (num) in place bit striper. Lay out row wise bits (LSB to MSB)
+/* N way (num) in place bit striper. Lay out row wise bits (MSB to LSB)
* column wise (from element 0 to N-1). num is the length of x, and dir
* reverses the direction of the transform. Best illustrated by example:
* Each digit in the below array is a single bit (num == 3):
*
- * {{ 76543210, } ----- stripe (dir == false) -----> {{ FCheb630, }
- * { hgfedcba, } { GDAfc741, }
- * { HGFEDCBA, }} <---- upstripe (dir == true) ----- { HEBgda52, }}
+ * {{ 76543210, } ----- stripe (dir == false) -----> {{ 741gdaFC, }
+ * { hgfedcba, } { 630fcHEB, }
+ * { HGFEDCBA, }} <---- upstripe (dir == true) ----- { 52hebGDA, }}
*/
static inline void stripe8(uint8_t *x, int num, bool dir)
@@ -223,15 +223,15 @@ static inline void stripe8(uint8_t *x, int num, bool dir)
uint8_t r[num];
memset(r, 0, sizeof(uint8_t) * num);
int idx[2] = {0, 0};
- int bit[2] = {0, 0};
+ int bit[2] = {0, 7};
int d = dir;
for (idx[0] = 0; idx[0] < num; ++idx[0]) {
- for (bit[0] = 0; bit[0] < 8; ++bit[0]) {
- r[idx[d]] |= x[idx[!d]] & 1 << bit[!d] ? 1 << bit[d] : 0;
+ for (bit[0] = 7; bit[0] != -1; bit[0] += -1) {
+ r[idx[!d]] |= x[idx[d]] & 1 << bit[d] ? 1 << bit[!d] : 0;
idx[1] = (idx[1] + 1) % num;
if (!idx[1]) {
- bit[1]++;
+ bit[1] += -1;
}
}
}
@@ -266,8 +266,9 @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
}
for (i = 0; i < num_effective_busses(s); ++i) {
+ int bus = num_effective_busses(s) - 1 - i;
DB_PRINT_L(debug_level, "tx = %02x\n", tx_rx[i]);
- tx_rx[i] = ssi_transfer(s->spi[i], (uint32_t)tx_rx[i]);
+ tx_rx[i] = ssi_transfer(s->spi[bus], (uint32_t)tx_rx[i]);
DB_PRINT_L(debug_level, "rx = %02x\n", tx_rx[i]);
}
--
2.9.3
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [Qemu-devel] [PATCH v3 08/13] xilinx_spips: Make tx/rx_data_bytes more generic and reusable
2017-10-24 19:51 [Qemu-devel] [PATCH v3 00/13] Add support for the ZynqMP Generic QSPI Francisco Iglesias
` (5 preceding siblings ...)
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 06/13] xilinx_spips: Update striping to be big-endian bit order Francisco Iglesias
@ 2017-10-24 19:51 ` Francisco Iglesias
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 10/13] xilinx_spips: Add support for 4 byte addresses in the LQSPI Francisco Iglesias
` (5 subsequent siblings)
12 siblings, 0 replies; 19+ messages in thread
From: Francisco Iglesias @ 2017-10-24 19:51 UTC (permalink / raw)
To: qemu-devel; +Cc: edgari, alistai, francisco.iglesias
Make tx/rx_data_bytes more generic so they can be reused (when adding
support for the Zynqmp Generic QSPI).
Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
---
hw/ssi/xilinx_spips.c | 64 +++++++++++++++++++++++++++++----------------------
1 file changed, 37 insertions(+), 27 deletions(-)
diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index 2ee70e9..ad9f1a0 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -47,7 +47,7 @@
/* config register */
#define R_CONFIG (0x00 / 4)
#define IFMODE (1U << 31)
-#define ENDIAN (1 << 26)
+#define R_CONFIG_ENDIAN (1 << 26)
#define MODEFAIL_GEN_EN (1 << 17)
#define MAN_START_COM (1 << 16)
#define MAN_START_EN (1 << 15)
@@ -445,13 +445,28 @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
}
}
-static inline void rx_data_bytes(XilinxSPIPS *s, uint8_t *value, int max)
+static inline void tx_data_bytes(Fifo8 *fifo, uint32_t value, int num, bool be)
{
int i;
+ for (i = 0; i < num && !fifo8_is_full(fifo); ++i) {
+ if (be) {
+ fifo8_push(fifo, (uint8_t)(value >> 24));
+ value <<= 8;
+ } else {
+ fifo8_push(fifo, (uint8_t)value);
+ value >>= 8;
+ }
+ }
+}
- for (i = 0; i < max && !fifo8_is_empty(&s->rx_fifo); ++i) {
- value[i] = fifo8_pop(&s->rx_fifo);
+static inline int rx_data_bytes(Fifo8 *fifo, uint8_t *value, int max)
+{
+ int i;
+
+ for (i = 0; i < max && !fifo8_is_empty(fifo); ++i) {
+ value[i] = fifo8_pop(fifo);
}
+ return max - i;
}
static uint64_t xilinx_spips_read(void *opaque, hwaddr addr,
@@ -461,6 +476,7 @@ static uint64_t xilinx_spips_read(void *opaque, hwaddr addr,
uint32_t mask = ~0;
uint32_t ret;
uint8_t rx_buf[4];
+ int shortfall;
addr >>= 2;
switch (addr) {
@@ -491,9 +507,13 @@ static uint64_t xilinx_spips_read(void *opaque, hwaddr addr,
break;
case R_RX_DATA:
memset(rx_buf, 0, sizeof(rx_buf));
- rx_data_bytes(s, rx_buf, s->num_txrx_bytes);
- ret = s->regs[R_CONFIG] & ENDIAN ? cpu_to_be32(*(uint32_t *)rx_buf)
- : cpu_to_le32(*(uint32_t *)rx_buf);
+ shortfall = rx_data_bytes(&s->rx_fifo, rx_buf, s->num_txrx_bytes);
+ ret = s->regs[R_CONFIG] & R_CONFIG_ENDIAN ?
+ cpu_to_be32(*(uint32_t *)rx_buf) :
+ cpu_to_le32(*(uint32_t *)rx_buf);
+ if (!(s->regs[R_CONFIG] & R_CONFIG_ENDIAN)) {
+ ret <<= 8 * shortfall;
+ }
DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr * 4, ret);
xilinx_spips_update_ixr(s);
return ret;
@@ -504,20 +524,6 @@ static uint64_t xilinx_spips_read(void *opaque, hwaddr addr,
}
-static inline void tx_data_bytes(XilinxSPIPS *s, uint32_t value, int num)
-{
- int i;
- for (i = 0; i < num && !fifo8_is_full(&s->tx_fifo); ++i) {
- if (s->regs[R_CONFIG] & ENDIAN) {
- fifo8_push(&s->tx_fifo, (uint8_t)(value >> 24));
- value <<= 8;
- } else {
- fifo8_push(&s->tx_fifo, (uint8_t)value);
- value >>= 8;
- }
- }
-}
-
static void xilinx_spips_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
@@ -558,16 +564,20 @@ static void xilinx_spips_write(void *opaque, hwaddr addr,
mask = 0;
break;
case R_TX_DATA:
- tx_data_bytes(s, (uint32_t)value, s->num_txrx_bytes);
+ tx_data_bytes(&s->tx_fifo, (uint32_t)value, s->num_txrx_bytes,
+ s->regs[R_CONFIG] & R_CONFIG_ENDIAN);
goto no_reg_update;
case R_TXD1:
- tx_data_bytes(s, (uint32_t)value, 1);
+ tx_data_bytes(&s->tx_fifo, (uint32_t)value, 1,
+ s->regs[R_CONFIG] & R_CONFIG_ENDIAN);
goto no_reg_update;
case R_TXD2:
- tx_data_bytes(s, (uint32_t)value, 2);
+ tx_data_bytes(&s->tx_fifo, (uint32_t)value, 2,
+ s->regs[R_CONFIG] & R_CONFIG_ENDIAN);
goto no_reg_update;
case R_TXD3:
- tx_data_bytes(s, (uint32_t)value, 3);
+ tx_data_bytes(&s->tx_fifo, (uint32_t)value, 3,
+ s->regs[R_CONFIG] & R_CONFIG_ENDIAN);
goto no_reg_update;
}
s->regs[addr] = (s->regs[addr] & ~mask) | (value & mask);
@@ -677,11 +687,11 @@ static void lqspi_load_cache(void *opaque, hwaddr addr)
while (cache_entry < LQSPI_CACHE_SIZE) {
for (i = 0; i < 64; ++i) {
- tx_data_bytes(s, 0, 1);
+ tx_data_bytes(&s->tx_fifo, 0, 1, false);
}
xilinx_spips_flush_txfifo(s);
for (i = 0; i < 64; ++i) {
- rx_data_bytes(s, &q->lqspi_buf[cache_entry++], 1);
+ rx_data_bytes(&s->rx_fifo, &q->lqspi_buf[cache_entry++], 1);
}
}
--
2.9.3
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [Qemu-devel] [PATCH v3 10/13] xilinx_spips: Add support for 4 byte addresses in the LQSPI
2017-10-24 19:51 [Qemu-devel] [PATCH v3 00/13] Add support for the ZynqMP Generic QSPI Francisco Iglesias
` (6 preceding siblings ...)
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 08/13] xilinx_spips: Make tx/rx_data_bytes more generic and reusable Francisco Iglesias
@ 2017-10-24 19:51 ` Francisco Iglesias
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 11/13] xilinx_spips: Don't set TX FIFO UNDERFLOW at cmd done Francisco Iglesias
` (4 subsequent siblings)
12 siblings, 0 replies; 19+ messages in thread
From: Francisco Iglesias @ 2017-10-24 19:51 UTC (permalink / raw)
To: qemu-devel; +Cc: edgari, alistai, francisco.iglesias
Add support for 4 byte addresses in the LQSPI and correct LQSPI_CFG_SEP_BUS.
Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
---
hw/ssi/xilinx_spips.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index df5d908..5e5e8cc 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -92,8 +92,9 @@
#define R_LQSPI_CFG_RESET 0x03A002EB
#define LQSPI_CFG_LQ_MODE (1U << 31)
#define LQSPI_CFG_TWO_MEM (1 << 30)
-#define LQSPI_CFG_SEP_BUS (1 << 30)
+#define LQSPI_CFG_SEP_BUS (1 << 29)
#define LQSPI_CFG_U_PAGE (1 << 28)
+#define LQSPI_CFG_ADDR4 (1 << 27)
#define LQSPI_CFG_MODE_EN (1 << 25)
#define LQSPI_CFG_MODE_WIDTH 8
#define LQSPI_CFG_MODE_SHIFT 16
@@ -697,6 +698,9 @@ static void lqspi_load_cache(void *opaque, hwaddr addr)
fifo8_push(&s->tx_fifo, s->regs[R_LQSPI_CFG] & LQSPI_CFG_INST_CODE);
/* read address */
DB_PRINT_L(0, "pushing read address %06x\n", flash_addr);
+ if (s->regs[R_LQSPI_CFG] & LQSPI_CFG_ADDR4) {
+ fifo8_push(&s->tx_fifo, (uint8_t)(flash_addr >> 24));
+ }
fifo8_push(&s->tx_fifo, (uint8_t)(flash_addr >> 16));
fifo8_push(&s->tx_fifo, (uint8_t)(flash_addr >> 8));
fifo8_push(&s->tx_fifo, (uint8_t)flash_addr);
--
2.9.3
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [Qemu-devel] [PATCH v3 11/13] xilinx_spips: Don't set TX FIFO UNDERFLOW at cmd done
2017-10-24 19:51 [Qemu-devel] [PATCH v3 00/13] Add support for the ZynqMP Generic QSPI Francisco Iglesias
` (7 preceding siblings ...)
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 10/13] xilinx_spips: Add support for 4 byte addresses in the LQSPI Francisco Iglesias
@ 2017-10-24 19:51 ` Francisco Iglesias
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 12/13] xilinx_spips: Add support for the ZynqMP Generic QSPI Francisco Iglesias
` (3 subsequent siblings)
12 siblings, 0 replies; 19+ messages in thread
From: Francisco Iglesias @ 2017-10-24 19:51 UTC (permalink / raw)
To: qemu-devel; +Cc: edgari, alistai, francisco.iglesias
Don't set TX FIFO UNDERFLOW interrupt after done transmiting the commands.
Also update interrupts after reading out the interrupt status.
Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
---
hw/ssi/xilinx_spips.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index 5e5e8cc..ffed5ba 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -324,9 +324,6 @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
uint8_t addr_length;
if (fifo8_is_empty(&s->tx_fifo)) {
- if (!(s->regs[R_LQSPI_CFG] & LQSPI_CFG_LQ_MODE)) {
- s->regs[R_INTR_STATUS] |= IXR_TX_FIFO_UNDERFLOW;
- }
xilinx_spips_update_ixr(s);
return;
} else if (s->snoop_state == SNOOP_STRIPING) {
@@ -525,6 +522,7 @@ static uint64_t xilinx_spips_read(void *opaque, hwaddr addr,
ret = s->regs[addr] & IXR_ALL;
s->regs[addr] = 0;
DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr * 4, ret);
+ xilinx_spips_update_ixr(s);
return ret;
case R_INTR_MASK:
mask = IXR_ALL;
--
2.9.3
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [Qemu-devel] [PATCH v3 12/13] xilinx_spips: Add support for the ZynqMP Generic QSPI
2017-10-24 19:51 [Qemu-devel] [PATCH v3 00/13] Add support for the ZynqMP Generic QSPI Francisco Iglesias
` (8 preceding siblings ...)
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 11/13] xilinx_spips: Don't set TX FIFO UNDERFLOW at cmd done Francisco Iglesias
@ 2017-10-24 19:51 ` Francisco Iglesias
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 13/13] xlnx-zcu102: Add support for the ZynqMP QSPI Francisco Iglesias
` (2 subsequent siblings)
12 siblings, 0 replies; 19+ messages in thread
From: Francisco Iglesias @ 2017-10-24 19:51 UTC (permalink / raw)
To: qemu-devel; +Cc: edgari, alistai, francisco.iglesias
Add support for the Zynq Ultrascale MPSoc Generic QSPI.
Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
---
default-configs/arm-softmmu.mak | 1 +
hw/ssi/xilinx_spips.c | 451 ++++++++++++++++++++++++++++++++++++----
include/hw/ssi/xilinx_spips.h | 30 ++-
3 files changed, 437 insertions(+), 45 deletions(-)
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 5059d13..d09fd34 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -130,3 +130,4 @@ CONFIG_SMBIOS=y
CONFIG_ASPEED_SOC=y
CONFIG_GPIO_KEY=y
CONFIG_MSF2=y
+CONFIG_XILINX_AXI=y
diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index ffed5ba..2a43fb8 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -31,6 +31,7 @@
#include "hw/ssi/xilinx_spips.h"
#include "qapi/error.h"
#include "hw/register.h"
+#include "sysemu/dma.h"
#include "migration/blocker.h"
#ifndef XILINX_SPIPS_ERR_DEBUG
@@ -69,13 +70,30 @@
#define R_INTR_DIS (0x0C / 4)
#define R_INTR_MASK (0x10 / 4)
#define IXR_TX_FIFO_UNDERFLOW (1 << 6)
+/* Poll timeout not implemented */
+#define IXR_RX_FIFO_EMPTY (1 << 11)
+#define IXR_GENERIC_FIFO_FULL (1 << 10)
+#define IXR_GENERIC_FIFO_NOT_FULL (1 << 9)
+#define IXR_TX_FIFO_EMPTY (1 << 8)
+#define IXR_GENERIC_FIFO_EMPTY (1 << 7)
#define IXR_RX_FIFO_FULL (1 << 5)
#define IXR_RX_FIFO_NOT_EMPTY (1 << 4)
#define IXR_TX_FIFO_FULL (1 << 3)
#define IXR_TX_FIFO_NOT_FULL (1 << 2)
#define IXR_TX_FIFO_MODE_FAIL (1 << 1)
#define IXR_RX_FIFO_OVERFLOW (1 << 0)
-#define IXR_ALL ((IXR_TX_FIFO_UNDERFLOW<<1)-1)
+#define IXR_ALL ((1 << 13) - 1)
+#define GQSPI_IXR_MASK 0xFBE
+#define IXR_SELF_CLEAR \
+(IXR_GENERIC_FIFO_EMPTY \
+| IXR_GENERIC_FIFO_FULL \
+| IXR_GENERIC_FIFO_NOT_FULL \
+| IXR_TX_FIFO_EMPTY \
+| IXR_TX_FIFO_FULL \
+| IXR_TX_FIFO_NOT_FULL \
+| IXR_RX_FIFO_EMPTY \
+| IXR_RX_FIFO_FULL \
+| IXR_RX_FIFO_NOT_EMPTY)
#define R_EN (0x14 / 4)
#define R_DELAY (0x18 / 4)
@@ -116,9 +134,54 @@
#define R_MOD_ID (0xFC / 4)
+#define R_GQSPI_SELECT (0x144 / 4)
+ FIELD(GQSPI_SELECT, GENERIC_QSPI_EN, 0, 1)
+#define R_GQSPI_ISR (0x104 / 4)
+#define R_GQSPI_IER (0x108 / 4)
+#define R_GQSPI_IDR (0x10c / 4)
+#define R_GQSPI_IMR (0x110 / 4)
+#define R_GQSPI_TX_THRESH (0x128 / 4)
+#define R_GQSPI_RX_THRESH (0x12c / 4)
+#define R_GQSPI_CNFG (0x100 / 4)
+ FIELD(GQSPI_CNFG, MODE_EN, 30, 2)
+ FIELD(GQSPI_CNFG, GEN_FIFO_START_MODE, 29, 1)
+ FIELD(GQSPI_CNFG, GEN_FIFO_START, 28, 1)
+ FIELD(GQSPI_CNFG, ENDIAN, 26, 1)
+ /* Poll timeout not implemented */
+ FIELD(GQSPI_CNFG, EN_POLL_TIMEOUT, 20, 1)
+ /* QEMU doesnt care about any of these last three */
+ FIELD(GQSPI_CNFG, BR, 3, 3)
+ FIELD(GQSPI_CNFG, CPH, 2, 1)
+ FIELD(GQSPI_CNFG, CPL, 1, 1)
+#define R_GQSPI_GEN_FIFO (0x140 / 4)
+#define R_GQSPI_TXD (0x11c / 4)
+#define R_GQSPI_RXD (0x120 / 4)
+#define R_GQSPI_FIFO_CTRL (0x14c / 4)
+ FIELD(GQSPI_FIFO_CTRL, RX_FIFO_RESET, 2, 1)
+ FIELD(GQSPI_FIFO_CTRL, TX_FIFO_RESET, 1, 1)
+ FIELD(GQSPI_FIFO_CTRL, GENERIC_FIFO_RESET, 0, 1)
+#define R_GQSPI_GFIFO_THRESH (0x150 / 4)
+#define R_GQSPI_DATA_STS (0x15c / 4)
+/* We use the snapshot register to hold the core state for the currently
+ * or most recently executed command. So the generic fifo format is defined
+ * for the snapshot register
+ */
+#define R_GQSPI_GF_SNAPSHOT (0x160 / 4)
+ FIELD(GQSPI_GF_SNAPSHOT, POLL, 19, 1)
+ FIELD(GQSPI_GF_SNAPSHOT, STRIPE, 18, 1)
+ FIELD(GQSPI_GF_SNAPSHOT, RECIEVE, 17, 1)
+ FIELD(GQSPI_GF_SNAPSHOT, TRANSMIT, 16, 1)
+ FIELD(GQSPI_GF_SNAPSHOT, DATA_BUS_SELECT, 14, 2)
+ FIELD(GQSPI_GF_SNAPSHOT, CHIP_SELECT, 12, 2)
+ FIELD(GQSPI_GF_SNAPSHOT, SPI_MODE, 10, 2)
+ FIELD(GQSPI_GF_SNAPSHOT, EXPONENT, 9, 1)
+ FIELD(GQSPI_GF_SNAPSHOT, DATA_XFER, 8, 1)
+ FIELD(GQSPI_GF_SNAPSHOT, IMMEDIATE_DATA, 0, 8)
+#define R_GQSPI_MOD_ID (0x168 / 4)
+#define R_GQSPI_MOD_ID_VALUE 0x010A0000
/* size of TXRX FIFOs */
-#define RXFF_A 32
-#define TXFF_A 32
+#define RXFF_A (128)
+#define TXFF_A (128)
#define RXFF_A_Q (64 * 4)
#define TXFF_A_Q (64 * 4)
@@ -137,37 +200,54 @@ static inline int num_effective_busses(XilinxSPIPS *s)
s->regs[R_LQSPI_CFG] & LQSPI_CFG_TWO_MEM) ? s->num_busses : 1;
}
-static inline bool xilinx_spips_cs_is_set(XilinxSPIPS *s, int i, int field)
+static void xilinx_spips_update_cs_lines_legacy(XilinxSPIPS *s, int *field)
{
- return ~field & (1 << i) && (s->regs[R_CONFIG] & MANUAL_CS
- || !fifo8_is_empty(&s->tx_fifo));
+ *field = ~((s->regs[R_CONFIG] & CS) >> CS_SHIFT);
+ /* In dual parallel, mirror low CS to both */
+ if (num_effective_busses(s) == 2) {
+ /* Single bit chip-select for qspi */
+ *field &= 0x1;
+ *field |= *field << 1;
+ /* Dual stack U-Page */
+ } else if (s->regs[R_LQSPI_CFG] & LQSPI_CFG_TWO_MEM &&
+ s->regs[R_LQSPI_STS] & LQSPI_CFG_U_PAGE) {
+ /* Single bit chip-select for qspi */
+ *field &= 0x1;
+ /* change from CS0 to CS1 */
+ *field <<= 1;
+ }
+ /* Auto CS */
+ if (!(s->regs[R_CONFIG] & MANUAL_CS) &&
+ fifo8_is_empty(&s->tx_fifo)) {
+ *field = 0;
+ }
}
static void xilinx_spips_update_cs_lines(XilinxSPIPS *s)
{
- int i, j;
- bool found = false;
- int field = s->regs[R_CONFIG] >> CS_SHIFT;
+ int i;
+ int field = 0;
+ if (!ARRAY_FIELD_EX32(s->regs, GQSPI_SELECT, GENERIC_QSPI_EN)) {
+ xilinx_spips_update_cs_lines_legacy(s, &field);
+ } else if (s->regs[R_GQSPI_GF_SNAPSHOT]) {
+ field = ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, CHIP_SELECT);
+ } else {
+ /* Do nothing */
+ return;
+ }
for (i = 0; i < s->num_cs; i++) {
- for (j = 0; j < num_effective_busses(s); j++) {
- int upage = !!(s->regs[R_LQSPI_STS] & LQSPI_CFG_U_PAGE);
- int cs_to_set = (j * s->num_cs + i + upage) %
- (s->num_cs * s->num_busses);
-
- if (xilinx_spips_cs_is_set(s, i, field) && !found) {
- DB_PRINT_L(0, "selecting slave %d\n", i);
- qemu_set_irq(s->cs_lines[cs_to_set], 0);
- } else {
- DB_PRINT_L(0, "deselecting slave %d\n", i);
- qemu_set_irq(s->cs_lines[cs_to_set], 1);
- }
- }
- if (xilinx_spips_cs_is_set(s, i, field)) {
- found = true;
+ bool old_state = s->cs_lines_state[i];
+ bool new_state = field & (1 << i);
+
+ if (old_state != new_state) {
+ s->cs_lines_state[i] = new_state;
+ s->rx_discard = ARRAY_FIELD_EX32(s->regs, CMND, RX_DISCARD);
+ DB_PRINT_L(0, "%sselecting slave %d\n", new_state ? "" : "de", i);
}
+ qemu_set_irq(s->cs_lines[i], !new_state);
}
- if (!found) {
+ if (!(field & ((1 << s->num_cs) - 1))) {
s->snoop_state = SNOOP_CHECKING;
s->cmd_dummies = 0;
s->link_state = 1;
@@ -179,22 +259,48 @@ static void xilinx_spips_update_cs_lines(XilinxSPIPS *s)
static void xilinx_spips_update_ixr(XilinxSPIPS *s)
{
- if (s->regs[R_LQSPI_CFG] & LQSPI_CFG_LQ_MODE) {
- return;
+ int new_irqline;
+ uint32_t qspi_int, gqspi_int;
+
+ s->regs[R_GQSPI_ISR] &= ~IXR_SELF_CLEAR;
+ s->regs[R_GQSPI_ISR] |=
+ (fifo32_is_empty(&s->fifo_g) ? IXR_GENERIC_FIFO_EMPTY : 0) |
+ (fifo32_is_full(&s->fifo_g) ? IXR_GENERIC_FIFO_FULL : 0) |
+ (s->fifo_g.fifo.num < s->regs[R_GQSPI_GFIFO_THRESH] ?
+ IXR_GENERIC_FIFO_NOT_FULL : 0) |
+ (fifo8_is_empty(&s->rx_fifo_g) ? IXR_RX_FIFO_EMPTY : 0) |
+ (fifo8_is_full(&s->rx_fifo_g) ? IXR_RX_FIFO_FULL : 0) |
+ (s->rx_fifo_g.num >= s->regs[R_GQSPI_RX_THRESH] ?
+ IXR_RX_FIFO_NOT_EMPTY : 0) |
+ (fifo8_is_empty(&s->tx_fifo_g) ? IXR_TX_FIFO_EMPTY : 0) |
+ (fifo8_is_full(&s->tx_fifo_g) ? IXR_TX_FIFO_FULL : 0) |
+ (s->tx_fifo_g.num < s->regs[R_GQSPI_TX_THRESH] ?
+ IXR_TX_FIFO_NOT_FULL : 0);
+
+ if (!(s->regs[R_LQSPI_CFG] & LQSPI_CFG_LQ_MODE)) {
+ s->regs[R_INTR_STATUS] &= ~IXR_SELF_CLEAR;
+ s->regs[R_INTR_STATUS] |=
+ (fifo8_is_full(&s->rx_fifo) ? IXR_RX_FIFO_FULL : 0) |
+ (s->rx_fifo.num >= s->regs[R_RX_THRES] ?
+ IXR_RX_FIFO_NOT_EMPTY : 0) |
+ (fifo8_is_full(&s->tx_fifo) ? IXR_TX_FIFO_FULL : 0) |
+ (s->tx_fifo.num < s->regs[R_TX_THRES] ? IXR_TX_FIFO_NOT_FULL : 0);
+ if (object_dynamic_cast(OBJECT(s), TYPE_XLNX_ZYNQMP_QSPIPS)) {
+ s->regs[R_INTR_STATUS] = fifo8_is_empty(&s->tx_fifo) ?
+ IXR_TX_FIFO_EMPTY : 0;
+ }
}
- /* These are set/cleared as they occur */
- s->regs[R_INTR_STATUS] &= (IXR_TX_FIFO_UNDERFLOW | IXR_RX_FIFO_OVERFLOW |
- IXR_TX_FIFO_MODE_FAIL);
- /* these are pure functions of fifo state, set them here */
- s->regs[R_INTR_STATUS] |=
- (fifo8_is_full(&s->rx_fifo) ? IXR_RX_FIFO_FULL : 0) |
- (s->rx_fifo.num >= s->regs[R_RX_THRES] ? IXR_RX_FIFO_NOT_EMPTY : 0) |
- (fifo8_is_full(&s->tx_fifo) ? IXR_TX_FIFO_FULL : 0) |
- (s->tx_fifo.num < s->regs[R_TX_THRES] ? IXR_TX_FIFO_NOT_FULL : 0);
+
+ /* QSPI/SPI Interrupt Trigger Status */
+ qspi_int = s->regs[R_INTR_MASK] & s->regs[R_INTR_STATUS];
+ /* GQSPI Interrupt Trigger Status */
+ gqspi_int = (~s->regs[R_GQSPI_IMR]) & s->regs[R_GQSPI_ISR] &
+ GQSPI_IXR_MASK;
/* drive external interrupt pin */
- int new_irqline = !!(s->regs[R_INTR_MASK] & s->regs[R_INTR_STATUS] &
- IXR_ALL);
+ new_irqline = !!((qspi_int | gqspi_int) & IXR_ALL);
if (new_irqline != s->irqline) {
+ DB_PRINT_L(0, "IRQ state is changing %" PRIx32 " -> %" PRIx32 "\n",
+ s->irqline, new_irqline);
s->irqline = new_irqline;
qemu_set_irq(s->irq, s->irqline);
}
@@ -211,11 +317,18 @@ static void xilinx_spips_reset(DeviceState *d)
fifo8_reset(&s->rx_fifo);
fifo8_reset(&s->rx_fifo);
+ fifo8_reset(&s->rx_fifo_g);
+ fifo8_reset(&s->rx_fifo_g);
+ fifo32_reset(&s->fifo_g);
/* non zero resets */
s->regs[R_CONFIG] |= MODEFAIL_GEN_EN;
s->regs[R_SLAVE_IDLE_COUNT] = 0xFF;
s->regs[R_TX_THRES] = 1;
s->regs[R_RX_THRES] = 1;
+ s->regs[R_GQSPI_TX_THRESH] = 1;
+ s->regs[R_GQSPI_RX_THRESH] = 1;
+ s->regs[R_GQSPI_GFIFO_THRESH] = 1;
+ s->regs[R_GQSPI_IMR] = GQSPI_IXR_MASK;
/* FIXME: move magic number definition somewhere sensible */
s->regs[R_MOD_ID] = 0x01090106;
s->regs[R_LQSPI_CFG] = R_LQSPI_CFG_RESET;
@@ -225,6 +338,7 @@ static void xilinx_spips_reset(DeviceState *d)
s->snoop_state = SNOOP_CHECKING;
s->cmd_dummies = 0;
s->man_start_com = false;
+ s->man_start_com_g = false;
xilinx_spips_update_ixr(s);
xilinx_spips_update_cs_lines(s);
}
@@ -259,6 +373,112 @@ static inline void stripe8(uint8_t *x, int num, bool dir)
memcpy(x, r, sizeof(uint8_t) * num);
}
+static void xilinx_spips_flush_fifo_g(XilinxSPIPS *s)
+{
+ while (s->regs[R_GQSPI_DATA_STS] || !fifo32_is_empty(&s->fifo_g)) {
+ uint8_t tx_rx[2] = { 0 };
+ int num_stripes = 1;
+ uint8_t busses;
+ int i;
+
+ if (!s->regs[R_GQSPI_DATA_STS]) {
+ uint8_t imm;
+
+ s->regs[R_GQSPI_GF_SNAPSHOT] = fifo32_pop(&s->fifo_g);
+ DB_PRINT_L(0, "Popped GQSPI command %" PRIx32 "\n",
+ s->regs[R_GQSPI_GF_SNAPSHOT]);
+ if (!s->regs[R_GQSPI_GF_SNAPSHOT]) {
+ DB_PRINT_L(0, "Dummy GQSPI Delay Command Entry, Do nothing");
+ continue;
+ }
+ xilinx_spips_update_cs_lines(s);
+
+ imm = ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, IMMEDIATE_DATA);
+ if (!ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, DATA_XFER)) {
+ /* immedate transfer */
+ if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, TRANSMIT) ||
+ ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE)) {
+ s->regs[R_GQSPI_DATA_STS] = 1;
+ /* CS setup/hold - do nothing */
+ } else {
+ s->regs[R_GQSPI_DATA_STS] = 0;
+ }
+ } else if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, EXPONENT)) {
+ if (imm > 31) {
+ qemu_log_mask(LOG_UNIMP, "QSPI exponential transfer too"
+ " long - 2 ^ %" PRId8 " requested\n", imm);
+ }
+ s->regs[R_GQSPI_DATA_STS] = 1ul << imm;
+ } else {
+ s->regs[R_GQSPI_DATA_STS] = imm;
+ }
+ }
+ /* Zero length transfer check */
+ if (!s->regs[R_GQSPI_DATA_STS]) {
+ continue;
+ }
+ if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE) &&
+ fifo8_is_full(&s->rx_fifo_g)) {
+ /* No space in RX fifo for transfer - try again later */
+ return;
+ }
+ if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, STRIPE) &&
+ (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, TRANSMIT) ||
+ ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE))) {
+ num_stripes = 2;
+ }
+ if (!ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, DATA_XFER)) {
+ tx_rx[0] = ARRAY_FIELD_EX32(s->regs,
+ GQSPI_GF_SNAPSHOT, IMMEDIATE_DATA);
+ } else if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, TRANSMIT)) {
+ for (i = 0; i < num_stripes; ++i) {
+ if (!fifo8_is_empty(&s->tx_fifo_g)) {
+ tx_rx[i] = fifo8_pop(&s->tx_fifo_g);
+ s->tx_fifo_g_align++;
+ } else {
+ return;
+ }
+ }
+ }
+ if (num_stripes == 1) {
+ /* mirror */
+ tx_rx[1] = tx_rx[0];
+ }
+ busses = ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, DATA_BUS_SELECT);
+ for (i = 0; i < 2; ++i) {
+ DB_PRINT_L((busses & (1 << i)) ? 1 : 0,
+ "bus %d tx = %02x\n", i, tx_rx[i]);
+ tx_rx[i] = ssi_transfer(s->spi[i], tx_rx[i]);
+ DB_PRINT_L((busses & (1 << i)) ? 1 : 0,
+ "bus %d rx = %02x\n", i, tx_rx[i]);
+ }
+ if (s->regs[R_GQSPI_DATA_STS] > 1 &&
+ busses == 0x3 && num_stripes == 2) {
+ s->regs[R_GQSPI_DATA_STS] -= 2;
+ } else if (s->regs[R_GQSPI_DATA_STS] > 0) {
+ s->regs[R_GQSPI_DATA_STS]--;
+ }
+ if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE)) {
+ for (i = 0; i < 2; ++i) {
+ if (busses & (1 << i)) {
+ DB_PRINT_L(1, "bus %d push_byte = %02x\n",
+ i, tx_rx[i]);
+ fifo8_push(&s->rx_fifo_g, tx_rx[i]);
+ s->rx_fifo_g_align++;
+ }
+ }
+ }
+ if (!s->regs[R_GQSPI_DATA_STS]) {
+ for (; s->tx_fifo_g_align % 4; s->tx_fifo_g_align++) {
+ fifo8_pop(&s->tx_fifo_g);
+ }
+ for (; s->rx_fifo_g_align % 4; s->rx_fifo_g_align++) {
+ fifo8_push(&s->rx_fifo_g, 0);
+ }
+ }
+ }
+}
+
static int xilinx_spips_num_dummies(XilinxQSPIPS *qs, uint8_t command)
{
if (!qs) {
@@ -482,15 +702,27 @@ static void xilinx_spips_check_zero_pump(XilinxSPIPS *s)
static void xilinx_spips_check_flush(XilinxSPIPS *s)
{
- if (s->man_start_com ||
- (!fifo8_is_empty(&s->tx_fifo) &&
- !(s->regs[R_CONFIG] & MAN_START_EN))) {
- xilinx_spips_check_zero_pump(s);
- xilinx_spips_flush_txfifo(s);
+ bool gqspi_has_work = s->regs[R_GQSPI_DATA_STS] ||
+ !fifo32_is_empty(&s->fifo_g);
+
+ if (ARRAY_FIELD_EX32(s->regs, GQSPI_SELECT, GENERIC_QSPI_EN)) {
+ if (s->man_start_com_g || (gqspi_has_work &&
+ !ARRAY_FIELD_EX32(s->regs, GQSPI_CNFG, GEN_FIFO_START_MODE))) {
+ xilinx_spips_flush_fifo_g(s);
+ }
+ } else {
+ if (s->man_start_com || (!fifo8_is_empty(&s->tx_fifo) &&
+ !(s->regs[R_CONFIG] & MAN_START_EN))) {
+ xilinx_spips_check_zero_pump(s);
+ xilinx_spips_flush_txfifo(s);
+ }
}
if (fifo8_is_empty(&s->tx_fifo) && !s->regs[R_TRANSFER_SIZE]) {
s->man_start_com = false;
}
+ if (!gqspi_has_work) {
+ s->man_start_com_g = false;
+ }
xilinx_spips_update_ixr(s);
}
@@ -504,6 +736,53 @@ static inline int rx_data_bytes(Fifo8 *fifo, uint8_t *value, int max)
return max - i;
}
+static const void *pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *num)
+{
+ void *ret;
+
+ if (max == 0 || max > fifo->num) {
+ abort();
+ }
+ *num = MIN(fifo->capacity - fifo->head, max);
+ ret = &fifo->data[fifo->head];
+ fifo->head += *num;
+ fifo->head %= fifo->capacity;
+ fifo->num -= *num;
+ return ret;
+}
+
+static void xlnx_zynqmp_qspips_notify(void *opaque)
+{
+ XlnxZynqMPQSPIPS *rq = XLNX_ZYNQMP_QSPIPS(opaque);
+ XilinxSPIPS *s = XILINX_SPIPS(rq);
+ Fifo8 *recv_fifo;
+
+ if (ARRAY_FIELD_EX32(s->regs, GQSPI_SELECT, GENERIC_QSPI_EN)) {
+ if (!(ARRAY_FIELD_EX32(s->regs, GQSPI_CNFG, MODE_EN) == 2)) {
+ return;
+ }
+ recv_fifo = &s->rx_fifo_g;
+ } else {
+ if (!(s->regs[R_CMND] & R_CMND_DMA_EN)) {
+ return;
+ }
+ recv_fifo = &s->rx_fifo;
+ }
+ while (recv_fifo->num >= 4
+ && stream_can_push(rq->dma, xlnx_zynqmp_qspips_notify, rq))
+ {
+ size_t ret;
+ uint32_t num;
+ const void *rxd = pop_buf(recv_fifo, 4, &num);
+
+ memcpy(rq->dma_buf, rxd, num);
+
+ ret = stream_push(rq->dma, rq->dma_buf, 4);
+ assert(ret == 4);
+ xilinx_spips_check_flush(s);
+ }
+}
+
static uint64_t xilinx_spips_read(void *opaque, hwaddr addr,
unsigned size)
{
@@ -551,6 +830,23 @@ static uint64_t xilinx_spips_read(void *opaque, hwaddr addr,
ret <<= 8 * shortfall;
}
DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr * 4, ret);
+ xilinx_spips_check_flush(s);
+ xilinx_spips_update_ixr(s);
+ return ret;
+ case R_GQSPI_RXD:
+ if (fifo8_is_empty(&s->rx_fifo_g)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Read from empty GQSPI RX FIFO\n");
+ return 0;
+ }
+ memset(rx_buf, 0, sizeof(rx_buf));
+ shortfall = rx_data_bytes(&s->rx_fifo_g, rx_buf, s->num_txrx_bytes);
+ ret = ARRAY_FIELD_EX32(s->regs, GQSPI_CNFG, ENDIAN) ?
+ cpu_to_be32(*(uint32_t *)rx_buf) :
+ cpu_to_le32(*(uint32_t *)rx_buf);
+ if (!ARRAY_FIELD_EX32(s->regs, GQSPI_CNFG, ENDIAN)) {
+ ret <<= 8 * shortfall;
+ }
+ xilinx_spips_check_flush(s);
xilinx_spips_update_ixr(s);
return ret;
}
@@ -614,6 +910,49 @@ static void xilinx_spips_write(void *opaque, hwaddr addr,
tx_data_bytes(&s->tx_fifo, (uint32_t)value, 3,
s->regs[R_CONFIG] & R_CONFIG_ENDIAN);
goto no_reg_update;
+ case R_GQSPI_CNFG:
+ mask = ~(R_GQSPI_CNFG_GEN_FIFO_START_MASK);
+ if (FIELD_EX32(value, GQSPI_CNFG, GEN_FIFO_START) &&
+ ARRAY_FIELD_EX32(s->regs, GQSPI_CNFG, GEN_FIFO_START_MODE)) {
+ s->man_start_com_g = true;
+ }
+ break;
+ case R_GQSPI_GEN_FIFO:
+ if (!fifo32_is_full(&s->fifo_g)) {
+ fifo32_push(&s->fifo_g, value);
+ }
+ goto no_reg_update;
+ case R_GQSPI_TXD:
+ tx_data_bytes(&s->tx_fifo_g, (uint32_t)value, 4,
+ ARRAY_FIELD_EX32(s->regs, GQSPI_CNFG, ENDIAN));
+ goto no_reg_update;
+ case R_GQSPI_FIFO_CTRL:
+ mask = 0;
+ if (FIELD_EX32(value, GQSPI_FIFO_CTRL, GENERIC_FIFO_RESET)) {
+ fifo32_reset(&s->fifo_g);
+ }
+ if (FIELD_EX32(value, GQSPI_FIFO_CTRL, TX_FIFO_RESET)) {
+ fifo8_reset(&s->tx_fifo_g);
+ }
+ if (FIELD_EX32(value, GQSPI_FIFO_CTRL, RX_FIFO_RESET)) {
+ fifo8_reset(&s->rx_fifo_g);
+ }
+ break;
+ case R_GQSPI_IDR:
+ s->regs[R_GQSPI_IMR] |= value;
+ goto no_reg_update;
+ case R_GQSPI_IER:
+ s->regs[R_GQSPI_IMR] &= ~value;
+ goto no_reg_update;
+ case R_GQSPI_ISR:
+ s->regs[R_GQSPI_ISR] &= ~value;
+ goto no_reg_update;
+ case R_GQSPI_IMR:
+ case R_GQSPI_RXD:
+ case R_GQSPI_GF_SNAPSHOT:
+ case R_GQSPI_MOD_ID:
+ mask = 0;
+ break;
}
s->regs[addr] = (s->regs[addr] & ~mask) | (value & mask);
no_reg_update:
@@ -657,6 +996,9 @@ static void xilinx_qspips_write(void *opaque, hwaddr addr,
if (s->regs[R_CMND] & R_CMND_RXFIFO_DRAIN) {
fifo8_reset(&s->rx_fifo);
}
+ if (object_dynamic_cast(OBJECT(s), TYPE_XLNX_ZYNQMP_QSPIPS)) {
+ xlnx_zynqmp_qspips_notify(s);
+ }
}
static const MemoryRegionOps qspips_ops = {
@@ -802,6 +1144,7 @@ static void xilinx_spips_realize(DeviceState *dev, Error **errp)
}
s->cs_lines = g_new0(qemu_irq, s->num_cs * s->num_busses);
+ s->cs_lines_state = g_new0(bool, s->num_cs * s->num_busses);
for (i = 0, cs = s->cs_lines; i < s->num_busses; ++i, cs += s->num_cs) {
ssi_auto_connect_slaves(DEVICE(s), cs, s->spi[i]);
}
@@ -819,6 +1162,9 @@ static void xilinx_spips_realize(DeviceState *dev, Error **errp)
fifo8_create(&s->rx_fifo, xsc->rx_fifo_size);
fifo8_create(&s->tx_fifo, xsc->tx_fifo_size);
+ fifo8_create(&s->rx_fifo_g, xsc->rx_fifo_size);
+ fifo8_create(&s->tx_fifo_g, xsc->tx_fifo_size);
+ fifo32_create(&s->fifo_g, 32);
}
static void xilinx_qspips_realize(DeviceState *dev, Error **errp)
@@ -850,6 +1196,17 @@ static void xilinx_qspips_realize(DeviceState *dev, Error **errp)
}
}
+static void xlnx_zynqmp_qspips_init(Object *obj)
+{
+ XlnxZynqMPQSPIPS *rq = XLNX_ZYNQMP_QSPIPS(obj);
+
+ object_property_add_link(obj, "stream-connected-dma", TYPE_STREAM_SLAVE,
+ (Object **)&rq->dma,
+ object_property_allow_set_link,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE,
+ NULL);
+}
+
static int xilinx_spips_post_load(void *opaque, int version_id)
{
xilinx_spips_update_ixr((XilinxSPIPS *)opaque);
@@ -930,10 +1287,18 @@ static const TypeInfo xilinx_qspips_info = {
.class_init = xilinx_qspips_class_init,
};
+static const TypeInfo xlnx_zynqmp_qspips_info = {
+ .name = TYPE_XLNX_ZYNQMP_QSPIPS,
+ .parent = TYPE_XILINX_QSPIPS,
+ .instance_size = sizeof(XlnxZynqMPQSPIPS),
+ .instance_init = xlnx_zynqmp_qspips_init,
+};
+
static void xilinx_spips_register_types(void)
{
type_register_static(&xilinx_spips_info);
type_register_static(&xilinx_qspips_info);
+ type_register_static(&xlnx_zynqmp_qspips_info);
}
type_init(xilinx_spips_register_types)
diff --git a/include/hw/ssi/xilinx_spips.h b/include/hw/ssi/xilinx_spips.h
index df6e245..35f6a6f 100644
--- a/include/hw/ssi/xilinx_spips.h
+++ b/include/hw/ssi/xilinx_spips.h
@@ -26,11 +26,12 @@
#define XILINX_SPIPS_H
#include "hw/ssi/ssi.h"
-#include "qemu/fifo8.h"
+#include "qemu/fifo32.h"
+#include "hw/stream.h"
typedef struct XilinxSPIPS XilinxSPIPS;
-#define XLNX_SPIPS_R_MAX (0x100 / 4)
+#define XLNX_SPIPS_R_MAX 0x200
/* Bite off 4k chunks at a time */
#define LQSPI_CACHE_SIZE 1024
@@ -66,10 +67,23 @@ struct XilinxSPIPS {
uint8_t link_state_next;
uint8_t link_state_next_when;
qemu_irq *cs_lines;
+ bool *cs_lines_state;
SSIBus **spi;
Fifo8 rx_fifo;
Fifo8 tx_fifo;
+ /* GQSPI has seperate tx/rx fifos */
+ Fifo8 rx_fifo_g;
+ Fifo8 tx_fifo_g;
+ Fifo32 fifo_g;
+ /*
+ * at the end of each generic command, misaligned extra bytes are discard
+ * or padded to tx and rx respectively to round it out (and avoid need for
+ * individual byte access. Since we use byte fifos, keep track of the
+ * alignment WRT to word access.
+ */
+ uint8_t rx_fifo_g_align;
+ uint8_t tx_fifo_g_align;
uint8_t num_txrx_bytes;
uint32_t rx_discard;
@@ -77,6 +91,7 @@ struct XilinxSPIPS {
uint32_t regs[XLNX_SPIPS_R_MAX];
bool man_start_com;
+ bool man_start_com_g;
};
typedef struct {
@@ -88,6 +103,13 @@ typedef struct {
bool mmio_execution_enabled;
} XilinxQSPIPS;
+typedef struct {
+ XilinxQSPIPS parent_obj;
+
+ StreamSlave *dma;
+ uint8_t dma_buf[4];
+} XlnxZynqMPQSPIPS;
+
typedef struct XilinxSPIPSClass {
SysBusDeviceClass parent_class;
@@ -99,6 +121,7 @@ typedef struct XilinxSPIPSClass {
#define TYPE_XILINX_SPIPS "xlnx.ps7-spi"
#define TYPE_XILINX_QSPIPS "xlnx.ps7-qspi"
+#define TYPE_XLNX_ZYNQMP_QSPIPS "xlnx.usmp-gqspi"
#define XILINX_SPIPS(obj) \
OBJECT_CHECK(XilinxSPIPS, (obj), TYPE_XILINX_SPIPS)
@@ -110,4 +133,7 @@ typedef struct XilinxSPIPSClass {
#define XILINX_QSPIPS(obj) \
OBJECT_CHECK(XilinxQSPIPS, (obj), TYPE_XILINX_QSPIPS)
+#define XLNX_ZYNQMP_QSPIPS(obj) \
+ OBJECT_CHECK(XlnxZynqMPQSPIPS, (obj), TYPE_XLNX_ZYNQMP_QSPIPS)
+
#endif /* XILINX_SPIPS_H */
--
2.9.3
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [Qemu-devel] [PATCH v3 13/13] xlnx-zcu102: Add support for the ZynqMP QSPI
2017-10-24 19:51 [Qemu-devel] [PATCH v3 00/13] Add support for the ZynqMP Generic QSPI Francisco Iglesias
` (9 preceding siblings ...)
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 12/13] xilinx_spips: Add support for the ZynqMP Generic QSPI Francisco Iglesias
@ 2017-10-24 19:51 ` Francisco Iglesias
2017-10-24 20:04 ` [Qemu-devel] [PATCH v3 09/13] xilinx_spips: Add support for zero pumping Francisco Iglesias
2017-11-02 23:59 ` [Qemu-devel] [PATCH v7 " Francisco Iglesias
12 siblings, 0 replies; 19+ messages in thread
From: Francisco Iglesias @ 2017-10-24 19:51 UTC (permalink / raw)
To: qemu-devel; +Cc: edgari, alistai, francisco.iglesias
Add support for the ZynqMP QSPI (consisting of the Generic QSPI and Legacy
QSPI) and connect Numonyx n25q512a11 flashes to it.
Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
---
hw/arm/xlnx-zcu102.c | 23 +++++++++++++++++++++++
hw/arm/xlnx-zynqmp.c | 24 ++++++++++++++++++++++++
include/hw/arm/xlnx-zynqmp.h | 5 +++++
3 files changed, 52 insertions(+)
diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c
index 519a16e..7d61972 100644
--- a/hw/arm/xlnx-zcu102.c
+++ b/hw/arm/xlnx-zcu102.c
@@ -150,6 +150,29 @@ static void xlnx_zynqmp_init(XlnxZCU102 *s, MachineState *machine)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.spi[i]), 1, cs_line);
}
+ for (i = 0; i < XLNX_ZYNQMP_NUM_QSPI_FLASH; i++) {
+ SSIBus *spi_bus;
+ DeviceState *flash_dev;
+ qemu_irq cs_line;
+ DriveInfo *dinfo = drive_get_next(IF_MTD);
+ int bus = i / XLNX_ZYNQMP_NUM_QSPI_BUS_CS;
+ gchar *bus_name = g_strdup_printf("qspi%d", bus);
+
+ spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(&s->soc), bus_name);
+ g_free(bus_name);
+
+ flash_dev = ssi_create_slave_no_init(spi_bus, "n25q512a11");
+ if (dinfo) {
+ qdev_prop_set_drive(flash_dev, "drive", blk_by_legacy_dinfo(dinfo),
+ &error_fatal);
+ }
+ qdev_init_nofail(flash_dev);
+
+ cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
+
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.qspi), i + 1, cs_line);
+ }
+
/* TODO create and connect IDE devices for ide_drive_get() */
xlnx_zcu102_binfo.ram_size = ram_size;
diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
index d4b6560..f7c8b4b 100644
--- a/hw/arm/xlnx-zynqmp.c
+++ b/hw/arm/xlnx-zynqmp.c
@@ -40,6 +40,10 @@
#define SATA_ADDR 0xFD0C0000
#define SATA_NUM_PORTS 2
+#define QSPI_ADDR 0xff0f0000
+#define LQSPI_ADDR 0xc0000000
+#define QSPI_IRQ 15
+
#define DP_ADDR 0xfd4a0000
#define DP_IRQ 113
@@ -169,6 +173,9 @@ static void xlnx_zynqmp_init(Object *obj)
qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default());
}
+ object_initialize(&s->qspi, sizeof(s->qspi), TYPE_XLNX_ZYNQMP_QSPIPS);
+ qdev_set_parent_bus(DEVICE(&s->qspi), sysbus_get_default());
+
object_initialize(&s->dp, sizeof(s->dp), TYPE_XLNX_DP);
qdev_set_parent_bus(DEVICE(&s->dp), sysbus_get_default());
@@ -405,6 +412,23 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
g_free(bus_name);
}
+ object_property_set_bool(OBJECT(&s->qspi), true, "realized", &err);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi), 0, QSPI_ADDR);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi), 1, LQSPI_ADDR);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->qspi), 0, gic_spi[QSPI_IRQ]);
+ for (i = 0; i < XLNX_ZYNQMP_NUM_QSPI_BUS; i++) {
+ gchar *bus_name;
+ gchar *target_bus;
+ /* Alias controller SPI bus to the SoC itself */
+ bus_name = g_strdup_printf("qspi%d", i);
+ target_bus = g_strdup_printf("spi%d", i);
+ object_property_add_alias(OBJECT(s), bus_name,
+ OBJECT(&s->qspi), target_bus,
+ &error_abort);
+ g_free(bus_name);
+ g_free(target_bus);
+ }
+
object_property_set_bool(OBJECT(&s->dp), true, "realized", &err);
if (err) {
error_propagate(errp, err);
diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h
index 6eff81a..3e6fb9b 100644
--- a/include/hw/arm/xlnx-zynqmp.h
+++ b/include/hw/arm/xlnx-zynqmp.h
@@ -40,6 +40,10 @@
#define XLNX_ZYNQMP_NUM_SDHCI 2
#define XLNX_ZYNQMP_NUM_SPIS 2
+#define XLNX_ZYNQMP_NUM_QSPI_BUS 2
+#define XLNX_ZYNQMP_NUM_QSPI_BUS_CS 2
+#define XLNX_ZYNQMP_NUM_QSPI_FLASH 4
+
#define XLNX_ZYNQMP_NUM_OCM_BANKS 4
#define XLNX_ZYNQMP_OCM_RAM_0_ADDRESS 0xFFFC0000
#define XLNX_ZYNQMP_OCM_RAM_SIZE 0x10000
@@ -83,6 +87,7 @@ typedef struct XlnxZynqMPState {
SysbusAHCIState sata;
SDHCIState sdhci[XLNX_ZYNQMP_NUM_SDHCI];
XilinxSPIPS spi[XLNX_ZYNQMP_NUM_SPIS];
+ XlnxZynqMPQSPIPS qspi;
XlnxDPState dp;
XlnxDPDMAState dpdma;
--
2.9.3
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [Qemu-devel] [PATCH v3 09/13] xilinx_spips: Add support for zero pumping
2017-10-24 19:51 [Qemu-devel] [PATCH v3 00/13] Add support for the ZynqMP Generic QSPI Francisco Iglesias
` (10 preceding siblings ...)
2017-10-24 19:51 ` [Qemu-devel] [PATCH v3 13/13] xlnx-zcu102: Add support for the ZynqMP QSPI Francisco Iglesias
@ 2017-10-24 20:04 ` Francisco Iglesias
2017-11-02 23:59 ` [Qemu-devel] [PATCH v7 " Francisco Iglesias
12 siblings, 0 replies; 19+ messages in thread
From: Francisco Iglesias @ 2017-10-24 20:04 UTC (permalink / raw)
To: qemu-devel; +Cc: edgari, alistai, francisco.iglesias
Add support for zero pumping according to the transfer size register.
Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
---
hw/ssi/xilinx_spips.c | 47 ++++++++++++++++++++++++++++++++++++-------
include/hw/ssi/xilinx_spips.h | 2 ++
2 files changed, 42 insertions(+), 7 deletions(-)
diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index ad9f1a0..df5d908 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -109,6 +109,7 @@
FIELD(CMND, DUMMY_CYCLES, 2, 6)
#define R_CMND_DMA_EN (1 << 1)
#define R_CMND_PUSH_WAIT (1 << 0)
+#define R_TRANSFER_SIZE (0xc4 / 4)
#define R_LQSPI_STS (0xA4 / 4)
#define LQSPI_STS_WR_RECVD (1 << 1)
@@ -222,6 +223,7 @@ static void xilinx_spips_reset(DeviceState *d)
s->link_state_next_when = 0;
s->snoop_state = SNOOP_CHECKING;
s->cmd_dummies = 0;
+ s->man_start_com = false;
xilinx_spips_update_ixr(s);
xilinx_spips_update_cs_lines(s);
}
@@ -459,6 +461,41 @@ static inline void tx_data_bytes(Fifo8 *fifo, uint32_t value, int num, bool be)
}
}
+static void xilinx_spips_check_zero_pump(XilinxSPIPS *s)
+{
+ if (!s->regs[R_TRANSFER_SIZE]) {
+ return;
+ }
+ if (!fifo8_is_empty(&s->tx_fifo) && s->regs[R_CMND] & R_CMND_PUSH_WAIT) {
+ return;
+ }
+ /*
+ * The zero pump must never fill tx fifo such that rx overflow is
+ * possible
+ */
+ while (s->regs[R_TRANSFER_SIZE] &&
+ s->rx_fifo.num + s->tx_fifo.num < RXFF_A_Q - 3) {
+ /* endianess just doesn't matter when zero pumping */
+ tx_data_bytes(&s->tx_fifo, 0, 4, false);
+ s->regs[R_TRANSFER_SIZE] &= ~0x03ull;
+ s->regs[R_TRANSFER_SIZE] -= 4;
+ }
+}
+
+static void xilinx_spips_check_flush(XilinxSPIPS *s)
+{
+ if (s->man_start_com ||
+ (!fifo8_is_empty(&s->tx_fifo) &&
+ !(s->regs[R_CONFIG] & MAN_START_EN))) {
+ xilinx_spips_check_zero_pump(s);
+ xilinx_spips_flush_txfifo(s);
+ }
+ if (fifo8_is_empty(&s->tx_fifo) && !s->regs[R_TRANSFER_SIZE]) {
+ s->man_start_com = false;
+ }
+ xilinx_spips_update_ixr(s);
+}
+
static inline int rx_data_bytes(Fifo8 *fifo, uint8_t *value, int max)
{
int i;
@@ -528,7 +565,6 @@ static void xilinx_spips_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
int mask = ~0;
- int man_start_com = 0;
XilinxSPIPS *s = opaque;
DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr, (unsigned)value);
@@ -536,8 +572,8 @@ static void xilinx_spips_write(void *opaque, hwaddr addr,
switch (addr) {
case R_CONFIG:
mask = ~(R_CONFIG_RSVD | MAN_START_COM);
- if (value & MAN_START_COM) {
- man_start_com = 1;
+ if ((value & MAN_START_COM) && (s->regs[R_CONFIG] & MAN_START_EN)) {
+ s->man_start_com = true;
}
break;
case R_INTR_STATUS:
@@ -583,10 +619,7 @@ static void xilinx_spips_write(void *opaque, hwaddr addr,
s->regs[addr] = (s->regs[addr] & ~mask) | (value & mask);
no_reg_update:
xilinx_spips_update_cs_lines(s);
- if ((man_start_com && s->regs[R_CONFIG] & MAN_START_EN) ||
- (fifo8_is_empty(&s->tx_fifo) && s->regs[R_CONFIG] & MAN_START_EN)) {
- xilinx_spips_flush_txfifo(s);
- }
+ xilinx_spips_check_flush(s);
xilinx_spips_update_cs_lines(s);
xilinx_spips_update_ixr(s);
}
diff --git a/include/hw/ssi/xilinx_spips.h b/include/hw/ssi/xilinx_spips.h
index 036f86f..df6e245 100644
--- a/include/hw/ssi/xilinx_spips.h
+++ b/include/hw/ssi/xilinx_spips.h
@@ -75,6 +75,8 @@ struct XilinxSPIPS {
uint32_t rx_discard;
uint32_t regs[XLNX_SPIPS_R_MAX];
+
+ bool man_start_com;
};
typedef struct {
--
2.9.3
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [Qemu-devel] [PATCH v7 09/13] xilinx_spips: Add support for zero pumping
2017-10-24 19:51 [Qemu-devel] [PATCH v3 00/13] Add support for the ZynqMP Generic QSPI Francisco Iglesias
` (11 preceding siblings ...)
2017-10-24 20:04 ` [Qemu-devel] [PATCH v3 09/13] xilinx_spips: Add support for zero pumping Francisco Iglesias
@ 2017-11-02 23:59 ` Francisco Iglesias
12 siblings, 0 replies; 19+ messages in thread
From: Francisco Iglesias @ 2017-11-02 23:59 UTC (permalink / raw)
To: qemu-devel
Cc: edgari, alistai, francisco.iglesias, mar.krzeminski,
peter.maydell
Add support for zero pumping according to the transfer size register.
Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
---
hw/ssi/xilinx_spips.c | 47 ++++++++++++++++++++++++++++++++++++-------
include/hw/ssi/xilinx_spips.h | 2 ++
2 files changed, 42 insertions(+), 7 deletions(-)
diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index e37d005..3a98799 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -109,6 +109,7 @@
FIELD(CMND, DUMMY_CYCLES, 2, 6)
#define R_CMND_DMA_EN (1 << 1)
#define R_CMND_PUSH_WAIT (1 << 0)
+#define R_TRANSFER_SIZE (0xc4 / 4)
#define R_LQSPI_STS (0xA4 / 4)
#define LQSPI_STS_WR_RECVD (1 << 1)
@@ -227,6 +228,7 @@ static void xilinx_spips_reset(DeviceState *d)
s->link_state_next_when = 0;
s->snoop_state = SNOOP_CHECKING;
s->cmd_dummies = 0;
+ s->man_start_com = false;
xilinx_spips_update_ixr(s);
xilinx_spips_update_cs_lines(s);
}
@@ -464,6 +466,41 @@ static inline void tx_data_bytes(Fifo8 *fifo, uint32_t value, int num, bool be)
}
}
+static void xilinx_spips_check_zero_pump(XilinxSPIPS *s)
+{
+ if (!s->regs[R_TRANSFER_SIZE]) {
+ return;
+ }
+ if (!fifo8_is_empty(&s->tx_fifo) && s->regs[R_CMND] & R_CMND_PUSH_WAIT) {
+ return;
+ }
+ /*
+ * The zero pump must never fill tx fifo such that rx overflow is
+ * possible
+ */
+ while (s->regs[R_TRANSFER_SIZE] &&
+ s->rx_fifo.num + s->tx_fifo.num < RXFF_A_Q - 3) {
+ /* endianess just doesn't matter when zero pumping */
+ tx_data_bytes(&s->tx_fifo, 0, 4, false);
+ s->regs[R_TRANSFER_SIZE] &= ~0x03ull;
+ s->regs[R_TRANSFER_SIZE] -= 4;
+ }
+}
+
+static void xilinx_spips_check_flush(XilinxSPIPS *s)
+{
+ if (s->man_start_com ||
+ (!fifo8_is_empty(&s->tx_fifo) &&
+ !(s->regs[R_CONFIG] & MAN_START_EN))) {
+ xilinx_spips_check_zero_pump(s);
+ xilinx_spips_flush_txfifo(s);
+ }
+ if (fifo8_is_empty(&s->tx_fifo) && !s->regs[R_TRANSFER_SIZE]) {
+ s->man_start_com = false;
+ }
+ xilinx_spips_update_ixr(s);
+}
+
static inline int rx_data_bytes(Fifo8 *fifo, uint8_t *value, int max)
{
int i;
@@ -533,7 +570,6 @@ static void xilinx_spips_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
int mask = ~0;
- int man_start_com = 0;
XilinxSPIPS *s = opaque;
DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr, (unsigned)value);
@@ -541,8 +577,8 @@ static void xilinx_spips_write(void *opaque, hwaddr addr,
switch (addr) {
case R_CONFIG:
mask = ~(R_CONFIG_RSVD | MAN_START_COM);
- if (value & MAN_START_COM) {
- man_start_com = 1;
+ if ((value & MAN_START_COM) && (s->regs[R_CONFIG] & MAN_START_EN)) {
+ s->man_start_com = true;
}
break;
case R_INTR_STATUS:
@@ -588,10 +624,7 @@ static void xilinx_spips_write(void *opaque, hwaddr addr,
s->regs[addr] = (s->regs[addr] & ~mask) | (value & mask);
no_reg_update:
xilinx_spips_update_cs_lines(s);
- if ((man_start_com && s->regs[R_CONFIG] & MAN_START_EN) ||
- (fifo8_is_empty(&s->tx_fifo) && s->regs[R_CONFIG] & MAN_START_EN)) {
- xilinx_spips_flush_txfifo(s);
- }
+ xilinx_spips_check_flush(s);
xilinx_spips_update_cs_lines(s);
xilinx_spips_update_ixr(s);
}
diff --git a/include/hw/ssi/xilinx_spips.h b/include/hw/ssi/xilinx_spips.h
index bac90a5..ad2175a 100644
--- a/include/hw/ssi/xilinx_spips.h
+++ b/include/hw/ssi/xilinx_spips.h
@@ -76,6 +76,8 @@ struct XilinxSPIPS {
uint32_t rx_discard;
uint32_t regs[XLNX_SPIPS_R_MAX];
+
+ bool man_start_com;
};
typedef struct {
--
2.9.3
^ permalink raw reply related [flat|nested] 19+ messages in thread