mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-08-18 03:15:14 +00:00

The conversion of all GPIO drivers to using the .set_rv() and .set_multiple_rv() callbacks from struct gpio_chip (which - unlike their predecessors - return an integer and allow the controller drivers to indicate failures to users) is now complete and the legacy ones have been removed. Rename the new callbacks back to their original names in one sweeping change. Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
1832 lines
49 KiB
C
1832 lines
49 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Driver for Raspberry Pi RP1 GPIO unit
|
|
*
|
|
* Copyright (C) 2023 Raspberry Pi Ltd.
|
|
*
|
|
* This driver is inspired by:
|
|
* pinctrl-bcm2835.c, please see original file for copyright information
|
|
*/
|
|
|
|
#include <linux/gpio/driver.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/pinctrl/pinconf.h>
|
|
#include <linux/pinctrl/pinmux.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/regmap.h>
|
|
|
|
#include "pinmux.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_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 GENMASK(3, 0)
|
|
#define RP1_INT_EDGE_BOTH (RP1_INT_EDGE_FALLING | \
|
|
RP1_INT_EDGE_RISING)
|
|
|
|
#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_PAD_DRIVE_2MA 0x0
|
|
#define RP1_PAD_DRIVE_4MA 0x1
|
|
#define RP1_PAD_DRIVE_8MA 0x2
|
|
#define RP1_PAD_DRIVE_12MA 0x3
|
|
|
|
enum {
|
|
RP1_PUD_OFF = 0,
|
|
RP1_PUD_DOWN = 1,
|
|
RP1_PUD_UP = 2,
|
|
};
|
|
|
|
enum {
|
|
RP1_DIR_OUTPUT = 0,
|
|
RP1_DIR_INPUT = 1,
|
|
};
|
|
|
|
enum {
|
|
RP1_OUTOVER_PERI = 0,
|
|
RP1_OUTOVER_INVPERI = 1,
|
|
RP1_OUTOVER_LOW = 2,
|
|
RP1_OUTOVER_HIGH = 3,
|
|
};
|
|
|
|
enum {
|
|
RP1_OEOVER_PERI = 0,
|
|
RP1_OEOVER_INVPERI = 1,
|
|
RP1_OEOVER_DISABLE = 2,
|
|
RP1_OEOVER_ENABLE = 3,
|
|
};
|
|
|
|
enum {
|
|
RP1_INOVER_PERI = 0,
|
|
RP1_INOVER_INVPERI = 1,
|
|
RP1_INOVER_LOW = 2,
|
|
RP1_INOVER_HIGH = 3,
|
|
};
|
|
|
|
enum {
|
|
RP1_GPIO_CTRL_IRQRESET_SET = 0,
|
|
RP1_GPIO_CTRL_INT_CLR = 1,
|
|
RP1_GPIO_CTRL_INT_SET = 2,
|
|
RP1_GPIO_CTRL_OEOVER = 3,
|
|
RP1_GPIO_CTRL_FUNCSEL = 4,
|
|
RP1_GPIO_CTRL_OUTOVER = 5,
|
|
RP1_GPIO_CTRL = 6,
|
|
};
|
|
|
|
enum {
|
|
RP1_INTE_SET = 0,
|
|
RP1_INTE_CLR = 1,
|
|
};
|
|
|
|
enum {
|
|
RP1_RIO_OUT_SET = 0,
|
|
RP1_RIO_OUT_CLR = 1,
|
|
RP1_RIO_OE = 2,
|
|
RP1_RIO_OE_SET = 3,
|
|
RP1_RIO_OE_CLR = 4,
|
|
RP1_RIO_IN = 5,
|
|
};
|
|
|
|
enum {
|
|
RP1_PAD_SLEWFAST = 0,
|
|
RP1_PAD_SCHMITT = 1,
|
|
RP1_PAD_PULL = 2,
|
|
RP1_PAD_DRIVE = 3,
|
|
RP1_PAD_IN_ENABLE = 4,
|
|
RP1_PAD_OUT_DISABLE = 5,
|
|
};
|
|
|
|
static const struct reg_field rp1_gpio_fields[] = {
|
|
[RP1_GPIO_CTRL_IRQRESET_SET] = REG_FIELD(0x2004, 28, 28),
|
|
[RP1_GPIO_CTRL_INT_CLR] = REG_FIELD(0x3004, 20, 23),
|
|
[RP1_GPIO_CTRL_INT_SET] = REG_FIELD(0x2004, 20, 23),
|
|
[RP1_GPIO_CTRL_OEOVER] = REG_FIELD(0x0004, 14, 15),
|
|
[RP1_GPIO_CTRL_FUNCSEL] = REG_FIELD(0x0004, 0, 4),
|
|
[RP1_GPIO_CTRL_OUTOVER] = REG_FIELD(0x0004, 12, 13),
|
|
[RP1_GPIO_CTRL] = REG_FIELD(0x0004, 0, 31),
|
|
};
|
|
|
|
static const struct reg_field rp1_inte_fields[] = {
|
|
[RP1_INTE_SET] = REG_FIELD(0x2000, 0, 0),
|
|
[RP1_INTE_CLR] = REG_FIELD(0x3000, 0, 0),
|
|
};
|
|
|
|
static const struct reg_field rp1_rio_fields[] = {
|
|
[RP1_RIO_OUT_SET] = REG_FIELD(0x2000, 0, 0),
|
|
[RP1_RIO_OUT_CLR] = REG_FIELD(0x3000, 0, 0),
|
|
[RP1_RIO_OE] = REG_FIELD(0x0004, 0, 0),
|
|
[RP1_RIO_OE_SET] = REG_FIELD(0x2004, 0, 0),
|
|
[RP1_RIO_OE_CLR] = REG_FIELD(0x3004, 0, 0),
|
|
[RP1_RIO_IN] = REG_FIELD(0x0008, 0, 0),
|
|
};
|
|
|
|
static const struct reg_field rp1_pad_fields[] = {
|
|
[RP1_PAD_SLEWFAST] = REG_FIELD(0, 0, 0),
|
|
[RP1_PAD_SCHMITT] = REG_FIELD(0, 1, 1),
|
|
[RP1_PAD_PULL] = REG_FIELD(0, 2, 3),
|
|
[RP1_PAD_DRIVE] = REG_FIELD(0, 4, 5),
|
|
[RP1_PAD_IN_ENABLE] = REG_FIELD(0, 6, 6),
|
|
[RP1_PAD_OUT_DISABLE] = REG_FIELD(0, 7, 7),
|
|
};
|
|
|
|
#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, \
|
|
}
|
|
|
|
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_dpi,
|
|
func_dsi0_te_ext,
|
|
func_dsi1_te_ext,
|
|
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_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_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;
|
|
|
|
struct regmap_field *gpio[ARRAY_SIZE(rp1_gpio_fields)];
|
|
struct regmap_field *rio[ARRAY_SIZE(rp1_rio_fields)];
|
|
struct regmap_field *inte[ARRAY_SIZE(rp1_inte_fields)];
|
|
struct regmap_field *pad[ARRAY_SIZE(rp1_pad_fields)];
|
|
};
|
|
|
|
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];
|
|
};
|
|
|
|
/* 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),
|
|
};
|
|
|
|
#define PIN_ARRAY(...) \
|
|
(const unsigned int []) {__VA_ARGS__}
|
|
#define PIN_ARRAY_SIZE(...) \
|
|
(sizeof((unsigned int[]) {__VA_ARGS__}) / sizeof(unsigned int))
|
|
#define RP1_GROUP(name, ...) \
|
|
PINCTRL_PINGROUP(#name, PIN_ARRAY(__VA_ARGS__), \
|
|
PIN_ARRAY_SIZE(__VA_ARGS__))
|
|
|
|
static const struct pingroup rp1_gpio_groups[] = {
|
|
RP1_GROUP(uart0, 14, 15),
|
|
RP1_GROUP(uart0_ctrl, 4, 5, 6, 7, 16, 17),
|
|
RP1_GROUP(uart1, 0, 1),
|
|
RP1_GROUP(uart1_ctrl, 2, 3),
|
|
RP1_GROUP(uart2, 4, 5),
|
|
RP1_GROUP(uart2_ctrl, 6, 7),
|
|
RP1_GROUP(uart3, 8, 9),
|
|
RP1_GROUP(uart3_ctrl, 10, 11),
|
|
RP1_GROUP(uart4, 12, 13),
|
|
RP1_GROUP(uart4_ctrl, 14, 15),
|
|
RP1_GROUP(uart5_0, 30, 31),
|
|
RP1_GROUP(uart5_0_ctrl, 32, 33),
|
|
RP1_GROUP(uart5_1, 36, 37),
|
|
RP1_GROUP(uart5_1_ctrl, 38, 39),
|
|
RP1_GROUP(uart5_2, 40, 41),
|
|
RP1_GROUP(uart5_2_ctrl, 42, 43),
|
|
RP1_GROUP(uart5_3, 48, 49),
|
|
RP1_GROUP(sd0, 22, 23, 24, 25, 26, 27),
|
|
RP1_GROUP(sd1, 28, 29, 30, 31, 32, 33),
|
|
RP1_GROUP(i2s0, 18, 19, 20, 21),
|
|
RP1_GROUP(i2s0_dual, 18, 19, 20, 21, 22, 23),
|
|
RP1_GROUP(i2s0_quad, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27),
|
|
RP1_GROUP(i2s1, 18, 19, 20, 21),
|
|
RP1_GROUP(i2s1_dual, 18, 19, 20, 21, 22, 23),
|
|
RP1_GROUP(i2s1_quad, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27),
|
|
RP1_GROUP(i2s2_0, 28, 29, 30, 31),
|
|
RP1_GROUP(i2s2_0_dual, 28, 29, 30, 31, 32, 33),
|
|
RP1_GROUP(i2s2_1, 42, 43, 44, 45),
|
|
RP1_GROUP(i2s2_1_dual, 42, 43, 44, 45, 46, 47),
|
|
RP1_GROUP(i2c4_0, 28, 29),
|
|
RP1_GROUP(i2c4_1, 34, 35),
|
|
RP1_GROUP(i2c4_2, 40, 41),
|
|
RP1_GROUP(i2c4_3, 46, 47),
|
|
RP1_GROUP(i2c6_0, 38, 39),
|
|
RP1_GROUP(i2c6_1, 51, 52),
|
|
RP1_GROUP(i2c5_0, 30, 31),
|
|
RP1_GROUP(i2c5_1, 36, 37),
|
|
RP1_GROUP(i2c5_2, 44, 45),
|
|
RP1_GROUP(i2c5_3, 49, 50),
|
|
RP1_GROUP(i2c0_0, 0, 1),
|
|
RP1_GROUP(i2c0_1, 8, 9),
|
|
RP1_GROUP(i2c1_0, 2, 3),
|
|
RP1_GROUP(i2c1_1, 10, 11),
|
|
RP1_GROUP(i2c2_0, 4, 5),
|
|
RP1_GROUP(i2c2_1, 12, 13),
|
|
RP1_GROUP(i2c3_0, 6, 7),
|
|
RP1_GROUP(i2c3_1, 14, 15),
|
|
RP1_GROUP(i2c3_2, 22, 23),
|
|
RP1_GROUP(dpi_16bit, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
|
11, 12, 13, 14, 15, 16, 17, 18, 19),
|
|
RP1_GROUP(dpi_16bit_cpadhi, 0, 1, 2, 3, 4, 5, 6, 7, 8,
|
|
12, 13, 14, 15, 16, 17, 20, 21, 22, 23, 24),
|
|
RP1_GROUP(dpi_16bit_pad666, 0, 1, 2, 3, 5, 6, 7, 8, 9,
|
|
12, 13, 14, 15, 16, 17, 21, 22, 23, 24, 25),
|
|
RP1_GROUP(dpi_18bit, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
|
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21),
|
|
RP1_GROUP(dpi_18bit_cpadhi, 0, 1, 2, 3, 4, 5, 6, 7, 8,
|
|
9, 12, 13, 14, 15, 16, 17, 20, 21, 22, 23, 24,
|
|
25),
|
|
RP1_GROUP(dpi_24bit, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
|
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
|
|
22, 23, 24, 25, 26, 27),
|
|
RP1_GROUP(spi0, 9, 10, 11),
|
|
RP1_GROUP(spi0_quad, 0, 1, 9, 10, 11),
|
|
RP1_GROUP(spi1, 19, 20, 21),
|
|
RP1_GROUP(spi2, 1, 2, 3),
|
|
RP1_GROUP(spi3, 5, 6, 7),
|
|
RP1_GROUP(spi4, 9, 10, 11),
|
|
RP1_GROUP(spi5, 13, 14, 15),
|
|
RP1_GROUP(spi6_0, 28, 29, 30),
|
|
RP1_GROUP(spi6_1, 40, 41, 42),
|
|
RP1_GROUP(spi7_0, 46, 47, 48),
|
|
RP1_GROUP(spi7_1, 49, 50, 51),
|
|
RP1_GROUP(spi8_0, 37, 38, 39),
|
|
RP1_GROUP(spi8_1, 49, 50, 51),
|
|
RP1_GROUP(aaud_0, 12, 13),
|
|
RP1_GROUP(aaud_1, 38, 39),
|
|
RP1_GROUP(aaud_2, 40, 41),
|
|
RP1_GROUP(aaud_3, 49, 50),
|
|
RP1_GROUP(aaud_4, 51, 52),
|
|
RP1_GROUP(vbus0_0, 28, 29),
|
|
RP1_GROUP(vbus0_1, 34, 35),
|
|
RP1_GROUP(vbus1, 42, 43),
|
|
RP1_GROUP(vbus2, 50, 51),
|
|
RP1_GROUP(vbus3, 52, 53),
|
|
RP1_GROUP(mic_0, 25, 26, 27),
|
|
RP1_GROUP(mic_1, 34, 35, 36),
|
|
RP1_GROUP(mic_2, 37, 38, 39),
|
|
RP1_GROUP(mic_3, 46, 47, 48),
|
|
RP1_GROUP(ir, 2, 3),
|
|
};
|
|
|
|
#define GRP_ARRAY(...) \
|
|
(const char * []) {__VA_ARGS__}
|
|
#define GRP_ARRAY_SIZE(...) \
|
|
(sizeof((char *[]) {__VA_ARGS__}) / sizeof(char *))
|
|
#define RP1_FNC(f, ...) \
|
|
[func_##f] = PINCTRL_PINFUNCTION(#f, GRP_ARRAY(__VA_ARGS__), \
|
|
GRP_ARRAY_SIZE(__VA_ARGS__))
|
|
#define RP1_NULL_FNC(f) \
|
|
[func_##f] = PINCTRL_PINFUNCTION(#f, NULL, 0)
|
|
#define RP1_ALL_LEGACY_PINS \
|
|
"gpio0", "gpio1", "gpio2", "gpio3", "gpio4", \
|
|
"gpio5", "gpio6", "gpio7", "gpio8", "gpio9", \
|
|
"gpio10", "gpio11", "gpio12", "gpio13", "gpio14", \
|
|
"gpio15", "gpio16", "gpio17", "gpio18", "gpio19", \
|
|
"gpio20", "gpio21", "gpio22", "gpio32", "gpio24", \
|
|
"gpio25", "gpio26", "gpio27"
|
|
#define RP1_ALL_PINS RP1_ALL_LEGACY_PINS, \
|
|
"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 struct pinfunction rp1_func_names[] = {
|
|
RP1_NULL_FNC(alt0),
|
|
RP1_NULL_FNC(alt1),
|
|
RP1_NULL_FNC(alt2),
|
|
RP1_NULL_FNC(alt3),
|
|
RP1_NULL_FNC(alt4),
|
|
RP1_FNC(gpio, RP1_ALL_PINS),
|
|
RP1_NULL_FNC(alt6),
|
|
RP1_NULL_FNC(alt7),
|
|
RP1_NULL_FNC(alt8),
|
|
RP1_NULL_FNC(none),
|
|
RP1_FNC(aaud, "aaud_0", "aaud_1", "aaud_2", "aaud_3", "aaud_4",
|
|
"gpio12", "gpio13", "gpio38", "gpio39", "gpio40", "gpio41",
|
|
"gpio49", "gpio50", "gpio51", "gpio52"),
|
|
RP1_FNC(dpi, "dpi_16bit", "dpi_16bit_cpadhi",
|
|
"dpi_16bit_pad666", "dpi_18bit, dpi_18bit_cpadhi",
|
|
"dpi_24bit", RP1_ALL_LEGACY_PINS),
|
|
RP1_FNC(dsi0_te_ext, "gpio16", "gpio38", "gpio46"),
|
|
RP1_FNC(dsi1_te_ext, "gpio17", "gpio39", "gpio47"),
|
|
RP1_FNC(gpclk0, "gpio4", "gpio20"),
|
|
RP1_FNC(gpclk1, "gpio5", "gpio18", "gpio21"),
|
|
RP1_FNC(gpclk2, "gpio6"),
|
|
RP1_FNC(gpclk3, "gpio32", "gpio34", "gpio46"),
|
|
RP1_FNC(gpclk4, "gpio33", "gpio43"),
|
|
RP1_FNC(gpclk5, "gpio42", "gpio44", "gpio47"),
|
|
RP1_FNC(i2c0, "i2c0_0", "i2c0_1", "gpio0", "gpio1", "gpio8", "gpio9"),
|
|
RP1_FNC(i2c1, "i2c1_0", "i2c1_1", "gpio2", "gpio3", "gpio10", "gpio11"),
|
|
RP1_FNC(i2c2, "i2c2_0", "i2c2_1", "gpio4", "gpio5", "gpio12", "gpio13"),
|
|
RP1_FNC(i2c3, "i2c3_0", "i2c3_1", "i2c3_2", "gpio6", "gpio7", "gpio14",
|
|
"gpio15", "gpio22", "gpio23"),
|
|
RP1_FNC(i2c4, "i2c4_0", "i2c4_1", "i2c4_2", "i2c4_3", "gpio28",
|
|
"gpio29", "gpio34", "gpio35", "gpio40", "gpio41", "gpio46",
|
|
"gpio47"),
|
|
RP1_FNC(i2c5, "i2c5_0", "i2c5_1", "i2c5_2", "i2c5_3", "gpio30",
|
|
"gpio31", "gpio36", "gpio37", "gpio44", "gpio45", "gpio49",
|
|
"gpio50"),
|
|
RP1_FNC(i2c6, "i2c6_0", "i2c6_1", "gpio38", "gpio39", "gpio51",
|
|
"gpio52"),
|
|
RP1_FNC(i2s0, "i2s0", "i2s0_dual", "i2s0_quad", "gpio18", "gpio19",
|
|
"gpio20", "gpio21", "gpio22", "gpio23", "gpio24", "gpio25",
|
|
"gpio26", "gpio27"),
|
|
RP1_FNC(i2s1, "i2s1", "i2s1_dual", "i2s1_quad", "gpio18", "gpio19",
|
|
"gpio20", "gpio21", "gpio22", "gpio23", "gpio24", "gpio25",
|
|
"gpio26", "gpio27"),
|
|
RP1_FNC(i2s2, "i2s2_0", "i2s2_0_dual", "i2s2_1", "i2s2_1_dual",
|
|
"gpio28", "gpio29", "gpio30", "gpio31", "gpio32", "gpio33",
|
|
"gpio42", "gpio43", "gpio44", "gpio45", "gpio46", "gpio47"),
|
|
RP1_FNC(ir, "gpio2", "gpio3"),
|
|
RP1_FNC(mic, "mic_0", "mic_1", "mic_2", "mic_3", "gpio25", "gpio26",
|
|
"gpio27", "gpio34", "gpio35", "gpio36", "gpio37", "gpio38",
|
|
"gpio39", "gpio46", "gpio47", "gpio48"),
|
|
RP1_FNC(pcie_clkreq_n, "gpio36", "gpio37", "gpio48", "gpio53"),
|
|
RP1_FNC(pio, RP1_ALL_LEGACY_PINS),
|
|
RP1_FNC(proc_rio, RP1_ALL_PINS),
|
|
RP1_FNC(pwm0, "gpio12", "gpio13", "gpio14", "gpio15", "gpio18",
|
|
"gpio19"),
|
|
RP1_FNC(pwm1, "gpio34", "gpio35", "gpio40", "gpio41", "gpio44",
|
|
"gpio45", "gpio48"),
|
|
RP1_FNC(sd0, "sd0", "gpio22", "gpio23", "gpio24", "gpio25", "gpio26",
|
|
"gpio27"),
|
|
RP1_FNC(sd1, "sd1", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32",
|
|
"gpio33"),
|
|
RP1_FNC(spi0, "spi0", "spi0_quad", "gpio0", "gpio1", "gpio2", "gpio3",
|
|
"gpio7", "gpio8", "gpio9", "gpio10", "gpio11"),
|
|
RP1_FNC(spi1, "spi1", "gpio19", "gpio20", "gpio21", "gpio16", "gpio17",
|
|
"gpio18", "gpio27"),
|
|
RP1_FNC(spi2, "spi2", "gpio0", "gpio1", "gpio2", "gpio3", "gpio24"),
|
|
RP1_FNC(spi3, "spi3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio25"),
|
|
RP1_FNC(spi4, "spi4", "gpio8", "gpio9", "gpio10", "gpio11"),
|
|
RP1_FNC(spi5, "spi5", "gpio12", "gpio13", "gpio14", "gpio15", "gpio26"),
|
|
RP1_FNC(spi6, "spi6_0", "spi6_1", "gpio28", "gpio29", "gpio30",
|
|
"gpio31", "gpio32", "gpio33", "gpio40", "gpio41", "gpio42",
|
|
"gpio43", "gpio44", "gpio45"),
|
|
RP1_FNC(spi7, "spi7_0", "spi7_1", "gpio45", "gpio46", "gpio47",
|
|
"gpio48", "gpio49", "gpio50", "gpio51", "gpio53"),
|
|
RP1_FNC(spi8, "spi8_0", "spi8_1", "gpio35", "gpio36", "gpio37",
|
|
"gpio38", "gpio39", "gpio49", "gpio50", "gpio51", "gpio52",
|
|
"gpio53"),
|
|
RP1_FNC(uart0, "uart0", "uart0_ctrl", "gpio4", "gpio5", "gpio6",
|
|
"gpio7", "gpio14", "gpio15", "gpio16", "gpio17"),
|
|
RP1_FNC(uart1, "uart1", "uart1_ctrl", "gpio0", "gpio1", "gpio2",
|
|
"gpio3"),
|
|
RP1_FNC(uart2, "uart2", "uart2_ctrl", "gpio4", "gpio5", "gpio6",
|
|
"gpio7"),
|
|
RP1_FNC(uart3, "uart3", "uart3_ctrl", "gpio8", "gpio9", "gpio10",
|
|
"gpio11"),
|
|
RP1_FNC(uart4, "uart4", "uart4_ctrl", "gpio12", "gpio13", "gpio14",
|
|
"gpio15"),
|
|
RP1_FNC(uart5, "uart5_0", "uart5_0_ctrl", "uart5_1", "uart5_1_ctrl",
|
|
"uart5_2", "uart5_2_ctrl", "uart5_3"),
|
|
RP1_FNC(vbus0, "vbus0_0", "vbus0_1", "gpio28", "gpio29", "gpio34",
|
|
"gpio35"),
|
|
RP1_FNC(vbus1, "vbus1", "gpio42", "gpio43"),
|
|
RP1_FNC(vbus2, "vbus2", "gpio50", "gpio51"),
|
|
RP1_FNC(vbus3, "vbus3", "gpio52", "gpio53"),
|
|
RP1_NULL_FNC(invalid), //[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, uart0, gpio, proc_rio, pio, spi3),
|
|
PIN(5, gpclk1, dpi, uart2, i2c2, uart0, gpio, proc_rio, pio, spi3),
|
|
PIN(6, gpclk2, dpi, uart2, i2c3, uart0, gpio, proc_rio, pio, spi3),
|
|
PIN(7, spi0, dpi, uart2, i2c3, uart0, 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 bool persist_gpio_outputs = true;
|
|
module_param(persist_gpio_outputs, bool, 0644);
|
|
MODULE_PARM_DESC(persist_gpio_outputs, "Enable GPIO_OUT persistence when pin is freed");
|
|
|
|
static 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 },
|
|
};
|
|
|
|
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_input_enable(struct rp1_pin_info *pin, int value)
|
|
{
|
|
regmap_field_write(pin->pad[RP1_PAD_IN_ENABLE], !!value);
|
|
}
|
|
|
|
static void rp1_output_enable(struct rp1_pin_info *pin, int value)
|
|
{
|
|
regmap_field_write(pin->pad[RP1_PAD_OUT_DISABLE], !value);
|
|
}
|
|
|
|
static u32 rp1_get_fsel(struct rp1_pin_info *pin)
|
|
{
|
|
u32 oeover, fsel;
|
|
|
|
regmap_field_read(pin->gpio[RP1_GPIO_CTRL_OEOVER], &oeover);
|
|
regmap_field_read(pin->gpio[RP1_GPIO_CTRL_FUNCSEL], &fsel);
|
|
|
|
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)
|
|
{
|
|
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) {
|
|
regmap_field_write(pin->gpio[RP1_GPIO_CTRL_OEOVER], RP1_OEOVER_DISABLE);
|
|
} else {
|
|
regmap_field_write(pin->gpio[RP1_GPIO_CTRL_OUTOVER], RP1_OUTOVER_PERI);
|
|
regmap_field_write(pin->gpio[RP1_GPIO_CTRL_OEOVER], RP1_OEOVER_PERI);
|
|
}
|
|
|
|
regmap_field_write(pin->gpio[RP1_GPIO_CTRL_FUNCSEL], fsel);
|
|
}
|
|
|
|
static int rp1_get_dir(struct rp1_pin_info *pin)
|
|
{
|
|
unsigned int val;
|
|
|
|
regmap_field_read(pin->rio[RP1_RIO_OE], &val);
|
|
|
|
return !val ? RP1_DIR_INPUT : RP1_DIR_OUTPUT;
|
|
}
|
|
|
|
static void rp1_set_dir(struct rp1_pin_info *pin, bool is_input)
|
|
{
|
|
int reg = is_input ? RP1_RIO_OE_CLR : RP1_RIO_OE_SET;
|
|
|
|
regmap_field_write(pin->rio[reg], 1);
|
|
}
|
|
|
|
static int rp1_get_value(struct rp1_pin_info *pin)
|
|
{
|
|
unsigned int val;
|
|
|
|
regmap_field_read(pin->rio[RP1_RIO_IN], &val);
|
|
|
|
return !!val;
|
|
}
|
|
|
|
static void rp1_set_value(struct rp1_pin_info *pin, int value)
|
|
{
|
|
/* Assume the pin is already an output */
|
|
int reg = value ? RP1_RIO_OUT_SET : RP1_RIO_OUT_CLR;
|
|
|
|
regmap_field_write(pin->rio[reg], 1);
|
|
}
|
|
|
|
static int rp1_gpio_get(struct gpio_chip *chip, unsigned int 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 int rp1_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
|
|
{
|
|
struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
|
|
|
|
if (pin)
|
|
rp1_set_value(pin, value);
|
|
|
|
return 0;
|
|
}
|
|
|
|
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 int 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 int 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 *chip, unsigned int offset,
|
|
unsigned long config)
|
|
{
|
|
struct rp1_pinctrl *pc = gpiochip_get_data(chip);
|
|
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 irq_chip *host_chip = irq_desc_get_chip(desc);
|
|
struct rp1_pinctrl *pc = gpiochip_get_data(chip);
|
|
const struct rp1_iobank_desc *bank;
|
|
int irq = irq_desc_get_irq(desc);
|
|
unsigned long ints;
|
|
int bit_pos;
|
|
|
|
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(bit_pos, &ints, 32) {
|
|
struct rp1_pin_info *pin = rp1_get_pin(chip, bit_pos);
|
|
|
|
regmap_field_write(pin->gpio[RP1_GPIO_CTRL_IRQRESET_SET], 1);
|
|
generic_handle_irq(irq_find_mapping(pc->gpio_chip.irq.domain,
|
|
bank->gpio_offset + bit_pos));
|
|
}
|
|
|
|
chained_irq_exit(host_chip, desc);
|
|
}
|
|
|
|
static void rp1_gpio_irq_config(struct rp1_pin_info *pin, bool enable)
|
|
{
|
|
int reg = enable ? RP1_INTE_SET : RP1_INTE_CLR;
|
|
|
|
regmap_field_write(pin->inte[reg], 1);
|
|
if (!enable)
|
|
/* Clear any latched events */
|
|
regmap_field_write(pin->gpio[RP1_GPIO_CTRL_IRQRESET_SET], 1);
|
|
}
|
|
|
|
static void rp1_gpio_irq_enable(struct irq_data *data)
|
|
{
|
|
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
|
unsigned int 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 int 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 */
|
|
regmap_field_write(pin->gpio[RP1_GPIO_CTRL_INT_CLR], RP1_INT_MASK);
|
|
|
|
/* Set those that are needed */
|
|
regmap_field_write(pin->gpio[RP1_GPIO_CTRL_INT_SET], irq_flags);
|
|
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);
|
|
unsigned int gpio = irqd_to_hwirq(data);
|
|
struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
|
|
struct rp1_pinctrl *pc = gpiochip_get_data(chip);
|
|
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 int gpio = irqd_to_hwirq(data);
|
|
struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
|
|
|
|
/* Clear any latched events */
|
|
regmap_field_write(pin->gpio[RP1_GPIO_CTRL_IRQRESET_SET], 1);
|
|
}
|
|
|
|
static int rp1_gpio_irq_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force)
|
|
{
|
|
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
|
struct rp1_pinctrl *pc = gpiochip_get_data(chip);
|
|
const struct rp1_iobank_desc *bank;
|
|
struct irq_data *parent_data = NULL;
|
|
int i;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
bank = &rp1_iobanks[i];
|
|
if (data->hwirq >= bank->min_gpio &&
|
|
data->hwirq < bank->min_gpio + bank->num_gpios) {
|
|
parent_data = irq_get_irq_data(pc->irq[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (parent_data && parent_data->chip->irq_set_affinity)
|
|
return parent_data->chip->irq_set_affinity(parent_data, dest, force);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
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,
|
|
.irq_set_affinity = rp1_gpio_irq_set_affinity,
|
|
.flags = IRQCHIP_IMMUTABLE,
|
|
GPIOCHIP_IRQ_RESOURCE_HELPERS,
|
|
};
|
|
|
|
static int rp1_pctl_get_groups_count(struct pinctrl_dev *pctldev)
|
|
{
|
|
return ARRAY_SIZE(rp1_gpio_groups) + ARRAY_SIZE(rp1_gpio_pins);
|
|
}
|
|
|
|
static const char *rp1_pctl_get_group_name(struct pinctrl_dev *pctldev,
|
|
unsigned int selector)
|
|
{
|
|
unsigned int ngroups = ARRAY_SIZE(rp1_gpio_groups);
|
|
|
|
if (selector < ngroups)
|
|
return rp1_gpio_groups[selector].name;
|
|
|
|
return rp1_gpio_pins[selector - ngroups].name;
|
|
}
|
|
|
|
static enum funcs rp1_get_fsel_func(unsigned int pin, unsigned int 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 int selector,
|
|
const unsigned int **pins,
|
|
unsigned int *num_pins)
|
|
{
|
|
unsigned int ngroups = ARRAY_SIZE(rp1_gpio_groups);
|
|
|
|
if (selector < ngroups) {
|
|
*pins = rp1_gpio_groups[selector].pins;
|
|
*num_pins = rp1_gpio_groups[selector].npins;
|
|
} else {
|
|
*pins = &rp1_gpio_pins[selector - ngroups].number;
|
|
*num_pins = 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rp1_pctl_pin_dbg_show(struct pinctrl_dev *pctldev,
|
|
struct seq_file *s,
|
|
unsigned int 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].name, rp1_func_names[func].name,
|
|
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 int 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;
|
|
}
|
|
|
|
if (pin < ARRAY_SIZE(legacy_fsel_map)) {
|
|
func = legacy_fsel_map[pin][fnum];
|
|
} else if (fnum < 2) {
|
|
func = func_gpio;
|
|
} else {
|
|
dev_err(pc->dev, "%pOF: invalid brcm,pins value %d\n",
|
|
np, pin);
|
|
return -EINVAL;
|
|
}
|
|
|
|
map->type = PIN_MAP_TYPE_MUX_GROUP;
|
|
map->data.mux.group = rp1_pctl_get_group_name(pc->pctl_dev,
|
|
ARRAY_SIZE(rp1_gpio_groups)
|
|
+ pin);
|
|
map->data.mux.function = rp1_func_names[func].name;
|
|
(*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 (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].name,
|
|
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].name,
|
|
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 int offset)
|
|
{
|
|
struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset);
|
|
u32 fsel = rp1_get_fsel(pin);
|
|
|
|
/* Return all pins to GPIO_IN, unless persist_gpio_outputs is set */
|
|
if (persist_gpio_outputs && fsel == RP1_FSEL_GPIO)
|
|
return 0;
|
|
|
|
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 int selector)
|
|
{
|
|
return (selector < func_count) ? rp1_func_names[selector].name : NULL;
|
|
}
|
|
|
|
static int rp1_pmx_get_function_groups(struct pinctrl_dev *pctldev,
|
|
unsigned int selector,
|
|
const char * const **groups,
|
|
unsigned * const num_groups)
|
|
{
|
|
*groups = rp1_func_names[selector].groups;
|
|
*num_groups = rp1_func_names[selector].ngroups;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rp1_pmx_set(struct pinctrl_dev *pctldev, unsigned int func_selector,
|
|
unsigned int group_selector)
|
|
{
|
|
struct rp1_pin_info *pin;
|
|
const unsigned int *pins;
|
|
const u8 *pin_funcs;
|
|
unsigned int num_pins;
|
|
int offset, fsel;
|
|
|
|
rp1_pctl_get_group_pins(pctldev, group_selector, &pins, &num_pins);
|
|
|
|
for (offset = 0; offset < num_pins; ++offset) {
|
|
pin = rp1_get_pin_pctl(pctldev, pins[offset]);
|
|
/* 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 int offset)
|
|
{
|
|
(void)rp1_pmx_free(pctldev, offset);
|
|
}
|
|
|
|
static int rp1_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
|
|
struct pinctrl_gpio_range *range,
|
|
unsigned int 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)
|
|
{
|
|
regmap_field_write(pin->pad[RP1_PAD_PULL], arg & 0x3);
|
|
}
|
|
|
|
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:
|
|
regmap_field_write(pin->pad[RP1_PAD_SCHMITT], !!arg);
|
|
break;
|
|
|
|
case PIN_CONFIG_SLEW_RATE:
|
|
regmap_field_write(pin->pad[RP1_PAD_SLEWFAST], !!arg);
|
|
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;
|
|
}
|
|
regmap_field_write(pin->pad[RP1_PAD_DRIVE], arg);
|
|
break;
|
|
|
|
default:
|
|
return -ENOTSUPP;
|
|
|
|
} /* switch param type */
|
|
} /* for each config */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rp1_pinconf_get(struct pinctrl_dev *pctldev, unsigned int 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;
|
|
|
|
switch (param) {
|
|
case PIN_CONFIG_INPUT_ENABLE:
|
|
regmap_field_read(pin->pad[RP1_PAD_IN_ENABLE], &padctrl);
|
|
arg = !!padctrl;
|
|
break;
|
|
case PIN_CONFIG_OUTPUT_ENABLE:
|
|
regmap_field_read(pin->pad[RP1_PAD_OUT_DISABLE], &padctrl);
|
|
arg = !padctrl;
|
|
break;
|
|
case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
|
|
regmap_field_read(pin->pad[RP1_PAD_SCHMITT], &padctrl);
|
|
arg = !!padctrl;
|
|
break;
|
|
case PIN_CONFIG_SLEW_RATE:
|
|
regmap_field_read(pin->pad[RP1_PAD_SLEWFAST], &padctrl);
|
|
arg = !!padctrl;
|
|
break;
|
|
case PIN_CONFIG_DRIVE_STRENGTH:
|
|
regmap_field_read(pin->pad[RP1_PAD_DRIVE], &padctrl);
|
|
switch (padctrl) {
|
|
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:
|
|
regmap_field_read(pin->pad[RP1_PAD_PULL], &padctrl);
|
|
arg = ((padctrl == RP1_PUD_OFF));
|
|
break;
|
|
case PIN_CONFIG_BIAS_PULL_DOWN:
|
|
regmap_field_read(pin->pad[RP1_PAD_PULL], &padctrl);
|
|
arg = ((padctrl == RP1_PUD_DOWN));
|
|
break;
|
|
|
|
case PIN_CONFIG_BIAS_PULL_UP:
|
|
regmap_field_read(pin->pad[RP1_PAD_PULL], &padctrl);
|
|
arg = ((padctrl == RP1_PUD_UP));
|
|
break;
|
|
default:
|
|
return -ENOTSUPP;
|
|
}
|
|
|
|
*config = pinconf_to_config_packed(param, arg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rp1_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned int selector,
|
|
unsigned long *config)
|
|
{
|
|
const unsigned int *pins;
|
|
unsigned int npins;
|
|
int ret;
|
|
|
|
ret = rp1_pctl_get_group_pins(pctldev, selector, &pins, &npins);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (!npins)
|
|
return -ENODEV;
|
|
|
|
ret = rp1_pinconf_get(pctldev, pins[0], config);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rp1_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned int selector,
|
|
unsigned long *configs, unsigned int num_configs)
|
|
{
|
|
const unsigned int *pins;
|
|
unsigned int npins;
|
|
int ret, i;
|
|
|
|
ret = rp1_pctl_get_group_pins(pctldev, selector, &pins, &npins);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
for (i = 0; i < npins; i++) {
|
|
ret = rp1_pinconf_set(pctldev, pins[i], configs, num_configs);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
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,
|
|
.pin_config_group_get = rp1_pinconf_group_get,
|
|
.pin_config_group_set = rp1_pinconf_group_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,
|
|
},
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, rp1_pinctrl_match);
|
|
|
|
static struct rp1_pinctrl rp1_pinctrl_data = {};
|
|
|
|
static const struct regmap_config rp1_pinctrl_regmap_cfg = {
|
|
.reg_bits = 32,
|
|
.val_bits = 32,
|
|
.reg_stride = 4,
|
|
.fast_io = true,
|
|
.name = "rp1-pinctrl",
|
|
};
|
|
|
|
static int rp1_gen_regfield(struct device *dev,
|
|
const struct reg_field *array,
|
|
size_t array_size,
|
|
int reg_off,
|
|
int pin_off,
|
|
bool additive_offset,
|
|
struct regmap *regmap,
|
|
struct regmap_field *out[])
|
|
{
|
|
struct reg_field regfield;
|
|
int k;
|
|
|
|
for (k = 0; k < array_size; k++) {
|
|
regfield = array[k];
|
|
regfield.reg = (additive_offset ? regfield.reg : 0) + reg_off;
|
|
if (pin_off >= 0) {
|
|
regfield.lsb = pin_off;
|
|
regfield.msb = regfield.lsb;
|
|
}
|
|
out[k] = devm_regmap_field_alloc(dev, regmap, regfield);
|
|
|
|
if (IS_ERR(out[k]))
|
|
return PTR_ERR(out[k]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rp1_pinctrl_probe(struct platform_device *pdev)
|
|
{
|
|
struct regmap *gpio_regmap, *rio_regmap, *pads_regmap;
|
|
struct rp1_pinctrl *pc = &rp1_pinctrl_data;
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = dev->of_node;
|
|
struct gpio_irq_chip *girq;
|
|
int err, i;
|
|
|
|
pc->dev = dev;
|
|
pc->gpio_chip = rp1_gpio_chip;
|
|
pc->gpio_chip.parent = dev;
|
|
|
|
pc->gpio_base = devm_platform_ioremap_resource(pdev, 0);
|
|
if (IS_ERR(pc->gpio_base))
|
|
return dev_err_probe(dev, PTR_ERR(pc->gpio_base), "could not get GPIO IO memory\n");
|
|
|
|
pc->rio_base = devm_platform_ioremap_resource(pdev, 1);
|
|
if (IS_ERR(pc->rio_base))
|
|
return dev_err_probe(dev, PTR_ERR(pc->rio_base), "could not get RIO IO memory\n");
|
|
|
|
pc->pads_base = devm_platform_ioremap_resource(pdev, 2);
|
|
if (IS_ERR(pc->pads_base))
|
|
return dev_err_probe(dev, PTR_ERR(pc->pads_base), "could not get PADS IO memory\n");
|
|
|
|
gpio_regmap = devm_regmap_init_mmio(dev, pc->gpio_base,
|
|
&rp1_pinctrl_regmap_cfg);
|
|
if (IS_ERR(gpio_regmap))
|
|
return dev_err_probe(dev, PTR_ERR(gpio_regmap), "could not init GPIO regmap\n");
|
|
|
|
rio_regmap = devm_regmap_init_mmio(dev, pc->rio_base,
|
|
&rp1_pinctrl_regmap_cfg);
|
|
if (IS_ERR(rio_regmap))
|
|
return dev_err_probe(dev, PTR_ERR(rio_regmap), "could not init RIO regmap\n");
|
|
|
|
pads_regmap = devm_regmap_init_mmio(dev, pc->pads_base,
|
|
&rp1_pinctrl_regmap_cfg);
|
|
if (IS_ERR(pads_regmap))
|
|
return dev_err_probe(dev, PTR_ERR(pads_regmap), "could not init PADS regmap\n");
|
|
|
|
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];
|
|
int reg_off;
|
|
|
|
pin->num = bank->min_gpio + j;
|
|
pin->bank = i;
|
|
pin->offset = j;
|
|
|
|
reg_off = bank->gpio_offset + pin->offset *
|
|
sizeof(u32) * 2;
|
|
err = rp1_gen_regfield(dev,
|
|
rp1_gpio_fields,
|
|
ARRAY_SIZE(rp1_gpio_fields),
|
|
reg_off,
|
|
-1,
|
|
true,
|
|
gpio_regmap,
|
|
pin->gpio);
|
|
|
|
if (err)
|
|
return dev_err_probe(dev, err,
|
|
"Unable to allocate regmap for gpio\n");
|
|
|
|
reg_off = bank->inte_offset;
|
|
err = rp1_gen_regfield(dev,
|
|
rp1_inte_fields,
|
|
ARRAY_SIZE(rp1_inte_fields),
|
|
reg_off,
|
|
pin->offset,
|
|
true,
|
|
gpio_regmap,
|
|
pin->inte);
|
|
|
|
if (err)
|
|
return dev_err_probe(dev, err,
|
|
"Unable to allocate regmap for inte\n");
|
|
|
|
reg_off = bank->rio_offset;
|
|
err = rp1_gen_regfield(dev,
|
|
rp1_rio_fields,
|
|
ARRAY_SIZE(rp1_rio_fields),
|
|
reg_off,
|
|
pin->offset,
|
|
true,
|
|
rio_regmap,
|
|
pin->rio);
|
|
|
|
if (err)
|
|
return dev_err_probe(dev, err,
|
|
"Unable to allocate regmap for rio\n");
|
|
|
|
reg_off = bank->pads_offset + pin->offset * sizeof(u32);
|
|
err = rp1_gen_regfield(dev,
|
|
rp1_pad_fields,
|
|
ARRAY_SIZE(rp1_pad_fields),
|
|
reg_off,
|
|
-1,
|
|
false,
|
|
pads_regmap,
|
|
pin->pad);
|
|
|
|
if (err)
|
|
return dev_err_probe(dev, err,
|
|
"Unable to allocate regmap for pad\n");
|
|
}
|
|
|
|
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 dev_err_probe(dev, PTR_ERR(pc->pctl_dev),
|
|
"Could not register pin controller\n");
|
|
|
|
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;
|
|
girq->default_type = IRQ_TYPE_NONE;
|
|
girq->handler = handle_level_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;
|
|
}
|
|
}
|
|
|
|
platform_set_drvdata(pdev, pc);
|
|
|
|
err = devm_gpiochip_add_data(dev, &pc->gpio_chip, pc);
|
|
if (err)
|
|
return dev_err_probe(dev, err, "could not add GPIO chip\n");
|
|
|
|
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,
|
|
},
|
|
};
|
|
module_platform_driver(rp1_pinctrl_driver);
|
|
|
|
MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>");
|
|
MODULE_AUTHOR("Andrea della Porta <andrea.porta@suse.com>");
|
|
MODULE_DESCRIPTION("RP1 pinctrl/gpio driver");
|
|
MODULE_LICENSE("GPL");
|