Skip to content

Commit

Permalink
pinctrl/nomadik: implement pin multiplexing
Browse files Browse the repository at this point in the history
Implements basic pinmux for the Nomadik pin controller.

The plan is to split the existing singular pin config interface
nmk_config_pin(), nmk_config_pins(), that will configure muxing
and other settings at the same time, into two interfaces
by splitting the code in pinmux and pinctrl and eventually
deleting the old interface and its helper functions when all
users are gone.

nmk_gpio_set_mode() and nmk_gpio_get_mode() are two older
interfaces for just configuring muxing/altfunctions that
will also be replaced in the end.

We take some extra care to handle the glitch-avoidance here,
but it is simpler now since there is only one altsetting per
pingroup so we know immediately if we need to avoid altfunc
C glitches for a certain group.

As part of the makeover implement the .request() and .free()
calls on the GPIO chips and have them call back into the
pinctrl layer to reserve GPIOs.

ChangeLog v1->v2:
- Rebased on pinctrl-mergebase-20120418 so we get the latest
  driver infrastructure where function count is done by a fixed
  value and we can drop a few range checks since this is now
  handled by the core.
- Include a GPIO muxing hunk erroneously part of the pin config
  patch.

Acked-by: Stephen Warren <swarren@wwwdotorg.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
  • Loading branch information
Linus Walleij committed May 11, 2012
1 parent 24cbdd7 commit dbfe8ca
Show file tree
Hide file tree
Showing 4 changed files with 353 additions and 2 deletions.
1 change: 1 addition & 0 deletions drivers/pinctrl/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ config PINCTRL_MMP2
config PINCTRL_NOMADIK
bool "Nomadik pin controller driver"
depends on ARCH_U8500
select PINMUX

config PINCTRL_DB8500
bool "DB8500 pin controller driver"
Expand Down
130 changes: 130 additions & 0 deletions drivers/pinctrl/pinctrl-nomadik-db8500.c
Original file line number Diff line number Diff line change
Expand Up @@ -711,11 +711,141 @@ static const struct nmk_pingroup nmk_db8500_groups[] = {
DB8500_PIN_GROUP(spi2_oc1_1, NMK_GPIO_ALT_C),
};

/* We use this macro to define the groups applicable to a function */
#define DB8500_FUNC_GROUPS(a, b...) \
static const char * const a##_groups[] = { b };

DB8500_FUNC_GROUPS(u0, "u0_a_1", "u0_c_1");
DB8500_FUNC_GROUPS(u1, "u1rxtx_a_1", "u1ctsrts_a_1");
/*
* UART2 can be muxed out with just RX/TX in four places, CTS+RTS is however
* only available on two pins in alternative function C
*/
DB8500_FUNC_GROUPS(u2, "u2rxtx_b_1", "u2rxtx_c_1", "u2ctsrts_c_1",
"u2rxtx_c_2", "u2rxtx_c_3");
DB8500_FUNC_GROUPS(ipi2c, "ipi2c_a_1", "ipi2c_a_2");
/*
* MSP0 can only be on a certain set of pins, but the TX/RX pins can be
* switched around by selecting the altfunction A or B. The SCK pin is
* only available on the altfunction B.
*/
DB8500_FUNC_GROUPS(msp0, "msp0txrx_a_1", "msp0tfstck_a_1", "msp0rfstck_a_1",
"msp0txrx_b_1", "msp0sck_b_1");
DB8500_FUNC_GROUPS(mc0, "mc0_a_1");
/* MSP0 can swap RX/TX like MSP0 but has no SCK pin available */
DB8500_FUNC_GROUPS(msp1, "msp1txrx_a_1", "msp1_a_1", "msp1txrx_b_1");
DB8500_FUNC_GROUPS(lcdb, "lcdb_a_1");
DB8500_FUNC_GROUPS(lcd, "lcdvsi0_a_1", "lcdvsi1_a_1", "lcd_d0_d7_a_1",
"lcd_d8_d11_a_1", "lcd_d12_d23_a_1", "lcd_b_1");
DB8500_FUNC_GROUPS(kp, "kp_a_1", "kp_b_1", "kp_c_1", "kp_oc1_1");
DB8500_FUNC_GROUPS(mc2, "mc2_a_1", "mc2rstn_c_1");
DB8500_FUNC_GROUPS(ssp1, "ssp1_a_1");
DB8500_FUNC_GROUPS(ssp0, "ssp0_a_1");
DB8500_FUNC_GROUPS(i2c0, "i2c0_a_1");
/* The image processor has 8 GPIO pins that can be muxed out */
DB8500_FUNC_GROUPS(ipgpio, "ipgpio0_a_1", "ipgpio1_a_1", "ipgpio7_b_1",
"ipgpio2_b_1", "ipgpio3_b_1", "ipgpio6_c_1", "ipgpio0_c_1",
"ipgpio1_c_1", "ipgpio3_c_1", "ipgpio2_c_1", "ipgpio4_c_1",
"ipgpio5_c_1", "ipgpio6_c_2", "ipgpio7_c_1", "ipgpio2_c_2",
"ipgpio3_c_2", "ipgpio4_c_2", "ipgpio5_c_2");
/* MSP2 can not invert the RX/TX pins but has the optional SCK pin */
DB8500_FUNC_GROUPS(msp2, "msp2sck_a_1", "msp2_a_1");
DB8500_FUNC_GROUPS(mc4, "mc4_a_1", "mc4rstn_c_1");
DB8500_FUNC_GROUPS(mc1, "mc1_a_1", "mc1dir_a_1");
DB8500_FUNC_GROUPS(hsi, "hsir1_a_1", "hsit1_a_1");
DB8500_FUNC_GROUPS(clkout, "clkout_a_1", "clkout_a_2", "clkout_c_1");
DB8500_FUNC_GROUPS(usb, "usb_a_1");
DB8500_FUNC_GROUPS(trig, "trig_b_1");
DB8500_FUNC_GROUPS(i2c4, "i2c4_b_1");
DB8500_FUNC_GROUPS(i2c1, "i2c1_b_1", "i2c1_b_2");
DB8500_FUNC_GROUPS(i2c2, "i2c2_b_1", "i2c2_b_2");
/*
* The modem UART can output its RX and TX pins in some different places,
* so select one of each.
*/
DB8500_FUNC_GROUPS(uartmod, "uartmodtx_b_1", "uartmodrx_b_1", "uartmodrx_b_2",
"uartmodrx_c_1", "uartmod_tx_c_1");
DB8500_FUNC_GROUPS(stmmod, "stmmod_b_1", "stmmod_c_1");
DB8500_FUNC_GROUPS(spi3, "spi3_b_1");
/* Select between CS0 on alt B or PS1 on alt C */
DB8500_FUNC_GROUPS(sm, "sm_b_1", "smcs0_b_1", "smcleale_c_1", "smps1_c_1");
DB8500_FUNC_GROUPS(lcda, "lcdaclk_b_1", "lcda_b_1");
DB8500_FUNC_GROUPS(ddrtrig, "ddrtrig_b_1");
DB8500_FUNC_GROUPS(pwl, "pwl_b_1", "pwl_b_2", "pwl_b_3", "pwl_b_4");
DB8500_FUNC_GROUPS(spi1, "spi1_b_1");
DB8500_FUNC_GROUPS(mc3, "mc3_b_1");
DB8500_FUNC_GROUPS(ipjtag, "ipjtag_c_1");
DB8500_FUNC_GROUPS(slim0, "slim0_c_1");
DB8500_FUNC_GROUPS(ms, "ms_c_1");
DB8500_FUNC_GROUPS(iptrigout, "iptrigout_c_1");
DB8500_FUNC_GROUPS(stmape, "stmape_c_1", "stmape_c_2");
DB8500_FUNC_GROUPS(mc5, "mc5_c_1");
DB8500_FUNC_GROUPS(usbsim, "usbsim_c_1", "usbsim_c_2");
DB8500_FUNC_GROUPS(i2c3, "i2c3_c_1", "i2c3_c_2");
DB8500_FUNC_GROUPS(spi0, "spi0_c_1");
DB8500_FUNC_GROUPS(spi2, "spi2_oc1_1");

#define FUNCTION(fname) \
{ \
.name = #fname, \
.groups = fname##_groups, \
.ngroups = ARRAY_SIZE(fname##_groups), \
}

static const struct nmk_function nmk_db8500_functions[] = {
FUNCTION(u0),
FUNCTION(u1),
FUNCTION(u2),
FUNCTION(ipi2c),
FUNCTION(msp0),
FUNCTION(mc0),
FUNCTION(msp1),
FUNCTION(lcdb),
FUNCTION(lcd),
FUNCTION(kp),
FUNCTION(mc2),
FUNCTION(ssp1),
FUNCTION(ssp0),
FUNCTION(i2c0),
FUNCTION(ipgpio),
FUNCTION(msp2),
FUNCTION(mc4),
FUNCTION(mc1),
FUNCTION(hsi),
FUNCTION(clkout),
FUNCTION(usb),
FUNCTION(trig),
FUNCTION(i2c4),
FUNCTION(i2c1),
FUNCTION(i2c2),
FUNCTION(uartmod),
FUNCTION(stmmod),
FUNCTION(spi3),
FUNCTION(sm),
FUNCTION(lcda),
FUNCTION(ddrtrig),
FUNCTION(pwl),
FUNCTION(spi1),
FUNCTION(mc3),
FUNCTION(ipjtag),
FUNCTION(slim0),
FUNCTION(ms),
FUNCTION(iptrigout),
FUNCTION(stmape),
FUNCTION(mc5),
FUNCTION(usbsim),
FUNCTION(i2c3),
FUNCTION(spi0),
FUNCTION(spi2),
};

static const struct nmk_pinctrl_soc_data nmk_db8500_soc = {
.gpio_ranges = nmk_db8500_ranges,
.gpio_num_ranges = ARRAY_SIZE(nmk_db8500_ranges),
.pins = nmk_db8500_pins,
.npins = ARRAY_SIZE(nmk_db8500_pins),
.functions = nmk_db8500_functions,
.nfunctions = ARRAY_SIZE(nmk_db8500_functions),
.groups = nmk_db8500_groups,
.ngroups = ARRAY_SIZE(nmk_db8500_groups),
};
Expand Down
204 changes: 204 additions & 0 deletions drivers/pinctrl/pinctrl-nomadik.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
#include <linux/irqdomain.h>
#include <linux/slab.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
/* Since we request GPIOs from ourself */
#include <linux/pinctrl/consumer.h>

#include <asm/mach/irq.h>

Expand Down Expand Up @@ -873,6 +876,25 @@ static int nmk_gpio_init_irq(struct nmk_gpio_chip *nmk_chip)
}

/* I/O Functions */

static int nmk_gpio_request(struct gpio_chip *chip, unsigned offset)
{
/*
* Map back to global GPIO space and request muxing, the direction
* parameter does not matter for this controller.
*/
int gpio = chip->base + offset;

return pinctrl_request_gpio(gpio);
}

static void nmk_gpio_free(struct gpio_chip *chip, unsigned offset)
{
int gpio = chip->base + offset;

pinctrl_free_gpio(gpio);
}

static int nmk_gpio_make_input(struct gpio_chip *chip, unsigned offset)
{
struct nmk_gpio_chip *nmk_chip =
Expand Down Expand Up @@ -1023,6 +1045,8 @@ static inline void nmk_gpio_dbg_show_one(struct seq_file *s,

/* This structure is replicated for each GPIO block allocated at probe time */
static struct gpio_chip nmk_gpio_template = {
.request = nmk_gpio_request,
.free = nmk_gpio_free,
.direction_input = nmk_gpio_make_input,
.get = nmk_gpio_get_input,
.direction_output = nmk_gpio_make_output,
Expand Down Expand Up @@ -1365,9 +1389,189 @@ static struct pinctrl_ops nmk_pinctrl_ops = {
.pin_dbg_show = nmk_pin_dbg_show,
};

static int nmk_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev)
{
struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev);

return npct->soc->nfunctions;
}

static const char *nmk_pmx_get_func_name(struct pinctrl_dev *pctldev,
unsigned function)
{
struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev);

return npct->soc->functions[function].name;
}

static int nmk_pmx_get_func_groups(struct pinctrl_dev *pctldev,
unsigned function,
const char * const **groups,
unsigned * const num_groups)
{
struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev);

*groups = npct->soc->functions[function].groups;
*num_groups = npct->soc->functions[function].ngroups;

return 0;
}

static int nmk_pmx_enable(struct pinctrl_dev *pctldev, unsigned function,
unsigned group)
{
struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev);
const struct nmk_pingroup *g;
static unsigned int slpm[NUM_BANKS];
unsigned long flags;
bool glitch;
int ret = -EINVAL;
int i;

g = &npct->soc->groups[group];

if (g->altsetting < 0)
return -EINVAL;

dev_dbg(npct->dev, "enable group %s, %u pins\n", g->name, g->npins);

/* Handle this special glitch on altfunction C */
glitch = (g->altsetting == NMK_GPIO_ALT_C);

if (glitch) {
spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);

/* Initially don't put any pins to sleep when switching */
memset(slpm, 0xff, sizeof(slpm));

/*
* Then mask the pins that need to be sleeping now when we're
* switching to the ALT C function.
*/
for (i = 0; i < g->npins; i++)
slpm[g->pins[i] / NMK_GPIO_PER_CHIP] &= ~BIT(g->pins[i]);
nmk_gpio_glitch_slpm_init(slpm);
}

for (i = 0; i < g->npins; i++) {
struct pinctrl_gpio_range *range;
struct nmk_gpio_chip *nmk_chip;
struct gpio_chip *chip;
unsigned bit;

range = nmk_match_gpio_range(pctldev, g->pins[i]);
if (!range) {
dev_err(npct->dev,
"invalid pin offset %d in group %s at index %d\n",
g->pins[i], g->name, i);
goto out_glitch;
}
if (!range->gc) {
dev_err(npct->dev, "GPIO chip missing in range for pin offset %d in group %s at index %d\n",
g->pins[i], g->name, i);
goto out_glitch;
}
chip = range->gc;
nmk_chip = container_of(chip, struct nmk_gpio_chip, chip);
dev_dbg(npct->dev, "setting pin %d to altsetting %d\n", g->pins[i], g->altsetting);

clk_enable(nmk_chip->clk);
bit = g->pins[i] % NMK_GPIO_PER_CHIP;
/*
* If the pin is switching to altfunc, and there was an
* interrupt installed on it which has been lazy disabled,
* actually mask the interrupt to prevent spurious interrupts
* that would occur while the pin is under control of the
* peripheral. Only SKE does this.
*/
nmk_gpio_disable_lazy_irq(nmk_chip, bit);

__nmk_gpio_set_mode_safe(nmk_chip, bit, g->altsetting, glitch);
clk_disable(nmk_chip->clk);
}

/* When all pins are successfully reconfigured we get here */
ret = 0;

out_glitch:
if (glitch) {
nmk_gpio_glitch_slpm_restore(slpm);
spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
}

return ret;
}

static void nmk_pmx_disable(struct pinctrl_dev *pctldev,
unsigned function, unsigned group)
{
struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev);
const struct nmk_pingroup *g;

g = &npct->soc->groups[group];

if (g->altsetting < 0)
return;

/* Poke out the mux, set the pin to some default state? */
dev_dbg(npct->dev, "disable group %s, %u pins\n", g->name, g->npins);
}

int nmk_gpio_request_enable(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset)
{
struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev);
struct nmk_gpio_chip *nmk_chip;
struct gpio_chip *chip;
unsigned bit;

if (!range) {
dev_err(npct->dev, "invalid range\n");
return -EINVAL;
}
if (!range->gc) {
dev_err(npct->dev, "missing GPIO chip in range\n");
return -EINVAL;
}
chip = range->gc;
nmk_chip = container_of(chip, struct nmk_gpio_chip, chip);

dev_dbg(npct->dev, "enable pin %u as GPIO\n", offset);

clk_enable(nmk_chip->clk);
bit = offset % NMK_GPIO_PER_CHIP;
/* There is no glitch when converting any pin to GPIO */
__nmk_gpio_set_mode(nmk_chip, bit, NMK_GPIO_ALT_GPIO);
clk_disable(nmk_chip->clk);

return 0;
}

void nmk_gpio_disable_free(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset)
{
struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev);

dev_dbg(npct->dev, "disable pin %u as GPIO\n", offset);
/* Set the pin to some default state, GPIO is usually default */
}

static struct pinmux_ops nmk_pinmux_ops = {
.get_functions_count = nmk_pmx_get_funcs_cnt,
.get_function_name = nmk_pmx_get_func_name,
.get_function_groups = nmk_pmx_get_func_groups,
.enable = nmk_pmx_enable,
.disable = nmk_pmx_disable,
.gpio_request_enable = nmk_gpio_request_enable,
.gpio_disable_free = nmk_gpio_disable_free,
};

static struct pinctrl_desc nmk_pinctrl_desc = {
.name = "pinctrl-nomadik",
.pctlops = &nmk_pinctrl_ops,
.pmxops = &nmk_pinmux_ops,
.owner = THIS_MODULE,
};

Expand Down
Loading

0 comments on commit dbfe8ca

Please sign in to comment.