From 218d72a77b0bc203649c28f03cad6f90af88a787 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 21 May 2018 11:00:01 +0100 Subject: pinctrl: madera: Add driver for Cirrus Logic Madera codecs These codecs have a variable number of I/O lines each of which is individually selectable to a wide range of possible functions. The functionality is slightly different from the traditional muxed GPIO since most of the functions can be mapped to any pin (and even the same function to multiple pins). Most pins have a dedicated "alternate" function that is only available on that pin. The alternate functions are usually a group of signals, though it is not always necessary to enable the full group, depending on the alternate function and how it is to be used. The mapping between alternate functions and GPIO pins varies between codecs depending on the number of alternate functions and available pins. Signed-off-by: Richard Fitzgerald Reviewed-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/cirrus/Kconfig | 14 + drivers/pinctrl/cirrus/Makefile | 13 + drivers/pinctrl/cirrus/pinctrl-cs47l35.c | 45 ++ drivers/pinctrl/cirrus/pinctrl-cs47l85.c | 59 ++ drivers/pinctrl/cirrus/pinctrl-cs47l90.c | 57 ++ drivers/pinctrl/cirrus/pinctrl-madera-core.c | 1076 ++++++++++++++++++++++++++ drivers/pinctrl/cirrus/pinctrl-madera.h | 41 + 9 files changed, 1307 insertions(+) create mode 100644 drivers/pinctrl/cirrus/Kconfig create mode 100644 drivers/pinctrl/cirrus/Makefile create mode 100644 drivers/pinctrl/cirrus/pinctrl-cs47l35.c create mode 100644 drivers/pinctrl/cirrus/pinctrl-cs47l85.c create mode 100644 drivers/pinctrl/cirrus/pinctrl-cs47l90.c create mode 100644 drivers/pinctrl/cirrus/pinctrl-madera-core.c create mode 100644 drivers/pinctrl/cirrus/pinctrl-madera.h (limited to 'drivers/pinctrl') diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 01fe8e0455a0..bc3bd2075ed0 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -359,6 +359,7 @@ source "drivers/pinctrl/vt8500/Kconfig" source "drivers/pinctrl/mediatek/Kconfig" source "drivers/pinctrl/zte/Kconfig" source "drivers/pinctrl/meson/Kconfig" +source "drivers/pinctrl/cirrus/Kconfig" config PINCTRL_XWAY bool diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 657332b121fb..e9aa4f913376 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -63,3 +63,4 @@ obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/ obj-$(CONFIG_ARCH_VT8500) += vt8500/ obj-y += mediatek/ obj-$(CONFIG_PINCTRL_ZX) += zte/ +obj-y += cirrus/ diff --git a/drivers/pinctrl/cirrus/Kconfig b/drivers/pinctrl/cirrus/Kconfig new file mode 100644 index 000000000000..27013e5949bc --- /dev/null +++ b/drivers/pinctrl/cirrus/Kconfig @@ -0,0 +1,14 @@ +# This is all selected by the Madera MFD driver Kconfig options +config PINCTRL_MADERA + tristate + select PINMUX + select GENERIC_PINCONF + +config PINCTRL_CS47L35 + bool + +config PINCTRL_CS47L85 + bool + +config PINCTRL_CS47L90 + bool diff --git a/drivers/pinctrl/cirrus/Makefile b/drivers/pinctrl/cirrus/Makefile new file mode 100644 index 000000000000..6e4938cde9e3 --- /dev/null +++ b/drivers/pinctrl/cirrus/Makefile @@ -0,0 +1,13 @@ +# Cirrus Logic pinctrl drivers +pinctrl-madera-objs := pinctrl-madera-core.o +ifeq ($(CONFIG_PINCTRL_CS47L35),y) +pinctrl-madera-objs += pinctrl-cs47l35.o +endif +ifeq ($(CONFIG_PINCTRL_CS47L85),y) +pinctrl-madera-objs += pinctrl-cs47l85.o +endif +ifeq ($(CONFIG_PINCTRL_CS47L90),y) +pinctrl-madera-objs += pinctrl-cs47l90.o +endif + +obj-$(CONFIG_PINCTRL_MADERA) += pinctrl-madera.o diff --git a/drivers/pinctrl/cirrus/pinctrl-cs47l35.c b/drivers/pinctrl/cirrus/pinctrl-cs47l35.c new file mode 100644 index 000000000000..06b59160783d --- /dev/null +++ b/drivers/pinctrl/cirrus/pinctrl-cs47l35.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Pinctrl for Cirrus Logic CS47L35 + * + * Copyright (C) 2016-2017 Cirrus Logic + * + * 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; version 2. + */ + +#include +#include + +#include "pinctrl-madera.h" + +/* + * The alt func groups are the most commonly used functions we place these at + * the lower function indexes for convenience, and the less commonly used gpio + * functions at higher indexes. + * + * To stay consistent with the datasheet the function names are the same as + * the group names for that function's pins + * + * Note - all 1 less than in datasheet because these are zero-indexed + */ +static const unsigned int cs47l35_aif3_pins[] = { 0, 1, 2, 3 }; +static const unsigned int cs47l35_spk_pins[] = { 4, 5 }; +static const unsigned int cs47l35_aif1_pins[] = { 7, 8, 9, 10 }; +static const unsigned int cs47l35_aif2_pins[] = { 11, 12, 13, 14 }; +static const unsigned int cs47l35_mif1_pins[] = { 6, 15 }; + +static const struct madera_pin_groups cs47l35_pin_groups[] = { + { "aif1", cs47l35_aif1_pins, ARRAY_SIZE(cs47l35_aif1_pins) }, + { "aif2", cs47l35_aif2_pins, ARRAY_SIZE(cs47l35_aif2_pins) }, + { "aif3", cs47l35_aif3_pins, ARRAY_SIZE(cs47l35_aif3_pins) }, + { "mif1", cs47l35_mif1_pins, ARRAY_SIZE(cs47l35_mif1_pins) }, + { "pdmspk1", cs47l35_spk_pins, ARRAY_SIZE(cs47l35_spk_pins) }, +}; + +const struct madera_pin_chip cs47l35_pin_chip = { + .n_pins = CS47L35_NUM_GPIOS, + .pin_groups = cs47l35_pin_groups, + .n_pin_groups = ARRAY_SIZE(cs47l35_pin_groups), +}; diff --git a/drivers/pinctrl/cirrus/pinctrl-cs47l85.c b/drivers/pinctrl/cirrus/pinctrl-cs47l85.c new file mode 100644 index 000000000000..0a322e2a0fde --- /dev/null +++ b/drivers/pinctrl/cirrus/pinctrl-cs47l85.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Pinctrl for Cirrus Logic CS47L85 + * + * Copyright (C) 2016-2017 Cirrus Logic + * + * 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; version 2. + */ + +#include +#include + +#include "pinctrl-madera.h" + +/* + * The alt func groups are the most commonly used functions we place these at + * the lower function indexes for convenience, and the less commonly used gpio + * functions at higher indexes. + * + * To stay consistent with the datasheet the function names are the same as + * the group names for that function's pins + * + * Note - all 1 less than in datasheet because these are zero-indexed + */ +static const unsigned int cs47l85_mif1_pins[] = { 8, 9 }; +static const unsigned int cs47l85_mif2_pins[] = { 10, 11 }; +static const unsigned int cs47l85_mif3_pins[] = { 12, 13 }; +static const unsigned int cs47l85_aif1_pins[] = { 14, 15, 16, 17 }; +static const unsigned int cs47l85_aif2_pins[] = { 18, 19, 20, 21 }; +static const unsigned int cs47l85_aif3_pins[] = { 22, 23, 24, 25 }; +static const unsigned int cs47l85_aif4_pins[] = { 26, 27, 28, 29 }; +static const unsigned int cs47l85_dmic4_pins[] = { 30, 31 }; +static const unsigned int cs47l85_dmic5_pins[] = { 32, 33 }; +static const unsigned int cs47l85_dmic6_pins[] = { 34, 35 }; +static const unsigned int cs47l85_spk1_pins[] = { 36, 38 }; +static const unsigned int cs47l85_spk2_pins[] = { 37, 39 }; + +static const struct madera_pin_groups cs47l85_pin_groups[] = { + { "aif1", cs47l85_aif1_pins, ARRAY_SIZE(cs47l85_aif1_pins) }, + { "aif2", cs47l85_aif2_pins, ARRAY_SIZE(cs47l85_aif2_pins) }, + { "aif3", cs47l85_aif3_pins, ARRAY_SIZE(cs47l85_aif3_pins) }, + { "aif4", cs47l85_aif4_pins, ARRAY_SIZE(cs47l85_aif4_pins) }, + { "mif1", cs47l85_mif1_pins, ARRAY_SIZE(cs47l85_mif1_pins) }, + { "mif2", cs47l85_mif2_pins, ARRAY_SIZE(cs47l85_mif2_pins) }, + { "mif3", cs47l85_mif3_pins, ARRAY_SIZE(cs47l85_mif3_pins) }, + { "dmic4", cs47l85_dmic4_pins, ARRAY_SIZE(cs47l85_dmic4_pins) }, + { "dmic5", cs47l85_dmic5_pins, ARRAY_SIZE(cs47l85_dmic5_pins) }, + { "dmic6", cs47l85_dmic6_pins, ARRAY_SIZE(cs47l85_dmic6_pins) }, + { "pdmspk1", cs47l85_spk1_pins, ARRAY_SIZE(cs47l85_spk1_pins) }, + { "pdmspk2", cs47l85_spk2_pins, ARRAY_SIZE(cs47l85_spk2_pins) }, +}; + +const struct madera_pin_chip cs47l85_pin_chip = { + .n_pins = CS47L85_NUM_GPIOS, + .pin_groups = cs47l85_pin_groups, + .n_pin_groups = ARRAY_SIZE(cs47l85_pin_groups), +}; diff --git a/drivers/pinctrl/cirrus/pinctrl-cs47l90.c b/drivers/pinctrl/cirrus/pinctrl-cs47l90.c new file mode 100644 index 000000000000..fc38f579f492 --- /dev/null +++ b/drivers/pinctrl/cirrus/pinctrl-cs47l90.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Pinctrl for Cirrus Logic CS47L90 + * + * Copyright (C) 2016-2017 Cirrus Logic + * + * 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; version 2. + */ + +#include +#include + +#include "pinctrl-madera.h" + +/* + * The alt func groups are the most commonly used functions we place these at + * the lower function indexes for convenience, and the less commonly used gpio + * functions at higher indexes. + * + * To stay consistent with the datasheet the function names are the same as + * the group names for that function's pins + * + * Note - all 1 less than in datasheet because these are zero-indexed + */ +static const unsigned int cs47l90_mif1_pins[] = { 8, 9 }; +static const unsigned int cs47l90_mif2_pins[] = { 10, 11 }; +static const unsigned int cs47l90_mif3_pins[] = { 12, 13 }; +static const unsigned int cs47l90_aif1_pins[] = { 14, 15, 16, 17 }; +static const unsigned int cs47l90_aif2_pins[] = { 18, 19, 20, 21 }; +static const unsigned int cs47l90_aif3_pins[] = { 22, 23, 24, 25 }; +static const unsigned int cs47l90_aif4_pins[] = { 26, 27, 28, 29 }; +static const unsigned int cs47l90_dmic4_pins[] = { 30, 31 }; +static const unsigned int cs47l90_dmic5_pins[] = { 32, 33 }; +static const unsigned int cs47l90_dmic3_pins[] = { 34, 35 }; +static const unsigned int cs47l90_spk1_pins[] = { 36, 37 }; + +static const struct madera_pin_groups cs47l90_pin_groups[] = { + { "aif1", cs47l90_aif1_pins, ARRAY_SIZE(cs47l90_aif1_pins) }, + { "aif2", cs47l90_aif2_pins, ARRAY_SIZE(cs47l90_aif2_pins) }, + { "aif3", cs47l90_aif3_pins, ARRAY_SIZE(cs47l90_aif3_pins) }, + { "aif4", cs47l90_aif4_pins, ARRAY_SIZE(cs47l90_aif4_pins) }, + { "mif1", cs47l90_mif1_pins, ARRAY_SIZE(cs47l90_mif1_pins) }, + { "mif2", cs47l90_mif2_pins, ARRAY_SIZE(cs47l90_mif2_pins) }, + { "mif3", cs47l90_mif3_pins, ARRAY_SIZE(cs47l90_mif3_pins) }, + { "dmic3", cs47l90_dmic3_pins, ARRAY_SIZE(cs47l90_dmic3_pins) }, + { "dmic4", cs47l90_dmic4_pins, ARRAY_SIZE(cs47l90_dmic4_pins) }, + { "dmic5", cs47l90_dmic5_pins, ARRAY_SIZE(cs47l90_dmic5_pins) }, + { "pdmspk1", cs47l90_spk1_pins, ARRAY_SIZE(cs47l90_spk1_pins) }, +}; + +const struct madera_pin_chip cs47l90_pin_chip = { + .n_pins = CS47L90_NUM_GPIOS, + .pin_groups = cs47l90_pin_groups, + .n_pin_groups = ARRAY_SIZE(cs47l90_pin_groups), +}; diff --git a/drivers/pinctrl/cirrus/pinctrl-madera-core.c b/drivers/pinctrl/cirrus/pinctrl-madera-core.c new file mode 100644 index 000000000000..ece41fb2848f --- /dev/null +++ b/drivers/pinctrl/cirrus/pinctrl-madera-core.c @@ -0,0 +1,1076 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Pinctrl for Cirrus Logic Madera codecs + * + * Copyright (C) 2016-2018 Cirrus Logic + * + * 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; version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../pinctrl-utils.h" + +#include "pinctrl-madera.h" + +/* + * Use pin GPIO names for consistency + * NOTE: IDs are zero-indexed for coding convenience + */ +static const struct pinctrl_pin_desc madera_pins[] = { + PINCTRL_PIN(0, "gpio1"), + PINCTRL_PIN(1, "gpio2"), + PINCTRL_PIN(2, "gpio3"), + PINCTRL_PIN(3, "gpio4"), + PINCTRL_PIN(4, "gpio5"), + PINCTRL_PIN(5, "gpio6"), + PINCTRL_PIN(6, "gpio7"), + PINCTRL_PIN(7, "gpio8"), + PINCTRL_PIN(8, "gpio9"), + PINCTRL_PIN(9, "gpio10"), + PINCTRL_PIN(10, "gpio11"), + PINCTRL_PIN(11, "gpio12"), + PINCTRL_PIN(12, "gpio13"), + PINCTRL_PIN(13, "gpio14"), + PINCTRL_PIN(14, "gpio15"), + PINCTRL_PIN(15, "gpio16"), + PINCTRL_PIN(16, "gpio17"), + PINCTRL_PIN(17, "gpio18"), + PINCTRL_PIN(18, "gpio19"), + PINCTRL_PIN(19, "gpio20"), + PINCTRL_PIN(20, "gpio21"), + PINCTRL_PIN(21, "gpio22"), + PINCTRL_PIN(22, "gpio23"), + PINCTRL_PIN(23, "gpio24"), + PINCTRL_PIN(24, "gpio25"), + PINCTRL_PIN(25, "gpio26"), + PINCTRL_PIN(26, "gpio27"), + PINCTRL_PIN(27, "gpio28"), + PINCTRL_PIN(28, "gpio29"), + PINCTRL_PIN(29, "gpio30"), + PINCTRL_PIN(30, "gpio31"), + PINCTRL_PIN(31, "gpio32"), + PINCTRL_PIN(32, "gpio33"), + PINCTRL_PIN(33, "gpio34"), + PINCTRL_PIN(34, "gpio35"), + PINCTRL_PIN(35, "gpio36"), + PINCTRL_PIN(36, "gpio37"), + PINCTRL_PIN(37, "gpio38"), + PINCTRL_PIN(38, "gpio39"), + PINCTRL_PIN(39, "gpio40"), +}; + +/* + * All single-pin functions can be mapped to any GPIO, however pinmux applies + * functions to pin groups and only those groups declared as supporting that + * function. To make this work we must put each pin in its own dummy group so + * that the functions can be described as applying to all pins. + * Since these do not correspond to anything in the actual hardware - they are + * merely an adaptation to pinctrl's view of the world - we use the same name + * as the pin to avoid confusion when comparing with datasheet instructions + */ +static const char * const madera_pin_single_group_names[] = { + "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", + "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", + "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", + "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", + "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", +}; + +/* set of pin numbers for single-pin groups, zero-indexed */ +static const unsigned int madera_pin_single_group_pins[] = { + 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, +}; + +static const char * const madera_aif1_group_names[] = { "aif1" }; +static const char * const madera_aif2_group_names[] = { "aif2" }; +static const char * const madera_aif3_group_names[] = { "aif3" }; +static const char * const madera_aif4_group_names[] = { "aif4" }; +static const char * const madera_mif1_group_names[] = { "mif1" }; +static const char * const madera_mif2_group_names[] = { "mif2" }; +static const char * const madera_mif3_group_names[] = { "mif3" }; +static const char * const madera_dmic3_group_names[] = { "dmic3" }; +static const char * const madera_dmic4_group_names[] = { "dmic4" }; +static const char * const madera_dmic5_group_names[] = { "dmic5" }; +static const char * const madera_dmic6_group_names[] = { "dmic6" }; +static const char * const madera_spk1_group_names[] = { "pdmspk1" }; +static const char * const madera_spk2_group_names[] = { "pdmspk2" }; + +/* + * alt-functions always apply to a single pin group, other functions always + * apply to all pins + */ +static const struct { + const char *name; + const char * const *group_names; + u32 func; +} madera_mux_funcs[] = { + { + .name = "aif1", + .group_names = madera_aif1_group_names, + .func = 0x000 + }, + { + .name = "aif2", + .group_names = madera_aif2_group_names, + .func = 0x000 + }, + { + .name = "aif3", + .group_names = madera_aif3_group_names, + .func = 0x000 + }, + { + .name = "aif4", + .group_names = madera_aif4_group_names, + .func = 0x000 + }, + { + .name = "mif1", + .group_names = madera_mif1_group_names, + .func = 0x000 + }, + { + .name = "mif2", + .group_names = madera_mif2_group_names, + .func = 0x000 + }, + { + .name = "mif3", + .group_names = madera_mif3_group_names, + .func = 0x000 + }, + { + .name = "dmic3", + .group_names = madera_dmic3_group_names, + .func = 0x000 + }, + { + .name = "dmic4", + .group_names = madera_dmic4_group_names, + .func = 0x000 + }, + { + .name = "dmic5", + .group_names = madera_dmic5_group_names, + .func = 0x000 + }, + { + .name = "dmic6", + .group_names = madera_dmic6_group_names, + .func = 0x000 + }, + { + .name = "pdmspk1", + .group_names = madera_spk1_group_names, + .func = 0x000 + }, + { + .name = "pdmspk2", + .group_names = madera_spk2_group_names, + .func = 0x000 + }, + { + .name = "io", + .group_names = madera_pin_single_group_names, + .func = 0x001 + }, + { + .name = "dsp-gpio", + .group_names = madera_pin_single_group_names, + .func = 0x002 + }, + { + .name = "irq1", + .group_names = madera_pin_single_group_names, + .func = 0x003 + }, + { + .name = "irq2", + .group_names = madera_pin_single_group_names, + .func = 0x004 + }, + { + .name = "fll1-clk", + .group_names = madera_pin_single_group_names, + .func = 0x010 + }, + { + .name = "fll2-clk", + .group_names = madera_pin_single_group_names, + .func = 0x011 + }, + { + .name = "fll3-clk", + .group_names = madera_pin_single_group_names, + .func = 0x012 + }, + { + .name = "fllao-clk", + .group_names = madera_pin_single_group_names, + .func = 0x013 + }, + { + .name = "fll1-lock", + .group_names = madera_pin_single_group_names, + .func = 0x018 + }, + { + .name = "fll2-lock", + .group_names = madera_pin_single_group_names, + .func = 0x019 + }, + { + .name = "fll3-lock", + .group_names = madera_pin_single_group_names, + .func = 0x01a + }, + { + .name = "fllao-lock", + .group_names = madera_pin_single_group_names, + .func = 0x01b + }, + { + .name = "opclk", + .group_names = madera_pin_single_group_names, + .func = 0x040 + }, + { + .name = "opclk-async", + .group_names = madera_pin_single_group_names, + .func = 0x041 + }, + { + .name = "pwm1", + .group_names = madera_pin_single_group_names, + .func = 0x048 + }, + { + .name = "pwm2", + .group_names = madera_pin_single_group_names, + .func = 0x049 + }, + { + .name = "spdif", + .group_names = madera_pin_single_group_names, + .func = 0x04c + }, + { + .name = "asrc1-in1-lock", + .group_names = madera_pin_single_group_names, + .func = 0x088 + }, + { + .name = "asrc1-in2-lock", + .group_names = madera_pin_single_group_names, + .func = 0x089 + }, + { + .name = "asrc2-in1-lock", + .group_names = madera_pin_single_group_names, + .func = 0x08a + }, + { + .name = "asrc2-in2-lock", + .group_names = madera_pin_single_group_names, + .func = 0x08b + }, + { + .name = "spkl-short-circuit", + .group_names = madera_pin_single_group_names, + .func = 0x0b6 + }, + { + .name = "spkr-short-circuit", + .group_names = madera_pin_single_group_names, + .func = 0x0b7 + }, + { + .name = "spk-shutdown", + .group_names = madera_pin_single_group_names, + .func = 0x0e0 + }, + { + .name = "spk-overheat-shutdown", + .group_names = madera_pin_single_group_names, + .func = 0x0e1 + }, + { + .name = "spk-overheat-warn", + .group_names = madera_pin_single_group_names, + .func = 0x0e2 + }, + { + .name = "timer1-sts", + .group_names = madera_pin_single_group_names, + .func = 0x140 + }, + { + .name = "timer2-sts", + .group_names = madera_pin_single_group_names, + .func = 0x141 + }, + { + .name = "timer3-sts", + .group_names = madera_pin_single_group_names, + .func = 0x142 + }, + { + .name = "timer4-sts", + .group_names = madera_pin_single_group_names, + .func = 0x143 + }, + { + .name = "timer5-sts", + .group_names = madera_pin_single_group_names, + .func = 0x144 + }, + { + .name = "timer6-sts", + .group_names = madera_pin_single_group_names, + .func = 0x145 + }, + { + .name = "timer7-sts", + .group_names = madera_pin_single_group_names, + .func = 0x146 + }, + { + .name = "timer8-sts", + .group_names = madera_pin_single_group_names, + .func = 0x147 + }, + { + .name = "log1-fifo-ne", + .group_names = madera_pin_single_group_names, + .func = 0x150 + }, + { + .name = "log2-fifo-ne", + .group_names = madera_pin_single_group_names, + .func = 0x151 + }, + { + .name = "log3-fifo-ne", + .group_names = madera_pin_single_group_names, + .func = 0x152 + }, + { + .name = "log4-fifo-ne", + .group_names = madera_pin_single_group_names, + .func = 0x153 + }, + { + .name = "log5-fifo-ne", + .group_names = madera_pin_single_group_names, + .func = 0x154 + }, + { + .name = "log6-fifo-ne", + .group_names = madera_pin_single_group_names, + .func = 0x155 + }, + { + .name = "log7-fifo-ne", + .group_names = madera_pin_single_group_names, + .func = 0x156 + }, + { + .name = "log8-fifo-ne", + .group_names = madera_pin_single_group_names, + .func = 0x157 + }, +}; + +static u16 madera_pin_make_drv_str(struct madera_pin_private *priv, + unsigned int milliamps) +{ + switch (milliamps) { + case 4: + return 0; + case 8: + return 2 << MADERA_GP1_DRV_STR_SHIFT; + default: + break; + } + + dev_warn(priv->dev, "%u mA not a valid drive strength", milliamps); + + return 0; +} + +static unsigned int madera_pin_unmake_drv_str(struct madera_pin_private *priv, + u16 regval) +{ + regval = (regval & MADERA_GP1_DRV_STR_MASK) >> MADERA_GP1_DRV_STR_SHIFT; + + switch (regval) { + case 0: + return 4; + case 2: + return 8; + default: + return 0; + } +} + +static int madera_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct madera_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + + /* Number of alt function groups plus number of single-pin groups */ + return priv->chip->n_pin_groups + priv->chip->n_pins; +} + +static const char *madera_get_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + struct madera_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + + if (selector < priv->chip->n_pin_groups) + return priv->chip->pin_groups[selector].name; + + selector -= priv->chip->n_pin_groups; + return madera_pin_single_group_names[selector]; +} + +static int madera_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int selector, + const unsigned int **pins, + unsigned int *num_pins) +{ + struct madera_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + + if (selector < priv->chip->n_pin_groups) { + *pins = priv->chip->pin_groups[selector].pins; + *num_pins = priv->chip->pin_groups[selector].n_pins; + } else { + /* return the dummy group for a single pin */ + selector -= priv->chip->n_pin_groups; + *pins = &madera_pin_single_group_pins[selector]; + *num_pins = 1; + } + return 0; +} + +static void madera_pin_dbg_show_fn(struct madera_pin_private *priv, + struct seq_file *s, + unsigned int pin, unsigned int fn) +{ + const struct madera_pin_chip *chip = priv->chip; + int i, g_pin; + + if (fn != 0) { + for (i = 0; i < ARRAY_SIZE(madera_mux_funcs); ++i) { + if (madera_mux_funcs[i].func == fn) { + seq_printf(s, " FN=%s", + madera_mux_funcs[i].name); + return; + } + } + return; /* ignore unknown function values */ + } + + /* alt function */ + for (i = 0; i < chip->n_pin_groups; ++i) { + for (g_pin = 0; g_pin < chip->pin_groups[i].n_pins; ++g_pin) { + if (chip->pin_groups[i].pins[g_pin] == pin) { + seq_printf(s, " FN=%s", + chip->pin_groups[i].name); + return; + } + } + } +} + +static void __maybe_unused madera_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned int pin) +{ + struct madera_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + unsigned int conf[2]; + unsigned int reg = MADERA_GPIO1_CTRL_1 + (2 * pin); + unsigned int fn; + int ret; + + ret = regmap_read(priv->madera->regmap, reg, &conf[0]); + if (ret) + return; + + ret = regmap_read(priv->madera->regmap, reg + 1, &conf[1]); + if (ret) + return; + + seq_printf(s, "%04x:%04x", conf[0], conf[1]); + + fn = (conf[0] & MADERA_GP1_FN_MASK) >> MADERA_GP1_FN_SHIFT; + madera_pin_dbg_show_fn(priv, s, pin, fn); + + /* State of direction bit is only relevant if function==1 */ + if (fn == 1) { + if (conf[1] & MADERA_GP1_DIR_MASK) + seq_puts(s, " IN"); + else + seq_puts(s, " OUT"); + } + + if (conf[1] & MADERA_GP1_PU_MASK) + seq_puts(s, " PU"); + + if (conf[1] & MADERA_GP1_PD_MASK) + seq_puts(s, " PD"); + + if (conf[0] & MADERA_GP1_DB_MASK) + seq_puts(s, " DB"); + + if (conf[0] & MADERA_GP1_OP_CFG_MASK) + seq_puts(s, " OD"); + else + seq_puts(s, " CMOS"); + + seq_printf(s, " DRV=%umA", madera_pin_unmake_drv_str(priv, conf[1])); + + if (conf[0] & MADERA_GP1_IP_CFG_MASK) + seq_puts(s, "SCHMITT"); +} + + +static const struct pinctrl_ops madera_pin_group_ops = { + .get_groups_count = madera_get_groups_count, + .get_group_name = madera_get_group_name, + .get_group_pins = madera_get_group_pins, +#if IS_ENABLED(CONFIG_OF) + .dt_node_to_map = pinconf_generic_dt_node_to_map_all, + .dt_free_map = pinctrl_utils_free_map, +#endif +#if IS_ENABLED(CONFIG_DEBUG_FS) + .pin_dbg_show = madera_pin_dbg_show, +#endif +}; + +static int madera_mux_get_funcs_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(madera_mux_funcs); +} + +static const char *madera_mux_get_func_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + return madera_mux_funcs[selector].name; +} + +static int madera_mux_get_groups(struct pinctrl_dev *pctldev, + unsigned int selector, + const char * const **groups, + unsigned int * const num_groups) +{ + struct madera_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + + *groups = madera_mux_funcs[selector].group_names; + + if (madera_mux_funcs[selector].func == 0) { + /* alt func always maps to a single group */ + *num_groups = 1; + } else { + /* other funcs map to all available gpio pins */ + *num_groups = priv->chip->n_pins; + } + + return 0; +} + +static int madera_mux_set_mux(struct pinctrl_dev *pctldev, + unsigned int selector, + unsigned int group) +{ + struct madera_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + struct madera *madera = priv->madera; + const struct madera_pin_groups *pin_group = priv->chip->pin_groups; + unsigned int n_chip_groups = priv->chip->n_pin_groups; + const char *func_name = madera_mux_funcs[selector].name; + unsigned int reg; + int i, ret; + + dev_dbg(priv->dev, "%s selecting %u (%s) for group %u (%s)\n", + __func__, selector, func_name, group, + madera_get_group_name(pctldev, group)); + + if (madera_mux_funcs[selector].func == 0) { + /* alt func pin assignments are codec-specific */ + for (i = 0; i < n_chip_groups; ++i) { + if (strcmp(func_name, pin_group->name) == 0) + break; + + ++pin_group; + } + + if (i == n_chip_groups) + return -EINVAL; + + for (i = 0; i < pin_group->n_pins; ++i) { + reg = MADERA_GPIO1_CTRL_1 + (2 * pin_group->pins[i]); + + dev_dbg(priv->dev, "%s setting 0x%x func bits to 0\n", + __func__, reg); + + ret = regmap_update_bits(madera->regmap, reg, + MADERA_GP1_FN_MASK, 0); + if (ret) + break; + + } + } else { + /* + * for other funcs the group will be the gpio number and will + * be offset by the number of chip-specific functions at the + * start of the group list + */ + group -= n_chip_groups; + reg = MADERA_GPIO1_CTRL_1 + (2 * group); + + dev_dbg(priv->dev, "%s setting 0x%x func bits to 0x%x\n", + __func__, reg, madera_mux_funcs[selector].func); + + ret = regmap_update_bits(madera->regmap, + reg, + MADERA_GP1_FN_MASK, + madera_mux_funcs[selector].func); + } + + if (ret) + dev_err(priv->dev, "Failed to write to 0x%x (%d)\n", reg, ret); + + return ret; +} + +static int madera_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset, + bool input) +{ + struct madera_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + struct madera *madera = priv->madera; + unsigned int reg = MADERA_GPIO1_CTRL_2 + (2 * offset); + unsigned int val; + int ret; + + if (input) + val = MADERA_GP1_DIR; + else + val = 0; + + ret = regmap_update_bits(madera->regmap, reg, MADERA_GP1_DIR_MASK, val); + if (ret) + dev_err(priv->dev, "Failed to write to 0x%x (%d)\n", reg, ret); + + return ret; +} + +static int madera_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset) +{ + struct madera_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + struct madera *madera = priv->madera; + unsigned int reg = MADERA_GPIO1_CTRL_1 + (2 * offset); + int ret; + + /* put the pin into GPIO mode */ + ret = regmap_update_bits(madera->regmap, reg, MADERA_GP1_FN_MASK, 1); + if (ret) + dev_err(priv->dev, "Failed to write to 0x%x (%d)\n", reg, ret); + + return ret; +} + +static void madera_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset) +{ + struct madera_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + struct madera *madera = priv->madera; + unsigned int reg = MADERA_GPIO1_CTRL_1 + (2 * offset); + int ret; + + /* disable GPIO by setting to GPIO IN */ + madera_gpio_set_direction(pctldev, range, offset, true); + + ret = regmap_update_bits(madera->regmap, reg, MADERA_GP1_FN_MASK, 1); + if (ret) + dev_err(priv->dev, "Failed to write to 0x%x (%d)\n", reg, ret); +} + +static const struct pinmux_ops madera_pin_mux_ops = { + .get_functions_count = madera_mux_get_funcs_count, + .get_function_name = madera_mux_get_func_name, + .get_function_groups = madera_mux_get_groups, + .set_mux = madera_mux_set_mux, + .gpio_request_enable = madera_gpio_request_enable, + .gpio_disable_free = madera_gpio_disable_free, + .gpio_set_direction = madera_gpio_set_direction, + .strict = true, /* GPIO and other functions are exclusive */ +}; + +static int madera_pin_conf_get(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + struct madera_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + unsigned int param = pinconf_to_config_param(*config); + unsigned int result = 0; + unsigned int reg = MADERA_GPIO1_CTRL_1 + (2 * pin); + unsigned int conf[2]; + int ret; + + ret = regmap_read(priv->madera->regmap, reg, &conf[0]); + if (!ret) + ret = regmap_read(priv->madera->regmap, reg + 1, &conf[1]); + + if (ret) { + dev_err(priv->dev, "Failed to read GP%d conf (%d)\n", + pin + 1, ret); + return ret; + } + + switch (param) { + case PIN_CONFIG_BIAS_BUS_HOLD: + conf[1] &= MADERA_GP1_PU_MASK | MADERA_GP1_PD_MASK; + if (conf[1] == (MADERA_GP1_PU | MADERA_GP1_PD)) + result = 1; + break; + case PIN_CONFIG_BIAS_DISABLE: + conf[1] &= MADERA_GP1_PU_MASK | MADERA_GP1_PD_MASK; + if (!conf[1]) + result = 1; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + conf[1] &= MADERA_GP1_PU_MASK | MADERA_GP1_PD_MASK; + if (conf[1] == MADERA_GP1_PD_MASK) + result = 1; + break; + case PIN_CONFIG_BIAS_PULL_UP: + conf[1] &= MADERA_GP1_PU_MASK | MADERA_GP1_PD_MASK; + if (conf[1] == MADERA_GP1_PU_MASK) + result = 1; + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + if (conf[0] & MADERA_GP1_OP_CFG_MASK) + result = 1; + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + if (!(conf[0] & MADERA_GP1_OP_CFG_MASK)) + result = 1; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + result = madera_pin_unmake_drv_str(priv, conf[1]); + break; + case PIN_CONFIG_INPUT_DEBOUNCE: + if (conf[0] & MADERA_GP1_DB_MASK) + result = 1; + break; + case PIN_CONFIG_INPUT_ENABLE: + if (conf[0] & MADERA_GP1_DIR_MASK) + result = 1; + break; + case PIN_CONFIG_INPUT_SCHMITT: + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + if (conf[0] & MADERA_GP1_IP_CFG_MASK) + result = 1; + break; + case PIN_CONFIG_OUTPUT: + if ((conf[1] & MADERA_GP1_DIR_MASK) && + (conf[0] & MADERA_GP1_LVL_MASK)) + result = 1; + break; + default: + break; + } + + *config = pinconf_to_config_packed(param, result); + + return 0; +} + +static int madera_pin_conf_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int num_configs) +{ + struct madera_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + u16 conf[2] = {0, 0}; + u16 mask[2] = {0, 0}; + unsigned int reg = MADERA_GPIO1_CTRL_1 + (2 * pin); + unsigned int val; + int ret; + + while (num_configs) { + dev_dbg(priv->dev, "%s config 0x%lx\n", __func__, *configs); + + switch (pinconf_to_config_param(*configs)) { + case PIN_CONFIG_BIAS_BUS_HOLD: + mask[1] |= MADERA_GP1_PU_MASK | MADERA_GP1_PD_MASK; + conf[1] |= MADERA_GP1_PU | MADERA_GP1_PD; + break; + case PIN_CONFIG_BIAS_DISABLE: + mask[1] |= MADERA_GP1_PU_MASK | MADERA_GP1_PD_MASK; + conf[1] &= ~(MADERA_GP1_PU | MADERA_GP1_PD); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + mask[1] |= MADERA_GP1_PU_MASK | MADERA_GP1_PD_MASK; + conf[1] |= MADERA_GP1_PD; + conf[1] &= ~MADERA_GP1_PU; + break; + case PIN_CONFIG_BIAS_PULL_UP: + mask[1] |= MADERA_GP1_PU_MASK | MADERA_GP1_PD_MASK; + conf[1] |= MADERA_GP1_PU; + conf[1] &= ~MADERA_GP1_PD; + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + mask[0] |= MADERA_GP1_OP_CFG_MASK; + conf[0] |= MADERA_GP1_OP_CFG; + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + mask[0] |= MADERA_GP1_OP_CFG_MASK; + conf[0] &= ~MADERA_GP1_OP_CFG; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + val = pinconf_to_config_argument(*configs); + mask[1] |= MADERA_GP1_DRV_STR_MASK; + conf[1] &= ~MADERA_GP1_DRV_STR_MASK; + conf[1] |= madera_pin_make_drv_str(priv, val); + break; + case PIN_CONFIG_INPUT_DEBOUNCE: + mask[0] |= MADERA_GP1_DB_MASK; + + /* + * we can't configure debounce time per-pin so value + * is just a flag + */ + val = pinconf_to_config_argument(*configs); + if (val) + conf[0] |= MADERA_GP1_DB; + else + conf[0] &= ~MADERA_GP1_DB; + break; + case PIN_CONFIG_INPUT_ENABLE: + val = pinconf_to_config_argument(*configs); + mask[1] |= MADERA_GP1_DIR_MASK; + if (val) + conf[1] |= MADERA_GP1_DIR; + else + conf[1] &= ~MADERA_GP1_DIR; + break; + case PIN_CONFIG_INPUT_SCHMITT: + val = pinconf_to_config_argument(*configs); + mask[0] |= MADERA_GP1_IP_CFG; + if (val) + conf[0] |= MADERA_GP1_IP_CFG; + else + conf[0] &= ~MADERA_GP1_IP_CFG; + + mask[1] |= MADERA_GP1_DIR_MASK; + conf[1] |= MADERA_GP1_DIR; + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + mask[0] |= MADERA_GP1_IP_CFG; + conf[0] |= MADERA_GP1_IP_CFG; + mask[1] |= MADERA_GP1_DIR_MASK; + conf[1] |= MADERA_GP1_DIR; + break; + case PIN_CONFIG_OUTPUT: + val = pinconf_to_config_argument(*configs); + mask[0] |= MADERA_GP1_LVL_MASK; + if (val) + conf[0] |= MADERA_GP1_LVL; + else + conf[0] &= ~MADERA_GP1_LVL; + + mask[1] |= MADERA_GP1_DIR_MASK; + conf[1] &= ~MADERA_GP1_DIR; + break; + default: + break; + } + + ++configs; + --num_configs; + } + + dev_dbg(priv->dev, + "%s gpio%d 0x%x:0x%x 0x%x:0x%x\n", + __func__, pin + 1, reg, conf[0], reg + 1, conf[1]); + + ret = regmap_update_bits(priv->madera->regmap, reg, mask[0], conf[0]); + if (ret) + goto err; + + ++reg; + ret = regmap_update_bits(priv->madera->regmap, reg, mask[1], conf[1]); + if (ret) + goto err; + + return 0; + +err: + dev_err(priv->dev, + "Failed to write GPIO%d conf (%d) reg 0x%x\n", + pin + 1, ret, reg); + + return ret; +} + +static int madera_pin_conf_group_set(struct pinctrl_dev *pctldev, + unsigned int selector, + unsigned long *configs, + unsigned int num_configs) +{ + struct madera_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + const struct madera_pin_groups *pin_group; + unsigned int n_groups = priv->chip->n_pin_groups; + int i, ret; + + dev_dbg(priv->dev, "%s setting group %s\n", __func__, + madera_get_group_name(pctldev, selector)); + + if (selector >= n_groups) { + /* group is a single pin, convert to pin number and set */ + return madera_pin_conf_set(pctldev, + selector - n_groups, + configs, + num_configs); + } else { + pin_group = &priv->chip->pin_groups[selector]; + + for (i = 0; i < pin_group->n_pins; ++i) { + ret = madera_pin_conf_set(pctldev, + pin_group->pins[i], + configs, + num_configs); + if (ret) + return ret; + } + } + + return 0; +} + +static const struct pinconf_ops madera_pin_conf_ops = { + .pin_config_get = madera_pin_conf_get, + .pin_config_set = madera_pin_conf_set, + .pin_config_group_set = madera_pin_conf_group_set, + +}; + +static struct pinctrl_desc madera_pin_desc = { + .name = "madera-pinctrl", + .pins = madera_pins, + .pctlops = &madera_pin_group_ops, + .pmxops = &madera_pin_mux_ops, + .confops = &madera_pin_conf_ops, + .owner = THIS_MODULE, +}; + +static int madera_pin_probe(struct platform_device *pdev) +{ + struct madera *madera = dev_get_drvdata(pdev->dev.parent); + const struct madera_pdata *pdata = dev_get_platdata(madera->dev); + struct madera_pin_private *priv; + int ret; + + BUILD_BUG_ON(ARRAY_SIZE(madera_pin_single_group_names) != + ARRAY_SIZE(madera_pin_single_group_pins)); + + dev_dbg(&pdev->dev, "%s\n", __func__); + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + priv->madera = madera; + pdev->dev.of_node = madera->dev->of_node; + + switch (madera->type) { + case CS47L35: + if (IS_ENABLED(CONFIG_PINCTRL_CS47L35)) + priv->chip = &cs47l35_pin_chip; + break; + case CS47L85: + case WM1840: + if (IS_ENABLED(CONFIG_PINCTRL_CS47L85)) + priv->chip = &cs47l85_pin_chip; + break; + case CS47L90: + case CS47L91: + if (IS_ENABLED(CONFIG_PINCTRL_CS47L90)) + priv->chip = &cs47l90_pin_chip; + break; + default: + break; + } + + if (!priv->chip) + return -ENODEV; + + madera_pin_desc.npins = priv->chip->n_pins; + + ret = devm_pinctrl_register_and_init(&pdev->dev, + &madera_pin_desc, + priv, + &priv->pctl); + if (ret) { + dev_err(priv->dev, "Failed pinctrl register (%d)\n", ret); + return ret; + } + + /* if the configuration is provided through pdata, apply it */ + if (pdata) { + ret = pinctrl_register_mappings(pdata->gpio_configs, + pdata->n_gpio_configs); + if (ret) { + dev_err(priv->dev, + "Failed to register pdata mappings (%d)\n", + ret); + return ret; + } + } + + ret = pinctrl_enable(priv->pctl); + if (ret) { + dev_err(priv->dev, "Failed to enable pinctrl (%d)\n", ret); + return ret; + } + + dev_dbg(priv->dev, "pinctrl probed ok\n"); + + return 0; +} + +static struct platform_driver madera_pin_driver = { + .probe = madera_pin_probe, + .driver = { + .name = "madera-pinctrl", + }, +}; + +module_platform_driver(madera_pin_driver); + +MODULE_DESCRIPTION("Madera pinctrl driver"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/cirrus/pinctrl-madera.h b/drivers/pinctrl/cirrus/pinctrl-madera.h new file mode 100644 index 000000000000..8000f4f832a1 --- /dev/null +++ b/drivers/pinctrl/cirrus/pinctrl-madera.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Pinctrl for Cirrus Logic Madera codecs + * + * Copyright (C) 2016-2017 Cirrus Logic + * + * 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; version 2. + */ + +#ifndef PINCTRL_MADERA_H +#define PINCTRL_MADERA_H + +struct madera_pin_groups { + const char *name; + const unsigned int *pins; + unsigned int n_pins; +}; + +struct madera_pin_chip { + unsigned int n_pins; + + const struct madera_pin_groups *pin_groups; + unsigned int n_pin_groups; +}; + +struct madera_pin_private { + struct madera *madera; + + const struct madera_pin_chip *chip; /* chip-specific groups */ + + struct device *dev; + struct pinctrl_dev *pctl; +}; + +extern const struct madera_pin_chip cs47l35_pin_chip; +extern const struct madera_pin_chip cs47l85_pin_chip; +extern const struct madera_pin_chip cs47l90_pin_chip; + +#endif -- cgit v1.2.3 From fb7298e1669ee84aa76f3483c91f1de4814aac11 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 31 May 2018 11:39:42 +0000 Subject: pinctrl: mediatek: remove redundant return value check of platform_get_resource() Remove unneeded error handling on the result of a call to platform_get_resource() when the value is passed to devm_ioremap_resource(). Signed-off-by: Wei Yongjun Signed-off-by: Linus Walleij --- drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/pinctrl') diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c index b3799695d8db..16ff56f93501 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c @@ -1000,11 +1000,6 @@ static int mtk_eint_init(struct mtk_pinctrl *pctl, struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Unable to get eint resource\n"); - return -ENODEV; - } - pctl->eint->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(pctl->eint->base)) return PTR_ERR(pctl->eint->base); -- cgit v1.2.3 From bc3322bc166a2905bc91f774d7b22773dc7c063a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 7 Jun 2018 13:51:33 -0300 Subject: pinctrl: devicetree: Fix pctldev pointer overwrite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit b89405b6102f ("pinctrl: devicetree: Fix dt_to_map_one_config handling of hogs") causes the pinctrl hog pins to not get initialized on i.MX platforms leaving them with the IOMUX settings untouched. This causes several regressions on i.MX such as: - OV5640 camera driver can not be probed anymore on imx6qdl-sabresd because the camera clock pin is in a pinctrl_hog group and since its pinctrl initialization is skipped, the camera clock is kept in GPIO functionality instead of CLK_CKO function. - Audio stopped working on imx6qdl-wandboard and imx53-qsb for the same reason. Richard Fitzgerald explains the problem: "I see the bug. If the hog node isn't a 1st level child of the pinctrl parent node it will go around the for(;;) loop again but on the first pass I overwrite pctldev with the result of get_pinctrl_dev_from_of_node() so it doesn't point to the pinctrl driver any more." Fix the issue by stashing the original pctldev so it doesn't get overwritten. Fixes: b89405b6102f ("pinctrl: devicetree: Fix dt_to_map_one_config handling of hogs") Cc: Reported-by: Mika Penttilä Reported-by: Steve Longerbeam Suggested-by: Richard Fitzgerald Signed-off-by: Fabio Estevam Reviewed-by: Dong Aisheng Reviewed-by: Richard Fitzgerald Signed-off-by: Linus Walleij --- drivers/pinctrl/devicetree.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/pinctrl') diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index b601039d6c69..c4aa411f5935 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -101,10 +101,11 @@ struct pinctrl_dev *of_pinctrl_get(struct device_node *np) } static int dt_to_map_one_config(struct pinctrl *p, - struct pinctrl_dev *pctldev, + struct pinctrl_dev *hog_pctldev, const char *statename, struct device_node *np_config) { + struct pinctrl_dev *pctldev = NULL; struct device_node *np_pctldev; const struct pinctrl_ops *ops; int ret; @@ -123,8 +124,10 @@ static int dt_to_map_one_config(struct pinctrl *p, return -EPROBE_DEFER; } /* If we're creating a hog we can use the passed pctldev */ - if (pctldev && (np_pctldev == p->dev->of_node)) + if (hog_pctldev && (np_pctldev == p->dev->of_node)) { + pctldev = hog_pctldev; break; + } pctldev = get_pinctrl_dev_from_of_node(np_pctldev); if (pctldev) break; -- cgit v1.2.3 From 7f57871f39912978e95db920ddbbfb2304a4bfbf Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 6 Jun 2018 14:43:38 +0100 Subject: pinctrl: single: Add allocation failure checking of saved_vals Currently saved_vals is being allocated and there is no check for failed allocation (which is more likely than normal when using GFP_ATOMIC). Fix this by checking for a failed allocation and propagating this error return down the the caller chain. Detected by CoverityScan, CID#1469841 ("Dereference null return value") Fixes: 88a1dbdec682 ("pinctrl: pinctrl-single: Add functions to save and restore pinctrl context") Signed-off-by: Colin Ian King Reviewed-by: Johan Hovold Acked-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-single.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers/pinctrl') diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index b3153c095199..e5647dac0818 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -1590,8 +1590,11 @@ static int pcs_save_context(struct pcs_device *pcs) mux_bytes = pcs->width / BITS_PER_BYTE; - if (!pcs->saved_vals) + if (!pcs->saved_vals) { pcs->saved_vals = devm_kzalloc(pcs->dev, pcs->size, GFP_ATOMIC); + if (!pcs->saved_vals) + return -ENOMEM; + } switch (pcs->width) { case 64: @@ -1651,8 +1654,13 @@ static int pinctrl_single_suspend(struct platform_device *pdev, if (!pcs) return -EINVAL; - if (pcs->flags & PCS_CONTEXT_LOSS_OFF) - pcs_save_context(pcs); + if (pcs->flags & PCS_CONTEXT_LOSS_OFF) { + int ret; + + ret = pcs_save_context(pcs); + if (ret < 0) + return ret; + } return pinctrl_force_sleep(pcs->pctl); } -- cgit v1.2.3 From 71fd5d07b791b90587f695f048ad82e8d4c1c67e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 8 Jun 2018 12:05:47 +0200 Subject: pinctrl: actions: Fix uninitialized error in owl_pin_config_set() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With gcc 4.1.2: drivers/pinctrl/actions/pinctrl-owl.c: In function ‘owl_pin_config_set’: drivers/pinctrl/actions/pinctrl-owl.c:336: warning: ‘ret’ may be used uninitialized in this function Indeed, if num_configs is zero, the uninitialized value will be returned as an error code. Fix this by preinitializing it to zero. Fixes: 2242ddfbf4d699b5 ("pinctrl: actions: Add Actions S900 pinctrl driver") Signed-off-by: Geert Uytterhoeven Acked-by: Manivannan Sadhasivam Signed-off-by: Linus Walleij --- drivers/pinctrl/actions/pinctrl-owl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pinctrl') diff --git a/drivers/pinctrl/actions/pinctrl-owl.c b/drivers/pinctrl/actions/pinctrl-owl.c index 76243caa08c6..b5c880b50bb3 100644 --- a/drivers/pinctrl/actions/pinctrl-owl.c +++ b/drivers/pinctrl/actions/pinctrl-owl.c @@ -333,7 +333,7 @@ static int owl_pin_config_set(struct pinctrl_dev *pctrldev, unsigned long flags; unsigned int param; u32 reg, bit, width, arg; - int ret, i; + int ret = 0, i; info = &pctrl->soc->padinfo[pin]; -- cgit v1.2.3 From 5f591543a937310e48baf0ee23680be09cdedfb8 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Thu, 14 Jun 2018 16:55:49 +0800 Subject: pinctrl: mt7622: fix a kernel panic when pio don't work as EINT controller The function, external interrupt controller, is made as an optional to mt7622 pinctrl. But if we don't want pio behaves as an external interrupt controller, it would lead to hw->eint not be created properly and then will cause 'kernel NULL pointer' issue when gpiochip try to call .to_irq or .set_config. To fix it, check hw->eint before accessing the member. [ 1.339494] Unable to handle kernel NULL pointer dereference at virtual address 00000010 [ 1.347857] Mem abort info: [ 1.350742] ESR = 0x96000005 [ 1.353905] Exception class = DABT (current EL), IL = 32 bits [ 1.360024] SET = 0, FnV = 0 [ 1.363185] EA = 0, S1PTW = 0 [ 1.366431] Data abort info: [ 1.369405] ISV = 0, ISS = 0x00000005 [ 1.373363] CM = 0, WnR = 0 [ 1.376437] [0000000000000010] user address but active_mm is swapper [ 1.383005] Internal error: Oops: 96000005 [#1] PREEMPT SMP [ 1.388748] Modules linked in: [ 1.391897] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.16.0-rc1+ #344 [ 1.398625] Hardware name: MediaTek MT7622 RFB1 board (DT) [ 1.404279] pstate: 80000005 (Nzcv daif -PAN -UAO) [ 1.409221] pc : mtk_eint_find_irq+0x8/0x24 [ 1.413532] lr : mtk_gpio_to_irq+0x20/0x28 [ 1.417749] sp : ffffff800801baf0 [ 1.421161] x29: ffffff800801baf0 x28: ffffff8008792f40 [ 1.426637] x27: ffffff800886b000 x26: ffffff8008615620 [ 1.432113] x25: ffffffc00e4dbdc8 x24: ffffff80087b8000 [ 1.437589] x23: ffffffc00325a000 x22: ffffffc00325a010 [ 1.443066] x21: ffffffc0033dec18 x20: 00000000ffffffea [ 1.448542] x19: ffffffc00e4db800 x18: 0000000000000130 [ 1.454018] x17: 000000000000000e x16: 0000000000000007 [ 1.459494] x15: ffffff80085ee000 x14: 0000000000000001 [ 1.464970] x13: 0000000000000001 x12: 0000000000000010 [ 1.470446] x11: 0101010101010101 x10: 0000000000000880 [ 1.475922] x9 : ffffff800801b990 x8 : ffffffc0030688e0 [ 1.481399] x7 : ffffff80080c0660 x6 : ffffffc00e4dbbb0 [ 1.486875] x5 : 0000000000000000 x4 : 0000000000000000 [ 1.492351] x3 : ffffff80082a92f4 x2 : 00000000fffffffa [ 1.497826] x1 : 0000000000000051 x0 : 0000000000000000 [ 1.503305] Process swapper/0 (pid: 1, stack limit = 0x0000000054e053bd) [ 1.510210] Call trace: [ 1.512727] mtk_eint_find_irq+0x8/0x24 [ 1.516677] mtk_gpio_to_irq+0x20/0x28 [ 1.520539] gpiod_to_irq+0x48/0x60 [ 1.524135] mmc_gpiod_request_cd_irq+0x3c/0xc4 [ 1.528804] mmc_start_host+0x6c/0x8c [ 1.532575] mmc_add_host+0x58/0x7c [ 1.536168] msdc_drv_probe+0x4fc/0x67c [ 1.540121] platform_drv_probe+0x58/0xa4 [ 1.544251] driver_probe_device+0x204/0x44c [ 1.548649] __driver_attach+0x84/0xf8 [ 1.552512] bus_for_each_dev+0x68/0xa0 [ 1.556461] driver_attach+0x20/0x28 [ 1.560142] bus_add_driver+0xec/0x240 [ 1.564002] driver_register+0x98/0xe4 [ 1.567863] __platform_driver_register+0x48/0x50 [ 1.572711] mt_msdc_driver_init+0x18/0x20 [ 1.576932] do_one_initcall+0x98/0x130 [ 1.580886] kernel_init_freeable+0x13c/0x1d4 [ 1.585375] kernel_init+0x10/0xf8 [ 1.588879] ret_from_fork+0x10/0x18 [ 1.592564] Code: a8c67bfd d65f03c0 a9bf7bfd 910003fd (f9400800) [ 1.598849] ---[ end trace 4bbcb7bc30e98492 ]--- [ 1.603677] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b [ 1.603677] cc: Kevin Hilman Cc: stable@vger.kernel.org Fixes: e6dabd38d8e7 ("pinctrl: mediatek: add EINT support to MT7622 SoC") Signed-off-by: Sean Wang Signed-off-by: Linus Walleij --- drivers/pinctrl/mediatek/pinctrl-mt7622.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/pinctrl') diff --git a/drivers/pinctrl/mediatek/pinctrl-mt7622.c b/drivers/pinctrl/mediatek/pinctrl-mt7622.c index ad6da1184c9f..e3f1ab2290fc 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt7622.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt7622.c @@ -1459,6 +1459,9 @@ static int mtk_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) struct mtk_pinctrl *hw = gpiochip_get_data(chip); unsigned long eint_n; + if (!hw->eint) + return -ENOTSUPP; + eint_n = offset; return mtk_eint_find_irq(hw->eint, eint_n); @@ -1471,7 +1474,8 @@ static int mtk_gpio_set_config(struct gpio_chip *chip, unsigned int offset, unsigned long eint_n; u32 debounce; - if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) + if (!hw->eint || + pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) return -ENOTSUPP; debounce = pinconf_to_config_argument(config); -- cgit v1.2.3