// SPDX-License-Identifier: GPL-2.0-only
/*
* Pinctrl driver for the Mobileye EyeQ5 platform.
*
* The registers are located in a syscon region called OLB. There are two pin
* banks, each being controlled by 5 registers (see enum eq5p_regs) for
* pull-down, pull-up, drive strength and muxing.
*
* For each pin, muxing is between two functions: (0) GPIO or (1) another one
* that is pin-dependent. Functions are declared statically in this driver.
*
* We create pinctrl groups that are 1:1 equivalent to pins: each group has a
* single pin, and its index/selector is the pin number.
*
* We use eq5p_ as prefix, as-in "EyeQ5 Pinctrl", but way shorter.
*
* Copyright (C) 2024 Mobileye Vision Technologies Ltd.
*/
#include <linux/array_size.h>
#include <linux/auxiliary_bus.h>
#include <linux/bits.h>
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/mod_devicetable.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include "core.h"
#include "pinctrl-utils.h"
struct eq5p_pinctrl {
struct pinctrl_desc desc;
void __iomem *base;
};
enum eq5p_bank {
EQ5P_BANK_A,
EQ5P_BANK_B,
EQ5P_BANK_COUNT,
};
enum eq5p_regs {
EQ5P_PD,
EQ5P_PU,
EQ5P_DS_LOW,
EQ5P_DS_HIGH,
EQ5P_IOCR,
EQ5P_REG_COUNT,
};
static const unsigned int eq5p_regs[EQ5P_BANK_COUNT][EQ5P_REG_COUNT] = {
[EQ5P_BANK_A] = {0x0C0, 0x0C4, 0x0D0, 0x0D4, 0x0B0},
[EQ5P_BANK_B] = {0x0C8, 0x0CC, 0x0D8, 0x0DC, 0x0B4},
};
/*
* Drive strength; two bits per pin.
*/
#define EQ5P_DS_MASK GENMASK(1, 0)
/*
* Comments to the right of each pin are the "signal name" in the datasheet.
*/
static const struct pinctrl_pin_desc eq5p_pins[] = {
/* Bank A */
PINCTRL_PIN(0, "PA0" ), /* A0_TIMER0_CK */
PINCTRL_PIN(1, "PA1" ), /* A1_TIMER0_EOC */
PINCTRL_PIN(2, "PA2" ), /* A2_TIMER1_CK */
PINCTRL_PIN(3, "PA3" ), /* A3_TIMER1_EOC */
PINCTRL_PIN(4, "PA4" ), /* A4_TIMER2_CK */
PINCTRL_PIN(5, "PA5" ), /* A5_TIMER2_EOC */
PINCTRL_PIN(6, "PA6" ), /* A6_TIMER5_EXT_INCAP1 */
PINCTRL_PIN(7, "PA7" ), /* A7_TIMER5_EXT_INCAP2 */
PINCTRL_PIN(8, "PA8" ), /* A8_TIMER5_EXT_OUTCMP1 */
PINCTRL_PIN(9, "PA9" ), /* A9_TIMER5_EXT_OUTCMP2 */
PINCTRL_PIN(10, "PA10" ), /* A10_UART_0_TX */
PINCTRL_PIN(11, "PA11" ), /* A11_UART_0_RX */
PINCTRL_PIN(12, "PA12" ), /* A12_UART_1_TX */
PINCTRL_PIN(13, "PA13" ), /* A13_UART_1_RX */
PINCTRL_PIN(14, "PA14" ), /* A14_CAN_0_TX */
PINCTRL_PIN(15, "PA15" ), /* A15_CAN_0_RX */
PINCTRL_PIN(16, "PA16" ), /* A16_CAN_1_TX */
PINCTRL_PIN(17, "PA17" ), /* A17_CAN_1_RX */
PINCTRL_PIN(18, "PA18" ), /* A18_SPI_0_DO */
PINCTRL_PIN(19, "PA19" ), /* A19_SPI_0_DI */
PINCTRL_PIN(20, "PA20" ), /* A20_SPI_0_CK */
PINCTRL_PIN(21, "PA21" ), /* A21_SPI_0_CS0 */
PINCTRL_PIN(22, "PA22" ), /* A22_SPI_0_CS1 */
PINCTRL_PIN(23, "PA23" ), /* A23_SPI_1_DO */
PINCTRL_PIN(24, "PA24" ), /* A24_SPI_1_DI */
PINCTRL_PIN(25, "PA25" ), /* A25_SPI_1_CK */
PINCTRL_PIN(26, "PA26" ), /* A26_SPI_1_CS0 */
PINCTRL_PIN(27, "PA27" ), /* A27_SPI_1_CS1 */
PINCTRL_PIN(28, "PA28" ), /* A28_REF_CLK0 */
#define EQ5P_PIN_OFFSET_BANK_B 29
/* Bank B */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 0, "PB0" ), /* B0_TIMER3_CK */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 1, "PB1" ), /* B1_TIMER3_EOC */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 2, "PB2" ), /* B2_TIMER4_CK */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 3, "PB3" ), /* B3_TIMER4_EOC */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 4, "PB4" ), /* B4_TIMER6_EXT_INCAP1 */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 5, "PB5" ), /* B5_TIMER6_EXT_INCAP2 */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 6, "PB6" ), /* B6_TIMER6_EXT_OUTCMP1 */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 7, "PB7" ), /* B7_TIMER6_EXT_OUTCMP2 */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 8, "PB8" ), /* B8_UART_2_TX */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 9, "PB9" ), /* B9_UART_2_RX */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 10, "PB10" ), /* B10_CAN_2_TX */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 11, "PB11" ), /* B11_CAN_2_RX */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 12, "PB12" ), /* B12_SPI_2_DO */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 13, "PB13" ), /* B13_SPI_2_DI */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 14, "PB14" ), /* B14_SPI_2_CK */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 15, "PB15" ), /* B15_SPI_2_CS0 */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 16, "PB16" ), /* B16_SPI_2_CS1 */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 17, "PB17" ), /* B17_SPI_3_DO */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 18, "PB18" ), /* B18_SPI_3_DI */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 19, "PB19" ), /* B19_SPI_3_CK */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 20, "PB20" ), /* B20_SPI_3_CS0 */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 21, "PB21" ), /* B21_SPI_3_CS1 */
PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 22, "PB22" ), /* B22_MCLK0 */
};
static const char * const gpio_groups[] = {
/* Bank A */
"PA0" , "PA1" , "PA2" , "PA3" , "PA4" , "PA5" , "PA6" , "PA7" ,
"PA8" , "PA9" , "PA10" , "PA11" , "PA12" , "PA13" , "PA14" , "PA15" ,
"PA16" , "PA17" , "PA18" , "PA19" , "PA20" , "PA21" , "PA22" , "PA23" ,
"PA24" , "PA25" , "PA26" , "PA27" , "PA28" ,
/* Bank B */
"PB0" , "PB1" , "PB2" , "PB3" , "PB4" , "PB5" , "PB6" , "PB7" ,
"PB8" , "PB9" , "PB10" , "PB11" , "PB12" , "PB13" , "PB14" , "PB15" ,
"PB16" , "PB17" , "PB18" , "PB19" , "PB20" , "PB21" , "PB22" ,
};
/* Groups of functions on bank A */
static const char * const timer0_groups[] = { "PA0" , "PA1" };
static const char * const timer1_groups[] = { "PA2" , "PA3" };
static const char * const timer2_groups[] = { "PA4" , "PA5" };
static const char * const timer5_groups[] = { "PA6" , "PA7" , "PA8" , "PA9" };
static const char * const uart0_groups[] = { "PA10" , "PA11" };
static const char * const uart1_groups[] = { "PA12" , "PA13" };
static const char * const can0_groups[] = { "PA14" , "PA15" };
static const char * const can1_groups[] = { "PA16" , "PA17" };
static const char * const spi0_groups[] = { "PA18" , "PA19" , "PA20" , "PA21" , "PA22" };
static const char * const spi1_groups[] = { "PA23" , "PA24" , "PA25" , "PA26" , "PA27" };
static const char * const refclk0_groups[] = { "PA28" };
/* Groups of functions on bank B */
static const char * const timer3_groups[] = { "PB0" , "PB1" };
static const char * const timer4_groups[] = { "PB2" , "PB3" };
static const char * const timer6_groups[] = { "PB4" , "PB5" , "PB6" , "PB7" };
static const char * const uart2_groups[] = { "PB8" , "PB9" };
static const char * const can2_groups[] = { "PB10" , "PB11" };
static const char * const spi2_groups[] = { "PB12" , "PB13" , "PB14" , "PB15" , "PB16" };
static const char * const spi3_groups[] = { "PB17" , "PB18" , "PB19" , "PB20" , "PB21" };
static const char * const mclk0_groups[] = { "PB22" };
static const struct pinfunction eq5p_functions[] = {
/* GPIO having a fixed index is depended upon, see GPIO_FUNC_SELECTOR. */
PINCTRL_PINFUNCTION("gpio" , gpio_groups, ARRAY_SIZE(gpio_groups)),
#define GPIO_FUNC_SELECTOR 0
/* Bank A functions */
PINCTRL_PINFUNCTION("timer0" , timer0_groups, ARRAY_SIZE(timer0_groups)),
PINCTRL_PINFUNCTION("timer1" , timer1_groups, ARRAY_SIZE(timer1_groups)),
PINCTRL_PINFUNCTION("timer2" , timer2_groups, ARRAY_SIZE(timer2_groups)),
PINCTRL_PINFUNCTION("timer5" , timer5_groups, ARRAY_SIZE(timer5_groups)),
PINCTRL_PINFUNCTION("uart0" , uart0_groups, ARRAY_SIZE(uart0_groups)),
PINCTRL_PINFUNCTION("uart1" , uart1_groups, ARRAY_SIZE(uart1_groups)),
PINCTRL_PINFUNCTION("can0" , can0_groups, ARRAY_SIZE(can0_groups)),
PINCTRL_PINFUNCTION("can1" , can1_groups, ARRAY_SIZE(can1_groups)),
PINCTRL_PINFUNCTION("spi0" , spi0_groups, ARRAY_SIZE(spi0_groups)),
PINCTRL_PINFUNCTION("spi1" , spi1_groups, ARRAY_SIZE(spi1_groups)),
PINCTRL_PINFUNCTION("refclk0" , refclk0_groups, ARRAY_SIZE(refclk0_groups)),
/* Bank B functions */
PINCTRL_PINFUNCTION("timer3" , timer3_groups, ARRAY_SIZE(timer3_groups)),
PINCTRL_PINFUNCTION("timer4" , timer4_groups, ARRAY_SIZE(timer4_groups)),
PINCTRL_PINFUNCTION("timer6" , timer6_groups, ARRAY_SIZE(timer6_groups)),
PINCTRL_PINFUNCTION("uart2" , uart2_groups, ARRAY_SIZE(uart2_groups)),
PINCTRL_PINFUNCTION("can2" , can2_groups, ARRAY_SIZE(can2_groups)),
PINCTRL_PINFUNCTION("spi2" , spi2_groups, ARRAY_SIZE(spi2_groups)),
PINCTRL_PINFUNCTION("spi3" , spi3_groups, ARRAY_SIZE(spi3_groups)),
PINCTRL_PINFUNCTION("mclk0" , mclk0_groups, ARRAY_SIZE(mclk0_groups)),
};
static void eq5p_update_bits(const struct eq5p_pinctrl *pctrl,
enum eq5p_bank bank, enum eq5p_regs reg,
u32 mask, u32 val)
{
void __iomem *ptr = pctrl->base + eq5p_regs[bank][reg];
writel((readl(ptr) & ~mask) | (val & mask), ptr);
}
static bool eq5p_test_bit(const struct eq5p_pinctrl *pctrl,
enum eq5p_bank bank, enum eq5p_regs reg, int offset)
{
u32 val = readl(pctrl->base + eq5p_regs[bank][reg]);
if (WARN_ON(offset > 31))
return false ;
return (val & BIT(offset)) != 0;
}
static enum eq5p_bank eq5p_pin_to_bank(unsigned int pin)
{
if (pin < EQ5P_PIN_OFFSET_BANK_B)
return EQ5P_BANK_A;
else
return EQ5P_BANK_B;
}
static unsigned int eq5p_pin_to_offset(unsigned int pin)
{
if (pin < EQ5P_PIN_OFFSET_BANK_B)
return pin;
else
return pin - EQ5P_PIN_OFFSET_BANK_B;
}
static int eq5p_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
{
return ARRAY_SIZE(eq5p_pins);
}
static const char *eq5p_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
unsigned int selector)
{
return pctldev->desc->pins[selector].name;
}
static int eq5p_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
unsigned int selector,
const unsigned int **pins,
unsigned int *num_pins)
{
*pins = &pctldev->desc->pins[selector].number;
*num_pins = 1;
return 0;
}
static int eq5p_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
unsigned long *config)
{
enum pin_config_param param = pinconf_to_config_param(*config);
struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
unsigned int offset = eq5p_pin_to_offset(pin);
enum eq5p_bank bank = eq5p_pin_to_bank(pin);
u32 val_ds, arg;
bool pd, pu;
pd = eq5p_test_bit(pctrl, bank, EQ5P_PD, offset);
pu = eq5p_test_bit(pctrl, bank, EQ5P_PU, offset);
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
arg = !(pd || pu);
break ;
case PIN_CONFIG_BIAS_PULL_DOWN:
arg = pd;
break ;
case PIN_CONFIG_BIAS_PULL_UP:
arg = pu;
break ;
case PIN_CONFIG_DRIVE_STRENGTH:
offset *= 2; /* two bits per pin */
if (offset >= 32) {
val_ds = readl(pctrl->base + eq5p_regs[bank][EQ5P_DS_HIGH]);
offset -= 32;
} else {
val_ds = readl(pctrl->base + eq5p_regs[bank][EQ5P_DS_LOW]);
}
arg = (val_ds >> offset) & EQ5P_DS_MASK;
break ;
default :
return -ENOTSUPP;
}
*config = pinconf_to_config_packed(param, arg);
return 0;
}
static void eq5p_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev,
struct seq_file *s,
unsigned int pin)
{
struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
const char *pin_name = pctrl->desc.pins[pin].name;
unsigned int offset = eq5p_pin_to_offset(pin);
enum eq5p_bank bank = eq5p_pin_to_bank(pin);
const char *func_name, *bias;
unsigned long ds_config;
u32 drive_strength;
bool pd, pu;
int i, j;
/*
* First, let's get the function name. All pins have only two functions:
* GPIO (IOCR == 0) and something else (IOCR == 1).
*/
if (eq5p_test_bit(pctrl, bank, EQ5P_IOCR, offset)) {
func_name = NULL;
for (i = 0; i < ARRAY_SIZE(eq5p_functions); i++) {
if (i == GPIO_FUNC_SELECTOR)
continue ;
for (j = 0; j < eq5p_functions[i].ngroups; j++) {
/* Groups and pins are the same thing for us. */
const char *x = eq5p_functions[i].groups[j];
if (strcmp(x, pin_name) == 0) {
func_name = eq5p_functions[i].name;
break ;
}
}
if (func_name)
break ;
}
/*
* We have not found the function attached to this pin, this
* should never occur as all pins have exactly two functions.
*/
if (!func_name)
func_name = "unknown" ;
} else {
func_name = eq5p_functions[GPIO_FUNC_SELECTOR].name;
}
/* Second, we retrieve the bias. */
pd = eq5p_test_bit(pctrl, bank, EQ5P_PD, offset);
pu = eq5p_test_bit(pctrl, bank, EQ5P_PU, offset);
if (pd && pu)
bias = "both" ;
else if (pd && !pu)
bias = "pulldown" ;
else if (!pd && pu)
bias = "pullup" ;
else
bias = "none" ;
/* Third, we get the drive strength. */
ds_config = pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH, 0);
eq5p_pinconf_get(pctldev, pin, &ds_config);
drive_strength = pinconf_to_config_argument(ds_config);
seq_printf(s, "function=%s bias=%s drive_strength=%d" ,
func_name, bias, drive_strength);
}
static const struct pinctrl_ops eq5p_pinctrl_ops = {
.get_groups_count = eq5p_pinctrl_get_groups_count,
.get_group_name = eq5p_pinctrl_get_group_name,
.get_group_pins = eq5p_pinctrl_get_group_pins,
.pin_dbg_show = eq5p_pinctrl_pin_dbg_show,
.dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
.dt_free_map = pinctrl_utils_free_map,
};
static int eq5p_pinmux_get_functions_count(struct pinctrl_dev *pctldev)
{
return ARRAY_SIZE(eq5p_functions);
}
static const char *eq5p_pinmux_get_function_name(struct pinctrl_dev *pctldev,
unsigned int selector)
{
return eq5p_functions[selector].name;
}
static int eq5p_pinmux_get_function_groups(struct pinctrl_dev *pctldev,
unsigned int selector,
const char * const **groups,
unsigned int *num_groups)
{
*groups = eq5p_functions[selector].groups;
*num_groups = eq5p_functions[selector].ngroups;
return 0;
}
static int eq5p_pinmux_set_mux(struct pinctrl_dev *pctldev,
unsigned int func_selector, unsigned int pin)
{
struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
const char *func_name = eq5p_functions[func_selector].name;
const char *group_name = pctldev->desc->pins[pin].name;
bool is_gpio = func_selector == GPIO_FUNC_SELECTOR;
unsigned int offset = eq5p_pin_to_offset(pin);
enum eq5p_bank bank = eq5p_pin_to_bank(pin);
u32 mask, val;
dev_dbg(pctldev->dev, "func=%s group=%s\n" , func_name, group_name);
mask = BIT(offset);
val = is_gpio ? 0 : mask;
eq5p_update_bits(pctrl, bank, EQ5P_IOCR, mask, val);
return 0;
}
static int eq5p_pinmux_gpio_request_enable(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned int pin)
{
/* Pin numbers and group selectors are the same thing in our case. */
return eq5p_pinmux_set_mux(pctldev, GPIO_FUNC_SELECTOR, pin);
}
static const struct pinmux_ops eq5p_pinmux_ops = {
.get_functions_count = eq5p_pinmux_get_functions_count,
.get_function_name = eq5p_pinmux_get_function_name,
.get_function_groups = eq5p_pinmux_get_function_groups,
.set_mux = eq5p_pinmux_set_mux,
.gpio_request_enable = eq5p_pinmux_gpio_request_enable,
.strict = true ,
};
static int eq5p_pinconf_set_drive_strength(struct pinctrl_dev *pctldev,
unsigned int pin, u32 arg)
{
struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
unsigned int offset = eq5p_pin_to_offset(pin);
enum eq5p_bank bank = eq5p_pin_to_bank(pin);
unsigned int reg;
u32 mask, val;
if (arg & ~EQ5P_DS_MASK) {
dev_err(pctldev->dev, "Unsupported drive strength: %u\n" , arg);
return -EINVAL;
}
offset *= 2; /* two bits per pin */
if (offset >= 32) {
reg = EQ5P_DS_HIGH;
offset -= 32;
} else {
reg = EQ5P_DS_LOW;
}
mask = EQ5P_DS_MASK << offset;
val = arg << offset;
eq5p_update_bits(pctrl, bank, reg, mask, val);
return 0;
}
static int eq5p_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
unsigned long *configs, unsigned int num_configs)
{
struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
const char *pin_name = pctldev->desc->pins[pin].name;
unsigned int offset = eq5p_pin_to_offset(pin);
enum eq5p_bank bank = eq5p_pin_to_bank(pin);
struct device *dev = pctldev->dev;
u32 val = BIT(offset);
unsigned int i;
for (i = 0; i < num_configs; i++) {
enum pin_config_param param = pinconf_to_config_param(configs[i]);
u32 arg = pinconf_to_config_argument(configs[i]);
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
dev_dbg(dev, "pin=%s bias_disable\n" , pin_name);
eq5p_update_bits(pctrl, bank, EQ5P_PD, val, 0);
eq5p_update_bits(pctrl, bank, EQ5P_PU, val, 0);
break ;
case PIN_CONFIG_BIAS_PULL_DOWN:
dev_dbg(dev, "pin=%s bias_pull_down arg=%u\n" ,
pin_name, arg);
if (arg == 0) /* cannot connect to GND */
return -ENOTSUPP;
eq5p_update_bits(pctrl, bank, EQ5P_PD, val, val);
eq5p_update_bits(pctrl, bank, EQ5P_PU, val, 0);
break ;
case PIN_CONFIG_BIAS_PULL_UP:
dev_dbg(dev, "pin=%s bias_pull_up arg=%u\n" ,
pin_name, arg);
if (arg == 0) /* cannot connect to VDD */
return -ENOTSUPP;
eq5p_update_bits(pctrl, bank, EQ5P_PD, val, 0);
eq5p_update_bits(pctrl, bank, EQ5P_PU, val, val);
break ;
case PIN_CONFIG_DRIVE_STRENGTH:
dev_dbg(dev, "pin=%s drive_strength arg=%u\n" ,
pin_name, arg);
eq5p_pinconf_set_drive_strength(pctldev, pin, arg);
break ;
default :
dev_err(dev, "Unsupported pinconf %u\n" , param);
return -ENOTSUPP;
}
}
return 0;
}
static const struct pinconf_ops eq5p_pinconf_ops = {
.is_generic = true ,
.pin_config_get = eq5p_pinconf_get,
.pin_config_set = eq5p_pinconf_set,
/* Pins and groups are equivalent in this driver. */
.pin_config_group_get = eq5p_pinconf_get,
.pin_config_group_set = eq5p_pinconf_set,
};
static int eq5p_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct device *dev = &adev->dev;
struct pinctrl_dev *pctldev;
struct eq5p_pinctrl *pctrl;
int ret;
pctrl = devm_kzalloc(dev, sizeof (*pctrl), GFP_KERNEL);
if (!pctrl)
return -ENOMEM;
pctrl->base = (void __iomem *)dev_get_platdata(dev);
pctrl->desc.name = dev_name(dev);
pctrl->desc.pins = eq5p_pins;
pctrl->desc.npins = ARRAY_SIZE(eq5p_pins);
pctrl->desc.pctlops = &eq5p_pinctrl_ops;
pctrl->desc.pmxops = &eq5p_pinmux_ops;
pctrl->desc.confops = &eq5p_pinconf_ops;
pctrl->desc.owner = THIS_MODULE;
ret = devm_pinctrl_register_and_init(dev, &pctrl->desc, pctrl, &pctldev);
if (ret)
return dev_err_probe(dev, ret, "failed registering pinctrl device\n" );
ret = pinctrl_enable(pctldev);
if (ret)
return dev_err_probe(dev, ret, "failed enabling pinctrl device\n" );
return 0;
}
static const struct auxiliary_device_id eq5p_id_table[] = {
{ .name = "clk_eyeq.pinctrl" },
{}
};
MODULE_DEVICE_TABLE(auxiliary, eq5p_id_table);
static struct auxiliary_driver eq5p_driver = {
.probe = eq5p_probe,
.id_table = eq5p_id_table,
};
module_auxiliary_driver(eq5p_driver);
Messung V0.5 C=95 H=97 G=95
¤ Dauer der Verarbeitung: 0.1 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland