]> www.infradead.org Git - users/hch/misc.git/commitdiff
dpll: zl3073x: Add functions to access hardware registers
authorIvan Vecera <ivecera@redhat.com>
Tue, 9 Sep 2025 09:15:28 +0000 (11:15 +0200)
committerJakub Kicinski <kuba@kernel.org>
Mon, 15 Sep 2025 15:08:38 +0000 (08:08 -0700)
Besides the device host registers that are directly accessible, there
are also hardware registers that can be accessed indirectly via specific
host registers.

Add register definitions for accessing hardware registers and provide
helper functions for working with them. Additionally, extend the number
of pages in the regmap configuration to 256, as the host registers used
for accessing hardware registers are located on page 255.

Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
Link: https://patch.msgid.link/20250909091532.11790-2-ivecera@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/dpll/zl3073x/core.c
drivers/dpll/zl3073x/core.h
drivers/dpll/zl3073x/regs.h

index 7ebcfc5ec1f090a99e8ee9e1d6a3e3f6bd669c6e..86c26edc90462b396f32afeb6091914aa1a867ca 100644 (file)
@@ -95,9 +95,9 @@ EXPORT_SYMBOL_NS_GPL(zl30735_chip_info, "ZL3073X");
 
 #define ZL_RANGE_OFFSET                0x80
 #define ZL_PAGE_SIZE           0x80
-#define ZL_NUM_PAGES           15
+#define ZL_NUM_PAGES           256
 #define ZL_PAGE_SEL            0x7F
-#define ZL_PAGE_SEL_MASK       GENMASK(3, 0)
+#define ZL_PAGE_SEL_MASK       GENMASK(7, 0)
 #define ZL_NUM_REGS            (ZL_NUM_PAGES * ZL_PAGE_SIZE)
 
 /* Regmap range configuration */
@@ -174,9 +174,10 @@ static bool
 zl3073x_check_reg(struct zl3073x_dev *zldev, unsigned int reg, size_t size)
 {
        /* Check that multiop lock is held when accessing registers
-        * from page 10 and above.
+        * from page 10 and above except the page 255 that does not
+        * need this protection.
         */
-       if (ZL_REG_PAGE(reg) >= 10)
+       if (ZL_REG_PAGE(reg) >= 10 && ZL_REG_PAGE(reg) < 255)
                lockdep_assert_held(&zldev->multiop_lock);
 
        /* Check the index is in valid range for indexed register */
@@ -446,6 +447,152 @@ int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 op_val,
        return zl3073x_poll_zero_u8(zldev, op_reg, op_val);
 }
 
+/**
+ * zl3073x_do_hwreg_op - Perform HW register read/write operation
+ * @zldev: zl3073x device pointer
+ * @op: operation to perform
+ *
+ * Performs requested operation and waits for its completion.
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int
+zl3073x_do_hwreg_op(struct zl3073x_dev *zldev, u8 op)
+{
+       int rc;
+
+       /* Set requested operation and set pending bit */
+       rc = zl3073x_write_u8(zldev, ZL_REG_HWREG_OP, op | ZL_HWREG_OP_PENDING);
+       if (rc)
+               return rc;
+
+       /* Poll for completion - pending bit cleared */
+       return zl3073x_poll_zero_u8(zldev, ZL_REG_HWREG_OP,
+                                   ZL_HWREG_OP_PENDING);
+}
+
+/**
+ * zl3073x_read_hwreg - Read HW register
+ * @zldev: zl3073x device pointer
+ * @addr: HW register address
+ * @value: Value of the HW register
+ *
+ * Reads HW register value and stores it into @value.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_read_hwreg(struct zl3073x_dev *zldev, u32 addr, u32 *value)
+{
+       int rc;
+
+       /* Set address to read data from */
+       rc = zl3073x_write_u32(zldev, ZL_REG_HWREG_ADDR, addr);
+       if (rc)
+               return rc;
+
+       /* Perform the read operation */
+       rc = zl3073x_do_hwreg_op(zldev, ZL_HWREG_OP_READ);
+       if (rc)
+               return rc;
+
+       /* Read the received data */
+       return zl3073x_read_u32(zldev, ZL_REG_HWREG_READ_DATA, value);
+}
+
+/**
+ * zl3073x_write_hwreg - Write value to HW register
+ * @zldev: zl3073x device pointer
+ * @addr: HW registers address
+ * @value: Value to be written to HW register
+ *
+ * Stores the requested value into HW register.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_write_hwreg(struct zl3073x_dev *zldev, u32 addr, u32 value)
+{
+       int rc;
+
+       /* Set address to write data to */
+       rc = zl3073x_write_u32(zldev, ZL_REG_HWREG_ADDR, addr);
+       if (rc)
+               return rc;
+
+       /* Set data to be written */
+       rc = zl3073x_write_u32(zldev, ZL_REG_HWREG_WRITE_DATA, value);
+       if (rc)
+               return rc;
+
+       /* Perform the write operation */
+       return zl3073x_do_hwreg_op(zldev, ZL_HWREG_OP_WRITE);
+}
+
+/**
+ * zl3073x_update_hwreg - Update certain bits in HW register
+ * @zldev: zl3073x device pointer
+ * @addr: HW register address
+ * @value: Value to be written into HW register
+ * @mask: Bitmask indicating bits to be updated
+ *
+ * Reads given HW register, updates requested bits specified by value and
+ * mask and writes result back to HW register.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_update_hwreg(struct zl3073x_dev *zldev, u32 addr, u32 value,
+                        u32 mask)
+{
+       u32 tmp;
+       int rc;
+
+       rc = zl3073x_read_hwreg(zldev, addr, &tmp);
+       if (rc)
+               return rc;
+
+       tmp &= ~mask;
+       tmp |= value & mask;
+
+       return zl3073x_write_hwreg(zldev, addr, tmp);
+}
+
+/**
+ * zl3073x_write_hwreg_seq - Write HW registers sequence
+ * @zldev: pointer to device structure
+ * @seq: pointer to first sequence item
+ * @num_items: number of items in sequence
+ *
+ * Writes given HW registers sequence.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_write_hwreg_seq(struct zl3073x_dev *zldev,
+                           const struct zl3073x_hwreg_seq_item *seq,
+                           size_t num_items)
+{
+       int i, rc = 0;
+
+       for (i = 0; i < num_items; i++) {
+               dev_dbg(zldev->dev, "Write 0x%0x [0x%0x] to 0x%0x",
+                       seq[i].value, seq[i].mask, seq[i].addr);
+
+               if (seq[i].mask == U32_MAX)
+                       /* Write value directly */
+                       rc = zl3073x_write_hwreg(zldev, seq[i].addr,
+                                                seq[i].value);
+               else
+                       /* Update only bits specified by the mask */
+                       rc = zl3073x_update_hwreg(zldev, seq[i].addr,
+                                                 seq[i].value, seq[i].mask);
+               if (rc)
+                       return rc;
+
+               if (seq->wait)
+                       msleep(seq->wait);
+       }
+
+       return rc;
+}
+
 /**
  * zl3073x_ref_state_fetch - get input reference state
  * @zldev: pointer to zl3073x_dev structure
index 71af2c8001109e2383201b7122c7d813cbaa1ed5..16e750d77e1dd49889ca708ea9b57a7fb158af64 100644 (file)
@@ -3,6 +3,7 @@
 #ifndef _ZL3073X_CORE_H
 #define _ZL3073X_CORE_H
 
+#include <linux/bitfield.h>
 #include <linux/kthread.h>
 #include <linux/list.h>
 #include <linux/mutex.h>
@@ -115,6 +116,28 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
  * Registers operations
  **********************/
 
+/**
+ * struct zl3073x_hwreg_seq_item - HW register write sequence item
+ * @addr: HW register to be written
+ * @value: value to be written to HW register
+ * @mask: bitmask indicating bits to be updated
+ * @wait: number of ms to wait after register write
+ */
+struct zl3073x_hwreg_seq_item {
+       u32     addr;
+       u32     value;
+       u32     mask;
+       u32     wait;
+};
+
+#define HWREG_SEQ_ITEM(_addr, _value, _mask, _wait)    \
+{                                                      \
+       .addr   = _addr,                                \
+       .value  = FIELD_PREP_CONST(_mask, _value),      \
+       .mask   = _mask,                                \
+       .wait   = _wait,                                \
+}
+
 int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 op_val,
                  unsigned int mask_reg, u16 mask_val);
 int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 mask);
@@ -126,6 +149,13 @@ int zl3073x_write_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 val);
 int zl3073x_write_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 val);
 int zl3073x_write_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 val);
 int zl3073x_write_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 val);
+int zl3073x_read_hwreg(struct zl3073x_dev *zldev, u32 addr, u32 *value);
+int zl3073x_write_hwreg(struct zl3073x_dev *zldev, u32 addr, u32 value);
+int zl3073x_update_hwreg(struct zl3073x_dev *zldev, u32 addr, u32 value,
+                        u32 mask);
+int zl3073x_write_hwreg_seq(struct zl3073x_dev *zldev,
+                           const struct zl3073x_hwreg_seq_item *seq,
+                           size_t num_items);
 
 /*****************
  * Misc operations
index 614e33128a5c9aa6f8dce3775625b7117b62c024..80922987add340e740b7a9d43ac3881f423784ee 100644 (file)
 #define ZL_REG_OUTPUT_ESYNC_WIDTH              ZL_REG(14, 0x18, 4)
 #define ZL_REG_OUTPUT_PHASE_COMP               ZL_REG(14, 0x20, 4)
 
+/*
+ * Register Page 255 - HW registers access
+ */
+#define ZL_REG_HWREG_OP                                ZL_REG(0xff, 0x00, 1)
+#define ZL_HWREG_OP_WRITE                      0x28
+#define ZL_HWREG_OP_READ                       0x29
+#define ZL_HWREG_OP_PENDING                    BIT(1)
+
+#define ZL_REG_HWREG_ADDR                      ZL_REG(0xff, 0x04, 4)
+#define ZL_REG_HWREG_WRITE_DATA                        ZL_REG(0xff, 0x08, 4)
+#define ZL_REG_HWREG_READ_DATA                 ZL_REG(0xff, 0x0c, 4)
+
 #endif /* _ZL3073X_REGS_H */