@@ -55,6 +55,8 @@
#define PIN_CFG_FILONOFF BIT(10)
#define PIN_CFG_FILNUM BIT(11)
#define PIN_CFG_FILCLKSEL BIT(12)
+#define PIN_CFG_IOLH_C BIT(13)
+#define PIN_CFG_SOFT_PS BIT(14)
#define RZG2L_MPXED_PIN_FUNCS (PIN_CFG_IOLH_A | \
PIN_CFG_SR | \
@@ -133,27 +135,40 @@ struct rzg2l_register_offsets {
u16 sd_ch;
};
+/* Value to be passed on drive strength arrays as invalid value. */
+#define RZG2L_INVALID_IOLH_VAL (0xffff)
+
/**
* enum rzg2l_iolh_index - starting indexes in IOLH specific arrays
+ * @RZG2L_IOLH_IDX_1V8: starting index for 1V8 power source
+ * @RZG2L_IOLH_IDX_2V5: starting index for 2V5 power source
* @RZG2L_IOLH_IDX_3V3: starting index for 3V3 power source
* @RZG2L_IOLH_IDX_MAX: maximum index
*/
enum rzg2l_iolh_index {
- RZG2L_IOLH_IDX_3V3 = 0,
- RZG2L_IOLH_IDX_MAX = 4,
+ RZG2L_IOLH_IDX_1V8 = 0,
+ RZG2L_IOLH_IDX_2V5 = 4,
+ RZG2L_IOLH_IDX_3V3 = 8,
+ RZG2L_IOLH_IDX_MAX = 12,
};
/**
* struct rzg2l_hwcfg - hardware configuration data structure
* @regs: hardware specific register offsets
* @iolh_groupa_ua: IOLH group A micro amps specific values
+ * @iolh_groupb_ua: IOLH group B micro amps specific values
+ * @iolh_groupc_ua: IOLH group C micro amps specific values
* @iolh_groupb_oi: IOLH group B output impedance specific values
+ * @drive_strength_ua: driver strenght in ua is supported (otherwise mA is supported)
* @func_base: base number for port function (see register PFC)
*/
struct rzg2l_hwcfg {
const struct rzg2l_register_offsets regs;
u16 iolh_groupa_ua[RZG2L_IOLH_IDX_MAX];
+ u16 iolh_groupb_ua[RZG2L_IOLH_IDX_MAX];
+ u16 iolh_groupc_ua[RZG2L_IOLH_IDX_MAX];
u16 iolh_groupb_oi[RZG2L_IOLH_IDX_MAX];
+ bool drive_strength_ua;
u8 func_base;
};
@@ -172,6 +187,16 @@ struct rzg2l_pinctrl_data {
const struct rzg2l_hwcfg *hwcfg;
};
+/**
+ * struct rzg2l_pinctrl_pin_settings - pin data
+ * @power_source: power source
+ * @drive_strength_ua: drive strength (in micro amps)
+ */
+struct rzg2l_pinctrl_pin_settings {
+ u16 power_source;
+ u16 drive_strength_ua;
+};
+
struct rzg2l_pinctrl {
struct pinctrl_dev *pctl;
struct pinctrl_desc desc;
@@ -189,8 +214,12 @@ struct rzg2l_pinctrl {
spinlock_t lock; /* lock read/write registers */
struct mutex mutex; /* serialize adding groups and functions */
+
+ struct rzg2l_pinctrl_pin_settings *settings;
};
+static const u16 available_ps[] = { 1800, 2500, 3300 };
+
static void rzg2l_pinctrl_set_pfc_mode(struct rzg2l_pinctrl *pctrl,
u8 pin, u8 off, u8 func)
{
@@ -555,6 +584,164 @@ static void rzg2l_rmw_pin_config(struct rzg2l_pinctrl *pctrl, u32 offset,
spin_unlock_irqrestore(&pctrl->lock, flags);
}
+static int rzg2l_get_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps)
+{
+ const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg;
+ const struct rzg2l_register_offsets *regs = &hwcfg->regs;
+ unsigned long flags;
+ void __iomem *addr;
+ u32 pwr_reg;
+ u16 ps;
+
+ if (caps & PIN_CFG_IO_VMC_SD0)
+ pwr_reg = SD_CH(regs->sd_ch, 0);
+ else if (caps & PIN_CFG_IO_VMC_SD1)
+ pwr_reg = SD_CH(regs->sd_ch, 1);
+ else if (caps & PIN_CFG_IO_VMC_QSPI)
+ pwr_reg = QSPI;
+ else if (!(caps & PIN_CFG_SOFT_PS))
+ return -EINVAL;
+
+ spin_lock_irqsave(&pctrl->lock, flags);
+ if (caps & PIN_CFG_SOFT_PS) {
+ ps = pctrl->settings[pin].power_source;
+ } else {
+ addr = pctrl->base + pwr_reg;
+ ps = (readl(addr) & PVDD_MASK) ? 1800 : 3300;
+ }
+ spin_unlock_irqrestore(&pctrl->lock, flags);
+
+ return ps;
+}
+
+static int rzg2l_set_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps, u32 ps)
+{
+ const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg;
+ const struct rzg2l_register_offsets *regs = &hwcfg->regs;
+ unsigned long flags;
+ void __iomem *addr;
+ u32 pwr_reg;
+
+ if (caps & PIN_CFG_IO_VMC_SD0)
+ pwr_reg = SD_CH(regs->sd_ch, 0);
+ else if (caps & PIN_CFG_IO_VMC_SD1)
+ pwr_reg = SD_CH(regs->sd_ch, 1);
+ else if (caps & PIN_CFG_IO_VMC_QSPI)
+ pwr_reg = QSPI;
+ else if (!(caps & PIN_CFG_SOFT_PS))
+ return -EINVAL;
+
+ addr = pctrl->base + pwr_reg;
+ spin_lock_irqsave(&pctrl->lock, flags);
+ if (!(caps & PIN_CFG_SOFT_PS))
+ writel((ps == 1800) ? PVDD_1800 : PVDD_3300, addr);
+ pctrl->settings[pin].power_source = ps;
+ spin_unlock_irqrestore(&pctrl->lock, flags);
+
+ return 0;
+}
+
+static bool rzg2l_ps_is_supported(u16 ps)
+{
+ u8 i;
+
+ for (i = 0; i < ARRAY_SIZE(available_ps); i++) {
+ if (available_ps[i] == ps)
+ return true;
+ }
+
+ return false;
+}
+
+static enum rzg2l_iolh_index rzg2l_ps_to_iolh_idx(u16 ps)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(available_ps); i++) {
+ if (available_ps[i] == ps)
+ break;
+ }
+
+ /*
+ * We multiply with 4 as we have 4 DS values per power source
+ * (see enum rzg2l_iolh_index)
+ */
+ return i * 4;
+}
+
+static u16 rzg2l_iolh_val_to_ua(const struct rzg2l_hwcfg *hwcfg, u32 caps, u8 val)
+{
+ if (caps & PIN_CFG_IOLH_A)
+ return hwcfg->iolh_groupa_ua[val];
+
+ if (caps & PIN_CFG_IOLH_B)
+ return hwcfg->iolh_groupb_ua[val];
+
+ if (caps & PIN_CFG_IOLH_C)
+ return hwcfg->iolh_groupc_ua[val];
+
+ /* Should not happen. */
+ return 0;
+}
+
+static u16 rzg2l_iolh_ua_to_val(const struct rzg2l_hwcfg *hwcfg, u32 caps,
+ enum rzg2l_iolh_index ps_index, u16 ua)
+{
+ const u16 *array = NULL;
+ u16 i;
+
+ if (caps & PIN_CFG_IOLH_A)
+ array = &hwcfg->iolh_groupa_ua[ps_index];
+
+ if (caps & PIN_CFG_IOLH_B)
+ array = &hwcfg->iolh_groupb_ua[ps_index];
+
+ if (caps & PIN_CFG_IOLH_C)
+ array = &hwcfg->iolh_groupc_ua[ps_index];
+
+ if (!array)
+ return RZG2L_INVALID_IOLH_VAL;
+
+ for (i = 0; i < 4; i++) {
+ if (array[i] == ua)
+ return i;
+ }
+
+ return RZG2L_INVALID_IOLH_VAL;
+}
+
+static bool rzg2l_ds_supported(struct rzg2l_pinctrl *pctrl, u32 caps,
+ enum rzg2l_iolh_index iolh_idx,
+ u16 ds)
+{
+ const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg;
+ const u16 *array = NULL;
+ u16 i;
+
+ if (caps & PIN_CFG_IOLH_A)
+ array = hwcfg->iolh_groupa_ua;
+
+ if (caps & PIN_CFG_IOLH_B)
+ array = hwcfg->iolh_groupb_ua;
+
+ if (caps & PIN_CFG_IOLH_C)
+ array = hwcfg->iolh_groupc_ua;
+
+ /* Should not happen. */
+ if (!array)
+ return false;
+
+ if (array[iolh_idx] == RZG2L_INVALID_IOLH_VAL)
+ return false;
+
+ for (i = 0; i < 4; i++) {
+ if (array[iolh_idx + i] == ds)
+ return true;
+ }
+
+ return false;
+}
+
static int rzg2l_pinctrl_pinconf_get(struct pinctrl_dev *pctldev,
unsigned int _pin,
unsigned long *config)
@@ -562,13 +749,11 @@ static int rzg2l_pinctrl_pinconf_get(struct pinctrl_dev *pctldev,
struct rzg2l_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
enum pin_config_param param = pinconf_to_config_param(*config);
const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg;
- const struct rzg2l_register_offsets *regs = &hwcfg->regs;
const struct pinctrl_pin_desc *pin = &pctrl->desc.pins[_pin];
unsigned int *pin_data = pin->drv_data;
unsigned int arg = 0;
- unsigned long flags;
- void __iomem *addr;
u32 off, cfg;
+ int ret;
u8 bit;
if (!pin_data)
@@ -594,40 +779,50 @@ static int rzg2l_pinctrl_pinconf_get(struct pinctrl_dev *pctldev,
return -EINVAL;
break;
- case PIN_CONFIG_POWER_SOURCE: {
- u32 pwr_reg = 0x0;
-
- if (cfg & PIN_CFG_IO_VMC_SD0)
- pwr_reg = SD_CH(regs->sd_ch, 0);
- else if (cfg & PIN_CFG_IO_VMC_SD1)
- pwr_reg = SD_CH(regs->sd_ch, 1);
- else if (cfg & PIN_CFG_IO_VMC_QSPI)
- pwr_reg = QSPI;
- else
- return -EINVAL;
-
- spin_lock_irqsave(&pctrl->lock, flags);
- addr = pctrl->base + pwr_reg;
- arg = (readl(addr) & PVDD_MASK) ? 1800 : 3300;
- spin_unlock_irqrestore(&pctrl->lock, flags);
+ case PIN_CONFIG_POWER_SOURCE:
+ ret = rzg2l_get_power_source(pctrl, _pin, cfg);
+ if (ret < 0)
+ return ret;
+ arg = ret;
break;
- }
case PIN_CONFIG_DRIVE_STRENGTH: {
unsigned int index;
- if (!(cfg & PIN_CFG_IOLH_A))
+ if (!(cfg & PIN_CFG_IOLH_A) || hwcfg->drive_strength_ua)
return -EINVAL;
index = rzg2l_read_pin_config(pctrl, IOLH(off), bit, IOLH_MASK);
+ /*
+ * Drive strenght mA is supported only by group A and only
+ * for 3V3 port source.
+ */
arg = hwcfg->iolh_groupa_ua[index + RZG2L_IOLH_IDX_3V3] / 1000;
break;
}
+ case PIN_CONFIG_DRIVE_STRENGTH_UA: {
+ enum rzg2l_iolh_index iolh_idx;
+ u8 val;
+
+ if (!(cfg & (PIN_CFG_IOLH_A | PIN_CFG_IOLH_B | PIN_CFG_IOLH_C)) ||
+ !hwcfg->drive_strength_ua)
+ return -EINVAL;
+
+ ret = rzg2l_get_power_source(pctrl, _pin, cfg);
+ if (ret < 0)
+ return ret;
+ iolh_idx = rzg2l_ps_to_iolh_idx(ret);
+ val = rzg2l_read_pin_config(pctrl, IOLH(off), bit, IOLH_MASK);
+ arg = rzg2l_iolh_val_to_ua(hwcfg, cfg, iolh_idx + val);
+ break;
+ }
+
case PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS: {
unsigned int index;
- if (!(cfg & PIN_CFG_IOLH_B))
+ if (!(cfg & PIN_CFG_IOLH_B) ||
+ hwcfg->iolh_groupb_oi[0] == RZG2L_INVALID_IOLH_VAL)
return -EINVAL;
index = rzg2l_read_pin_config(pctrl, IOLH(off), bit, IOLH_MASK);
@@ -651,12 +846,10 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev,
{
struct rzg2l_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
const struct pinctrl_pin_desc *pin = &pctrl->desc.pins[_pin];
- unsigned int *pin_data = pin->drv_data;
const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg;
- const struct rzg2l_register_offsets *regs = &hwcfg->regs;
+ struct rzg2l_pinctrl_pin_settings settings = pctrl->settings[_pin];
+ unsigned int *pin_data = pin->drv_data;
enum pin_config_param param;
- unsigned long flags;
- void __iomem *addr;
unsigned int i;
u32 cfg, off;
u8 bit;
@@ -689,34 +882,15 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev,
break;
}
- case PIN_CONFIG_POWER_SOURCE: {
- unsigned int mV = pinconf_to_config_argument(_configs[i]);
- u32 pwr_reg = 0x0;
-
- if (mV != 1800 && mV != 3300)
- return -EINVAL;
-
- if (cfg & PIN_CFG_IO_VMC_SD0)
- pwr_reg = SD_CH(regs->sd_ch, 0);
- else if (cfg & PIN_CFG_IO_VMC_SD1)
- pwr_reg = SD_CH(regs->sd_ch, 1);
- else if (cfg & PIN_CFG_IO_VMC_QSPI)
- pwr_reg = QSPI;
- else
- return -EINVAL;
-
- addr = pctrl->base + pwr_reg;
- spin_lock_irqsave(&pctrl->lock, flags);
- writel((mV == 1800) ? PVDD_1800 : PVDD_3300, addr);
- spin_unlock_irqrestore(&pctrl->lock, flags);
+ case PIN_CONFIG_POWER_SOURCE:
+ settings.power_source = pinconf_to_config_argument(_configs[i]);
break;
- }
case PIN_CONFIG_DRIVE_STRENGTH: {
unsigned int arg = pinconf_to_config_argument(_configs[i]);
unsigned int index;
- if (!(cfg & PIN_CFG_IOLH_A))
+ if (!(cfg & PIN_CFG_IOLH_A) || hwcfg->drive_strength_ua)
return -EINVAL;
for (index = RZG2L_IOLH_IDX_3V3; index < RZG2L_IOLH_IDX_3V3 + 4; index++) {
@@ -730,11 +904,20 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev,
break;
}
+ case PIN_CONFIG_DRIVE_STRENGTH_UA:
+ if (!(cfg & (PIN_CFG_IOLH_A | PIN_CFG_IOLH_B | PIN_CFG_IOLH_C)) ||
+ !hwcfg->drive_strength_ua)
+ return -EINVAL;
+
+ settings.drive_strength_ua = pinconf_to_config_argument(_configs[i]);
+ break;
+
case PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS: {
unsigned int arg = pinconf_to_config_argument(_configs[i]);
unsigned int index;
- if (!(cfg & PIN_CFG_IOLH_B))
+ if (!(cfg & PIN_CFG_IOLH_B) ||
+ hwcfg->iolh_groupb_oi[0] == RZG2L_INVALID_IOLH_VAL)
return -EINVAL;
for (index = 0; index < ARRAY_SIZE(hwcfg->iolh_groupb_oi); index++) {
@@ -753,6 +936,47 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev,
}
}
+ /* Apply drive strength and power source. */
+ if (memcmp(&settings, &pctrl->settings[_pin], sizeof(settings))) {
+ enum rzg2l_iolh_index iolh_idx;
+ unsigned long flags;
+ int ret;
+ u16 val;
+
+ if (settings.power_source == pctrl->settings[_pin].power_source)
+ goto apply_drive_strength;
+
+ ret = rzg2l_ps_is_supported(settings.power_source);
+ if (!ret)
+ return -EINVAL;
+
+ /* Apply power source. */
+ ret = rzg2l_set_power_source(pctrl, _pin, cfg, settings.power_source);
+ if (ret)
+ return ret;
+
+apply_drive_strength:
+ if (settings.drive_strength_ua == pctrl->settings[_pin].drive_strength_ua)
+ return 0;
+
+ iolh_idx = rzg2l_ps_to_iolh_idx(settings.power_source);
+ ret = rzg2l_ds_supported(pctrl, cfg, iolh_idx,
+ settings.drive_strength_ua);
+ if (!ret)
+ return -EINVAL;
+
+ /* Get register value for this PS/DS tuple. */
+ val = rzg2l_iolh_ua_to_val(hwcfg, cfg, iolh_idx, settings.drive_strength_ua);
+ if (val == RZG2L_INVALID_IOLH_VAL)
+ return -EINVAL;
+
+ /* Apply drive strength. */
+ rzg2l_rmw_pin_config(pctrl, IOLH(off), bit, IOLH_MASK, val);
+ spin_lock_irqsave(&pctrl->lock, flags);
+ pctrl->settings[_pin].drive_strength_ua = settings.drive_strength_ua;
+ spin_unlock_irqrestore(&pctrl->lock, flags);
+ }
+
return 0;
}
@@ -1459,6 +1683,7 @@ static int rzg2l_gpio_register(struct rzg2l_pinctrl *pctrl)
static int rzg2l_pinctrl_register(struct rzg2l_pinctrl *pctrl)
{
+ const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg;
struct pinctrl_pin_desc *pins;
unsigned int i, j;
u32 *pin_data;
@@ -1501,6 +1726,22 @@ static int rzg2l_pinctrl_register(struct rzg2l_pinctrl *pctrl)
pins[index].drv_data = &pin_data[index];
}
+ pctrl->settings = devm_kzalloc(pctrl->dev, sizeof(*pctrl->settings) * pctrl->desc.npins,
+ GFP_KERNEL);
+ if (!pctrl->settings)
+ return -ENOMEM;
+
+ for (i = 0; hwcfg->drive_strength_ua && i < pctrl->desc.npins; i++) {
+ if (pin_data[i] & PIN_CFG_SOFT_PS) {
+ pctrl->settings[i].power_source = 3300;
+ } else {
+ ret = rzg2l_get_power_source(pctrl, i, pin_data[i]);
+ if (ret < 0)
+ continue;
+ pctrl->settings[i].power_source = ret;
+ }
+ }
+
ret = devm_pinctrl_register_and_init(pctrl->dev, &pctrl->desc, pctrl,
&pctrl->pctl);
if (ret) {
@@ -1574,6 +1815,8 @@ static const struct rzg2l_hwcfg rzg2l_hwcfg = {
.sd_ch = 0x3000,
},
.iolh_groupa_ua = {
+ /* 1v8, 2v5 power source */
+ [RZG2L_IOLH_IDX_1V8 ... RZG2L_IOLH_IDX_3V3 - 1] = RZG2L_INVALID_IOLH_VAL,
/* 3v3 power source */
[RZG2L_IOLH_IDX_3V3] = 2000, 4000, 8000, 12000,
},