diff options
| -rw-r--r-- | drivers/dpll/zl3073x/core.c | 155 | ||||
| -rw-r--r-- | drivers/dpll/zl3073x/core.h | 30 | ||||
| -rw-r--r-- | drivers/dpll/zl3073x/regs.h | 12 |
3 files changed, 193 insertions, 4 deletions
diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c index 7ebcfc5ec1f0..86c26edc9046 100644 --- a/drivers/dpll/zl3073x/core.c +++ b/drivers/dpll/zl3073x/core.c @@ -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 */ @@ -447,6 +448,152 @@ int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 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: input reference index to fetch state for diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h index 71af2c800110..16e750d77e1d 100644 --- a/drivers/dpll/zl3073x/core.h +++ b/drivers/dpll/zl3073x/core.h @@ -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 diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h index 614e33128a5c..80922987add3 100644 --- a/drivers/dpll/zl3073x/regs.h +++ b/drivers/dpll/zl3073x/regs.h @@ -260,4 +260,16 @@ #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 */ |
