* [PATCH v4 04/10] pinctrl: axp209: rename everything from gpio to pctl
From: Quentin Schulz @ 2017-12-01 13:44 UTC (permalink / raw)
To: linus.walleij-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
wens-jdAy2FN1RRM, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
lee.jones-QSEj5FYQhm4dnm+yROfE0A
Cc: linux-gpio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Quentin Schulz
In-Reply-To: <cover.3ff63fdf302c6bda02ea7d160ad2aa5afee0899d.1512135804.git-series.quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
This driver used to do only GPIO features of the GPIOs in X-Powers
AXP20X. Now that we have migrated everything to the pinctrl subsystem
and added pinctrl features, rename everything related to pinctrl from
gpio to pctl to ease the understanding of differences between GPIO
and pinctrl features.
Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/pinctrl/pinctrl-axp209.c | 180 ++++++++++++++++----------------
1 file changed, 90 insertions(+), 90 deletions(-)
diff --git a/drivers/pinctrl/pinctrl-axp209.c b/drivers/pinctrl/pinctrl-axp209.c
index 2dc286f..6201d2b 100644
--- a/drivers/pinctrl/pinctrl-axp209.c
+++ b/drivers/pinctrl/pinctrl-axp209.c
@@ -57,7 +57,7 @@ struct axp20x_pinctrl_function {
unsigned int ngroups;
};
-struct axp20x_gpio {
+struct axp20x_pctl {
struct gpio_chip chip;
struct regmap *regmap;
struct pinctrl_dev *pctl_dev;
@@ -100,11 +100,11 @@ static int axp20x_gpio_input(struct gpio_chip *chip, unsigned offset)
static int axp20x_gpio_get(struct gpio_chip *chip, unsigned offset)
{
- struct axp20x_gpio *gpio = gpiochip_get_data(chip);
+ struct axp20x_pctl *pctl = gpiochip_get_data(chip);
unsigned int val;
int ret;
- ret = regmap_read(gpio->regmap, AXP20X_GPIO20_SS, &val);
+ ret = regmap_read(pctl->regmap, AXP20X_GPIO20_SS, &val);
if (ret)
return ret;
@@ -113,7 +113,7 @@ static int axp20x_gpio_get(struct gpio_chip *chip, unsigned offset)
static int axp20x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
{
- struct axp20x_gpio *gpio = gpiochip_get_data(chip);
+ struct axp20x_pctl *pctl = gpiochip_get_data(chip);
unsigned int val;
int reg, ret;
@@ -121,7 +121,7 @@ static int axp20x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
if (reg < 0)
return reg;
- ret = regmap_read(gpio->regmap, reg, &val);
+ ret = regmap_read(pctl->regmap, reg, &val);
if (ret)
return ret;
@@ -151,14 +151,14 @@ static int axp20x_gpio_output(struct gpio_chip *chip, unsigned offset,
static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset,
int value)
{
- struct axp20x_gpio *gpio = gpiochip_get_data(chip);
+ struct axp20x_pctl *pctl = gpiochip_get_data(chip);
int reg;
reg = axp20x_gpio_get_reg(offset);
if (reg < 0)
return;
- regmap_update_bits(gpio->regmap, reg,
+ regmap_update_bits(pctl->regmap, reg,
AXP20X_GPIO_FUNCTIONS,
value ? AXP20X_GPIO_FUNCTION_OUT_HIGH :
AXP20X_GPIO_FUNCTION_OUT_LOW);
@@ -167,30 +167,30 @@ static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset,
static int axp20x_pmx_set(struct pinctrl_dev *pctldev, unsigned int offset,
u8 config)
{
- struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+ struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
int reg;
reg = axp20x_gpio_get_reg(offset);
if (reg < 0)
return reg;
- return regmap_update_bits(gpio->regmap, reg, AXP20X_GPIO_FUNCTIONS,
+ return regmap_update_bits(pctl->regmap, reg, AXP20X_GPIO_FUNCTIONS,
config);
}
static int axp20x_pmx_func_cnt(struct pinctrl_dev *pctldev)
{
- struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+ struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
- return ARRAY_SIZE(gpio->funcs);
+ return ARRAY_SIZE(pctl->funcs);
}
static const char *axp20x_pmx_func_name(struct pinctrl_dev *pctldev,
unsigned int selector)
{
- struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+ struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
- return gpio->funcs[selector].name;
+ return pctl->funcs[selector].name;
}
static int axp20x_pmx_func_groups(struct pinctrl_dev *pctldev,
@@ -198,10 +198,10 @@ static int axp20x_pmx_func_groups(struct pinctrl_dev *pctldev,
const char * const **groups,
unsigned int *num_groups)
{
- struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+ struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
- *groups = gpio->funcs[selector].groups;
- *num_groups = gpio->funcs[selector].ngroups;
+ *groups = pctl->funcs[selector].groups;
+ *num_groups = pctl->funcs[selector].ngroups;
return 0;
}
@@ -209,18 +209,18 @@ static int axp20x_pmx_func_groups(struct pinctrl_dev *pctldev,
static int axp20x_pmx_set_mux(struct pinctrl_dev *pctldev,
unsigned int function, unsigned int group)
{
- struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+ struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
unsigned int mask;
/* Every pin supports GPIO_OUT and GPIO_IN functions */
if (function <= AXP20X_FUNC_GPIO_IN)
return axp20x_pmx_set(pctldev, group,
- gpio->funcs[function].muxval);
+ pctl->funcs[function].muxval);
if (function == AXP20X_FUNC_LDO)
- mask = gpio->desc->ldo_mask;
+ mask = pctl->desc->ldo_mask;
else
- mask = gpio->desc->adc_mask;
+ mask = pctl->desc->adc_mask;
if (!(BIT(group) & mask))
return -EINVAL;
@@ -234,21 +234,21 @@ static int axp20x_pmx_set_mux(struct pinctrl_dev *pctldev,
if (function == AXP20X_FUNC_LDO)
return 0;
- return axp20x_pmx_set(pctldev, group, gpio->funcs[function].muxval);
+ return axp20x_pmx_set(pctldev, group, pctl->funcs[function].muxval);
}
static int axp20x_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned int offset, bool input)
{
- struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+ struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
if (input)
return axp20x_pmx_set(pctldev, offset,
- gpio->funcs[AXP20X_FUNC_GPIO_IN].muxval);
+ pctl->funcs[AXP20X_FUNC_GPIO_IN].muxval);
return axp20x_pmx_set(pctldev, offset,
- gpio->funcs[AXP20X_FUNC_GPIO_OUT].muxval);
+ pctl->funcs[AXP20X_FUNC_GPIO_OUT].muxval);
}
static const struct pinmux_ops axp20x_pmx_ops = {
@@ -262,17 +262,17 @@ static const struct pinmux_ops axp20x_pmx_ops = {
static int axp20x_groups_cnt(struct pinctrl_dev *pctldev)
{
- struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+ struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
- return gpio->desc->npins;
+ return pctl->desc->npins;
}
static int axp20x_group_pins(struct pinctrl_dev *pctldev, unsigned int selector,
const unsigned int **pins, unsigned int *num_pins)
{
- struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+ struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
- *pins = (unsigned int *)&gpio->desc->pins[selector];
+ *pins = (unsigned int *)&pctl->desc->pins[selector];
*num_pins = 1;
return 0;
@@ -281,9 +281,9 @@ static int axp20x_group_pins(struct pinctrl_dev *pctldev, unsigned int selector,
static const char *axp20x_group_name(struct pinctrl_dev *pctldev,
unsigned int selector)
{
- struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+ struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
- return gpio->desc->pins[selector].name;
+ return pctl->desc->pins[selector].name;
}
static const struct pinctrl_ops axp20x_pctrl_ops = {
@@ -318,46 +318,46 @@ static void axp20x_funcs_groups_from_mask(struct device *dev, unsigned int mask,
static void axp20x_build_funcs_groups(struct platform_device *pdev)
{
- struct axp20x_gpio *gpio = platform_get_drvdata(pdev);
+ struct axp20x_pctl *pctl = platform_get_drvdata(pdev);
int i, pin;
- gpio->funcs[AXP20X_FUNC_GPIO_OUT].name = "gpio_out";
- gpio->funcs[AXP20X_FUNC_GPIO_OUT].muxval = AXP20X_MUX_GPIO_OUT;
- gpio->funcs[AXP20X_FUNC_GPIO_IN].name = "gpio_in";
- gpio->funcs[AXP20X_FUNC_GPIO_IN].muxval = AXP20X_MUX_GPIO_IN;
- gpio->funcs[AXP20X_FUNC_LDO].name = "ldo";
+ pctl->funcs[AXP20X_FUNC_GPIO_OUT].name = "gpio_out";
+ pctl->funcs[AXP20X_FUNC_GPIO_OUT].muxval = AXP20X_MUX_GPIO_OUT;
+ pctl->funcs[AXP20X_FUNC_GPIO_IN].name = "gpio_in";
+ pctl->funcs[AXP20X_FUNC_GPIO_IN].muxval = AXP20X_MUX_GPIO_IN;
+ pctl->funcs[AXP20X_FUNC_LDO].name = "ldo";
/*
* Muxval for LDO is useless as we won't use it.
* See comment in axp20x_pmx_set_mux.
*/
- gpio->funcs[AXP20X_FUNC_ADC].name = "adc";
- gpio->funcs[AXP20X_FUNC_ADC].muxval = AXP20X_MUX_ADC;
+ pctl->funcs[AXP20X_FUNC_ADC].name = "adc";
+ pctl->funcs[AXP20X_FUNC_ADC].muxval = AXP20X_MUX_ADC;
/* Every pin supports GPIO_OUT and GPIO_IN functions */
for (i = 0; i <= AXP20X_FUNC_GPIO_IN; i++) {
- gpio->funcs[i].ngroups = gpio->desc->npins;
- gpio->funcs[i].groups = devm_kzalloc(&pdev->dev,
- gpio->desc->npins * sizeof(const char *),
+ pctl->funcs[i].ngroups = pctl->desc->npins;
+ pctl->funcs[i].groups = devm_kzalloc(&pdev->dev,
+ pctl->desc->npins * sizeof(const char *),
GFP_KERNEL);
- for (pin = 0; pin < gpio->desc->npins; pin++)
- gpio->funcs[i].groups[pin] = gpio->desc->pins[pin].name;
+ for (pin = 0; pin < pctl->desc->npins; pin++)
+ pctl->funcs[i].groups[pin] = pctl->desc->pins[pin].name;
}
- axp20x_funcs_groups_from_mask(&pdev->dev, gpio->desc->ldo_mask,
- gpio->desc->npins,
- &gpio->funcs[AXP20X_FUNC_LDO],
- gpio->desc->pins);
+ axp20x_funcs_groups_from_mask(&pdev->dev, pctl->desc->ldo_mask,
+ pctl->desc->npins,
+ &pctl->funcs[AXP20X_FUNC_LDO],
+ pctl->desc->pins);
- axp20x_funcs_groups_from_mask(&pdev->dev, gpio->desc->adc_mask,
- gpio->desc->npins,
- &gpio->funcs[AXP20X_FUNC_ADC],
- gpio->desc->pins);
+ axp20x_funcs_groups_from_mask(&pdev->dev, pctl->desc->adc_mask,
+ pctl->desc->npins,
+ &pctl->funcs[AXP20X_FUNC_ADC],
+ pctl->desc->pins);
}
-static int axp20x_gpio_probe(struct platform_device *pdev)
+static int axp20x_pctl_probe(struct platform_device *pdev)
{
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
- struct axp20x_gpio *gpio;
+ struct axp20x_pctl *pctl;
struct pinctrl_desc *pctrl_desc;
int ret;
@@ -369,29 +369,29 @@ static int axp20x_gpio_probe(struct platform_device *pdev)
return -EINVAL;
}
- gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
- if (!gpio)
+ pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
+ if (!pctl)
return -ENOMEM;
- gpio->chip.base = -1;
- gpio->chip.can_sleep = true;
- gpio->chip.request = gpiochip_generic_request;
- gpio->chip.free = gpiochip_generic_free;
- gpio->chip.parent = &pdev->dev;
- gpio->chip.label = dev_name(&pdev->dev);
- gpio->chip.owner = THIS_MODULE;
- gpio->chip.get = axp20x_gpio_get;
- gpio->chip.get_direction = axp20x_gpio_get_direction;
- gpio->chip.set = axp20x_gpio_set;
- gpio->chip.direction_input = axp20x_gpio_input;
- gpio->chip.direction_output = axp20x_gpio_output;
- gpio->chip.ngpio = 3;
-
- gpio->desc = &axp20x_data;
- gpio->regmap = axp20x->regmap;
- gpio->dev = &pdev->dev;
-
- platform_set_drvdata(pdev, gpio);
+ pctl->chip.base = -1;
+ pctl->chip.can_sleep = true;
+ pctl->chip.request = gpiochip_generic_request;
+ pctl->chip.free = gpiochip_generic_free;
+ pctl->chip.parent = &pdev->dev;
+ pctl->chip.label = dev_name(&pdev->dev);
+ pctl->chip.owner = THIS_MODULE;
+ pctl->chip.get = axp20x_gpio_get;
+ pctl->chip.get_direction = axp20x_gpio_get_direction;
+ pctl->chip.set = axp20x_gpio_set;
+ pctl->chip.direction_input = axp20x_gpio_input;
+ pctl->chip.direction_output = axp20x_gpio_output;
+ pctl->chip.ngpio = 3;
+
+ pctl->desc = &axp20x_data;
+ pctl->regmap = axp20x->regmap;
+ pctl->dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, pctl);
axp20x_build_funcs_groups(pdev);
@@ -401,27 +401,27 @@ static int axp20x_gpio_probe(struct platform_device *pdev)
pctrl_desc->name = dev_name(&pdev->dev);
pctrl_desc->owner = THIS_MODULE;
- pctrl_desc->pins = gpio->desc->pins;
- pctrl_desc->npins = gpio->desc->npins;
+ pctrl_desc->pins = pctl->desc->pins;
+ pctrl_desc->npins = pctl->desc->npins;
pctrl_desc->pctlops = &axp20x_pctrl_ops;
pctrl_desc->pmxops = &axp20x_pmx_ops;
- gpio->pctl_dev = devm_pinctrl_register(&pdev->dev, pctrl_desc, gpio);
- if (IS_ERR(gpio->pctl_dev)) {
+ pctl->pctl_dev = devm_pinctrl_register(&pdev->dev, pctrl_desc, pctl);
+ if (IS_ERR(pctl->pctl_dev)) {
dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
- return PTR_ERR(gpio->pctl_dev);
+ return PTR_ERR(pctl->pctl_dev);
}
- ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
+ ret = devm_gpiochip_add_data(&pdev->dev, &pctl->chip, pctl);
if (ret) {
dev_err(&pdev->dev, "Failed to register GPIO chip\n");
return ret;
}
- ret = gpiochip_add_pin_range(&gpio->chip, dev_name(&pdev->dev),
- gpio->desc->pins->number,
- gpio->desc->pins->number,
- gpio->desc->npins);
+ ret = gpiochip_add_pin_range(&pctl->chip, dev_name(&pdev->dev),
+ pctl->desc->pins->number,
+ pctl->desc->pins->number,
+ pctl->desc->npins);
if (ret) {
dev_err(&pdev->dev, "failed to add pin range\n");
return ret;
@@ -432,21 +432,21 @@ static int axp20x_gpio_probe(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id axp20x_gpio_match[] = {
+static const struct of_device_id axp20x_pctl_match[] = {
{ .compatible = "x-powers,axp209-gpio" },
{ }
};
-MODULE_DEVICE_TABLE(of, axp20x_gpio_match);
+MODULE_DEVICE_TABLE(of, axp20x_pctl_match);
-static struct platform_driver axp20x_gpio_driver = {
- .probe = axp20x_gpio_probe,
+static struct platform_driver axp20x_pctl_driver = {
+ .probe = axp20x_pctl_probe,
.driver = {
.name = "axp20x-gpio",
- .of_match_table = axp20x_gpio_match,
+ .of_match_table = axp20x_pctl_match,
},
};
-module_platform_driver(axp20x_gpio_driver);
+module_platform_driver(axp20x_pctl_driver);
MODULE_AUTHOR("Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>");
MODULE_AUTHOR("Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>");
--
git-series 0.9.1
^ permalink raw reply related
* [PATCH v4 03/10] dt-bindings: gpio: gpio-axp209: add pinctrl features
From: Quentin Schulz @ 2017-12-01 13:44 UTC (permalink / raw)
To: linus.walleij-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
wens-jdAy2FN1RRM, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
lee.jones-QSEj5FYQhm4dnm+yROfE0A
Cc: linux-gpio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Quentin Schulz
In-Reply-To: <cover.3ff63fdf302c6bda02ea7d160ad2aa5afee0899d.1512135804.git-series.quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
The X-Powers AXP209 has 3 GPIOs. GPIO0/1 can each act either as a GPIO,
an ADC or a LDO regulator. GPIO2 can only act as a GPIO.
This adds the pinctrl features to the driver so GPIO0/1 can be used as
ADC or LDO regulator.
Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
Documentation/devicetree/bindings/gpio/gpio-axp209.txt | 28 ++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/gpio/gpio-axp209.txt b/Documentation/devicetree/bindings/gpio/gpio-axp209.txt
index a661130..4705020 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-axp209.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio-axp209.txt
@@ -1,4 +1,4 @@
-AXP209 GPIO controller
+AXP209 GPIO & pinctrl controller
This driver follows the usual GPIO bindings found in
Documentation/devicetree/bindings/gpio/gpio.txt
@@ -28,3 +28,29 @@ axp209: pmic@34 {
#gpio-cells = <2>;
};
};
+
+The GPIOs can be muxed to other functions and therefore, must be a subnode of
+axp_gpio.
+
+Example:
+
+&axp_gpio {
+ gpio0_adc: gpio0-adc {
+ pins = "GPIO0";
+ function = "adc";
+ };
+};
+
+&example_node {
+ pinctrl-names = "default";
+ pinctrl-0 = <&gpio0_adc>;
+};
+
+GPIOs and their functions
+-------------------------
+
+GPIO | Functions
+------------------------
+GPIO0 | gpio_in, gpio_out, ldo, adc
+GPIO1 | gpio_in, gpio_out, ldo, adc
+GPIO2 | gpio_in, gpio_out
--
git-series 0.9.1
^ permalink raw reply related
* [PATCH v4 02/10] pinctrl: axp209: add pinctrl features
From: Quentin Schulz @ 2017-12-01 13:44 UTC (permalink / raw)
To: linus.walleij-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
wens-jdAy2FN1RRM, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
lee.jones-QSEj5FYQhm4dnm+yROfE0A
Cc: linux-gpio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Quentin Schulz
In-Reply-To: <cover.3ff63fdf302c6bda02ea7d160ad2aa5afee0899d.1512135804.git-series.quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
The X-Powers AXP209 has 3 GPIOs. GPIO0/1 can each act either as a GPIO,
an ADC or a LDO regulator. GPIO2 can only act as a GPIO.
This adds the pinctrl features to the driver so GPIO0/1 can be used as
ADC or LDO regulator.
Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/pinctrl/pinctrl-axp209.c | 306 +++++++++++++++++++++++++++++---
1 file changed, 286 insertions(+), 20 deletions(-)
diff --git a/drivers/pinctrl/pinctrl-axp209.c b/drivers/pinctrl/pinctrl-axp209.c
index 4a346b7..2dc286f 100644
--- a/drivers/pinctrl/pinctrl-axp209.c
+++ b/drivers/pinctrl/pinctrl-axp209.c
@@ -1,7 +1,8 @@
/*
- * AXP20x GPIO driver
+ * AXP20x pinctrl and GPIO driver
*
* Copyright (C) 2016 Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
+ * Copyright (C) 2017 Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -18,6 +19,9 @@
#include <linux/mfd/axp20x.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -27,9 +31,52 @@
#define AXP20X_GPIO_FUNCTION_OUT_HIGH 1
#define AXP20X_GPIO_FUNCTION_INPUT 2
+#define AXP20X_FUNC_GPIO_OUT 0
+#define AXP20X_FUNC_GPIO_IN 1
+#define AXP20X_FUNC_LDO 2
+#define AXP20X_FUNC_ADC 3
+#define AXP20X_FUNCS_NB 4
+
+#define AXP20X_MUX_GPIO_OUT 0
+#define AXP20X_MUX_GPIO_IN BIT(1)
+#define AXP20X_MUX_ADC BIT(2)
+
+struct axp20x_pctrl_desc {
+ const struct pinctrl_pin_desc *pins;
+ unsigned int npins;
+ /* Stores the pins supporting LDO function. Bit offset is pin number. */
+ u8 ldo_mask;
+ /* Stores the pins supporting ADC function. Bit offset is pin number. */
+ u8 adc_mask;
+};
+
+struct axp20x_pinctrl_function {
+ const char *name;
+ unsigned int muxval;
+ const char **groups;
+ unsigned int ngroups;
+};
+
struct axp20x_gpio {
struct gpio_chip chip;
struct regmap *regmap;
+ struct pinctrl_dev *pctl_dev;
+ struct device *dev;
+ const struct axp20x_pctrl_desc *desc;
+ struct axp20x_pinctrl_function funcs[AXP20X_FUNCS_NB];
+};
+
+static const struct pinctrl_pin_desc axp209_pins[] = {
+ PINCTRL_PIN(0, "GPIO0"),
+ PINCTRL_PIN(1, "GPIO1"),
+ PINCTRL_PIN(2, "GPIO2"),
+};
+
+static const struct axp20x_pctrl_desc axp20x_data = {
+ .pins = axp209_pins,
+ .npins = ARRAY_SIZE(axp209_pins),
+ .ldo_mask = BIT(0) | BIT(1),
+ .adc_mask = BIT(0) | BIT(1),
};
static int axp20x_gpio_get_reg(unsigned offset)
@@ -48,16 +95,7 @@ static int axp20x_gpio_get_reg(unsigned offset)
static int axp20x_gpio_input(struct gpio_chip *chip, unsigned offset)
{
- struct axp20x_gpio *gpio = gpiochip_get_data(chip);
- int reg;
-
- reg = axp20x_gpio_get_reg(offset);
- if (reg < 0)
- return reg;
-
- return regmap_update_bits(gpio->regmap, reg,
- AXP20X_GPIO_FUNCTIONS,
- AXP20X_GPIO_FUNCTION_INPUT);
+ return pinctrl_gpio_direction_input(chip->base + offset);
}
static int axp20x_gpio_get(struct gpio_chip *chip, unsigned offset)
@@ -105,29 +143,222 @@ static int axp20x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
static int axp20x_gpio_output(struct gpio_chip *chip, unsigned offset,
int value)
{
+ chip->set(chip, offset, value);
+
+ return 0;
+}
+
+static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
struct axp20x_gpio *gpio = gpiochip_get_data(chip);
int reg;
reg = axp20x_gpio_get_reg(offset);
if (reg < 0)
+ return;
+
+ regmap_update_bits(gpio->regmap, reg,
+ AXP20X_GPIO_FUNCTIONS,
+ value ? AXP20X_GPIO_FUNCTION_OUT_HIGH :
+ AXP20X_GPIO_FUNCTION_OUT_LOW);
+}
+
+static int axp20x_pmx_set(struct pinctrl_dev *pctldev, unsigned int offset,
+ u8 config)
+{
+ struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+ int reg;
+
+ reg = axp20x_gpio_get_reg(offset);
+ if (reg < 0)
return reg;
- return regmap_update_bits(gpio->regmap, reg,
- AXP20X_GPIO_FUNCTIONS,
- value ? AXP20X_GPIO_FUNCTION_OUT_HIGH
- : AXP20X_GPIO_FUNCTION_OUT_LOW);
+ return regmap_update_bits(gpio->regmap, reg, AXP20X_GPIO_FUNCTIONS,
+ config);
}
-static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset,
- int value)
+static int axp20x_pmx_func_cnt(struct pinctrl_dev *pctldev)
+{
+ struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+
+ return ARRAY_SIZE(gpio->funcs);
+}
+
+static const char *axp20x_pmx_func_name(struct pinctrl_dev *pctldev,
+ unsigned int selector)
+{
+ struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+
+ return gpio->funcs[selector].name;
+}
+
+static int axp20x_pmx_func_groups(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ const char * const **groups,
+ unsigned int *num_groups)
+{
+ struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+
+ *groups = gpio->funcs[selector].groups;
+ *num_groups = gpio->funcs[selector].ngroups;
+
+ return 0;
+}
+
+static int axp20x_pmx_set_mux(struct pinctrl_dev *pctldev,
+ unsigned int function, unsigned int group)
+{
+ struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+ unsigned int mask;
+
+ /* Every pin supports GPIO_OUT and GPIO_IN functions */
+ if (function <= AXP20X_FUNC_GPIO_IN)
+ return axp20x_pmx_set(pctldev, group,
+ gpio->funcs[function].muxval);
+
+ if (function == AXP20X_FUNC_LDO)
+ mask = gpio->desc->ldo_mask;
+ else
+ mask = gpio->desc->adc_mask;
+
+ if (!(BIT(group) & mask))
+ return -EINVAL;
+
+ /*
+ * We let the regulator framework handle the LDO muxing as muxing bits
+ * are basically also regulators on/off bits. It's better not to enforce
+ * any state of the regulator when selecting LDO mux so that we don't
+ * interfere with the regulator driver.
+ */
+ if (function == AXP20X_FUNC_LDO)
+ return 0;
+
+ return axp20x_pmx_set(pctldev, group, gpio->funcs[function].muxval);
+}
+
+static int axp20x_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned int offset, bool input)
+{
+ struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+
+ if (input)
+ return axp20x_pmx_set(pctldev, offset,
+ gpio->funcs[AXP20X_FUNC_GPIO_IN].muxval);
+
+ return axp20x_pmx_set(pctldev, offset,
+ gpio->funcs[AXP20X_FUNC_GPIO_OUT].muxval);
+}
+
+static const struct pinmux_ops axp20x_pmx_ops = {
+ .get_functions_count = axp20x_pmx_func_cnt,
+ .get_function_name = axp20x_pmx_func_name,
+ .get_function_groups = axp20x_pmx_func_groups,
+ .set_mux = axp20x_pmx_set_mux,
+ .gpio_set_direction = axp20x_pmx_gpio_set_direction,
+ .strict = true,
+};
+
+static int axp20x_groups_cnt(struct pinctrl_dev *pctldev)
+{
+ struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+
+ return gpio->desc->npins;
+}
+
+static int axp20x_group_pins(struct pinctrl_dev *pctldev, unsigned int selector,
+ const unsigned int **pins, unsigned int *num_pins)
+{
+ struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+
+ *pins = (unsigned int *)&gpio->desc->pins[selector];
+ *num_pins = 1;
+
+ return 0;
+}
+
+static const char *axp20x_group_name(struct pinctrl_dev *pctldev,
+ unsigned int selector)
{
- axp20x_gpio_output(chip, offset, value);
+ struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
+
+ return gpio->desc->pins[selector].name;
+}
+
+static const struct pinctrl_ops axp20x_pctrl_ops = {
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_group,
+ .dt_free_map = pinconf_generic_dt_free_map,
+ .get_groups_count = axp20x_groups_cnt,
+ .get_group_name = axp20x_group_name,
+ .get_group_pins = axp20x_group_pins,
+};
+
+static void axp20x_funcs_groups_from_mask(struct device *dev, unsigned int mask,
+ unsigned int mask_len,
+ struct axp20x_pinctrl_function *func,
+ const struct pinctrl_pin_desc *pins)
+{
+ unsigned long int mask_cpy = mask;
+ const char **group;
+ unsigned int ngroups = hweight8(mask);
+ int bit;
+
+ func->ngroups = ngroups;
+ if (func->ngroups > 0) {
+ func->groups = devm_kzalloc(dev, ngroups * sizeof(const char *),
+ GFP_KERNEL);
+ group = func->groups;
+ for_each_set_bit(bit, &mask_cpy, mask_len) {
+ *group = pins[bit].name;
+ group++;
+ }
+ }
+}
+
+static void axp20x_build_funcs_groups(struct platform_device *pdev)
+{
+ struct axp20x_gpio *gpio = platform_get_drvdata(pdev);
+ int i, pin;
+
+ gpio->funcs[AXP20X_FUNC_GPIO_OUT].name = "gpio_out";
+ gpio->funcs[AXP20X_FUNC_GPIO_OUT].muxval = AXP20X_MUX_GPIO_OUT;
+ gpio->funcs[AXP20X_FUNC_GPIO_IN].name = "gpio_in";
+ gpio->funcs[AXP20X_FUNC_GPIO_IN].muxval = AXP20X_MUX_GPIO_IN;
+ gpio->funcs[AXP20X_FUNC_LDO].name = "ldo";
+ /*
+ * Muxval for LDO is useless as we won't use it.
+ * See comment in axp20x_pmx_set_mux.
+ */
+ gpio->funcs[AXP20X_FUNC_ADC].name = "adc";
+ gpio->funcs[AXP20X_FUNC_ADC].muxval = AXP20X_MUX_ADC;
+
+ /* Every pin supports GPIO_OUT and GPIO_IN functions */
+ for (i = 0; i <= AXP20X_FUNC_GPIO_IN; i++) {
+ gpio->funcs[i].ngroups = gpio->desc->npins;
+ gpio->funcs[i].groups = devm_kzalloc(&pdev->dev,
+ gpio->desc->npins * sizeof(const char *),
+ GFP_KERNEL);
+ for (pin = 0; pin < gpio->desc->npins; pin++)
+ gpio->funcs[i].groups[pin] = gpio->desc->pins[pin].name;
+ }
+
+ axp20x_funcs_groups_from_mask(&pdev->dev, gpio->desc->ldo_mask,
+ gpio->desc->npins,
+ &gpio->funcs[AXP20X_FUNC_LDO],
+ gpio->desc->pins);
+
+ axp20x_funcs_groups_from_mask(&pdev->dev, gpio->desc->adc_mask,
+ gpio->desc->npins,
+ &gpio->funcs[AXP20X_FUNC_ADC],
+ gpio->desc->pins);
}
static int axp20x_gpio_probe(struct platform_device *pdev)
{
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
struct axp20x_gpio *gpio;
+ struct pinctrl_desc *pctrl_desc;
int ret;
if (!of_device_is_available(pdev->dev.of_node))
@@ -144,6 +375,8 @@ static int axp20x_gpio_probe(struct platform_device *pdev)
gpio->chip.base = -1;
gpio->chip.can_sleep = true;
+ gpio->chip.request = gpiochip_generic_request;
+ gpio->chip.free = gpiochip_generic_free;
gpio->chip.parent = &pdev->dev;
gpio->chip.label = dev_name(&pdev->dev);
gpio->chip.owner = THIS_MODULE;
@@ -154,7 +387,30 @@ static int axp20x_gpio_probe(struct platform_device *pdev)
gpio->chip.direction_output = axp20x_gpio_output;
gpio->chip.ngpio = 3;
+ gpio->desc = &axp20x_data;
gpio->regmap = axp20x->regmap;
+ gpio->dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, gpio);
+
+ axp20x_build_funcs_groups(pdev);
+
+ pctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctrl_desc), GFP_KERNEL);
+ if (!pctrl_desc)
+ return -ENOMEM;
+
+ pctrl_desc->name = dev_name(&pdev->dev);
+ pctrl_desc->owner = THIS_MODULE;
+ pctrl_desc->pins = gpio->desc->pins;
+ pctrl_desc->npins = gpio->desc->npins;
+ pctrl_desc->pctlops = &axp20x_pctrl_ops;
+ pctrl_desc->pmxops = &axp20x_pmx_ops;
+
+ gpio->pctl_dev = devm_pinctrl_register(&pdev->dev, pctrl_desc, gpio);
+ if (IS_ERR(gpio->pctl_dev)) {
+ dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
+ return PTR_ERR(gpio->pctl_dev);
+ }
ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
if (ret) {
@@ -162,7 +418,16 @@ static int axp20x_gpio_probe(struct platform_device *pdev)
return ret;
}
- dev_info(&pdev->dev, "AXP209 GPIO driver loaded\n");
+ ret = gpiochip_add_pin_range(&gpio->chip, dev_name(&pdev->dev),
+ gpio->desc->pins->number,
+ gpio->desc->pins->number,
+ gpio->desc->npins);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add pin range\n");
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "AXP209 pinctrl and GPIO driver loaded\n");
return 0;
}
@@ -184,5 +449,6 @@ static struct platform_driver axp20x_gpio_driver = {
module_platform_driver(axp20x_gpio_driver);
MODULE_AUTHOR("Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>");
-MODULE_DESCRIPTION("AXP20x PMIC GPIO driver");
+MODULE_AUTHOR("Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>");
+MODULE_DESCRIPTION("AXP20x PMIC pinctrl and GPIO driver");
MODULE_LICENSE("GPL");
--
git-series 0.9.1
^ permalink raw reply related
* [PATCH v4 01/10] pinctrl: move gpio-axp209 to pinctrl
From: Quentin Schulz @ 2017-12-01 13:44 UTC (permalink / raw)
To: linus.walleij-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
wens-jdAy2FN1RRM, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
lee.jones-QSEj5FYQhm4dnm+yROfE0A
Cc: linux-gpio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Quentin Schulz
In-Reply-To: <cover.3ff63fdf302c6bda02ea7d160ad2aa5afee0899d.1512135804.git-series.quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
To prepare the driver for the upcoming pinctrl features, move the GPIO
driver AXP209 from GPIO to pinctrl subsystem.
Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Acked-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Acked-by: Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org>
---
drivers/gpio/Kconfig | 6 +-
drivers/gpio/Makefile | 1 +-
drivers/gpio/gpio-axp209.c | 188 +--------------------------------
drivers/pinctrl/Kconfig | 6 +-
drivers/pinctrl/Makefile | 1 +-
drivers/pinctrl/pinctrl-axp209.c | 188 ++++++++++++++++++++++++++++++++-
6 files changed, 195 insertions(+), 195 deletions(-)
delete mode 100644 drivers/gpio/gpio-axp209.c
create mode 100644 drivers/pinctrl/pinctrl-axp209.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index d6a8e85..395669b 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -122,12 +122,6 @@ config GPIO_ATH79
Select this option to enable GPIO driver for
Atheros AR71XX/AR724X/AR913X SoC devices.
-config GPIO_AXP209
- tristate "X-Powers AXP209 PMIC GPIO Support"
- depends on MFD_AXP20X
- help
- Say yes to enable GPIO support for the AXP209 PMIC
-
config GPIO_BCM_KONA
bool "Broadcom Kona GPIO"
depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST)
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 4bc24fe..bc5dd67 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -32,7 +32,6 @@ obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o
obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o
obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o
-obj-$(CONFIG_GPIO_AXP209) += gpio-axp209.o
obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
diff --git a/drivers/gpio/gpio-axp209.c b/drivers/gpio/gpio-axp209.c
deleted file mode 100644
index 4a346b7..0000000
--- a/drivers/gpio/gpio-axp209.c
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * AXP20x GPIO driver
- *
- * Copyright (C) 2016 Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#include <linux/bitops.h>
-#include <linux/device.h>
-#include <linux/gpio/driver.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/mfd/axp20x.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/regmap.h>
-#include <linux/slab.h>
-
-#define AXP20X_GPIO_FUNCTIONS 0x7
-#define AXP20X_GPIO_FUNCTION_OUT_LOW 0
-#define AXP20X_GPIO_FUNCTION_OUT_HIGH 1
-#define AXP20X_GPIO_FUNCTION_INPUT 2
-
-struct axp20x_gpio {
- struct gpio_chip chip;
- struct regmap *regmap;
-};
-
-static int axp20x_gpio_get_reg(unsigned offset)
-{
- switch (offset) {
- case 0:
- return AXP20X_GPIO0_CTRL;
- case 1:
- return AXP20X_GPIO1_CTRL;
- case 2:
- return AXP20X_GPIO2_CTRL;
- }
-
- return -EINVAL;
-}
-
-static int axp20x_gpio_input(struct gpio_chip *chip, unsigned offset)
-{
- struct axp20x_gpio *gpio = gpiochip_get_data(chip);
- int reg;
-
- reg = axp20x_gpio_get_reg(offset);
- if (reg < 0)
- return reg;
-
- return regmap_update_bits(gpio->regmap, reg,
- AXP20X_GPIO_FUNCTIONS,
- AXP20X_GPIO_FUNCTION_INPUT);
-}
-
-static int axp20x_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
- struct axp20x_gpio *gpio = gpiochip_get_data(chip);
- unsigned int val;
- int ret;
-
- ret = regmap_read(gpio->regmap, AXP20X_GPIO20_SS, &val);
- if (ret)
- return ret;
-
- return !!(val & BIT(offset + 4));
-}
-
-static int axp20x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
-{
- struct axp20x_gpio *gpio = gpiochip_get_data(chip);
- unsigned int val;
- int reg, ret;
-
- reg = axp20x_gpio_get_reg(offset);
- if (reg < 0)
- return reg;
-
- ret = regmap_read(gpio->regmap, reg, &val);
- if (ret)
- return ret;
-
- /*
- * This shouldn't really happen if the pin is in use already,
- * or if it's not in use yet, it doesn't matter since we're
- * going to change the value soon anyway. Default to output.
- */
- if ((val & AXP20X_GPIO_FUNCTIONS) > 2)
- return 0;
-
- /*
- * The GPIO directions are the three lowest values.
- * 2 is input, 0 and 1 are output
- */
- return val & 2;
-}
-
-static int axp20x_gpio_output(struct gpio_chip *chip, unsigned offset,
- int value)
-{
- struct axp20x_gpio *gpio = gpiochip_get_data(chip);
- int reg;
-
- reg = axp20x_gpio_get_reg(offset);
- if (reg < 0)
- return reg;
-
- return regmap_update_bits(gpio->regmap, reg,
- AXP20X_GPIO_FUNCTIONS,
- value ? AXP20X_GPIO_FUNCTION_OUT_HIGH
- : AXP20X_GPIO_FUNCTION_OUT_LOW);
-}
-
-static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset,
- int value)
-{
- axp20x_gpio_output(chip, offset, value);
-}
-
-static int axp20x_gpio_probe(struct platform_device *pdev)
-{
- struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
- struct axp20x_gpio *gpio;
- int ret;
-
- if (!of_device_is_available(pdev->dev.of_node))
- return -ENODEV;
-
- if (!axp20x) {
- dev_err(&pdev->dev, "Parent drvdata not set\n");
- return -EINVAL;
- }
-
- gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
- if (!gpio)
- return -ENOMEM;
-
- gpio->chip.base = -1;
- gpio->chip.can_sleep = true;
- gpio->chip.parent = &pdev->dev;
- gpio->chip.label = dev_name(&pdev->dev);
- gpio->chip.owner = THIS_MODULE;
- gpio->chip.get = axp20x_gpio_get;
- gpio->chip.get_direction = axp20x_gpio_get_direction;
- gpio->chip.set = axp20x_gpio_set;
- gpio->chip.direction_input = axp20x_gpio_input;
- gpio->chip.direction_output = axp20x_gpio_output;
- gpio->chip.ngpio = 3;
-
- gpio->regmap = axp20x->regmap;
-
- ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
- if (ret) {
- dev_err(&pdev->dev, "Failed to register GPIO chip\n");
- return ret;
- }
-
- dev_info(&pdev->dev, "AXP209 GPIO driver loaded\n");
-
- return 0;
-}
-
-static const struct of_device_id axp20x_gpio_match[] = {
- { .compatible = "x-powers,axp209-gpio" },
- { }
-};
-MODULE_DEVICE_TABLE(of, axp20x_gpio_match);
-
-static struct platform_driver axp20x_gpio_driver = {
- .probe = axp20x_gpio_probe,
- .driver = {
- .name = "axp20x-gpio",
- .of_match_table = axp20x_gpio_match,
- },
-};
-
-module_platform_driver(axp20x_gpio_driver);
-
-MODULE_AUTHOR("Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>");
-MODULE_DESCRIPTION("AXP20x PMIC GPIO driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 4571cc0..58e8cc8 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -63,6 +63,12 @@ config PINCTRL_AS3722
open drain configuration for the GPIO pins of AS3722 devices. It also
supports the GPIO functionality through gpiolib.
+config PINCTRL_AXP209
+ tristate "X-Powers AXP209 PMIC pinctrl and GPIO Support"
+ depends on MFD_AXP20X
+ help
+ Say yes to enable pinctrl and GPIO support for the AXP209 PMIC
+
config PINCTRL_BF54x
def_bool y if BF54x
select PINCTRL_ADI2
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index d0d4844..4777f15 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o
obj-$(CONFIG_PINCTRL_ADI2) += pinctrl-adi2.o
obj-$(CONFIG_PINCTRL_ARTPEC6) += pinctrl-artpec6.o
obj-$(CONFIG_PINCTRL_AS3722) += pinctrl-as3722.o
+obj-$(CONFIG_PINCTRL_AXP209) += pinctrl-axp209.o
obj-$(CONFIG_PINCTRL_BF54x) += pinctrl-adi2-bf54x.o
obj-$(CONFIG_PINCTRL_BF60x) += pinctrl-adi2-bf60x.o
obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o
diff --git a/drivers/pinctrl/pinctrl-axp209.c b/drivers/pinctrl/pinctrl-axp209.c
new file mode 100644
index 0000000..4a346b7
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-axp209.c
@@ -0,0 +1,188 @@
+/*
+ * AXP20x GPIO driver
+ *
+ * Copyright (C) 2016 Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define AXP20X_GPIO_FUNCTIONS 0x7
+#define AXP20X_GPIO_FUNCTION_OUT_LOW 0
+#define AXP20X_GPIO_FUNCTION_OUT_HIGH 1
+#define AXP20X_GPIO_FUNCTION_INPUT 2
+
+struct axp20x_gpio {
+ struct gpio_chip chip;
+ struct regmap *regmap;
+};
+
+static int axp20x_gpio_get_reg(unsigned offset)
+{
+ switch (offset) {
+ case 0:
+ return AXP20X_GPIO0_CTRL;
+ case 1:
+ return AXP20X_GPIO1_CTRL;
+ case 2:
+ return AXP20X_GPIO2_CTRL;
+ }
+
+ return -EINVAL;
+}
+
+static int axp20x_gpio_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct axp20x_gpio *gpio = gpiochip_get_data(chip);
+ int reg;
+
+ reg = axp20x_gpio_get_reg(offset);
+ if (reg < 0)
+ return reg;
+
+ return regmap_update_bits(gpio->regmap, reg,
+ AXP20X_GPIO_FUNCTIONS,
+ AXP20X_GPIO_FUNCTION_INPUT);
+}
+
+static int axp20x_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct axp20x_gpio *gpio = gpiochip_get_data(chip);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(gpio->regmap, AXP20X_GPIO20_SS, &val);
+ if (ret)
+ return ret;
+
+ return !!(val & BIT(offset + 4));
+}
+
+static int axp20x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+ struct axp20x_gpio *gpio = gpiochip_get_data(chip);
+ unsigned int val;
+ int reg, ret;
+
+ reg = axp20x_gpio_get_reg(offset);
+ if (reg < 0)
+ return reg;
+
+ ret = regmap_read(gpio->regmap, reg, &val);
+ if (ret)
+ return ret;
+
+ /*
+ * This shouldn't really happen if the pin is in use already,
+ * or if it's not in use yet, it doesn't matter since we're
+ * going to change the value soon anyway. Default to output.
+ */
+ if ((val & AXP20X_GPIO_FUNCTIONS) > 2)
+ return 0;
+
+ /*
+ * The GPIO directions are the three lowest values.
+ * 2 is input, 0 and 1 are output
+ */
+ return val & 2;
+}
+
+static int axp20x_gpio_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct axp20x_gpio *gpio = gpiochip_get_data(chip);
+ int reg;
+
+ reg = axp20x_gpio_get_reg(offset);
+ if (reg < 0)
+ return reg;
+
+ return regmap_update_bits(gpio->regmap, reg,
+ AXP20X_GPIO_FUNCTIONS,
+ value ? AXP20X_GPIO_FUNCTION_OUT_HIGH
+ : AXP20X_GPIO_FUNCTION_OUT_LOW);
+}
+
+static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ axp20x_gpio_output(chip, offset, value);
+}
+
+static int axp20x_gpio_probe(struct platform_device *pdev)
+{
+ struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+ struct axp20x_gpio *gpio;
+ int ret;
+
+ if (!of_device_is_available(pdev->dev.of_node))
+ return -ENODEV;
+
+ if (!axp20x) {
+ dev_err(&pdev->dev, "Parent drvdata not set\n");
+ return -EINVAL;
+ }
+
+ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+
+ gpio->chip.base = -1;
+ gpio->chip.can_sleep = true;
+ gpio->chip.parent = &pdev->dev;
+ gpio->chip.label = dev_name(&pdev->dev);
+ gpio->chip.owner = THIS_MODULE;
+ gpio->chip.get = axp20x_gpio_get;
+ gpio->chip.get_direction = axp20x_gpio_get_direction;
+ gpio->chip.set = axp20x_gpio_set;
+ gpio->chip.direction_input = axp20x_gpio_input;
+ gpio->chip.direction_output = axp20x_gpio_output;
+ gpio->chip.ngpio = 3;
+
+ gpio->regmap = axp20x->regmap;
+
+ ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register GPIO chip\n");
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "AXP209 GPIO driver loaded\n");
+
+ return 0;
+}
+
+static const struct of_device_id axp20x_gpio_match[] = {
+ { .compatible = "x-powers,axp209-gpio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, axp20x_gpio_match);
+
+static struct platform_driver axp20x_gpio_driver = {
+ .probe = axp20x_gpio_probe,
+ .driver = {
+ .name = "axp20x-gpio",
+ .of_match_table = axp20x_gpio_match,
+ },
+};
+
+module_platform_driver(axp20x_gpio_driver);
+
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>");
+MODULE_DESCRIPTION("AXP20x PMIC GPIO driver");
+MODULE_LICENSE("GPL");
--
git-series 0.9.1
^ permalink raw reply related
* [PATCH v4 00/10] add pinmuxing support for pins in AXP209 and AXP813 PMICs
From: Quentin Schulz @ 2017-12-01 13:44 UTC (permalink / raw)
To: linus.walleij-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
wens-jdAy2FN1RRM, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
lee.jones-QSEj5FYQhm4dnm+yROfE0A
Cc: linux-gpio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Quentin Schulz
The AXP209 and AXP813 PMICs have several pins (respectively 3 and 2) that can
be used either as GPIOs or for other purposes (ADC or LDO here).
We already have a GPIO driver for the GPIO use of those pins on the AXP209.
Let's "upgrade" this driver to support all the functions these pins can have.
Then we add support to this driver for the AXP813 which is slighlty different
(basically a different offset in two registers and one less pin).
I suggest patches 1 to 8 go through Linus's tree and 9 and 10 via Maxime or
Chen-Yu's tree.
v4:
- separate dt-binding patch when adding pinctrl feature,
- use - instead of _ in DT node name,
- remove muxing operation from pinctrl driver when choosing LDO mux in order
to not interfere with the regulator framework,
- add adc_mux to specify specific mux value for ADC (different between AXP209
and AXP813),
- misc modifications (header include reordering, unsigned int -> u8,
new line removal),
v3:
- add defines for GPIO funcs,
- use again get_regs function instead of drv_data (which was implemented in
v2),
- use of_device_id data for device specific data (gpio_status_offset and pins
description),
- change compatible from axp813-pctl to axp813-gpio,
- use axp81x DT label instead of axp813 since AXP813 and AXP818 are similar,
- add dtsi include for all boards embedding axp813/axp818,
v2:
- add support for AXP813 pins,
- split into more patches so it is easier to follow the modifications,
- reorder of some patches,
- register all pins within the same range instead of a range per pin,
Thanks,
Quentin
Quentin Schulz (10):
pinctrl: move gpio-axp209 to pinctrl
pinctrl: axp209: add pinctrl features
dt-bindings: gpio: gpio-axp209: add pinctrl features
pinctrl: axp209: rename everything from gpio to pctl
pinctrl: axp209: add programmable gpio_status_offset
pinctrl: axp209: add programmable ADC muxing value
pinctrl: axp209: add support for AXP813 GPIOs
mfd: axp20x: add pinctrl cell for AXP813
ARM: dtsi: axp81x: add GPIO DT node
ARM: dtsi: axp81x: set pinmux for GPIO0/1 when used as LDOs
Documentation/devicetree/bindings/gpio/gpio-axp209.txt | 41 +-
arch/arm/boot/dts/axp81x.dtsi | 20 +-
drivers/gpio/Kconfig | 6 +-
drivers/gpio/Makefile | 1 +-
drivers/gpio/gpio-axp209.c | 188 +----
drivers/mfd/axp20x.c | 3 +-
drivers/pinctrl/Kconfig | 6 +-
drivers/pinctrl/Makefile | 1 +-
drivers/pinctrl/pinctrl-axp209.c | 477 ++++++++++-
9 files changed, 546 insertions(+), 197 deletions(-)
delete mode 100644 drivers/gpio/gpio-axp209.c
create mode 100644 drivers/pinctrl/pinctrl-axp209.c
base-commit: fb20eb9d798d2f4c1a75b7fe981d72dfa8d7270d
--
git-series 0.9.1
^ permalink raw reply
* Applied "ASoC: tlv320aic31xx: General source formatting cleanup" to the asoc tree
From: Mark Brown @ 2017-12-01 13:43 UTC (permalink / raw)
Cc: Mark Rutland, devicetree, alsa-devel, Tony Lindgren,
Liam Girdwood, Rob Herring, linux-kernel, Mark Brown,
Benoît Cousson, Andrew F . Davis
In-Reply-To: <20171129213300.20021-5-afd@ti.com>
The patch
ASoC: tlv320aic31xx: General source formatting cleanup
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
>From ca7840fb47208a3521f3b60c1b78a2f0c59b4dc5 Mon Sep 17 00:00:00 2001
From: "Andrew F. Davis" <afd@ti.com>
Date: Wed, 29 Nov 2017 15:32:45 -0600
Subject: [PATCH] ASoC: tlv320aic31xx: General source formatting cleanup
Simple non-functional changes including:
* Fix spelling errors
* Reformat code for easier reading
* Remove unneeded code
* Remove assignments that are always overridden
* Normalize function return paths
Signed-off-by: Andrew F. Davis <afd@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
sound/soc/codecs/tlv320aic31xx.c | 63 ++++++++++++++++++++++------------------
1 file changed, 35 insertions(+), 28 deletions(-)
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index d974e8651e30..07c014501e5e 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -136,8 +136,7 @@ static const struct regmap_config aic31xx_i2c_regmap = {
.max_register = 12 * 128,
};
-#define AIC31XX_NUM_SUPPLIES 6
-static const char * const aic31xx_supply_names[AIC31XX_NUM_SUPPLIES] = {
+static const char * const aic31xx_supply_names[] = {
"HPVDD",
"SPRVDD",
"SPLVDD",
@@ -146,6 +145,8 @@ static const char * const aic31xx_supply_names[AIC31XX_NUM_SUPPLIES] = {
"DVDD",
};
+#define AIC31XX_NUM_SUPPLIES ARRAY_SIZE(aic31xx_supply_names)
+
struct aic31xx_disable_nb {
struct notifier_block nb;
struct aic31xx_priv *aic31xx;
@@ -177,7 +178,7 @@ struct aic31xx_rate_divs {
u8 madc;
};
-/* ADC dividers can be disabled by cofiguring them to 0 */
+/* ADC dividers can be disabled by configuring them to 0 */
static const struct aic31xx_rate_divs aic31xx_divs[] = {
/* mclk/p rate pll: j d dosr ndac mdac aors nadc madc */
/* 8k rate */
@@ -832,11 +833,17 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec,
dev_dbg(codec->dev,
"pll %d.%04d/%d dosr %d n %d m %d aosr %d n %d m %d bclk_n %d\n",
- aic31xx_divs[i].pll_j, aic31xx_divs[i].pll_d,
- aic31xx->p_div, aic31xx_divs[i].dosr,
- aic31xx_divs[i].ndac, aic31xx_divs[i].mdac,
- aic31xx_divs[i].aosr, aic31xx_divs[i].nadc,
- aic31xx_divs[i].madc, bclk_n);
+ aic31xx_divs[i].pll_j,
+ aic31xx_divs[i].pll_d,
+ aic31xx->p_div,
+ aic31xx_divs[i].dosr,
+ aic31xx_divs[i].ndac,
+ aic31xx_divs[i].mdac,
+ aic31xx_divs[i].aosr,
+ aic31xx_divs[i].nadc,
+ aic31xx_divs[i].madc,
+ bclk_n
+ );
return 0;
}
@@ -973,8 +980,9 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
dev_dbg(codec->dev, "## %s: clk_id = %d, freq = %d, dir = %d\n",
__func__, clk_id, freq, dir);
- for (i = 1; freq/i > 20000000 && i < 8; i++)
- ;
+ for (i = 1; i < 8; i++)
+ if (freq / i <= 20000000)
+ break;
if (freq/i > 20000000) {
dev_err(aic31xx->dev, "%s: Too high mclk frequency %u\n",
__func__, freq);
@@ -982,9 +990,9 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
}
aic31xx->p_div = i;
- for (i = 0; i < ARRAY_SIZE(aic31xx_divs) &&
- aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++)
- ;
+ for (i = 0; i < ARRAY_SIZE(aic31xx_divs); i++)
+ if (aic31xx_divs[i].mclk_p == freq / aic31xx->p_div)
+ break;
if (i == ARRAY_SIZE(aic31xx_divs)) {
dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n",
__func__, freq);
@@ -996,6 +1004,7 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
clk_id << AIC31XX_PLL_CLKIN_SHIFT);
aic31xx->sysclk = freq;
+
return 0;
}
@@ -1057,7 +1066,7 @@ static void aic31xx_clk_off(struct snd_soc_codec *codec)
static int aic31xx_power_on(struct snd_soc_codec *codec)
{
struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
- int ret = 0;
+ int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(aic31xx->supplies),
aic31xx->supplies);
@@ -1070,7 +1079,7 @@ static int aic31xx_power_on(struct snd_soc_codec *codec)
}
regcache_cache_only(aic31xx->regmap, false);
ret = regcache_sync(aic31xx->regmap);
- if (ret != 0) {
+ if (ret) {
dev_err(codec->dev,
"Failed to restore cache: %d\n", ret);
regcache_cache_only(aic31xx->regmap, true);
@@ -1078,6 +1087,7 @@ static int aic31xx_power_on(struct snd_soc_codec *codec)
aic31xx->supplies);
return ret;
}
+
return 0;
}
@@ -1126,14 +1136,11 @@ static int aic31xx_set_bias_level(struct snd_soc_codec *codec,
static int aic31xx_codec_probe(struct snd_soc_codec *codec)
{
- int ret = 0;
struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
- int i;
+ int i, ret;
dev_dbg(aic31xx->dev, "## %s\n", __func__);
- aic31xx = snd_soc_codec_get_drvdata(codec);
-
aic31xx->codec = codec;
for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) {
@@ -1158,8 +1165,10 @@ static int aic31xx_codec_probe(struct snd_soc_codec *codec)
return ret;
ret = aic31xx_add_widgets(codec);
+ if (ret)
+ return ret;
- return ret;
+ return 0;
}
static int aic31xx_codec_remove(struct snd_soc_codec *codec)
@@ -1322,10 +1331,12 @@ static int aic31xx_device_init(struct aic31xx_priv *aic31xx)
ret = devm_regulator_bulk_get(aic31xx->dev,
ARRAY_SIZE(aic31xx->supplies),
aic31xx->supplies);
- if (ret != 0)
+ if (ret) {
dev_err(aic31xx->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
- return ret;
+ return 0;
}
static int aic31xx_i2c_probe(struct i2c_client *i2c,
@@ -1333,18 +1344,15 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
{
struct aic31xx_priv *aic31xx;
int ret;
- const struct regmap_config *regmap_config;
dev_dbg(&i2c->dev, "## %s: %s codec_type = %d\n", __func__,
id->name, (int) id->driver_data);
- regmap_config = &aic31xx_i2c_regmap;
-
aic31xx = devm_kzalloc(&i2c->dev, sizeof(*aic31xx), GFP_KERNEL);
- if (aic31xx == NULL)
+ if (!aic31xx)
return -ENOMEM;
- aic31xx->regmap = devm_regmap_init_i2c(i2c, regmap_config);
+ aic31xx->regmap = devm_regmap_init_i2c(i2c, &aic31xx_i2c_regmap);
if (IS_ERR(aic31xx->regmap)) {
ret = PTR_ERR(aic31xx->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
@@ -1400,7 +1408,6 @@ static struct i2c_driver aic31xx_i2c_driver = {
.remove = aic31xx_i2c_remove,
.id_table = aic31xx_i2c_id,
};
-
module_i2c_driver(aic31xx_i2c_driver);
MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
--
2.15.0
^ permalink raw reply related
* Applied "ASoC: tlv320aic31xx: Merge init function into probe" to the asoc tree
From: Mark Brown @ 2017-12-01 13:43 UTC (permalink / raw)
Cc: Mark Rutland, devicetree, alsa-devel, Tony Lindgren,
Liam Girdwood, Rob Herring, linux-kernel, Mark Brown,
Benoît Cousson, Andrew F . Davis
In-Reply-To: <20171109002741.10897-5-afd@ti.com>
The patch
ASoC: tlv320aic31xx: Merge init function into probe
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
>From e88c3881361cee9b778bf4e4ded43da7a0917ce1 Mon Sep 17 00:00:00 2001
From: "Andrew F. Davis" <afd@ti.com>
Date: Wed, 29 Nov 2017 15:32:48 -0600
Subject: [PATCH] ASoC: tlv320aic31xx: Merge init function into probe
The function aic31xx_device_init() is only called from probe and
does nothing that logically shouldn't be in probe, remove this
unneeded function call and move its code into probe where it was called.
Signed-off-by: Andrew F. Davis <afd@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
sound/soc/codecs/tlv320aic31xx.c | 55 ++++++++++++++++------------------------
1 file changed, 22 insertions(+), 33 deletions(-)
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index 07c014501e5e..c84febd991a0 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -1302,9 +1302,29 @@ static const struct acpi_device_id aic31xx_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, aic31xx_acpi_match);
#endif
-static int aic31xx_device_init(struct aic31xx_priv *aic31xx)
+static int aic31xx_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
{
- int ret, i;
+ struct aic31xx_priv *aic31xx;
+ int i, ret;
+
+ dev_dbg(&i2c->dev, "## %s: %s codec_type = %d\n", __func__,
+ id->name, (int)id->driver_data);
+
+ aic31xx = devm_kzalloc(&i2c->dev, sizeof(*aic31xx), GFP_KERNEL);
+ if (!aic31xx)
+ return -ENOMEM;
+
+ aic31xx->regmap = devm_regmap_init_i2c(i2c, &aic31xx_i2c_regmap);
+ if (IS_ERR(aic31xx->regmap)) {
+ ret = PTR_ERR(aic31xx->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+ aic31xx->dev = &i2c->dev;
+
+ aic31xx->pdata.codec_type = id->driver_data;
dev_set_drvdata(aic31xx->dev, aic31xx);
@@ -1336,37 +1356,6 @@ static int aic31xx_device_init(struct aic31xx_priv *aic31xx)
return ret;
}
- return 0;
-}
-
-static int aic31xx_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
-{
- struct aic31xx_priv *aic31xx;
- int ret;
-
- dev_dbg(&i2c->dev, "## %s: %s codec_type = %d\n", __func__,
- id->name, (int) id->driver_data);
-
- aic31xx = devm_kzalloc(&i2c->dev, sizeof(*aic31xx), GFP_KERNEL);
- if (!aic31xx)
- return -ENOMEM;
-
- aic31xx->regmap = devm_regmap_init_i2c(i2c, &aic31xx_i2c_regmap);
- if (IS_ERR(aic31xx->regmap)) {
- ret = PTR_ERR(aic31xx->regmap);
- dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
- ret);
- return ret;
- }
- aic31xx->dev = &i2c->dev;
-
- aic31xx->pdata.codec_type = id->driver_data;
-
- ret = aic31xx_device_init(aic31xx);
- if (ret)
- return ret;
-
if (aic31xx->pdata.codec_type & DAC31XX_BIT)
return snd_soc_register_codec(&i2c->dev,
&soc_codec_driver_aic31xx,
--
2.15.0
^ permalink raw reply related
* Applied "ASoC: tlv320aic31xx: Switch GPIO handling to use gpiod_* API" to the asoc tree
From: Mark Brown @ 2017-12-01 13:43 UTC (permalink / raw)
Cc: Mark Rutland, devicetree, alsa-devel, Tony Lindgren,
Liam Girdwood, Rob Herring, linux-kernel, Mark Brown,
Benoît Cousson, Andrew F . Davis
In-Reply-To: <20171109002741.10897-6-afd@ti.com>
The patch
ASoC: tlv320aic31xx: Switch GPIO handling to use gpiod_* API
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
>From b6b247cd5e37560e410c88b108e7408dafe60c15 Mon Sep 17 00:00:00 2001
From: "Andrew F. Davis" <afd@ti.com>
Date: Wed, 29 Nov 2017 15:32:49 -0600
Subject: [PATCH] ASoC: tlv320aic31xx: Switch GPIO handling to use gpiod_* API
Move to using newer gpiod_* GPIO handling functions. This simplifies
the code and eases dropping platform data in the next patch. Also
remember GPIO are active low, so set "1" to reset.
Signed-off-by: Andrew F. Davis <afd@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
sound/soc/codecs/tlv320aic31xx.c | 23 ++++++++++-------------
1 file changed, 10 insertions(+), 13 deletions(-)
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index c84febd991a0..ab03a19f6aaa 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -157,6 +157,7 @@ struct aic31xx_priv {
u8 i2c_regs_status;
struct device *dev;
struct regmap *regmap;
+ struct gpio_desc *gpio_reset;
struct aic31xx_pdata pdata;
struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES];
struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES];
@@ -1020,8 +1021,8 @@ static int aic31xx_regulator_event(struct notifier_block *nb,
* Put codec to reset and as at least one of the
* supplies was disabled.
*/
- if (gpio_is_valid(aic31xx->pdata.gpio_reset))
- gpio_set_value(aic31xx->pdata.gpio_reset, 0);
+ if (aic31xx->gpio_reset)
+ gpiod_set_value(aic31xx->gpio_reset, 1);
regcache_mark_dirty(aic31xx->regmap);
dev_dbg(aic31xx->dev, "## %s: DISABLE received\n", __func__);
@@ -1073,8 +1074,8 @@ static int aic31xx_power_on(struct snd_soc_codec *codec)
if (ret)
return ret;
- if (gpio_is_valid(aic31xx->pdata.gpio_reset)) {
- gpio_set_value(aic31xx->pdata.gpio_reset, 1);
+ if (aic31xx->gpio_reset) {
+ gpiod_set_value(aic31xx->gpio_reset, 0);
udelay(100);
}
regcache_cache_only(aic31xx->regmap, false);
@@ -1334,15 +1335,11 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
else if (aic31xx->dev->of_node)
aic31xx_pdata_from_of(aic31xx);
- if (aic31xx->pdata.gpio_reset) {
- ret = devm_gpio_request_one(aic31xx->dev,
- aic31xx->pdata.gpio_reset,
- GPIOF_OUT_INIT_HIGH,
- "aic31xx-reset-pin");
- if (ret < 0) {
- dev_err(aic31xx->dev, "not able to acquire gpio\n");
- return ret;
- }
+ aic31xx->gpio_reset = devm_gpiod_get_optional(aic31xx->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(aic31xx->gpio_reset)) {
+ dev_err(aic31xx->dev, "not able to acquire gpio\n");
+ return PTR_ERR(aic31xx->gpio_reset);
}
for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++)
--
2.15.0
^ permalink raw reply related
* Applied "ASoC: tlv320aic31xx: Check clock and divider before division" to the asoc tree
From: Mark Brown @ 2017-12-01 13:43 UTC (permalink / raw)
Cc: Mark Rutland, devicetree, alsa-devel, Tony Lindgren,
Liam Girdwood, Rob Herring, linux-kernel, Mark Brown,
Benoît Cousson, Andrew F . Davis
In-Reply-To: <20171109002741.10897-9-afd@ti.com>
The patch
ASoC: tlv320aic31xx: Check clock and divider before division
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
>From c6b8c779213dfe2a31e12400b1a2cf2a9a843236 Mon Sep 17 00:00:00 2001
From: "Andrew F. Davis" <afd@ti.com>
Date: Wed, 29 Nov 2017 15:32:52 -0600
Subject: [PATCH] ASoC: tlv320aic31xx: Check clock and divider before division
If our set_sysclk DAI callback has not been called yet p_div will be 0
and dividing by this will cause an error. Print an error message and
leave before this.
Signed-off-by: Andrew F. Davis <afd@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
sound/soc/codecs/tlv320aic31xx.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index ab03a19f6aaa..05e6d194d6a9 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -754,11 +754,17 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec,
{
struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
int bclk_score = snd_soc_params_to_frame_size(params);
- int mclk_p = aic31xx->sysclk / aic31xx->p_div;
+ int mclk_p;
int bclk_n = 0;
int match = -1;
int i;
+ if (!aic31xx->sysclk || !aic31xx->p_div) {
+ dev_err(codec->dev, "Master clock not supplied\n");
+ return -EINVAL;
+ }
+ mclk_p = aic31xx->sysclk / aic31xx->p_div;
+
/* Use PLL as CODEC_CLKIN and DAC_CLK as BDIV_CLKIN */
snd_soc_update_bits(codec, AIC31XX_CLKMUX,
AIC31XX_CODEC_CLKIN_MASK, AIC31XX_CODEC_CLKIN_PLL);
--
2.15.0
^ permalink raw reply related
* Applied "ASoC: tlv320aic31xx: Add CODEC clock slave support" to the asoc tree
From: Mark Brown @ 2017-12-01 13:43 UTC (permalink / raw)
Cc: Mark Rutland, devicetree, alsa-devel, Tony Lindgren,
Liam Girdwood, Rob Herring, linux-kernel, Mark Brown,
Benoît Cousson, Andrew F . Davis
In-Reply-To: <20171129213300.20021-13-afd@ti.com>
The patch
ASoC: tlv320aic31xx: Add CODEC clock slave support
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
>From 77f8b3cfc33cd4231cc2748bcac9f43b9eea546c Mon Sep 17 00:00:00 2001
From: "Andrew F. Davis" <afd@ti.com>
Date: Wed, 29 Nov 2017 15:32:53 -0600
Subject: [PATCH] ASoC: tlv320aic31xx: Add CODEC clock slave support
This CODEC supports being the WCLK and/or BCLK slave, add
support for this here.
Also make the alert into an error as alert is more urgent
than needed here and is rarely used.
Signed-off-by: Andrew F. Davis <afd@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
sound/soc/codecs/tlv320aic31xx.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index 05e6d194d6a9..d77cc36d7360 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -925,8 +925,16 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
case SND_SOC_DAIFMT_CBM_CFM:
iface_reg1 |= AIC31XX_BCLK_MASTER | AIC31XX_WCLK_MASTER;
break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ iface_reg1 |= AIC31XX_WCLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ iface_reg1 |= AIC31XX_BCLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
default:
- dev_alert(codec->dev, "Invalid DAI master/slave interface\n");
+ dev_err(codec->dev, "Invalid DAI master/slave interface\n");
return -EINVAL;
}
--
2.15.0
^ permalink raw reply related
* Applied "ASoC: tlv320aic31xx: Handle inverted BCLK in non-DSP modes" to the asoc tree
From: Mark Brown @ 2017-12-01 13:42 UTC (permalink / raw)
Cc: Mark Rutland, devicetree, alsa-devel, Tony Lindgren,
Liam Girdwood, Rob Herring, linux-kernel, Mark Brown,
Benoît Cousson, Andrew F . Davis
In-Reply-To: <20171129213300.20021-14-afd@ti.com>
The patch
ASoC: tlv320aic31xx: Handle inverted BCLK in non-DSP modes
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
>From dcb407b257af06fa58b0544ec01ec9e0d3927e02 Mon Sep 17 00:00:00 2001
From: "Andrew F. Davis" <afd@ti.com>
Date: Wed, 29 Nov 2017 15:32:54 -0600
Subject: [PATCH] ASoC: tlv320aic31xx: Handle inverted BCLK in non-DSP modes
Currently BCLK inverting is only handled when the DAI format is
DSP, but the BCLK may be inverted in any supported mode. Without
this using this CODEC in any other mode than DSP with the BCLK
inverted leads to bad sampling timing and very poor audio quality.
Signed-off-by: Andrew F. Davis <afd@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
sound/soc/codecs/tlv320aic31xx.c | 28 ++++++++++++++++++----------
1 file changed, 18 insertions(+), 10 deletions(-)
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index d77cc36d7360..38fd6ea275fb 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -938,6 +938,18 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
+ /* signal polarity */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface_reg2 |= AIC31XX_BCLKINV_MASK;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI clock signal polarity\n");
+ return -EINVAL;
+ }
+
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
@@ -945,16 +957,12 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
case SND_SOC_DAIFMT_DSP_A:
dsp_a_val = 0x1; /* fall through */
case SND_SOC_DAIFMT_DSP_B:
- /* NOTE: BCLKINV bit value 1 equas NB and 0 equals IB */
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- iface_reg2 |= AIC31XX_BCLKINV_MASK;
- break;
- case SND_SOC_DAIFMT_IB_NF:
- break;
- default:
- return -EINVAL;
- }
+ /*
+ * NOTE: This CODEC samples on the falling edge of BCLK in
+ * DSP mode, this is inverted compared to what most DAIs
+ * expect, so we invert for this mode
+ */
+ iface_reg2 ^= AIC31XX_BCLKINV_MASK;
iface_reg1 |= (AIC31XX_DSP_MODE <<
AIC31XX_IFACE1_DATATYPE_SHIFT);
break;
--
2.15.0
^ permalink raw reply related
* Re: [PATCH v2 18/19] ASoC: tlv320aic31xx: Add headphone/headset detection
From: Mark Brown @ 2017-12-01 13:41 UTC (permalink / raw)
To: Andrew F. Davis
Cc: Liam Girdwood, Rob Herring, Mark Rutland, Benoît Cousson,
Tony Lindgren, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20171129213300.20021-19-afd-l0cyMroinI0@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 562 bytes --]
On Wed, Nov 29, 2017 at 03:32:59PM -0600, Andrew F. Davis wrote:
> This device can detect the insertion/removal of headphones and headsets.
> Enable reporting this status by enabling this interrupt and forwarding
> this to upper-layers if a jack has been defined.
>
> This jack definition and the resulting operation from a jack detection
> event must currently be defined by sound card platform code until CODEC
> outputs to jack mappings can be defined generically.
This only does half the job, there's no way for anything to specify a
jack here.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH 2/2] regulator: sc2731: Add regulator driver to support Spreadtrum SC2731 PMIC
From: Philippe Ombredanne @ 2017-12-01 13:39 UTC (permalink / raw)
To: Mark Brown
Cc: Erick Chen, Rob Herring, Mark Rutland,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, LKML,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
baolin.wang-QSEj5FYQhm4dnm+yROfE0A,
baolin.wang-lxIno14LUO0EEoCn2XhGlw, Linus Torvalds,
Greg Kroah-Hartman, Thomas Gleixner
In-Reply-To: <20171201124935.qgqclyoatnnph63p-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>
Mark,
On Fri, Dec 1, 2017 at 1:49 PM, Mark Brown <broonie-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Fri, Dec 01, 2017 at 10:13:27AM +0100, Philippe Ombredanne wrote:
>> On Fri, Dec 1, 2017 at 9:58 AM, Erick Chen <erick.chen-lxIno14LUO0EEoCn2XhGlw@public.gmane.org> wrote:
>
>> I think that per Linus, and Thomas doc patches for SPDX ids this
>> should be instead either:
>
>> > +// SPDX-License-Identifier: GPL-2.0
>> > + // Copyright (c) 2017 Spreadtrum Communications Inc.
>
>> or at least this with the id on the first and the // comment style
>
>> > +// SPDX-License-Identifier: GPL-2.0
>
> Are you saying SPDX requires C++ style comments? That seems totally
> broken.
In can understand your point, but for reference please check Linus
[1][2][3], Thomas[4] and Greg[5] comments on the topic.
I am just a lowly messenger and even though I personally agree with
Linus points and taste in this area, my weightless voice does not
matter.
CC: Linus, Greg and Thomas
[1] https://lkml.org/lkml/2017/11/2/715
[2] https://lkml.org/lkml/2017/11/25/125
[3] https://lkml.org/lkml/2017/11/25/133
[4] https://lkml.org/lkml/2017/11/2/805
[5] https://lkml.org/lkml/2017/10/19/165
--
Cordially
Philippe Ombredanne
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 16/19] ASoC: tlv320aic31xx: Add short circuit detection support
From: Mark Brown @ 2017-12-01 13:39 UTC (permalink / raw)
To: Andrew F. Davis
Cc: Liam Girdwood, Rob Herring, Mark Rutland, Benoît Cousson,
Tony Lindgren, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20171129213300.20021-17-afd-l0cyMroinI0@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 1109 bytes --]
On Wed, Nov 29, 2017 at 03:32:57PM -0600, Andrew F. Davis wrote:
> +static irqreturn_t aic31xx_irq(int irq, void *data)
> +{
> + struct aic31xx_priv *aic31xx = (struct aic31xx_priv *)data;
There is no need to cast away from void, doing so can only mask bugs.
> + ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG, &value);
> + if (ret) {
> + dev_err(dev, "Failed to read interrupt mask: %d\n", ret);
> + return IRQ_NONE;
> + }
> +
> + if (value & AIC31XX_HPLSCDETECT)
> + dev_err(dev, "Short circuit on Left output is detected\n");
> + if (value & AIC31XX_HPRSCDETECT)
> + dev_err(dev, "Short circuit on Right output is detected\n");
> +
> + return IRQ_HANDLED;
This will report the interrupt as handled even if we didn't see an
interrupt we understand which will break shared interrupt lines. At the
very least we should log other interrupt sources numerically.
> + if (aic31xx->irq > 0) {
> + regmap_update_bits(aic31xx->regmap, AIC31XX_GPIO1,
> + AIC31XX_GPIO1_FUNC_MASK,
> + AIC31XX_GPIO1_INT1 <<
> + AIC31XX_GPIO1_FUNC_SHIFT);
Is the interrupt only available on GPIO1?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH v2 15/19] ASoC: tlv320aic31xx: Reset registers during power up
From: Mark Brown @ 2017-12-01 13:37 UTC (permalink / raw)
To: Andrew F. Davis
Cc: Mark Rutland, devicetree, alsa-devel, Tony Lindgren,
Liam Girdwood, linux-kernel, Rob Herring, Benoît Cousson
In-Reply-To: <20171129213300.20021-16-afd@ti.com>
[-- Attachment #1.1: Type: text/plain, Size: 449 bytes --]
On Wed, Nov 29, 2017 at 03:32:56PM -0600, Andrew F. Davis wrote:
> + gpiod_set_value(aic31xx->gpio_reset, 1);
> + ndelay(10); /* At least 10ns */
> + gpiod_set_value(aic31xx->gpio_reset, 0);
_cansleep(), this isn't being done from interrupt context.
> + } else {
> + ret = regmap_write(aic31xx->regmap, AIC31XX_RESET, 1);
> + if (ret < 0)
> + dev_err(aic31xx->dev, "Could not reset device\n");
Print the error to help people doing debug.
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply
* Re: [PATCH v2 14/19] ASoC: tlv320aic31xx: Remove regulator notification handling
From: Mark Brown @ 2017-12-01 13:36 UTC (permalink / raw)
To: Andrew F. Davis
Cc: Liam Girdwood, Rob Herring, Mark Rutland, Benoît Cousson,
Tony Lindgren, alsa-devel, devicetree, linux-kernel
In-Reply-To: <20171129213300.20021-15-afd@ti.com>
[-- Attachment #1: Type: text/plain, Size: 478 bytes --]
On Wed, Nov 29, 2017 at 03:32:55PM -0600, Andrew F. Davis wrote:
> A regulator being forcefully disabled is a catastrophic event that
> should never happen to most devices, especially not sound CODECs.
That's not what the disable notification handling is for. It's there so
that the driver can skip having to reinitialize the device if other
constraints mean the power doesn't actually get turned off when it
disables the regualtors. It's nothing to do with forced disables.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH v2 10/19] ASoC: tlv320aic31xx: Add MICBIAS off setting
From: Mark Brown @ 2017-12-01 13:30 UTC (permalink / raw)
To: Andrew F. Davis
Cc: Liam Girdwood, Rob Herring, Mark Rutland, Benoît Cousson,
Tony Lindgren, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20171129213300.20021-11-afd-l0cyMroinI0@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 732 bytes --]
On Wed, Nov 29, 2017 at 03:32:51PM -0600, Andrew F. Davis wrote:
> Leaving microphone bias off is a valid setting and even used in the DT
> binding document example. Add this setting here and document the same.
> - ai31xx-micbias-vg - MicBias Voltage setting
> + 0 or MICBIAS_OFF - MICBIAS output is powered off
> 1 or MICBIAS_2_0V - MICBIAS output is powered to 2.0V
> 2 or MICBIAS_2_5V - MICBIAS output is powered to 2.5V
> 3 or MICBIAS_AVDD - MICBIAS output is connected to AVDD
This doesn't make much sense. If we enable the microphone with a
voltage of 0V that's not going to work terribly well - I'd expect this
property to control the voltage set when it's enabled, not if it's
enabled.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH v2 09/19] ASoC: tlv320aic31xx: Remove platform data
From: Mark Brown @ 2017-12-01 13:26 UTC (permalink / raw)
To: Andrew F. Davis
Cc: Liam Girdwood, Rob Herring, Mark Rutland, Benoît Cousson,
Tony Lindgren, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20171129213300.20021-10-afd-l0cyMroinI0@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 309 bytes --]
On Wed, Nov 29, 2017 at 03:32:50PM -0600, Andrew F. Davis wrote:
> Platform data is not used by anyone (at least in upstream) so
> drop this data and switch to using fwnode(DT/ACPI) only.
The advantage being...? Not all architectures use DT or ACPI so it's
not clear that this is a step forwards in itself.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH v2 0/4] DRM driver for ILI9225 display panels
From: Noralf Trønnes @ 2017-12-01 13:17 UTC (permalink / raw)
To: David Lechner, dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Rob Herring, Mark Rutland, linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1511122328-31133-1-git-send-email-david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
Den 19.11.2017 21.12, skrev David Lechner:
> This is a new driver for ILI9225 based display panels.
Thanks, applied to drm-misc.
Noralf.
> v2 changes:
> * New patch for ilitek vendor prefix.
> * Use "ilitek" instead of "generic" in dt-bindings
> * New patch to export 2 mipi_dbi_* functions
> * Clean up ILI9225 driver based on feedback
>
> David Lechner (4):
> dt-bindings: Add vendor prefix for ilitek
> dt-bindings: Add binding for Ilitek ILI9225 display panels
> drm/tinydrm: export mipi_dbi_buf_copy and mipi_dbi_spi_cmd_max_speed
> drm/tinydrm: add driver for ILI9225 panels
>
> .../devicetree/bindings/display/ilitek,ili9225.txt | 25 ++
> .../devicetree/bindings/vendor-prefixes.txt | 1 +
> MAINTAINERS | 6 +
> drivers/gpu/drm/tinydrm/Kconfig | 10 +
> drivers/gpu/drm/tinydrm/Makefile | 1 +
> drivers/gpu/drm/tinydrm/ili9225.c | 468 +++++++++++++++++++++
> drivers/gpu/drm/tinydrm/mipi-dbi.c | 24 +-
> include/drm/tinydrm/mipi-dbi.h | 4 +-
> 8 files changed, 534 insertions(+), 5 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/display/ilitek,ili9225.txt
> create mode 100644 drivers/gpu/drm/tinydrm/ili9225.c
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH] fsi: Add Self Boot Engine FIFO FSI client
From: Andrew Jeffery @ 2017-12-01 13:07 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Andrew Jeffery, gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8,
bradleyb-r5pk2Da7Bxt8sGd51Jp2sdBPR1lH4CV8,
cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
joel-U3u1mxZcP9KHXe+LvDLADg,
eajames-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8, Edward A. James
In-Reply-To: <1510947274-7519-5-git-send-email-eajames-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
The POWER Self Boot Engine (SBE) is an on-chip micro-controller that cranks
POWER processors into action and provides support for some runtime services.
One such runtime service is mediation of communication between the service
processor (that initiated the boot process) and the POWER chip at large. This
is handled using message passing over a pair of FIFOs on top of an FSI bus
between the service processor and the POWER chip.
This driver implements support for management of the SBE FIFO interface to the
POWER chip.
The FIFO semantics are fairly straight forward:
1. A command message is enqueued from the service processor to the upstream
FIFO, followed by queuing an End-of-Transfer (EOT) marker
2. The message on the upstream FIFO is consumed by the SBE and acted on as
required
3. The SBE responds on the downstream FIFO, terminating its response with an
EOT marker
Additionally, the following rules apply:
1. Only one command is accepted on the upstream FIFO until the EOT marker on
the downstream FIFO is dequeued.
2. A response will not be enqueued on the downstream FIFO until a complete
command with EOT has been dequeued from the upstream FIFO
A significant pain point in the design is an interrupt is not available to the
service processor to know when data is available for dequeuing from the
downstream FIFO. As a consequence the driver sets up a timer as required to
poll the hardware state and drive consumption of a response when enqueued.
If the FIFOs become corrupt via either parity or logic faults, they can be
reset. With respect to resets the SBE will always act as a slave and will never
initiate a FIFO reset, rather the service processor must request the reset and
receive an acknowledgement from the SBE that the reset has been performed.
Both FIFOs are implemented in hardware as a ring of eight 32-bit data slots,
where each slot has associated EOT and validity bits.
The implementation provides mutual exclusion over access to the FIFO to enforce
integrity whilst supporting multiple open clients. An in-kernel API is exposed
that maps nicely onto character device semantics, making way for a
straight-forward patch to expose the FIFO to userspace e.g. via a misc-device.
Signed-off-by: Andrew Jeffery <andrew-zrmu5oMJ5Fs@public.gmane.org>
---
Hello,
I'm reviewing the patch by way of offering an alternative implementation. My
concerns with Eddie and Brad's implementation are:
1. Transfers are entirely handled by the timer callback, driving complexity
with enqueuing and dequeuing jobs from the "xfers" list (which are consumed
by the callback). This enqueuing/dequeuing is necessary even if the transfer
is started immediately, as this is done by tripping the timer using a zero
offset from jiffies. In the past we ran into a number of instances of memory
corruption with mishandling of the list.
2. Delegation of transfers to the timer callback leads to complexity of
reference counting for the FIFO and clients as everything is done
asynchronously, increasing the cleanup complexity
3. Complexity of the transfer state machine, represented by properties that are
not mutually exclusive (i.e. a state is built from a set of independent bit
states).
4. Unergonomic interfaces for handling in-kernel vs userspace consumers: For
instance sbefifo_read_common() takes both userspace and kernel buffer
pointers as arguments, one of which is expected to be NULL, and the
userspace pointer takes preference. I think a better design would be to
separate the userspace interface from the driver, and make the userspace
implementation call through the in-kernel APIs.
5. The sbefifo_buf struct mirrors the hardware ringbuffers with in-memory ring
buffers, adding a layer of indirection that feels odd
6. Unclear locking semantics combined with {READ,WRITE}_ONCE() makes it more
difficult to verify correct behaviour
So, I thought I'd have a crack at implementing something more direct. It may
still have disadvantages - for instance I haven't thought about how to
implement non-blocking IO with my approach, which is something that Eddie's
patch has. It may prove to be an Achilles heal.
The two approaches roughly break even in size when the chardev interface is
implemented (not included here).
Something that I hoped would turn out simpler than it did was state management
of the FIFO, clients and the polling thread. But I think there are still
benefits to having explicit states.
So the approach I've taken is:
1. Reduce the timer callback to something approximating an interrupt: When it
detects the FIFO is ready it simply issues a wake_up() to the waiter on the
poll waitqueue.
2. The consequence of this is we no-longer need the xfer lists and intermediate
buffers, as the FIFO is directly accessed the calling process
3. We wind up with a system of three waitqueues, reflecting the three concepts
required for use of the FIFO: One to mediate access to the FIFO, woken on
FIFO state changes; one for each client, woken when it's the client's turn
to access the FIFO, and one for the timer, woken when the hardware becomes
ready for reading or writing.
4. With the waitqueues we use two locks: The FIFO waitqueue lock covers FIFO
and client state changes, whilst the timer waitqueue lock covers changes to
the polling state.
5. Between the waitqueues and state machines we mostly only have to hold the
locks on state transitions. The exception is on enqueuing and dequeuing data
from the FIFO, where we need to first check if the client has been
release()ed or the driver remove()ed
I've tested the patch by reworking the OCC/hwmon patches on top of the
in-kernel interface implemented here. It's survived light testing with probing
and reading values via the hwmon sysfs interface. It survives reboots of the
host, then killing the host to exercise the cleanup on remove. The final test
was booting with the host "half-on" (power applied but nothing further), where
we can talk to the FIFO but the SBE is not active. This exercises the timeout
paths in the driver on probe. It survived these tests, but this still falls far
short of the testing that Eddie's driver has seen internally, which is another
feather in its cap.
I'm interested in everyone's thoughts on the pros and cons of each approach.
Cheers,
Andrew
drivers/fsi/Kconfig | 2 +
drivers/fsi/Makefile | 9 +-
drivers/fsi/clients/Kconfig | 5 +
drivers/fsi/clients/Makefile | 1 +
drivers/fsi/clients/fsi-sbefifo.c | 892 ++++++++++++++++++++++++++++++++++++++
drivers/fsi/clients/fsi-sbefifo.h | 82 ++++
6 files changed, 987 insertions(+), 4 deletions(-)
create mode 100644 drivers/fsi/clients/Kconfig
create mode 100644 drivers/fsi/clients/Makefile
create mode 100644 drivers/fsi/clients/fsi-sbefifo.c
create mode 100644 drivers/fsi/clients/fsi-sbefifo.h
diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index 6821ed0cd5e8..1a6548e3be92 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -33,6 +33,8 @@ config FSI_SCOM
---help---
This option enables an FSI based SCOM device driver.
+source "drivers/fsi/clients/Kconfig"
+
endif
endmenu
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index 65eb99dfafdb..8e158a299701 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -1,5 +1,6 @@
+obj-$(CONFIG_FSI) += fsi-core.o
+obj-$(CONFIG_FSI_MASTER_HUB) += fsi-master-hub.o
+obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
+obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
-obj-$(CONFIG_FSI) += fsi-core.o
-obj-$(CONFIG_FSI_MASTER_HUB) += fsi-master-hub.o
-obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
-obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
+obj-$(CONFIG_FSI) += clients/
diff --git a/drivers/fsi/clients/Kconfig b/drivers/fsi/clients/Kconfig
new file mode 100644
index 000000000000..3cb3feba84bb
--- /dev/null
+++ b/drivers/fsi/clients/Kconfig
@@ -0,0 +1,5 @@
+config FSI_SBEFIFO
+ tristate "Self Boot Engine FIFO"
+ ---help---
+ The Self Boot Engine FIFO manages communication between a service
+ processor and the Self Boot Engine on POWER processors.
diff --git a/drivers/fsi/clients/Makefile b/drivers/fsi/clients/Makefile
new file mode 100644
index 000000000000..d71a4de7aa0f
--- /dev/null
+++ b/drivers/fsi/clients/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_FSI_SBEFIFO) += fsi-sbefifo.o
diff --git a/drivers/fsi/clients/fsi-sbefifo.c b/drivers/fsi/clients/fsi-sbefifo.c
new file mode 100644
index 000000000000..c93c2d577f6d
--- /dev/null
+++ b/drivers/fsi/clients/fsi-sbefifo.c
@@ -0,0 +1,892 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* Copyright (C) IBM Corporation 2017 */
+
+#include <linux/bitops.h>
+#include <linux/fsi.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+#include "fsi-sbefifo.h"
+
+#define FSI_ENGINE_ID_SBE 0x22
+
+#define SBEFIFO_FIFO_DEPTH 8
+
+#define SBEFIFO_UP_FIFO 0x0
+#define SBEFIFO_UP_STS 0x4
+#define SBEFIFO_STS_PARITY BIT(29)
+#define SBEFIFO_STS_RESET BIT(25)
+#define SBEFIFO_STS_FULL BIT(21)
+#define SBEFIFO_STS_EMPTY BIT(20)
+#define SBEFIFO_STS_ENTRIES_SHIFT 16
+#define SBEFIFO_STS_ENTRIES_MASK GENMASK(19, 16)
+#define SBEFIFO_STS_VALID_SHIFT 8
+#define SBEFIFO_STS_VALID_MASK GENMASK(15, 8)
+#define SBEFIFO_STS_EOT_MASK GENMASK(7, 0)
+#define SBEFIFO_UP_EOT 0x8
+#define SBEFIFO_UP_RESET_REQ 0xC
+
+#define SBEFIFO_DOWN_FIFO 0x40
+#define SBEFIFO_DOWN_STS 0x44
+#define SBEFIFO_DOWN_RESET 0x48
+#define SBEFIFO_DOWN_EOT_ACK 0x54
+
+#define SBEFIFO_POLL_INTERVAL msecs_to_jiffies(50)
+#define SBEFIFO_LONG_TIMEOUT msecs_to_jiffies(30 * 1000)
+
+LIST_HEAD(sbefifos);
+
+static DEFINE_IDA(sbefifo_ida);
+
+static int sbefifo_readl(struct sbefifo *fifo, u32 addr, u32 *word)
+{
+ __be32 raw;
+ int rv;
+
+ rv = fsi_device_read(fifo->fsi, addr, &raw, sizeof(raw));
+ if (rv < 0)
+ return rv;
+
+ *word = be32_to_cpu(raw);
+
+ return 0;
+}
+
+static int sbefifo_writel(struct sbefifo *fifo, u32 addr, u32 word)
+{
+ __be32 cooked = cpu_to_be32(word);
+
+ return fsi_device_write(fifo->fsi, addr, &cooked, sizeof(cooked));
+}
+
+#define sbefifo_up_sts(f, dp) sbefifo_readl(f, SBEFIFO_UP_STS, dp)
+#define sbefifo_down_sts(f, dp) sbefifo_readl(f, SBEFIFO_DOWN_STS, dp);
+
+#define sbefifo_parity(sts) ((sts) & SBEFIFO_STS_PARITY)
+#define sbefifo_populated(sts) \
+ (((sts) & SBEFIFO_STS_ENTRIES_MASK) >> SBEFIFO_STS_ENTRIES_SHIFT)
+#define sbefifo_vacant(sts) (SBEFIFO_FIFO_DEPTH - sbefifo_populated(sts))
+#define sbefifo_empty(sts) ((sts) & SBEFIFO_STS_EMPTY)
+#define sbefifo_full(sts) ((sts) & SBEFIFO_STS_FULL)
+#define sbefifo_eot_set(sts) ((sts) & SBEFIFO_STS_EOT_MASK)
+#define sbefifo_valid_set(sts) \
+ (((sts) & SBEFIFO_STS_VALID_MASK) >> SBEFIFO_STS_VALID_SHIFT)
+
+#define sbefifo_reset_req(sts) (!!((sts) & SBEFIFO_STS_RESET))
+#define sbefifo_do_reset(f) sbefifo_writel(f, SBEFIFO_DOWN_RESET, 0)
+#define sbefifo_req_reset(f) sbefifo_writel(f, SBEFIFO_UP_RESET_REQ, 0)
+
+static int sbefifo_wait_reset(struct sbefifo *fifo, unsigned long expire)
+{
+ u32 sts;
+ int rv;
+
+ do {
+ rv = sbefifo_up_sts(fifo, &sts);
+ if (rv < 0)
+ return rv;
+ } while (sbefifo_reset_req(sts) && time_before(jiffies, expire));
+
+ if (sbefifo_reset_req(sts)) {
+ dev_warn(fifo->dev, "FIFO reset request timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ dev_info(fifo->dev, "SBE acknowleged reset request, FIFO is reset\n");
+
+ return 0;
+}
+
+static int sbefifo_reset(struct sbefifo *fifo)
+{
+ int rv;
+
+ rv = sbefifo_req_reset(fifo);
+ if (!rv)
+ rv = sbefifo_wait_reset(fifo, jiffies + SBEFIFO_POLL_INTERVAL);
+
+ if (rv < 0)
+ dev_err(fifo->dev, "FIFO reset failed: %d\n", rv);
+
+ return rv;
+}
+
+static int sbefifo_wait(struct sbefifo *fifo, enum sbefifo_direction dir,
+ unsigned long period)
+{
+ struct sbefifo_poll *poll = &fifo->poll;
+ unsigned long flags;
+ bool ready;
+ u32 addr;
+ bool up;
+ u32 sts;
+ int rv;
+
+ up = (dir == sbefifo_up);
+ addr = up ? SBEFIFO_UP_STS : SBEFIFO_DOWN_STS;
+ rv = sbefifo_readl(fifo, addr, &sts);
+ if (rv < 0)
+ return rv;
+
+ ready = !(up ? sbefifo_full(sts) : sbefifo_empty(sts));
+ if (ready)
+ return 0;
+
+ dev_info(fifo->dev, "Polling for FIFO response every %ld jiffies (%s)",
+ SBEFIFO_POLL_INTERVAL, period ? "bounded" : "unbounded");
+
+ spin_lock_irqsave(&poll->wait.lock, flags);
+ poll->interval = SBEFIFO_POLL_INTERVAL;
+ poll->expire = period;
+ poll->expire_at = period ? jiffies + period : 0;
+ poll->state = sbefifo_poll_wait;
+ poll->dir = dir;
+ poll->rv = 0;
+
+ mod_timer(&poll->timer, jiffies + poll->interval);
+
+ if (period) {
+ rv = wait_event_interruptible_locked_irq(poll->wait,
+ (poll->state != sbefifo_poll_wait || poll->rv ||
+ time_after(jiffies, poll->expire_at)));
+ } else {
+ rv = wait_event_interruptible_locked_irq(poll->wait,
+ (poll->state != sbefifo_poll_wait || poll->rv));
+ }
+
+ if (rv < 0) {
+ spin_unlock_irqrestore(&poll->wait.lock, flags);
+ return rv;
+ }
+
+ if (poll->state == sbefifo_poll_wait && !poll->rv) {
+ spin_unlock_irqrestore(&poll->wait.lock, flags);
+ return -ETIMEDOUT;
+ }
+
+ if (poll->state == sbefifo_poll_ready || poll->rv) {
+ rv = poll->rv;
+ spin_unlock_irqrestore(&poll->wait.lock, flags);
+ return rv;
+ }
+
+ WARN_ON(poll->state != sbefifo_poll_reset);
+ spin_unlock_irqrestore(&poll->wait.lock, flags);
+
+ return -EIO;
+}
+
+#define sbefifo_wait_vacant(f, p) sbefifo_wait(f, sbefifo_up, p);
+#define sbefifo_wait_primed(f, p) sbefifo_wait(f, sbefifo_down, p);
+
+static void sbefifo_poll_device(unsigned long context)
+{
+ struct sbefifo *fifo = (struct sbefifo *) context;
+ struct sbefifo_poll *poll = &fifo->poll;
+ unsigned long flags;
+ u32 addr;
+ bool up;
+ u32 sts;
+ int rv;
+
+ /* Sanity check poll settings */
+ spin_lock_irqsave(&poll->wait.lock, flags);
+ up = (poll->dir == sbefifo_up);
+ spin_unlock_irqrestore(&poll->wait.lock, flags);
+
+ /* Read status */
+ addr = up ? SBEFIFO_UP_STS : SBEFIFO_DOWN_STS;
+ rv = sbefifo_readl(fifo, addr, &sts);
+ if (rv < 0) {
+ poll->rv = rv;
+ wake_up(&poll->wait);
+ return;
+ }
+
+ /* Update poll state */
+ spin_lock_irqsave(&poll->wait.lock, flags);
+ if (sbefifo_parity(sts))
+ poll->state = sbefifo_poll_reset;
+ else if (!(up ? sbefifo_full(sts) : sbefifo_empty(sts)))
+ poll->state = sbefifo_poll_ready;
+
+ if (poll->state != sbefifo_poll_wait) {
+ wake_up_locked(&poll->wait);
+ } else if (poll->expire && time_after(jiffies, poll->expire_at)) {
+ wake_up_locked(&poll->wait);
+ } else {
+ dev_dbg(fifo->dev, "Not ready, waiting another %lu jiffies\n",
+ poll->interval);
+ mod_timer(&fifo->poll.timer, jiffies + poll->interval);
+ }
+ spin_unlock_irqrestore(&poll->wait.lock, flags);
+}
+
+/* Precondition: Upstream FIFO is not full */
+static int sbefifo_enqueue(struct sbefifo *fifo, u32 data)
+{
+ unsigned long flags;
+ int rv;
+
+ /* Detect if we need to bail due to release() or remove() */
+ spin_lock_irqsave(&fifo->wait.lock, flags);
+ if (likely(fifo->state == sbefifo_tx))
+ rv = fsi_device_write(fifo->fsi, SBEFIFO_UP_FIFO, &data,
+ sizeof(data));
+ else
+ rv = -EIO;
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+
+ return rv;
+}
+
+/* Precondition: Downstream FIFO is not empty */
+static int sbefifo_dequeue(struct sbefifo *fifo, u32 *data)
+{
+ unsigned long flags;
+ int rv;
+
+ /* Detect if we need to bail due to release() or remove() */
+ spin_lock_irqsave(&fifo->wait.lock, flags);
+ if (likely(fifo->state == sbefifo_rx))
+ rv = fsi_device_read(fifo->fsi, SBEFIFO_DOWN_FIFO, data,
+ sizeof(*data));
+ else
+ rv = -EIO;
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+
+ return rv;
+}
+
+static int sbefifo_fill(struct sbefifo *fifo, const u32 *buf, ssize_t len)
+{
+ ssize_t vacant, remaining;
+ u32 sts;
+ int rv;
+
+ rv = sbefifo_up_sts(fifo, &sts);
+ if (rv < 0)
+ return rv;
+
+ vacant = sbefifo_vacant(sts);
+
+ vacant = min(vacant, len);
+ remaining = vacant;
+ while (remaining--) {
+ rv = sbefifo_enqueue(fifo, *buf++);
+ if (rv < 0)
+ return rv;
+ }
+
+ return vacant;
+}
+
+static int sbefifo_signal_eot(struct sbefifo *fifo)
+{
+ int rv;
+
+ rv = sbefifo_wait_vacant(fifo, SBEFIFO_LONG_TIMEOUT);
+ if (rv < 0)
+ return rv;
+
+ rv = sbefifo_writel(fifo, SBEFIFO_UP_EOT, 0);
+ return rv;
+}
+
+static ssize_t sbefifo_up_write(struct sbefifo *fifo, const u32 *buf,
+ ssize_t len)
+{
+ ssize_t remaining = len;
+ int wrote;
+ int rv;
+
+ while (remaining) {
+ rv = sbefifo_wait_vacant(fifo, SBEFIFO_LONG_TIMEOUT);
+ if (rv < 0)
+ return rv;
+
+ wrote = sbefifo_fill(fifo, buf, len);
+ if (wrote < 0)
+ return wrote;
+
+ buf += wrote;
+ remaining -= wrote;
+ }
+
+ rv = sbefifo_signal_eot(fifo);
+ if (rv < 0)
+ return rv;
+
+ return len;
+}
+
+#define TEST_SET(s) ((s) & BIT(7))
+#define IS_EOT(s) TEST_SET(s)
+#define IS_VALID(s) TEST_SET(s)
+
+static int sbefifo_drain(struct sbefifo *fifo, u32 *buf, ssize_t len)
+{
+ ssize_t nr_valid;
+ u8 valid_set;
+ int nr_xfer;
+ ssize_t rem;
+ u8 eot_set;
+ u32 sts;
+ u32 val;
+ int rv;
+
+ rv = sbefifo_down_sts(fifo, &sts);
+ if (rv < 0)
+ return rv;
+
+ /* Determine tranfer characteristics */
+ nr_xfer = sbefifo_populated(sts);
+ valid_set = sbefifo_valid_set(sts);
+ eot_set = sbefifo_eot_set(sts);
+
+ if (hweight8(eot_set) > 1) {
+ dev_err(fifo->dev, "More than one EOT in the pipe!\n");
+ return -EIO;
+ }
+
+ /* Number of data words in the transfer */
+ nr_valid = hweight8(valid_set);
+ len = min(len, nr_valid);
+ rem = len;
+
+ dev_dbg(fifo->dev, "%s: valid_set: 0x%x, eot_set: 0x%x, nr_valid: %d, nr_xfer: %d, rem: %d\n",
+ __func__, valid_set, eot_set, nr_valid, nr_xfer, rem);
+
+ /* Dequeue data */
+ while (nr_xfer && rem && !IS_EOT(eot_set)) {
+ rv = sbefifo_dequeue(fifo, &val);
+ if (rv < 0)
+ return rv;
+
+ if (IS_VALID(valid_set)) {
+ *buf++ = val;
+ rem--;
+ }
+
+ valid_set <<= 1;
+ eot_set <<= 1;
+ nr_xfer--;
+ }
+
+ dev_dbg(fifo->dev, "%s: Data phase complete: valid_set: 0x%x, eot_set: 0x%x, nr_valid: %d, nr_xfer: %d, rem: %d\n",
+ __func__, valid_set, eot_set, nr_valid, nr_xfer, rem);
+
+ /*
+ * To allow the upper layers to manage state transitions, don't dequeue
+ * EOT yet. Leave that for the subsequent, terminating read.
+ */
+ if (nr_valid > 0)
+ return len;
+
+ /* Dequeue and ACK EOT word */
+ while (nr_xfer && IS_EOT(eot_set) && !IS_VALID(valid_set)) {
+ rv = sbefifo_dequeue(fifo, &val);
+ if (rv < 0)
+ return rv;
+
+ rv = sbefifo_writel(fifo, SBEFIFO_DOWN_EOT_ACK, val);
+ if (rv < 0)
+ return rv;
+
+ valid_set <<= 1;
+ eot_set <<= 1;
+ nr_xfer--;
+ }
+
+ dev_dbg(fifo->dev, "%s: EOT phase complete: valid_set: 0x%x, eot_set: 0x%x, nr_valid: %d\n, nr_xfer: %d, rem: %d\n",
+ __func__, valid_set, eot_set, nr_valid, nr_xfer, rem);
+
+ /* Dequeue any remaining dummy values */
+ while (nr_xfer && !IS_EOT(eot_set) && !IS_VALID(valid_set)) {
+ rv = sbefifo_dequeue(fifo, &val);
+ if (rv < 0)
+ return rv;
+
+ valid_set <<= 1;
+ eot_set <<= 1;
+ nr_xfer--;
+ }
+
+ dev_dbg(fifo->dev, "%s: Drain phase complete: valid_set: 0x%x, eot_set: 0x%x, nr_valid: %d, nr_xfer: %d, rem: %d\n",
+ __func__, valid_set, eot_set, nr_valid, nr_xfer, rem);
+
+ /* Test for parity failures */
+ rv = sbefifo_down_sts(fifo, &sts);
+ if (rv < 0)
+ return rv;
+
+ if (sbefifo_parity(sts)) {
+ dev_warn(fifo->dev, "Downstream FIFO parity failure\n");
+ return -EIO;
+ }
+
+ return len;
+}
+
+static ssize_t sbefifo_down_read(struct sbefifo *fifo, u32 *buf, ssize_t len)
+{
+ ssize_t rem = len;
+ int read;
+ int rv;
+
+ if (!rem)
+ return 0;
+
+ do {
+ rv = sbefifo_wait_primed(fifo, SBEFIFO_LONG_TIMEOUT);
+ if (rv < 0)
+ return rv;
+
+ read = sbefifo_drain(fifo, buf, rem);
+ if (read < 0)
+ return read;
+
+ buf += read;
+ rem -= read;
+ } while (rem && read && read == min((rem + read), SBEFIFO_FIFO_DEPTH));
+
+ return len - rem;
+}
+
+/* In-kernel API */
+
+/**
+ * sbefifo_open()
+ *
+ * @client The client context for the SBEFIFO
+ * @flags Flags controlling how to open the client.
+ *
+ * Returns 0 on success or negative values on failure.
+ */
+int sbefifo_open(struct sbefifo *fifo, struct sbefifo_client *client,
+ unsigned long oflags)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&fifo->wait.lock, flags);
+ if (fifo->state == sbefifo_dead) {
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+ return -ENODEV;
+ }
+ if (WARN(client->state != sbefifo_client_closed, "Already open\n")) {
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+ return -EINVAL;
+ }
+
+ /* No flags at the moment, probably O_NONBLOCK in the future */
+ if (oflags) {
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+ return -EINVAL;
+ }
+
+ init_waitqueue_head(&client->wait);
+ client->fifo = fifo;
+ client->flags = oflags;
+ client->state = sbefifo_client_idle;
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sbefifo_open);
+
+/**
+ * sbefifo_write()
+ *
+ * @client The client context for the SBEFIFO
+ * @buf The buffer of data to write, at least @len elements
+ * @len The number elements in @buffer
+ *
+ * The buffer must represent a complete chip-op: EOT is signalled after the
+ * last element is written to the upstream FIFO.
+ *
+ * Returns the number of elements written on success and negative values on
+ * failure. If the call is successful a subsequent call to sbefifo_read() MUST
+ * be made.
+ */
+ssize_t sbefifo_write(struct sbefifo_client *client, const u32 *buf,
+ ssize_t len)
+{
+ struct sbefifo *fifo = client->fifo;
+ unsigned long flags;
+ ssize_t rv;
+
+ spin_lock_irqsave(&fifo->wait.lock, flags);
+
+ if (client->state == sbefifo_client_active) {
+ dev_warn(fifo->dev, "Transfer already in progress\n");
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+ return -EBUSY;
+ }
+
+ rv = wait_event_interruptible_locked_irq(fifo->wait,
+ fifo->state == sbefifo_ready ||
+ fifo->state == sbefifo_dead);
+ if (rv < 0) {
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+ return rv;
+ }
+
+ if (fifo->state == sbefifo_dead) {
+ client->state = sbefifo_client_closed;
+ wake_up(&client->wait);
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+ return -ENODEV;
+ }
+
+ WARN_ON(fifo->state != sbefifo_ready);
+
+ fifo->curr = client;
+ fifo->state = sbefifo_tx;
+
+ /* Move a threaded read() onto waiting for FIFO read readiness */
+ client->state = sbefifo_client_active;
+ wake_up(&client->wait);
+
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+
+ /* FIFO Tx, reset the FIFO on error */
+ rv = sbefifo_up_write(fifo, buf, len);
+ if (rv < len) {
+ dev_err(fifo->dev, "FIFO write failed: %d\n", rv);
+ rv = sbefifo_reset(fifo);
+ if (rv < 0)
+ return rv;
+
+ spin_lock_irqsave(&fifo->wait.lock, flags);
+ fifo->state = sbefifo_ready;
+ client->state = sbefifo_client_idle;
+ wake_up(&client->wait);
+ wake_up_locked(&fifo->wait);
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+
+ return -EIO;
+ }
+
+ WARN(rv > len, "Unreachable state: len: %d, rv: %d\n", len, rv);
+
+ /* Write completed successfully */
+ spin_lock_irqsave(&fifo->wait.lock, flags);
+ fifo->state = sbefifo_interval;
+ wake_up(&client->wait);
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+
+ return rv;
+}
+EXPORT_SYMBOL_GPL(sbefifo_write);
+
+/**
+ * sbefifo_read()
+ *
+ * @client The client context for the SBEFIFO
+ * @data The buffer of data to write, at least @len elements
+ * @len The number elements in @buffer
+ *
+ * Returns the number of elements read on success and negative values on
+ * failure. A return value of 0 indicates EOT.
+ */
+ssize_t sbefifo_read(struct sbefifo_client *client, u32 *buf, ssize_t len)
+{
+ struct sbefifo *fifo = client->fifo;
+ unsigned long flags;
+ ssize_t rv;
+
+ rv = wait_event_interruptible(client->wait,
+ (client->state == sbefifo_client_active ||
+ client->state == sbefifo_client_closed));
+ if (rv < 0)
+ return rv;
+
+ spin_lock_irqsave(&fifo->wait.lock, flags);
+ if (client->state == sbefifo_client_closed) {
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+ return -EBADFD;
+ }
+
+ if (client->state == sbefifo_client_idle) {
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+ return -EIO;
+ }
+
+ rv = wait_event_interruptible_locked_irq(fifo->wait,
+ fifo->state == sbefifo_interval ||
+ fifo->state == sbefifo_rx ||
+ fifo->state == sbefifo_ready ||
+ fifo->state == sbefifo_dead);
+ if (rv < 0) {
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+ return rv;
+ }
+
+ if (fifo->state == sbefifo_ready) {
+ /* We've reset FIFO, whatever we were waiting for has gone */
+ client->state = sbefifo_client_idle;
+ /* We're done, wake another task up as the FIFO is ready */
+ wake_up_locked(&fifo->wait);
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+ return -EIO;
+ }
+
+ if (fifo->state == sbefifo_dead) {
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+ return -ENODEV;
+ }
+
+ fifo->state = sbefifo_rx;
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+
+ rv = sbefifo_down_read(fifo, buf, len);
+ if (rv > 0)
+ return rv;
+
+ /* Reset the FIFO on error */
+ if (rv < 0) {
+ dev_err(fifo->dev, "FIFO read failed: %d\n", rv);
+ rv = sbefifo_reset(fifo);
+ if (rv < 0)
+ return rv;
+
+ rv = -EIO;
+ }
+
+ /* Read is complete one way or the other (0 length read or error) */
+ spin_lock_irqsave(&fifo->wait.lock, flags);
+ client->state = sbefifo_client_idle;
+
+ /* Queue next FIFO transfer */
+ fifo->curr = NULL;
+ fifo->state = sbefifo_ready;
+ wake_up_locked(&fifo->wait);
+
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+
+ return rv;
+}
+EXPORT_SYMBOL_GPL(sbefifo_read);
+
+/**
+ * sbefifo_release()
+ *
+ * @client The client context for the SBEFIFO
+ *
+ */
+int sbefifo_release(struct sbefifo_client *client)
+{
+ struct sbefifo *fifo = client->fifo;
+ enum sbefifo_client_state old;
+ unsigned long flags;
+ int rv;
+
+ /* Determine if we need to clean up */
+ spin_lock_irqsave(&client->fifo->wait.lock, flags);
+ old = client->state;
+ client->state = sbefifo_client_closed;
+
+ if (old == sbefifo_client_closed) {
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+ return -EBADFD;
+ }
+
+ if (old == sbefifo_client_idle) {
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+ return 0;
+ }
+
+ /* We need to clean up, get noisy about inconsistencies */
+ dev_warn(fifo->dev, "Releasing client with transfer in progress!\n");
+ WARN_ON(old != sbefifo_client_active);
+ WARN_ON(fifo->state == sbefifo_ready);
+
+ /* Mark ourselves as broken for cleanup */
+ fifo->state = sbefifo_broken;
+ fifo->curr = NULL;
+
+ wake_up(&client->wait);
+ spin_unlock_irqrestore(&client->fifo->wait.lock, flags);
+
+ /* Clean up poll waiter */
+ spin_lock_irqsave(&fifo->poll.wait.lock, flags);
+ del_timer_sync(&fifo->poll.timer);
+ fifo->poll.rv = -EBADFD;
+ wake_up_all_locked(&fifo->poll.wait);
+ spin_unlock_irqrestore(&fifo->poll.wait.lock, flags);
+
+ /* Reset the FIFO */
+ rv = sbefifo_reset(fifo);
+ if (rv < 0)
+ return rv;
+
+ /* Mark the FIFO as ready and wake pending transfer */
+ spin_lock_irqsave(&client->fifo->wait.lock, flags);
+ fifo->state = sbefifo_ready;
+ wake_up_locked(&fifo->wait);
+ spin_unlock_irqrestore(&client->fifo->wait.lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sbefifo_release);
+
+static int sbefifo_unregister_child(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ of_device_unregister(pdev);
+ if (dev->of_node)
+ of_node_clear_flag(dev->of_node, OF_POPULATED);
+
+ return 0;
+}
+
+static int sbefifo_probe(struct device *dev)
+{
+ struct device_node *np;
+ struct sbefifo *fifo;
+ int child_idx;
+ u32 up, down;
+ int rv;
+
+ fifo = devm_kzalloc(dev, sizeof(*fifo), GFP_KERNEL);
+ if (!fifo)
+ return -ENOMEM;
+
+ fifo->dev = dev;
+ fifo->state = sbefifo_ready;
+ fifo->fsi = to_fsi_dev(dev);
+
+ fifo->id = ida_simple_get(&sbefifo_ida, 0, 0, GFP_KERNEL);
+ if (fifo->id < 0)
+ return fifo->id;
+
+ init_waitqueue_head(&fifo->wait);
+
+ /* No interrupts, need to poll the controller */
+ setup_timer(&fifo->poll.timer, sbefifo_poll_device,
+ (unsigned long)fifo);
+ init_waitqueue_head(&fifo->poll.wait);
+
+ rv = sbefifo_up_sts(fifo, &up);
+ if (rv < 0)
+ return rv;
+
+ rv = sbefifo_down_sts(fifo, &down);
+ if (rv < 0)
+ return rv;
+
+ if (!(sbefifo_empty(up) && sbefifo_empty(down))) {
+ dev_warn(fifo->dev, "FIFOs were not empty, requesting reset from SBE\n");
+ /* Request the SBE reset the FIFOs */
+ rv = sbefifo_reset(fifo);
+ if (rv == -ETIMEDOUT) {
+ dev_warn(fifo->dev, "SBE unresponsive, probing FIFO clients may fail. Performing hard FIFO reset\n");
+ rv = sbefifo_do_reset(fifo);
+ if (rv < 0)
+ return rv;
+ } else if (rv < 0) {
+ return rv;
+ }
+ }
+
+ dev_set_drvdata(dev, fifo);
+ list_add(&fifo->entry, &sbefifos);
+
+ child_idx = 0;
+ for_each_available_child_of_node(dev->of_node, np) {
+ struct platform_device *child;
+ char name[32];
+
+ snprintf(name, sizeof(name), "sbefifo%d-dev%d", fifo->id,
+ child_idx++);
+ child = of_platform_device_create(np, name, dev);
+ if (!child)
+ dev_warn(dev, "Failed to create platform device %s\n",
+ name);
+ }
+
+ return 0;
+}
+
+static int sbefifo_remove(struct device *dev)
+{
+ struct sbefifo *fifo = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ /*
+ * Don't wait to reach sbefifo_ready, we may deadlock through power
+ * being removed to the host without the FIFO driver being unbound,
+ * which can stall the in-progress transfers. We don't really care as
+ * the driver is now going away, and the reset in the probe() path
+ * should recover it.
+ */
+
+ device_for_each_child(dev, NULL, sbefifo_unregister_child);
+
+ list_del(&fifo->entry);
+
+ /* Kick out the waiting clients */
+ spin_lock_irqsave(&fifo->wait.lock, flags);
+ fifo->state = sbefifo_dead;
+
+ if (fifo->curr) {
+ fifo->curr->state = sbefifo_client_closed;
+ wake_up_all(&fifo->curr->wait);
+ }
+
+ wake_up_all_locked(&fifo->wait);
+ spin_unlock_irqrestore(&fifo->wait.lock, flags);
+
+ /* Kick out any in-progress job */
+ spin_lock_irqsave(&fifo->poll.wait.lock, flags);
+ del_timer_sync(&fifo->poll.timer);
+ fifo->poll.rv = -ENODEV;
+ wake_up_all_locked(&fifo->poll.wait);
+ spin_unlock_irqrestore(&fifo->poll.wait.lock, flags);
+
+ while (wq_has_sleeper(&fifo->wait) || wq_has_sleeper(&fifo->poll.wait))
+ schedule();
+
+ ida_simple_remove(&sbefifo_ida, fifo->id);
+
+ return 0;
+}
+
+static const struct fsi_device_id sbefifo_ids[] = {
+ { .engine_type = FSI_ENGINE_ID_SBE, .version = FSI_VERSION_ANY, },
+ { 0 },
+};
+
+static struct fsi_driver sbefifo_drv = {
+ .id_table = sbefifo_ids,
+ .drv = {
+ .name = "sbefifo",
+ .bus = &fsi_bus_type,
+ .probe = sbefifo_probe,
+ .remove = sbefifo_remove,
+ },
+};
+
+static __init int sbefifo_init(void)
+{
+ ida_init(&sbefifo_ida);
+ fsi_driver_register(&sbefifo_drv);
+
+ return 0;
+}
+
+static __exit void sbefifo_exit(void)
+{
+ fsi_driver_unregister(&sbefifo_drv);
+ ida_destroy(&sbefifo_ida);
+}
+
+module_init(sbefifo_init);
+module_exit(sbefifo_exit);
+
+MODULE_AUTHOR("Andrew Jeffery <andrew-zrmu5oMJ5Fs@public.gmane.org>");
+MODULE_DESCRIPTION("POWER9 Self Boot Engine FIFO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/fsi/clients/fsi-sbefifo.h b/drivers/fsi/clients/fsi-sbefifo.h
new file mode 100644
index 000000000000..323fabfa7fce
--- /dev/null
+++ b/drivers/fsi/clients/fsi-sbefifo.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef DRIVERS_FSI_SBEFIFO_H
+
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+enum sbefifo_direction {
+ sbefifo_up = 0,
+ sbefifo_down,
+};
+
+enum sbefifo_poll_state {
+ sbefifo_poll_wait,
+ sbefifo_poll_ready,
+ sbefifo_poll_reset,
+};
+
+/* Readiness Polling */
+struct sbefifo_poll {
+ struct timer_list timer;
+ wait_queue_head_t wait;
+ enum sbefifo_direction dir;
+ unsigned long interval;
+ bool expire;
+ unsigned long expire_at;
+ enum sbefifo_poll_state state;
+ int rv;
+};
+
+struct sbefifo_client;
+
+enum sbefifo_state {
+ sbefifo_ready = 0,
+ sbefifo_tx,
+ sbefifo_interval,
+ sbefifo_rx,
+ sbefifo_broken,
+ sbefifo_dead,
+};
+
+/**
+ * @eot True when read() dequeues and ACKs an EOT. Set false in the write() path
+ */
+struct sbefifo {
+ struct device *dev;
+ struct fsi_device *fsi;
+ int id;
+ enum sbefifo_state state;
+ struct sbefifo_poll poll;
+ struct sbefifo_client *curr;
+ wait_queue_head_t wait;
+
+ struct list_head entry;
+};
+
+enum sbefifo_client_state {
+ sbefifo_client_closed = 0,
+ sbefifo_client_idle,
+ sbefifo_client_active,
+};
+
+struct sbefifo_client {
+ struct sbefifo *fifo;
+
+ wait_queue_head_t wait;
+ enum sbefifo_client_state state;
+ unsigned int flags;
+};
+
+int sbefifo_open(struct sbefifo *fifo, struct sbefifo_client *client,
+ unsigned long flags);
+ssize_t sbefifo_write(struct sbefifo_client *client, const u32 *buf, ssize_t len);
+ssize_t sbefifo_read(struct sbefifo_client *client, u32 *buf, ssize_t len);
+int sbefifo_release(struct sbefifo_client *client);
+
+extern struct list_head sbefifos;
+
+#define sbefifo_for_each_dev(pos) \
+ list_for_each_entry(pos, &sbefifos, entry)
+
+#endif /* DRIVERS_FSI_SBEFIFO_H */
--
2.14.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH v2 04/11] ARM: dts: imx6dl: Pass the reg properties for mipi nodes
From: Philipp Zabel @ 2017-12-01 13:05 UTC (permalink / raw)
To: Fabio Estevam, shawnguo-DgEjT+Ai2ygdnm+yROfE0A
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Fabio Estevam
In-Reply-To: <1511981684-13433-4-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On Wed, 2017-11-29 at 16:54 -0200, Fabio Estevam wrote:
> From: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
>
> Pass the reg properties for mipi nodes in order to fix the
> following build warnings with W=1:
>
> arch/arm/boot/dts/imx6dl-apf6dev.dtb: Warning (unit_address_vs_reg): Node /soc/aips-bus@2100000/mipi@21dc000/port@1/endpoint@0 has a unit name, but no reg property
> arch/arm/boot/dts/imx6dl-apf6dev.dtb: Warning (unit_address_vs_reg): Node /soc/aips-bus@2100000/mipi@21dc000/port@1/endpoint@1 has a unit name, but no reg property
> arch/arm/boot/dts/imx6dl-apf6dev.dtb: Warning (unit_address_vs_reg): Node /soc/aips-bus@2100000/mipi@21dc000/port@2/endpoint@0 has a unit name, but no reg property
> arch/arm/boot/dts/imx6dl-apf6dev.dtb: Warning (unit_address_vs_reg): Node /soc/aips-bus@2100000/mipi@21dc000/port@2/endpoint@1 has a unit name, but no reg property
> arch/arm/boot/dts/imx6dl-apf6dev.dtb: Warning (unit_address_vs_reg): Node /soc/aips-bus@2100000/mipi@21dc000/port@3/endpoint@0 has a unit name, but no reg property
> arch/arm/boot/dts/imx6dl-apf6dev.dtb: Warning (unit_address_vs_reg): Node /soc/aips-bus@2100000/mipi@21dc000/port@3/endpoint@1 has a unit name, but no reg property
> arch/arm/boot/dts/imx6dl-apf6dev.dtb: Warning (unit_address_vs_reg): Node /soc/aips-bus@2100000/mipi@21dc000/port@4/endpoint@0 has a unit name, but no reg property
> arch/arm/boot/dts/imx6dl-apf6dev.dtb: Warning (unit_address_vs_reg): Node /soc/aips-bus@2100000/mipi@21dc000/port@4/endpoint@1 has a unit name, but no reg property
>
> Cc: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> Signed-off-by: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
Reviewed-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
regards
Philipp
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 05/11] ARM: dts: imx6dl: Remove unneeded label and unit address
From: Philipp Zabel @ 2017-12-01 13:04 UTC (permalink / raw)
To: Fabio Estevam, shawnguo-DgEjT+Ai2ygdnm+yROfE0A
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Fabio Estevam
In-Reply-To: <1511981684-13433-5-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On Wed, 2017-11-29 at 16:54 -0200, Fabio Estevam wrote:
> From: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
>
> Remove unneeded label and unit address in order to fix the following
> build warnings with W=1:
>
> arch/arm/boot/dts/imx6dl-apf6dev.dtb: Warning (unit_address_vs_reg): Node /soc/aips-bus@2000000/iomuxc-gpr@20e0000/ipu1_csi0_mux@34 has a unit name, but no reg property
> arch/arm/boot/dts/imx6dl-apf6dev.dtb: Warning (unit_address_vs_reg): Node /soc/aips-bus@2000000/iomuxc-gpr@20e0000/ipu1_csi1_mux@34 has a unit name, but no
>
> Cc: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> Signed-off-by: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
Reviewed-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
regards
Philipp
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 2/2] regulator: sc2731: Add regulator driver to support Spreadtrum SC2731 PMIC
From: Mark Brown @ 2017-12-01 13:01 UTC (permalink / raw)
To: Erick Chen
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
baolin.wang-QSEj5FYQhm4dnm+yROfE0A,
baolin.wang-lxIno14LUO0EEoCn2XhGlw
In-Reply-To: <75a6a48f603a25d744ef46287e036975b0203608.1512118703.git.erick.chen-lxIno14LUO0EEoCn2XhGlw@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 879 bytes --]
On Fri, Dec 01, 2017 at 04:58:16PM +0800, Erick Chen wrote:
> +static const struct of_device_id sc2731_regulator_of_match[] = {
> + {.compatible = "sprd,sc2731-regulator",},
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, sc2731_regulator_of_match);
This looks like a subdriver for one specific device that can't be reused
with anything else as it's specific to an individual device so I'd not
expect it to appear as a separate thing in DT - the way Linux splits
things up might not map well onto other OSs and may even change in
future versions of Linux (look at all the audio drivers with clock
controllers in them for example). I'd expect the MFD to just register
the subdevice without needing it to appear in the DT.
> +subsys_initcall(sc2731_regulator_init);
> +module_exit(sc2731_regulator_exit);
Why not module_platform_driver()? Deferred probe should sort out probe
order.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH 2/2] regulator: sc2731: Add regulator driver to support Spreadtrum SC2731 PMIC
From: Mark Brown @ 2017-12-01 12:49 UTC (permalink / raw)
To: Philippe Ombredanne
Cc: Erick Chen, Rob Herring, Mark Rutland,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, LKML,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
baolin.wang-QSEj5FYQhm4dnm+yROfE0A,
baolin.wang-lxIno14LUO0EEoCn2XhGlw
In-Reply-To: <CAOFm3uFWhOMy=0nibOjDP_vP+m7Dcf1cGOmXXgcrSy6PDfqEWQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 561 bytes --]
On Fri, Dec 01, 2017 at 10:13:27AM +0100, Philippe Ombredanne wrote:
> On Fri, Dec 1, 2017 at 9:58 AM, Erick Chen <erick.chen-lxIno14LUO0EEoCn2XhGlw@public.gmane.org> wrote:
> I think that per Linus, and Thomas doc patches for SPDX ids this
> should be instead either:
> > +// SPDX-License-Identifier: GPL-2.0
> > + // Copyright (c) 2017 Spreadtrum Communications Inc.
> or at least this with the id on the first and the // comment style
> > +// SPDX-License-Identifier: GPL-2.0
Are you saying SPDX requires C++ style comments? That seems totally
broken.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH v2 33/35] clocksource/drivers/atcpit100: Add andestech atcpit100 timer
From: Linus Walleij @ 2017-12-01 12:30 UTC (permalink / raw)
To: Greentime Hu
Cc: greentime, linux-kernel@vger.kernel.org, Arnd Bergmann,
linux-arch, Thomas Gleixner, Jason Cooper, Marc Zyngier,
Rob Herring, netdev, deanbo422,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Al Viro, David Howells, Will Deacon, Daniel Lezcano, linux-serial,
Rick Chen
In-Reply-To: <672e0b3843953d1ab69bc19baf1a0f217ec1b1fa.1511785528.git.green.hu@gmail.com>
On Mon, Nov 27, 2017 at 1:28 PM, Greentime Hu <green.hu@gmail.com> wrote:
> From: Rick Chen <rickchen36@gmail.com>
>
> ATCPIT100 is often used on the Andes architecture,
> This timer provide 4 PIT channels. Each PIT channel is a
> multi-function timer, can be configured as 32,16,8 bit timers
> or PWM as well.
>
> For system timer it will set channel 1 32-bit timer0 as clock
> source and count downwards until underflow and restart again.
>
> It also set channel 0 32-bit timer0 as clock event and count
> downwards until condition match. It will generate an interrupt
> for handling periodically.
>
> Signed-off-by: Rick Chen <rickchen36@gmail.com>
> Signed-off-by: Greentime Hu <green.hu@gmail.com>
The driver looks nice overall.
> +static struct timer_of to = {
> + .flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE,
> +
> + .clkevt = {
> + .name = "atcpit100_tick",
> + .rating = 300,
> + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
> + .set_state_shutdown = atcpit100_clkevt_shutdown,
> + .set_state_periodic = atcpit100_clkevt_set_periodic,
> + .set_state_oneshot = atcpit100_clkevt_set_oneshot,
> + .tick_resume = atcpit100_clkevt_shutdown,
> + .set_next_event = atcpit100_clkevt_next_event,
> + .cpumask = cpu_all_mask,
> + },
> +
> + .of_irq = {
> + .handler = atcpit100_timer_interrupt,
> + .flags = IRQF_TIMER | IRQF_IRQPOLL,
> + },
I would add:
.of_clk = {
.name = "PCLK",
};
To be explicit on what we use.
(I hope I understand this OF timer right.)
Otherwise it looks good!
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Yours,
Linus Walleij
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox