390 lines
9.9 KiB
C
390 lines
9.9 KiB
C
/*
|
|
* Copyright (c) 2021-2023 IoT.bzh
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT renesas_rcar_pfc
|
|
|
|
#include "pfc_rcar.h"
|
|
#include <zephyr/arch/cpu.h>
|
|
#include <zephyr/devicetree.h>
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/sys/util.h>
|
|
#include <zephyr/sys/device_mmio.h>
|
|
|
|
#define PFC_RCAR_PMMR 0x0
|
|
|
|
/* Gen3 only has one base address, Gen4 has one per GPIO controller */
|
|
#if defined(CONFIG_SOC_SERIES_RCAR_GEN3)
|
|
#define PFC_RCAR_GPSR 0x100
|
|
#define PFC_RCAR_IPSR 0x200
|
|
#elif defined(CONFIG_SOC_SERIES_RCAR_GEN4)
|
|
#define PFC_RCAR_GPSR 0x040
|
|
#define PFC_RCAR_IPSR 0x060
|
|
#else
|
|
#error Unsupported SoC Series
|
|
#endif
|
|
|
|
/* swap both arguments */
|
|
#define PFC_REG_ADDRESS(idx, inst) DT_INST_REG_ADDR_BY_IDX(inst, idx)
|
|
static uintptr_t reg_base[] = {
|
|
LISTIFY(DT_NUM_REGS(DT_DRV_INST(0)), PFC_REG_ADDRESS, (,), 0)
|
|
};
|
|
|
|
#define PFC_REG_SIZE(idx, inst) DT_INST_REG_SIZE_BY_IDX(inst, idx)
|
|
static const uintptr_t __maybe_unused reg_sizes[] = {
|
|
LISTIFY(DT_NUM_REGS(DT_DRV_INST(0)), PFC_REG_SIZE, (,), 0)
|
|
};
|
|
|
|
#ifdef CONFIG_PINCTRL_RCAR_VOLTAGE_CONTROL
|
|
/* POC Control Register can control IO voltage level that is supplied to the pin */
|
|
struct pfc_pocctrl_reg {
|
|
uint32_t offset;
|
|
const uint16_t pins[32];
|
|
};
|
|
#endif /* CONFIG_PINCTRL_RCAR_VOLTAGE_CONTROL */
|
|
|
|
/*
|
|
* 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(uintptr_t pfc_base, uint32_t offs, uint32_t val)
|
|
{
|
|
sys_write32(~val, pfc_base + PFC_RCAR_PMMR);
|
|
sys_write32(val, pfc_base + offs);
|
|
}
|
|
|
|
/* Set the pin either in gpio or peripheral */
|
|
static void pfc_rcar_set_gpsr(uintptr_t pfc_base,
|
|
uint16_t pin, bool peripheral)
|
|
{
|
|
#if defined(CONFIG_SOC_SERIES_RCAR_GEN3)
|
|
/* On Gen3 we have multiple GPSR at one base address */
|
|
uint8_t bank = pin / 32;
|
|
#elif defined(CONFIG_SOC_SERIES_RCAR_GEN4)
|
|
/* On Gen4 we have one GPSR at multiple base address */
|
|
uint8_t bank = 0;
|
|
#endif
|
|
uint8_t bit = pin % 32;
|
|
uint32_t val = sys_read32(pfc_base + PFC_RCAR_GPSR +
|
|
bank * sizeof(uint32_t));
|
|
|
|
if (peripheral) {
|
|
val |= BIT(bit);
|
|
} else {
|
|
val &= ~BIT(bit);
|
|
}
|
|
pfc_rcar_write(pfc_base, PFC_RCAR_GPSR + bank * sizeof(uint32_t), val);
|
|
}
|
|
|
|
/* Set peripheral function */
|
|
static void pfc_rcar_set_ipsr(uintptr_t pfc_base,
|
|
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_base + reg_offs);
|
|
|
|
val &= ~(0xFU << rcar_func->shift);
|
|
val |= (rcar_func->func << rcar_func->shift);
|
|
pfc_rcar_write(pfc_base, 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(uintptr_t pfc_base, 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_base + reg);
|
|
val &= ~GENMASK(offset + size - 1U, offset);
|
|
val |= strength << offset;
|
|
|
|
pfc_rcar_write(pfc_base, 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 around 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(uintptr_t pfc_base, 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_base + bias_reg->puen);
|
|
if ((flags & RCAR_PIN_FLAGS_PUEN) == 0U) {
|
|
sys_write32(val & ~BIT(bit), pfc_base + bias_reg->puen);
|
|
return 0;
|
|
}
|
|
sys_write32(val | BIT(bit), pfc_base + bias_reg->puen);
|
|
|
|
/* pull - up/down */
|
|
val = sys_read32(pfc_base + bias_reg->pud);
|
|
if (flags & RCAR_PIN_FLAGS_PUD) {
|
|
sys_write32(val | BIT(bit), pfc_base + bias_reg->pud);
|
|
} else {
|
|
sys_write32(val & ~BIT(bit), pfc_base + bias_reg->pud);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PINCTRL_RCAR_VOLTAGE_CONTROL
|
|
|
|
const struct pfc_pocctrl_reg pfc_r8a77951_r8a77961_volt_regs[] = {
|
|
{
|
|
.offset = 0x0380,
|
|
.pins = {
|
|
[0] = RCAR_GP_PIN(3, 0), /* SD0_CLK */
|
|
[1] = RCAR_GP_PIN(3, 1), /* SD0_CMD */
|
|
[2] = RCAR_GP_PIN(3, 2), /* SD0_DAT0 */
|
|
[3] = RCAR_GP_PIN(3, 3), /* SD0_DAT1 */
|
|
[4] = RCAR_GP_PIN(3, 4), /* SD0_DAT2 */
|
|
[5] = RCAR_GP_PIN(3, 5), /* SD0_DAT3 */
|
|
[6] = RCAR_GP_PIN(3, 6), /* SD1_CLK */
|
|
[7] = RCAR_GP_PIN(3, 7), /* SD1_CMD */
|
|
[8] = RCAR_GP_PIN(3, 8), /* SD1_DAT0 */
|
|
[9] = RCAR_GP_PIN(3, 9), /* SD1_DAT1 */
|
|
[10] = RCAR_GP_PIN(3, 10), /* SD1_DAT2 */
|
|
[11] = RCAR_GP_PIN(3, 11), /* SD1_DAT3 */
|
|
[12] = RCAR_GP_PIN(4, 0), /* SD2_CLK */
|
|
[13] = RCAR_GP_PIN(4, 1), /* SD2_CMD */
|
|
[14] = RCAR_GP_PIN(4, 2), /* SD2_DAT0 */
|
|
[15] = RCAR_GP_PIN(4, 3), /* SD2_DAT1 */
|
|
[16] = RCAR_GP_PIN(4, 4), /* SD2_DAT2 */
|
|
[17] = RCAR_GP_PIN(4, 5), /* SD2_DAT3 */
|
|
[18] = RCAR_GP_PIN(4, 6), /* SD2_DS */
|
|
[19] = RCAR_GP_PIN(4, 7), /* SD3_CLK */
|
|
[20] = RCAR_GP_PIN(4, 8), /* SD3_CMD */
|
|
[21] = RCAR_GP_PIN(4, 9), /* SD3_DAT0 */
|
|
[22] = RCAR_GP_PIN(4, 10), /* SD3_DAT1 */
|
|
[23] = RCAR_GP_PIN(4, 11), /* SD3_DAT2 */
|
|
[24] = RCAR_GP_PIN(4, 12), /* SD3_DAT3 */
|
|
[25] = RCAR_GP_PIN(4, 13), /* SD3_DAT4 */
|
|
[26] = RCAR_GP_PIN(4, 14), /* SD3_DAT5 */
|
|
[27] = RCAR_GP_PIN(4, 15), /* SD3_DAT6 */
|
|
[28] = RCAR_GP_PIN(4, 16), /* SD3_DAT7 */
|
|
[29] = RCAR_GP_PIN(4, 17), /* SD3_DS */
|
|
[30] = -1,
|
|
[31] = -1,
|
|
}
|
|
},
|
|
{ /* sentinel */ },
|
|
};
|
|
|
|
static const struct pfc_pocctrl_reg *pfc_rcar_get_io_voltage_regs(void)
|
|
{
|
|
return pfc_r8a77951_r8a77961_volt_regs;
|
|
}
|
|
|
|
static const struct pfc_pocctrl_reg *pfc_rcar_get_pocctrl_reg(uint16_t pin, uint8_t *bit)
|
|
{
|
|
const struct pfc_pocctrl_reg *voltage_regs = pfc_rcar_get_io_voltage_regs();
|
|
|
|
BUILD_ASSERT(ARRAY_SIZE(voltage_regs->pins) < UINT8_MAX);
|
|
|
|
/* Loop around all the registers to find the bit for a given pin */
|
|
while (voltage_regs && voltage_regs->offset) {
|
|
uint8_t i;
|
|
|
|
for (i = 0U; i < ARRAY_SIZE(voltage_regs->pins); i++) {
|
|
if (voltage_regs->pins[i] == pin) {
|
|
*bit = i;
|
|
return voltage_regs;
|
|
}
|
|
}
|
|
voltage_regs++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void pfc_rcar_set_voltage(uintptr_t pfc_base, uint16_t pin, uint16_t voltage)
|
|
{
|
|
uint32_t val;
|
|
uint8_t bit;
|
|
const struct pfc_pocctrl_reg *voltage_reg;
|
|
|
|
voltage_reg = pfc_rcar_get_pocctrl_reg(pin, &bit);
|
|
if (!voltage_reg) {
|
|
return;
|
|
}
|
|
|
|
val = sys_read32(pfc_base + voltage_reg->offset);
|
|
|
|
switch (voltage) {
|
|
case PIN_VOLTAGE_1P8V:
|
|
if (!(val & BIT(bit))) {
|
|
return;
|
|
}
|
|
val &= ~BIT(bit);
|
|
break;
|
|
case PIN_VOLTAGE_3P3V:
|
|
if (val & BIT(bit)) {
|
|
return;
|
|
}
|
|
val |= BIT(bit);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
pfc_rcar_write(pfc_base, voltage_reg->offset, val);
|
|
}
|
|
#endif /* CONFIG_PINCTRL_RCAR_VOLTAGE_CONTROL */
|
|
|
|
int pinctrl_configure_pin(const pinctrl_soc_pin_t *pin)
|
|
{
|
|
int ret = 0;
|
|
uint8_t reg_index;
|
|
uintptr_t pfc_base;
|
|
|
|
ret = pfc_rcar_get_reg_index(pin->pin, ®_index);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
if (reg_index >= ARRAY_SIZE(reg_base)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
pfc_base = reg_base[reg_index];
|
|
|
|
/* Set pin as GPIO if capable */
|
|
if (RCAR_IS_GP_PIN(pin->pin)) {
|
|
pfc_rcar_set_gpsr(pfc_base, 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;
|
|
}
|
|
|
|
#ifdef CONFIG_PINCTRL_RCAR_VOLTAGE_CONTROL
|
|
if (pin->voltage != PIN_VOLTAGE_NONE) {
|
|
pfc_rcar_set_voltage(pfc_base, pin->pin, pin->voltage);
|
|
}
|
|
#endif
|
|
|
|
/* Select function for pin */
|
|
if ((pin->flags & RCAR_PIN_FLAGS_FUNC_SET) != 0U) {
|
|
pfc_rcar_set_ipsr(pfc_base, &pin->func);
|
|
|
|
if (RCAR_IS_GP_PIN(pin->pin)) {
|
|
pfc_rcar_set_gpsr(pfc_base, pin->pin, true);
|
|
}
|
|
|
|
if ((pin->flags & RCAR_PIN_FLAGS_PULL_SET) != 0U) {
|
|
ret = pfc_rcar_set_bias(pfc_base, pin->pin, pin->flags);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pin->drive_strength != 0U) {
|
|
ret = pfc_rcar_set_drive_strength(pfc_base, 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;
|
|
}
|
|
|
|
#if defined(DEVICE_MMIO_IS_IN_RAM)
|
|
__boot_func static int pfc_rcar_driver_init(void)
|
|
{
|
|
for (unsigned int i = 0; i < ARRAY_SIZE(reg_base); i++) {
|
|
device_map(reg_base + i, reg_base[i], reg_sizes[i], K_MEM_CACHE_NONE);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
SYS_INIT(pfc_rcar_driver_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
|
#endif
|