/* * Copyright (c) 2021 IoT.bzh * * SPDX-License-Identifier: Apache-2.0 * */ #define DT_DRV_COMPAT renesas_rcar_pfc #include #include #include #include #define PFC_REG_BASE DT_INST_REG_ADDR(0) #define PFC_RCAR_PMMR 0x0 #define PFC_RCAR_GPSR 0x100 #define PFC_RCAR_IPSR 0x200 /* * Each drive step is either encoded in 2 or 3 bits. * So based on a 24 mA maximum value each step is either * 24/4 mA or 24/8 mA. */ #define PFC_RCAR_DRIVE_MAX 24U #define PFC_RCAR_DRIVE_STEP(size) \ (size == 2 ? PFC_RCAR_DRIVE_MAX / 4 : PFC_RCAR_DRIVE_MAX / 8) /* Some registers such as IPSR GPSR or DRVCTRL are protected and * must be preceded to a write to PMMR with the inverse value. */ static void pfc_rcar_write(uint32_t offs, uint32_t val) { sys_write32(~val, PFC_REG_BASE + PFC_RCAR_PMMR); sys_write32(val, PFC_REG_BASE + offs); } /* Set the pin either in gpio or peripheral */ static void pfc_rcar_set_gpsr(uint16_t pin, bool peripheral) { uint8_t bank = pin / 32; uint8_t bit = pin % 32; uint32_t val = sys_read32(PFC_REG_BASE + PFC_RCAR_GPSR + bank * sizeof(uint32_t)); if (peripheral) { val |= BIT(bit); } else { val &= ~BIT(bit); } pfc_rcar_write(PFC_RCAR_GPSR + bank * sizeof(uint32_t), val); } /* Set peripheral function */ static void pfc_rcar_set_ipsr(const struct rcar_pin_func *rcar_func) { uint16_t reg_offs = PFC_RCAR_IPSR + rcar_func->bank * sizeof(uint32_t); uint32_t val = sys_read32(PFC_REG_BASE + reg_offs); val &= ~(0xFU << rcar_func->shift); val |= (rcar_func->func << rcar_func->shift); pfc_rcar_write(reg_offs, val); } static uint32_t pfc_rcar_get_drive_reg(uint16_t pin, uint8_t *offset, uint8_t *size) { const struct pfc_drive_reg *drive_regs = pfc_rcar_get_drive_regs(); while (drive_regs->reg != 0U) { for (size_t i = 0U; i < ARRAY_SIZE(drive_regs->fields); i++) { if (drive_regs->fields[i].pin == pin) { *offset = drive_regs->fields[i].offset; *size = drive_regs->fields[i].size; return drive_regs->reg; } } drive_regs++; } return 0; } /* * Maximum drive strength is 24mA. This value can be lowered * using DRVCTRLx registers, some pins have 8 steps (3 bits size encoded) * some have 4 steps (2 bits size encoded). */ static int pfc_rcar_set_drive_strength(uint16_t pin, uint8_t strength) { uint8_t offset, size, step; uint32_t reg, val; reg = pfc_rcar_get_drive_reg(pin, &offset, &size); if (reg == 0U) { return -EINVAL; } step = PFC_RCAR_DRIVE_STEP(size); if ((strength < step) || (strength > PFC_RCAR_DRIVE_MAX)) { return -EINVAL; } /* Convert the value from mA based on a full drive strength * value of 24mA. */ strength = (strength / step) - 1U; /* clear previous drive strength value */ val = sys_read32(PFC_REG_BASE + reg); val &= ~GENMASK(offset + size - 1U, offset); val |= strength << offset; pfc_rcar_write(reg, val); return 0; } static const struct pfc_bias_reg *pfc_rcar_get_bias_reg(uint16_t pin, uint8_t *bit) { const struct pfc_bias_reg *bias_regs = pfc_rcar_get_bias_regs(); /* Loop arround all the registers to find the bit for a given pin */ while (bias_regs->puen && bias_regs->pud) { for (size_t i = 0U; i < ARRAY_SIZE(bias_regs->pins); i++) { if (bias_regs->pins[i] == pin) { *bit = i; return bias_regs; } } bias_regs++; } return NULL; } int pfc_rcar_set_bias(uint16_t pin, uint16_t flags) { uint32_t val; uint8_t bit; const struct pfc_bias_reg *bias_reg = pfc_rcar_get_bias_reg(pin, &bit); if (bias_reg == NULL) { return -EINVAL; } /* pull enable/disable*/ val = sys_read32(PFC_REG_BASE + bias_reg->puen); if ((flags & RCAR_PIN_FLAGS_PUEN) == 0U) { sys_write32(val & ~BIT(bit), PFC_REG_BASE + bias_reg->puen); return 0; } sys_write32(val | BIT(bit), PFC_REG_BASE + bias_reg->puen); /* pull - up/down */ val = sys_read32(PFC_REG_BASE + bias_reg->pud); if (flags & RCAR_PIN_FLAGS_PUD) { sys_write32(val | BIT(bit), PFC_REG_BASE + bias_reg->pud); } else { sys_write32(val & ~BIT(bit), PFC_REG_BASE + bias_reg->pud); } return 0; } int pinctrl_configure_pin(const pinctrl_soc_pin_t *pin) { int ret = 0; /* Set pin as GPIO if capable */ if (RCAR_IS_GP_PIN(pin->pin)) { pfc_rcar_set_gpsr(pin->pin, false); } else if ((pin->flags & RCAR_PIN_FLAGS_FUNC_SET) == 0U) { /* A function must be set for non GPIO capable pin */ return -EINVAL; } /* Select function for pin */ if ((pin->flags & RCAR_PIN_FLAGS_FUNC_SET) != 0U) { pfc_rcar_set_ipsr(&pin->func); if (RCAR_IS_GP_PIN(pin->pin)) { pfc_rcar_set_gpsr(pin->pin, true); } if ((pin->flags & RCAR_PIN_FLAGS_PULL_SET) != 0U) { ret = pfc_rcar_set_bias(pin->pin, pin->flags); if (ret < 0) { return ret; } } } if (pin->drive_strength != 0U) { ret = pfc_rcar_set_drive_strength(pin->pin, pin->drive_strength); } return ret; } int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, uintptr_t reg) { int ret = 0; ARG_UNUSED(reg); while (pin_cnt-- > 0U) { ret = pinctrl_configure_pin(pins++); if (ret < 0) { break; } } return ret; }