mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-20 22:23:27 +00:00
1667 lines
43 KiB
Diff
1667 lines
43 KiB
Diff
|
From 4d4cc5be473a7767052122a87393a83d10f9ed41 Mon Sep 17 00:00:00 2001
|
||
|
From: Phil Elwell <phil@raspberrypi.com>
|
||
|
Date: Mon, 10 Oct 2022 14:21:11 +0100
|
||
|
Subject: [PATCH] pinctrl: Add rp1 driver
|
||
|
|
||
|
RP1 exposes GPIOs. Add a pinctrl driver to allow control of those.
|
||
|
|
||
|
Signed-off-by: Phil Elwell <phil@raspberrypi.com>
|
||
|
---
|
||
|
drivers/pinctrl/Kconfig | 7 +
|
||
|
drivers/pinctrl/Makefile | 1 +
|
||
|
drivers/pinctrl/pinctrl-rp1.c | 1571 +++++++++++++++++++++++++++++
|
||
|
include/dt-bindings/pinctrl/rp1.h | 46 -
|
||
|
4 files changed, 1579 insertions(+), 46 deletions(-)
|
||
|
create mode 100644 drivers/pinctrl/pinctrl-rp1.c
|
||
|
delete mode 100644 include/dt-bindings/pinctrl/rp1.h
|
||
|
|
||
|
--- a/drivers/pinctrl/Kconfig
|
||
|
+++ b/drivers/pinctrl/Kconfig
|
||
|
@@ -512,6 +512,13 @@ config PINCTRL_ZYNQMP
|
||
|
This driver can also be built as a module. If so, the module
|
||
|
will be called pinctrl-zynqmp.
|
||
|
|
||
|
+config PINCTRL_RP1
|
||
|
+ bool "Pinctrl driver for RP1"
|
||
|
+ select PINMUX
|
||
|
+ select PINCONF
|
||
|
+ select GENERIC_PINCONF
|
||
|
+ select GPIOLIB_IRQCHIP
|
||
|
+
|
||
|
source "drivers/pinctrl/actions/Kconfig"
|
||
|
source "drivers/pinctrl/aspeed/Kconfig"
|
||
|
source "drivers/pinctrl/bcm/Kconfig"
|
||
|
--- a/drivers/pinctrl/Makefile
|
||
|
+++ b/drivers/pinctrl/Makefile
|
||
|
@@ -42,6 +42,7 @@ obj-$(CONFIG_PINCTRL_PIC32) += pinctrl-p
|
||
|
obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o
|
||
|
obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o
|
||
|
obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
|
||
|
+obj-$(CONFIG_PINCTRL_RP1) += pinctrl-rp1.o
|
||
|
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
|
||
|
obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
|
||
|
obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/pinctrl/pinctrl-rp1.c
|
||
|
@@ -0,0 +1,1571 @@
|
||
|
+// SPDX-License-Identifier: GPL-2.0
|
||
|
+/*
|
||
|
+ * Driver for Raspberry Pi RP1 GPIO unit (pinctrl + GPIO)
|
||
|
+ *
|
||
|
+ * Copyright (C) 2023 Raspberry Pi Ltd.
|
||
|
+ *
|
||
|
+ * This driver is inspired by:
|
||
|
+ * pinctrl-bcm2835.c, please see original file for copyright information
|
||
|
+ */
|
||
|
+
|
||
|
+#include <linux/bitmap.h>
|
||
|
+#include <linux/bitops.h>
|
||
|
+#include <linux/bug.h>
|
||
|
+#include <linux/delay.h>
|
||
|
+#include <linux/device.h>
|
||
|
+#include <linux/err.h>
|
||
|
+#include <linux/gpio/driver.h>
|
||
|
+#include <linux/io.h>
|
||
|
+#include <linux/irq.h>
|
||
|
+#include <linux/irqdesc.h>
|
||
|
+#include <linux/init.h>
|
||
|
+#include <linux/of_address.h>
|
||
|
+#include <linux/of.h>
|
||
|
+#include <linux/of_irq.h>
|
||
|
+#include <linux/pinctrl/consumer.h>
|
||
|
+#include <linux/pinctrl/machine.h>
|
||
|
+#include <linux/pinctrl/pinconf.h>
|
||
|
+#include <linux/pinctrl/pinctrl.h>
|
||
|
+#include <linux/pinctrl/pinmux.h>
|
||
|
+#include <linux/pinctrl/pinconf-generic.h>
|
||
|
+#include <linux/platform_device.h>
|
||
|
+#include <linux/seq_file.h>
|
||
|
+#include <linux/spinlock.h>
|
||
|
+#include <linux/types.h>
|
||
|
+#include "core.h"
|
||
|
+#include "pinconf.h"
|
||
|
+#include "pinctrl-utils.h"
|
||
|
+
|
||
|
+#define MODULE_NAME "pinctrl-rp1"
|
||
|
+#define RP1_NUM_GPIOS 54
|
||
|
+#define RP1_NUM_BANKS 3
|
||
|
+
|
||
|
+#define RP1_RW_OFFSET 0x0000
|
||
|
+#define RP1_XOR_OFFSET 0x1000
|
||
|
+#define RP1_SET_OFFSET 0x2000
|
||
|
+#define RP1_CLR_OFFSET 0x3000
|
||
|
+
|
||
|
+#define RP1_GPIO_STATUS 0x0000
|
||
|
+#define RP1_GPIO_CTRL 0x0004
|
||
|
+
|
||
|
+#define RP1_GPIO_PCIE_INTE 0x011c
|
||
|
+#define RP1_GPIO_PCIE_INTS 0x0124
|
||
|
+
|
||
|
+#define RP1_GPIO_EVENTS_SHIFT_RAW 20
|
||
|
+#define RP1_GPIO_STATUS_FALLING BIT(20)
|
||
|
+#define RP1_GPIO_STATUS_RISING BIT(21)
|
||
|
+#define RP1_GPIO_STATUS_LOW BIT(22)
|
||
|
+#define RP1_GPIO_STATUS_HIGH BIT(23)
|
||
|
+
|
||
|
+#define RP1_GPIO_EVENTS_SHIFT_FILTERED 24
|
||
|
+#define RP1_GPIO_STATUS_F_FALLING BIT(24)
|
||
|
+#define RP1_GPIO_STATUS_F_RISING BIT(25)
|
||
|
+#define RP1_GPIO_STATUS_F_LOW BIT(26)
|
||
|
+#define RP1_GPIO_STATUS_F_HIGH BIT(27)
|
||
|
+
|
||
|
+#define RP1_GPIO_CTRL_FUNCSEL_LSB 0
|
||
|
+#define RP1_GPIO_CTRL_FUNCSEL_MASK 0x0000001f
|
||
|
+#define RP1_GPIO_CTRL_OUTOVER_LSB 12
|
||
|
+#define RP1_GPIO_CTRL_OUTOVER_MASK 0x00003000
|
||
|
+#define RP1_GPIO_CTRL_OEOVER_LSB 14
|
||
|
+#define RP1_GPIO_CTRL_OEOVER_MASK 0x0000c000
|
||
|
+#define RP1_GPIO_CTRL_INOVER_LSB 16
|
||
|
+#define RP1_GPIO_CTRL_INOVER_MASK 0x00030000
|
||
|
+#define RP1_GPIO_CTRL_IRQEN_FALLING BIT(20)
|
||
|
+#define RP1_GPIO_CTRL_IRQEN_RISING BIT(21)
|
||
|
+#define RP1_GPIO_CTRL_IRQEN_LOW BIT(22)
|
||
|
+#define RP1_GPIO_CTRL_IRQEN_HIGH BIT(23)
|
||
|
+#define RP1_GPIO_CTRL_IRQEN_F_FALLING BIT(24)
|
||
|
+#define RP1_GPIO_CTRL_IRQEN_F_RISING BIT(25)
|
||
|
+#define RP1_GPIO_CTRL_IRQEN_F_LOW BIT(26)
|
||
|
+#define RP1_GPIO_CTRL_IRQEN_F_HIGH BIT(27)
|
||
|
+#define RP1_GPIO_CTRL_IRQRESET BIT(28)
|
||
|
+#define RP1_GPIO_CTRL_IRQOVER_LSB 30
|
||
|
+#define RP1_GPIO_CTRL_IRQOVER_MASK 0xc0000000
|
||
|
+
|
||
|
+#define RP1_INT_EDGE_FALLING BIT(0)
|
||
|
+#define RP1_INT_EDGE_RISING BIT(1)
|
||
|
+#define RP1_INT_LEVEL_LOW BIT(2)
|
||
|
+#define RP1_INT_LEVEL_HIGH BIT(3)
|
||
|
+#define RP1_INT_MASK 0xf
|
||
|
+
|
||
|
+#define RP1_INT_EDGE_BOTH (RP1_INT_EDGE_FALLING | \
|
||
|
+ RP1_INT_EDGE_RISING)
|
||
|
+#define RP1_PUD_OFF 0
|
||
|
+#define RP1_PUD_DOWN 1
|
||
|
+#define RP1_PUD_UP 2
|
||
|
+
|
||
|
+#define RP1_FSEL_COUNT 9
|
||
|
+
|
||
|
+#define RP1_FSEL_ALT0 0x00
|
||
|
+#define RP1_FSEL_GPIO 0x05
|
||
|
+#define RP1_FSEL_NONE 0x09
|
||
|
+#define RP1_FSEL_NONE_HW 0x1f
|
||
|
+
|
||
|
+#define RP1_DIR_OUTPUT 0
|
||
|
+#define RP1_DIR_INPUT 1
|
||
|
+
|
||
|
+#define RP1_OUTOVER_PERI 0
|
||
|
+#define RP1_OUTOVER_INVPERI 1
|
||
|
+#define RP1_OUTOVER_LOW 2
|
||
|
+#define RP1_OUTOVER_HIGH 3
|
||
|
+
|
||
|
+#define RP1_OEOVER_PERI 0
|
||
|
+#define RP1_OEOVER_INVPERI 1
|
||
|
+#define RP1_OEOVER_DISABLE 2
|
||
|
+#define RP1_OEOVER_ENABLE 3
|
||
|
+
|
||
|
+#define RP1_INOVER_PERI 0
|
||
|
+#define RP1_INOVER_INVPERI 1
|
||
|
+#define RP1_INOVER_LOW 2
|
||
|
+#define RP1_INOVER_HIGH 3
|
||
|
+
|
||
|
+#define RP1_RIO_OUT 0x00
|
||
|
+#define RP1_RIO_OE 0x04
|
||
|
+#define RP1_RIO_IN 0x08
|
||
|
+
|
||
|
+#define RP1_PAD_SLEWFAST_MASK 0x00000001
|
||
|
+#define RP1_PAD_SLEWFAST_LSB 0
|
||
|
+#define RP1_PAD_SCHMITT_MASK 0x00000002
|
||
|
+#define RP1_PAD_SCHMITT_LSB 1
|
||
|
+#define RP1_PAD_PULL_MASK 0x0000000c
|
||
|
+#define RP1_PAD_PULL_LSB 2
|
||
|
+#define RP1_PAD_DRIVE_MASK 0x00000030
|
||
|
+#define RP1_PAD_DRIVE_LSB 4
|
||
|
+#define RP1_PAD_IN_ENABLE_MASK 0x00000040
|
||
|
+#define RP1_PAD_IN_ENABLE_LSB 6
|
||
|
+#define RP1_PAD_OUT_DISABLE_MASK 0x00000080
|
||
|
+#define RP1_PAD_OUT_DISABLE_LSB 7
|
||
|
+
|
||
|
+#define RP1_PAD_DRIVE_2MA 0x00000000
|
||
|
+#define RP1_PAD_DRIVE_4MA 0x00000010
|
||
|
+#define RP1_PAD_DRIVE_8MA 0x00000020
|
||
|
+#define RP1_PAD_DRIVE_12MA 0x00000030
|
||
|
+
|
||
|
+#define FLD_GET(r, f) (((r) & (f ## _MASK)) >> (f ## _LSB))
|
||
|
+#define FLD_SET(r, f, v) r = (((r) & ~(f ## _MASK)) | ((v) << (f ## _LSB)))
|
||
|
+
|
||
|
+#define FUNC(f) \
|
||
|
+ [func_##f] = #f
|
||
|
+#define RP1_MAX_FSEL 8
|
||
|
+#define PIN(i, f0, f1, f2, f3, f4, f5, f6, f7, f8) \
|
||
|
+ [i] = { \
|
||
|
+ .funcs = { \
|
||
|
+ func_##f0, \
|
||
|
+ func_##f1, \
|
||
|
+ func_##f2, \
|
||
|
+ func_##f3, \
|
||
|
+ func_##f4, \
|
||
|
+ func_##f5, \
|
||
|
+ func_##f6, \
|
||
|
+ func_##f7, \
|
||
|
+ func_##f8, \
|
||
|
+ }, \
|
||
|
+ }
|
||
|
+
|
||
|
+#define LEGACY_MAP(n, f0, f1, f2, f3, f4, f5) \
|
||
|
+ [n] = { \
|
||
|
+ func_gpio, \
|
||
|
+ func_gpio, \
|
||
|
+ func_##f5, \
|
||
|
+ func_##f4, \
|
||
|
+ func_##f0, \
|
||
|
+ func_##f1, \
|
||
|
+ func_##f2, \
|
||
|
+ func_##f3, \
|
||
|
+ }
|
||
|
+
|
||
|
+struct rp1_iobank_desc {
|
||
|
+ int min_gpio;
|
||
|
+ int num_gpios;
|
||
|
+ int gpio_offset;
|
||
|
+ int inte_offset;
|
||
|
+ int ints_offset;
|
||
|
+ int rio_offset;
|
||
|
+ int pads_offset;
|
||
|
+};
|
||
|
+
|
||
|
+struct rp1_pin_info {
|
||
|
+ u8 num;
|
||
|
+ u8 bank;
|
||
|
+ u8 offset;
|
||
|
+ u8 fsel;
|
||
|
+ u8 irq_type;
|
||
|
+
|
||
|
+ void __iomem *gpio;
|
||
|
+ void __iomem *rio;
|
||
|
+ void __iomem *inte;
|
||
|
+ void __iomem *ints;
|
||
|
+ void __iomem *pad;
|
||
|
+};
|
||
|
+
|
||
|
+enum funcs {
|
||
|
+ func_alt0,
|
||
|
+ func_alt1,
|
||
|
+ func_alt2,
|
||
|
+ func_alt3,
|
||
|
+ func_alt4,
|
||
|
+ func_gpio,
|
||
|
+ func_alt6,
|
||
|
+ func_alt7,
|
||
|
+ func_alt8,
|
||
|
+ func_none,
|
||
|
+ func_aaud,
|
||
|
+ func_dcd0,
|
||
|
+ func_dpi,
|
||
|
+ func_dsi0_te_ext,
|
||
|
+ func_dsi1_te_ext,
|
||
|
+ func_dsr0,
|
||
|
+ func_dtr0,
|
||
|
+ func_gpclk0,
|
||
|
+ func_gpclk1,
|
||
|
+ func_gpclk2,
|
||
|
+ func_gpclk3,
|
||
|
+ func_gpclk4,
|
||
|
+ func_gpclk5,
|
||
|
+ func_i2c0,
|
||
|
+ func_i2c1,
|
||
|
+ func_i2c2,
|
||
|
+ func_i2c3,
|
||
|
+ func_i2c4,
|
||
|
+ func_i2c5,
|
||
|
+ func_i2c6,
|
||
|
+ func_i2s0,
|
||
|
+ func_i2s1,
|
||
|
+ func_i2s2,
|
||
|
+ func_ir,
|
||
|
+ func_mic,
|
||
|
+ func_pcie_clkreq_n,
|
||
|
+ func_pio,
|
||
|
+ func_proc_rio,
|
||
|
+ func_pwm0,
|
||
|
+ func_pwm1,
|
||
|
+ func_ri0,
|
||
|
+ func_sd0,
|
||
|
+ func_sd1,
|
||
|
+ func_spi0,
|
||
|
+ func_spi1,
|
||
|
+ func_spi2,
|
||
|
+ func_spi3,
|
||
|
+ func_spi4,
|
||
|
+ func_spi5,
|
||
|
+ func_spi6,
|
||
|
+ func_spi7,
|
||
|
+ func_spi8,
|
||
|
+ func_uart0,
|
||
|
+ func_uart1,
|
||
|
+ func_uart2,
|
||
|
+ func_uart3,
|
||
|
+ func_uart4,
|
||
|
+ func_uart5,
|
||
|
+ func_vbus0,
|
||
|
+ func_vbus1,
|
||
|
+ func_vbus2,
|
||
|
+ func_vbus3,
|
||
|
+ func__,
|
||
|
+ func_count = func__,
|
||
|
+ func_invalid = func__,
|
||
|
+};
|
||
|
+
|
||
|
+struct rp1_pin_funcs {
|
||
|
+ u8 funcs[RP1_FSEL_COUNT];
|
||
|
+};
|
||
|
+
|
||
|
+struct rp1_pinctrl {
|
||
|
+ struct device *dev;
|
||
|
+ void __iomem *gpio_base;
|
||
|
+ void __iomem *rio_base;
|
||
|
+ void __iomem *pads_base;
|
||
|
+ int irq[RP1_NUM_BANKS];
|
||
|
+ struct rp1_pin_info pins[RP1_NUM_GPIOS];
|
||
|
+
|
||
|
+ struct pinctrl_dev *pctl_dev;
|
||
|
+ struct gpio_chip gpio_chip;
|
||
|
+ struct pinctrl_gpio_range gpio_range;
|
||
|
+
|
||
|
+ raw_spinlock_t irq_lock[RP1_NUM_BANKS];
|
||
|
+};
|
||
|
+
|
||
|
+const struct rp1_iobank_desc rp1_iobanks[RP1_NUM_BANKS] = {
|
||
|
+ /* gpio inte ints rio pads */
|
||
|
+ { 0, 28, 0x0000, 0x011c, 0x0124, 0x0000, 0x0004 },
|
||
|
+ { 28, 6, 0x4000, 0x411c, 0x4124, 0x4000, 0x4004 },
|
||
|
+ { 34, 20, 0x8000, 0x811c, 0x8124, 0x8000, 0x8004 },
|
||
|
+};
|
||
|
+
|
||
|
+/* pins are just named GPIO0..GPIO53 */
|
||
|
+#define RP1_GPIO_PIN(a) PINCTRL_PIN(a, "gpio" #a)
|
||
|
+static struct pinctrl_pin_desc rp1_gpio_pins[] = {
|
||
|
+ RP1_GPIO_PIN(0),
|
||
|
+ RP1_GPIO_PIN(1),
|
||
|
+ RP1_GPIO_PIN(2),
|
||
|
+ RP1_GPIO_PIN(3),
|
||
|
+ RP1_GPIO_PIN(4),
|
||
|
+ RP1_GPIO_PIN(5),
|
||
|
+ RP1_GPIO_PIN(6),
|
||
|
+ RP1_GPIO_PIN(7),
|
||
|
+ RP1_GPIO_PIN(8),
|
||
|
+ RP1_GPIO_PIN(9),
|
||
|
+ RP1_GPIO_PIN(10),
|
||
|
+ RP1_GPIO_PIN(11),
|
||
|
+ RP1_GPIO_PIN(12),
|
||
|
+ RP1_GPIO_PIN(13),
|
||
|
+ RP1_GPIO_PIN(14),
|
||
|
+ RP1_GPIO_PIN(15),
|
||
|
+ RP1_GPIO_PIN(16),
|
||
|
+ RP1_GPIO_PIN(17),
|
||
|
+ RP1_GPIO_PIN(18),
|
||
|
+ RP1_GPIO_PIN(19),
|
||
|
+ RP1_GPIO_PIN(20),
|
||
|
+ RP1_GPIO_PIN(21),
|
||
|
+ RP1_GPIO_PIN(22),
|
||
|
+ RP1_GPIO_PIN(23),
|
||
|
+ RP1_GPIO_PIN(24),
|
||
|
+ RP1_GPIO_PIN(25),
|
||
|
+ RP1_GPIO_PIN(26),
|
||
|
+ RP1_GPIO_PIN(27),
|
||
|
+ RP1_GPIO_PIN(28),
|
||
|
+ RP1_GPIO_PIN(29),
|
||
|
+ RP1_GPIO_PIN(30),
|
||
|
+ RP1_GPIO_PIN(31),
|
||
|
+ RP1_GPIO_PIN(32),
|
||
|
+ RP1_GPIO_PIN(33),
|
||
|
+ RP1_GPIO_PIN(34),
|
||
|
+ RP1_GPIO_PIN(35),
|
||
|
+ RP1_GPIO_PIN(36),
|
||
|
+ RP1_GPIO_PIN(37),
|
||
|
+ RP1_GPIO_PIN(38),
|
||
|
+ RP1_GPIO_PIN(39),
|
||
|
+ RP1_GPIO_PIN(40),
|
||
|
+ RP1_GPIO_PIN(41),
|
||
|
+ RP1_GPIO_PIN(42),
|
||
|
+ RP1_GPIO_PIN(43),
|
||
|
+ RP1_GPIO_PIN(44),
|
||
|
+ RP1_GPIO_PIN(45),
|
||
|
+ RP1_GPIO_PIN(46),
|
||
|
+ RP1_GPIO_PIN(47),
|
||
|
+ RP1_GPIO_PIN(48),
|
||
|
+ RP1_GPIO_PIN(49),
|
||
|
+ RP1_GPIO_PIN(50),
|
||
|
+ RP1_GPIO_PIN(51),
|
||
|
+ RP1_GPIO_PIN(52),
|
||
|
+ RP1_GPIO_PIN(53),
|
||
|
+};
|
||
|
+
|
||
|
+/* one pin per group */
|
||
|
+static const char * const rp1_gpio_groups[] = {
|
||
|
+ "gpio0",
|
||
|
+ "gpio1",
|
||
|
+ "gpio2",
|
||
|
+ "gpio3",
|
||
|
+ "gpio4",
|
||
|
+ "gpio5",
|
||
|
+ "gpio6",
|
||
|
+ "gpio7",
|
||
|
+ "gpio8",
|
||
|
+ "gpio9",
|
||
|
+ "gpio10",
|
||
|
+ "gpio11",
|
||
|
+ "gpio12",
|
||
|
+ "gpio13",
|
||
|
+ "gpio14",
|
||
|
+ "gpio15",
|
||
|
+ "gpio16",
|
||
|
+ "gpio17",
|
||
|
+ "gpio18",
|
||
|
+ "gpio19",
|
||
|
+ "gpio20",
|
||
|
+ "gpio21",
|
||
|
+ "gpio22",
|
||
|
+ "gpio23",
|
||
|
+ "gpio24",
|
||
|
+ "gpio25",
|
||
|
+ "gpio26",
|
||
|
+ "gpio27",
|
||
|
+ "gpio28",
|
||
|
+ "gpio29",
|
||
|
+ "gpio30",
|
||
|
+ "gpio31",
|
||
|
+ "gpio32",
|
||
|
+ "gpio33",
|
||
|
+ "gpio34",
|
||
|
+ "gpio35",
|
||
|
+ "gpio36",
|
||
|
+ "gpio37",
|
||
|
+ "gpio38",
|
||
|
+ "gpio39",
|
||
|
+ "gpio40",
|
||
|
+ "gpio41",
|
||
|
+ "gpio42",
|
||
|
+ "gpio43",
|
||
|
+ "gpio44",
|
||
|
+ "gpio45",
|
||
|
+ "gpio46",
|
||
|
+ "gpio47",
|
||
|
+ "gpio48",
|
||
|
+ "gpio49",
|
||
|
+ "gpio50",
|
||
|
+ "gpio51",
|
||
|
+ "gpio52",
|
||
|
+ "gpio53",
|
||
|
+};
|
||
|
+
|
||
|
+static const char * const rp1_func_names[] = {
|
||
|
+ FUNC(alt0),
|
||
|
+ FUNC(alt1),
|
||
|
+ FUNC(alt2),
|
||
|
+ FUNC(alt3),
|
||
|
+ FUNC(alt4),
|
||
|
+ FUNC(gpio),
|
||
|
+ FUNC(alt6),
|
||
|
+ FUNC(alt7),
|
||
|
+ FUNC(alt8),
|
||
|
+ FUNC(none),
|
||
|
+ FUNC(aaud),
|
||
|
+ FUNC(dcd0),
|
||
|
+ FUNC(dpi),
|
||
|
+ FUNC(dsi0_te_ext),
|
||
|
+ FUNC(dsi1_te_ext),
|
||
|
+ FUNC(dsr0),
|
||
|
+ FUNC(dtr0),
|
||
|
+ FUNC(gpclk0),
|
||
|
+ FUNC(gpclk1),
|
||
|
+ FUNC(gpclk2),
|
||
|
+ FUNC(gpclk3),
|
||
|
+ FUNC(gpclk4),
|
||
|
+ FUNC(gpclk5),
|
||
|
+ FUNC(i2c0),
|
||
|
+ FUNC(i2c1),
|
||
|
+ FUNC(i2c2),
|
||
|
+ FUNC(i2c3),
|
||
|
+ FUNC(i2c4),
|
||
|
+ FUNC(i2c5),
|
||
|
+ FUNC(i2c6),
|
||
|
+ FUNC(i2s0),
|
||
|
+ FUNC(i2s1),
|
||
|
+ FUNC(i2s2),
|
||
|
+ FUNC(ir),
|
||
|
+ FUNC(mic),
|
||
|
+ FUNC(pcie_clkreq_n),
|
||
|
+ FUNC(pio),
|
||
|
+ FUNC(proc_rio),
|
||
|
+ FUNC(pwm0),
|
||
|
+ FUNC(pwm1),
|
||
|
+ FUNC(ri0),
|
||
|
+ FUNC(sd0),
|
||
|
+ FUNC(sd1),
|
||
|
+ FUNC(spi0),
|
||
|
+ FUNC(spi1),
|
||
|
+ FUNC(spi2),
|
||
|
+ FUNC(spi3),
|
||
|
+ FUNC(spi4),
|
||
|
+ FUNC(spi5),
|
||
|
+ FUNC(spi6),
|
||
|
+ FUNC(spi7),
|
||
|
+ FUNC(spi8),
|
||
|
+ FUNC(uart0),
|
||
|
+ FUNC(uart1),
|
||
|
+ FUNC(uart2),
|
||
|
+ FUNC(uart3),
|
||
|
+ FUNC(uart4),
|
||
|
+ FUNC(uart5),
|
||
|
+ FUNC(vbus0),
|
||
|
+ FUNC(vbus1),
|
||
|
+ FUNC(vbus2),
|
||
|
+ FUNC(vbus3),
|
||
|
+ [func_invalid] = "?"
|
||
|
+};
|
||
|
+
|
||
|
+static const struct rp1_pin_funcs rp1_gpio_pin_funcs[] = {
|
||
|
+ PIN(0, spi0, dpi, uart1, i2c0, _, gpio, proc_rio, pio, spi2),
|
||
|
+ PIN(1, spi0, dpi, uart1, i2c0, _, gpio, proc_rio, pio, spi2),
|
||
|
+ PIN(2, spi0, dpi, uart1, i2c1, ir, gpio, proc_rio, pio, spi2),
|
||
|
+ PIN(3, spi0, dpi, uart1, i2c1, ir, gpio, proc_rio, pio, spi2),
|
||
|
+ PIN(4, gpclk0, dpi, uart2, i2c2, ri0, gpio, proc_rio, pio, spi3),
|
||
|
+ PIN(5, gpclk1, dpi, uart2, i2c2, dtr0, gpio, proc_rio, pio, spi3),
|
||
|
+ PIN(6, gpclk2, dpi, uart2, i2c3, dcd0, gpio, proc_rio, pio, spi3),
|
||
|
+ PIN(7, spi0, dpi, uart2, i2c3, dsr0, gpio, proc_rio, pio, spi3),
|
||
|
+ PIN(8, spi0, dpi, uart3, i2c0, _, gpio, proc_rio, pio, spi4),
|
||
|
+ PIN(9, spi0, dpi, uart3, i2c0, _, gpio, proc_rio, pio, spi4),
|
||
|
+ PIN(10, spi0, dpi, uart3, i2c1, _, gpio, proc_rio, pio, spi4),
|
||
|
+ PIN(11, spi0, dpi, uart3, i2c1, _, gpio, proc_rio, pio, spi4),
|
||
|
+ PIN(12, pwm0, dpi, uart4, i2c2, aaud, gpio, proc_rio, pio, spi5),
|
||
|
+ PIN(13, pwm0, dpi, uart4, i2c2, aaud, gpio, proc_rio, pio, spi5),
|
||
|
+ PIN(14, pwm0, dpi, uart4, i2c3, uart0, gpio, proc_rio, pio, spi5),
|
||
|
+ PIN(15, pwm0, dpi, uart4, i2c3, uart0, gpio, proc_rio, pio, spi5),
|
||
|
+ PIN(16, spi1, dpi, dsi0_te_ext, _, uart0, gpio, proc_rio, pio, _),
|
||
|
+ PIN(17, spi1, dpi, dsi1_te_ext, _, uart0, gpio, proc_rio, pio, _),
|
||
|
+ PIN(18, spi1, dpi, i2s0, pwm0, i2s1, gpio, proc_rio, pio, gpclk1),
|
||
|
+ PIN(19, spi1, dpi, i2s0, pwm0, i2s1, gpio, proc_rio, pio, _),
|
||
|
+ PIN(20, spi1, dpi, i2s0, gpclk0, i2s1, gpio, proc_rio, pio, _),
|
||
|
+ PIN(21, spi1, dpi, i2s0, gpclk1, i2s1, gpio, proc_rio, pio, _),
|
||
|
+ PIN(22, sd0, dpi, i2s0, i2c3, i2s1, gpio, proc_rio, pio, _),
|
||
|
+ PIN(23, sd0, dpi, i2s0, i2c3, i2s1, gpio, proc_rio, pio, _),
|
||
|
+ PIN(24, sd0, dpi, i2s0, _, i2s1, gpio, proc_rio, pio, spi2),
|
||
|
+ PIN(25, sd0, dpi, i2s0, mic, i2s1, gpio, proc_rio, pio, spi3),
|
||
|
+ PIN(26, sd0, dpi, i2s0, mic, i2s1, gpio, proc_rio, pio, spi5),
|
||
|
+ PIN(27, sd0, dpi, i2s0, mic, i2s1, gpio, proc_rio, pio, spi1),
|
||
|
+ PIN(28, sd1, i2c4, i2s2, spi6, vbus0, gpio, proc_rio, _, _),
|
||
|
+ PIN(29, sd1, i2c4, i2s2, spi6, vbus0, gpio, proc_rio, _, _),
|
||
|
+ PIN(30, sd1, i2c5, i2s2, spi6, uart5, gpio, proc_rio, _, _),
|
||
|
+ PIN(31, sd1, i2c5, i2s2, spi6, uart5, gpio, proc_rio, _, _),
|
||
|
+ PIN(32, sd1, gpclk3, i2s2, spi6, uart5, gpio, proc_rio, _, _),
|
||
|
+ PIN(33, sd1, gpclk4, i2s2, spi6, uart5, gpio, proc_rio, _, _),
|
||
|
+ PIN(34, pwm1, gpclk3, vbus0, i2c4, mic, gpio, proc_rio, _, _),
|
||
|
+ PIN(35, spi8, pwm1, vbus0, i2c4, mic, gpio, proc_rio, _, _),
|
||
|
+ PIN(36, spi8, uart5, pcie_clkreq_n, i2c5, mic, gpio, proc_rio, _, _),
|
||
|
+ PIN(37, spi8, uart5, mic, i2c5, pcie_clkreq_n, gpio, proc_rio, _, _),
|
||
|
+ PIN(38, spi8, uart5, mic, i2c6, aaud, gpio, proc_rio, dsi0_te_ext, _),
|
||
|
+ PIN(39, spi8, uart5, mic, i2c6, aaud, gpio, proc_rio, dsi1_te_ext, _),
|
||
|
+ PIN(40, pwm1, uart5, i2c4, spi6, aaud, gpio, proc_rio, _, _),
|
||
|
+ PIN(41, pwm1, uart5, i2c4, spi6, aaud, gpio, proc_rio, _, _),
|
||
|
+ PIN(42, gpclk5, uart5, vbus1, spi6, i2s2, gpio, proc_rio, _, _),
|
||
|
+ PIN(43, gpclk4, uart5, vbus1, spi6, i2s2, gpio, proc_rio, _, _),
|
||
|
+ PIN(44, gpclk5, i2c5, pwm1, spi6, i2s2, gpio, proc_rio, _, _),
|
||
|
+ PIN(45, pwm1, i2c5, spi7, spi6, i2s2, gpio, proc_rio, _, _),
|
||
|
+ PIN(46, gpclk3, i2c4, spi7, mic, i2s2, gpio, proc_rio, dsi0_te_ext, _),
|
||
|
+ PIN(47, gpclk5, i2c4, spi7, mic, i2s2, gpio, proc_rio, dsi1_te_ext, _),
|
||
|
+ PIN(48, pwm1, pcie_clkreq_n, spi7, mic, uart5, gpio, proc_rio, _, _),
|
||
|
+ PIN(49, spi8, spi7, i2c5, aaud, uart5, gpio, proc_rio, _, _),
|
||
|
+ PIN(50, spi8, spi7, i2c5, aaud, vbus2, gpio, proc_rio, _, _),
|
||
|
+ PIN(51, spi8, spi7, i2c6, aaud, vbus2, gpio, proc_rio, _, _),
|
||
|
+ PIN(52, spi8, _, i2c6, aaud, vbus3, gpio, proc_rio, _, _),
|
||
|
+ PIN(53, spi8, spi7, _, pcie_clkreq_n, vbus3, gpio, proc_rio, _, _),
|
||
|
+};
|
||
|
+
|
||
|
+static const u8 legacy_fsel_map[][8] = {
|
||
|
+ LEGACY_MAP(0, i2c0, _, dpi, spi2, uart1, _),
|
||
|
+ LEGACY_MAP(1, i2c0, _, dpi, spi2, uart1, _),
|
||
|
+ LEGACY_MAP(2, i2c1, _, dpi, spi2, uart1, _),
|
||
|
+ LEGACY_MAP(3, i2c1, _, dpi, spi2, uart1, _),
|
||
|
+ LEGACY_MAP(4, gpclk0, _, dpi, spi3, uart2, i2c2),
|
||
|
+ LEGACY_MAP(5, gpclk1, _, dpi, spi3, uart2, i2c2),
|
||
|
+ LEGACY_MAP(6, gpclk2, _, dpi, spi3, uart2, i2c3),
|
||
|
+ LEGACY_MAP(7, spi0, _, dpi, spi3, uart2, i2c3),
|
||
|
+ LEGACY_MAP(8, spi0, _, dpi, _, uart3, i2c0),
|
||
|
+ LEGACY_MAP(9, spi0, _, dpi, _, uart3, i2c0),
|
||
|
+ LEGACY_MAP(10, spi0, _, dpi, _, uart3, i2c1),
|
||
|
+ LEGACY_MAP(11, spi0, _, dpi, _, uart3, i2c1),
|
||
|
+ LEGACY_MAP(12, pwm0, _, dpi, spi5, uart4, i2c2),
|
||
|
+ LEGACY_MAP(13, pwm0, _, dpi, spi5, uart4, i2c2),
|
||
|
+ LEGACY_MAP(14, uart0, _, dpi, spi5, uart4, _),
|
||
|
+ LEGACY_MAP(15, uart0, _, dpi, spi5, uart4, _),
|
||
|
+ LEGACY_MAP(16, _, _, dpi, uart0, spi1, _),
|
||
|
+ LEGACY_MAP(17, _, _, dpi, uart0, spi1, _),
|
||
|
+ LEGACY_MAP(18, i2s0, _, dpi, _, spi1, pwm0),
|
||
|
+ LEGACY_MAP(19, i2s0, _, dpi, _, spi1, pwm0),
|
||
|
+ LEGACY_MAP(20, i2s0, _, dpi, _, spi1, gpclk0),
|
||
|
+ LEGACY_MAP(21, i2s0, _, dpi, _, spi1, gpclk1),
|
||
|
+ LEGACY_MAP(22, sd0, _, dpi, _, _, i2c3),
|
||
|
+ LEGACY_MAP(23, sd0, _, dpi, _, _, i2c3),
|
||
|
+ LEGACY_MAP(24, sd0, _, dpi, _, _, spi2),
|
||
|
+ LEGACY_MAP(25, sd0, _, dpi, _, _, spi3),
|
||
|
+ LEGACY_MAP(26, sd0, _, dpi, _, _, spi5),
|
||
|
+ LEGACY_MAP(27, sd0, _, dpi, _, _, _),
|
||
|
+};
|
||
|
+
|
||
|
+static const char * const irq_type_names[] = {
|
||
|
+ [IRQ_TYPE_NONE] = "none",
|
||
|
+ [IRQ_TYPE_EDGE_RISING] = "edge-rising",
|
||
|
+ [IRQ_TYPE_EDGE_FALLING] = "edge-falling",
|
||
|
+ [IRQ_TYPE_EDGE_BOTH] = "edge-both",
|
||
|
+ [IRQ_TYPE_LEVEL_HIGH] = "level-high",
|
||
|
+ [IRQ_TYPE_LEVEL_LOW] = "level-low",
|
||
|
+};
|
||
|
+
|
||
|
+static int rp1_pinconf_set(struct pinctrl_dev *pctldev,
|
||
|
+ unsigned int offset, unsigned long *configs,
|
||
|
+ unsigned int num_configs);
|
||
|
+
|
||
|
+static struct rp1_pin_info *rp1_get_pin(struct gpio_chip *chip,
|
||
|
+ unsigned int offset)
|
||
|
+{
|
||
|
+ struct rp1_pinctrl *pc = gpiochip_get_data(chip);
|
||
|
+
|
||
|
+ if (pc && offset < RP1_NUM_GPIOS)
|
||
|
+ return &pc->pins[offset];
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
+static struct rp1_pin_info *rp1_get_pin_pctl(struct pinctrl_dev *pctldev,
|
||
|
+ unsigned int offset)
|
||
|
+{
|
||
|
+ struct rp1_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
|
||
|
+
|
||
|
+ if (pc && offset < RP1_NUM_GPIOS)
|
||
|
+ return &pc->pins[offset];
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
+static void rp1_pad_update(struct rp1_pin_info *pin, u32 clr, u32 set)
|
||
|
+{
|
||
|
+ u32 padctrl = readl(pin->pad);
|
||
|
+
|
||
|
+ padctrl &= ~clr;
|
||
|
+ padctrl |= set;
|
||
|
+
|
||
|
+ writel(padctrl, pin->pad);
|
||
|
+}
|
||
|
+
|
||
|
+static void rp1_input_enable(struct rp1_pin_info *pin, int value)
|
||
|
+{
|
||
|
+ rp1_pad_update(pin, RP1_PAD_IN_ENABLE_MASK,
|
||
|
+ value ? RP1_PAD_IN_ENABLE_MASK : 0);
|
||
|
+}
|
||
|
+
|
||
|
+static void rp1_output_enable(struct rp1_pin_info *pin, int value)
|
||
|
+{
|
||
|
+ rp1_pad_update(pin, RP1_PAD_OUT_DISABLE_MASK,
|
||
|
+ value ? 0 : RP1_PAD_OUT_DISABLE_MASK);
|
||
|
+}
|
||
|
+
|
||
|
+static u32 rp1_get_fsel(struct rp1_pin_info *pin)
|
||
|
+{
|
||
|
+ u32 ctrl = readl(pin->gpio + RP1_GPIO_CTRL);
|
||
|
+ u32 oeover = FLD_GET(ctrl, RP1_GPIO_CTRL_OEOVER);
|
||
|
+ u32 fsel = FLD_GET(ctrl, RP1_GPIO_CTRL_FUNCSEL);
|
||
|
+
|
||
|
+ if (oeover != RP1_OEOVER_PERI || fsel >= RP1_FSEL_COUNT)
|
||
|
+ fsel = RP1_FSEL_NONE;
|
||
|
+
|
||
|
+ return fsel;
|
||
|
+}
|
||
|
+
|
||
|
+static void rp1_set_fsel(struct rp1_pin_info *pin, u32 fsel)
|
||
|
+{
|
||
|
+ u32 ctrl = readl(pin->gpio + RP1_GPIO_CTRL);
|
||
|
+
|
||
|
+ if (fsel >= RP1_FSEL_COUNT)
|
||
|
+ fsel = RP1_FSEL_NONE_HW;
|
||
|
+
|
||
|
+ rp1_input_enable(pin, 1);
|
||
|
+ rp1_output_enable(pin, 1);
|
||
|
+
|
||
|
+ if (fsel == RP1_FSEL_NONE) {
|
||
|
+ FLD_SET(ctrl, RP1_GPIO_CTRL_OEOVER, RP1_OEOVER_DISABLE);
|
||
|
+ } else {
|
||
|
+ FLD_SET(ctrl, RP1_GPIO_CTRL_OUTOVER, RP1_OUTOVER_PERI);
|
||
|
+ FLD_SET(ctrl, RP1_GPIO_CTRL_OEOVER, RP1_OEOVER_PERI);
|
||
|
+ }
|
||
|
+ FLD_SET(ctrl, RP1_GPIO_CTRL_FUNCSEL, fsel);
|
||
|
+ writel(ctrl, pin->gpio + RP1_GPIO_CTRL);
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_get_dir(struct rp1_pin_info *pin)
|
||
|
+{
|
||
|
+ return !(readl(pin->rio + RP1_RIO_OE) & (1 << pin->offset)) ?
|
||
|
+ RP1_DIR_INPUT : RP1_DIR_OUTPUT;
|
||
|
+}
|
||
|
+
|
||
|
+static void rp1_set_dir(struct rp1_pin_info *pin, bool is_input)
|
||
|
+{
|
||
|
+ int offset = is_input ? RP1_CLR_OFFSET : RP1_SET_OFFSET;
|
||
|
+
|
||
|
+ writel(1 << pin->offset, pin->rio + RP1_RIO_OE + offset);
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_get_value(struct rp1_pin_info *pin)
|
||
|
+{
|
||
|
+ return !!(readl(pin->rio + RP1_RIO_IN) & (1 << pin->offset));
|
||
|
+}
|
||
|
+
|
||
|
+static void rp1_set_value(struct rp1_pin_info *pin, int value)
|
||
|
+{
|
||
|
+ /* Assume the pin is already an output */
|
||
|
+ writel(1 << pin->offset,
|
||
|
+ pin->rio + RP1_RIO_OUT + (value ? RP1_SET_OFFSET : RP1_CLR_OFFSET));
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||
|
+{
|
||
|
+ struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ if (!pin)
|
||
|
+ return -EINVAL;
|
||
|
+ ret = rp1_get_value(pin);
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+static void rp1_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||
|
+{
|
||
|
+ struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
|
||
|
+
|
||
|
+ if (pin)
|
||
|
+ rp1_set_value(pin, value);
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
|
||
|
+{
|
||
|
+ struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
|
||
|
+ u32 fsel;
|
||
|
+
|
||
|
+ if (!pin)
|
||
|
+ return -EINVAL;
|
||
|
+ fsel = rp1_get_fsel(pin);
|
||
|
+ if (fsel != RP1_FSEL_GPIO)
|
||
|
+ return -EINVAL;
|
||
|
+ return (rp1_get_dir(pin) == RP1_DIR_OUTPUT) ?
|
||
|
+ GPIO_LINE_DIRECTION_OUT :
|
||
|
+ GPIO_LINE_DIRECTION_IN;
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
|
||
|
+{
|
||
|
+ struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
|
||
|
+
|
||
|
+ if (!pin)
|
||
|
+ return -EINVAL;
|
||
|
+ rp1_set_dir(pin, RP1_DIR_INPUT);
|
||
|
+ rp1_set_fsel(pin, RP1_FSEL_GPIO);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
|
||
|
+ int value)
|
||
|
+{
|
||
|
+ struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
|
||
|
+
|
||
|
+ if (!pin)
|
||
|
+ return -EINVAL;
|
||
|
+ rp1_set_value(pin, value);
|
||
|
+ rp1_set_dir(pin, RP1_DIR_OUTPUT);
|
||
|
+ rp1_set_fsel(pin, RP1_FSEL_GPIO);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_gpio_set_config(struct gpio_chip *gc, unsigned offset,
|
||
|
+ unsigned long config)
|
||
|
+{
|
||
|
+ struct rp1_pinctrl *pc = gpiochip_get_data(gc);
|
||
|
+ unsigned long configs[] = { config };
|
||
|
+
|
||
|
+ return rp1_pinconf_set(pc->pctl_dev, offset, configs,
|
||
|
+ ARRAY_SIZE(configs));
|
||
|
+}
|
||
|
+
|
||
|
+static const struct gpio_chip rp1_gpio_chip = {
|
||
|
+ .label = MODULE_NAME,
|
||
|
+ .owner = THIS_MODULE,
|
||
|
+ .request = gpiochip_generic_request,
|
||
|
+ .free = gpiochip_generic_free,
|
||
|
+ .direction_input = rp1_gpio_direction_input,
|
||
|
+ .direction_output = rp1_gpio_direction_output,
|
||
|
+ .get_direction = rp1_gpio_get_direction,
|
||
|
+ .get = rp1_gpio_get,
|
||
|
+ .set = rp1_gpio_set,
|
||
|
+ .base = -1,
|
||
|
+ .set_config = rp1_gpio_set_config,
|
||
|
+ .ngpio = RP1_NUM_GPIOS,
|
||
|
+ .can_sleep = false,
|
||
|
+};
|
||
|
+
|
||
|
+static void rp1_gpio_irq_handler(struct irq_desc *desc)
|
||
|
+{
|
||
|
+ struct gpio_chip *chip = irq_desc_get_handler_data(desc);
|
||
|
+ struct rp1_pinctrl *pc = gpiochip_get_data(chip);
|
||
|
+ struct irq_chip *host_chip = irq_desc_get_chip(desc);
|
||
|
+ const struct rp1_iobank_desc *bank;
|
||
|
+ int irq = irq_desc_get_irq(desc);
|
||
|
+ unsigned long ints;
|
||
|
+ int b;
|
||
|
+
|
||
|
+ if (pc->irq[0] == irq)
|
||
|
+ bank = &rp1_iobanks[0];
|
||
|
+ else if (pc->irq[1] == irq)
|
||
|
+ bank = &rp1_iobanks[1];
|
||
|
+ else
|
||
|
+ bank = &rp1_iobanks[2];
|
||
|
+
|
||
|
+ chained_irq_enter(host_chip, desc);
|
||
|
+
|
||
|
+ ints = readl(pc->gpio_base + bank->ints_offset);
|
||
|
+ for_each_set_bit(b, &ints, 32) {
|
||
|
+ struct rp1_pin_info *pin = rp1_get_pin(chip, b);
|
||
|
+
|
||
|
+ writel(RP1_GPIO_CTRL_IRQRESET,
|
||
|
+ pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL);
|
||
|
+ generic_handle_irq(irq_linear_revmap(pc->gpio_chip.irq.domain,
|
||
|
+ bank->gpio_offset + b));
|
||
|
+ }
|
||
|
+
|
||
|
+ chained_irq_exit(host_chip, desc);
|
||
|
+}
|
||
|
+
|
||
|
+static void rp1_gpio_irq_config(struct rp1_pin_info *pin, bool enable)
|
||
|
+{
|
||
|
+ writel(1 << pin->offset,
|
||
|
+ pin->inte + (enable ? RP1_SET_OFFSET : RP1_CLR_OFFSET));
|
||
|
+ if (!enable)
|
||
|
+ /* Clear any latched events */
|
||
|
+ writel(RP1_GPIO_CTRL_IRQRESET,
|
||
|
+ pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL);
|
||
|
+}
|
||
|
+
|
||
|
+static void rp1_gpio_irq_enable(struct irq_data *data)
|
||
|
+{
|
||
|
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||
|
+ unsigned gpio = irqd_to_hwirq(data);
|
||
|
+ struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
|
||
|
+
|
||
|
+ rp1_gpio_irq_config(pin, true);
|
||
|
+}
|
||
|
+
|
||
|
+static void rp1_gpio_irq_disable(struct irq_data *data)
|
||
|
+{
|
||
|
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||
|
+ unsigned gpio = irqd_to_hwirq(data);
|
||
|
+ struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
|
||
|
+
|
||
|
+ rp1_gpio_irq_config(pin, false);
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_irq_set_type(struct rp1_pin_info *pin, unsigned int type)
|
||
|
+{
|
||
|
+ u32 irq_flags;
|
||
|
+
|
||
|
+ switch (type) {
|
||
|
+ case IRQ_TYPE_NONE:
|
||
|
+ irq_flags = 0;
|
||
|
+ break;
|
||
|
+ case IRQ_TYPE_EDGE_RISING:
|
||
|
+ irq_flags = RP1_INT_EDGE_RISING;
|
||
|
+ break;
|
||
|
+ case IRQ_TYPE_EDGE_FALLING:
|
||
|
+ irq_flags = RP1_INT_EDGE_FALLING;
|
||
|
+ break;
|
||
|
+ case IRQ_TYPE_EDGE_BOTH:
|
||
|
+ irq_flags = RP1_INT_EDGE_RISING | RP1_INT_EDGE_FALLING;
|
||
|
+ break;
|
||
|
+ case IRQ_TYPE_LEVEL_HIGH:
|
||
|
+ irq_flags = RP1_INT_LEVEL_HIGH;
|
||
|
+ break;
|
||
|
+ case IRQ_TYPE_LEVEL_LOW:
|
||
|
+ irq_flags = RP1_INT_LEVEL_LOW;
|
||
|
+ break;
|
||
|
+
|
||
|
+ default:
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Clear them all */
|
||
|
+ writel(RP1_INT_MASK << RP1_GPIO_EVENTS_SHIFT_RAW,
|
||
|
+ pin->gpio + RP1_CLR_OFFSET + RP1_GPIO_CTRL);
|
||
|
+ /* Set those that are needed */
|
||
|
+ writel(irq_flags << RP1_GPIO_EVENTS_SHIFT_RAW,
|
||
|
+ pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL);
|
||
|
+ pin->irq_type = type;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_gpio_irq_set_type(struct irq_data *data, unsigned int type)
|
||
|
+{
|
||
|
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||
|
+ struct rp1_pinctrl *pc = gpiochip_get_data(chip);
|
||
|
+ unsigned gpio = irqd_to_hwirq(data);
|
||
|
+ struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
|
||
|
+ int bank = pin->bank;
|
||
|
+ unsigned long flags;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ raw_spin_lock_irqsave(&pc->irq_lock[bank], flags);
|
||
|
+
|
||
|
+ ret = rp1_irq_set_type(pin, type);
|
||
|
+ if (!ret) {
|
||
|
+ if (type & IRQ_TYPE_EDGE_BOTH)
|
||
|
+ irq_set_handler_locked(data, handle_edge_irq);
|
||
|
+ else
|
||
|
+ irq_set_handler_locked(data, handle_level_irq);
|
||
|
+ }
|
||
|
+
|
||
|
+ raw_spin_unlock_irqrestore(&pc->irq_lock[bank], flags);
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+static void rp1_gpio_irq_ack(struct irq_data *data)
|
||
|
+{
|
||
|
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||
|
+ unsigned gpio = irqd_to_hwirq(data);
|
||
|
+ struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
|
||
|
+
|
||
|
+ /* Clear any latched events */
|
||
|
+ writel(RP1_GPIO_CTRL_IRQRESET, pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL);
|
||
|
+}
|
||
|
+
|
||
|
+static struct irq_chip rp1_gpio_irq_chip = {
|
||
|
+ .name = MODULE_NAME,
|
||
|
+ .irq_enable = rp1_gpio_irq_enable,
|
||
|
+ .irq_disable = rp1_gpio_irq_disable,
|
||
|
+ .irq_set_type = rp1_gpio_irq_set_type,
|
||
|
+ .irq_ack = rp1_gpio_irq_ack,
|
||
|
+ .irq_mask = rp1_gpio_irq_disable,
|
||
|
+ .irq_unmask = rp1_gpio_irq_enable,
|
||
|
+ .flags = IRQCHIP_IMMUTABLE,
|
||
|
+};
|
||
|
+
|
||
|
+static int rp1_pctl_get_groups_count(struct pinctrl_dev *pctldev)
|
||
|
+{
|
||
|
+ return ARRAY_SIZE(rp1_gpio_groups);
|
||
|
+}
|
||
|
+
|
||
|
+static const char *rp1_pctl_get_group_name(struct pinctrl_dev *pctldev,
|
||
|
+ unsigned selector)
|
||
|
+{
|
||
|
+ return rp1_gpio_groups[selector];
|
||
|
+}
|
||
|
+
|
||
|
+static enum funcs rp1_get_fsel_func(unsigned pin, unsigned fsel)
|
||
|
+{
|
||
|
+ if (pin < RP1_NUM_GPIOS) {
|
||
|
+ if (fsel < RP1_FSEL_COUNT)
|
||
|
+ return rp1_gpio_pin_funcs[pin].funcs[fsel];
|
||
|
+ else if (fsel == RP1_FSEL_NONE)
|
||
|
+ return func_none;
|
||
|
+ }
|
||
|
+ return func_invalid;
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_pctl_get_group_pins(struct pinctrl_dev *pctldev,
|
||
|
+ unsigned selector,
|
||
|
+ const unsigned **pins,
|
||
|
+ unsigned *num_pins)
|
||
|
+{
|
||
|
+ *pins = &rp1_gpio_pins[selector].number;
|
||
|
+ *num_pins = 1;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void rp1_pctl_pin_dbg_show(struct pinctrl_dev *pctldev,
|
||
|
+ struct seq_file *s,
|
||
|
+ unsigned offset)
|
||
|
+{
|
||
|
+ struct rp1_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
|
||
|
+ struct gpio_chip *chip = &pc->gpio_chip;
|
||
|
+ struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset);
|
||
|
+ u32 fsel = rp1_get_fsel(pin);
|
||
|
+ enum funcs func = rp1_get_fsel_func(offset, fsel);
|
||
|
+ int value = rp1_get_value(pin);
|
||
|
+ int irq = irq_find_mapping(chip->irq.domain, offset);
|
||
|
+
|
||
|
+ seq_printf(s, "function %s (%s) in %s; irq %d (%s)",
|
||
|
+ rp1_func_names[fsel], rp1_func_names[func],
|
||
|
+ value ? "hi" : "lo",
|
||
|
+ irq, irq_type_names[pin->irq_type]);
|
||
|
+}
|
||
|
+
|
||
|
+static void rp1_pctl_dt_free_map(struct pinctrl_dev *pctldev,
|
||
|
+ struct pinctrl_map *maps, unsigned num_maps)
|
||
|
+{
|
||
|
+ int i;
|
||
|
+
|
||
|
+ for (i = 0; i < num_maps; i++)
|
||
|
+ if (maps[i].type == PIN_MAP_TYPE_CONFIGS_PIN)
|
||
|
+ kfree(maps[i].data.configs.configs);
|
||
|
+
|
||
|
+ kfree(maps);
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_pctl_legacy_map_func(struct rp1_pinctrl *pc,
|
||
|
+ struct device_node *np, u32 pin, u32 fnum,
|
||
|
+ struct pinctrl_map *maps,
|
||
|
+ unsigned int *num_maps)
|
||
|
+{
|
||
|
+ struct pinctrl_map *map = &maps[*num_maps];
|
||
|
+ enum funcs func;
|
||
|
+
|
||
|
+ if (fnum >= ARRAY_SIZE(legacy_fsel_map[0])) {
|
||
|
+ dev_err(pc->dev, "%pOF: invalid brcm,function %d\n", np, fnum);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ func = legacy_fsel_map[pin][fnum];
|
||
|
+ if (func == func_invalid) {
|
||
|
+ dev_err(pc->dev, "%pOF: brcm,function %d not supported on pin %d\n",
|
||
|
+ np, fnum, pin);
|
||
|
+ }
|
||
|
+
|
||
|
+ map->type = PIN_MAP_TYPE_MUX_GROUP;
|
||
|
+ map->data.mux.group = rp1_gpio_groups[pin];
|
||
|
+ map->data.mux.function = rp1_func_names[func];
|
||
|
+ (*num_maps)++;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_pctl_legacy_map_pull(struct rp1_pinctrl *pc,
|
||
|
+ struct device_node *np, u32 pin, u32 pull,
|
||
|
+ struct pinctrl_map *maps,
|
||
|
+ unsigned int *num_maps)
|
||
|
+{
|
||
|
+ struct pinctrl_map *map = &maps[*num_maps];
|
||
|
+ enum pin_config_param param;
|
||
|
+ unsigned long *configs;
|
||
|
+
|
||
|
+ switch (pull) {
|
||
|
+ case RP1_PUD_OFF:
|
||
|
+ param = PIN_CONFIG_BIAS_DISABLE;
|
||
|
+ break;
|
||
|
+ case RP1_PUD_DOWN:
|
||
|
+ param = PIN_CONFIG_BIAS_PULL_DOWN;
|
||
|
+ break;
|
||
|
+ case RP1_PUD_UP:
|
||
|
+ param = PIN_CONFIG_BIAS_PULL_UP;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ dev_err(pc->dev, "%pOF: invalid brcm,pull %d\n", np, pull);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ configs = kzalloc(sizeof(*configs), GFP_KERNEL);
|
||
|
+ if (!configs)
|
||
|
+ return -ENOMEM;
|
||
|
+
|
||
|
+ configs[0] = pinconf_to_config_packed(param, 0);
|
||
|
+ map->type = PIN_MAP_TYPE_CONFIGS_PIN;
|
||
|
+ map->data.configs.group_or_pin = rp1_gpio_pins[pin].name;
|
||
|
+ map->data.configs.configs = configs;
|
||
|
+ map->data.configs.num_configs = 1;
|
||
|
+ (*num_maps)++;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_pctl_dt_node_to_map(struct pinctrl_dev *pctldev,
|
||
|
+ struct device_node *np,
|
||
|
+ struct pinctrl_map **map,
|
||
|
+ unsigned int *num_maps)
|
||
|
+{
|
||
|
+ struct rp1_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
|
||
|
+ struct property *pins, *funcs, *pulls;
|
||
|
+ int num_pins, num_funcs, num_pulls, maps_per_pin;
|
||
|
+ struct pinctrl_map *maps;
|
||
|
+ unsigned long *configs = NULL;
|
||
|
+ const char *function = NULL;
|
||
|
+ unsigned int reserved_maps;
|
||
|
+ int num_configs = 0;
|
||
|
+ int i, err;
|
||
|
+ u32 pin, func, pull;
|
||
|
+
|
||
|
+ /* Check for legacy pin declaration */
|
||
|
+ pins = of_find_property(np, "brcm,pins", NULL);
|
||
|
+
|
||
|
+ if (!pins) /* Assume generic bindings in this node */
|
||
|
+ return pinconf_generic_dt_node_to_map_all(pctldev, np, map, num_maps);
|
||
|
+
|
||
|
+ funcs = of_find_property(np, "brcm,function", NULL);
|
||
|
+ if (!funcs)
|
||
|
+ of_property_read_string(np, "function", &function);
|
||
|
+
|
||
|
+ pulls = of_find_property(np, "brcm,pull", NULL);
|
||
|
+ if (!pulls)
|
||
|
+ pinconf_generic_parse_dt_config(np, pctldev, &configs, &num_configs);
|
||
|
+
|
||
|
+ if (!function && !funcs && !num_configs && !pulls) {
|
||
|
+ dev_err(pc->dev,
|
||
|
+ "%pOF: no function, brcm,function, brcm,pull, etc.\n",
|
||
|
+ np);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ num_pins = pins->length / 4;
|
||
|
+ num_funcs = funcs ? (funcs->length / 4) : 0;
|
||
|
+ num_pulls = pulls ? (pulls->length / 4) : 0;
|
||
|
+
|
||
|
+ if (num_funcs > 1 && num_funcs != num_pins) {
|
||
|
+ dev_err(pc->dev,
|
||
|
+ "%pOF: brcm,function must have 1 or %d entries\n",
|
||
|
+ np, num_pins);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (num_pulls > 1 && num_pulls != num_pins) {
|
||
|
+ dev_err(pc->dev,
|
||
|
+ "%pOF: brcm,pull must have 1 or %d entries\n",
|
||
|
+ np, num_pins);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ maps_per_pin = 0;
|
||
|
+ if (function || num_funcs)
|
||
|
+ maps_per_pin++;
|
||
|
+ if (num_configs || num_pulls)
|
||
|
+ maps_per_pin++;
|
||
|
+ reserved_maps = num_pins * maps_per_pin;
|
||
|
+ maps = kcalloc(reserved_maps, sizeof(*maps), GFP_KERNEL);
|
||
|
+ if (!maps)
|
||
|
+ return -ENOMEM;
|
||
|
+
|
||
|
+ *num_maps = 0;
|
||
|
+
|
||
|
+ for (i = 0; i < num_pins; i++) {
|
||
|
+ err = of_property_read_u32_index(np, "brcm,pins", i, &pin);
|
||
|
+ if (err)
|
||
|
+ goto out;
|
||
|
+ if (pin >= ARRAY_SIZE(legacy_fsel_map)) {
|
||
|
+ dev_err(pc->dev, "%pOF: invalid brcm,pins value %d\n",
|
||
|
+ np, pin);
|
||
|
+ err = -EINVAL;
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (num_funcs) {
|
||
|
+ err = of_property_read_u32_index(np, "brcm,function",
|
||
|
+ (num_funcs > 1) ? i : 0,
|
||
|
+ &func);
|
||
|
+ if (err)
|
||
|
+ goto out;
|
||
|
+ err = rp1_pctl_legacy_map_func(pc, np, pin, func,
|
||
|
+ maps, num_maps);
|
||
|
+ } else if (function) {
|
||
|
+ err = pinctrl_utils_add_map_mux(pctldev, &maps,
|
||
|
+ &reserved_maps, num_maps,
|
||
|
+ rp1_gpio_groups[pin],
|
||
|
+ function);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (err)
|
||
|
+ goto out;
|
||
|
+
|
||
|
+ if (num_pulls) {
|
||
|
+ err = of_property_read_u32_index(np, "brcm,pull",
|
||
|
+ (num_pulls > 1) ? i : 0,
|
||
|
+ &pull);
|
||
|
+ if (err)
|
||
|
+ goto out;
|
||
|
+ err = rp1_pctl_legacy_map_pull(pc, np, pin, pull,
|
||
|
+ maps, num_maps);
|
||
|
+ } else if (num_configs) {
|
||
|
+ err = pinctrl_utils_add_map_configs(pctldev, &maps,
|
||
|
+ &reserved_maps, num_maps,
|
||
|
+ rp1_gpio_groups[pin],
|
||
|
+ configs, num_configs,
|
||
|
+ PIN_MAP_TYPE_CONFIGS_PIN);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (err)
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+
|
||
|
+ *map = maps;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+
|
||
|
+out:
|
||
|
+ rp1_pctl_dt_free_map(pctldev, maps, reserved_maps);
|
||
|
+ return err;
|
||
|
+}
|
||
|
+
|
||
|
+static const struct pinctrl_ops rp1_pctl_ops = {
|
||
|
+ .get_groups_count = rp1_pctl_get_groups_count,
|
||
|
+ .get_group_name = rp1_pctl_get_group_name,
|
||
|
+ .get_group_pins = rp1_pctl_get_group_pins,
|
||
|
+ .pin_dbg_show = rp1_pctl_pin_dbg_show,
|
||
|
+ .dt_node_to_map = rp1_pctl_dt_node_to_map,
|
||
|
+ .dt_free_map = rp1_pctl_dt_free_map,
|
||
|
+};
|
||
|
+
|
||
|
+static int rp1_pmx_free(struct pinctrl_dev *pctldev, unsigned offset)
|
||
|
+{
|
||
|
+ struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset);
|
||
|
+ u32 fsel = rp1_get_fsel(pin);
|
||
|
+
|
||
|
+ /* Return non-GPIOs to GPIO_IN */
|
||
|
+ if (fsel != RP1_FSEL_GPIO) {
|
||
|
+ rp1_set_dir(pin, RP1_DIR_INPUT);
|
||
|
+ rp1_set_fsel(pin, RP1_FSEL_GPIO);
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_pmx_get_functions_count(struct pinctrl_dev *pctldev)
|
||
|
+{
|
||
|
+ return func_count;
|
||
|
+}
|
||
|
+
|
||
|
+static const char *rp1_pmx_get_function_name(struct pinctrl_dev *pctldev,
|
||
|
+ unsigned selector)
|
||
|
+{
|
||
|
+ return (selector < func_count) ? rp1_func_names[selector] : NULL;
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_pmx_get_function_groups(struct pinctrl_dev *pctldev,
|
||
|
+ unsigned selector,
|
||
|
+ const char * const **groups,
|
||
|
+ unsigned * const num_groups)
|
||
|
+{
|
||
|
+ /* every pin can do every function */
|
||
|
+ *groups = rp1_gpio_groups;
|
||
|
+ *num_groups = ARRAY_SIZE(rp1_gpio_groups);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_pmx_set(struct pinctrl_dev *pctldev, unsigned func_selector,
|
||
|
+ unsigned group_selector)
|
||
|
+{
|
||
|
+ struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, group_selector);
|
||
|
+ const u8 *pin_funcs;
|
||
|
+ int fsel;
|
||
|
+
|
||
|
+ /* func_selector is an enum funcs, so needs translation */
|
||
|
+
|
||
|
+ if (func_selector >= RP1_FSEL_COUNT) {
|
||
|
+ /* Convert to an fsel number */
|
||
|
+ pin_funcs = rp1_gpio_pin_funcs[pin->num].funcs;
|
||
|
+ for (fsel = 0; fsel < RP1_FSEL_COUNT; fsel++) {
|
||
|
+ if (pin_funcs[fsel] == func_selector)
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ fsel = (int)func_selector;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (fsel >= RP1_FSEL_COUNT && fsel != RP1_FSEL_NONE)
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ rp1_set_fsel(pin, fsel);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void rp1_pmx_gpio_disable_free(struct pinctrl_dev *pctldev,
|
||
|
+ struct pinctrl_gpio_range *range,
|
||
|
+ unsigned offset)
|
||
|
+{
|
||
|
+ (void)rp1_pmx_free(pctldev, offset);
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
|
||
|
+ struct pinctrl_gpio_range *range,
|
||
|
+ unsigned offset,
|
||
|
+ bool input)
|
||
|
+{
|
||
|
+ struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset);
|
||
|
+
|
||
|
+ rp1_set_dir(pin, input);
|
||
|
+ rp1_set_fsel(pin, RP1_FSEL_GPIO);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static const struct pinmux_ops rp1_pmx_ops = {
|
||
|
+ .free = rp1_pmx_free,
|
||
|
+ .get_functions_count = rp1_pmx_get_functions_count,
|
||
|
+ .get_function_name = rp1_pmx_get_function_name,
|
||
|
+ .get_function_groups = rp1_pmx_get_function_groups,
|
||
|
+ .set_mux = rp1_pmx_set,
|
||
|
+ .gpio_disable_free = rp1_pmx_gpio_disable_free,
|
||
|
+ .gpio_set_direction = rp1_pmx_gpio_set_direction,
|
||
|
+};
|
||
|
+
|
||
|
+static void rp1_pull_config_set(struct rp1_pin_info *pin, unsigned int arg)
|
||
|
+{
|
||
|
+ u32 padctrl = readl(pin->pad);
|
||
|
+
|
||
|
+ FLD_SET(padctrl, RP1_PAD_PULL, arg & 0x3);
|
||
|
+
|
||
|
+ writel(padctrl, pin->pad);
|
||
|
+}
|
||
|
+
|
||
|
+/* Generic pinconf methods */
|
||
|
+
|
||
|
+static int rp1_pinconf_set(struct pinctrl_dev *pctldev, unsigned int offset,
|
||
|
+ unsigned long *configs, unsigned int num_configs)
|
||
|
+{
|
||
|
+ struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset);
|
||
|
+ u32 param, arg;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ if (!pin)
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ for (i = 0; i < num_configs; i++) {
|
||
|
+ param = pinconf_to_config_param(configs[i]);
|
||
|
+ arg = pinconf_to_config_argument(configs[i]);
|
||
|
+
|
||
|
+ switch (param) {
|
||
|
+ case PIN_CONFIG_BIAS_DISABLE:
|
||
|
+ rp1_pull_config_set(pin, RP1_PUD_OFF);
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PIN_CONFIG_BIAS_PULL_DOWN:
|
||
|
+ rp1_pull_config_set(pin, RP1_PUD_DOWN);
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PIN_CONFIG_BIAS_PULL_UP:
|
||
|
+ rp1_pull_config_set(pin, RP1_PUD_UP);
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PIN_CONFIG_INPUT_ENABLE:
|
||
|
+ rp1_input_enable(pin, arg);
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PIN_CONFIG_OUTPUT_ENABLE:
|
||
|
+ rp1_output_enable(pin, arg);
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PIN_CONFIG_OUTPUT:
|
||
|
+ rp1_set_value(pin, arg);
|
||
|
+ rp1_set_dir(pin, RP1_DIR_OUTPUT);
|
||
|
+ rp1_set_fsel(pin, RP1_FSEL_GPIO);
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
|
||
|
+ rp1_pad_update(pin, RP1_PAD_SCHMITT_MASK,
|
||
|
+ arg ? RP1_PAD_SCHMITT_MASK : 0);
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PIN_CONFIG_SLEW_RATE:
|
||
|
+ rp1_pad_update(pin, RP1_PAD_SLEWFAST_MASK,
|
||
|
+ arg ? RP1_PAD_SLEWFAST_MASK : 0);
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PIN_CONFIG_DRIVE_STRENGTH:
|
||
|
+ switch (arg) {
|
||
|
+ case 2:
|
||
|
+ arg = RP1_PAD_DRIVE_2MA;
|
||
|
+ break;
|
||
|
+ case 4:
|
||
|
+ arg = RP1_PAD_DRIVE_4MA;
|
||
|
+ break;
|
||
|
+ case 8:
|
||
|
+ arg = RP1_PAD_DRIVE_8MA;
|
||
|
+ break;
|
||
|
+ case 12:
|
||
|
+ arg = RP1_PAD_DRIVE_12MA;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ return -ENOTSUPP;
|
||
|
+ }
|
||
|
+ rp1_pad_update(pin, RP1_PAD_DRIVE_MASK, arg);
|
||
|
+ break;
|
||
|
+
|
||
|
+ default:
|
||
|
+ return -ENOTSUPP;
|
||
|
+
|
||
|
+ } /* switch param type */
|
||
|
+ } /* for each config */
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_pinconf_get(struct pinctrl_dev *pctldev, unsigned offset,
|
||
|
+ unsigned long *config)
|
||
|
+{
|
||
|
+ struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset);
|
||
|
+ enum pin_config_param param = pinconf_to_config_param(*config);
|
||
|
+ u32 padctrl;
|
||
|
+ u32 arg;
|
||
|
+
|
||
|
+ if (!pin)
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ padctrl = readl(pin->pad);
|
||
|
+
|
||
|
+ switch (param) {
|
||
|
+ case PIN_CONFIG_INPUT_ENABLE:
|
||
|
+ arg = !!(padctrl & RP1_PAD_IN_ENABLE_MASK);
|
||
|
+ break;
|
||
|
+ case PIN_CONFIG_OUTPUT_ENABLE:
|
||
|
+ arg = !(padctrl & RP1_PAD_OUT_DISABLE_MASK);
|
||
|
+ break;
|
||
|
+ case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
|
||
|
+ arg = !!(padctrl & RP1_PAD_SCHMITT_MASK);
|
||
|
+ break;
|
||
|
+ case PIN_CONFIG_SLEW_RATE:
|
||
|
+ arg = !!(padctrl & RP1_PAD_SLEWFAST_MASK);
|
||
|
+ break;
|
||
|
+ case PIN_CONFIG_DRIVE_STRENGTH:
|
||
|
+ switch (padctrl & RP1_PAD_DRIVE_MASK) {
|
||
|
+ case RP1_PAD_DRIVE_2MA:
|
||
|
+ arg = 2;
|
||
|
+ break;
|
||
|
+ case RP1_PAD_DRIVE_4MA:
|
||
|
+ arg = 4;
|
||
|
+ break;
|
||
|
+ case RP1_PAD_DRIVE_8MA:
|
||
|
+ arg = 8;
|
||
|
+ break;
|
||
|
+ case RP1_PAD_DRIVE_12MA:
|
||
|
+ arg = 12;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ case PIN_CONFIG_BIAS_DISABLE:
|
||
|
+ arg = ((padctrl & RP1_PAD_PULL_MASK) == (RP1_PUD_OFF << RP1_PAD_PULL_LSB));
|
||
|
+ break;
|
||
|
+ case PIN_CONFIG_BIAS_PULL_DOWN:
|
||
|
+ arg = ((padctrl & RP1_PAD_PULL_MASK) == (RP1_PUD_DOWN << RP1_PAD_PULL_LSB));
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PIN_CONFIG_BIAS_PULL_UP:
|
||
|
+ arg = ((padctrl & RP1_PAD_PULL_MASK) == (RP1_PUD_UP << RP1_PAD_PULL_LSB));
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ return -ENOTSUPP;
|
||
|
+ }
|
||
|
+
|
||
|
+ *config = pinconf_to_config_packed(param, arg);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static const struct pinconf_ops rp1_pinconf_ops = {
|
||
|
+ .is_generic = true,
|
||
|
+ .pin_config_get = rp1_pinconf_get,
|
||
|
+ .pin_config_set = rp1_pinconf_set,
|
||
|
+};
|
||
|
+
|
||
|
+static struct pinctrl_desc rp1_pinctrl_desc = {
|
||
|
+ .name = MODULE_NAME,
|
||
|
+ .pins = rp1_gpio_pins,
|
||
|
+ .npins = ARRAY_SIZE(rp1_gpio_pins),
|
||
|
+ .pctlops = &rp1_pctl_ops,
|
||
|
+ .pmxops = &rp1_pmx_ops,
|
||
|
+ .confops = &rp1_pinconf_ops,
|
||
|
+ .owner = THIS_MODULE,
|
||
|
+};
|
||
|
+
|
||
|
+static struct pinctrl_gpio_range rp1_pinctrl_gpio_range = {
|
||
|
+ .name = MODULE_NAME,
|
||
|
+ .npins = RP1_NUM_GPIOS,
|
||
|
+};
|
||
|
+
|
||
|
+static const struct of_device_id rp1_pinctrl_match[] = {
|
||
|
+ {
|
||
|
+ .compatible = "raspberrypi,rp1-gpio",
|
||
|
+ .data = &rp1_pinconf_ops,
|
||
|
+ },
|
||
|
+ {}
|
||
|
+};
|
||
|
+
|
||
|
+static inline void __iomem *devm_auto_iomap(struct platform_device *pdev,
|
||
|
+ unsigned int index)
|
||
|
+{
|
||
|
+ struct device *dev = &pdev->dev;
|
||
|
+ struct device_node *np = dev->of_node;
|
||
|
+
|
||
|
+ if (np)
|
||
|
+ return devm_of_iomap(dev, np, (int)index, NULL);
|
||
|
+ else
|
||
|
+ return devm_platform_ioremap_resource(pdev, index);
|
||
|
+}
|
||
|
+
|
||
|
+static int rp1_pinctrl_probe(struct platform_device *pdev)
|
||
|
+{
|
||
|
+ struct device *dev = &pdev->dev;
|
||
|
+ struct device_node *np = dev->of_node;
|
||
|
+ struct rp1_pinctrl *pc;
|
||
|
+ struct gpio_irq_chip *girq;
|
||
|
+ int err, i;
|
||
|
+
|
||
|
+ BUILD_BUG_ON(ARRAY_SIZE(rp1_gpio_pins) != RP1_NUM_GPIOS);
|
||
|
+ BUILD_BUG_ON(ARRAY_SIZE(rp1_gpio_groups) != RP1_NUM_GPIOS);
|
||
|
+
|
||
|
+ pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
|
||
|
+ if (!pc)
|
||
|
+ return -ENOMEM;
|
||
|
+
|
||
|
+ platform_set_drvdata(pdev, pc);
|
||
|
+ pc->dev = dev;
|
||
|
+
|
||
|
+ pc->gpio_base = devm_auto_iomap(pdev, 0);
|
||
|
+ if (IS_ERR(pc->gpio_base)) {
|
||
|
+ dev_err(dev, "could not get GPIO IO memory\n");
|
||
|
+ return PTR_ERR(pc->gpio_base);
|
||
|
+ }
|
||
|
+
|
||
|
+ pc->rio_base = devm_auto_iomap(pdev, 1);
|
||
|
+ if (IS_ERR(pc->rio_base)) {
|
||
|
+ dev_err(dev, "could not get RIO IO memory\n");
|
||
|
+ return PTR_ERR(pc->rio_base);
|
||
|
+ }
|
||
|
+
|
||
|
+ pc->pads_base = devm_auto_iomap(pdev, 2);
|
||
|
+ if (IS_ERR(pc->pads_base)) {
|
||
|
+ dev_err(dev, "could not get PADS IO memory\n");
|
||
|
+ return PTR_ERR(pc->pads_base);
|
||
|
+ }
|
||
|
+
|
||
|
+ pc->gpio_chip = rp1_gpio_chip;
|
||
|
+ pc->gpio_chip.parent = dev;
|
||
|
+
|
||
|
+ for (i = 0; i < RP1_NUM_BANKS; i++) {
|
||
|
+ const struct rp1_iobank_desc *bank = &rp1_iobanks[i];
|
||
|
+ int j;
|
||
|
+
|
||
|
+ for (j = 0; j < bank->num_gpios; j++) {
|
||
|
+ struct rp1_pin_info *pin =
|
||
|
+ &pc->pins[bank->min_gpio + j];
|
||
|
+
|
||
|
+ pin->num = bank->min_gpio + j;
|
||
|
+ pin->bank = i;
|
||
|
+ pin->offset = j;
|
||
|
+
|
||
|
+ pin->gpio = pc->gpio_base + bank->gpio_offset +
|
||
|
+ j * sizeof(u32) * 2;
|
||
|
+ pin->inte = pc->gpio_base + bank->inte_offset;
|
||
|
+ pin->ints = pc->gpio_base + bank->ints_offset;
|
||
|
+ pin->rio = pc->rio_base + bank->rio_offset;
|
||
|
+ pin->pad = pc->pads_base + bank->pads_offset +
|
||
|
+ j * sizeof(u32);
|
||
|
+ }
|
||
|
+
|
||
|
+ raw_spin_lock_init(&pc->irq_lock[i]);
|
||
|
+ }
|
||
|
+
|
||
|
+ pc->pctl_dev = devm_pinctrl_register(dev, &rp1_pinctrl_desc, pc);
|
||
|
+ if (IS_ERR(pc->pctl_dev))
|
||
|
+ return PTR_ERR(pc->pctl_dev);
|
||
|
+
|
||
|
+ girq = &pc->gpio_chip.irq;
|
||
|
+ girq->chip = &rp1_gpio_irq_chip;
|
||
|
+ girq->parent_handler = rp1_gpio_irq_handler;
|
||
|
+ girq->num_parents = RP1_NUM_BANKS;
|
||
|
+ girq->parents = pc->irq;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Use the same handler for all groups: this is necessary
|
||
|
+ * since we use one gpiochip to cover all lines - the
|
||
|
+ * irq handler then needs to figure out which group and
|
||
|
+ * bank that was firing the IRQ and look up the per-group
|
||
|
+ * and bank data.
|
||
|
+ */
|
||
|
+ for (i = 0; i < RP1_NUM_BANKS; i++) {
|
||
|
+ pc->irq[i] = irq_of_parse_and_map(np, i);
|
||
|
+ if (!pc->irq[i]) {
|
||
|
+ girq->num_parents = i;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ girq->default_type = IRQ_TYPE_NONE;
|
||
|
+ girq->handler = handle_level_irq;
|
||
|
+
|
||
|
+ err = devm_gpiochip_add_data(dev, &pc->gpio_chip, pc);
|
||
|
+ if (err) {
|
||
|
+ dev_err(dev, "could not add GPIO chip\n");
|
||
|
+ return err;
|
||
|
+ }
|
||
|
+
|
||
|
+ pc->gpio_range = rp1_pinctrl_gpio_range;
|
||
|
+ pc->gpio_range.base = pc->gpio_chip.base;
|
||
|
+ pc->gpio_range.gc = &pc->gpio_chip;
|
||
|
+ pinctrl_add_gpio_range(pc->pctl_dev, &pc->gpio_range);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static struct platform_driver rp1_pinctrl_driver = {
|
||
|
+ .probe = rp1_pinctrl_probe,
|
||
|
+ .driver = {
|
||
|
+ .name = MODULE_NAME,
|
||
|
+ .of_match_table = rp1_pinctrl_match,
|
||
|
+ .suppress_bind_attrs = true,
|
||
|
+ },
|
||
|
+};
|
||
|
+builtin_platform_driver(rp1_pinctrl_driver);
|
||
|
--- a/include/dt-bindings/pinctrl/rp1.h
|
||
|
+++ /dev/null
|
||
|
@@ -1,46 +0,0 @@
|
||
|
-/* SPDX-License-Identifier: GPL-2.0 */
|
||
|
-/*
|
||
|
- * Header providing constants for RP1 pinctrl bindings.
|
||
|
- *
|
||
|
- * Copyright (C) 2019-2022 Raspberry Pi Ltd.
|
||
|
- */
|
||
|
-
|
||
|
-#ifndef __DT_BINDINGS_PINCTRL_RP1_H__
|
||
|
-#define __DT_BINDINGS_PINCTRL_RP1_H__
|
||
|
-
|
||
|
-/* brcm,function property */
|
||
|
-#define RP1_FSEL_GPIO_IN 0
|
||
|
-#define RP1_FSEL_GPIO_OUT 1
|
||
|
-#define RP1_FSEL_ALT0_LEGACY 4
|
||
|
-#define RP1_FSEL_ALT1_LEGACY 5
|
||
|
-#define RP1_FSEL_ALT2_LEGACY 6
|
||
|
-#define RP1_FSEL_ALT3_LEGACY 7
|
||
|
-#define RP1_FSEL_ALT4_LEGACY 3
|
||
|
-#define RP1_FSEL_ALT5_LEGACY 2
|
||
|
-#define RP1_FSEL_ALT0 0x08
|
||
|
-#define RP1_FSEL_ALT0INV 0x09
|
||
|
-#define RP1_FSEL_ALT1 0x0a
|
||
|
-#define RP1_FSEL_ALT1INV 0x0b
|
||
|
-#define RP1_FSEL_ALT2 0x0c
|
||
|
-#define RP1_FSEL_ALT2INV 0x0d
|
||
|
-#define RP1_FSEL_ALT3 0x0e
|
||
|
-#define RP1_FSEL_ALT3INV 0x0f
|
||
|
-#define RP1_FSEL_ALT4 0x10
|
||
|
-#define RP1_FSEL_ALT4INV 0x11
|
||
|
-#define RP1_FSEL_ALT5 0x12
|
||
|
-#define RP1_FSEL_ALT5INV 0x13
|
||
|
-#define RP1_FSEL_ALT6 0x14
|
||
|
-#define RP1_FSEL_ALT6INV 0x15
|
||
|
-#define RP1_FSEL_ALT7 0x16
|
||
|
-#define RP1_FSEL_ALT7INV 0x17
|
||
|
-#define RP1_FSEL_ALT8 0x18
|
||
|
-#define RP1_FSEL_ALT8INV 0x19
|
||
|
-#define RP1_FSEL_NONE 0x1a
|
||
|
-
|
||
|
-/* brcm,pull property */
|
||
|
-#define RP1_PUD_OFF 0
|
||
|
-#define RP1_PUD_DOWN 1
|
||
|
-#define RP1_PUD_UP 2
|
||
|
-#define RP1_PUD_KEEP 3
|
||
|
-
|
||
|
-#endif /* __DT_BINDINGS_PINCTRL_RP1_H__ */
|