[PATCH v1 1/2] pinctrl: mediatek: support access registers without race-condition

From: light.hsieh
Date: Tue Aug 18 2020 - 04:36:27 EST


From: Light Hsieh <light.hsieh@xxxxxxxxxxxx>

Some MediaTek SOC provide more control registers other than value register.
Generanll, a value register need read-modify-write is at offset 0xXXXXXXXX0.
A corresponding SET register is at offset 0xXXXXXXX4. Write 1s' to some bits
of SET register will set same bits in value register.
A corresponding CLR register is at offset 0xXXXXXXX8. Write 1s' to some bits
of CLR register will clear same bits in value register.
For GPIO mode selection, MWR register is provided at offset 0xXXXXXXXC.
With MWR, the MSBit of GPIO mode selection field is for modification-enable,
not for GPIO mode selection, and the remaining LSBits are for mode
selection.
Take mode selection field with 4-bits as example, to select mode 0~7 via
MWR register, 8~15 (instead of 0~7) shall be written to corresponding mode
selection field.
When using SET/CLR/MWR registers, read-modify-write of value register is not
necessary. This can prevent from race condition when multiple bus masters
concurrently read-modify-write the same value register for setting different
fields of the same value register.

Signed-off-by: Light Hsieh <light.hsieh@xxxxxxxxxxxx>
---
drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c | 69 ++++++++++++++++++++++--
drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h | 2 +
2 files changed, 67 insertions(+), 4 deletions(-)

diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
index b77b18f..51f0b53 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
@@ -18,6 +18,29 @@
#include "mtk-eint.h"
#include "pinctrl-mtk-common-v2.h"

+/* Some MediaTek SOC provide more control registers other than value register.
+ * Generanll, a value register need read-modify-write is at offset 0xXXXXXXXX0.
+ * A corresponding SET register is at offset 0xXXXXXXX4. Write 1s' to some bits
+ * of SET register will set same bits in value register.
+ * A corresponding CLR register is at offset 0xXXXXXXX8. Write 1s' to some bits
+ * of CLR register will clear same bits in value register.
+ * For GPIO mode selection, MWR register is provided at offset 0xXXXXXXXC.
+ * With MWR, the MSBit of GPIO mode selection field is for modification-enable,
+ * not for GPIO mode selection, and the remaining LSBits are for mode
+ * selection.
+ * Take mode selection field with 4-bits as example, to select mode 0~7 via
+ * MWR register, 8~15 (instead of 0~7) shall be written to corresponding mode
+ * selection field.
+ * When using SET/CLR/MWR registers, read-modify-write of value register is not
+ * necessary. This can prevent from race condition when multiple bus masters
+ * concurrently read-modify-write the same value register for setting different
+ * fields of the same value register.
+ */
+
+#define SET_OFFSET 0x4
+#define CLR_OFFSET 0x8
+#define MWR_OFFSET 0xC
+
/**
* struct mtk_drive_desc - the structure that holds the information
* of the driving current
@@ -64,6 +87,38 @@ void mtk_rmw(struct mtk_pinctrl *pctl, u8 i, u32 reg, u32 mask, u32 set)
mtk_w32(pctl, i, reg, val);
}

+
+static void mtk_hw_set_value_race_free(struct mtk_pinctrl *pctl,
+ struct mtk_pin_field *pf, u32 value)
+{
+ unsigned int set, clr;
+
+ set = value & pf->mask;
+ clr = (~set) & pf->mask;
+
+ if (set)
+ mtk_w32(pctl, pf->index, pf->offset + SET_OFFSET,
+ set << pf->bitpos);
+ if (clr)
+ mtk_w32(pctl, pf->index, pf->offset + CLR_OFFSET,
+ clr << pf->bitpos);
+}
+
+static void mtk_hw_set_mode_race_free(struct mtk_pinctrl *pctl,
+ struct mtk_pin_field *pf, u32 value)
+{
+ unsigned int value_new;
+
+ /* MSB of mask is modification-enable bit, set this bit */
+ value_new = (1 << (pctl->soc->mwr_field_width - 1)) | value;
+ if (value_new == value)
+ dev_notice(pctl->dev,
+ "invalid mode 0x%x, use it by ignoring MSBit!\n",
+ value);
+ mtk_w32(pctl, pf->index, pf->offset + MWR_OFFSET,
+ value_new << pf->bitpos);
+}
+
static int mtk_hw_pin_field_lookup(struct mtk_pinctrl *hw,
const struct mtk_pin_desc *desc,
int field, struct mtk_pin_field *pfd)
@@ -197,10 +252,16 @@ int mtk_hw_set_value(struct mtk_pinctrl *hw, const struct mtk_pin_desc *desc,
if (value < 0 || value > pf.mask)
return -EINVAL;

- if (!pf.next)
- mtk_rmw(hw, pf.index, pf.offset, pf.mask << pf.bitpos,
- (value & pf.mask) << pf.bitpos);
- else
+ if (!pf.next) {
+ if (hw->soc->race_free_access) {
+ if (field == PINCTRL_PIN_REG_MODE)
+ mtk_hw_set_mode_race_free(hw, &pf, value);
+ else
+ mtk_hw_set_value_race_free(hw, &pf, value);
+ } else
+ mtk_rmw(hw, pf.index, pf.offset, pf.mask << pf.bitpos,
+ (value & pf.mask) << pf.bitpos);
+ } else
mtk_hw_write_cross_field(hw, &pf, value);

return 0;
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h
index 27df087..95fb329 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h
@@ -203,6 +203,8 @@ struct mtk_pin_soc {
/* Specific parameters per SoC */
u8 gpio_m;
bool ies_present;
+ bool race_free_access;
+ unsigned int mwr_field_width;
const char * const *base_names;
unsigned int nbase_names;

--
1.8.1.1.dirty