mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 16:01:44 -04:00
This generic pin config property is confusingly named so let's rename it to make things clearer. There are already drivers in the tree that use PIN_CONFIG_OUTPUT to *read* the value of an output driven pin, which is a big semantic confusion for the head: are we then reading the setting of the output or the actual value/level that is put out on the pin? We already have PIN_CONFIG_OUTPUT_ENABLE that turns on driver buffers for output, so this can by logical conclusion only drive the voltage level if it should be any different. But if we read the pin, are we then reading the *setting* of the output value or the *actual* value we can see on the line? If the pin has not first been set into output mode with PIN_CONFIG_OUTPUT_ENABLE, but is instead in some input mode or tristate, what will reading this property actually return? Reading the current users reading this property it is clear that what we read is the logical level of the pin as 0 or 1 depending on if it is low or high. Rename it to PIN_CONFIG_LEVEL so it is crystal clear that we set or read the voltage level of the pin and nothing else. Acked-by: Sudeep Holla <sudeep.holla@arm.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
978 lines
26 KiB
C
978 lines
26 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
|
|
* Copyright (c) 2020 Western Digital Corporation or its affiliates.
|
|
*/
|
|
#include <linux/bitfield.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/io.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/of.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/pinctrl/pinconf-generic.h>
|
|
#include <linux/pinctrl/pinconf.h>
|
|
#include <linux/pinctrl/pinctrl.h>
|
|
#include <linux/pinctrl/pinmux.h>
|
|
|
|
#include <dt-bindings/pinctrl/k210-fpioa.h>
|
|
|
|
#include "core.h"
|
|
#include "pinconf.h"
|
|
#include "pinctrl-utils.h"
|
|
|
|
/*
|
|
* The K210 only implements 8 drive levels, even though
|
|
* there is register space for 16
|
|
*/
|
|
#define K210_PC_DRIVE_MASK GENMASK(11, 8)
|
|
#define K210_PC_DRIVE_SHIFT 8
|
|
#define K210_PC_DRIVE_0 (0 << K210_PC_DRIVE_SHIFT)
|
|
#define K210_PC_DRIVE_1 (1 << K210_PC_DRIVE_SHIFT)
|
|
#define K210_PC_DRIVE_2 (2 << K210_PC_DRIVE_SHIFT)
|
|
#define K210_PC_DRIVE_3 (3 << K210_PC_DRIVE_SHIFT)
|
|
#define K210_PC_DRIVE_4 (4 << K210_PC_DRIVE_SHIFT)
|
|
#define K210_PC_DRIVE_5 (5 << K210_PC_DRIVE_SHIFT)
|
|
#define K210_PC_DRIVE_6 (6 << K210_PC_DRIVE_SHIFT)
|
|
#define K210_PC_DRIVE_7 (7 << K210_PC_DRIVE_SHIFT)
|
|
#define K210_PC_DRIVE_MAX 7
|
|
#define K210_PC_MODE_MASK GENMASK(23, 12)
|
|
|
|
/*
|
|
* output enabled == PC_OE & (PC_OE_INV ^ FUNCTION_OE)
|
|
* where FUNCTION_OE is a physical signal from the function.
|
|
*/
|
|
#define K210_PC_OE BIT(12) /* Output Enable */
|
|
#define K210_PC_OE_INV BIT(13) /* INVert Output Enable */
|
|
#define K210_PC_DO_OE BIT(14) /* set Data Out to Output Enable sig */
|
|
#define K210_PC_DO_INV BIT(15) /* INVert final Data Output */
|
|
#define K210_PC_PU BIT(16) /* Pull Up */
|
|
#define K210_PC_PD BIT(17) /* Pull Down */
|
|
/* Strong pull up not implemented on K210 */
|
|
#define K210_PC_SL BIT(19) /* reduce SLew rate */
|
|
/* Same semantics as OE above */
|
|
#define K210_PC_IE BIT(20) /* Input Enable */
|
|
#define K210_PC_IE_INV BIT(21) /* INVert Input Enable */
|
|
#define K210_PC_DI_INV BIT(22) /* INVert Data Input */
|
|
#define K210_PC_ST BIT(23) /* Schmitt Trigger */
|
|
#define K210_PC_DI BIT(31) /* raw Data Input */
|
|
|
|
#define K210_PC_BIAS_MASK (K210_PC_PU & K210_PC_PD)
|
|
|
|
#define K210_PC_MODE_IN (K210_PC_IE | K210_PC_ST)
|
|
#define K210_PC_MODE_OUT (K210_PC_DRIVE_7 | K210_PC_OE)
|
|
#define K210_PC_MODE_I2C (K210_PC_MODE_IN | K210_PC_SL | \
|
|
K210_PC_OE | K210_PC_PU)
|
|
#define K210_PC_MODE_SCCB (K210_PC_MODE_I2C | \
|
|
K210_PC_OE_INV | K210_PC_IE_INV)
|
|
#define K210_PC_MODE_SPI (K210_PC_MODE_IN | K210_PC_IE_INV | \
|
|
K210_PC_MODE_OUT | K210_PC_OE_INV)
|
|
#define K210_PC_MODE_GPIO (K210_PC_MODE_IN | K210_PC_MODE_OUT)
|
|
|
|
#define K210_PG_FUNC GENMASK(7, 0)
|
|
#define K210_PG_DO BIT(8)
|
|
#define K210_PG_PIN GENMASK(22, 16)
|
|
|
|
/*
|
|
* struct k210_fpioa: Kendryte K210 FPIOA memory mapped registers
|
|
* @pins: 48 32-bits IO pin registers
|
|
* @tie_en: 256 (one per function) input tie enable bits
|
|
* @tie_val: 256 (one per function) input tie value bits
|
|
*/
|
|
struct k210_fpioa {
|
|
u32 pins[48];
|
|
u32 tie_en[8];
|
|
u32 tie_val[8];
|
|
};
|
|
|
|
struct k210_fpioa_data {
|
|
|
|
struct device *dev;
|
|
struct pinctrl_dev *pctl;
|
|
|
|
struct k210_fpioa __iomem *fpioa;
|
|
struct regmap *sysctl_map;
|
|
u32 power_offset;
|
|
};
|
|
|
|
#define K210_PIN_NAME(i) ("IO_" #i)
|
|
#define K210_PIN(i) [(i)] = PINCTRL_PIN((i), K210_PIN_NAME(i))
|
|
|
|
static const struct pinctrl_pin_desc k210_pins[] = {
|
|
K210_PIN(0), K210_PIN(1), K210_PIN(2),
|
|
K210_PIN(3), K210_PIN(4), K210_PIN(5),
|
|
K210_PIN(6), K210_PIN(7), K210_PIN(8),
|
|
K210_PIN(9), K210_PIN(10), K210_PIN(11),
|
|
K210_PIN(12), K210_PIN(13), K210_PIN(14),
|
|
K210_PIN(15), K210_PIN(16), K210_PIN(17),
|
|
K210_PIN(18), K210_PIN(19), K210_PIN(20),
|
|
K210_PIN(21), K210_PIN(22), K210_PIN(23),
|
|
K210_PIN(24), K210_PIN(25), K210_PIN(26),
|
|
K210_PIN(27), K210_PIN(28), K210_PIN(29),
|
|
K210_PIN(30), K210_PIN(31), K210_PIN(32),
|
|
K210_PIN(33), K210_PIN(34), K210_PIN(35),
|
|
K210_PIN(36), K210_PIN(37), K210_PIN(38),
|
|
K210_PIN(39), K210_PIN(40), K210_PIN(41),
|
|
K210_PIN(42), K210_PIN(43), K210_PIN(44),
|
|
K210_PIN(45), K210_PIN(46), K210_PIN(47)
|
|
};
|
|
|
|
#define K210_NPINS ARRAY_SIZE(k210_pins)
|
|
|
|
/*
|
|
* Pin groups: each of the 48 programmable pins is a group.
|
|
* To this are added 8 power domain groups, which for the purposes of
|
|
* the pin subsystem, contain no pins. The power domain groups only exist
|
|
* to set the power level. The id should never be used (since there are
|
|
* no pins 48-55).
|
|
*/
|
|
static const char *const k210_group_names[] = {
|
|
/* The first 48 groups are for pins, one each */
|
|
K210_PIN_NAME(0), K210_PIN_NAME(1), K210_PIN_NAME(2),
|
|
K210_PIN_NAME(3), K210_PIN_NAME(4), K210_PIN_NAME(5),
|
|
K210_PIN_NAME(6), K210_PIN_NAME(7), K210_PIN_NAME(8),
|
|
K210_PIN_NAME(9), K210_PIN_NAME(10), K210_PIN_NAME(11),
|
|
K210_PIN_NAME(12), K210_PIN_NAME(13), K210_PIN_NAME(14),
|
|
K210_PIN_NAME(15), K210_PIN_NAME(16), K210_PIN_NAME(17),
|
|
K210_PIN_NAME(18), K210_PIN_NAME(19), K210_PIN_NAME(20),
|
|
K210_PIN_NAME(21), K210_PIN_NAME(22), K210_PIN_NAME(23),
|
|
K210_PIN_NAME(24), K210_PIN_NAME(25), K210_PIN_NAME(26),
|
|
K210_PIN_NAME(27), K210_PIN_NAME(28), K210_PIN_NAME(29),
|
|
K210_PIN_NAME(30), K210_PIN_NAME(31), K210_PIN_NAME(32),
|
|
K210_PIN_NAME(33), K210_PIN_NAME(34), K210_PIN_NAME(35),
|
|
K210_PIN_NAME(36), K210_PIN_NAME(37), K210_PIN_NAME(38),
|
|
K210_PIN_NAME(39), K210_PIN_NAME(40), K210_PIN_NAME(41),
|
|
K210_PIN_NAME(42), K210_PIN_NAME(43), K210_PIN_NAME(44),
|
|
K210_PIN_NAME(45), K210_PIN_NAME(46), K210_PIN_NAME(47),
|
|
[48] = "A0", [49] = "A1", [50] = "A2",
|
|
[51] = "B3", [52] = "B4", [53] = "B5",
|
|
[54] = "C6", [55] = "C7"
|
|
};
|
|
|
|
#define K210_NGROUPS ARRAY_SIZE(k210_group_names)
|
|
|
|
enum k210_pinctrl_mode_id {
|
|
K210_PC_DEFAULT_DISABLED,
|
|
K210_PC_DEFAULT_IN,
|
|
K210_PC_DEFAULT_IN_TIE,
|
|
K210_PC_DEFAULT_OUT,
|
|
K210_PC_DEFAULT_I2C,
|
|
K210_PC_DEFAULT_SCCB,
|
|
K210_PC_DEFAULT_SPI,
|
|
K210_PC_DEFAULT_GPIO,
|
|
K210_PC_DEFAULT_INT13,
|
|
};
|
|
|
|
#define K210_PC_DEFAULT(mode) \
|
|
[K210_PC_DEFAULT_##mode] = K210_PC_MODE_##mode
|
|
|
|
static const u32 k210_pinconf_mode_id_to_mode[] = {
|
|
[K210_PC_DEFAULT_DISABLED] = 0,
|
|
K210_PC_DEFAULT(IN),
|
|
[K210_PC_DEFAULT_IN_TIE] = K210_PC_MODE_IN,
|
|
K210_PC_DEFAULT(OUT),
|
|
K210_PC_DEFAULT(I2C),
|
|
K210_PC_DEFAULT(SCCB),
|
|
K210_PC_DEFAULT(SPI),
|
|
K210_PC_DEFAULT(GPIO),
|
|
[K210_PC_DEFAULT_INT13] = K210_PC_MODE_IN | K210_PC_PU,
|
|
};
|
|
|
|
#undef K210_PC_DEFAULT
|
|
|
|
/*
|
|
* Pin functions configuration information.
|
|
*/
|
|
struct k210_pcf_info {
|
|
char name[15];
|
|
u8 mode_id;
|
|
};
|
|
|
|
#define K210_FUNC(id, mode) \
|
|
[K210_PCF_##id] = { \
|
|
.name = #id, \
|
|
.mode_id = K210_PC_DEFAULT_##mode \
|
|
}
|
|
|
|
static const struct k210_pcf_info k210_pcf_infos[] = {
|
|
K210_FUNC(JTAG_TCLK, IN),
|
|
K210_FUNC(JTAG_TDI, IN),
|
|
K210_FUNC(JTAG_TMS, IN),
|
|
K210_FUNC(JTAG_TDO, OUT),
|
|
K210_FUNC(SPI0_D0, SPI),
|
|
K210_FUNC(SPI0_D1, SPI),
|
|
K210_FUNC(SPI0_D2, SPI),
|
|
K210_FUNC(SPI0_D3, SPI),
|
|
K210_FUNC(SPI0_D4, SPI),
|
|
K210_FUNC(SPI0_D5, SPI),
|
|
K210_FUNC(SPI0_D6, SPI),
|
|
K210_FUNC(SPI0_D7, SPI),
|
|
K210_FUNC(SPI0_SS0, OUT),
|
|
K210_FUNC(SPI0_SS1, OUT),
|
|
K210_FUNC(SPI0_SS2, OUT),
|
|
K210_FUNC(SPI0_SS3, OUT),
|
|
K210_FUNC(SPI0_ARB, IN_TIE),
|
|
K210_FUNC(SPI0_SCLK, OUT),
|
|
K210_FUNC(UARTHS_RX, IN),
|
|
K210_FUNC(UARTHS_TX, OUT),
|
|
K210_FUNC(RESV6, IN),
|
|
K210_FUNC(RESV7, IN),
|
|
K210_FUNC(CLK_SPI1, OUT),
|
|
K210_FUNC(CLK_I2C1, OUT),
|
|
K210_FUNC(GPIOHS0, GPIO),
|
|
K210_FUNC(GPIOHS1, GPIO),
|
|
K210_FUNC(GPIOHS2, GPIO),
|
|
K210_FUNC(GPIOHS3, GPIO),
|
|
K210_FUNC(GPIOHS4, GPIO),
|
|
K210_FUNC(GPIOHS5, GPIO),
|
|
K210_FUNC(GPIOHS6, GPIO),
|
|
K210_FUNC(GPIOHS7, GPIO),
|
|
K210_FUNC(GPIOHS8, GPIO),
|
|
K210_FUNC(GPIOHS9, GPIO),
|
|
K210_FUNC(GPIOHS10, GPIO),
|
|
K210_FUNC(GPIOHS11, GPIO),
|
|
K210_FUNC(GPIOHS12, GPIO),
|
|
K210_FUNC(GPIOHS13, GPIO),
|
|
K210_FUNC(GPIOHS14, GPIO),
|
|
K210_FUNC(GPIOHS15, GPIO),
|
|
K210_FUNC(GPIOHS16, GPIO),
|
|
K210_FUNC(GPIOHS17, GPIO),
|
|
K210_FUNC(GPIOHS18, GPIO),
|
|
K210_FUNC(GPIOHS19, GPIO),
|
|
K210_FUNC(GPIOHS20, GPIO),
|
|
K210_FUNC(GPIOHS21, GPIO),
|
|
K210_FUNC(GPIOHS22, GPIO),
|
|
K210_FUNC(GPIOHS23, GPIO),
|
|
K210_FUNC(GPIOHS24, GPIO),
|
|
K210_FUNC(GPIOHS25, GPIO),
|
|
K210_FUNC(GPIOHS26, GPIO),
|
|
K210_FUNC(GPIOHS27, GPIO),
|
|
K210_FUNC(GPIOHS28, GPIO),
|
|
K210_FUNC(GPIOHS29, GPIO),
|
|
K210_FUNC(GPIOHS30, GPIO),
|
|
K210_FUNC(GPIOHS31, GPIO),
|
|
K210_FUNC(GPIO0, GPIO),
|
|
K210_FUNC(GPIO1, GPIO),
|
|
K210_FUNC(GPIO2, GPIO),
|
|
K210_FUNC(GPIO3, GPIO),
|
|
K210_FUNC(GPIO4, GPIO),
|
|
K210_FUNC(GPIO5, GPIO),
|
|
K210_FUNC(GPIO6, GPIO),
|
|
K210_FUNC(GPIO7, GPIO),
|
|
K210_FUNC(UART1_RX, IN),
|
|
K210_FUNC(UART1_TX, OUT),
|
|
K210_FUNC(UART2_RX, IN),
|
|
K210_FUNC(UART2_TX, OUT),
|
|
K210_FUNC(UART3_RX, IN),
|
|
K210_FUNC(UART3_TX, OUT),
|
|
K210_FUNC(SPI1_D0, SPI),
|
|
K210_FUNC(SPI1_D1, SPI),
|
|
K210_FUNC(SPI1_D2, SPI),
|
|
K210_FUNC(SPI1_D3, SPI),
|
|
K210_FUNC(SPI1_D4, SPI),
|
|
K210_FUNC(SPI1_D5, SPI),
|
|
K210_FUNC(SPI1_D6, SPI),
|
|
K210_FUNC(SPI1_D7, SPI),
|
|
K210_FUNC(SPI1_SS0, OUT),
|
|
K210_FUNC(SPI1_SS1, OUT),
|
|
K210_FUNC(SPI1_SS2, OUT),
|
|
K210_FUNC(SPI1_SS3, OUT),
|
|
K210_FUNC(SPI1_ARB, IN_TIE),
|
|
K210_FUNC(SPI1_SCLK, OUT),
|
|
K210_FUNC(SPI2_D0, SPI),
|
|
K210_FUNC(SPI2_SS, IN),
|
|
K210_FUNC(SPI2_SCLK, IN),
|
|
K210_FUNC(I2S0_MCLK, OUT),
|
|
K210_FUNC(I2S0_SCLK, OUT),
|
|
K210_FUNC(I2S0_WS, OUT),
|
|
K210_FUNC(I2S0_IN_D0, IN),
|
|
K210_FUNC(I2S0_IN_D1, IN),
|
|
K210_FUNC(I2S0_IN_D2, IN),
|
|
K210_FUNC(I2S0_IN_D3, IN),
|
|
K210_FUNC(I2S0_OUT_D0, OUT),
|
|
K210_FUNC(I2S0_OUT_D1, OUT),
|
|
K210_FUNC(I2S0_OUT_D2, OUT),
|
|
K210_FUNC(I2S0_OUT_D3, OUT),
|
|
K210_FUNC(I2S1_MCLK, OUT),
|
|
K210_FUNC(I2S1_SCLK, OUT),
|
|
K210_FUNC(I2S1_WS, OUT),
|
|
K210_FUNC(I2S1_IN_D0, IN),
|
|
K210_FUNC(I2S1_IN_D1, IN),
|
|
K210_FUNC(I2S1_IN_D2, IN),
|
|
K210_FUNC(I2S1_IN_D3, IN),
|
|
K210_FUNC(I2S1_OUT_D0, OUT),
|
|
K210_FUNC(I2S1_OUT_D1, OUT),
|
|
K210_FUNC(I2S1_OUT_D2, OUT),
|
|
K210_FUNC(I2S1_OUT_D3, OUT),
|
|
K210_FUNC(I2S2_MCLK, OUT),
|
|
K210_FUNC(I2S2_SCLK, OUT),
|
|
K210_FUNC(I2S2_WS, OUT),
|
|
K210_FUNC(I2S2_IN_D0, IN),
|
|
K210_FUNC(I2S2_IN_D1, IN),
|
|
K210_FUNC(I2S2_IN_D2, IN),
|
|
K210_FUNC(I2S2_IN_D3, IN),
|
|
K210_FUNC(I2S2_OUT_D0, OUT),
|
|
K210_FUNC(I2S2_OUT_D1, OUT),
|
|
K210_FUNC(I2S2_OUT_D2, OUT),
|
|
K210_FUNC(I2S2_OUT_D3, OUT),
|
|
K210_FUNC(RESV0, DISABLED),
|
|
K210_FUNC(RESV1, DISABLED),
|
|
K210_FUNC(RESV2, DISABLED),
|
|
K210_FUNC(RESV3, DISABLED),
|
|
K210_FUNC(RESV4, DISABLED),
|
|
K210_FUNC(RESV5, DISABLED),
|
|
K210_FUNC(I2C0_SCLK, I2C),
|
|
K210_FUNC(I2C0_SDA, I2C),
|
|
K210_FUNC(I2C1_SCLK, I2C),
|
|
K210_FUNC(I2C1_SDA, I2C),
|
|
K210_FUNC(I2C2_SCLK, I2C),
|
|
K210_FUNC(I2C2_SDA, I2C),
|
|
K210_FUNC(DVP_XCLK, OUT),
|
|
K210_FUNC(DVP_RST, OUT),
|
|
K210_FUNC(DVP_PWDN, OUT),
|
|
K210_FUNC(DVP_VSYNC, IN),
|
|
K210_FUNC(DVP_HSYNC, IN),
|
|
K210_FUNC(DVP_PCLK, IN),
|
|
K210_FUNC(DVP_D0, IN),
|
|
K210_FUNC(DVP_D1, IN),
|
|
K210_FUNC(DVP_D2, IN),
|
|
K210_FUNC(DVP_D3, IN),
|
|
K210_FUNC(DVP_D4, IN),
|
|
K210_FUNC(DVP_D5, IN),
|
|
K210_FUNC(DVP_D6, IN),
|
|
K210_FUNC(DVP_D7, IN),
|
|
K210_FUNC(SCCB_SCLK, SCCB),
|
|
K210_FUNC(SCCB_SDA, SCCB),
|
|
K210_FUNC(UART1_CTS, IN),
|
|
K210_FUNC(UART1_DSR, IN),
|
|
K210_FUNC(UART1_DCD, IN),
|
|
K210_FUNC(UART1_RI, IN),
|
|
K210_FUNC(UART1_SIR_IN, IN),
|
|
K210_FUNC(UART1_DTR, OUT),
|
|
K210_FUNC(UART1_RTS, OUT),
|
|
K210_FUNC(UART1_OUT2, OUT),
|
|
K210_FUNC(UART1_OUT1, OUT),
|
|
K210_FUNC(UART1_SIR_OUT, OUT),
|
|
K210_FUNC(UART1_BAUD, OUT),
|
|
K210_FUNC(UART1_RE, OUT),
|
|
K210_FUNC(UART1_DE, OUT),
|
|
K210_FUNC(UART1_RS485_EN, OUT),
|
|
K210_FUNC(UART2_CTS, IN),
|
|
K210_FUNC(UART2_DSR, IN),
|
|
K210_FUNC(UART2_DCD, IN),
|
|
K210_FUNC(UART2_RI, IN),
|
|
K210_FUNC(UART2_SIR_IN, IN),
|
|
K210_FUNC(UART2_DTR, OUT),
|
|
K210_FUNC(UART2_RTS, OUT),
|
|
K210_FUNC(UART2_OUT2, OUT),
|
|
K210_FUNC(UART2_OUT1, OUT),
|
|
K210_FUNC(UART2_SIR_OUT, OUT),
|
|
K210_FUNC(UART2_BAUD, OUT),
|
|
K210_FUNC(UART2_RE, OUT),
|
|
K210_FUNC(UART2_DE, OUT),
|
|
K210_FUNC(UART2_RS485_EN, OUT),
|
|
K210_FUNC(UART3_CTS, IN),
|
|
K210_FUNC(UART3_DSR, IN),
|
|
K210_FUNC(UART3_DCD, IN),
|
|
K210_FUNC(UART3_RI, IN),
|
|
K210_FUNC(UART3_SIR_IN, IN),
|
|
K210_FUNC(UART3_DTR, OUT),
|
|
K210_FUNC(UART3_RTS, OUT),
|
|
K210_FUNC(UART3_OUT2, OUT),
|
|
K210_FUNC(UART3_OUT1, OUT),
|
|
K210_FUNC(UART3_SIR_OUT, OUT),
|
|
K210_FUNC(UART3_BAUD, OUT),
|
|
K210_FUNC(UART3_RE, OUT),
|
|
K210_FUNC(UART3_DE, OUT),
|
|
K210_FUNC(UART3_RS485_EN, OUT),
|
|
K210_FUNC(TIMER0_TOGGLE1, OUT),
|
|
K210_FUNC(TIMER0_TOGGLE2, OUT),
|
|
K210_FUNC(TIMER0_TOGGLE3, OUT),
|
|
K210_FUNC(TIMER0_TOGGLE4, OUT),
|
|
K210_FUNC(TIMER1_TOGGLE1, OUT),
|
|
K210_FUNC(TIMER1_TOGGLE2, OUT),
|
|
K210_FUNC(TIMER1_TOGGLE3, OUT),
|
|
K210_FUNC(TIMER1_TOGGLE4, OUT),
|
|
K210_FUNC(TIMER2_TOGGLE1, OUT),
|
|
K210_FUNC(TIMER2_TOGGLE2, OUT),
|
|
K210_FUNC(TIMER2_TOGGLE3, OUT),
|
|
K210_FUNC(TIMER2_TOGGLE4, OUT),
|
|
K210_FUNC(CLK_SPI2, OUT),
|
|
K210_FUNC(CLK_I2C2, OUT),
|
|
K210_FUNC(INTERNAL0, OUT),
|
|
K210_FUNC(INTERNAL1, OUT),
|
|
K210_FUNC(INTERNAL2, OUT),
|
|
K210_FUNC(INTERNAL3, OUT),
|
|
K210_FUNC(INTERNAL4, OUT),
|
|
K210_FUNC(INTERNAL5, OUT),
|
|
K210_FUNC(INTERNAL6, OUT),
|
|
K210_FUNC(INTERNAL7, OUT),
|
|
K210_FUNC(INTERNAL8, OUT),
|
|
K210_FUNC(INTERNAL9, IN),
|
|
K210_FUNC(INTERNAL10, IN),
|
|
K210_FUNC(INTERNAL11, IN),
|
|
K210_FUNC(INTERNAL12, IN),
|
|
K210_FUNC(INTERNAL13, INT13),
|
|
K210_FUNC(INTERNAL14, I2C),
|
|
K210_FUNC(INTERNAL15, IN),
|
|
K210_FUNC(INTERNAL16, IN),
|
|
K210_FUNC(INTERNAL17, IN),
|
|
K210_FUNC(CONSTANT, DISABLED),
|
|
K210_FUNC(INTERNAL18, IN),
|
|
K210_FUNC(DEBUG0, OUT),
|
|
K210_FUNC(DEBUG1, OUT),
|
|
K210_FUNC(DEBUG2, OUT),
|
|
K210_FUNC(DEBUG3, OUT),
|
|
K210_FUNC(DEBUG4, OUT),
|
|
K210_FUNC(DEBUG5, OUT),
|
|
K210_FUNC(DEBUG6, OUT),
|
|
K210_FUNC(DEBUG7, OUT),
|
|
K210_FUNC(DEBUG8, OUT),
|
|
K210_FUNC(DEBUG9, OUT),
|
|
K210_FUNC(DEBUG10, OUT),
|
|
K210_FUNC(DEBUG11, OUT),
|
|
K210_FUNC(DEBUG12, OUT),
|
|
K210_FUNC(DEBUG13, OUT),
|
|
K210_FUNC(DEBUG14, OUT),
|
|
K210_FUNC(DEBUG15, OUT),
|
|
K210_FUNC(DEBUG16, OUT),
|
|
K210_FUNC(DEBUG17, OUT),
|
|
K210_FUNC(DEBUG18, OUT),
|
|
K210_FUNC(DEBUG19, OUT),
|
|
K210_FUNC(DEBUG20, OUT),
|
|
K210_FUNC(DEBUG21, OUT),
|
|
K210_FUNC(DEBUG22, OUT),
|
|
K210_FUNC(DEBUG23, OUT),
|
|
K210_FUNC(DEBUG24, OUT),
|
|
K210_FUNC(DEBUG25, OUT),
|
|
K210_FUNC(DEBUG26, OUT),
|
|
K210_FUNC(DEBUG27, OUT),
|
|
K210_FUNC(DEBUG28, OUT),
|
|
K210_FUNC(DEBUG29, OUT),
|
|
K210_FUNC(DEBUG30, OUT),
|
|
K210_FUNC(DEBUG31, OUT),
|
|
};
|
|
|
|
#define PIN_CONFIG_OUTPUT_INVERT (PIN_CONFIG_END + 1)
|
|
#define PIN_CONFIG_INPUT_INVERT (PIN_CONFIG_END + 2)
|
|
|
|
static const struct pinconf_generic_params k210_pinconf_custom_params[] = {
|
|
{ "output-polarity-invert", PIN_CONFIG_OUTPUT_INVERT, 1 },
|
|
{ "input-polarity-invert", PIN_CONFIG_INPUT_INVERT, 1 },
|
|
};
|
|
|
|
/*
|
|
* Max drive strength in uA.
|
|
*/
|
|
static const int k210_pinconf_drive_strength[] = {
|
|
[0] = 11200,
|
|
[1] = 16800,
|
|
[2] = 22300,
|
|
[3] = 27800,
|
|
[4] = 33300,
|
|
[5] = 38700,
|
|
[6] = 44100,
|
|
[7] = 49500,
|
|
};
|
|
|
|
static int k210_pinconf_get_drive(unsigned int max_strength_ua)
|
|
{
|
|
int i;
|
|
|
|
for (i = K210_PC_DRIVE_MAX; i >= 0; i--) {
|
|
if (k210_pinconf_drive_strength[i] <= max_strength_ua)
|
|
return i;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static void k210_pinmux_set_pin_function(struct pinctrl_dev *pctldev,
|
|
u32 pin, u32 func)
|
|
{
|
|
struct k210_fpioa_data *pdata = pinctrl_dev_get_drvdata(pctldev);
|
|
const struct k210_pcf_info *info = &k210_pcf_infos[func];
|
|
u32 mode = k210_pinconf_mode_id_to_mode[info->mode_id];
|
|
u32 val = func | mode;
|
|
|
|
dev_dbg(pdata->dev, "set pin %u function %s (%u) -> 0x%08x\n",
|
|
pin, info->name, func, val);
|
|
|
|
writel(val, &pdata->fpioa->pins[pin]);
|
|
}
|
|
|
|
static int k210_pinconf_set_param(struct pinctrl_dev *pctldev,
|
|
unsigned int pin,
|
|
unsigned int param, unsigned int arg)
|
|
{
|
|
struct k210_fpioa_data *pdata = pinctrl_dev_get_drvdata(pctldev);
|
|
u32 val = readl(&pdata->fpioa->pins[pin]);
|
|
int drive;
|
|
|
|
dev_dbg(pdata->dev, "set pin %u param %u, arg 0x%x\n",
|
|
pin, param, arg);
|
|
|
|
switch (param) {
|
|
case PIN_CONFIG_BIAS_DISABLE:
|
|
val &= ~K210_PC_BIAS_MASK;
|
|
break;
|
|
case PIN_CONFIG_BIAS_PULL_DOWN:
|
|
if (!arg)
|
|
return -EINVAL;
|
|
val |= K210_PC_PD;
|
|
break;
|
|
case PIN_CONFIG_BIAS_PULL_UP:
|
|
if (!arg)
|
|
return -EINVAL;
|
|
val |= K210_PC_PU;
|
|
break;
|
|
case PIN_CONFIG_DRIVE_STRENGTH:
|
|
arg *= 1000;
|
|
fallthrough;
|
|
case PIN_CONFIG_DRIVE_STRENGTH_UA:
|
|
drive = k210_pinconf_get_drive(arg);
|
|
if (drive < 0)
|
|
return drive;
|
|
val &= ~K210_PC_DRIVE_MASK;
|
|
val |= FIELD_PREP(K210_PC_DRIVE_MASK, drive);
|
|
break;
|
|
case PIN_CONFIG_INPUT_ENABLE:
|
|
if (arg)
|
|
val |= K210_PC_IE;
|
|
else
|
|
val &= ~K210_PC_IE;
|
|
break;
|
|
case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
|
|
if (arg)
|
|
val |= K210_PC_ST;
|
|
else
|
|
val &= ~K210_PC_ST;
|
|
break;
|
|
case PIN_CONFIG_LEVEL:
|
|
k210_pinmux_set_pin_function(pctldev, pin, K210_PCF_CONSTANT);
|
|
val = readl(&pdata->fpioa->pins[pin]);
|
|
val |= K210_PC_MODE_OUT;
|
|
if (!arg)
|
|
val |= K210_PC_DO_INV;
|
|
break;
|
|
case PIN_CONFIG_OUTPUT_ENABLE:
|
|
if (arg)
|
|
val |= K210_PC_OE;
|
|
else
|
|
val &= ~K210_PC_OE;
|
|
break;
|
|
case PIN_CONFIG_SLEW_RATE:
|
|
if (arg)
|
|
val |= K210_PC_SL;
|
|
else
|
|
val &= ~K210_PC_SL;
|
|
break;
|
|
case PIN_CONFIG_OUTPUT_INVERT:
|
|
if (arg)
|
|
val |= K210_PC_DO_INV;
|
|
else
|
|
val &= ~K210_PC_DO_INV;
|
|
break;
|
|
case PIN_CONFIG_INPUT_INVERT:
|
|
if (arg)
|
|
val |= K210_PC_DI_INV;
|
|
else
|
|
val &= ~K210_PC_DI_INV;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
writel(val, &pdata->fpioa->pins[pin]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int k210_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
|
|
unsigned long *configs, unsigned int num_configs)
|
|
{
|
|
unsigned int param, arg;
|
|
int i, ret;
|
|
|
|
if (WARN_ON(pin >= K210_NPINS))
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < num_configs; i++) {
|
|
param = pinconf_to_config_param(configs[i]);
|
|
arg = pinconf_to_config_argument(configs[i]);
|
|
ret = k210_pinconf_set_param(pctldev, pin, param, arg);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void k210_pinconf_dbg_show(struct pinctrl_dev *pctldev,
|
|
struct seq_file *s, unsigned int pin)
|
|
{
|
|
struct k210_fpioa_data *pdata = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
seq_printf(s, "%#x", readl(&pdata->fpioa->pins[pin]));
|
|
}
|
|
|
|
static int k210_pinconf_group_set(struct pinctrl_dev *pctldev,
|
|
unsigned int selector, unsigned long *configs,
|
|
unsigned int num_configs)
|
|
{
|
|
struct k210_fpioa_data *pdata = pinctrl_dev_get_drvdata(pctldev);
|
|
unsigned int param, arg;
|
|
u32 bit;
|
|
int i;
|
|
|
|
/* Pins should be configured with pinmux, not groups*/
|
|
if (selector < K210_NPINS)
|
|
return -EINVAL;
|
|
|
|
/* Otherwise it's a power domain */
|
|
for (i = 0; i < num_configs; i++) {
|
|
param = pinconf_to_config_param(configs[i]);
|
|
if (param != PIN_CONFIG_POWER_SOURCE)
|
|
return -EINVAL;
|
|
|
|
arg = pinconf_to_config_argument(configs[i]);
|
|
bit = BIT(selector - K210_NPINS);
|
|
regmap_update_bits(pdata->sysctl_map,
|
|
pdata->power_offset,
|
|
bit, arg ? bit : 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void k210_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
|
|
struct seq_file *s,
|
|
unsigned int selector)
|
|
{
|
|
struct k210_fpioa_data *pdata = pinctrl_dev_get_drvdata(pctldev);
|
|
int ret;
|
|
u32 val;
|
|
|
|
if (selector < K210_NPINS)
|
|
return k210_pinconf_dbg_show(pctldev, s, selector);
|
|
|
|
ret = regmap_read(pdata->sysctl_map, pdata->power_offset, &val);
|
|
if (ret) {
|
|
dev_err(pdata->dev, "Failed to read power reg\n");
|
|
return;
|
|
}
|
|
|
|
seq_printf(s, "%s: %s V", k210_group_names[selector],
|
|
val & BIT(selector - K210_NPINS) ? "1.8" : "3.3");
|
|
}
|
|
|
|
static const struct pinconf_ops k210_pinconf_ops = {
|
|
.is_generic = true,
|
|
.pin_config_set = k210_pinconf_set,
|
|
.pin_config_group_set = k210_pinconf_group_set,
|
|
.pin_config_dbg_show = k210_pinconf_dbg_show,
|
|
.pin_config_group_dbg_show = k210_pinconf_group_dbg_show,
|
|
};
|
|
|
|
static int k210_pinmux_get_function_count(struct pinctrl_dev *pctldev)
|
|
{
|
|
return ARRAY_SIZE(k210_pcf_infos);
|
|
}
|
|
|
|
static const char *k210_pinmux_get_function_name(struct pinctrl_dev *pctldev,
|
|
unsigned int selector)
|
|
{
|
|
return k210_pcf_infos[selector].name;
|
|
}
|
|
|
|
static int k210_pinmux_get_function_groups(struct pinctrl_dev *pctldev,
|
|
unsigned int selector,
|
|
const char * const **groups,
|
|
unsigned int * const num_groups)
|
|
{
|
|
/* Any function can be mapped to any pin */
|
|
*groups = k210_group_names;
|
|
*num_groups = K210_NPINS;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int k210_pinmux_set_mux(struct pinctrl_dev *pctldev,
|
|
unsigned int function,
|
|
unsigned int group)
|
|
{
|
|
/* Can't mux power domains */
|
|
if (group >= K210_NPINS)
|
|
return -EINVAL;
|
|
|
|
k210_pinmux_set_pin_function(pctldev, group, function);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct pinmux_ops k210_pinmux_ops = {
|
|
.get_functions_count = k210_pinmux_get_function_count,
|
|
.get_function_name = k210_pinmux_get_function_name,
|
|
.get_function_groups = k210_pinmux_get_function_groups,
|
|
.set_mux = k210_pinmux_set_mux,
|
|
.strict = true,
|
|
};
|
|
|
|
static int k210_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
|
|
{
|
|
return K210_NGROUPS;
|
|
}
|
|
|
|
static const char *k210_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
|
|
unsigned int group)
|
|
{
|
|
return k210_group_names[group];
|
|
}
|
|
|
|
static int k210_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
|
|
unsigned int group,
|
|
const unsigned int **pins,
|
|
unsigned int *npins)
|
|
{
|
|
if (group >= K210_NPINS) {
|
|
*pins = NULL;
|
|
*npins = 0;
|
|
return 0;
|
|
}
|
|
|
|
*pins = &k210_pins[group].number;
|
|
*npins = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void k210_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev,
|
|
struct seq_file *s, unsigned int offset)
|
|
{
|
|
seq_printf(s, "%s", dev_name(pctldev->dev));
|
|
}
|
|
|
|
static int k210_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
|
|
struct device_node *np,
|
|
struct pinctrl_map **map,
|
|
unsigned int *reserved_maps,
|
|
unsigned int *num_maps)
|
|
{
|
|
int ret, pinmux_groups;
|
|
u32 pinmux_group;
|
|
unsigned long *configs = NULL;
|
|
unsigned int num_configs = 0;
|
|
unsigned int reserve = 0;
|
|
|
|
ret = of_property_count_strings(np, "groups");
|
|
if (!ret)
|
|
return pinconf_generic_dt_subnode_to_map(pctldev, np, map,
|
|
reserved_maps, num_maps,
|
|
PIN_MAP_TYPE_CONFIGS_GROUP);
|
|
|
|
pinmux_groups = of_property_count_u32_elems(np, "pinmux");
|
|
if (pinmux_groups <= 0) {
|
|
/* Ignore this node */
|
|
return 0;
|
|
}
|
|
|
|
ret = pinconf_generic_parse_dt_config(np, pctldev, &configs,
|
|
&num_configs);
|
|
if (ret < 0) {
|
|
dev_err(pctldev->dev, "%pOF: could not parse node property\n",
|
|
np);
|
|
return ret;
|
|
}
|
|
|
|
reserve = pinmux_groups * (1 + num_configs);
|
|
ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, num_maps,
|
|
reserve);
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
of_property_for_each_u32(np, "pinmux", pinmux_group) {
|
|
const char *group_name, *func_name;
|
|
u32 pin = FIELD_GET(K210_PG_PIN, pinmux_group);
|
|
u32 func = FIELD_GET(K210_PG_FUNC, pinmux_group);
|
|
|
|
if (pin >= K210_NPINS) {
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
group_name = k210_group_names[pin];
|
|
func_name = k210_pcf_infos[func].name;
|
|
|
|
dev_dbg(pctldev->dev, "Pinmux %s: pin %u func %s\n",
|
|
np->name, pin, func_name);
|
|
|
|
ret = pinctrl_utils_add_map_mux(pctldev, map, reserved_maps,
|
|
num_maps, group_name,
|
|
func_name);
|
|
if (ret < 0) {
|
|
dev_err(pctldev->dev, "%pOF add mux map failed %d\n",
|
|
np, ret);
|
|
goto exit;
|
|
}
|
|
|
|
if (num_configs) {
|
|
ret = pinctrl_utils_add_map_configs(pctldev, map,
|
|
reserved_maps, num_maps, group_name,
|
|
configs, num_configs,
|
|
PIN_MAP_TYPE_CONFIGS_PIN);
|
|
if (ret < 0) {
|
|
dev_err(pctldev->dev,
|
|
"%pOF add configs map failed %d\n",
|
|
np, ret);
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
exit:
|
|
kfree(configs);
|
|
return ret;
|
|
}
|
|
|
|
static int k210_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
|
|
struct device_node *np_config,
|
|
struct pinctrl_map **map,
|
|
unsigned int *num_maps)
|
|
{
|
|
unsigned int reserved_maps;
|
|
int ret;
|
|
|
|
reserved_maps = 0;
|
|
*map = NULL;
|
|
*num_maps = 0;
|
|
|
|
ret = k210_pinctrl_dt_subnode_to_map(pctldev, np_config, map,
|
|
&reserved_maps, num_maps);
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
for_each_available_child_of_node_scoped(np_config, np) {
|
|
ret = k210_pinctrl_dt_subnode_to_map(pctldev, np, map,
|
|
&reserved_maps, num_maps);
|
|
if (ret < 0)
|
|
goto err;
|
|
}
|
|
return 0;
|
|
|
|
err:
|
|
pinctrl_utils_free_map(pctldev, *map, *num_maps);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static const struct pinctrl_ops k210_pinctrl_ops = {
|
|
.get_groups_count = k210_pinctrl_get_groups_count,
|
|
.get_group_name = k210_pinctrl_get_group_name,
|
|
.get_group_pins = k210_pinctrl_get_group_pins,
|
|
.pin_dbg_show = k210_pinctrl_pin_dbg_show,
|
|
.dt_node_to_map = k210_pinctrl_dt_node_to_map,
|
|
.dt_free_map = pinconf_generic_dt_free_map,
|
|
};
|
|
|
|
static const struct pinctrl_desc k210_pinctrl_desc = {
|
|
.name = "k210-pinctrl",
|
|
.pins = k210_pins,
|
|
.npins = K210_NPINS,
|
|
.pctlops = &k210_pinctrl_ops,
|
|
.pmxops = &k210_pinmux_ops,
|
|
.confops = &k210_pinconf_ops,
|
|
.custom_params = k210_pinconf_custom_params,
|
|
.num_custom_params = ARRAY_SIZE(k210_pinconf_custom_params),
|
|
};
|
|
|
|
static void k210_fpioa_init_ties(struct k210_fpioa_data *pdata)
|
|
{
|
|
struct k210_fpioa __iomem *fpioa = pdata->fpioa;
|
|
u32 val;
|
|
int i, j;
|
|
|
|
dev_dbg(pdata->dev, "Init pin ties\n");
|
|
|
|
/* Init pin functions input ties */
|
|
for (i = 0; i < ARRAY_SIZE(fpioa->tie_en); i++) {
|
|
val = 0;
|
|
for (j = 0; j < 32; j++) {
|
|
if (k210_pcf_infos[i * 32 + j].mode_id ==
|
|
K210_PC_DEFAULT_IN_TIE) {
|
|
dev_dbg(pdata->dev,
|
|
"tie_en function %d (%s)\n",
|
|
i * 32 + j,
|
|
k210_pcf_infos[i * 32 + j].name);
|
|
val |= BIT(j);
|
|
}
|
|
}
|
|
|
|
/* Set value before enable */
|
|
writel(val, &fpioa->tie_val[i]);
|
|
writel(val, &fpioa->tie_en[i]);
|
|
}
|
|
}
|
|
|
|
static int k210_fpioa_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = dev->of_node;
|
|
struct k210_fpioa_data *pdata;
|
|
struct clk *clk, *pclk;
|
|
|
|
dev_info(dev, "K210 FPIOA pin controller\n");
|
|
|
|
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
|
if (!pdata)
|
|
return -ENOMEM;
|
|
|
|
pdata->dev = dev;
|
|
platform_set_drvdata(pdev, pdata);
|
|
|
|
pdata->fpioa = devm_platform_ioremap_resource(pdev, 0);
|
|
if (IS_ERR(pdata->fpioa))
|
|
return PTR_ERR(pdata->fpioa);
|
|
|
|
clk = devm_clk_get_enabled(dev, "ref");
|
|
if (IS_ERR(clk))
|
|
return PTR_ERR(clk);
|
|
|
|
pclk = devm_clk_get_optional_enabled(dev, "pclk");
|
|
if (IS_ERR(pclk))
|
|
return PTR_ERR(pclk);
|
|
|
|
pdata->sysctl_map =
|
|
syscon_regmap_lookup_by_phandle_args(np,
|
|
"canaan,k210-sysctl-power",
|
|
1, &pdata->power_offset);
|
|
if (IS_ERR(pdata->sysctl_map))
|
|
return PTR_ERR(pdata->sysctl_map);
|
|
|
|
k210_fpioa_init_ties(pdata);
|
|
|
|
pdata->pctl = pinctrl_register(&k210_pinctrl_desc, dev, (void *)pdata);
|
|
if (IS_ERR(pdata->pctl))
|
|
return PTR_ERR(pdata->pctl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id k210_fpioa_dt_ids[] = {
|
|
{ .compatible = "canaan,k210-fpioa" },
|
|
{ /* sentinel */ },
|
|
};
|
|
|
|
static struct platform_driver k210_fpioa_driver = {
|
|
.probe = k210_fpioa_probe,
|
|
.driver = {
|
|
.name = "k210-fpioa",
|
|
.of_match_table = k210_fpioa_dt_ids,
|
|
},
|
|
};
|
|
builtin_platform_driver(k210_fpioa_driver);
|