From patchwork Wed May 27 12:43:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Heiko Schocher X-Patchwork-Id: 246680 List-Id: U-Boot discussion From: hs at denx.de (Heiko Schocher) Date: Wed, 27 May 2020 14:43:14 +0200 Subject: [PATCH v2 3/5] powerpc, qe: add DTS support for parallel I/O ports In-Reply-To: <20200527124316.3339103-1-hs@denx.de> References: <20200527124316.3339103-1-hs@denx.de> Message-ID: <20200527124316.3339103-4-hs@denx.de> add DM support for parallel I/O ports on QUICC Engine Block Signed-off-by: Heiko Schocher --- Open questions / discussion: - I let the old none DM based implementation in code so boards should work with old implementation. This should be removed if all boards are converted to DM/DTS. - Unfortunately linux DTS does not use "pinctrl-" properties, instead "pio-handle" properties. Even worser old U-Boot code initializes all pins defined in "const qe_iop_conf_t qe_iop_conf_tab[]" table in board code. As linux does the same I decided to also scan through all subnodes containing "pio-map" property and initialize them too. The proper solution would be to check for "pio-handle" when a device is probed. Changes in v2: - remove RFC - fixed Codingstyle errors, therefore new patch powerpc, mpc83xx: fix codingstyle issues for qe_io.c - moved DM part to drivers/pinctrl arch/powerpc/cpu/mpc83xx/Makefile | 2 + arch/powerpc/cpu/mpc83xx/cpu_init.c | 8 + arch/powerpc/cpu/mpc83xx/qe_io.c | 83 ++++++--- drivers/pinctrl/Kconfig | 7 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-qe-io.c | 255 ++++++++++++++++++++++++++++ include/fsl_qe.h | 3 + 7 files changed, 334 insertions(+), 25 deletions(-) create mode 100644 drivers/pinctrl/pinctrl-qe-io.c diff --git a/arch/powerpc/cpu/mpc83xx/Makefile b/arch/powerpc/cpu/mpc83xx/Makefile index 304029977e..aeb42b109d 100644 --- a/arch/powerpc/cpu/mpc83xx/Makefile +++ b/arch/powerpc/cpu/mpc83xx/Makefile @@ -27,7 +27,9 @@ obj-y += cpu_init.o obj-y += speed.o obj-y += interrupts.o obj-y += ecc.o +ifndef CONFIG_PINCTRL obj-$(CONFIG_QE) += qe_io.o +endif obj-$(CONFIG_FSL_SERDES) += serdes.o ifndef CONFIG_ARCH_MPC8308 obj-$(CONFIG_PCI) += pci.o diff --git a/arch/powerpc/cpu/mpc83xx/cpu_init.c b/arch/powerpc/cpu/mpc83xx/cpu_init.c index b21b0b0324..840f907acb 100644 --- a/arch/powerpc/cpu/mpc83xx/cpu_init.c +++ b/arch/powerpc/cpu/mpc83xx/cpu_init.c @@ -14,6 +14,9 @@ #include #endif #include +#ifdef CONFIG_QE +#include +#endif #include "lblaw/lblaw.h" #include "elbc/elbc.h" @@ -28,6 +31,7 @@ extern qe_iop_conf_t qe_iop_conf_tab[]; extern void qe_config_iopin(u8 port, u8 pin, int dir, int open_drain, int assign); +#if !defined(CONFIG_PINCTRL) static void config_qe_ioports(void) { u8 port, pin; @@ -44,6 +48,7 @@ static void config_qe_ioports(void) } } #endif +#endif /* * Breathe some life into the CPU... @@ -190,10 +195,13 @@ void cpu_init_f (volatile immap_t * im) __raw_writel(CONFIG_SYS_OBIR, &im->sysconf.obir); #endif +#if !defined(CONFIG_PINCTRL) #ifdef CONFIG_QE /* Config QE ioports */ config_qe_ioports(); #endif +#endif + /* Set up preliminary BR/OR regs */ init_early_memctl_regs(); diff --git a/arch/powerpc/cpu/mpc83xx/qe_io.c b/arch/powerpc/cpu/mpc83xx/qe_io.c index 1079ae128a..52360703a7 100644 --- a/arch/powerpc/cpu/mpc83xx/qe_io.c +++ b/arch/powerpc/cpu/mpc83xx/qe_io.c @@ -12,24 +12,35 @@ #include #define NUM_OF_PINS 32 -void qe_config_iopin(u8 port, u8 pin, int dir, int open_drain, int assign) + +/** qe_cfg_iopin configure one io pin setting + * + * @par_io: pointer to parallel I/O base + * @port: io pin port + * @pin: io pin number which get configured + * @dir: direction of io pin 2 bits valid + * 00 = pin disabled + * 01 = output + * 10 = input + * 11 = pin is I/O + * @open_drain: is pin open drain + * @assign: pin assignment registers select the function of the pin + */ +static void qe_cfg_iopin(qepio83xx_t *par_io, u8 port, u8 pin, int dir, + int open_drain, int assign) { - u32 2bit_mask; - u32 2bit_dir; - u32 2bit_assign; - u32 1bit_mask; - u32 tmp_val; - immap_t *im; - qepio83xx_t *par_io; - int offset; + u32 dbit_mask; + u32 dbit_dir; + u32 dbit_asgn; + u32 bit_mask; + u32 tmp_val; + int offset; - im = (immap_t *)CONFIG_SYS_IMMR; - par_io = (qepio83xx_t *)&im->qepio; offset = (NUM_OF_PINS - (pin % (NUM_OF_PINS / 2) + 1) * 2); /* Calculate pin location and 2bit mask and dir */ - 2bit_mask = (u32)(0x3 << offset); - 2bit_dir = (u32)(dir << offset); + dbit_mask = (u32)(0x3 << offset); + dbit_dir = (u32)(dir << offset); /* Setup the direction */ tmp_val = (pin > (NUM_OF_PINS / 2) - 1) ? @@ -37,35 +48,57 @@ void qe_config_iopin(u8 port, u8 pin, int dir, int open_drain, int assign) in_be32(&par_io->ioport[port].dir1); if (pin > (NUM_OF_PINS / 2) - 1) { - out_be32(&par_io->ioport[port].dir2, ~2bit_mask & tmp_val); - out_be32(&par_io->ioport[port].dir2, 2bit_dir | tmp_val); + out_be32(&par_io->ioport[port].dir2, ~dbit_mask & tmp_val); + out_be32(&par_io->ioport[port].dir2, dbit_dir | tmp_val); } else { - out_be32(&par_io->ioport[port].dir1, ~2bit_mask & tmp_val); - out_be32(&par_io->ioport[port].dir1, 2bit_dir | tmp_val); + out_be32(&par_io->ioport[port].dir1, ~dbit_mask & tmp_val); + out_be32(&par_io->ioport[port].dir1, dbit_dir | tmp_val); } /* Calculate pin location for 1bit mask */ - 1bit_mask = (u32)(1 << (NUM_OF_PINS - (pin + 1))); + bit_mask = (u32)(1 << (NUM_OF_PINS - (pin + 1))); /* Setup the open drain */ tmp_val = in_be32(&par_io->ioport[port].podr); if (open_drain) - out_be32(&par_io->ioport[port].podr, 1bit_mask | tmp_val); + out_be32(&par_io->ioport[port].podr, bit_mask | tmp_val); else - out_be32(&par_io->ioport[port].podr, ~1bit_mask & tmp_val); + out_be32(&par_io->ioport[port].podr, ~bit_mask & tmp_val); /* Setup the assignment */ tmp_val = (pin > (NUM_OF_PINS / 2) - 1) ? in_be32(&par_io->ioport[port].ppar2) : in_be32(&par_io->ioport[port].ppar1); - 2bit_assign = (u32)(assign << offset); + dbit_asgn = (u32)(assign << offset); /* Clear and set 2 bits mask */ if (pin > (NUM_OF_PINS / 2) - 1) { - out_be32(&par_io->ioport[port].ppar2, ~2bit_mask & tmp_val); - out_be32(&par_io->ioport[port].ppar2, 2bit_assign | tmp_val); + out_be32(&par_io->ioport[port].ppar2, ~dbit_mask & tmp_val); + out_be32(&par_io->ioport[port].ppar2, dbit_asgn | tmp_val); } else { - out_be32(&par_io->ioport[port].ppar1, ~2bit_mask & tmp_val); - out_be32(&par_io->ioport[port].ppar1, 2bit_assign | tmp_val); + out_be32(&par_io->ioport[port].ppar1, ~dbit_mask & tmp_val); + out_be32(&par_io->ioport[port].ppar1, dbit_asgn | tmp_val); } } + +#if !defined(CONFIG_PINCTRL) +/** qe_config_iopin configure one io pin setting + * + * @port: io pin port + * @pin: io pin number which get configured + * @dir: direction of io pin 2 bits valid + * 00 = pin disabled + * 01 = output + * 10 = input + * 11 = pin is I/O + * @open_drain: is pin open drain + * @assign: pin assignment registers select the function of the pin + */ +void qe_config_iopin(u8 port, u8 pin, int dir, int open_drain, int assign) +{ + immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + qepio83xx_t *par_io = (qepio83xx_t *)&im->qepio; + + qe_cfg_iopin(par_io, port, pin, dir, open_drain, assign); +} +#endif diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 83e39b9de3..06df148b06 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -205,6 +205,13 @@ config PINCTRL_QCA953X the GPIO definitions and pin control functions for each available multiplex function. +config PINCTRL_QE + bool "QE based pinctrl driver, like on mpc83xx" + depends on DM + help + This option is to enable the QE pinctrl driver for QE based io + controller. + config PINCTRL_ROCKCHIP_RV1108 bool "Rockchip rv1108 pin control driver" depends on DM diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 4f662c4f6d..7b9a672198 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_PINCTRL_MESON) += meson/ obj-$(CONFIG_PINCTRL_MTK) += mediatek/ obj-$(CONFIG_PINCTRL_MSCC) += mscc/ obj-$(CONFIG_ARCH_MVEBU) += mvebu/ +obj-$(CONFIG_PINCTRL_QE) += pinctrl-qe-io.o obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o obj-$(CONFIG_PINCTRL_STI) += pinctrl-sti.o obj-$(CONFIG_PINCTRL_STM32) += pinctrl_stm32.o diff --git a/drivers/pinctrl/pinctrl-qe-io.c b/drivers/pinctrl/pinctrl-qe-io.c new file mode 100644 index 0000000000..85521eabd4 --- /dev/null +++ b/drivers/pinctrl/pinctrl-qe-io.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2006 Freescale Semiconductor, Inc. + * + * Dave Liu + * based on source code of Shlomi Gridish + */ + +#include +#include +#include +#include + +#if defined(CONFIG_PINCTRL) +#include +#include +#include +#include + +/** + * struct qe_io_platdata + * + * @base: Base register address + * @num_par_io_ports number of io ports + */ +struct qe_io_platdata { + qepio83xx_t *base; + u32 num_io_ports; +}; +#endif + +#define NUM_OF_PINS 32 + +/** qe_cfg_iopin configure one io pin setting + * + * @par_io: pointer to parallel I/O base + * @port: io pin port + * @pin: io pin number which get configured + * @dir: direction of io pin 2 bits valid + * 00 = pin disabled + * 01 = output + * 10 = input + * 11 = pin is I/O + * @open_drain: is pin open drain + * @assign: pin assignment registers select the function of the pin + */ +static void qe_cfg_iopin(qepio83xx_t *par_io, u8 port, u8 pin, int dir, + int open_drain, int assign) +{ + u32 dbit_mask; + u32 dbit_dir; + u32 dbit_asgn; + u32 bit_mask; + u32 tmp_val; + int offset; + + offset = (NUM_OF_PINS - (pin % (NUM_OF_PINS / 2) + 1) * 2); + + /* Calculate pin location and 2bit mask and dir */ + dbit_mask = (u32)(0x3 << offset); + dbit_dir = (u32)(dir << offset); + + /* Setup the direction */ + tmp_val = (pin > (NUM_OF_PINS / 2) - 1) ? + in_be32(&par_io->ioport[port].dir2) : + in_be32(&par_io->ioport[port].dir1); + + if (pin > (NUM_OF_PINS / 2) - 1) { + out_be32(&par_io->ioport[port].dir2, ~dbit_mask & tmp_val); + out_be32(&par_io->ioport[port].dir2, dbit_dir | tmp_val); + } else { + out_be32(&par_io->ioport[port].dir1, ~dbit_mask & tmp_val); + out_be32(&par_io->ioport[port].dir1, dbit_dir | tmp_val); + } + + /* Calculate pin location for 1bit mask */ + bit_mask = (u32)(1 << (NUM_OF_PINS - (pin + 1))); + + /* Setup the open drain */ + tmp_val = in_be32(&par_io->ioport[port].podr); + if (open_drain) + out_be32(&par_io->ioport[port].podr, bit_mask | tmp_val); + else + out_be32(&par_io->ioport[port].podr, ~bit_mask & tmp_val); + + /* Setup the assignment */ + tmp_val = (pin > (NUM_OF_PINS / 2) - 1) ? + in_be32(&par_io->ioport[port].ppar2) : + in_be32(&par_io->ioport[port].ppar1); + dbit_asgn = (u32)(assign << offset); + + /* Clear and set 2 bits mask */ + if (pin > (NUM_OF_PINS / 2) - 1) { + out_be32(&par_io->ioport[port].ppar2, ~dbit_mask & tmp_val); + out_be32(&par_io->ioport[port].ppar2, dbit_asgn | tmp_val); + } else { + out_be32(&par_io->ioport[port].ppar1, ~dbit_mask & tmp_val); + out_be32(&par_io->ioport[port].ppar1, dbit_asgn | tmp_val); + } +} + +#if !defined(CONFIG_PINCTRL) +/** qe_config_iopin configure one io pin setting + * + * @port: io pin port + * @pin: io pin number which get configured + * @dir: direction of io pin 2 bits valid + * 00 = pin disabled + * 01 = output + * 10 = input + * 11 = pin is I/O + * @open_drain: is pin open drain + * @assign: pin assignment registers select the function of the pin + */ +void qe_config_iopin(u8 port, u8 pin, int dir, int open_drain, int assign) +{ + immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + qepio83xx_t *par_io = (qepio83xx_t *)&im->qepio; + + qe_cfg_iopin(par_io, port, pin, dir, open_drain, assign); +} +#else +static int qe_io_ofdata_to_platdata(struct udevice *dev) +{ + struct qe_io_platdata *plat = dev->platdata; + fdt_addr_t addr; + + addr = dev_read_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + plat->base = (qepio83xx_t *)addr; + if (dev_read_u32(dev, "num-ports", &plat->num_io_ports)) + return -EINVAL; + + return 0; +} + +/** + * par_io_of_config_node config + * @dev: pointer to pinctrl device + * @pio: ofnode of pinconfig property + */ +static int par_io_of_config_node(struct udevice *dev, ofnode pio) +{ + struct qe_io_platdata *plat = dev->platdata; + qepio83xx_t *par_io = plat->base; + const unsigned int *pio_map; + int pio_map_len; + + pio_map = ofnode_get_property(pio, "pio-map", &pio_map_len); + if (!pio_map) + return -ENOENT; + + pio_map_len /= sizeof(unsigned int); + if ((pio_map_len % 6) != 0) { + dev_err(dev, "%s: pio-map format wrong!\n", __func__); + return -EINVAL; + } + + while (pio_map_len > 0) { + /* + * column pio_map[5] from linux (has_irq) not + * supported in u-boot yet. + */ + qe_cfg_iopin(par_io, (u8)pio_map[0], (u8)pio_map[1], + (int)pio_map[2], (int)pio_map[3], + (int)pio_map[4]); + pio_map += 6; + pio_map_len -= 6; + } + return 0; +} + +int par_io_of_config(struct udevice *dev) +{ + u32 phandle; + ofnode pio; + int err; + + err = ofnode_read_u32(dev_ofnode(dev), "pio-handle", &phandle); + if (err) { + dev_err(dev, "%s: pio-handle not available\n", __func__); + return err; + } + + pio = ofnode_get_by_phandle(phandle); + if (!ofnode_valid(pio)) { + dev_err(dev, "%s: unable to find node\n", __func__); + return -EINVAL; + } + + /* To Do: find pinctrl device and pass it */ + return par_io_of_config_node(NULL, pio); +} + +/* + * This is not nice! + * pinsettings should work with "pinctrl-" properties. + * Unfortunately on mpc83xx powerpc linux device trees + * devices handle this with "pio-handle" properties ... + * + * Even worser, old board code inits all par_io + * pins in one step, if U-Boot uses the device + * or not. So init all par_io definitions here too + * as linux does this also. + */ +static void config_qe_ioports(struct udevice *dev) +{ + ofnode ofn; + + for (ofn = dev_read_first_subnode(dev); ofnode_valid(ofn); + ofn = dev_read_next_subnode(ofn)) { + /* + * ignore errors here, as may the subnode + * has no pio-handle + */ + par_io_of_config_node(dev, ofn); + } +} + +static int par_io_pinctrl_probe(struct udevice *dev) +{ + config_qe_ioports(dev); + + return 0; +} + +static int par_io_pinctrl_set_state(struct udevice *dev, struct udevice *config) +{ + return 0; +} + +const struct pinctrl_ops par_io_pinctrl_ops = { + .set_state = par_io_pinctrl_set_state, +}; + +static const struct udevice_id par_io_pinctrl_match[] = { + { .compatible = "fsl,mpc8360-par_io"}, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(par_io_pinctrl) = { + .name = "par-io-pinctrl", + .id = UCLASS_PINCTRL, + .of_match = of_match_ptr(par_io_pinctrl_match), + .probe = par_io_pinctrl_probe, + .ofdata_to_platdata = qe_io_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct qe_io_platdata), + .ops = &par_io_pinctrl_ops, +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) + .flags = DM_FLAG_PRE_RELOC, +#endif +}; +#endif diff --git a/include/fsl_qe.h b/include/fsl_qe.h index 6e44cbdb56..1c6941347f 100644 --- a/include/fsl_qe.h +++ b/include/fsl_qe.h @@ -296,4 +296,7 @@ int u_qe_firmware_resume(const struct qe_firmware *firmware, qe_map_t *qe_immrr); #endif +#if defined(CONFIG_PINCTRL) +int par_io_of_config(struct udevice *dev); +#endif #endif /* __QE_H__ */