]> rtime.felk.cvut.cz Git - zynq/linux.git/commitdiff
misc: xilinx-sdfec: Add IOCTL to get LDPC Params
authorDerek Kiernan <Derek.Kiernan@xilinx.com>
Tue, 13 Mar 2018 17:11:57 +0000 (17:11 +0000)
committerMichal Simek <michal.simek@xilinx.com>
Wed, 14 Mar 2018 14:11:32 +0000 (15:11 +0100)
Adding IOTCL XSDFEC_GET_LDPC_CODE_PARAMS to allow user verify LDPC
code params.

Signed-off-by: Derek Kiernan <Derek.Kiernan@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
drivers/misc/xilinx_sdfec.c
include/uapi/misc/xilinx_sdfec.h

index 2a7a380d1d4a98bcbb025580e4b32e3c197f489d..927b0c155fa9d76c7f587d2217deef22960abccb 100644 (file)
@@ -465,6 +465,31 @@ xsdfec_reg0_write(struct xsdfec_dev *xsdfec,
        return 0;
 }
 
+static int
+xsdfec_collect_ldpc_reg0(struct xsdfec_dev *xsdfec,
+                        u32 code_id,
+                        struct xsdfec_ldpc_params *ldpc_params)
+{
+       u32 reg_value;
+       u32 reg_addr = XSDFEC_LDPC_CODE_REG0_ADDR_BASE +
+               (code_id * XSDFEC_LDPC_REG_JUMP);
+
+       if (reg_addr > XSDFEC_LDPC_CODE_REG0_ADDR_HIGH) {
+               dev_err(xsdfec->dev,
+                       "Accessing outside of LDPC reg0 space 0x%x",
+                       reg_addr);
+               return -EINVAL;
+       }
+
+       reg_value = xsdfec_regread(xsdfec, reg_addr);
+
+       ldpc_params->n = (reg_value >> XSDFEC_REG0_N_LSB) & XSDFEC_REG0_N_MASK;
+
+       ldpc_params->k = (reg_value >> XSDFEC_REG0_K_LSB) & XSDFEC_REG0_K_MASK;
+
+       return 0;
+}
+
 #define XSDFEC_REG1_PSIZE_MASK         (0x000001ff)
 #define XSDFEC_REG1_NO_PACKING_MASK    (0x00000400)
 #define XSDFEC_REG1_NO_PACKING_LSB     (10)
@@ -504,6 +529,34 @@ xsdfec_reg1_write(struct xsdfec_dev *xsdfec, u32 psize,
        return 0;
 }
 
+static int
+xsdfec_collect_ldpc_reg1(struct xsdfec_dev *xsdfec,
+                        u32 code_id,
+                        struct xsdfec_ldpc_params *ldpc_params)
+{
+       u32 reg_value;
+       u32 reg_addr = XSDFEC_LDPC_CODE_REG1_ADDR_BASE +
+               (code_id * XSDFEC_LDPC_REG_JUMP);
+
+       if (reg_addr > XSDFEC_LDPC_CODE_REG1_ADDR_HIGH) {
+               dev_err(xsdfec->dev,
+                       "Accessing outside of LDPC reg1 space 0x%x",
+                       reg_addr);
+               return -EINVAL;
+       }
+
+       reg_value = xsdfec_regread(xsdfec, reg_addr);
+
+       ldpc_params->psize = reg_value & XSDFEC_REG1_PSIZE_MASK;
+
+       ldpc_params->no_packing = ((reg_value >> XSDFEC_REG1_NO_PACKING_LSB) &
+                                   XSDFEC_REG1_NO_PACKING_MASK);
+
+       ldpc_params->nm = (reg_value >> XSDFEC_REG1_NM_LSB) &
+                          XSDFEC_REG1_NM_MASK;
+       return 0;
+}
+
 #define XSDFEC_REG2_NLAYERS_MASK               (0x000001FF)
 #define XSDFEC_REG2_NLAYERS_LSB                        (0)
 #define XSDFEC_REG2_NNMQC_MASK                 (0x000FFE00)
@@ -569,6 +622,47 @@ xsdfec_reg2_write(struct xsdfec_dev *xsdfec, u32 nlayers, u32 nmqc,
        return 0;
 }
 
+static int
+xsdfec_collect_ldpc_reg2(struct xsdfec_dev *xsdfec,
+                        u32 code_id,
+                        struct xsdfec_ldpc_params *ldpc_params)
+{
+       u32 reg_value;
+       u32 reg_addr = XSDFEC_LDPC_CODE_REG2_ADDR_BASE +
+               (code_id * XSDFEC_LDPC_REG_JUMP);
+
+       if (reg_addr > XSDFEC_LDPC_CODE_REG2_ADDR_HIGH) {
+               dev_err(xsdfec->dev,
+                       "Accessing outside of LDPC reg1 space 0x%x",
+                       reg_addr);
+               return -EINVAL;
+       }
+
+       reg_value = xsdfec_regread(xsdfec, reg_addr);
+
+       ldpc_params->nlayers = ((reg_value >> XSDFEC_REG2_NLAYERS_LSB) &
+                               XSDFEC_REG2_NLAYERS_MASK);
+
+       ldpc_params->nmqc = (reg_value >> XSDFEC_REG2_NMQC_LSB) &
+                            XSDFEC_REG2_NNMQC_MASK;
+
+       ldpc_params->norm_type = ((reg_value >> XSDFEC_REG2_NORM_TYPE_LSB) &
+                                 XSDFEC_REG2_NORM_TYPE_MASK);
+
+       ldpc_params->special_qc = ((reg_value >> XSDFEC_REG2_SPEICAL_QC_LSB) &
+                                  XSDFEC_REG2_SPECIAL_QC_MASK);
+
+       ldpc_params->no_final_parity =
+               ((reg_value >> XSDFEC_REG2_NO_FINAL_PARITY_LSB) &
+                XSDFEC_REG2_NO_FINAL_PARITY_MASK);
+
+       ldpc_params->max_schedule =
+               ((reg_value >> XSDFEC_REG2_MAX_SCHEDULE_LSB) &
+                XSDFEC_REG2_MAX_SCHEDULE_MASK);
+
+       return 0;
+}
+
 #define XSDFEC_REG3_LA_OFF_LSB         (8)
 #define XSDFEC_REG3_QC_OFF_LSB         (16)
 static int
@@ -593,6 +687,31 @@ xsdfec_reg3_write(struct xsdfec_dev *xsdfec, u8 sc_off,
        return 0;
 }
 
+static int
+xsdfec_collect_ldpc_reg3(struct xsdfec_dev *xsdfec,
+                        u32 code_id,
+                        struct xsdfec_ldpc_params *ldpc_params)
+{
+       u32 reg_value;
+       u32 reg_addr = XSDFEC_LDPC_CODE_REG3_ADDR_BASE +
+               (code_id * XSDFEC_LDPC_REG_JUMP);
+
+       if (reg_addr > XSDFEC_LDPC_CODE_REG3_ADDR_HIGH) {
+               dev_err(xsdfec->dev,
+                       "Accessing outside of LDPC reg3 space 0x%x",
+                       reg_addr);
+               return -EINVAL;
+       }
+
+       reg_value = xsdfec_regread(xsdfec, reg_addr);
+
+       ldpc_params->qc_off = (reg_addr >> XSDFEC_REG3_QC_OFF_LSB) & 0xFF;
+       ldpc_params->la_off = (reg_addr >> XSDFEC_REG3_LA_OFF_LSB) & 0xFF;
+       ldpc_params->sc_off = (reg_addr & 0xFF);
+
+       return 0;
+}
+
 #define XSDFEC_SC_TABLE_DEPTH          (0x3fc)
 #define XSDFEC_REG_WIDTH_JUMP          (4)
 static int
@@ -629,6 +748,29 @@ xsdfec_sc_table_write(struct xsdfec_dev *xsdfec, u32 offset,
        return reg;
 }
 
+static int
+xsdfec_collect_sc_table(struct xsdfec_dev *xsdfec, u32 offset,
+                       u32 *sc_ptr, u32 len)
+{
+       u32 reg;
+       u32 reg_addr;
+       u32 deepest_reach = (XSDFEC_REG_WIDTH_JUMP * (offset + len));
+
+       if (deepest_reach > XSDFEC_SC_TABLE_DEPTH) {
+               dev_err(xsdfec->dev, "Access will exceed SC table length");
+               return -EINVAL;
+       }
+
+       for (reg = 0; reg < len; reg++) {
+               reg_addr = XSDFEC_LDPC_SC_TABLE_ADDR_BASE +
+                       ((offset + reg) * XSDFEC_REG_WIDTH_JUMP);
+
+               sc_ptr[reg] = xsdfec_regread(xsdfec, reg_addr);
+       }
+
+       return 0;
+}
+
 #define XSDFEC_LA_TABLE_DEPTH          (0xFFC)
 static int
 xsdfec_la_table_write(struct xsdfec_dev *xsdfec, u32 offset,
@@ -655,6 +797,29 @@ xsdfec_la_table_write(struct xsdfec_dev *xsdfec, u32 offset,
        return reg;
 }
 
+static int
+xsdfec_collect_la_table(struct xsdfec_dev *xsdfec, u32 offset,
+                       u32 *la_ptr, u32 len)
+{
+       u32 reg;
+       u32 reg_addr;
+       u32 deepest_reach = (XSDFEC_REG_WIDTH_JUMP * (offset + len));
+
+       if (deepest_reach > XSDFEC_LA_TABLE_DEPTH) {
+               dev_err(xsdfec->dev, "Access will exceed LA table length");
+               return -EINVAL;
+       }
+
+       for (reg = 0; reg < len; reg++) {
+               reg_addr = XSDFEC_LDPC_LA_TABLE_ADDR_BASE +
+                               ((offset + reg) * XSDFEC_REG_WIDTH_JUMP);
+
+               la_ptr[reg] = xsdfec_regread(xsdfec, reg_addr);
+       }
+
+       return 0;
+}
+
 #define XSDFEC_QC_TABLE_DEPTH          (0x7FFC)
 static int
 xsdfec_qc_table_write(struct xsdfec_dev *xsdfec,
@@ -681,6 +846,29 @@ xsdfec_qc_table_write(struct xsdfec_dev *xsdfec,
        return reg;
 }
 
+static int
+xsdfec_collect_qc_table(struct xsdfec_dev *xsdfec,
+                       u32 offset, u32 *qc_ptr, u32 len)
+{
+       u32 reg;
+       u32 reg_addr;
+       u32 deepest_reach = (XSDFEC_REG_WIDTH_JUMP * (offset + len));
+
+       if (deepest_reach > XSDFEC_QC_TABLE_DEPTH) {
+               dev_err(xsdfec->dev, "Access will exceed QC table length");
+               return -EINVAL;
+       }
+
+       for (reg = 0; reg < len; reg++) {
+               reg_addr = XSDFEC_LDPC_QC_TABLE_ADDR_BASE +
+                (offset + reg) * XSDFEC_REG_WIDTH_JUMP;
+
+               qc_ptr[reg] = xsdfec_regread(xsdfec, reg_addr);
+       }
+
+       return 0;
+}
+
 static int
 xsdfec_add_ldpc(struct xsdfec_dev *xsdfec, void __user *arg)
 {
@@ -760,6 +948,88 @@ err_out:
        return err;
 }
 
+static int
+xsdfec_get_ldpc_code_params(struct xsdfec_dev *xsdfec, void __user *arg)
+{
+       struct xsdfec_ldpc_params *ldpc_params;
+       int err = 0;
+
+       if (xsdfec->code == XSDFEC_TURBO_CODE) {
+               dev_err(xsdfec->dev,
+                       "%s: SDFEC%d is configured for TURBO, check DT",
+                               __func__, xsdfec->fec_id);
+               return -EIO;
+       }
+
+       ldpc_params = kzalloc(sizeof(*ldpc_params), GFP_KERNEL);
+       if (!ldpc_params)
+               return -ENOMEM;
+
+       err = copy_from_user(ldpc_params, arg, sizeof(*ldpc_params));
+       if (err) {
+               dev_err(xsdfec->dev,
+                       "%s failed to copy from user for SDFEC%d",
+                       __func__, xsdfec->fec_id);
+               return -EFAULT;
+       }
+
+       err = xsdfec_collect_ldpc_reg0(xsdfec, ldpc_params->code_id,
+                                      ldpc_params);
+       if (err)
+               goto err_out;
+
+       err = xsdfec_collect_ldpc_reg1(xsdfec, ldpc_params->code_id,
+                                      ldpc_params);
+       if (err)
+               goto err_out;
+
+       err = xsdfec_collect_ldpc_reg2(xsdfec, ldpc_params->code_id,
+                                      ldpc_params);
+       if (err)
+               goto err_out;
+
+       err = xsdfec_collect_ldpc_reg3(xsdfec, ldpc_params->code_id,
+                                      ldpc_params);
+       if (err)
+               goto err_out;
+
+       /*
+        * Collect the shared table values, needs to happen after reading
+        * the registers
+        */
+       err = xsdfec_collect_sc_table(xsdfec, ldpc_params->sc_off,
+                                     ldpc_params->sc_table,
+                                     ldpc_params->nlayers);
+       if (err < 0)
+               goto err_out;
+
+       err = xsdfec_collect_la_table(xsdfec, 4 * ldpc_params->la_off,
+                                     ldpc_params->la_table,
+                                     ldpc_params->nlayers);
+       if (err < 0)
+               goto err_out;
+
+       err = xsdfec_collect_qc_table(xsdfec, 4 * ldpc_params->qc_off,
+                                     ldpc_params->qc_table,
+                                     ldpc_params->nqc);
+       if (err < 0)
+               goto err_out;
+
+       err = copy_to_user(arg, ldpc_params, sizeof(*ldpc_params));
+       if (err) {
+               dev_err(xsdfec->dev, "%s failed for SDFEC%d",
+                       __func__, xsdfec->fec_id);
+               err = -EFAULT;
+       }
+
+       kfree(ldpc_params);
+       return 0;
+       /* Error Path */
+err_out:
+       kfree(ldpc_params);
+       return err;
+}
+
 static int xsdfec_start(struct xsdfec_dev *xsdfec)
 {
        u32 regread;
@@ -884,6 +1154,9 @@ xsdfec_dev_ioctl(struct file *fptr, unsigned int cmd, unsigned long data)
        case XSDFEC_ADD_LDPC_CODE_PARAMS:
                rval  = xsdfec_add_ldpc(xsdfec, arg);
                break;
+       case XSDFEC_GET_LDPC_CODE_PARAMS:
+               rval = xsdfec_get_ldpc_code_params(xsdfec, arg);
+               break;
        default:
                /* Should not get here */
                dev_err(xsdfec->dev, "Undefined SDFEC IOCTL");
index c09f146808abb6fca81f7334a15f3089381800d2..1ef0b4fe16ee23f191e916dc0941895c5f64449c 100644 (file)
@@ -186,5 +186,8 @@ struct xsdfec_irq {
 #define XSDFEC_GET_CONFIG      _IOR(XSDFEC_MAGIC, 7, struct xsdfec_config *)
 /* ioctl that returns sdfec turbo param values */
 #define XSDFEC_GET_TURBO       _IOR(XSDFEC_MAGIC, 8, struct xsdfec_turbo *)
+/* ioctl that returns sdfec LDPC code param values, code_id must be specified */
+#define XSDFEC_GET_LDPC_CODE_PARAMS \
+       _IOWR(XSDFEC_MAGIC, 9, struct xsdfec_ldpc_params *)
 
 #endif /* __XILINX_SDFEC_H__ */