mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 22:44:27 -05:00 
			
		
		
		
	Siflower SF19A2890 is an SoC with: Dual-core MIPS InterAptiv at 800MHz DDR3 controller One Gigabit Ethernet MAC with RGMII and IPv4 HNAT engine Built-in 2x2 11N + 2x2 11AC WiFi radio USB 2.0 OTG I2C/SPI/GPIO and various other peripherals This PR adds support for SF19A2890 EVB with ethernet support. EVB spec: Memory: DDR3 128M Ethernet: RTL8367RB 5-port gigabit switch Flash: 16M NOR Others: MicroUSB OTG, LED x 1, Reset button x1 The built image can be flashed using u-boot recovery. This target is marked as source-only until support for a commercial router board comes. Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
		
			
				
	
	
		
			516 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			516 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0+
 | 
						|
/*
 | 
						|
 * Driver for Siflower SF19A2890 pinctrl.
 | 
						|
 *
 | 
						|
 * Based on:
 | 
						|
 * Driver for Broadcom BCM2835 GPIO unit (pinctrl + GPIO)
 | 
						|
 *
 | 
						|
 * Copyright (C) 2012 Chris Boot, Simon Arlott, Stephen Warren
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/bitmap.h>
 | 
						|
#include <linux/device.h>
 | 
						|
#include <linux/err.h>
 | 
						|
#include <linux/io.h>
 | 
						|
#include <linux/init.h>
 | 
						|
#include <linux/module.h>
 | 
						|
#include <linux/of.h>
 | 
						|
#include <linux/pinctrl/consumer.h>
 | 
						|
#include <linux/pinctrl/machine.h>
 | 
						|
#include <linux/pinctrl/pinconf.h>
 | 
						|
#include <linux/pinctrl/pinctrl.h>
 | 
						|
#include <linux/pinctrl/pinmux.h>
 | 
						|
#include <linux/pinctrl/pinconf-generic.h>
 | 
						|
#include <linux/platform_device.h>
 | 
						|
#include <linux/seq_file.h>
 | 
						|
#include <linux/slab.h>
 | 
						|
#include <linux/spinlock.h>
 | 
						|
#include <linux/types.h>
 | 
						|
 | 
						|
#define MODULE_NAME "sf19a2890-pinctrl"
 | 
						|
 | 
						|
struct sf_pinctrl {
 | 
						|
	struct device *dev;
 | 
						|
	void __iomem *base;
 | 
						|
 | 
						|
	struct pinctrl_dev *pctl_dev;
 | 
						|
	struct pinctrl_desc pctl_desc;
 | 
						|
	struct pinctrl_gpio_range gpio_range;
 | 
						|
};
 | 
						|
 | 
						|
#define SF19A28_NUM_GPIOS	49
 | 
						|
 | 
						|
#define SF19A28_REG_PC(pin)	((pin) * 0x8)
 | 
						|
#define  PC_OEN			BIT(7)
 | 
						|
#define  PC_ST			BIT(6)
 | 
						|
#define  PC_IE			BIT(5)
 | 
						|
#define  PC_PD			BIT(4)
 | 
						|
#define  PC_PU			BIT(3)
 | 
						|
#define  PC_DS			GENMASK(2, 0)
 | 
						|
 | 
						|
#define DRIVE_MIN		6
 | 
						|
#define DRIVE_STEP		3
 | 
						|
#define DRIVE_MAX		(7 * DRIVE_STEP)
 | 
						|
 | 
						|
#define SF19A28_REG_PMX(pin)	((pin) * 0x8 + 0x4)
 | 
						|
/*
 | 
						|
 * FUNC_SW:
 | 
						|
 *  0: Override pad output enable with PC_OEN
 | 
						|
 *  1: take OEN from GPIO or alternative function
 | 
						|
 * FMUX_SEL:
 | 
						|
 *  0: Alternative function mode
 | 
						|
 *  1: GPIO mode
 | 
						|
 */
 | 
						|
#define  PMX_FUNC_SW		BIT(3)
 | 
						|
#define  PMX_FMUX_SEL		BIT(2)
 | 
						|
#define  PMX_MODE		GENMASK(1, 0)
 | 
						|
 | 
						|
static struct pinctrl_pin_desc sf19a2890_gpio_pins[] = {
 | 
						|
	PINCTRL_PIN(0, "JTAG_TDO"),
 | 
						|
	PINCTRL_PIN(1, "JTAG_TDI"),
 | 
						|
	PINCTRL_PIN(2, "JTAG_TMS"),
 | 
						|
	PINCTRL_PIN(3, "JTAG_TCK"),
 | 
						|
	PINCTRL_PIN(4, "JTAG_RST"),
 | 
						|
	PINCTRL_PIN(5, "SPI_TXD"),
 | 
						|
	PINCTRL_PIN(6, "SPI_RXD"),
 | 
						|
	PINCTRL_PIN(7, "SPI_CLK"),
 | 
						|
	PINCTRL_PIN(8, "SPI_CSN"),
 | 
						|
	PINCTRL_PIN(9, "UART_TX"),
 | 
						|
	PINCTRL_PIN(10, "UART_RX"),
 | 
						|
	PINCTRL_PIN(11, "I2C_DAT"),
 | 
						|
	PINCTRL_PIN(12, "I2C_CLK"),
 | 
						|
	PINCTRL_PIN(13, "RGMII_GTX_CLK"),
 | 
						|
	PINCTRL_PIN(14, "RGMII_TXCLK"),
 | 
						|
	PINCTRL_PIN(15, "RGMII_TXD0"),
 | 
						|
	PINCTRL_PIN(16, "RGMII_TXD1"),
 | 
						|
	PINCTRL_PIN(17, "RGMII_TXD2"),
 | 
						|
	PINCTRL_PIN(18, "RGMII_TXD3"),
 | 
						|
	PINCTRL_PIN(19, "RGMII_TXCTL"),
 | 
						|
	PINCTRL_PIN(20, "RGMII_RXCLK"),
 | 
						|
	PINCTRL_PIN(21, "RGMII_RXD0"),
 | 
						|
	PINCTRL_PIN(22, "RGMII_RXD1"),
 | 
						|
	PINCTRL_PIN(23, "RGMII_RXD2"),
 | 
						|
	PINCTRL_PIN(24, "RGMII_RXD3"),
 | 
						|
	PINCTRL_PIN(25, "RGMII_RXCTL"),
 | 
						|
	PINCTRL_PIN(26, "RGMII_COL"),
 | 
						|
	PINCTRL_PIN(27, "RGMII_CRS"),
 | 
						|
	PINCTRL_PIN(28, "RGMII_MDC"),
 | 
						|
	PINCTRL_PIN(29, "RGMII_MDIO"),
 | 
						|
	PINCTRL_PIN(30, "HB0_PA_EN"),
 | 
						|
	PINCTRL_PIN(31, "HB0_LNA_EN"),
 | 
						|
	PINCTRL_PIN(32, "HB0_SW_CTRL0"),
 | 
						|
	PINCTRL_PIN(33, "HB0_SW_CTRL1"),
 | 
						|
	PINCTRL_PIN(34, "HB1_PA_EN"),
 | 
						|
	PINCTRL_PIN(35, "HB1_LNA_EN"),
 | 
						|
	PINCTRL_PIN(36, "HB1_SW_CTRL0"),
 | 
						|
	PINCTRL_PIN(37, "HB1_SW_CTRL1"),
 | 
						|
	PINCTRL_PIN(38, "LB0_PA_EN"),
 | 
						|
	PINCTRL_PIN(39, "LB0_LNA_EN"),
 | 
						|
	PINCTRL_PIN(40, "LB0_SW_CTRL0"),
 | 
						|
	PINCTRL_PIN(41, "LB0_SW_CTRL1"),
 | 
						|
	PINCTRL_PIN(42, "LB1_PA_EN"),
 | 
						|
	PINCTRL_PIN(43, "LB1_LNA_EN"),
 | 
						|
	PINCTRL_PIN(44, "LB1_SW_CTRL0"),
 | 
						|
	PINCTRL_PIN(45, "LB1_SW_CTRL1"),
 | 
						|
	PINCTRL_PIN(46, "CLK_OUT"),
 | 
						|
	PINCTRL_PIN(47, "EXT_CLK_IN"),
 | 
						|
	PINCTRL_PIN(48, "DRVVBUS0"),
 | 
						|
};
 | 
						|
 | 
						|
static const char * const sf19a2890_gpio_groups[] = {
 | 
						|
	"JTAG_TDO",
 | 
						|
	"JTAG_TDI",
 | 
						|
	"JTAG_TMS",
 | 
						|
	"JTAG_TCK",
 | 
						|
	"JTAG_RST",
 | 
						|
	"SPI_TXD",
 | 
						|
	"SPI_RXD",
 | 
						|
	"SPI_CLK",
 | 
						|
	"SPI_CSN",
 | 
						|
	"UART_TX",
 | 
						|
	"UART_RX",
 | 
						|
	"I2C_DAT",
 | 
						|
	"I2C_CLK",
 | 
						|
	"RGMII_GTX_CLK",
 | 
						|
	"RGMII_TXCLK",
 | 
						|
	"RGMII_TXD0",
 | 
						|
	"RGMII_TXD1",
 | 
						|
	"RGMII_TXD2",
 | 
						|
	"RGMII_TXD3",
 | 
						|
	"RGMII_TXCTL",
 | 
						|
	"RGMII_RXCLK",
 | 
						|
	"RGMII_RXD0",
 | 
						|
	"RGMII_RXD1",
 | 
						|
	"RGMII_RXD2",
 | 
						|
	"RGMII_RXD3",
 | 
						|
	"RGMII_RXCTL",
 | 
						|
	"RGMII_COL",
 | 
						|
	"RGMII_CRS",
 | 
						|
	"RGMII_MDC",
 | 
						|
	"RGMII_MDIO",
 | 
						|
	"HB0_PA_EN",
 | 
						|
	"HB0_LNA_EN",
 | 
						|
	"HB0_SW_CTRL0",
 | 
						|
	"HB0_SW_CTRL1",
 | 
						|
	"HB1_PA_EN",
 | 
						|
	"HB1_LNA_EN",
 | 
						|
	"HB1_SW_CTRL0",
 | 
						|
	"HB1_SW_CTRL1",
 | 
						|
	"LB0_PA_EN",
 | 
						|
	"LB0_LNA_EN",
 | 
						|
	"LB0_SW_CTRL0",
 | 
						|
	"LB0_SW_CTRL1",
 | 
						|
	"LB1_PA_EN",
 | 
						|
	"LB1_LNA_EN",
 | 
						|
	"LB1_SW_CTRL0",
 | 
						|
	"LB1_SW_CTRL1",
 | 
						|
	"CLK_OUT",
 | 
						|
	"EXT_CLK_IN",
 | 
						|
	"DRVVBUS0",
 | 
						|
};
 | 
						|
 | 
						|
#define SF19A28_FUNC0		0
 | 
						|
#define SF19A28_FUNC1		1
 | 
						|
#define SF19A28_FUNC2		2
 | 
						|
#define SF19A28_FUNC3		3
 | 
						|
#define SF19A28_NUM_FUNCS	4
 | 
						|
 | 
						|
static const char * const sf19a2890_functions[] = {
 | 
						|
	"func0", "func1", "func2", "func3"
 | 
						|
};
 | 
						|
 | 
						|
static inline u32 sf_pinctrl_rd(struct sf_pinctrl *pc, ulong reg)
 | 
						|
{
 | 
						|
	return readl(pc->base + reg);
 | 
						|
}
 | 
						|
 | 
						|
static inline void sf_pinctrl_wr(struct sf_pinctrl *pc, ulong reg, u32 val)
 | 
						|
{
 | 
						|
	writel(val, pc->base + reg);
 | 
						|
}
 | 
						|
 | 
						|
static inline void sf_pinctrl_rmw(struct sf_pinctrl *pc, ulong reg, u32 clr,
 | 
						|
				  u32 set)
 | 
						|
{
 | 
						|
	u32 val;
 | 
						|
 | 
						|
	val = sf_pinctrl_rd(pc, reg);
 | 
						|
	val &= ~clr;
 | 
						|
	val |= set;
 | 
						|
	sf_pinctrl_wr(pc, reg, val);
 | 
						|
}
 | 
						|
 | 
						|
static int sf19a2890_pctl_get_groups_count(struct pinctrl_dev *pctldev)
 | 
						|
{
 | 
						|
	return SF19A28_NUM_GPIOS;
 | 
						|
}
 | 
						|
 | 
						|
static const char *sf19a2890_pctl_get_group_name(struct pinctrl_dev *pctldev,
 | 
						|
						 unsigned selector)
 | 
						|
{
 | 
						|
	return sf19a2890_gpio_groups[selector];
 | 
						|
}
 | 
						|
 | 
						|
static int sf19a2890_pctl_get_group_pins(struct pinctrl_dev *pctldev,
 | 
						|
					 unsigned selector,
 | 
						|
					 const unsigned **pins,
 | 
						|
					 unsigned *num_pins)
 | 
						|
{
 | 
						|
	*pins = &sf19a2890_gpio_pins[selector].number;
 | 
						|
	*num_pins = 1;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void sf19a2890_pctl_pin_dbg_show(struct pinctrl_dev *pctldev,
 | 
						|
					struct seq_file *s, unsigned offset)
 | 
						|
{
 | 
						|
	struct sf_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
 | 
						|
	u32 conf = sf_pinctrl_rd(pc, SF19A28_REG_PC(offset));
 | 
						|
	u32 mux = sf_pinctrl_rd(pc, SF19A28_REG_PMX(offset));
 | 
						|
 | 
						|
	if (!(mux & PMX_FUNC_SW))
 | 
						|
		seq_puts(s, "Forced OE");
 | 
						|
	else if (mux & PMX_FMUX_SEL)
 | 
						|
		seq_puts(s, "GPIO");
 | 
						|
	else
 | 
						|
		seq_printf(s, "Func%lu", mux & PMX_MODE);
 | 
						|
	seq_puts(s, " |");
 | 
						|
 | 
						|
	if (!(conf & PC_OEN) && !(mux & PMX_FUNC_SW))
 | 
						|
		seq_puts(s, " Output");
 | 
						|
	if ((conf & PC_ST))
 | 
						|
		seq_puts(s, " Schmitt_Trigger");
 | 
						|
	if ((conf & PC_IE))
 | 
						|
		seq_puts(s, " Input");
 | 
						|
	if ((conf & PC_PD))
 | 
						|
		seq_puts(s, " Pull_Down");
 | 
						|
	if ((conf & PC_PU))
 | 
						|
		seq_puts(s, " Pull_Up");
 | 
						|
 | 
						|
	seq_printf(s, " Drive: %lu mA",
 | 
						|
		   DRIVE_MIN + (conf & PC_DS) * DRIVE_STEP);
 | 
						|
}
 | 
						|
 | 
						|
static const struct pinctrl_ops sf19a2890_pctl_ops = {
 | 
						|
	.get_groups_count = sf19a2890_pctl_get_groups_count,
 | 
						|
	.get_group_name = sf19a2890_pctl_get_group_name,
 | 
						|
	.get_group_pins = sf19a2890_pctl_get_group_pins,
 | 
						|
	.pin_dbg_show = sf19a2890_pctl_pin_dbg_show,
 | 
						|
	.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
 | 
						|
	.dt_free_map = pinconf_generic_dt_free_map,
 | 
						|
};
 | 
						|
 | 
						|
static int sf19a2890_pmx_free(struct pinctrl_dev *pctldev, unsigned offset)
 | 
						|
{
 | 
						|
	struct sf_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
 | 
						|
	sf_pinctrl_rmw(pc, SF19A28_REG_PC(offset), PC_IE, PC_OEN);
 | 
						|
	sf_pinctrl_rmw(pc, SF19A28_REG_PMX(offset), PMX_FUNC_SW, 0);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int sf19a2890_pmx_get_functions_count(struct pinctrl_dev *pctldev)
 | 
						|
{
 | 
						|
	return SF19A28_NUM_FUNCS;
 | 
						|
}
 | 
						|
 | 
						|
static const char *sf19a2890_pmx_get_function_name(struct pinctrl_dev *pctldev,
 | 
						|
						   unsigned selector)
 | 
						|
{
 | 
						|
	return sf19a2890_functions[selector];
 | 
						|
}
 | 
						|
 | 
						|
static int sf19a2890_pmx_get_function_groups(struct pinctrl_dev *pctldev,
 | 
						|
					     unsigned selector,
 | 
						|
					     const char *const **groups,
 | 
						|
					     unsigned *const num_groups)
 | 
						|
{
 | 
						|
	/* every pin can do every function */
 | 
						|
	*groups = sf19a2890_gpio_groups;
 | 
						|
	*num_groups = SF19A28_NUM_GPIOS;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int sf19a2890_pmx_set(struct pinctrl_dev *pctldev,
 | 
						|
			     unsigned func_selector, unsigned group_selector)
 | 
						|
{
 | 
						|
	struct sf_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
 | 
						|
	unsigned pin = group_selector;
 | 
						|
 | 
						|
	sf_pinctrl_wr(pc, SF19A28_REG_PMX(pin),
 | 
						|
		      PMX_FUNC_SW | FIELD_PREP(PMX_MODE, func_selector));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int sf19a2890_pmx_gpio_request_enable(struct pinctrl_dev *pctldev,
 | 
						|
					     struct pinctrl_gpio_range *range,
 | 
						|
					     unsigned offset)
 | 
						|
{
 | 
						|
	struct sf_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
 | 
						|
	/* Set to GPIO mode & Let peripheral control OEN */
 | 
						|
	sf_pinctrl_wr(pc, SF19A28_REG_PMX(offset), PMX_FUNC_SW | PMX_FMUX_SEL);
 | 
						|
	/*
 | 
						|
	 * Set PC_IE regardless of whether GPIO is in input mode.
 | 
						|
	 * Otherwise GPIO driver can't read back its status in output mode.
 | 
						|
	 */
 | 
						|
	sf_pinctrl_rmw(pc, SF19A28_REG_PC(offset), 0, PC_IE);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void sf19a2890_pmx_gpio_disable_free(struct pinctrl_dev *pctldev,
 | 
						|
					    struct pinctrl_gpio_range *range,
 | 
						|
					    unsigned offset)
 | 
						|
{
 | 
						|
	sf19a2890_pmx_free(pctldev, offset);
 | 
						|
}
 | 
						|
 | 
						|
static const struct pinmux_ops sf19a2890_pmx_ops = {
 | 
						|
	.free = sf19a2890_pmx_free,
 | 
						|
	.get_functions_count = sf19a2890_pmx_get_functions_count,
 | 
						|
	.get_function_name = sf19a2890_pmx_get_function_name,
 | 
						|
	.get_function_groups = sf19a2890_pmx_get_function_groups,
 | 
						|
	.set_mux = sf19a2890_pmx_set,
 | 
						|
	.gpio_request_enable = sf19a2890_pmx_gpio_request_enable,
 | 
						|
	.gpio_disable_free = sf19a2890_pmx_gpio_disable_free,
 | 
						|
};
 | 
						|
 | 
						|
static int sf19a2890_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin,
 | 
						|
				 unsigned long *config)
 | 
						|
{
 | 
						|
	struct sf_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
 | 
						|
	enum pin_config_param param = pinconf_to_config_param(*config);
 | 
						|
	u32 arg = 0;
 | 
						|
	u32 val = 0;
 | 
						|
 | 
						|
	if (pin >= SF19A28_NUM_GPIOS)
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	val = sf_pinctrl_rd(pc, SF19A28_REG_PC(pin));
 | 
						|
 | 
						|
	switch (param) {
 | 
						|
	case PIN_CONFIG_INPUT_SCHMITT:
 | 
						|
		val &= PC_ST;
 | 
						|
		if (val)
 | 
						|
			arg = 1;
 | 
						|
		break;
 | 
						|
 | 
						|
	case PIN_CONFIG_INPUT_ENABLE:
 | 
						|
		val &= PC_IE;
 | 
						|
		if (val)
 | 
						|
			arg = 1;
 | 
						|
		break;
 | 
						|
 | 
						|
	case PIN_CONFIG_BIAS_PULL_DOWN:
 | 
						|
		val &= PC_PD;
 | 
						|
		if (val)
 | 
						|
			arg = 1;
 | 
						|
		break;
 | 
						|
 | 
						|
	case PIN_CONFIG_BIAS_PULL_UP:
 | 
						|
		val &= PC_PU;
 | 
						|
		if (val)
 | 
						|
			arg = 1;
 | 
						|
		break;
 | 
						|
 | 
						|
	case PIN_CONFIG_DRIVE_STRENGTH:
 | 
						|
		arg = DRIVE_MIN + (val & PC_DS) * DRIVE_STEP;
 | 
						|
		break;
 | 
						|
 | 
						|
	default:
 | 
						|
		return -ENOTSUPP;
 | 
						|
	}
 | 
						|
 | 
						|
	*config = pinconf_to_config_packed(param, arg);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int sf19a2890_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin,
 | 
						|
				 unsigned long *configs, unsigned num_configs)
 | 
						|
{
 | 
						|
	struct sf_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
 | 
						|
	enum pin_config_param param;
 | 
						|
	u32 arg, val;
 | 
						|
	int i;
 | 
						|
 | 
						|
	val = sf_pinctrl_rd(pc, SF19A28_REG_PC(pin));
 | 
						|
 | 
						|
	if (pin >= SF19A28_NUM_GPIOS)
 | 
						|
		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_INPUT_SCHMITT_ENABLE:
 | 
						|
			if (arg)
 | 
						|
				val |= PC_ST;
 | 
						|
			else
 | 
						|
				val &= ~PC_ST;
 | 
						|
			break;
 | 
						|
 | 
						|
		case PIN_CONFIG_INPUT_ENABLE:
 | 
						|
			if (arg)
 | 
						|
				val |= PC_IE;
 | 
						|
			else
 | 
						|
				val &= ~PC_IE;
 | 
						|
			break;
 | 
						|
 | 
						|
		case PIN_CONFIG_BIAS_PULL_DOWN:
 | 
						|
			if (arg) {
 | 
						|
				val |= PC_PD;
 | 
						|
				val &= ~PC_PU;
 | 
						|
			} else {
 | 
						|
				val &= ~PC_PD;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
 | 
						|
		case PIN_CONFIG_BIAS_PULL_UP:
 | 
						|
			if (arg) {
 | 
						|
				val |= PC_PU;
 | 
						|
				val &= ~PC_PD;
 | 
						|
			} else {
 | 
						|
				val &= ~PC_PU;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
 | 
						|
		case PIN_CONFIG_DRIVE_STRENGTH:
 | 
						|
			val &= ~PC_DS;
 | 
						|
			if (arg > DRIVE_MAX)
 | 
						|
				val |= PC_DS;
 | 
						|
			else if (arg > DRIVE_MIN)
 | 
						|
				val |= FIELD_PREP(PC_DS, (arg - DRIVE_MIN) /
 | 
						|
								 DRIVE_STEP);
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		sf_pinctrl_wr(pc, SF19A28_REG_PC(pin), val);
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static const struct pinconf_ops sf19a2890_pinconf_ops = {
 | 
						|
	.is_generic = true,
 | 
						|
	.pin_config_get = sf19a2890_pinconf_get,
 | 
						|
	.pin_config_set = sf19a2890_pinconf_set,
 | 
						|
};
 | 
						|
 | 
						|
static const struct pinctrl_desc sf19a2890_pinctrl_desc = {
 | 
						|
	.name = MODULE_NAME,
 | 
						|
	.pins = sf19a2890_gpio_pins,
 | 
						|
	.npins = SF19A28_NUM_GPIOS,
 | 
						|
	.pctlops = &sf19a2890_pctl_ops,
 | 
						|
	.pmxops = &sf19a2890_pmx_ops,
 | 
						|
	.confops = &sf19a2890_pinconf_ops,
 | 
						|
	.owner = THIS_MODULE,
 | 
						|
};
 | 
						|
 | 
						|
static const struct pinctrl_gpio_range sf_pinctrl_gpio_range = {
 | 
						|
	.name = MODULE_NAME,
 | 
						|
	.npins = SF19A28_NUM_GPIOS,
 | 
						|
};
 | 
						|
 | 
						|
static const struct of_device_id sf_pinctrl_match[] = {
 | 
						|
	{ .compatible = "siflower,sf19a2890-pinctrl" },
 | 
						|
	{}
 | 
						|
};
 | 
						|
 | 
						|
static int sf_pinctrl_probe(struct platform_device *pdev)
 | 
						|
{
 | 
						|
	struct device *dev = &pdev->dev;
 | 
						|
	struct sf_pinctrl *pc;
 | 
						|
 | 
						|
	pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
 | 
						|
	if (!pc)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	platform_set_drvdata(pdev, pc);
 | 
						|
	pc->dev = dev;
 | 
						|
 | 
						|
	pc->base = devm_platform_ioremap_resource(pdev, 0);
 | 
						|
	if (IS_ERR(pc->base))
 | 
						|
		return PTR_ERR(pc->base);
 | 
						|
 | 
						|
	pc->pctl_desc = sf19a2890_pinctrl_desc;
 | 
						|
	pc->pctl_dev = devm_pinctrl_register(dev, &pc->pctl_desc, pc);
 | 
						|
	if (IS_ERR(pc->pctl_dev))
 | 
						|
		return PTR_ERR(pc->pctl_dev);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static struct platform_driver sf_pinctrl_driver = {
 | 
						|
	.probe = sf_pinctrl_probe,
 | 
						|
	.driver = {
 | 
						|
		.name = MODULE_NAME,
 | 
						|
		.of_match_table = sf_pinctrl_match,
 | 
						|
		.suppress_bind_attrs = true,
 | 
						|
	},
 | 
						|
};
 | 
						|
module_platform_driver(sf_pinctrl_driver);
 | 
						|
 | 
						|
MODULE_AUTHOR("Chuanhong Guo <gch981213@gmail.com>");
 | 
						|
MODULE_DESCRIPTION("Siflower SF19A2890 pinctrl driver");
 | 
						|
MODULE_LICENSE("GPL");
 |