[flash_ctrl] Support the notion of a 'program-repair'.

This function is specific to the underlying flash, and not all will support it.
As a result, expose a "functional available" output from the flash wrapper and allow the controller to properly control whether a transaction should be allowed.

Note, need to add software read back to that status.

Signed-off-by: Timothy Chen <timothytim@google.com>

[flash_ctrl] minor updates

- Add software read back to check what program types are supported

Signed-off-by: Timothy Chen <timothytim@google.com>

[flash_ctrl] Add otp disable to program repair

Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/hw/ip/flash_ctrl/data/flash_ctrl.hjson b/hw/ip/flash_ctrl/data/flash_ctrl.hjson
index b42db7a..f420430 100644
--- a/hw/ip/flash_ctrl/data/flash_ctrl.hjson
+++ b/hw/ip/flash_ctrl/data/flash_ctrl.hjson
@@ -149,9 +149,31 @@
             },
           ]
         },
+
         { bits: "6",
+          name: "PROG_SEL",
+          desc: "Flash program operation type selection",
+          resval: "0"
+          enum: [
+            { value: "0",
+              name: "Normal program",
+              desc: '''
+                Normal program operation to the flash
+                '''
+            },
+            { value: "1",
+              name: "Program repair",
+              desc: '''
+                Repair program operation to the flash.  Whether this is actually
+                supported depends on the underlying flash memory.
+                '''
+            },
+          ]
+        },
+
+        { bits: "7",
           name: "ERASE_SEL",
-          desc: "Flash operation selection",
+          desc: "Flash erase operation type selection",
           resval: "0"
           enum: [
             { value: "0",
@@ -168,7 +190,7 @@
             },
           ]
         },
-        { bits: "7",
+        { bits: "8",
           name: "PARTITION_SEL",
           desc: '''
             Selects either info or data partition for operation.
@@ -537,10 +559,33 @@
         { bits: "1",    name: "rd_empty",   desc: "Flash read FIFO empty", resval: "1"},
         { bits: "2",    name: "prog_full",  desc: "Flash program FIFO full"},
         { bits: "3",    name: "prog_empty", desc: "Flash program FIFO empty, software must provide data", resval: "1"},
-        { bits: "4",    name: "init_wip",   desc: "Flash controller undergoing init"},
+        { bits: "4",    name: "init_wip",   desc: "Flash controller undergoing init, inclusive of phy init"},
         { bits: "16:8", name: "error_addr", desc: "Flash controller error address."},
       ]
     },
+
+    { name: "PHY_STATUS",
+      desc: "Flash Phy Status",
+      swaccess: "ro",
+      hwaccess: "hwo",
+      fields: [
+        { bits: "0",
+          name: "init_wip",
+          desc: "Flash phy controller initializing"
+        },
+        { bits: "1",
+          name: "prog_normal_avail",
+          resval: "0x1",
+          desc: "Normal program supported"
+        },
+        { bits: "2",
+          name: "prog_repair_avail",
+          resval: "0x1",
+          desc: "Program repair supported"
+        },
+      ]
+    },
+
     { name: "Scratch",
       desc: "Flash Controller Scratch",
       swaccess: "rw",
diff --git a/hw/ip/flash_ctrl/rtl/flash_ctrl.sv b/hw/ip/flash_ctrl/rtl/flash_ctrl.sv
index 5866710..d5bbda4 100644
--- a/hw/ip/flash_ctrl/rtl/flash_ctrl.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_ctrl.sv
@@ -91,7 +91,7 @@
   // Erase Control Connections
   logic erase_flash_req;
   logic [BusAddrW-1:0] erase_flash_addr;
-  logic [EraseBitWidth-1:0] erase_flash_type;
+  flash_erase_e erase_flash_type;
 
   // Done / Error signaling from ctrl modules
   logic prog_done, rd_done, erase_done;
@@ -104,6 +104,7 @@
   logic flash_error;
   logic [BusWidth-1:0] flash_prog_data;
   logic flash_prog_last;
+  flash_prog_e flash_prog_type;
   logic [BusWidth-1:0] flash_rd_data;
   logic flash_phy_busy;
   logic rd_op;
@@ -138,7 +139,8 @@
   logic [BusAddrW-1:0] op_addr;
   flash_op_e op_type;
   flash_part_e op_part;
-  logic [EraseBitWidth-1:0] op_erase_type;
+  flash_erase_e op_erase_type;
+  flash_prog_e op_prog_type;
 
   logic ctrl_init_busy;
   logic fifo_clr;
@@ -220,7 +222,8 @@
 
   assign op_start      = muxed_ctrl.start.q;
   assign op_num_words  = muxed_ctrl.num.q;
-  assign op_erase_type = muxed_ctrl.erase_sel.q;
+  assign op_erase_type = flash_erase_e'(muxed_ctrl.erase_sel.q);
+  assign op_prog_type  = flash_prog_e'(muxed_ctrl.prog_sel.q);
   assign op_addr       = muxed_addr[BusByteWidth +: BusAddrW];
   assign op_type       = flash_op_e'(muxed_ctrl.op.q);
   assign op_part       = flash_part_e'(muxed_ctrl.partition_sel.q);
@@ -323,16 +326,24 @@
   );
 
   // Program handler is consumer of prog_fifo
+
+  // OTP control bits can be used to explicitly disable repair
+  logic [ProgTypes-1:0] otp_en;
+  assign otp_en[FlashProgNormal] = 1'b1;
+  assign otp_en[FlashProgRepair] = otp_i.prog_repair_en;
+
   flash_ctrl_prog u_flash_ctrl_prog (
     .clk_i,
     .rst_ni,
 
-    // Software Interface
+    // Control interface
     .op_start_i     (prog_op_valid),
     .op_num_words_i (op_num_words),
     .op_done_o      (prog_done),
     .op_err_o       (prog_err),
     .op_addr_i      (op_addr),
+    .op_type_i      (op_prog_type),
+    .type_avail_i   (flash_i.prog_type_avail & otp_en),
 
     // FIFO Interface
     .data_i         (prog_fifo_rdata),
@@ -345,6 +356,7 @@
     .flash_ovfl_o   (prog_flash_ovfl),
     .flash_data_o   (flash_prog_data),
     .flash_last_o   (flash_prog_last),
+    .flash_type_o   (flash_prog_type),
     .flash_done_i   (flash_prog_done),
     .flash_error_i  (flash_error)
   );
@@ -579,9 +591,18 @@
   // if software operation not selected, software is free to change contents
   assign hw2reg.ctrl_regwen.d        = sw_sel ? !op_start : 1'b1;
 
+  // phy status
+  assign hw2reg.phy_status.init_wip.d  = flash_phy_busy;
+  assign hw2reg.phy_status.init_wip.de = 1'b1;
+  assign hw2reg.phy_status.prog_normal_avail.d  = flash_i.prog_type_avail[FlashProgNormal];
+  assign hw2reg.phy_status.prog_normal_avail.de = 1'b1;
+  assign hw2reg.phy_status.prog_repair_avail.d  = flash_i.prog_type_avail[FlashProgRepair];
+  assign hw2reg.phy_status.prog_repair_avail.de = 1'b1;
+
   // Flash Interface
   assign flash_o.addr = flash_addr;
   assign flash_o.part = flash_part_sel;
+  assign flash_o.prog_type = flash_prog_type;
   assign flash_o.prog_data = flash_prog_data;
   assign flash_o.prog_last = flash_prog_last;
   assign flash_o.scramble_en = reg2hw.scramble_en.q;
diff --git a/hw/ip/flash_ctrl/rtl/flash_ctrl_erase.sv b/hw/ip/flash_ctrl/rtl/flash_ctrl_erase.sv
index f55f1ca..2c93a70 100644
--- a/hw/ip/flash_ctrl/rtl/flash_ctrl_erase.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_ctrl_erase.sv
@@ -7,18 +7,18 @@
 
 module flash_ctrl_erase import flash_ctrl_pkg::*; (
   // Software Interface
-  input                     op_start_i,
-  input [EraseBitWidth-1:0] op_type_i,
-  input [BusAddrW-1:0]         op_addr_i,
-  output logic              op_done_o,
-  output logic              op_err_o,
+  input                       op_start_i,
+  input flash_erase_e         op_type_i,
+  input [BusAddrW-1:0]        op_addr_i,
+  output logic                op_done_o,
+  output logic                op_err_o,
 
   // Flash Macro Interface
-  output logic             flash_req_o,
+  output logic                flash_req_o,
   output logic [BusAddrW-1:0] flash_addr_o,
-  output logic [EraseBitWidth-1:0] flash_op_o,
-  input                    flash_done_i,
-  input                    flash_error_i
+  output flash_erase_e        flash_op_o,
+  input                       flash_done_i,
+  input                       flash_error_i
 );
 
   import flash_ctrl_pkg::*;
diff --git a/hw/ip/flash_ctrl/rtl/flash_ctrl_lcmgr.sv b/hw/ip/flash_ctrl/rtl/flash_ctrl_lcmgr.sv
index 8b1ffe6..fcf14f0 100644
--- a/hw/ip/flash_ctrl/rtl/flash_ctrl_lcmgr.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_ctrl_lcmgr.sv
@@ -162,12 +162,14 @@
 
   logic start;
   flash_op_e op;
+  flash_prog_e prog_type;
   flash_erase_e erase_type;
   flash_part_e part_sel;
   logic [11:0] num_words;
   logic [BusAddrW-1:0] addr;
   logic [BusWidth-1:0] rsp_mask;
 
+  assign prog_type = FlashProgNormal;
   assign erase_type = FlashErasePage;
   // seed phase is always read
   // rma phase is erase unless we are validating
@@ -332,6 +334,7 @@
   assign rma_token_o = rsp_token[0] ^ rsp_token[1] ^ rsp_mask;
   assign ctrl_o.start.q = start;
   assign ctrl_o.op.q = op;
+  assign ctrl_o.prog_sel.q = prog_type;
   assign ctrl_o.erase_sel.q = erase_type;
   assign ctrl_o.partition_sel.q = part_sel;
   assign ctrl_o.num = num_words;
diff --git a/hw/ip/flash_ctrl/rtl/flash_ctrl_pkg.sv b/hw/ip/flash_ctrl/rtl/flash_ctrl_pkg.sv
index f253df7..ffdbc88 100644
--- a/hw/ip/flash_ctrl/rtl/flash_ctrl_pkg.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_ctrl_pkg.sv
@@ -167,14 +167,19 @@
     FlashOpInvalid  = 2'h3
   } flash_op_e;
 
+  // Flash Program Operations Supported
+  typedef enum logic {
+    FlashProgNormal = 0,
+    FlashProgRepair = 1
+  } flash_prog_e;
+  parameter int ProgTypes = 2;
+
   // Flash Erase Operations Supported
   typedef enum logic  {
     FlashErasePage  = 0,
     FlashEraseBank  = 1
   } flash_erase_e;
 
-  parameter int EraseBitWidth = $bits(flash_erase_e);
-
   // Flash function select
   typedef enum logic [1:0] {
     NoneSel,
@@ -205,6 +210,7 @@
     logic [BusAddrW-1:0]  addr;
     logic [BusWidth-1:0]  prog_data;
     logic                 prog_last;
+    flash_prog_e          prog_type;
     logic                 scramble_en;
     logic [127:0]         addr_key;
     logic [127:0]         data_key;
@@ -221,6 +227,7 @@
     addr:      '0,
     prog_data: '0,
     prog_last: '0,
+    prog_type: FlashProgNormal,
     scramble_en: '0,
     addr_key:  128'hDEADBEEFBEEFFACEDEADBEEF5A5AA5A5,
     data_key:  128'hDEADBEEF5A5AA5A5DEADBEEFBEEFFACE
@@ -228,6 +235,7 @@
 
   // memory to flash controller
   typedef struct packed {
+    logic [ProgTypes-1:0] prog_type_avail;
     logic                rd_done;
     logic                prog_done;
     logic                erase_done;
@@ -237,6 +245,7 @@
 
   // default value of flash_rsp_t (for dangling ports)
   parameter flash_rsp_t FLASH_RSP_DEFAULT = '{
+    prog_type_avail: '{default: '1},
     rd_done:    1'b0,
     prog_done:  1'b0,
     erase_done: 1'b0,
@@ -254,6 +263,7 @@
     logic [127:0] data_key;
     // TBD: this signal will become multi-bit in the future
     logic seed_valid;
+    logic prog_repair_en;
   } otp_flash_t;
 
   // lc to flash_ctrl
@@ -281,7 +291,8 @@
   parameter otp_flash_t OTP_FLASH_DEFAULT = '{
     addr_key: 128'hDEADBEEFBEEFFACEDEADBEEF5A5AA5A5,
     data_key: 128'hDEADBEEF5A5AA5A5DEADBEEFBEEFFACE,
-    seed_valid: 1'b1
+    seed_valid: 1'b1,
+    prog_repair_en: 1'b1
   };
 
   parameter lc_flash_req_t LC_FLASH_REQ_DEFAULT = '{
diff --git a/hw/ip/flash_ctrl/rtl/flash_ctrl_prog.sv b/hw/ip/flash_ctrl/rtl/flash_ctrl_prog.sv
index 0fcbae5..5e2b197 100644
--- a/hw/ip/flash_ctrl/rtl/flash_ctrl_prog.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_ctrl_prog.sv
@@ -9,12 +9,14 @@
   input clk_i,
   input rst_ni,
 
-  // Software Interface
+  // Control Interface
   input                    op_start_i,
   input  [11:0]            op_num_words_i,
   output logic             op_done_o,
   output logic             op_err_o,
-  input [BusAddrW-1:0]        op_addr_i,
+  input [BusAddrW-1:0]     op_addr_i,
+  input flash_prog_e       op_type_i,
+  input [ProgTypes-1:0]    type_avail_i,
 
   // FIFO Interface
   input                    data_rdy_i,
@@ -27,6 +29,7 @@
   output logic             flash_ovfl_o,
   output logic [BusWidth-1:0] flash_data_o,
   output logic             flash_last_o, // last beat of prog data
+  output flash_prog_e      flash_type_o,
   input                    flash_done_i,
   input                    flash_error_i
 );
@@ -56,6 +59,10 @@
   assign txn_done = flash_req_o && flash_done_i;
   assign cnt_hit = (cnt == op_num_words_i);
 
+  // if the requested prog type is available
+  logic prog_type_avail;
+  assign prog_type_avail = type_avail_i[op_type_i];
+
   // when error'd, continue to drain all program fifo contents like normal operation
   // if this is not done, software may fill up the fifo without anyone
   // draining the contents, leading to a lockup
@@ -69,17 +76,22 @@
 
     unique case (st)
       StNorm: begin
-        flash_req_o = op_start_i & data_rdy_i;
+        // if the select operation type is not available, error
+        if (op_start_i && prog_type_avail) begin
+          flash_req_o = data_rdy_i;
 
-        if(txn_done && cnt_hit) begin
-          cnt_nxt = '0;
-          data_rd_o = 1'b1;
-          op_done_o = 1'b1;
-          op_err_o = flash_error_i;
-        end else if(txn_done) begin
-          cnt_nxt = cnt + 1'b1;
-          data_rd_o = 1'b1;
-          st_nxt = flash_error_i ? StErr : StNorm;
+          if(txn_done && cnt_hit) begin
+            cnt_nxt = '0;
+            data_rd_o = 1'b1;
+            op_done_o = 1'b1;
+            op_err_o = flash_error_i;
+          end else if(txn_done) begin
+            cnt_nxt = cnt + 1'b1;
+            data_rd_o = 1'b1;
+            st_nxt = flash_error_i ? StErr : StNorm;
+          end
+        end else if (op_start_i && !prog_type_avail) begin
+          st_nxt = StErr;
         end
       end
       StErr: begin
@@ -103,5 +115,6 @@
   assign flash_addr_o = int_addr[0 +: BusAddrW];
   assign flash_ovfl_o = int_addr[BusAddrW];
   assign flash_last_o = flash_req_o & cnt_hit;
+  assign flash_type_o = op_type_i;
 
 endmodule // flash_ctrl_prog
diff --git a/hw/ip/flash_ctrl/rtl/flash_phy.sv b/hw/ip/flash_ctrl/rtl/flash_phy.sv
index 28ad2a1..8f52256 100644
--- a/hw/ip/flash_ctrl/rtl/flash_phy.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_phy.sv
@@ -57,6 +57,7 @@
   logic [NumBanks-1:0]  prog_done;
   logic [NumBanks-1:0]  erase_done;
   logic [NumBanks-1:0]  init_busy;
+  logic [ProgTypes-1:0] prog_type_avail [NumBanks];
 
   // common interface
   logic [BusWidth-1:0] rd_data [NumBanks];
@@ -72,6 +73,8 @@
   assign host_req_done_o = seq_fifo_pending & host_rsp_vld[rsp_bank_sel];
   assign host_rdata_o = host_rsp_data[rsp_bank_sel];
 
+  // all banks are assumed to be the same in terms of prog_type support
+  assign flash_ctrl_o.prog_type_avail = prog_type_avail[0];
   assign flash_ctrl_o.rd_done = rd_done[ctrl_bank_sel];
   assign flash_ctrl_o.prog_done = prog_done[ctrl_bank_sel];
   assign flash_ctrl_o.erase_done = erase_done[ctrl_bank_sel];
@@ -148,8 +151,10 @@
       .addr_i(flash_ctrl_i.addr[0 +: BusBankAddrW]),
       .prog_data_i(flash_ctrl_i.prog_data),
       .prog_last_i(flash_ctrl_i.prog_last),
+      .prog_type_i(flash_ctrl_i.prog_type),
       .addr_key_i(flash_ctrl_i.addr_key),
       .data_key_i(flash_ctrl_i.data_key),
+      .prog_type_avail_o(prog_type_avail[bank]),
       .host_req_rdy_o(host_req_rdy[bank]),
       .host_req_done_o(host_req_done[bank]),
       .rd_done_o(rd_done[bank]),
@@ -160,4 +165,19 @@
     );
   end
 
+  //////////////////////////////////////////////
+  // Assertions, Assumptions, and Coverpoints //
+  /////////////////////////////////////////////
+  logic [ProgTypes-1:0] unused_prog_type;
+
+  always_comb begin
+    unused_prog_type = '0;
+    for (int i = 0; i < NumBanks; i++) begin : gen_prog_type_xor
+      unused_prog_type ^= prog_type_avail[i];
+    end
+  end
+
+  // all banks must support the same kinds of programs
+  `ASSERT(homogenousProg_a,  unused_prog_type == '0)
+
 endmodule // flash_phy
diff --git a/hw/ip/flash_ctrl/rtl/flash_phy_core.sv b/hw/ip/flash_ctrl/rtl/flash_phy_core.sv
index eacd507..bf57e16 100644
--- a/hw/ip/flash_ctrl/rtl/flash_phy_core.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_phy_core.sv
@@ -27,8 +27,10 @@
   input [BusBankAddrW-1:0]           addr_i,
   input [BusWidth-1:0]               prog_data_i,
   input                              prog_last_i,
+  input flash_ctrl_pkg::flash_prog_e prog_type_i,
   input [KeySize-1:0]                addr_key_i,
   input [KeySize-1:0]                data_key_i,
+  output logic [ProgTypes-1:0]       prog_type_avail_o,
   output logic                       host_req_rdy_o,
   output logic                       host_req_done_o,
   output logic                       rd_done_o,
@@ -340,11 +342,13 @@
     .rst_ni,
     .rd_i(flash_rd_req),
     .prog_i(flash_prog_req),
+    .prog_type_i(prog_type_i),
     .pg_erase_i(reqs[PhyPgErase]),
     .bk_erase_i(reqs[PhyBkErase]),
     .addr_i(muxed_addr[BusBankAddrW-1:LsbAddrBit]),
     .part_i(muxed_part),
     .prog_data_i(prog_data),
+    .prog_type_avail_o(prog_type_avail_o),
     .ack_o(ack),
     .rd_data_o(flash_rdata),
     .init_busy_o, // TBD this needs to be looked at later. What init do we need to do,
diff --git a/hw/ip/flash_ctrl/rtl/flash_phy_pkg.sv b/hw/ip/flash_ctrl/rtl/flash_phy_pkg.sv
index 6da6b6e..738ff86 100644
--- a/hw/ip/flash_ctrl/rtl/flash_phy_pkg.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_phy_pkg.sv
@@ -23,9 +23,10 @@
                                    // will switch to this after bus widening
 
   // flash ctrl / bus parameters
-  parameter int BusWidth      = flash_ctrl_pkg::BusWidth;
-  parameter int BusBankAddrW  = flash_ctrl_pkg::BusBankAddrW;
-  parameter int BusWordW      = flash_ctrl_pkg::BusWordW;
+  parameter int BusWidth       = flash_ctrl_pkg::BusWidth;
+  parameter int BusBankAddrW   = flash_ctrl_pkg::BusBankAddrW;
+  parameter int BusWordW       = flash_ctrl_pkg::BusWordW;
+  parameter int ProgTypes      = flash_ctrl_pkg::ProgTypes;
 
   // address bits remain must be 0
   parameter int AddrBitsRemain = DataWidth % BusWidth;
diff --git a/hw/ip/prim_generic/rtl/prim_generic_flash.sv b/hw/ip/prim_generic/rtl/prim_generic_flash.sv
index 29fbabc..b6962d7 100644
--- a/hw/ip/prim_generic/rtl/prim_generic_flash.sv
+++ b/hw/ip/prim_generic/rtl/prim_generic_flash.sv
@@ -21,14 +21,18 @@
   input                              rst_ni,
   input                              rd_i,
   input                              prog_i,
+  // the generic model does not make use of program types
+  input flash_ctrl_pkg::flash_prog_e prog_type_i,
   input                              pg_erase_i,
   input                              bk_erase_i,
   input [AddrW-1:0]                  addr_i,
   input flash_ctrl_pkg::flash_part_e part_i,
   input [DataWidth-1:0]              prog_data_i,
+  output logic [flash_ctrl_pkg::ProgTypes-1:0] prog_type_avail_o,
   output logic                       ack_o,
   output logic [DataWidth-1:0]       rd_data_o,
   output logic                       init_busy_o,
+
   input                              tck_i,
   input                              tdi_i,
   input                              tms_i,
@@ -328,4 +332,8 @@
   // hard-wire assignment for now
   assign tdo_o = 1'b0;
 
+  // this represents the type of program operations that are supported
+  assign prog_type_avail_o[flash_ctrl_pkg::FlashProgNormal] = 1'b1;
+  assign prog_type_avail_o[flash_ctrl_pkg::FlashProgRepair] = 1'b1;
+
 endmodule // prim_generic_flash