You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
kernel_samsung_sm7125/drivers/pinctrl/pinctrl-tacna.c

1208 lines
30 KiB

/*
* Pinctrl for Cirrus Logic Tacna codecs
*
* Copyright 2017-2018 Cirrus Logic, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/mfd/tacna/core.h>
#include <linux/mfd/tacna/registers.h>
#include "pinctrl-utils.h"
/*
* Pins are named after their GPIO number
* NOTE: IDs are zero-indexed for coding convenience
*/
static const struct pinctrl_pin_desc tacna_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"),
};
/*
* 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 tacna_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",
};
/* set of pin numbers for single-pin groups */
static const unsigned int tacna_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,
};
static const char * const tacna_asp1_group_names[] = { "asp1" };
static const char * const tacna_asp2_group_names[] = { "asp2" };
static const char * const tacna_asp3_group_names[] = { "asp3" };
static const char * const tacna_asp4_group_names[] = { "asp4" };
static const char * const tacna_asp1ao_group_names[] = { "asp1ao" };
static const char * const tacna_dsd1_group_names[] = { "dsd1" };
static const char * const tacna_in1pdm_group_names[] = { "in1-pdm" };
static const char * const tacna_in2pdm_group_names[] = { "in2-pdm" };
static const char * const tacna_in3pdm_group_names[] = { "in3-pdm" };
static const char * const tacna_in4pdm_group_names[] = { "in4-pdm" };
static const char * const tacna_out5pdm_group_names[] = { "out5-pdm" };
static const char * const tacna_spi2_group_names[] = { "spi2" };
/*
* alt-functions always apply to only one group, other functions always
* apply to all pins
*/
static const struct {
const char *name;
const char * const *group_names;
u32 func;
} tacna_mux_funcs[] = {
{
.name = "asp1ao",
.group_names = tacna_asp1ao_group_names,
.func = 0x000
},
{
.name = "asp1",
.group_names = tacna_asp1_group_names,
.func = 0x000
},
{
.name = "asp2",
.group_names = tacna_asp2_group_names,
.func = 0x000
},
{
.name = "asp3",
.group_names = tacna_asp3_group_names,
.func = 0x000
},
{
.name = "asp4",
.group_names = tacna_asp4_group_names,
.func = 0x000
},
{
.name = "dsd1",
.group_names = tacna_dsd1_group_names,
.func = 0x000
},
{
.name = "in1-pdm",
.group_names = tacna_in1pdm_group_names,
.func = 0x000
},
{
.name = "in2-pdm",
.group_names = tacna_in2pdm_group_names,
.func = 0x000,
},
{
.name = "in3-pdm",
.group_names = tacna_in3pdm_group_names,
.func = 0x000
},
{
.name = "in4-pdm",
.group_names = tacna_in4pdm_group_names,
.func = 0x000,
},
{
.name = "out5-pdm",
.group_names = tacna_out5pdm_group_names,
.func = 0x000
},
{
.name = "spi2",
.group_names = tacna_spi2_group_names,
.func = 0x000
},
{
.name = "io",
.group_names = tacna_pin_single_group_names,
.func = 0x001
},
{
.name = "dsp-gpio",
.group_names = tacna_pin_single_group_names,
.func = 0x002
},
{
.name = "irq1",
.group_names = tacna_pin_single_group_names,
.func = 0x003
},
{
.name = "fll1-clk",
.group_names = tacna_pin_single_group_names,
.func = 0x010
},
{
.name = "fll2-clk",
.group_names = tacna_pin_single_group_names,
.func = 0x011
},
{
.name = "fll3-clk",
.group_names = tacna_pin_single_group_names,
.func = 0x012
},
{
.name = "fll1-lock",
.group_names = tacna_pin_single_group_names,
.func = 0x018
},
{
.name = "fll2-lock",
.group_names = tacna_pin_single_group_names,
.func = 0x01a
},
{
.name = "fll3-lock",
.group_names = tacna_pin_single_group_names,
.func = 0x01c
},
{
.name = "opclk",
.group_names = tacna_pin_single_group_names,
.func = 0x048
},
{
.name = "opclk-async",
.group_names = tacna_pin_single_group_names,
.func = 0x049
},
{
.name = "opclk-dsp",
.group_names = tacna_pin_single_group_names,
.func = 0x04a
},
{
.name = "uart",
.group_names = tacna_pin_single_group_names,
.func = 0x04c
},
{
.name = "pwm1-out",
.group_names = tacna_pin_single_group_names,
.func = 0x080
},
{
.name = "pwm2-out",
.group_names = tacna_pin_single_group_names,
.func = 0x081
},
{
.name = "input-path-signal-detect",
.group_names = tacna_pin_single_group_names,
.func = 0x08c
},
{
.name = "ultrasonic-in1-activity-detect",
.group_names = tacna_pin_single_group_names,
.func = 0x090
},
{
.name = "ultrasonic-in2-activity-detect",
.group_names = tacna_pin_single_group_names,
.func = 0x092
},
{
.name = "asrc1-in1-lock",
.group_names = tacna_pin_single_group_names,
.func = 0x098
},
{
.name = "asrc1-in2-lock",
.group_names = tacna_pin_single_group_names,
.func = 0x09a
},
{
.name = "dfc1-saturate",
.group_names = tacna_pin_single_group_names,
.func = 0x0fa
},
{
.name = "dma-ch0-programmable-transfer-complete",
.group_names = tacna_pin_single_group_names,
.func = 0x190
},
{
.name = "dma-ch1-programmable-transfer-complete",
.group_names = tacna_pin_single_group_names,
.func = 0x191
},
{
.name = "dma-ch2-programmable-transfer-complete",
.group_names = tacna_pin_single_group_names,
.func = 0x192
},
{
.name = "dma-ch3-programmable-transfer-complete",
.group_names = tacna_pin_single_group_names,
.func = 0x193
},
{
.name = "dma-ch4-programmable-transfer-complete",
.group_names = tacna_pin_single_group_names,
.func = 0x194
},
{
.name = "dma-ch5-programmable-transfer-complete",
.group_names = tacna_pin_single_group_names,
.func = 0x195
},
{
.name = "dma-ch6-programmable-transfer-complete",
.group_names = tacna_pin_single_group_names,
.func = 0x196
},
{
.name = "dma-ch7-programmable-transfer-complete",
.group_names = tacna_pin_single_group_names,
.func = 0x197
},
{
.name = "out1r-hp1-enable-disable-sequence",
.group_names = tacna_pin_single_group_names,
.func = 0x1f8
},
{
.name = "out1l-hp1-enable-disable-sequence",
.group_names = tacna_pin_single_group_names,
.func = 0x1fa
},
{
.name = "out1r-hp2-enable-disable-sequence",
.group_names = tacna_pin_single_group_names,
.func = 0x1fc
},
{
.name = "out1l-hp2-enable-disable-sequence",
.group_names = tacna_pin_single_group_names,
.func = 0x1fe
},
{
.name = "out2r-enable-disable-sequence",
.group_names = tacna_pin_single_group_names,
.func = 0x200
},
{
.name = "out2l-enable-disable-sequence",
.group_names = tacna_pin_single_group_names,
.func = 0x202
},
{
.name = "outh-enable-disable-sequence",
.group_names = tacna_pin_single_group_names,
.func = 0x20c
},
{
.name = "sample-rate-change-trigger-a",
.group_names = tacna_pin_single_group_names,
.func = 0x214
},
{
.name = "sample-rate-change-trigger-b",
.group_names = tacna_pin_single_group_names,
.func = 0x215
},
{
.name = "sample-rate-change-trigger-c",
.group_names = tacna_pin_single_group_names,
.func = 0x216
},
{
.name = "sample-rate-change-trigger-d",
.group_names = tacna_pin_single_group_names,
.func = 0x217
},
{
.name = "timer1-irq-ch1",
.group_names = tacna_pin_single_group_names,
.func = 0x230
},
{
.name = "timer1-irq-ch2",
.group_names = tacna_pin_single_group_names,
.func = 0x231
},
{
.name = "timer1-irq-ch3",
.group_names = tacna_pin_single_group_names,
.func = 0x232
},
{
.name = "timer1-irq-ch4",
.group_names = tacna_pin_single_group_names,
.func = 0x233
},
{
.name = "timer2-irq-ch1",
.group_names = tacna_pin_single_group_names,
.func = 0x234
},
{
.name = "timer2-irq-ch2",
.group_names = tacna_pin_single_group_names,
.func = 0x235
},
{
.name = "timer2-irq-ch3",
.group_names = tacna_pin_single_group_names,
.func = 0x236
},
{
.name = "timer2-irq-ch4",
.group_names = tacna_pin_single_group_names,
.func = 0x237
},
{
.name = "timer3-irq-ch1",
.group_names = tacna_pin_single_group_names,
.func = 0x238
},
{
.name = "timer3-irq-ch2",
.group_names = tacna_pin_single_group_names,
.func = 0x239
},
{
.name = "timer3-irq-ch3",
.group_names = tacna_pin_single_group_names,
.func = 0x23a
},
{
.name = "timer3-irq-ch4",
.group_names = tacna_pin_single_group_names,
.func = 0x23b
},
{
.name = "timer4-irq-ch1",
.group_names = tacna_pin_single_group_names,
.func = 0x23c
},
{
.name = "timer4-irq-ch2",
.group_names = tacna_pin_single_group_names,
.func = 0x23d
},
{
.name = "timer4-irq-ch3",
.group_names = tacna_pin_single_group_names,
.func = 0x23e
},
{
.name = "timer4-irq-ch4",
.group_names = tacna_pin_single_group_names,
.func = 0x23f
},
{
.name = "timer5-irq-ch1",
.group_names = tacna_pin_single_group_names,
.func = 0x240
},
{
.name = "timer5-irq-ch2",
.group_names = tacna_pin_single_group_names,
.func = 0x241
},
{
.name = "timer5-irq-ch3",
.group_names = tacna_pin_single_group_names,
.func = 0x242
},
{
.name = "timer5-irq-ch4",
.group_names = tacna_pin_single_group_names,
.func = 0x243
},
{
.name = "timer-1",
.group_names = tacna_pin_single_group_names,
.func = 0x250
},
{
.name = "timer-2",
.group_names = tacna_pin_single_group_names,
.func = 0x251
},
{
.name = "timer-3",
.group_names = tacna_pin_single_group_names,
.func = 0x252
},
{
.name = "timer-4",
.group_names = tacna_pin_single_group_names,
.func = 0x253
},
{
.name = "timer-5",
.group_names = tacna_pin_single_group_names,
.func = 0x254
},
};
struct tacna_pin_groups {
const char *name;
const unsigned int *pins;
unsigned int n_pins;
};
struct tacna_pin_chip {
unsigned int n_pins;
const struct tacna_pin_groups *pin_groups;
unsigned int n_pin_groups;
};
struct tacna_pin_private {
struct tacna *tacna;
const struct tacna_pin_chip *chip; /* chip-specific groups */
struct device *dev;
struct pinctrl_dev *pctl;
};
/*
* Chip-specific configs
* The alt func groups available differ between codecs. Since these 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
*/
#ifdef CONFIG_PINCTRL_CS47L96
/* Note - all 1 less than in datasheet because these are zero-indexed */
static const unsigned int cs47l96_asp2_pins[] = { 8, 9, 10, 11 };
static const unsigned int cs47l96_asp3_pins[] = { 12, 13, 14, 15 };
static const unsigned int cs47l96_asp1ao_pins[] = { 19, 20, 21, 22 };
static const unsigned int cs47l96_dsd1_pins[] = { 16, 17, 18 };
static const unsigned int cs47l96_in3pdm_pins[] = { 4, 5 };
static const unsigned int cs47l96_in4pdm_pins[] = { 6, 7 };
static const unsigned int cs47l96_out5pdm_pins[] = { 2, 3 };
static const struct tacna_pin_groups cs47l96_pin_groups[] = {
{ "asp2", cs47l96_asp2_pins, ARRAY_SIZE(cs47l96_asp2_pins) },
{ "asp3", cs47l96_asp3_pins, ARRAY_SIZE(cs47l96_asp3_pins) },
{ "asp1ao", cs47l96_asp1ao_pins, ARRAY_SIZE(cs47l96_asp1ao_pins) },
{ "dsd1", cs47l96_dsd1_pins, ARRAY_SIZE(cs47l96_dsd1_pins) },
{ "in3-pdm", cs47l96_in3pdm_pins, ARRAY_SIZE(cs47l96_in3pdm_pins) },
{ "in4-pdm", cs47l96_in4pdm_pins, ARRAY_SIZE(cs47l96_in4pdm_pins) },
{ "out5-pdm", cs47l96_out5pdm_pins, ARRAY_SIZE(cs47l96_out5pdm_pins) },
};
static const struct tacna_pin_chip cs47l96_pin_chip = {
.n_pins = CS47L96_NUM_GPIOS,
.pin_groups = cs47l96_pin_groups,
.n_pin_groups = ARRAY_SIZE(cs47l96_pin_groups),
};
#endif
#ifdef CONFIG_PINCTRL_CS48L32
/* Note - all 1 less than in datasheet because these are zero-indexed */
static const unsigned int cs48l32_asp1_pins[] = { 2, 3, 4, 5 };
static const unsigned int cs48l32_asp2_pins[] = { 6, 7, 8, 9 };
static const unsigned int cs48l32_spi2_pins[] = { 10, 11, 12, 13, 14, 15 };
static const struct tacna_pin_groups cs48l32_pin_groups[] = {
{ "asp1", cs48l32_asp1_pins, ARRAY_SIZE(cs48l32_asp1_pins) },
{ "asp2", cs48l32_asp2_pins, ARRAY_SIZE(cs48l32_asp2_pins) },
{ "spi2", cs48l32_spi2_pins, ARRAY_SIZE(cs48l32_spi2_pins) },
};
static const struct tacna_pin_chip cs48l32_pin_chip = {
.n_pins = CS48L32_NUM_GPIOS,
.pin_groups = cs48l32_pin_groups,
.n_pin_groups = ARRAY_SIZE(cs48l32_pin_groups),
};
#endif
#ifdef CONFIG_PINCTRL_CS48LX50
/* Note - all 1 less than in datasheet because these are zero-indexed */
static const unsigned int cs48lx50_asp1_pins[] = { 4, 5, 6, 7 };
static const unsigned int cs48lx50_asp2_pins[] = { 8, 9, 10, 11 };
static const unsigned int cs48lx50_asp3_pins[] = { 12, 13, 14, 15 };
static const unsigned int cs48lx50_asp4_pins[] = { 16, 17, 18, 19 };
static const unsigned int cs48lx50_in1pdm_pins[] = { 20, 21 };
static const unsigned int cs48lx50_in2pdm_pins[] = { 22, 23 };
static const unsigned int cs48lx50_in3pdm_pins[] = { 24, 25 };
static const unsigned int cs48lx50_in4pdm_pins[] = { 26, 27 };
static const struct tacna_pin_groups cs48lx50_pin_groups[] = {
{ "asp1", cs48lx50_asp1_pins, ARRAY_SIZE(cs48lx50_asp1_pins) },
{ "asp2", cs48lx50_asp2_pins, ARRAY_SIZE(cs48lx50_asp2_pins) },
{ "asp3", cs48lx50_asp3_pins, ARRAY_SIZE(cs48lx50_asp3_pins) },
{ "asp4", cs48lx50_asp4_pins, ARRAY_SIZE(cs48lx50_asp4_pins) },
{ "in1-pdm", cs48lx50_in1pdm_pins, ARRAY_SIZE(cs48lx50_in1pdm_pins) },
{ "in2-pdm", cs48lx50_in2pdm_pins, ARRAY_SIZE(cs48lx50_in2pdm_pins) },
{ "in3-pdm", cs48lx50_in3pdm_pins, ARRAY_SIZE(cs48lx50_in3pdm_pins) },
{ "in4-pdm", cs48lx50_in4pdm_pins, ARRAY_SIZE(cs48lx50_in4pdm_pins) },
};
static const struct tacna_pin_chip cs48lx50_pin_chip = {
.n_pins = CS48LX50_NUM_GPIOS,
.pin_groups = cs48lx50_pin_groups,
.n_pin_groups = ARRAY_SIZE(cs48lx50_pin_groups),
};
#endif
static unsigned int tacna_pin_make_drv_str(struct tacna_pin_private *priv,
unsigned int milliamps)
{
switch (priv->tacna->type) {
case CS47L96:
case CS47L97:
case CS48L31:
case CS48L32:
case CS48L33:
switch (milliamps) {
case 4:
return 0;
case 8:
return 1 << TACNA_GP1_DRV_STR_SHIFT;
default:
break;
}
break;
case CS48LX50:
switch (milliamps) {
case 2:
return 0;
case 4:
return 1 << TACNA_GP1_DRV_STR_SHIFT;
case 10:
return 2 << TACNA_GP1_DRV_STR_SHIFT;
case 12:
return 3 << TACNA_GP1_DRV_STR_SHIFT;
default:
break;
}
break;
default:
break;
}
dev_warn(priv->dev, "%u mA is not a valid drive strength\n", milliamps);
return 0;
}
static unsigned int tacna_pin_unmake_drv_str(struct tacna_pin_private *priv,
unsigned int regval)
{
regval = (regval & TACNA_GP1_DRV_STR_MASK) >> TACNA_GP1_DRV_STR_SHIFT;
switch (priv->tacna->type) {
case CS47L96:
case CS47L97:
case CS48L31:
case CS48L32:
case CS48L33:
switch (regval) {
case 0:
return 4;
case 1:
return 8;
default:
break;
}
break;
case CS48LX50:
switch (regval) {
case 0:
return 2;
case 1:
return 4;
case 2:
return 10;
case 3:
return 12;
default:
break;
}
break;
default:
break;
}
return 0;
}
static int tacna_get_groups_count(struct pinctrl_dev *pctldev)
{
struct tacna_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 *tacna_get_group_name(struct pinctrl_dev *pctldev,
unsigned int selector)
{
struct tacna_pin_private *priv = pinctrl_dev_get_drvdata(pctldev);
if (selector < priv->chip->n_pin_groups) {
return priv->chip->pin_groups[selector].name;
} else {
selector -= priv->chip->n_pin_groups;
return tacna_pin_single_group_names[selector];
}
}
static int tacna_get_group_pins(struct pinctrl_dev *pctldev,
unsigned int selector,
const unsigned int **pins,
unsigned int *num_pins)
{
struct tacna_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 = &tacna_pin_single_group_pins[selector];
*num_pins = 1;
}
return 0;
}
static void tacna_pin_dbg_show_fn(struct tacna_pin_private *priv,
struct seq_file *s,
unsigned int pin, unsigned int fn)
{
const struct tacna_pin_chip *chip = priv->chip;
int i, g_pin;
if (fn != 0) {
for (i = 0; i < ARRAY_SIZE(tacna_mux_funcs); ++i) {
if (tacna_mux_funcs[i].func == fn) {
seq_printf(s, " FN=%s",
tacna_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 tacna_pin_dbg_show(struct pinctrl_dev *pctldev,
struct seq_file *s, unsigned int pin)
{
struct tacna_pin_private *priv = pinctrl_dev_get_drvdata(pctldev);
unsigned int reg = TACNA_GPIO1_CTRL1 + (4 * pin);
unsigned int conf, fn;
int ret;
ret = regmap_read(priv->tacna->regmap, reg, &conf);
if (ret)
return;
seq_printf(s, "%08x", conf);
fn = (conf & TACNA_GP1_FN_MASK) >> TACNA_GP1_FN_SHIFT;
tacna_pin_dbg_show_fn(priv, s, pin, fn);
/* State of direction bit is only relevant if function==1 */
if (fn == 1) {
if (conf & TACNA_GP1_DIR_MASK)
seq_puts(s, " IN");
else
seq_puts(s, " OUT");
}
if (conf & TACNA_GP1_PU_MASK)
seq_puts(s, " PU");
if (conf & TACNA_GP1_PD_MASK)
seq_puts(s, " PD");
if (conf & TACNA_GP1_DB_MASK)
seq_puts(s, " DB");
if (conf & TACNA_GP1_OP_CFG_MASK)
seq_puts(s, " OD");
else
seq_puts(s, " CMOS");
seq_printf(s, " DRV=%umA", tacna_pin_unmake_drv_str(priv, conf));
}
static const struct pinctrl_ops tacna_pin_group_ops = {
.get_groups_count = &tacna_get_groups_count,
.get_group_name = &tacna_get_group_name,
.get_group_pins = &tacna_get_group_pins,
.pin_dbg_show = &tacna_pin_dbg_show,
.dt_node_to_map = &pinconf_generic_dt_node_to_map_all,
.dt_free_map = &pinctrl_utils_free_map,
};
static int tacna_mux_get_funcs_count(struct pinctrl_dev *pctldev)
{
return ARRAY_SIZE(tacna_mux_funcs);
}
static const char *tacna_mux_get_func_name(struct pinctrl_dev *pctldev,
unsigned int selector)
{
return tacna_mux_funcs[selector].name;
}
static int tacna_mux_get_groups(struct pinctrl_dev *pctldev,
unsigned int selector,
const char * const **groups,
unsigned int * const num_groups)
{
struct tacna_pin_private *priv = pinctrl_dev_get_drvdata(pctldev);
*groups = tacna_mux_funcs[selector].group_names;
if (tacna_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 tacna_mux_set_mux(struct pinctrl_dev *pctldev, unsigned int selector,
unsigned int group)
{
struct tacna_pin_private *priv = pinctrl_dev_get_drvdata(pctldev);
struct tacna *tacna = priv->tacna;
const struct tacna_pin_groups *pin_group = priv->chip->pin_groups;
unsigned int n_chip_groups = priv->chip->n_pin_groups;
const char *func_name = tacna_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,
tacna_get_group_name(pctldev, group));
if (tacna_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 = TACNA_GPIO1_CTRL1 + (4 * pin_group->pins[i]);
dev_dbg(priv->dev, "%s setting 0x%x func bits to 0\n",
__func__, reg);
ret = regmap_update_bits(tacna->regmap, reg,
TACNA_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 = TACNA_GPIO1_CTRL1 + (4 * group);
dev_dbg(priv->dev, "%s setting 0x%x func bits to 0x%x\n",
__func__, reg, tacna_mux_funcs[selector].func);
ret = regmap_update_bits(tacna->regmap,
reg,
TACNA_GP1_FN_MASK,
tacna_mux_funcs[selector].func);
}
if (ret)
dev_err(priv->dev, "Failed to write to 0x%x (%d)\n",
reg, ret);
return ret;
}
static const struct pinmux_ops tacna_pin_mux_ops = {
.get_functions_count = &tacna_mux_get_funcs_count,
.get_function_name = &tacna_mux_get_func_name,
.get_function_groups = &tacna_mux_get_groups,
.set_mux = &tacna_mux_set_mux,
};
static int tacna_pin_conf_get(struct pinctrl_dev *pctldev, unsigned int pin,
unsigned long *config)
{
struct tacna_pin_private *priv = pinctrl_dev_get_drvdata(pctldev);
unsigned int param = pinconf_to_config_param(*config);
unsigned int result = 0;
unsigned int conf;
int ret;
ret = regmap_read(priv->tacna->regmap, TACNA_GPIO1_CTRL1 + (4 * pin),
&conf);
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 &= TACNA_GP1_PU_MASK | TACNA_GP1_PD_MASK;
if (conf == (TACNA_GP1_PU | TACNA_GP1_PD))
result = 1;
break;
case PIN_CONFIG_BIAS_DISABLE:
conf &= TACNA_GP1_PU_MASK | TACNA_GP1_PD_MASK;
if (!conf)
result = 1;
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
conf &= TACNA_GP1_PU_MASK | TACNA_GP1_PD_MASK;
if (conf == TACNA_GP1_PD_MASK)
result = 1;
break;
case PIN_CONFIG_BIAS_PULL_UP:
conf &= TACNA_GP1_PU_MASK | TACNA_GP1_PD_MASK;
if (conf == TACNA_GP1_PU_MASK)
result = 1;
break;
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
if (conf & TACNA_GP1_OP_CFG_MASK)
result = 1;
break;
case PIN_CONFIG_DRIVE_PUSH_PULL:
if (!(conf & TACNA_GP1_OP_CFG_MASK))
result = 1;
break;
case PIN_CONFIG_DRIVE_STRENGTH:
result = tacna_pin_unmake_drv_str(priv, conf);
break;
case PIN_CONFIG_INPUT_DEBOUNCE:
dev_dbg(priv->dev,
"Input debounce time not supported.");
break;
case PIN_CONFIG_INPUT_ENABLE:
if (conf & TACNA_GP1_DIR_MASK)
result = 1;
break;
case PIN_CONFIG_OUTPUT:
if ((conf & TACNA_GP1_DIR_MASK) &&
(conf & TACNA_GP1_LVL_MASK))
result = 1;
break;
default:
return -ENOTSUPP;
}
*config = pinconf_to_config_packed(param, result);
return 0;
}
static int tacna_pin_conf_set(struct pinctrl_dev *pctldev, unsigned int pin,
unsigned long *configs, unsigned int num_configs)
{
struct tacna_pin_private *priv = pinctrl_dev_get_drvdata(pctldev);
unsigned int conf = 0;
unsigned int mask = 0;
unsigned int reg = TACNA_GPIO1_CTRL1 + (4 * 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 |= TACNA_GP1_PU_MASK | TACNA_GP1_PD_MASK;
conf |= TACNA_GP1_PU | TACNA_GP1_PD;
break;
case PIN_CONFIG_BIAS_DISABLE:
mask |= TACNA_GP1_PU_MASK | TACNA_GP1_PD_MASK;
conf &= ~(TACNA_GP1_PU | TACNA_GP1_PD);
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
mask |= TACNA_GP1_PU_MASK | TACNA_GP1_PD_MASK;
conf |= TACNA_GP1_PD;
conf &= ~TACNA_GP1_PU;
break;
case PIN_CONFIG_BIAS_PULL_UP:
mask |= TACNA_GP1_PU_MASK | TACNA_GP1_PD_MASK;
conf |= TACNA_GP1_PU;
conf &= ~TACNA_GP1_PD;
break;
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
mask |= TACNA_GP1_OP_CFG_MASK;
conf |= TACNA_GP1_OP_CFG;
break;
case PIN_CONFIG_DRIVE_PUSH_PULL:
mask |= TACNA_GP1_OP_CFG_MASK;
conf &= ~TACNA_GP1_OP_CFG;
break;
case PIN_CONFIG_DRIVE_STRENGTH:
val = pinconf_to_config_argument(*configs);
mask |= TACNA_GP1_DRV_STR_MASK;
conf &= ~TACNA_GP1_DRV_STR_MASK;
conf |= tacna_pin_make_drv_str(priv, val);
break;
case PIN_CONFIG_INPUT_DEBOUNCE:
dev_dbg(priv->dev,
"Input debounce time not supported.");
break;
case PIN_CONFIG_INPUT_ENABLE:
val = pinconf_to_config_argument(*configs);
mask |= TACNA_GP1_DIR_MASK;
if (val)
conf |= TACNA_GP1_DIR;
else
conf &= ~TACNA_GP1_DIR;
break;
case PIN_CONFIG_OUTPUT:
val = pinconf_to_config_argument(*configs);
mask |= TACNA_GP1_LVL_MASK;
if (val)
conf |= TACNA_GP1_LVL;
else
conf &= ~TACNA_GP1_LVL;
mask |= TACNA_GP1_DIR_MASK;
conf &= ~TACNA_GP1_DIR;
break;
default:
return -ENOTSUPP;
}
++configs;
--num_configs;
}
dev_dbg(priv->dev, "%s gpio%d 0x%x:0x%x\n",
__func__, pin + 1, reg, conf);
ret = regmap_update_bits(priv->tacna->regmap, reg, mask, conf);
if (ret)
dev_err(priv->dev,
"Failed to write GPIO%d conf (%d) reg 0x%x\n",
pin + 1, ret, reg);
return ret;
}
static int tacna_pin_conf_group_set(struct pinctrl_dev *pctldev,
unsigned int selector,
unsigned long *configs,
unsigned int num_configs)
{
struct tacna_pin_private *priv = pinctrl_dev_get_drvdata(pctldev);
const struct tacna_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__,
tacna_get_group_name(pctldev, selector));
if (selector >= n_groups) {
/* group is a single pin, convert to pin number and set */
return tacna_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 = tacna_pin_conf_set(pctldev,
pin_group->pins[i],
configs,
num_configs);
if (ret)
return ret;
}
}
return 0;
}
static const struct pinconf_ops tacna_pin_conf_ops = {
.is_generic = true,
.pin_config_get = &tacna_pin_conf_get,
.pin_config_set = &tacna_pin_conf_set,
.pin_config_group_set = &tacna_pin_conf_group_set,
};
static struct pinctrl_desc tacna_pin_desc = {
.name = "tacna-pinctrl",
.pins = tacna_pins,
.pctlops = &tacna_pin_group_ops,
.pmxops = &tacna_pin_mux_ops,
.confops = &tacna_pin_conf_ops,
.owner = THIS_MODULE,
};
static int tacna_pin_probe(struct platform_device *pdev)
{
struct tacna *tacna = dev_get_drvdata(pdev->dev.parent);
const struct tacna_pdata *pdata = dev_get_platdata(tacna->dev);
struct tacna_pin_private *priv;
int ret;
BUILD_BUG_ON(ARRAY_SIZE(tacna_pin_single_group_names) !=
ARRAY_SIZE(tacna_pin_single_group_pins));
dev_dbg(&pdev->dev, "%s\n", __func__);
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
priv->dev = &pdev->dev;
priv->tacna = tacna;
switch (tacna->type) {
case CS47L96:
case CS47L97:
#ifdef CONFIG_PINCTRL_CS47L96
priv->chip = &cs47l96_pin_chip;
#endif
break;
case CS48L31:
case CS48L32:
case CS48L33:
#ifdef CONFIG_PINCTRL_CS48L32
priv->chip = &cs48l32_pin_chip;
#endif
break;
case CS48LX50:
#ifdef CONFIG_PINCTRL_CS48LX50
priv->chip = &cs48lx50_pin_chip;
#endif
break;
default:
break;
}
if (!priv->chip)
return -ENODEV;
tacna_pin_desc.npins = priv->chip->n_pins;
priv->pctl = pinctrl_register(&tacna_pin_desc, &pdev->dev, priv);
if (IS_ERR(priv->pctl)) {
ret = PTR_ERR(priv->pctl);
dev_err(priv->dev, "Failed pinctrl register (%d)\n", ret);
return ret;
}
if (pdata && pdata->gpio_configs) {
ret = pinctrl_register_mappings(pdata->gpio_configs,
pdata->n_gpio_configs);
dev_err(priv->dev, "Failed to register pdata mappings (%d)\n",
ret);
return ret;
}
dev_dbg(priv->dev, "pinctrl registered\n");
return 0;
}
static int tacna_pin_remove(struct platform_device *pdev)
{
struct tacna_pin_private *priv = platform_get_drvdata(pdev);
pinctrl_unregister(priv->pctl);
return 0;
}
static struct platform_driver tacna_pin_driver = {
.probe = &tacna_pin_probe,
.remove = &tacna_pin_remove,
.driver = {
.name = "tacna-pinctrl",
.suppress_bind_attrs = true,
},
};
module_platform_driver(tacna_pin_driver);
MODULE_DESCRIPTION("Tacna pinctrl driver");
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.wolfsonmicro.com>");
MODULE_AUTHOR("Piotr Stankiewicz <piotrs@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL v2");