-
Notifications
You must be signed in to change notification settings - Fork 54.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pinctrl: mlxbf3: Add pinctrl driver support
NVIDIA BlueField-3 SoC has a few pins that can be used as GPIOs or take the default hardware functionality. Add a driver for the pin muxing. Signed-off-by: Asmaa Mnebhi <asmaa@nvidia.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Link: https://lore.kernel.org/r/20230315215027.30685-3-asmaa@nvidia.com Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
- Loading branch information
Showing
3 changed files
with
334 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,320 @@ | ||
// SPDX-License-Identifier: GPL-2.0-only or BSD-3-Clause | ||
/* Copyright (C) 2022 NVIDIA CORPORATION & AFFILIATES */ | ||
|
||
#include <linux/bitfield.h> | ||
#include <linux/bitops.h> | ||
#include <linux/err.h> | ||
#include <linux/io.h> | ||
#include <linux/module.h> | ||
#include <linux/mod_devicetable.h> | ||
#include <linux/platform_device.h> | ||
#include <linux/types.h> | ||
|
||
#include <linux/pinctrl/pinctrl.h> | ||
#include <linux/pinctrl/pinmux.h> | ||
|
||
#define MLXBF3_NGPIOS_GPIO0 32 | ||
#define MLXBF3_MAX_GPIO_PINS 56 | ||
|
||
enum { | ||
MLXBF3_GPIO_HW_MODE, | ||
MLXBF3_GPIO_SW_MODE, | ||
}; | ||
|
||
struct mlxbf3_pinctrl { | ||
void __iomem *fw_ctrl_set0; | ||
void __iomem *fw_ctrl_clr0; | ||
void __iomem *fw_ctrl_set1; | ||
void __iomem *fw_ctrl_clr1; | ||
struct device *dev; | ||
struct pinctrl_dev *pctl; | ||
struct pinctrl_gpio_range gpio_range; | ||
}; | ||
|
||
#define MLXBF3_GPIO_RANGE(_id, _pinbase, _gpiobase, _npins) \ | ||
{ \ | ||
.name = "mlxbf3_gpio_range", \ | ||
.id = _id, \ | ||
.base = _gpiobase, \ | ||
.pin_base = _pinbase, \ | ||
.npins = _npins, \ | ||
} | ||
|
||
static struct pinctrl_gpio_range mlxbf3_pinctrl_gpio_ranges[] = { | ||
MLXBF3_GPIO_RANGE(0, 0, 480, 32), | ||
MLXBF3_GPIO_RANGE(1, 32, 456, 24), | ||
}; | ||
|
||
static const struct pinctrl_pin_desc mlxbf3_pins[] = { | ||
PINCTRL_PIN(0, "gpio0"), | ||
PINCTRL_PIN(1, "gpio1"), | ||
PINCTRL_PIN(2, "gpio2"), | ||
PINCTRL_PIN(3, "gpio3"), | ||
PINCTRL_PIN(4, "gpio4"), | ||
PINCTRL_PIN(5, "gpio5"), | ||
PINCTRL_PIN(6, "gpio6"), | ||
PINCTRL_PIN(7, "gpio7"), | ||
PINCTRL_PIN(8, "gpio8"), | ||
PINCTRL_PIN(9, "gpio9"), | ||
PINCTRL_PIN(10, "gpio10"), | ||
PINCTRL_PIN(11, "gpio11"), | ||
PINCTRL_PIN(12, "gpio12"), | ||
PINCTRL_PIN(13, "gpio13"), | ||
PINCTRL_PIN(14, "gpio14"), | ||
PINCTRL_PIN(15, "gpio15"), | ||
PINCTRL_PIN(16, "gpio16"), | ||
PINCTRL_PIN(17, "gpio17"), | ||
PINCTRL_PIN(18, "gpio18"), | ||
PINCTRL_PIN(19, "gpio19"), | ||
PINCTRL_PIN(20, "gpio20"), | ||
PINCTRL_PIN(21, "gpio21"), | ||
PINCTRL_PIN(22, "gpio22"), | ||
PINCTRL_PIN(23, "gpio23"), | ||
PINCTRL_PIN(24, "gpio24"), | ||
PINCTRL_PIN(25, "gpio25"), | ||
PINCTRL_PIN(26, "gpio26"), | ||
PINCTRL_PIN(27, "gpio27"), | ||
PINCTRL_PIN(28, "gpio28"), | ||
PINCTRL_PIN(29, "gpio29"), | ||
PINCTRL_PIN(30, "gpio30"), | ||
PINCTRL_PIN(31, "gpio31"), | ||
PINCTRL_PIN(32, "gpio32"), | ||
PINCTRL_PIN(33, "gpio33"), | ||
PINCTRL_PIN(34, "gpio34"), | ||
PINCTRL_PIN(35, "gpio35"), | ||
PINCTRL_PIN(36, "gpio36"), | ||
PINCTRL_PIN(37, "gpio37"), | ||
PINCTRL_PIN(38, "gpio38"), | ||
PINCTRL_PIN(39, "gpio39"), | ||
PINCTRL_PIN(40, "gpio40"), | ||
PINCTRL_PIN(41, "gpio41"), | ||
PINCTRL_PIN(42, "gpio42"), | ||
PINCTRL_PIN(43, "gpio43"), | ||
PINCTRL_PIN(44, "gpio44"), | ||
PINCTRL_PIN(45, "gpio45"), | ||
PINCTRL_PIN(46, "gpio46"), | ||
PINCTRL_PIN(47, "gpio47"), | ||
PINCTRL_PIN(48, "gpio48"), | ||
PINCTRL_PIN(49, "gpio49"), | ||
PINCTRL_PIN(50, "gpio50"), | ||
PINCTRL_PIN(51, "gpio51"), | ||
PINCTRL_PIN(52, "gpio52"), | ||
PINCTRL_PIN(53, "gpio53"), | ||
PINCTRL_PIN(54, "gpio54"), | ||
PINCTRL_PIN(55, "gpio55"), | ||
}; | ||
|
||
/* | ||
* All single-pin functions can be mapped to any GPIO, however pinmux applies | ||
* functions to pin groups and only those groups declared as supporting that | ||
* function. To make this work we must put each pin in its own dummy group so | ||
* that the functions can be described as applying to all pins. | ||
* We use the same name as in the datasheet. | ||
*/ | ||
static const char * const mlxbf3_pinctrl_single_group_names[] = { | ||
"gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", | ||
"gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15", | ||
"gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22", "gpio23", | ||
"gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", | ||
"gpio32", "gpio33", "gpio34", "gpio35", "gpio36", "gpio37", "gpio38", "gpio39", | ||
"gpio40", "gpio41", "gpio42", "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", | ||
"gpio48", "gpio49", "gpio50", "gpio51", "gpio52", "gpio53", "gpio54", "gpio55", | ||
}; | ||
|
||
static int mlxbf3_get_groups_count(struct pinctrl_dev *pctldev) | ||
{ | ||
/* Number single-pin groups */ | ||
return MLXBF3_MAX_GPIO_PINS; | ||
} | ||
|
||
static const char *mlxbf3_get_group_name(struct pinctrl_dev *pctldev, | ||
unsigned int selector) | ||
{ | ||
return mlxbf3_pinctrl_single_group_names[selector]; | ||
} | ||
|
||
static int mlxbf3_get_group_pins(struct pinctrl_dev *pctldev, | ||
unsigned int selector, | ||
const unsigned int **pins, | ||
unsigned int *num_pins) | ||
{ | ||
/* return the dummy group for a single pin */ | ||
*pins = &selector; | ||
*num_pins = 1; | ||
|
||
return 0; | ||
} | ||
|
||
static const struct pinctrl_ops mlxbf3_pinctrl_group_ops = { | ||
.get_groups_count = mlxbf3_get_groups_count, | ||
.get_group_name = mlxbf3_get_group_name, | ||
.get_group_pins = mlxbf3_get_group_pins, | ||
}; | ||
|
||
/* | ||
* Only 2 functions are supported and they apply to all pins: | ||
* 1) Default hardware functionality | ||
* 2) Software controlled GPIO | ||
*/ | ||
static const char * const mlxbf3_gpiofunc_group_names[] = { "swctrl" }; | ||
static const char * const mlxbf3_hwfunc_group_names[] = { "hwctrl" }; | ||
|
||
struct pinfunction mlxbf3_pmx_funcs[] = { | ||
PINCTRL_PINFUNCTION("hwfunc", mlxbf3_hwfunc_group_names, 1), | ||
PINCTRL_PINFUNCTION("gpiofunc", mlxbf3_gpiofunc_group_names, 1), | ||
}; | ||
|
||
static int mlxbf3_pmx_get_funcs_count(struct pinctrl_dev *pctldev) | ||
{ | ||
return ARRAY_SIZE(mlxbf3_pmx_funcs); | ||
} | ||
|
||
static const char *mlxbf3_pmx_get_func_name(struct pinctrl_dev *pctldev, | ||
unsigned int selector) | ||
{ | ||
return mlxbf3_pmx_funcs[selector].name; | ||
} | ||
|
||
static int mlxbf3_pmx_get_groups(struct pinctrl_dev *pctldev, | ||
unsigned int selector, | ||
const char * const **groups, | ||
unsigned int * const num_groups) | ||
{ | ||
*groups = mlxbf3_pmx_funcs[selector].groups; | ||
*num_groups = MLXBF3_MAX_GPIO_PINS; | ||
|
||
return 0; | ||
} | ||
|
||
static int mlxbf3_pmx_set(struct pinctrl_dev *pctldev, | ||
unsigned int selector, | ||
unsigned int group) | ||
{ | ||
struct mlxbf3_pinctrl *priv = pinctrl_dev_get_drvdata(pctldev); | ||
|
||
if (selector == MLXBF3_GPIO_HW_MODE) { | ||
if (group < MLXBF3_NGPIOS_GPIO0) | ||
writel(BIT(group), priv->fw_ctrl_clr0); | ||
else | ||
writel(BIT(group % MLXBF3_NGPIOS_GPIO0), priv->fw_ctrl_clr1); | ||
} | ||
|
||
if (selector == MLXBF3_GPIO_SW_MODE) { | ||
if (group < MLXBF3_NGPIOS_GPIO0) | ||
writel(BIT(group), priv->fw_ctrl_set0); | ||
else | ||
writel(BIT(group % MLXBF3_NGPIOS_GPIO0), priv->fw_ctrl_set1); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static int mlxbf3_gpio_request_enable(struct pinctrl_dev *pctldev, | ||
struct pinctrl_gpio_range *range, | ||
unsigned int offset) | ||
{ | ||
struct mlxbf3_pinctrl *priv = pinctrl_dev_get_drvdata(pctldev); | ||
|
||
if (offset < MLXBF3_NGPIOS_GPIO0) | ||
writel(BIT(offset), priv->fw_ctrl_set0); | ||
else | ||
writel(BIT(offset % MLXBF3_NGPIOS_GPIO0), priv->fw_ctrl_set1); | ||
|
||
return 0; | ||
} | ||
|
||
static void mlxbf3_gpio_disable_free(struct pinctrl_dev *pctldev, | ||
struct pinctrl_gpio_range *range, | ||
unsigned int offset) | ||
{ | ||
struct mlxbf3_pinctrl *priv = pinctrl_dev_get_drvdata(pctldev); | ||
|
||
/* disable GPIO functionality by giving control back to hardware */ | ||
if (offset < MLXBF3_NGPIOS_GPIO0) | ||
writel(BIT(offset), priv->fw_ctrl_clr0); | ||
else | ||
writel(BIT(offset % MLXBF3_NGPIOS_GPIO0), priv->fw_ctrl_clr1); | ||
} | ||
|
||
static const struct pinmux_ops mlxbf3_pmx_ops = { | ||
.get_functions_count = mlxbf3_pmx_get_funcs_count, | ||
.get_function_name = mlxbf3_pmx_get_func_name, | ||
.get_function_groups = mlxbf3_pmx_get_groups, | ||
.set_mux = mlxbf3_pmx_set, | ||
.gpio_request_enable = mlxbf3_gpio_request_enable, | ||
.gpio_disable_free = mlxbf3_gpio_disable_free, | ||
}; | ||
|
||
static struct pinctrl_desc mlxbf3_pin_desc = { | ||
.name = "pinctrl-mlxbf3", | ||
.pins = mlxbf3_pins, | ||
.npins = ARRAY_SIZE(mlxbf3_pins), | ||
.pctlops = &mlxbf3_pinctrl_group_ops, | ||
.pmxops = &mlxbf3_pmx_ops, | ||
.owner = THIS_MODULE, | ||
}; | ||
|
||
static_assert(ARRAY_SIZE(mlxbf3_pinctrl_single_group_names) == MLXBF3_MAX_GPIO_PINS); | ||
|
||
static int mlxbf3_pinctrl_probe(struct platform_device *pdev) | ||
{ | ||
struct device *dev = &pdev->dev; | ||
struct mlxbf3_pinctrl *priv; | ||
int ret; | ||
|
||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
if (!priv) | ||
return -ENOMEM; | ||
|
||
priv->dev = &pdev->dev; | ||
|
||
priv->fw_ctrl_set0 = devm_platform_ioremap_resource(pdev, 0); | ||
if (IS_ERR(priv->fw_ctrl_set0)) | ||
return PTR_ERR(priv->fw_ctrl_set0); | ||
|
||
priv->fw_ctrl_clr0 = devm_platform_ioremap_resource(pdev, 1); | ||
if (IS_ERR(priv->fw_ctrl_set0)) | ||
return PTR_ERR(priv->fw_ctrl_set0); | ||
|
||
priv->fw_ctrl_set1 = devm_platform_ioremap_resource(pdev, 2); | ||
if (IS_ERR(priv->fw_ctrl_set0)) | ||
return PTR_ERR(priv->fw_ctrl_set0); | ||
|
||
priv->fw_ctrl_clr1 = devm_platform_ioremap_resource(pdev, 3); | ||
if (IS_ERR(priv->fw_ctrl_set0)) | ||
return PTR_ERR(priv->fw_ctrl_set0); | ||
|
||
ret = devm_pinctrl_register_and_init(dev, | ||
&mlxbf3_pin_desc, | ||
priv, | ||
&priv->pctl); | ||
if (ret) | ||
return dev_err_probe(dev, ret, "Failed to register pinctrl\n"); | ||
|
||
ret = pinctrl_enable(priv->pctl); | ||
if (ret) | ||
return dev_err_probe(dev, ret, "Failed to enable pinctrl\n"); | ||
|
||
pinctrl_add_gpio_ranges(priv->pctl, mlxbf3_pinctrl_gpio_ranges, 2); | ||
|
||
return 0; | ||
} | ||
|
||
static const struct acpi_device_id mlxbf3_pinctrl_acpi_ids[] = { | ||
{ "MLNXBF34", 0 }, | ||
{} | ||
}; | ||
MODULE_DEVICE_TABLE(acpi, mlxbf3_pinctrl_acpi_ids); | ||
|
||
static struct platform_driver mlxbf3_pinctrl_driver = { | ||
.driver = { | ||
.name = "pinctrl-mlxbf3", | ||
.acpi_match_table = mlxbf3_pinctrl_acpi_ids, | ||
}, | ||
.probe = mlxbf3_pinctrl_probe, | ||
}; | ||
module_platform_driver(mlxbf3_pinctrl_driver); | ||
|
||
MODULE_DESCRIPTION("NVIDIA pinctrl driver"); | ||
MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>"); | ||
MODULE_LICENSE("Dual BSD/GPL"); |