| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Intel Thunder Bay SOC pinctrl/GPIO driver |
| * |
| * Copyright (C) 2021 Intel Corporation |
| */ |
| |
| #include <linux/device.h> |
| #include <linux/err.h> |
| #include <linux/gpio/driver.h> |
| #include <linux/init.h> |
| #include <linux/interrupt.h> |
| #include <linux/io.h> |
| #include <linux/irq.h> |
| #include <linux/module.h> |
| #include <linux/of.h> |
| #include <linux/of_irq.h> |
| |
| #include <linux/pinctrl/pinconf.h> |
| #include <linux/pinctrl/pinconf-generic.h> |
| #include <linux/pinctrl/pinctrl.h> |
| #include <linux/pinctrl/pinmux.h> |
| |
| #include <linux/platform_device.h> |
| #include <linux/slab.h> |
| #include <linux/spinlock.h> |
| |
| #include "core.h" |
| #include "pinconf.h" |
| #include "pinctrl-utils.h" |
| #include "pinmux.h" |
| |
| /* Bit 0:2 and 4:6 should be used for mode selection */ |
| #define THB_GPIO_PINMUX_MODE_0 0x00 |
| #define THB_GPIO_PINMUX_MODE_1 0x11 |
| #define THB_GPIO_PINMUX_MODE_2 0x22 |
| #define THB_GPIO_PINMUX_MODE_3 0x33 |
| #define THB_GPIO_PINMUX_MODE_4 0x44 |
| |
| #define THB_GPIO_PORT_SELECT_MASK BIT(8) |
| #define THB_GPIO_PAD_DIRECTION_MASK BIT(10) |
| #define THB_GPIO_SPU_MASK BIT(11) |
| #define THB_GPIO_PULL_ENABLE_MASK BIT(12) |
| #define THB_GPIO_PULL_UP_MASK BIT(13) |
| #define THB_GPIO_PULL_DOWN_MASK BIT(14) |
| #define THB_GPIO_ENAQ_MASK BIT(15) |
| /* bit 16-19: Drive Strength for the Pad */ |
| #define THB_GPIO_DRIVE_STRENGTH_MASK (0xF0000) |
| #define THB_GPIO_SLEW_RATE_MASK BIT(20) |
| #define THB_GPIO_SCHMITT_TRIGGER_MASK BIT(21) |
| |
| #define THB_GPIO_REG_OFFSET(pin_num) ((pin_num) * (0x4)) |
| #define THB_MAX_MODE_SUPPORTED (5u) |
| #define THB_MAX_NPINS_SUPPORTED (67u) |
| |
| /* store Pin status */ |
| static u32 thb_pinx_status[THB_MAX_NPINS_SUPPORTED]; |
| |
| struct thunderbay_mux_desc { |
| u8 mode; |
| const char *name; |
| }; |
| |
| #define THUNDERBAY_PIN_DESC(pin_number, pin_name, ...) { \ |
| .number = pin_number, \ |
| .name = pin_name, \ |
| .drv_data = &(struct thunderbay_mux_desc[]) { \ |
| __VA_ARGS__, { } }, \ |
| } |
| |
| #define THUNDERBAY_MUX(pin_mode, pin_function) { \ |
| .mode = pin_mode, \ |
| .name = pin_function, \ |
| } |
| |
| struct thunderbay_pin_soc { |
| const struct pinctrl_pin_desc *pins; |
| unsigned int npins; |
| }; |
| |
| /** |
| * struct thunderbay_pinctrl - Intel Thunderbay pinctrl structure |
| * @pctrl: Pointer to the pin controller device |
| * @base0: First register base address |
| * @dev: Pointer to the device structure |
| * @chip: GPIO chip used by this pin controller |
| * @soc: Pin control configuration data based on SoC |
| * @ngroups: Number of pin groups available |
| * @nfuncs: Number of pin functions available |
| */ |
| struct thunderbay_pinctrl { |
| struct pinctrl_dev *pctrl; |
| void __iomem *base0; |
| struct device *dev; |
| struct gpio_chip chip; |
| const struct thunderbay_pin_soc *soc; |
| unsigned int ngroups; |
| unsigned int nfuncs; |
| }; |
| |
| static const struct pinctrl_pin_desc thunderbay_pins[] = { |
| THUNDERBAY_PIN_DESC(0, "GPIO0", |
| THUNDERBAY_MUX(0X0, "I2C0_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(1, "GPIO1", |
| THUNDERBAY_MUX(0X0, "I2C0_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(2, "GPIO2", |
| THUNDERBAY_MUX(0X0, "I2C1_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(3, "GPIO3", |
| THUNDERBAY_MUX(0X0, "I2C1_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(4, "GPIO4", |
| THUNDERBAY_MUX(0X0, "I2C2_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(5, "GPIO5", |
| THUNDERBAY_MUX(0X0, "I2C2_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(6, "GPIO6", |
| THUNDERBAY_MUX(0X0, "I2C3_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(7, "GPIO7", |
| THUNDERBAY_MUX(0X0, "I2C3_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(8, "GPIO8", |
| THUNDERBAY_MUX(0X0, "I2C4_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(9, "GPIO9", |
| THUNDERBAY_MUX(0X0, "I2C4_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(10, "GPIO10", |
| THUNDERBAY_MUX(0X0, "UART0_M0"), |
| THUNDERBAY_MUX(0X1, "RT0_DSU_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(11, "GPIO11", |
| THUNDERBAY_MUX(0X0, "UART0_M0"), |
| THUNDERBAY_MUX(0X1, "RT0_DSU_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(12, "GPIO12", |
| THUNDERBAY_MUX(0X0, "UART0_M0"), |
| THUNDERBAY_MUX(0X1, "RT1_DSU_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(13, "GPIO13", |
| THUNDERBAY_MUX(0X0, "UART0_M0"), |
| THUNDERBAY_MUX(0X1, "RT1_DSU_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(14, "GPIO14", |
| THUNDERBAY_MUX(0X0, "UART1_M0"), |
| THUNDERBAY_MUX(0X1, "RT2_DSU_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "TRIGGER_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(15, "GPIO15", |
| THUNDERBAY_MUX(0X0, "UART1_M0"), |
| THUNDERBAY_MUX(0X1, "RT2_DSU_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "TRIGGER_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(16, "GPIO16", |
| THUNDERBAY_MUX(0X0, "UART1_M0"), |
| THUNDERBAY_MUX(0X1, "RT3_DSU_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(17, "GPIO17", |
| THUNDERBAY_MUX(0X0, "UART1_M0"), |
| THUNDERBAY_MUX(0X1, "RT3_DSU_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(18, "GPIO18", |
| THUNDERBAY_MUX(0X0, "SPI0_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(19, "GPIO19", |
| THUNDERBAY_MUX(0X0, "SPI0_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(20, "GPIO20", |
| THUNDERBAY_MUX(0X0, "SPI0_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_TRACE_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(21, "GPIO21", |
| THUNDERBAY_MUX(0X0, "SPI0_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_TRACE_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(22, "GPIO22", |
| THUNDERBAY_MUX(0X0, "SPI1_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M0"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(23, "GPIO23", |
| THUNDERBAY_MUX(0X0, "SPI1_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(24, "GPIO24", |
| THUNDERBAY_MUX(0X0, "SPI1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_TRACE_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(25, "GPIO25", |
| THUNDERBAY_MUX(0X0, "SPI1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_TRACE_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(26, "GPIO26", |
| THUNDERBAY_MUX(0X0, "ETHER0_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DEBUG_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(27, "GPIO27", |
| THUNDERBAY_MUX(0X0, "ETHER0_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DEBUG_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(28, "GPIO28", |
| THUNDERBAY_MUX(0X0, "ETHER0_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DEBUG_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(29, "GPIO29", |
| THUNDERBAY_MUX(0X0, "ETHER0_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DEBUG_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(30, "GPIO30", |
| THUNDERBAY_MUX(0X0, "ETHER0_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DEBUG_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(31, "GPIO31", |
| THUNDERBAY_MUX(0X0, "ETHER0_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DEBUG_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(32, "GPIO32", |
| THUNDERBAY_MUX(0X0, "ETHER0_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DEBUG_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(33, "GPIO33", |
| THUNDERBAY_MUX(0X0, "ETHER0_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DEBUG_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(34, "GPIO34", |
| THUNDERBAY_MUX(0X0, "ETHER0_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DIG_VIEW_0"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(35, "GPIO35", |
| THUNDERBAY_MUX(0X0, "ETHER0_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DIG_VIEW_1"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(36, "GPIO36", |
| THUNDERBAY_MUX(0X0, "ETHER0_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "CPR_IO_OUT_CLK_0"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(37, "GPIO37", |
| THUNDERBAY_MUX(0X0, "ETHER0_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "CPR_IO_OUT_CLK_1"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(38, "GPIO38", |
| THUNDERBAY_MUX(0X0, "ETHER0_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "CPR_IO_OUT_CLK_2"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(39, "GPIO39", |
| THUNDERBAY_MUX(0X0, "ETHER0_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "CPR_IO_OUT_CLK_3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(40, "GPIO40", |
| THUNDERBAY_MUX(0X0, "ETHER0_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(41, "GPIO41", |
| THUNDERBAY_MUX(0X0, "POWER_INTERRUPT_MAX_PLATFORM_POWER_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(42, "GPIO42", |
| THUNDERBAY_MUX(0X0, "ETHER1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DEBUG_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(43, "GPIO43", |
| THUNDERBAY_MUX(0X0, "ETHER1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DEBUG_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(44, "GPIO44", |
| THUNDERBAY_MUX(0X0, "ETHER1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DEBUG_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(45, "GPIO45", |
| THUNDERBAY_MUX(0X0, "ETHER1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DEBUG_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(46, "GPIO46", |
| THUNDERBAY_MUX(0X0, "ETHER1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DEBUG_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(47, "GPIO47", |
| THUNDERBAY_MUX(0X0, "ETHER1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DEBUG_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(48, "GPIO48", |
| THUNDERBAY_MUX(0X0, "ETHER1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DEBUG_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(49, "GPIO49", |
| THUNDERBAY_MUX(0X0, "ETHER1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DEBUG_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(50, "GPIO50", |
| THUNDERBAY_MUX(0X0, "ETHER1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DIG_VIEW_0"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(51, "GPIO51", |
| THUNDERBAY_MUX(0X0, "ETHER1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "DIG_VIEW_1"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(52, "GPIO52", |
| THUNDERBAY_MUX(0X0, "ETHER1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "CPR_IO_OUT_CLK_0"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(53, "GPIO53", |
| THUNDERBAY_MUX(0X0, "ETHER1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "CPR_IO_OUT_CLK_1"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(54, "GPIO54", |
| THUNDERBAY_MUX(0X0, "ETHER1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "CPR_IO_OUT_CLK_2"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(55, "GPIO55", |
| THUNDERBAY_MUX(0X0, "ETHER1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "CPR_IO_OUT_CLK_3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(56, "GPIO56", |
| THUNDERBAY_MUX(0X0, "ETHER1_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "POWER_INTERRUPT_ICCMAX_VDDD_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(57, "GPIO57", |
| THUNDERBAY_MUX(0X0, "POWER_INTERRUPT_ICCMAX_VPU_M0"), |
| THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"), |
| THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(58, "GPIO58", |
| THUNDERBAY_MUX(0X0, "THERMTRIP_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(59, "GPIO59", |
| THUNDERBAY_MUX(0X0, "THERMTRIP_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(60, "GPIO60", |
| THUNDERBAY_MUX(0X0, "SMBUS_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(61, "GPIO61", |
| THUNDERBAY_MUX(0X0, "SMBUS_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "POWER_INTERRUPT_ICCMAX_VDDD_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(62, "GPIO62", |
| THUNDERBAY_MUX(0X0, "PLATFORM_RESET_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(63, "GPIO63", |
| THUNDERBAY_MUX(0X0, "PLATFORM_RESET_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(64, "GPIO64", |
| THUNDERBAY_MUX(0X0, "PLATFORM_SHUTDOWN_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(65, "GPIO65", |
| THUNDERBAY_MUX(0X0, "PLATFORM_SHUTDOWN_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| THUNDERBAY_PIN_DESC(66, "GPIO66", |
| THUNDERBAY_MUX(0X0, "POWER_INTERRUPT_ICCMAX_MEDIA_M0"), |
| THUNDERBAY_MUX(0X1, "EMPTY_M1"), |
| THUNDERBAY_MUX(0X2, "EMPTY_M2"), |
| THUNDERBAY_MUX(0X3, "EMPTY_M3"), |
| THUNDERBAY_MUX(0X4, "GPIO_M4")), |
| }; |
| |
| static const struct thunderbay_pin_soc thunderbay_data = { |
| .pins = thunderbay_pins, |
| .npins = ARRAY_SIZE(thunderbay_pins), |
| }; |
| |
| static u32 thb_gpio_read_reg(struct gpio_chip *chip, unsigned int pinnr) |
| { |
| struct thunderbay_pinctrl *tpc = gpiochip_get_data(chip); |
| |
| return readl(tpc->base0 + THB_GPIO_REG_OFFSET(pinnr)); |
| } |
| |
| static u32 thb_gpio_write_reg(struct gpio_chip *chip, unsigned int pinnr, u32 value) |
| { |
| struct thunderbay_pinctrl *tpc = gpiochip_get_data(chip); |
| |
| writel(value, (tpc->base0 + THB_GPIO_REG_OFFSET(pinnr))); |
| return 0; |
| } |
| |
| static int thb_read_gpio_data(struct gpio_chip *chip, unsigned int offset, unsigned int pad_dir) |
| { |
| int data_offset; |
| u32 data_reg; |
| |
| /* as per GPIO Spec = pad_dir 0:input, 1:output */ |
| data_offset = 0x2000u + (offset / 32); |
| if (!pad_dir) |
| data_offset += 4; |
| data_reg = thb_gpio_read_reg(chip, data_offset); |
| |
| return data_reg & BIT(offset % 32); |
| } |
| |
| static int thb_write_gpio_data(struct gpio_chip *chip, unsigned int offset, unsigned int value) |
| { |
| int data_offset; |
| u32 data_reg; |
| |
| data_offset = 0x2000u + (offset / 32); |
| |
| data_reg = thb_gpio_read_reg(chip, data_offset); |
| |
| if (value > 0) |
| data_reg |= BIT(offset % 32); |
| else |
| data_reg &= ~BIT(offset % 32); |
| |
| return thb_gpio_write_reg(chip, data_offset, data_reg); |
| } |
| |
| static int thunderbay_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) |
| { |
| u32 reg = thb_gpio_read_reg(chip, offset); |
| |
| /* Return direction only if configured as GPIO else negative error */ |
| if (reg & THB_GPIO_PORT_SELECT_MASK) |
| return !(reg & THB_GPIO_PAD_DIRECTION_MASK); |
| return -EINVAL; |
| } |
| |
| static int thunderbay_gpio_set_direction_input(struct gpio_chip *chip, unsigned int offset) |
| { |
| u32 reg = thb_gpio_read_reg(chip, offset); |
| |
| /* set pin as input only if it is GPIO else error */ |
| if (reg & THB_GPIO_PORT_SELECT_MASK) { |
| reg &= (~THB_GPIO_PAD_DIRECTION_MASK); |
| thb_gpio_write_reg(chip, offset, reg); |
| return 0; |
| } |
| return -EINVAL; |
| } |
| |
| static void thunderbay_gpio_set_value(struct gpio_chip *chip, unsigned int offset, int value) |
| { |
| u32 reg = thb_gpio_read_reg(chip, offset); |
| |
| /* update pin value only if it is GPIO-output else error */ |
| if ((reg & THB_GPIO_PORT_SELECT_MASK) && (reg & THB_GPIO_PAD_DIRECTION_MASK)) |
| thb_write_gpio_data(chip, offset, value); |
| } |
| |
| static int thunderbay_gpio_set_direction_output(struct gpio_chip *chip, |
| unsigned int offset, int value) |
| { |
| u32 reg = thb_gpio_read_reg(chip, offset); |
| |
| /* set pin as output only if it is GPIO else error */ |
| if (reg & THB_GPIO_PORT_SELECT_MASK) { |
| reg |= THB_GPIO_PAD_DIRECTION_MASK; |
| thb_gpio_write_reg(chip, offset, reg); |
| thunderbay_gpio_set_value(chip, offset, value); |
| return 0; |
| } |
| return -EINVAL; |
| } |
| |
| static int thunderbay_gpio_get_value(struct gpio_chip *chip, unsigned int offset) |
| { |
| u32 reg = thb_gpio_read_reg(chip, offset); |
| int gpio_dir = 0; |
| |
| /* Read pin value only if it is GPIO else error */ |
| if (reg & THB_GPIO_PORT_SELECT_MASK) { |
| /* 0=in, 1=out */ |
| gpio_dir = (reg & THB_GPIO_PAD_DIRECTION_MASK) > 0; |
| |
| /* Returns negative value when pin is configured as PORT */ |
| return thb_read_gpio_data(chip, offset, gpio_dir); |
| } |
| return -EINVAL; |
| } |
| |
| static int thunderbay_gpiochip_probe(struct thunderbay_pinctrl *tpc) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| int ret; |
| |
| chip->label = dev_name(tpc->dev); |
| chip->parent = tpc->dev; |
| chip->request = gpiochip_generic_request; |
| chip->free = gpiochip_generic_free; |
| chip->get_direction = thunderbay_gpio_get_direction; |
| chip->direction_input = thunderbay_gpio_set_direction_input; |
| chip->direction_output = thunderbay_gpio_set_direction_output; |
| chip->get = thunderbay_gpio_get_value; |
| chip->set = thunderbay_gpio_set_value; |
| chip->set_config = gpiochip_generic_config; |
| /* identifies the first GPIO number handled by this chip; or, |
| * if negative during registration, requests dynamic ID allocation. |
| * Please pass -1 as base to let gpiolib select the chip base in all possible cases. |
| * We want to get rid of the static GPIO number space in the long run. |
| */ |
| chip->base = -1; |
| /* Number of GPIOs handled by this controller; the last GPIO handled is (base + ngpio - 1)*/ |
| chip->ngpio = THB_MAX_NPINS_SUPPORTED; |
| |
| /* Register/add Thunder Bay GPIO chip with Linux framework */ |
| ret = gpiochip_add_data(chip, tpc); |
| if (ret) |
| dev_err(tpc->dev, "Failed to add gpiochip\n"); |
| return ret; |
| } |
| |
| static int thunderbay_request_gpio(struct pinctrl_dev *pctldev, |
| struct pinctrl_gpio_range *range, |
| unsigned int pin) |
| { |
| struct thunderbay_pinctrl *tpc = pinctrl_dev_get_drvdata(pctldev); |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg = 0; |
| |
| if (thb_pinx_status[pin] == 0u) { |
| reg = thb_gpio_read_reg(chip, pin); |
| /* Updates PIN configuration as GPIO and sets GPIO to MODE-4*/ |
| reg |= (THB_GPIO_PORT_SELECT_MASK | THB_GPIO_PINMUX_MODE_4); |
| thb_gpio_write_reg(chip, pin, reg); |
| |
| /* update pin status as busy */ |
| thb_pinx_status[pin] = 1u; |
| |
| return 0; |
| } |
| return -EINVAL; |
| } |
| |
| static void thunderbay_free_gpio(struct pinctrl_dev *pctldev, |
| struct pinctrl_gpio_range *range, |
| unsigned int pin) |
| { |
| struct thunderbay_pinctrl *tpc = pinctrl_dev_get_drvdata(pctldev); |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg = 0; |
| |
| if (thb_pinx_status[pin] == 1u) { |
| reg = thb_gpio_read_reg(chip, pin); |
| |
| /* Updates PIN configuration from GPIO to PORT */ |
| reg &= (~THB_GPIO_PORT_SELECT_MASK); |
| |
| /* Change Port/gpio mode to default mode-0 */ |
| reg &= (~THB_GPIO_PINMUX_MODE_4); |
| |
| thb_gpio_write_reg(chip, pin, reg); |
| |
| /* update pin status as free */ |
| thb_pinx_status[pin] = 0u; |
| } |
| } |
| |
| static int thb_pinctrl_set_mux(struct pinctrl_dev *pctldev, |
| unsigned int func_select, unsigned int group_select) |
| { |
| struct thunderbay_pinctrl *tpc = pinctrl_dev_get_drvdata(pctldev); |
| struct gpio_chip *chip = &tpc->chip; |
| struct function_desc *function; |
| unsigned int i, pin_mode; |
| struct group_desc *group; |
| int ret = -EINVAL; |
| u32 reg = 0u; |
| |
| group = pinctrl_generic_get_group(pctldev, group_select); |
| if (!group) |
| return -EINVAL; |
| |
| function = pinmux_generic_get_function(pctldev, func_select); |
| if (!function) |
| return -EINVAL; |
| |
| pin_mode = *(unsigned int *)(function->data); |
| |
| /* Change modes for pins in the selected group */ |
| for (i = 0; i < group->num_pins; i++) { |
| reg = thb_gpio_read_reg(chip, group->pins[i]); |
| |
| switch (pin_mode) { |
| case 0u: |
| reg |= THB_GPIO_PINMUX_MODE_0; |
| break; |
| case 1u: |
| reg |= THB_GPIO_PINMUX_MODE_1; |
| break; |
| case 2u: |
| reg |= THB_GPIO_PINMUX_MODE_2; |
| break; |
| case 3u: |
| reg |= THB_GPIO_PINMUX_MODE_3; |
| break; |
| case 4u: |
| reg |= THB_GPIO_PINMUX_MODE_4; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| ret = thb_gpio_write_reg(chip, group->pins[i], reg); |
| if (~ret) { |
| /* update pin status as busy */ |
| thb_pinx_status[group->pins[i]] = 1u; |
| } |
| } |
| return ret; |
| } |
| |
| static int thunderbay_build_groups(struct thunderbay_pinctrl *tpc) |
| { |
| struct group_desc *thunderbay_groups; |
| int i; |
| |
| tpc->ngroups = tpc->soc->npins; |
| thunderbay_groups = devm_kcalloc(tpc->dev, tpc->ngroups, |
| sizeof(*thunderbay_groups), GFP_KERNEL); |
| if (!thunderbay_groups) |
| return -ENOMEM; |
| |
| for (i = 0; i < tpc->ngroups; i++) { |
| struct group_desc *group = thunderbay_groups + i; |
| const struct pinctrl_pin_desc *pin_info = thunderbay_pins + i; |
| |
| group->name = pin_info->name; |
| group->pins = (int *)&pin_info->number; |
| pinctrl_generic_add_group(tpc->pctrl, group->name, |
| group->pins, 1, NULL); |
| } |
| return 0; |
| } |
| |
| static int thunderbay_add_functions(struct thunderbay_pinctrl *tpc, struct function_desc *funcs) |
| { |
| int i; |
| |
| /* Assign the groups for each function */ |
| for (i = 0; i < tpc->nfuncs; i++) { |
| struct function_desc *func = &funcs[i]; |
| const char **group_names; |
| unsigned int grp_idx = 0; |
| int j; |
| |
| group_names = devm_kcalloc(tpc->dev, func->num_group_names, |
| sizeof(*group_names), GFP_KERNEL); |
| if (!group_names) |
| return -ENOMEM; |
| |
| for (j = 0; j < tpc->soc->npins; j++) { |
| const struct pinctrl_pin_desc *pin_info = &thunderbay_pins[j]; |
| struct thunderbay_mux_desc *pin_mux; |
| |
| for (pin_mux = pin_info->drv_data; pin_mux->name; pin_mux++) { |
| if (!strcmp(pin_mux->name, func->name)) |
| group_names[grp_idx++] = pin_info->name; |
| } |
| } |
| |
| func->group_names = group_names; |
| } |
| |
| /* Add all functions */ |
| for (i = 0; i < tpc->nfuncs; i++) { |
| pinmux_generic_add_function(tpc->pctrl, |
| funcs[i].name, |
| funcs[i].group_names, |
| funcs[i].num_group_names, |
| funcs[i].data); |
| } |
| |
| return 0; |
| } |
| |
| static int thunderbay_build_functions(struct thunderbay_pinctrl *tpc) |
| { |
| struct function_desc *thunderbay_funcs; |
| void *ptr; |
| int pin; |
| int ret; |
| |
| /* |
| * Allocate maximum possible number of functions. Assume every pin |
| * being part of 8 (hw maximum) globally unique muxes. |
| */ |
| tpc->nfuncs = 0; |
| thunderbay_funcs = kcalloc(tpc->soc->npins * 8, |
| sizeof(*thunderbay_funcs), GFP_KERNEL); |
| if (!thunderbay_funcs) |
| return -ENOMEM; |
| |
| /* Setup 1 function for each unique mux */ |
| for (pin = 0; pin < tpc->soc->npins; pin++) { |
| const struct pinctrl_pin_desc *pin_info = thunderbay_pins + pin; |
| struct thunderbay_mux_desc *pin_mux; |
| |
| for (pin_mux = pin_info->drv_data; pin_mux->name; pin_mux++) { |
| struct function_desc *func; |
| |
| /* Check if we already have function for this mux */ |
| for (func = thunderbay_funcs; func->name; func++) { |
| if (!strcmp(pin_mux->name, func->name)) { |
| func->num_group_names++; |
| break; |
| } |
| } |
| |
| if (!func->name) { |
| func->name = pin_mux->name; |
| func->num_group_names = 1; |
| func->data = (int *)&pin_mux->mode; |
| tpc->nfuncs++; |
| } |
| } |
| } |
| |
| /* Reallocate memory based on actual number of functions */ |
| ptr = krealloc(thunderbay_funcs, |
| tpc->nfuncs * sizeof(*thunderbay_funcs), GFP_KERNEL); |
| if (!ptr) |
| return -ENOMEM; |
| |
| thunderbay_funcs = ptr; |
| ret = thunderbay_add_functions(tpc, thunderbay_funcs); |
| |
| kfree(thunderbay_funcs); |
| return ret; |
| } |
| |
| static int thunderbay_pinconf_set_tristate(struct thunderbay_pinctrl *tpc, |
| unsigned int pin, u32 config) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg; |
| |
| reg = thb_gpio_read_reg(chip, pin); |
| if (config > 0) |
| reg |= THB_GPIO_ENAQ_MASK; |
| else |
| reg &= ~THB_GPIO_ENAQ_MASK; |
| |
| return thb_gpio_write_reg(chip, pin, reg); |
| } |
| |
| static int thunderbay_pinconf_get_tristate(struct thunderbay_pinctrl *tpc, |
| unsigned int pin, u32 *config) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg; |
| |
| reg = thb_gpio_read_reg(chip, pin); |
| *config = (reg & THB_GPIO_ENAQ_MASK) > 0; |
| |
| return 0; |
| } |
| |
| static int thunderbay_pinconf_set_pulldown(struct thunderbay_pinctrl *tpc, |
| unsigned int pin, u32 config) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg; |
| |
| reg = thb_gpio_read_reg(chip, pin); |
| if (config > 0) |
| reg |= THB_GPIO_PULL_DOWN_MASK; |
| else |
| reg &= ~THB_GPIO_PULL_DOWN_MASK; |
| |
| return thb_gpio_write_reg(chip, pin, reg); |
| } |
| |
| static int thunderbay_pinconf_get_pulldown(struct thunderbay_pinctrl *tpc, |
| unsigned int pin, u32 *config) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg = 0; |
| |
| reg = thb_gpio_read_reg(chip, pin); |
| *config = ((reg & THB_GPIO_PULL_DOWN_MASK) > 0) ? 1 : 0; |
| |
| return 0; |
| } |
| |
| static int thunderbay_pinconf_set_pullup(struct thunderbay_pinctrl *tpc, |
| unsigned int pin, u32 config) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg; |
| |
| reg = thb_gpio_read_reg(chip, pin); |
| if (config > 0) |
| reg &= ~THB_GPIO_PULL_UP_MASK; |
| else |
| reg |= THB_GPIO_PULL_UP_MASK; |
| |
| return thb_gpio_write_reg(chip, pin, reg); |
| } |
| |
| static int thunderbay_pinconf_get_pullup(struct thunderbay_pinctrl *tpc, |
| unsigned int pin, u32 *config) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg; |
| |
| reg = thb_gpio_read_reg(chip, pin); |
| *config = ((reg & THB_GPIO_PULL_UP_MASK) == 0) ? 1 : 0; |
| |
| return 0; |
| } |
| |
| static int thunderbay_pinconf_set_opendrain(struct thunderbay_pinctrl *tpc, |
| unsigned int pin, u32 config) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg; |
| |
| reg = thb_gpio_read_reg(chip, pin); |
| if (config > 0) |
| reg &= ~THB_GPIO_PULL_ENABLE_MASK; |
| else |
| reg |= THB_GPIO_PULL_ENABLE_MASK; |
| |
| return thb_gpio_write_reg(chip, pin, reg); |
| } |
| |
| static int thunderbay_pinconf_get_opendrain(struct thunderbay_pinctrl *tpc, |
| unsigned int pin, u32 *config) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg; |
| |
| reg = thb_gpio_read_reg(chip, pin); |
| *config = ((reg & THB_GPIO_PULL_ENABLE_MASK) == 0) ? 1 : 0; |
| |
| return 0; |
| } |
| |
| static int thunderbay_pinconf_set_pushpull(struct thunderbay_pinctrl *tpc, |
| unsigned int pin, u32 config) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg; |
| |
| reg = thb_gpio_read_reg(chip, pin); |
| if (config > 0) |
| reg |= THB_GPIO_PULL_ENABLE_MASK; |
| else |
| reg &= ~THB_GPIO_PULL_ENABLE_MASK; |
| |
| return thb_gpio_write_reg(chip, pin, reg); |
| } |
| |
| static int thunderbay_pinconf_get_pushpull(struct thunderbay_pinctrl *tpc, |
| unsigned int pin, u32 *config) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg; |
| |
| reg = thb_gpio_read_reg(chip, pin); |
| *config = ((reg & THB_GPIO_PULL_ENABLE_MASK) > 0) ? 1 : 0; |
| |
| return 0; |
| } |
| |
| static int thunderbay_pinconf_set_drivestrength(struct thunderbay_pinctrl *tpc, |
| unsigned int pin, u32 config) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg; |
| |
| reg = thb_gpio_read_reg(chip, pin); |
| |
| /* Drive Strength: 0x0 to 0xF */ |
| if (config <= 0xF) { |
| reg = (reg | config); |
| return thb_gpio_write_reg(chip, pin, reg); |
| } |
| |
| return -EINVAL; |
| } |
| |
| static int thunderbay_pinconf_get_drivestrength(struct thunderbay_pinctrl *tpc, |
| unsigned int pin, u32 *config) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg; |
| |
| reg = thb_gpio_read_reg(chip, pin); |
| reg = (reg & THB_GPIO_DRIVE_STRENGTH_MASK) >> 16; |
| *config = (reg > 0) ? reg : 0; |
| |
| return 0; |
| } |
| |
| static int thunderbay_pinconf_set_schmitt(struct thunderbay_pinctrl *tpc, |
| unsigned int pin, u32 config) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg; |
| |
| reg = thb_gpio_read_reg(chip, pin); |
| if (config > 0) |
| reg |= THB_GPIO_SCHMITT_TRIGGER_MASK; |
| else |
| reg &= ~THB_GPIO_SCHMITT_TRIGGER_MASK; |
| |
| return thb_gpio_write_reg(chip, pin, reg); |
| } |
| |
| static int thunderbay_pinconf_get_schmitt(struct thunderbay_pinctrl *tpc, |
| unsigned int pin, u32 *config) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg; |
| |
| reg = thb_gpio_read_reg(chip, pin); |
| *config = ((reg & THB_GPIO_SCHMITT_TRIGGER_MASK) > 0) ? 1 : 0; |
| |
| return 0; |
| } |
| |
| static int thunderbay_pinconf_set_slew_rate(struct thunderbay_pinctrl *tpc, |
| unsigned int pin, u32 config) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg = 0; |
| |
| reg = thb_gpio_read_reg(chip, pin); |
| if (config > 0) |
| reg |= THB_GPIO_SLEW_RATE_MASK; |
| else |
| reg &= ~THB_GPIO_SLEW_RATE_MASK; |
| |
| return thb_gpio_write_reg(chip, pin, reg); |
| } |
| |
| static int thunderbay_pinconf_get_slew_rate(struct thunderbay_pinctrl *tpc, |
| unsigned int pin, u32 *config) |
| { |
| struct gpio_chip *chip = &tpc->chip; |
| u32 reg; |
| |
| reg = thb_gpio_read_reg(chip, pin); |
| *config = ((reg & THB_GPIO_SLEW_RATE_MASK) > 0) ? 1 : 0; |
| |
| return 0; |
| } |
| |
| static int thunderbay_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, |
| unsigned long *config) |
| { |
| struct thunderbay_pinctrl *tpc = pinctrl_dev_get_drvdata(pctldev); |
| enum pin_config_param param = pinconf_to_config_param(*config); |
| u32 arg; |
| int ret; |
| |
| switch (param) { |
| case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: |
| ret = thunderbay_pinconf_get_tristate(tpc, pin, &arg); |
| break; |
| |
| case PIN_CONFIG_BIAS_PULL_DOWN: |
| ret = thunderbay_pinconf_get_pulldown(tpc, pin, &arg); |
| break; |
| |
| case PIN_CONFIG_BIAS_PULL_UP: |
| ret = thunderbay_pinconf_get_pullup(tpc, pin, &arg); |
| break; |
| |
| case PIN_CONFIG_DRIVE_OPEN_DRAIN: |
| ret = thunderbay_pinconf_get_opendrain(tpc, pin, &arg); |
| break; |
| |
| case PIN_CONFIG_DRIVE_PUSH_PULL: |
| ret = thunderbay_pinconf_get_pushpull(tpc, pin, &arg); |
| break; |
| |
| case PIN_CONFIG_DRIVE_STRENGTH: |
| ret = thunderbay_pinconf_get_drivestrength(tpc, pin, &arg); |
| break; |
| |
| case PIN_CONFIG_INPUT_SCHMITT_ENABLE: |
| ret = thunderbay_pinconf_get_schmitt(tpc, pin, &arg); |
| break; |
| |
| case PIN_CONFIG_SLEW_RATE: |
| ret = thunderbay_pinconf_get_slew_rate(tpc, pin, &arg); |
| break; |
| |
| default: |
| return -ENOTSUPP; |
| } |
| |
| *config = pinconf_to_config_packed(param, arg); |
| |
| return ret; |
| } |
| |
| static int thunderbay_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, |
| unsigned long *configs, unsigned int num_configs) |
| { |
| struct thunderbay_pinctrl *tpc = pinctrl_dev_get_drvdata(pctldev); |
| enum pin_config_param param; |
| unsigned int pinconf; |
| int ret = 0; |
| u32 arg; |
| |
| for (pinconf = 0; pinconf < num_configs; pinconf++) { |
| param = pinconf_to_config_param(configs[pinconf]); |
| arg = pinconf_to_config_argument(configs[pinconf]); |
| |
| switch (param) { |
| case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: |
| ret = thunderbay_pinconf_set_tristate(tpc, pin, arg); |
| break; |
| |
| case PIN_CONFIG_BIAS_PULL_DOWN: |
| ret = thunderbay_pinconf_set_pulldown(tpc, pin, arg); |
| break; |
| |
| case PIN_CONFIG_BIAS_PULL_UP: |
| ret = thunderbay_pinconf_set_pullup(tpc, pin, arg); |
| break; |
| |
| case PIN_CONFIG_DRIVE_OPEN_DRAIN: |
| ret = thunderbay_pinconf_set_opendrain(tpc, pin, arg); |
| break; |
| |
| case PIN_CONFIG_DRIVE_PUSH_PULL: |
| ret = thunderbay_pinconf_set_pushpull(tpc, pin, arg); |
| break; |
| |
| case PIN_CONFIG_DRIVE_STRENGTH: |
| ret = thunderbay_pinconf_set_drivestrength(tpc, pin, arg); |
| break; |
| |
| case PIN_CONFIG_INPUT_SCHMITT_ENABLE: |
| ret = thunderbay_pinconf_set_schmitt(tpc, pin, arg); |
| break; |
| |
| case PIN_CONFIG_SLEW_RATE: |
| ret = thunderbay_pinconf_set_slew_rate(tpc, pin, arg); |
| break; |
| |
| default: |
| return -ENOTSUPP; |
| } |
| } |
| return ret; |
| } |
| |
| static const struct pinctrl_ops thunderbay_pctlops = { |
| .get_groups_count = pinctrl_generic_get_group_count, |
| .get_group_name = pinctrl_generic_get_group_name, |
| .get_group_pins = pinctrl_generic_get_group_pins, |
| .dt_node_to_map = pinconf_generic_dt_node_to_map_all, |
| .dt_free_map = pinconf_generic_dt_free_map, |
| }; |
| |
| static const struct pinmux_ops thunderbay_pmxops = { |
| .get_functions_count = pinmux_generic_get_function_count, |
| .get_function_name = pinmux_generic_get_function_name, |
| .get_function_groups = pinmux_generic_get_function_groups, |
| .set_mux = thb_pinctrl_set_mux, |
| .gpio_request_enable = thunderbay_request_gpio, |
| .gpio_disable_free = thunderbay_free_gpio, |
| }; |
| |
| static const struct pinconf_ops thunderbay_confops = { |
| .is_generic = true, |
| .pin_config_get = thunderbay_pinconf_get, |
| .pin_config_set = thunderbay_pinconf_set, |
| }; |
| |
| static struct pinctrl_desc thunderbay_pinctrl_desc = { |
| .name = "thunderbay-pinmux", |
| .pctlops = &thunderbay_pctlops, |
| .pmxops = &thunderbay_pmxops, |
| .confops = &thunderbay_confops, |
| .owner = THIS_MODULE, |
| }; |
| |
| static const struct of_device_id thunderbay_pinctrl_match[] = { |
| { |
| .compatible = "intel,thunderbay-pinctrl", |
| .data = &thunderbay_data |
| }, |
| {} |
| }; |
| |
| static int thunderbay_pinctrl_probe(struct platform_device *pdev) |
| { |
| const struct of_device_id *of_id; |
| struct device *dev = &pdev->dev; |
| struct thunderbay_pinctrl *tpc; |
| int ret; |
| |
| of_id = of_match_node(thunderbay_pinctrl_match, pdev->dev.of_node); |
| if (!of_id) |
| return -ENODEV; |
| |
| tpc = devm_kzalloc(dev, sizeof(*tpc), GFP_KERNEL); |
| if (!tpc) |
| return -ENOMEM; |
| |
| tpc->dev = dev; |
| tpc->soc = of_id->data; |
| |
| tpc->base0 = devm_platform_ioremap_resource(pdev, 0); |
| if (IS_ERR(tpc->base0)) |
| return PTR_ERR(tpc->base0); |
| |
| thunderbay_pinctrl_desc.pins = tpc->soc->pins; |
| thunderbay_pinctrl_desc.npins = tpc->soc->npins; |
| |
| /* Register pinctrl */ |
| tpc->pctrl = devm_pinctrl_register(dev, &thunderbay_pinctrl_desc, tpc); |
| if (IS_ERR(tpc->pctrl)) |
| return PTR_ERR(tpc->pctrl); |
| |
| /* Setup pinmux groups */ |
| ret = thunderbay_build_groups(tpc); |
| if (ret) |
| return ret; |
| |
| /* Setup pinmux functions */ |
| ret = thunderbay_build_functions(tpc); |
| if (ret) |
| return ret; |
| |
| /* Setup GPIO */ |
| ret = thunderbay_gpiochip_probe(tpc); |
| if (ret < 0) |
| return ret; |
| |
| platform_set_drvdata(pdev, tpc); |
| |
| return 0; |
| } |
| |
| static struct platform_driver thunderbay_pinctrl_driver = { |
| .driver = { |
| .name = "thunderbay-pinctrl", |
| .of_match_table = thunderbay_pinctrl_match, |
| }, |
| .probe = thunderbay_pinctrl_probe, |
| }; |
| |
| builtin_platform_driver(thunderbay_pinctrl_driver); |
| |
| MODULE_AUTHOR("Lakshmi Sowjanya D <lakshmi.sowjanya.d@intel.com>"); |
| MODULE_AUTHOR("Kiran Kumar S <kiran.kumar1.s@intel.com>"); |
| MODULE_DESCRIPTION("Intel Thunder Bay Pinctrl/GPIO Driver"); |
| MODULE_LICENSE("GPL v2"); |