mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 10:49:33 +00:00
![Fan Wu](/assets/img/avatar_default.png)
What the patch does: 1. Call pinmux_disable_setting ahead of pinmux_enable_setting each time pinctrl_select_state is called 2. Remove the HW disable operation in pinmux_disable_setting function. 3. Remove the disable ops in struct pinmux_ops 4. Remove all the disable ops users in current code base. Notes: 1. Great thanks for the suggestion from Linus, Tony Lindgren and Stephen Warren and Everyone that shared comments on this patch. 2. The patch also includes comment fixes from Stephen Warren. The reason why we do this: 1. To avoid duplicated calling of the enable_setting operation without disabling operation inbetween which will let the pin descriptor desc->mux_usecount increase monotonously. 2. The HW pin disable operation is not useful for any of the existing platforms. And this can be used to avoid the HW glitch after using the item #1 modification. In the following case, the issue can be reproduced: 1. There is a driver that need to switch pin state dynamically, e.g. between "sleep" and "default" state 2. The pin setting configuration in a DTS node may be like this: component a { pinctrl-names = "default", "sleep"; pinctrl-0 = <&a_grp_setting &c_grp_setting>; pinctrl-1 = <&b_grp_setting &c_grp_setting>; } The "c_grp_setting" config node is totally identical, maybe like following one: c_grp_setting: c_grp_setting { pinctrl-single,pins = <GPIO48 AF6>; } 3. When switching the pin state in the following official pinctrl sequence: pin = pinctrl_get(); state = pinctrl_lookup_state(wanted_state); pinctrl_select_state(state); pinctrl_put(); Test Result: 1. The switch is completed as expected, that is: the device's pin configuration is changed according to the description in the "wanted_state" group setting 2. The "desc->mux_usecount" of the corresponding pins in "c_group" is increased without being decreased, because the "desc" is for each physical pin while the setting is for each setting node in the DTS. Thus, if the "c_grp_setting" in pinctrl-0 is not disabled ahead of enabling "c_grp_setting" in pinctrl-1, the desc->mux_usecount will keep increasing without any chance to be decreased. According to the comments in the original code, only the setting, in old state but not in new state, will be "disabled" (calling pinmux_disable_setting), which is correct logic but not intact. We still need consider case that the setting is in both old state and new state. We can do this in the following two ways: 1. Avoid to "enable"(calling pinmux_enable_setting) the "same pin setting" repeatedly 2. "Disable"(calling pinmux_disable_setting) the "same pin setting", actually two setting instances, ahead of enabling them. Analysis: 1. The solution #2 is better because it can avoid too much iteration. 2. If we disable all of the settings in the old state and one of the setting(s) exist in the new state, the pins mux function change may happen when some SoC vendors defined the "pinctrl-single,function-off" in their DTS file. old_setting => disabled_setting => new_setting. 3. In the pinmux framework, when a pin state is switched, the setting in the old state should be marked as "disabled". Conclusion: 1. To Remove the HW disabling operation to above the glitch mentioned above. 2. Handle the issue mentioned above by disabling all of the settings in old state and then enable the all of the settings in new state. Signed-off-by: Fan Wu <fwu@marvell.com> Acked-by: Stephen Warren <swarren@nvidia.com> Acked-by: Patrice Chotard <patrice.chotard@st.com> Acked-by: Heiko Stuebner <heiko@sntech.de> Acked-by: Maxime Coquelin <maxime.coquelin@st.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2018 lines
53 KiB
C
2018 lines
53 KiB
C
/*
|
|
* Pinctrl driver for the Toumaz Xenif TZ1090 SoC
|
|
*
|
|
* Copyright (c) 2013, Imagination Technologies Ltd.
|
|
*
|
|
* Derived from Tegra code:
|
|
* Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* Derived from code:
|
|
* Copyright (C) 2010 Google, Inc.
|
|
* Copyright (C) 2010 NVIDIA Corporation
|
|
* Copyright (C) 2009-2011 ST-Ericsson AB
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*/
|
|
|
|
#include <linux/bitops.h>
|
|
#include <linux/io.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pinctrl/machine.h>
|
|
#include <linux/pinctrl/pinconf-generic.h>
|
|
#include <linux/pinctrl/pinctrl.h>
|
|
#include <linux/pinctrl/pinmux.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spinlock.h>
|
|
|
|
/*
|
|
* The registers may be shared with other threads/cores, so we need to use the
|
|
* metag global lock2 for atomicity.
|
|
*/
|
|
#include <asm/global_lock.h>
|
|
|
|
#include "core.h"
|
|
#include "pinconf.h"
|
|
|
|
/* Register offsets from bank base address */
|
|
#define REG_PINCTRL_SELECT 0x10
|
|
#define REG_PINCTRL_SCHMITT 0x90
|
|
#define REG_PINCTRL_PU_PD 0xa0
|
|
#define REG_PINCTRL_SR 0xc0
|
|
#define REG_PINCTRL_DR 0xd0
|
|
#define REG_PINCTRL_IF_CTL 0xe0
|
|
|
|
/* REG_PINCTRL_PU_PD field values */
|
|
#define REG_PU_PD_TRISTATE 0
|
|
#define REG_PU_PD_UP 1
|
|
#define REG_PU_PD_DOWN 2
|
|
#define REG_PU_PD_REPEATER 3
|
|
|
|
/* REG_PINCTRL_DR field values */
|
|
#define REG_DR_2mA 0
|
|
#define REG_DR_4mA 1
|
|
#define REG_DR_8mA 2
|
|
#define REG_DR_12mA 3
|
|
|
|
/**
|
|
* struct tz1090_function - TZ1090 pinctrl mux function
|
|
* @name: The name of the function, exported to pinctrl core.
|
|
* @groups: An array of pin groups that may select this function.
|
|
* @ngroups: The number of entries in @groups.
|
|
*/
|
|
struct tz1090_function {
|
|
const char *name;
|
|
const char * const *groups;
|
|
unsigned int ngroups;
|
|
};
|
|
|
|
/**
|
|
* struct tz1090_muxdesc - TZ1090 individual mux description
|
|
* @funcs: Function for each mux value.
|
|
* @reg: Mux register offset. 0 if unsupported.
|
|
* @bit: Mux register bit. 0 if unsupported.
|
|
* @width: Mux field width. 0 if unsupported.
|
|
*
|
|
* A representation of a group of signals (possibly just one signal) in the
|
|
* TZ1090 which can be muxed to a set of functions or sub muxes.
|
|
*/
|
|
struct tz1090_muxdesc {
|
|
int funcs[5];
|
|
u16 reg;
|
|
u8 bit;
|
|
u8 width;
|
|
};
|
|
|
|
/**
|
|
* struct tz1090_pingroup - TZ1090 pin group
|
|
* @name: Name of pin group.
|
|
* @pins: Array of pin numbers in this pin group.
|
|
* @npins: Number of pins in this pin group.
|
|
* @mux: Top level mux.
|
|
* @drv: Drive control supported, 0 if unsupported.
|
|
* This means Schmitt, Slew, and Drive strength.
|
|
* @slw_bit: Slew register bit. 0 if unsupported.
|
|
* The same bit is used for Schmitt, and Drive (*2).
|
|
* @func: Currently muxed function.
|
|
* @func_count: Number of pins using current mux function.
|
|
*
|
|
* A representation of a group of pins (possibly just one pin) in the TZ1090
|
|
* pin controller. Each group allows some parameter or parameters to be
|
|
* configured. The most common is mux function selection.
|
|
*/
|
|
struct tz1090_pingroup {
|
|
const char *name;
|
|
const unsigned int *pins;
|
|
unsigned int npins;
|
|
struct tz1090_muxdesc mux;
|
|
|
|
bool drv;
|
|
u8 slw_bit;
|
|
|
|
int func;
|
|
unsigned int func_count;
|
|
};
|
|
|
|
/*
|
|
* Most pins affected by the pinmux can also be GPIOs. Define these first.
|
|
* These must match how the GPIO driver names/numbers its pins.
|
|
*/
|
|
|
|
enum tz1090_pin {
|
|
/* GPIO pins */
|
|
TZ1090_PIN_SDIO_CLK,
|
|
TZ1090_PIN_SDIO_CMD,
|
|
TZ1090_PIN_SDIO_D0,
|
|
TZ1090_PIN_SDIO_D1,
|
|
TZ1090_PIN_SDIO_D2,
|
|
TZ1090_PIN_SDIO_D3,
|
|
TZ1090_PIN_SDH_CD,
|
|
TZ1090_PIN_SDH_WP,
|
|
TZ1090_PIN_SPI0_MCLK,
|
|
TZ1090_PIN_SPI0_CS0,
|
|
TZ1090_PIN_SPI0_CS1,
|
|
TZ1090_PIN_SPI0_CS2,
|
|
TZ1090_PIN_SPI0_DOUT,
|
|
TZ1090_PIN_SPI0_DIN,
|
|
TZ1090_PIN_SPI1_MCLK,
|
|
TZ1090_PIN_SPI1_CS0,
|
|
TZ1090_PIN_SPI1_CS1,
|
|
TZ1090_PIN_SPI1_CS2,
|
|
TZ1090_PIN_SPI1_DOUT,
|
|
TZ1090_PIN_SPI1_DIN,
|
|
TZ1090_PIN_UART0_RXD,
|
|
TZ1090_PIN_UART0_TXD,
|
|
TZ1090_PIN_UART0_CTS,
|
|
TZ1090_PIN_UART0_RTS,
|
|
TZ1090_PIN_UART1_RXD,
|
|
TZ1090_PIN_UART1_TXD,
|
|
TZ1090_PIN_SCB0_SDAT,
|
|
TZ1090_PIN_SCB0_SCLK,
|
|
TZ1090_PIN_SCB1_SDAT,
|
|
TZ1090_PIN_SCB1_SCLK,
|
|
TZ1090_PIN_SCB2_SDAT,
|
|
TZ1090_PIN_SCB2_SCLK,
|
|
TZ1090_PIN_I2S_MCLK,
|
|
TZ1090_PIN_I2S_BCLK_OUT,
|
|
TZ1090_PIN_I2S_LRCLK_OUT,
|
|
TZ1090_PIN_I2S_DOUT0,
|
|
TZ1090_PIN_I2S_DOUT1,
|
|
TZ1090_PIN_I2S_DOUT2,
|
|
TZ1090_PIN_I2S_DIN,
|
|
TZ1090_PIN_PDM_A,
|
|
TZ1090_PIN_PDM_B,
|
|
TZ1090_PIN_PDM_C,
|
|
TZ1090_PIN_PDM_D,
|
|
TZ1090_PIN_TFT_RED0,
|
|
TZ1090_PIN_TFT_RED1,
|
|
TZ1090_PIN_TFT_RED2,
|
|
TZ1090_PIN_TFT_RED3,
|
|
TZ1090_PIN_TFT_RED4,
|
|
TZ1090_PIN_TFT_RED5,
|
|
TZ1090_PIN_TFT_RED6,
|
|
TZ1090_PIN_TFT_RED7,
|
|
TZ1090_PIN_TFT_GREEN0,
|
|
TZ1090_PIN_TFT_GREEN1,
|
|
TZ1090_PIN_TFT_GREEN2,
|
|
TZ1090_PIN_TFT_GREEN3,
|
|
TZ1090_PIN_TFT_GREEN4,
|
|
TZ1090_PIN_TFT_GREEN5,
|
|
TZ1090_PIN_TFT_GREEN6,
|
|
TZ1090_PIN_TFT_GREEN7,
|
|
TZ1090_PIN_TFT_BLUE0,
|
|
TZ1090_PIN_TFT_BLUE1,
|
|
TZ1090_PIN_TFT_BLUE2,
|
|
TZ1090_PIN_TFT_BLUE3,
|
|
TZ1090_PIN_TFT_BLUE4,
|
|
TZ1090_PIN_TFT_BLUE5,
|
|
TZ1090_PIN_TFT_BLUE6,
|
|
TZ1090_PIN_TFT_BLUE7,
|
|
TZ1090_PIN_TFT_VDDEN_GD,
|
|
TZ1090_PIN_TFT_PANELCLK,
|
|
TZ1090_PIN_TFT_BLANK_LS,
|
|
TZ1090_PIN_TFT_VSYNC_NS,
|
|
TZ1090_PIN_TFT_HSYNC_NR,
|
|
TZ1090_PIN_TFT_VD12ACB,
|
|
TZ1090_PIN_TFT_PWRSAVE,
|
|
TZ1090_PIN_TX_ON,
|
|
TZ1090_PIN_RX_ON,
|
|
TZ1090_PIN_PLL_ON,
|
|
TZ1090_PIN_PA_ON,
|
|
TZ1090_PIN_RX_HP,
|
|
TZ1090_PIN_GAIN0,
|
|
TZ1090_PIN_GAIN1,
|
|
TZ1090_PIN_GAIN2,
|
|
TZ1090_PIN_GAIN3,
|
|
TZ1090_PIN_GAIN4,
|
|
TZ1090_PIN_GAIN5,
|
|
TZ1090_PIN_GAIN6,
|
|
TZ1090_PIN_GAIN7,
|
|
TZ1090_PIN_ANT_SEL0,
|
|
TZ1090_PIN_ANT_SEL1,
|
|
TZ1090_PIN_SDH_CLK_IN,
|
|
|
|
/* Non-GPIO pins */
|
|
TZ1090_PIN_TCK,
|
|
TZ1090_PIN_TRST,
|
|
TZ1090_PIN_TDI,
|
|
TZ1090_PIN_TDO,
|
|
TZ1090_PIN_TMS,
|
|
TZ1090_PIN_CLK_OUT0,
|
|
TZ1090_PIN_CLK_OUT1,
|
|
|
|
NUM_GPIOS = TZ1090_PIN_TCK,
|
|
};
|
|
|
|
/* Pin names */
|
|
|
|
static const struct pinctrl_pin_desc tz1090_pins[] = {
|
|
/* GPIO pins */
|
|
PINCTRL_PIN(TZ1090_PIN_SDIO_CLK, "sdio_clk"),
|
|
PINCTRL_PIN(TZ1090_PIN_SDIO_CMD, "sdio_cmd"),
|
|
PINCTRL_PIN(TZ1090_PIN_SDIO_D0, "sdio_d0"),
|
|
PINCTRL_PIN(TZ1090_PIN_SDIO_D1, "sdio_d1"),
|
|
PINCTRL_PIN(TZ1090_PIN_SDIO_D2, "sdio_d2"),
|
|
PINCTRL_PIN(TZ1090_PIN_SDIO_D3, "sdio_d3"),
|
|
PINCTRL_PIN(TZ1090_PIN_SDH_CD, "sdh_cd"),
|
|
PINCTRL_PIN(TZ1090_PIN_SDH_WP, "sdh_wp"),
|
|
PINCTRL_PIN(TZ1090_PIN_SPI0_MCLK, "spi0_mclk"),
|
|
PINCTRL_PIN(TZ1090_PIN_SPI0_CS0, "spi0_cs0"),
|
|
PINCTRL_PIN(TZ1090_PIN_SPI0_CS1, "spi0_cs1"),
|
|
PINCTRL_PIN(TZ1090_PIN_SPI0_CS2, "spi0_cs2"),
|
|
PINCTRL_PIN(TZ1090_PIN_SPI0_DOUT, "spi0_dout"),
|
|
PINCTRL_PIN(TZ1090_PIN_SPI0_DIN, "spi0_din"),
|
|
PINCTRL_PIN(TZ1090_PIN_SPI1_MCLK, "spi1_mclk"),
|
|
PINCTRL_PIN(TZ1090_PIN_SPI1_CS0, "spi1_cs0"),
|
|
PINCTRL_PIN(TZ1090_PIN_SPI1_CS1, "spi1_cs1"),
|
|
PINCTRL_PIN(TZ1090_PIN_SPI1_CS2, "spi1_cs2"),
|
|
PINCTRL_PIN(TZ1090_PIN_SPI1_DOUT, "spi1_dout"),
|
|
PINCTRL_PIN(TZ1090_PIN_SPI1_DIN, "spi1_din"),
|
|
PINCTRL_PIN(TZ1090_PIN_UART0_RXD, "uart0_rxd"),
|
|
PINCTRL_PIN(TZ1090_PIN_UART0_TXD, "uart0_txd"),
|
|
PINCTRL_PIN(TZ1090_PIN_UART0_CTS, "uart0_cts"),
|
|
PINCTRL_PIN(TZ1090_PIN_UART0_RTS, "uart0_rts"),
|
|
PINCTRL_PIN(TZ1090_PIN_UART1_RXD, "uart1_rxd"),
|
|
PINCTRL_PIN(TZ1090_PIN_UART1_TXD, "uart1_txd"),
|
|
PINCTRL_PIN(TZ1090_PIN_SCB0_SDAT, "scb0_sdat"),
|
|
PINCTRL_PIN(TZ1090_PIN_SCB0_SCLK, "scb0_sclk"),
|
|
PINCTRL_PIN(TZ1090_PIN_SCB1_SDAT, "scb1_sdat"),
|
|
PINCTRL_PIN(TZ1090_PIN_SCB1_SCLK, "scb1_sclk"),
|
|
PINCTRL_PIN(TZ1090_PIN_SCB2_SDAT, "scb2_sdat"),
|
|
PINCTRL_PIN(TZ1090_PIN_SCB2_SCLK, "scb2_sclk"),
|
|
PINCTRL_PIN(TZ1090_PIN_I2S_MCLK, "i2s_mclk"),
|
|
PINCTRL_PIN(TZ1090_PIN_I2S_BCLK_OUT, "i2s_bclk_out"),
|
|
PINCTRL_PIN(TZ1090_PIN_I2S_LRCLK_OUT, "i2s_lrclk_out"),
|
|
PINCTRL_PIN(TZ1090_PIN_I2S_DOUT0, "i2s_dout0"),
|
|
PINCTRL_PIN(TZ1090_PIN_I2S_DOUT1, "i2s_dout1"),
|
|
PINCTRL_PIN(TZ1090_PIN_I2S_DOUT2, "i2s_dout2"),
|
|
PINCTRL_PIN(TZ1090_PIN_I2S_DIN, "i2s_din"),
|
|
PINCTRL_PIN(TZ1090_PIN_PDM_A, "pdm_a"),
|
|
PINCTRL_PIN(TZ1090_PIN_PDM_B, "pdm_b"),
|
|
PINCTRL_PIN(TZ1090_PIN_PDM_C, "pdm_c"),
|
|
PINCTRL_PIN(TZ1090_PIN_PDM_D, "pdm_d"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_RED0, "tft_red0"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_RED1, "tft_red1"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_RED2, "tft_red2"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_RED3, "tft_red3"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_RED4, "tft_red4"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_RED5, "tft_red5"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_RED6, "tft_red6"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_RED7, "tft_red7"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_GREEN0, "tft_green0"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_GREEN1, "tft_green1"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_GREEN2, "tft_green2"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_GREEN3, "tft_green3"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_GREEN4, "tft_green4"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_GREEN5, "tft_green5"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_GREEN6, "tft_green6"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_GREEN7, "tft_green7"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_BLUE0, "tft_blue0"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_BLUE1, "tft_blue1"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_BLUE2, "tft_blue2"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_BLUE3, "tft_blue3"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_BLUE4, "tft_blue4"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_BLUE5, "tft_blue5"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_BLUE6, "tft_blue6"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_BLUE7, "tft_blue7"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_VDDEN_GD, "tft_vdden_gd"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_PANELCLK, "tft_panelclk"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_BLANK_LS, "tft_blank_ls"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_VSYNC_NS, "tft_vsync_ns"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_HSYNC_NR, "tft_hsync_nr"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_VD12ACB, "tft_vd12acb"),
|
|
PINCTRL_PIN(TZ1090_PIN_TFT_PWRSAVE, "tft_pwrsave"),
|
|
PINCTRL_PIN(TZ1090_PIN_TX_ON, "tx_on"),
|
|
PINCTRL_PIN(TZ1090_PIN_RX_ON, "rx_on"),
|
|
PINCTRL_PIN(TZ1090_PIN_PLL_ON, "pll_on"),
|
|
PINCTRL_PIN(TZ1090_PIN_PA_ON, "pa_on"),
|
|
PINCTRL_PIN(TZ1090_PIN_RX_HP, "rx_hp"),
|
|
PINCTRL_PIN(TZ1090_PIN_GAIN0, "gain0"),
|
|
PINCTRL_PIN(TZ1090_PIN_GAIN1, "gain1"),
|
|
PINCTRL_PIN(TZ1090_PIN_GAIN2, "gain2"),
|
|
PINCTRL_PIN(TZ1090_PIN_GAIN3, "gain3"),
|
|
PINCTRL_PIN(TZ1090_PIN_GAIN4, "gain4"),
|
|
PINCTRL_PIN(TZ1090_PIN_GAIN5, "gain5"),
|
|
PINCTRL_PIN(TZ1090_PIN_GAIN6, "gain6"),
|
|
PINCTRL_PIN(TZ1090_PIN_GAIN7, "gain7"),
|
|
PINCTRL_PIN(TZ1090_PIN_ANT_SEL0, "ant_sel0"),
|
|
PINCTRL_PIN(TZ1090_PIN_ANT_SEL1, "ant_sel1"),
|
|
PINCTRL_PIN(TZ1090_PIN_SDH_CLK_IN, "sdh_clk_in"),
|
|
|
|
/* Non-GPIO pins */
|
|
PINCTRL_PIN(TZ1090_PIN_TCK, "tck"),
|
|
PINCTRL_PIN(TZ1090_PIN_TRST, "trst"),
|
|
PINCTRL_PIN(TZ1090_PIN_TDI, "tdi"),
|
|
PINCTRL_PIN(TZ1090_PIN_TDO, "tdo"),
|
|
PINCTRL_PIN(TZ1090_PIN_TMS, "tms"),
|
|
PINCTRL_PIN(TZ1090_PIN_CLK_OUT0, "clk_out0"),
|
|
PINCTRL_PIN(TZ1090_PIN_CLK_OUT1, "clk_out1"),
|
|
};
|
|
|
|
/* Pins in each pin group */
|
|
|
|
static const unsigned int spi1_cs2_pins[] = {
|
|
TZ1090_PIN_SPI1_CS2,
|
|
};
|
|
|
|
static const unsigned int pdm_d_pins[] = {
|
|
TZ1090_PIN_PDM_D,
|
|
};
|
|
|
|
static const unsigned int tft_pins[] = {
|
|
TZ1090_PIN_TFT_RED0,
|
|
TZ1090_PIN_TFT_RED1,
|
|
TZ1090_PIN_TFT_RED2,
|
|
TZ1090_PIN_TFT_RED3,
|
|
TZ1090_PIN_TFT_RED4,
|
|
TZ1090_PIN_TFT_RED5,
|
|
TZ1090_PIN_TFT_RED6,
|
|
TZ1090_PIN_TFT_RED7,
|
|
TZ1090_PIN_TFT_GREEN0,
|
|
TZ1090_PIN_TFT_GREEN1,
|
|
TZ1090_PIN_TFT_GREEN2,
|
|
TZ1090_PIN_TFT_GREEN3,
|
|
TZ1090_PIN_TFT_GREEN4,
|
|
TZ1090_PIN_TFT_GREEN5,
|
|
TZ1090_PIN_TFT_GREEN6,
|
|
TZ1090_PIN_TFT_GREEN7,
|
|
TZ1090_PIN_TFT_BLUE0,
|
|
TZ1090_PIN_TFT_BLUE1,
|
|
TZ1090_PIN_TFT_BLUE2,
|
|
TZ1090_PIN_TFT_BLUE3,
|
|
TZ1090_PIN_TFT_BLUE4,
|
|
TZ1090_PIN_TFT_BLUE5,
|
|
TZ1090_PIN_TFT_BLUE6,
|
|
TZ1090_PIN_TFT_BLUE7,
|
|
TZ1090_PIN_TFT_VDDEN_GD,
|
|
TZ1090_PIN_TFT_PANELCLK,
|
|
TZ1090_PIN_TFT_BLANK_LS,
|
|
TZ1090_PIN_TFT_VSYNC_NS,
|
|
TZ1090_PIN_TFT_HSYNC_NR,
|
|
TZ1090_PIN_TFT_VD12ACB,
|
|
TZ1090_PIN_TFT_PWRSAVE,
|
|
};
|
|
|
|
static const unsigned int afe_pins[] = {
|
|
TZ1090_PIN_TX_ON,
|
|
TZ1090_PIN_RX_ON,
|
|
TZ1090_PIN_PLL_ON,
|
|
TZ1090_PIN_PA_ON,
|
|
TZ1090_PIN_RX_HP,
|
|
TZ1090_PIN_ANT_SEL0,
|
|
TZ1090_PIN_ANT_SEL1,
|
|
TZ1090_PIN_GAIN0,
|
|
TZ1090_PIN_GAIN1,
|
|
TZ1090_PIN_GAIN2,
|
|
TZ1090_PIN_GAIN3,
|
|
TZ1090_PIN_GAIN4,
|
|
TZ1090_PIN_GAIN5,
|
|
TZ1090_PIN_GAIN6,
|
|
TZ1090_PIN_GAIN7,
|
|
};
|
|
|
|
static const unsigned int sdio_pins[] = {
|
|
TZ1090_PIN_SDIO_CLK,
|
|
TZ1090_PIN_SDIO_CMD,
|
|
TZ1090_PIN_SDIO_D0,
|
|
TZ1090_PIN_SDIO_D1,
|
|
TZ1090_PIN_SDIO_D2,
|
|
TZ1090_PIN_SDIO_D3,
|
|
};
|
|
|
|
static const unsigned int sdh_pins[] = {
|
|
TZ1090_PIN_SDH_CD,
|
|
TZ1090_PIN_SDH_WP,
|
|
TZ1090_PIN_SDH_CLK_IN,
|
|
};
|
|
|
|
static const unsigned int spi0_pins[] = {
|
|
TZ1090_PIN_SPI0_MCLK,
|
|
TZ1090_PIN_SPI0_CS0,
|
|
TZ1090_PIN_SPI0_CS1,
|
|
TZ1090_PIN_SPI0_CS2,
|
|
TZ1090_PIN_SPI0_DOUT,
|
|
TZ1090_PIN_SPI0_DIN,
|
|
};
|
|
|
|
static const unsigned int spi1_pins[] = {
|
|
TZ1090_PIN_SPI1_MCLK,
|
|
TZ1090_PIN_SPI1_CS0,
|
|
TZ1090_PIN_SPI1_CS1,
|
|
TZ1090_PIN_SPI1_CS2,
|
|
TZ1090_PIN_SPI1_DOUT,
|
|
TZ1090_PIN_SPI1_DIN,
|
|
};
|
|
|
|
static const unsigned int uart0_pins[] = {
|
|
TZ1090_PIN_UART0_RTS,
|
|
TZ1090_PIN_UART0_CTS,
|
|
TZ1090_PIN_UART0_TXD,
|
|
TZ1090_PIN_UART0_RXD,
|
|
};
|
|
|
|
static const unsigned int uart1_pins[] = {
|
|
TZ1090_PIN_UART1_TXD,
|
|
TZ1090_PIN_UART1_RXD,
|
|
};
|
|
|
|
static const unsigned int uart_pins[] = {
|
|
TZ1090_PIN_UART1_TXD,
|
|
TZ1090_PIN_UART1_RXD,
|
|
TZ1090_PIN_UART0_RTS,
|
|
TZ1090_PIN_UART0_CTS,
|
|
TZ1090_PIN_UART0_TXD,
|
|
TZ1090_PIN_UART0_RXD,
|
|
};
|
|
|
|
static const unsigned int scb0_pins[] = {
|
|
TZ1090_PIN_SCB0_SDAT,
|
|
TZ1090_PIN_SCB0_SCLK,
|
|
};
|
|
|
|
static const unsigned int scb1_pins[] = {
|
|
TZ1090_PIN_SCB1_SDAT,
|
|
TZ1090_PIN_SCB1_SCLK,
|
|
};
|
|
|
|
static const unsigned int scb2_pins[] = {
|
|
TZ1090_PIN_SCB2_SDAT,
|
|
TZ1090_PIN_SCB2_SCLK,
|
|
};
|
|
|
|
static const unsigned int i2s_pins[] = {
|
|
TZ1090_PIN_I2S_MCLK,
|
|
TZ1090_PIN_I2S_BCLK_OUT,
|
|
TZ1090_PIN_I2S_LRCLK_OUT,
|
|
TZ1090_PIN_I2S_DOUT0,
|
|
TZ1090_PIN_I2S_DOUT1,
|
|
TZ1090_PIN_I2S_DOUT2,
|
|
TZ1090_PIN_I2S_DIN,
|
|
};
|
|
|
|
static const unsigned int jtag_pins[] = {
|
|
TZ1090_PIN_TCK,
|
|
TZ1090_PIN_TRST,
|
|
TZ1090_PIN_TDI,
|
|
TZ1090_PIN_TDO,
|
|
TZ1090_PIN_TMS,
|
|
};
|
|
|
|
/* Pins in each drive pin group */
|
|
|
|
static const unsigned int drive_sdio_pins[] = {
|
|
TZ1090_PIN_SDIO_CLK,
|
|
TZ1090_PIN_SDIO_CMD,
|
|
TZ1090_PIN_SDIO_D0,
|
|
TZ1090_PIN_SDIO_D1,
|
|
TZ1090_PIN_SDIO_D2,
|
|
TZ1090_PIN_SDIO_D3,
|
|
TZ1090_PIN_SDH_WP,
|
|
TZ1090_PIN_SDH_CD,
|
|
TZ1090_PIN_SDH_CLK_IN,
|
|
};
|
|
|
|
static const unsigned int drive_i2s_pins[] = {
|
|
TZ1090_PIN_CLK_OUT1,
|
|
TZ1090_PIN_I2S_DIN,
|
|
TZ1090_PIN_I2S_DOUT0,
|
|
TZ1090_PIN_I2S_DOUT1,
|
|
TZ1090_PIN_I2S_DOUT2,
|
|
TZ1090_PIN_I2S_LRCLK_OUT,
|
|
TZ1090_PIN_I2S_BCLK_OUT,
|
|
TZ1090_PIN_I2S_MCLK,
|
|
};
|
|
|
|
static const unsigned int drive_scb0_pins[] = {
|
|
TZ1090_PIN_SCB0_SCLK,
|
|
TZ1090_PIN_SCB0_SDAT,
|
|
TZ1090_PIN_PDM_D,
|
|
TZ1090_PIN_PDM_C,
|
|
};
|
|
|
|
static const unsigned int drive_pdm_pins[] = {
|
|
TZ1090_PIN_CLK_OUT0,
|
|
TZ1090_PIN_PDM_B,
|
|
TZ1090_PIN_PDM_A,
|
|
};
|
|
|
|
/* Pin groups each function can be muxed to */
|
|
|
|
/*
|
|
* The magic "perip" function allows otherwise non-muxing pins to be enabled in
|
|
* peripheral mode.
|
|
*/
|
|
static const char * const perip_groups[] = {
|
|
/* non-muxing convenient gpio pingroups */
|
|
"uart",
|
|
"uart0",
|
|
"uart1",
|
|
"spi0",
|
|
"spi1",
|
|
"scb0",
|
|
"scb1",
|
|
"scb2",
|
|
"i2s",
|
|
/* individual pins not part of a pin mux group */
|
|
"spi0_mclk",
|
|
"spi0_cs0",
|
|
"spi0_cs1",
|
|
"spi0_cs2",
|
|
"spi0_dout",
|
|
"spi0_din",
|
|
"spi1_mclk",
|
|
"spi1_cs0",
|
|
"spi1_cs1",
|
|
"spi1_dout",
|
|
"spi1_din",
|
|
"uart0_rxd",
|
|
"uart0_txd",
|
|
"uart0_cts",
|
|
"uart0_rts",
|
|
"uart1_rxd",
|
|
"uart1_txd",
|
|
"scb0_sdat",
|
|
"scb0_sclk",
|
|
"scb1_sdat",
|
|
"scb1_sclk",
|
|
"scb2_sdat",
|
|
"scb2_sclk",
|
|
"i2s_mclk",
|
|
"i2s_bclk_out",
|
|
"i2s_lrclk_out",
|
|
"i2s_dout0",
|
|
"i2s_dout1",
|
|
"i2s_dout2",
|
|
"i2s_din",
|
|
"pdm_a",
|
|
"pdm_b",
|
|
"pdm_c",
|
|
};
|
|
|
|
static const char * const sdh_sdio_groups[] = {
|
|
"sdh",
|
|
"sdio",
|
|
/* sdh pins */
|
|
"sdh_cd",
|
|
"sdh_wp",
|
|
"sdh_clk_in",
|
|
/* sdio pins */
|
|
"sdio_clk",
|
|
"sdio_cmd",
|
|
"sdio_d0",
|
|
"sdio_d1",
|
|
"sdio_d2",
|
|
"sdio_d3",
|
|
};
|
|
|
|
static const char * const spi1_cs2_groups[] = {
|
|
"spi1_cs2",
|
|
};
|
|
|
|
static const char * const pdm_dac_groups[] = {
|
|
"pdm_d",
|
|
};
|
|
|
|
static const char * const usb_vbus_groups[] = {
|
|
"spi1_cs2",
|
|
"pdm_d",
|
|
};
|
|
|
|
static const char * const afe_groups[] = {
|
|
"afe",
|
|
/* afe pins */
|
|
"tx_on",
|
|
"rx_on",
|
|
"pll_on",
|
|
"pa_on",
|
|
"rx_hp",
|
|
"ant_sel0",
|
|
"ant_sel1",
|
|
"gain0",
|
|
"gain1",
|
|
"gain2",
|
|
"gain3",
|
|
"gain4",
|
|
"gain5",
|
|
"gain6",
|
|
"gain7",
|
|
};
|
|
|
|
static const char * const tft_groups[] = {
|
|
"tft",
|
|
/* tft pins */
|
|
"tft_red0",
|
|
"tft_red1",
|
|
"tft_red2",
|
|
"tft_red3",
|
|
"tft_red4",
|
|
"tft_red5",
|
|
"tft_red6",
|
|
"tft_red7",
|
|
"tft_green0",
|
|
"tft_green1",
|
|
"tft_green2",
|
|
"tft_green3",
|
|
"tft_green4",
|
|
"tft_green5",
|
|
"tft_green6",
|
|
"tft_green7",
|
|
"tft_blue0",
|
|
"tft_blue1",
|
|
"tft_blue2",
|
|
"tft_blue3",
|
|
"tft_blue4",
|
|
"tft_blue5",
|
|
"tft_blue6",
|
|
"tft_blue7",
|
|
"tft_vdden_gd",
|
|
"tft_panelclk",
|
|
"tft_blank_ls",
|
|
"tft_vsync_ns",
|
|
"tft_hsync_nr",
|
|
"tft_vd12acb",
|
|
"tft_pwrsave",
|
|
};
|
|
|
|
/* Mux functions that can be used by a mux */
|
|
|
|
enum tz1090_mux {
|
|
/* internal placeholder */
|
|
TZ1090_MUX_NA = -1,
|
|
/* magic per-non-muxing-GPIO-pin peripheral mode mux */
|
|
TZ1090_MUX_PERIP,
|
|
/* SDH/SDIO mux */
|
|
TZ1090_MUX_SDH,
|
|
TZ1090_MUX_SDIO,
|
|
/* USB_VBUS muxes */
|
|
TZ1090_MUX_SPI1_CS2,
|
|
TZ1090_MUX_PDM_DAC,
|
|
TZ1090_MUX_USB_VBUS,
|
|
/* AFE mux */
|
|
TZ1090_MUX_AFE,
|
|
TZ1090_MUX_TS_OUT_0,
|
|
/* EXT_DAC mux */
|
|
TZ1090_MUX_DAC,
|
|
TZ1090_MUX_NOT_IQADC_STB,
|
|
TZ1090_MUX_IQDAC_STB,
|
|
/* TFT mux */
|
|
TZ1090_MUX_TFT,
|
|
TZ1090_MUX_EXT_DAC,
|
|
TZ1090_MUX_TS_OUT_1,
|
|
TZ1090_MUX_LCD_TRACE,
|
|
TZ1090_MUX_PHY_RINGOSC,
|
|
};
|
|
|
|
#define FUNCTION(mux, fname, group) \
|
|
[(TZ1090_MUX_ ## mux)] = { \
|
|
.name = #fname, \
|
|
.groups = group##_groups, \
|
|
.ngroups = ARRAY_SIZE(group##_groups), \
|
|
}
|
|
/* For intermediate functions with submuxes */
|
|
#define NULL_FUNCTION(mux, fname) \
|
|
[(TZ1090_MUX_ ## mux)] = { \
|
|
.name = #fname, \
|
|
}
|
|
|
|
/* Must correlate with enum tz1090_mux */
|
|
static const struct tz1090_function tz1090_functions[] = {
|
|
/* FUNCTION function name pingroups */
|
|
FUNCTION(PERIP, perip, perip),
|
|
FUNCTION(SDH, sdh, sdh_sdio),
|
|
FUNCTION(SDIO, sdio, sdh_sdio),
|
|
FUNCTION(SPI1_CS2, spi1_cs2, spi1_cs2),
|
|
FUNCTION(PDM_DAC, pdm_dac, pdm_dac),
|
|
FUNCTION(USB_VBUS, usb_vbus, usb_vbus),
|
|
FUNCTION(AFE, afe, afe),
|
|
FUNCTION(TS_OUT_0, ts_out_0, afe),
|
|
FUNCTION(DAC, ext_dac, tft),
|
|
FUNCTION(NOT_IQADC_STB, not_iqadc_stb, tft),
|
|
FUNCTION(IQDAC_STB, iqdac_stb, tft),
|
|
FUNCTION(TFT, tft, tft),
|
|
NULL_FUNCTION(EXT_DAC, _ext_dac),
|
|
FUNCTION(TS_OUT_1, ts_out_1, tft),
|
|
FUNCTION(LCD_TRACE, lcd_trace, tft),
|
|
FUNCTION(PHY_RINGOSC, phy_ringosc, tft),
|
|
};
|
|
|
|
/* Sub muxes */
|
|
|
|
/**
|
|
* MUX() - Initialise a mux description.
|
|
* @f0: Function 0 (TZ1090_MUX_ is prepended, NA for none)
|
|
* @f1: Function 1 (TZ1090_MUX_ is prepended, NA for none)
|
|
* @f2: Function 2 (TZ1090_MUX_ is prepended, NA for none)
|
|
* @f3: Function 3 (TZ1090_MUX_ is prepended, NA for none)
|
|
* @f4: Function 4 (TZ1090_MUX_ is prepended, NA for none)
|
|
* @mux_r: Mux register (REG_PINCTRL_ is prepended)
|
|
* @mux_b: Bit number in register that the mux field begins
|
|
* @mux_w: Width of mux field in register
|
|
*/
|
|
#define MUX(f0, f1, f2, f3, f4, mux_r, mux_b, mux_w) \
|
|
{ \
|
|
.funcs = { \
|
|
TZ1090_MUX_ ## f0, \
|
|
TZ1090_MUX_ ## f1, \
|
|
TZ1090_MUX_ ## f2, \
|
|
TZ1090_MUX_ ## f3, \
|
|
TZ1090_MUX_ ## f4, \
|
|
}, \
|
|
.reg = (REG_PINCTRL_ ## mux_r), \
|
|
.bit = (mux_b), \
|
|
.width = (mux_w), \
|
|
}
|
|
|
|
/**
|
|
* DEFINE_SUBMUX() - Defines a submux description separate from a pin group.
|
|
* @mux: Mux name (_submux is appended)
|
|
* @f0: Function 0 (TZ1090_MUX_ is prepended, NA for none)
|
|
* @f1: Function 1 (TZ1090_MUX_ is prepended, NA for none)
|
|
* @f2: Function 2 (TZ1090_MUX_ is prepended, NA for none)
|
|
* @f3: Function 3 (TZ1090_MUX_ is prepended, NA for none)
|
|
* @f4: Function 4 (TZ1090_MUX_ is prepended, NA for none)
|
|
* @mux_r: Mux register (REG_PINCTRL_ is prepended)
|
|
* @mux_b: Bit number in register that the mux field begins
|
|
* @mux_w: Width of mux field in register
|
|
*
|
|
* A sub mux is a nested mux that can be bound to a magic function number used
|
|
* by another mux description. For example value 4 of the top level mux might
|
|
* correspond to a function which has a submux pointed to in tz1090_submux[].
|
|
* The outer mux can then take on any function in the top level mux or the
|
|
* submux, and if a submux function is chosen both muxes are updated to route
|
|
* the signal from the submux.
|
|
*
|
|
* The submux can be defined with DEFINE_SUBMUX and pointed to from
|
|
* tz1090_submux[] using SUBMUX.
|
|
*/
|
|
#define DEFINE_SUBMUX(mux, f0, f1, f2, f3, f4, mux_r, mux_b, mux_w) \
|
|
static struct tz1090_muxdesc mux ## _submux = \
|
|
MUX(f0, f1, f2, f3, f4, mux_r, mux_b, mux_w)
|
|
|
|
/**
|
|
* SUBMUX() - Link a submux to a function number.
|
|
* @f: Function name (TZ1090_MUX_ is prepended)
|
|
* @submux: Submux name (_submux is appended)
|
|
*
|
|
* For use in tz1090_submux[] initialisation to link an intermediate function
|
|
* number to a particular submux description. It indicates that when the
|
|
* function is chosen the signal is connected to the submux.
|
|
*/
|
|
#define SUBMUX(f, submux) [(TZ1090_MUX_ ## f)] = &(submux ## _submux)
|
|
|
|
/**
|
|
* MUX_PG() - Initialise a pin group with mux control
|
|
* @pg_name: Pin group name (stringified, _pins appended to get pins array)
|
|
* @f0: Function 0 (TZ1090_MUX_ is prepended, NA for none)
|
|
* @f1: Function 1 (TZ1090_MUX_ is prepended, NA for none)
|
|
* @f2: Function 2 (TZ1090_MUX_ is prepended, NA for none)
|
|
* @f3: Function 3 (TZ1090_MUX_ is prepended, NA for none)
|
|
* @f4: Function 4 (TZ1090_MUX_ is prepended, NA for none)
|
|
* @mux_r: Mux register (REG_PINCTRL_ is prepended)
|
|
* @mux_b: Bit number in register that the mux field begins
|
|
* @mux_w: Width of mux field in register
|
|
*/
|
|
#define MUX_PG(pg_name, f0, f1, f2, f3, f4, \
|
|
mux_r, mux_b, mux_w) \
|
|
{ \
|
|
.name = #pg_name, \
|
|
.pins = pg_name##_pins, \
|
|
.npins = ARRAY_SIZE(pg_name##_pins), \
|
|
.mux = MUX(f0, f1, f2, f3, f4, \
|
|
mux_r, mux_b, mux_w), \
|
|
}
|
|
|
|
/**
|
|
* SIMPLE_PG() - Initialise a simple convenience pin group
|
|
* @pg_name: Pin group name (stringified, _pins appended to get pins array)
|
|
*
|
|
* A simple pin group is simply used for binding pins together so they can be
|
|
* referred to by a single name instead of having to list every pin
|
|
* individually.
|
|
*/
|
|
#define SIMPLE_PG(pg_name) \
|
|
{ \
|
|
.name = #pg_name, \
|
|
.pins = pg_name##_pins, \
|
|
.npins = ARRAY_SIZE(pg_name##_pins), \
|
|
}
|
|
|
|
/**
|
|
* DRV_PG() - Initialise a pin group with drive control
|
|
* @pg_name: Pin group name (stringified, _pins appended to get pins array)
|
|
* @slw_b: Slew register bit.
|
|
* The same bit is used for Schmitt, and Drive (*2).
|
|
*/
|
|
#define DRV_PG(pg_name, slw_b) \
|
|
{ \
|
|
.name = #pg_name, \
|
|
.pins = pg_name##_pins, \
|
|
.npins = ARRAY_SIZE(pg_name##_pins), \
|
|
.drv = true, \
|
|
.slw_bit = (slw_b), \
|
|
}
|
|
|
|
/*
|
|
* Define main muxing pin groups
|
|
*/
|
|
|
|
/* submuxes */
|
|
|
|
/* name f0, f1, f2, f3, f4, mux r/b/w */
|
|
DEFINE_SUBMUX(ext_dac, DAC, NOT_IQADC_STB, IQDAC_STB, NA, NA, IF_CTL, 6, 2);
|
|
|
|
/* bind submuxes to internal functions */
|
|
static struct tz1090_muxdesc *tz1090_submux[] = {
|
|
SUBMUX(EXT_DAC, ext_dac),
|
|
};
|
|
|
|
/*
|
|
* These are the pin mux groups. Pin muxing can be enabled and disabled for each
|
|
* pin individually so these groups are internal. The mapping of pins to pin mux
|
|
* group is below (tz1090_mux_pins).
|
|
*/
|
|
static struct tz1090_pingroup tz1090_mux_groups[] = {
|
|
/* Muxing pin groups */
|
|
/* pg_name, f0, f1, f2, f3, f4, mux r/b/w */
|
|
MUX_PG(sdh, SDH, SDIO, NA, NA, NA, IF_CTL, 20, 2),
|
|
MUX_PG(sdio, SDIO, SDH, NA, NA, NA, IF_CTL, 16, 2),
|
|
MUX_PG(spi1_cs2, SPI1_CS2, USB_VBUS, NA, NA, NA, IF_CTL, 10, 2),
|
|
MUX_PG(pdm_d, PDM_DAC, USB_VBUS, NA, NA, NA, IF_CTL, 8, 2),
|
|
MUX_PG(afe, AFE, TS_OUT_0, NA, NA, NA, IF_CTL, 4, 2),
|
|
MUX_PG(tft, TFT, EXT_DAC, TS_OUT_1, LCD_TRACE, PHY_RINGOSC, IF_CTL, 0, 3),
|
|
};
|
|
|
|
/*
|
|
* This is the mapping from GPIO pins to pin mux groups in tz1090_mux_groups[].
|
|
* Pins which aren't muxable to multiple peripherals are set to
|
|
* TZ1090_MUX_GROUP_MAX to enable the "perip" function to enable/disable
|
|
* peripheral control of the pin.
|
|
*
|
|
* This array is initialised in tz1090_init_mux_pins().
|
|
*/
|
|
static u8 tz1090_mux_pins[NUM_GPIOS];
|
|
|
|
/* TZ1090_MUX_GROUP_MAX is used in tz1090_mux_pins[] for non-muxing pins */
|
|
#define TZ1090_MUX_GROUP_MAX ARRAY_SIZE(tz1090_mux_groups)
|
|
|
|
/**
|
|
* tz1090_init_mux_pins() - Initialise GPIO pin to mux group mapping.
|
|
*
|
|
* Initialises the tz1090_mux_pins[] array to be the inverse of the pin lists in
|
|
* each pin mux group in tz1090_mux_groups[].
|
|
*
|
|
* It is assumed that no pin mux groups overlap (share pins).
|
|
*/
|
|
static void __init tz1090_init_mux_pins(void)
|
|
{
|
|
unsigned int g, p;
|
|
const struct tz1090_pingroup *grp;
|
|
const unsigned int *pin;
|
|
|
|
for (p = 0; p < NUM_GPIOS; ++p)
|
|
tz1090_mux_pins[p] = TZ1090_MUX_GROUP_MAX;
|
|
|
|
grp = tz1090_mux_groups;
|
|
for (g = 0, grp = tz1090_mux_groups;
|
|
g < ARRAY_SIZE(tz1090_mux_groups); ++g, ++grp)
|
|
for (pin = grp->pins, p = 0; p < grp->npins; ++p, ++pin)
|
|
tz1090_mux_pins[*pin] = g;
|
|
}
|
|
|
|
/*
|
|
* These are the externally visible pin groups. Some of them allow group control
|
|
* of drive configuration. Some are just simple convenience pingroups. All the
|
|
* internal pin mux groups in tz1090_mux_groups[] are mirrored here with the
|
|
* same pins.
|
|
* Pseudo pin groups follow in the group numbers after this array for each GPIO
|
|
* pin. Any group used for muxing must have all pins belonging to the same pin
|
|
* mux group.
|
|
*/
|
|
static struct tz1090_pingroup tz1090_groups[] = {
|
|
/* Pin groups with drive control (with no out of place pins) */
|
|
/* pg_name, slw/schmitt/drv b */
|
|
DRV_PG(jtag, 11 /* 11, 22 */),
|
|
DRV_PG(tft, 10 /* 10, 20 */),
|
|
DRV_PG(scb2, 9 /* 9, 18 */),
|
|
DRV_PG(spi0, 7 /* 7, 14 */),
|
|
DRV_PG(uart, 5 /* 5, 10 */),
|
|
DRV_PG(scb1, 4 /* 4, 8 */),
|
|
DRV_PG(spi1, 3 /* 3, 6 */),
|
|
DRV_PG(afe, 0 /* 0, 0 */),
|
|
|
|
/*
|
|
* Drive specific pin groups (with odd combinations of pins which makes
|
|
* the pin group naming somewhat arbitrary)
|
|
*/
|
|
/* pg_name, slw/schmitt/drv b */
|
|
DRV_PG(drive_sdio, 8 /* 8, 16 */), /* sdio_* + sdh_* */
|
|
DRV_PG(drive_i2s, 6 /* 6, 12 */), /* i2s_* + clk_out1 */
|
|
DRV_PG(drive_scb0, 2 /* 2, 4 */), /* scb0_* + pdm_{c,d} */
|
|
DRV_PG(drive_pdm, 1 /* 1, 2 */), /* pdm_{a,b} + clk_out0 */
|
|
|
|
/* Convenience pin groups */
|
|
/* pg_name */
|
|
SIMPLE_PG(uart0),
|
|
SIMPLE_PG(uart1),
|
|
SIMPLE_PG(scb0),
|
|
SIMPLE_PG(i2s),
|
|
SIMPLE_PG(sdh),
|
|
SIMPLE_PG(sdio),
|
|
|
|
/* pseudo-pingroups for each GPIO pin follow */
|
|
};
|
|
|
|
/**
|
|
* struct tz1090_pmx - Private pinctrl data
|
|
* @dev: Platform device
|
|
* @pctl: Pin control device
|
|
* @regs: Register region
|
|
* @lock: Lock protecting coherency of pin_en, gpio_en, and SELECT regs
|
|
* @pin_en: Pins that have been enabled (32 pins packed into each element)
|
|
* @gpio_en: GPIOs that have been enabled (32 pins packed into each element)
|
|
*/
|
|
struct tz1090_pmx {
|
|
struct device *dev;
|
|
struct pinctrl_dev *pctl;
|
|
void __iomem *regs;
|
|
spinlock_t lock;
|
|
u32 pin_en[3];
|
|
u32 gpio_en[3];
|
|
};
|
|
|
|
static inline u32 pmx_read(struct tz1090_pmx *pmx, u32 reg)
|
|
{
|
|
return ioread32(pmx->regs + reg);
|
|
}
|
|
|
|
static inline void pmx_write(struct tz1090_pmx *pmx, u32 val, u32 reg)
|
|
{
|
|
iowrite32(val, pmx->regs + reg);
|
|
}
|
|
|
|
/*
|
|
* Pin control operations
|
|
*/
|
|
|
|
/* each GPIO pin has it's own pseudo pingroup containing only itself */
|
|
|
|
static int tz1090_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
|
|
{
|
|
return ARRAY_SIZE(tz1090_groups) + NUM_GPIOS;
|
|
}
|
|
|
|
static const char *tz1090_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
|
|
unsigned int group)
|
|
{
|
|
if (group < ARRAY_SIZE(tz1090_groups)) {
|
|
/* normal pingroup */
|
|
return tz1090_groups[group].name;
|
|
} else {
|
|
/* individual gpio pin pseudo-pingroup */
|
|
unsigned int pin = group - ARRAY_SIZE(tz1090_groups);
|
|
return tz1090_pins[pin].name;
|
|
}
|
|
}
|
|
|
|
static int tz1090_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
|
|
unsigned int group,
|
|
const unsigned int **pins,
|
|
unsigned int *num_pins)
|
|
{
|
|
if (group < ARRAY_SIZE(tz1090_groups)) {
|
|
/* normal pingroup */
|
|
*pins = tz1090_groups[group].pins;
|
|
*num_pins = tz1090_groups[group].npins;
|
|
} else {
|
|
/* individual gpio pin pseudo-pingroup */
|
|
unsigned int pin = group - ARRAY_SIZE(tz1090_groups);
|
|
*pins = &tz1090_pins[pin].number;
|
|
*num_pins = 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
static void tz1090_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev,
|
|
struct seq_file *s,
|
|
unsigned int offset)
|
|
{
|
|
seq_printf(s, " %s", dev_name(pctldev->dev));
|
|
}
|
|
#endif
|
|
|
|
static int reserve_map(struct device *dev, struct pinctrl_map **map,
|
|
unsigned int *reserved_maps, unsigned int *num_maps,
|
|
unsigned int reserve)
|
|
{
|
|
unsigned int old_num = *reserved_maps;
|
|
unsigned int new_num = *num_maps + reserve;
|
|
struct pinctrl_map *new_map;
|
|
|
|
if (old_num >= new_num)
|
|
return 0;
|
|
|
|
new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL);
|
|
if (!new_map) {
|
|
dev_err(dev, "krealloc(map) failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map));
|
|
|
|
*map = new_map;
|
|
*reserved_maps = new_num;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int add_map_mux(struct pinctrl_map **map, unsigned int *reserved_maps,
|
|
unsigned int *num_maps, const char *group,
|
|
const char *function)
|
|
{
|
|
if (WARN_ON(*num_maps == *reserved_maps))
|
|
return -ENOSPC;
|
|
|
|
(*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP;
|
|
(*map)[*num_maps].data.mux.group = group;
|
|
(*map)[*num_maps].data.mux.function = function;
|
|
(*num_maps)++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int add_map_configs(struct device *dev,
|
|
struct pinctrl_map **map,
|
|
unsigned int *reserved_maps, unsigned int *num_maps,
|
|
const char *group, unsigned long *configs,
|
|
unsigned int num_configs)
|
|
{
|
|
unsigned long *dup_configs;
|
|
|
|
if (WARN_ON(*num_maps == *reserved_maps))
|
|
return -ENOSPC;
|
|
|
|
dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs),
|
|
GFP_KERNEL);
|
|
if (!dup_configs) {
|
|
dev_err(dev, "kmemdup(configs) failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
(*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_GROUP;
|
|
(*map)[*num_maps].data.configs.group_or_pin = group;
|
|
(*map)[*num_maps].data.configs.configs = dup_configs;
|
|
(*map)[*num_maps].data.configs.num_configs = num_configs;
|
|
(*num_maps)++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void tz1090_pinctrl_dt_free_map(struct pinctrl_dev *pctldev,
|
|
struct pinctrl_map *map,
|
|
unsigned int num_maps)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < num_maps; i++)
|
|
if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP)
|
|
kfree(map[i].data.configs.configs);
|
|
|
|
kfree(map);
|
|
}
|
|
|
|
static int tz1090_pinctrl_dt_subnode_to_map(struct device *dev,
|
|
struct device_node *np,
|
|
struct pinctrl_map **map,
|
|
unsigned int *reserved_maps,
|
|
unsigned int *num_maps)
|
|
{
|
|
int ret;
|
|
const char *function;
|
|
unsigned long *configs = NULL;
|
|
unsigned int num_configs = 0;
|
|
unsigned int reserve;
|
|
struct property *prop;
|
|
const char *group;
|
|
|
|
ret = of_property_read_string(np, "tz1090,function", &function);
|
|
if (ret < 0) {
|
|
/* EINVAL=missing, which is fine since it's optional */
|
|
if (ret != -EINVAL)
|
|
dev_err(dev, "could not parse property function\n");
|
|
function = NULL;
|
|
}
|
|
|
|
ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs);
|
|
if (ret)
|
|
return ret;
|
|
|
|
reserve = 0;
|
|
if (function != NULL)
|
|
reserve++;
|
|
if (num_configs)
|
|
reserve++;
|
|
ret = of_property_count_strings(np, "tz1090,pins");
|
|
if (ret < 0) {
|
|
dev_err(dev, "could not parse property pins\n");
|
|
goto exit;
|
|
}
|
|
reserve *= ret;
|
|
|
|
ret = reserve_map(dev, map, reserved_maps, num_maps, reserve);
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
of_property_for_each_string(np, "tz1090,pins", prop, group) {
|
|
if (function) {
|
|
ret = add_map_mux(map, reserved_maps, num_maps,
|
|
group, function);
|
|
if (ret < 0)
|
|
goto exit;
|
|
}
|
|
|
|
if (num_configs) {
|
|
ret = add_map_configs(dev, map, reserved_maps,
|
|
num_maps, group, configs,
|
|
num_configs);
|
|
if (ret < 0)
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
exit:
|
|
kfree(configs);
|
|
return ret;
|
|
}
|
|
|
|
static int tz1090_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
|
|
struct device_node *np_config,
|
|
struct pinctrl_map **map,
|
|
unsigned int *num_maps)
|
|
{
|
|
unsigned int reserved_maps;
|
|
struct device_node *np;
|
|
int ret;
|
|
|
|
reserved_maps = 0;
|
|
*map = NULL;
|
|
*num_maps = 0;
|
|
|
|
for_each_child_of_node(np_config, np) {
|
|
ret = tz1090_pinctrl_dt_subnode_to_map(pctldev->dev, np, map,
|
|
&reserved_maps,
|
|
num_maps);
|
|
if (ret < 0) {
|
|
tz1090_pinctrl_dt_free_map(pctldev, *map, *num_maps);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct pinctrl_ops tz1090_pinctrl_ops = {
|
|
.get_groups_count = tz1090_pinctrl_get_groups_count,
|
|
.get_group_name = tz1090_pinctrl_get_group_name,
|
|
.get_group_pins = tz1090_pinctrl_get_group_pins,
|
|
#ifdef CONFIG_DEBUG_FS
|
|
.pin_dbg_show = tz1090_pinctrl_pin_dbg_show,
|
|
#endif
|
|
.dt_node_to_map = tz1090_pinctrl_dt_node_to_map,
|
|
.dt_free_map = tz1090_pinctrl_dt_free_map,
|
|
};
|
|
|
|
/*
|
|
* Pin mux operations
|
|
*/
|
|
|
|
static int tz1090_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev)
|
|
{
|
|
return ARRAY_SIZE(tz1090_functions);
|
|
}
|
|
|
|
static const char *tz1090_pinctrl_get_func_name(struct pinctrl_dev *pctldev,
|
|
unsigned int function)
|
|
{
|
|
return tz1090_functions[function].name;
|
|
}
|
|
|
|
static int tz1090_pinctrl_get_func_groups(struct pinctrl_dev *pctldev,
|
|
unsigned int function,
|
|
const char * const **groups,
|
|
unsigned int * const num_groups)
|
|
{
|
|
/* pingroup functions */
|
|
*groups = tz1090_functions[function].groups;
|
|
*num_groups = tz1090_functions[function].ngroups;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* tz1090_pinctrl_select() - update bit in SELECT register
|
|
* @pmx: Pinmux data
|
|
* @pin: Pin number (must be within GPIO range)
|
|
*/
|
|
static void tz1090_pinctrl_select(struct tz1090_pmx *pmx,
|
|
unsigned int pin)
|
|
{
|
|
u32 reg, reg_shift, select, val;
|
|
unsigned int pmx_index, pmx_shift;
|
|
unsigned long flags;
|
|
|
|
/* uses base 32 instead of base 30 */
|
|
pmx_index = pin >> 5;
|
|
pmx_shift = pin & 0x1f;
|
|
|
|
/* select = !perip || gpio */
|
|
select = ((~pmx->pin_en[pmx_index] |
|
|
pmx->gpio_en[pmx_index]) >> pmx_shift) & 1;
|
|
|
|
/* find register and bit offset (base 30) */
|
|
reg = REG_PINCTRL_SELECT + 4*(pin / 30);
|
|
reg_shift = pin % 30;
|
|
|
|
/* modify gpio select bit */
|
|
__global_lock2(flags);
|
|
val = pmx_read(pmx, reg);
|
|
val &= ~BIT(reg_shift);
|
|
val |= select << reg_shift;
|
|
pmx_write(pmx, val, reg);
|
|
__global_unlock2(flags);
|
|
}
|
|
|
|
/**
|
|
* tz1090_pinctrl_gpio_select() - enable/disable GPIO usage for a pin
|
|
* @pmx: Pinmux data
|
|
* @pin: Pin number
|
|
* @gpio_select: true to enable pin as GPIO,
|
|
* false to leave control to whatever function is enabled
|
|
*
|
|
* Records that GPIO usage is enabled/disabled so that enabling a function
|
|
* doesn't override the SELECT register bit.
|
|
*/
|
|
static void tz1090_pinctrl_gpio_select(struct tz1090_pmx *pmx,
|
|
unsigned int pin,
|
|
bool gpio_select)
|
|
{
|
|
unsigned int index, shift;
|
|
u32 gpio_en;
|
|
|
|
if (pin >= NUM_GPIOS)
|
|
return;
|
|
|
|
/* uses base 32 instead of base 30 */
|
|
index = pin >> 5;
|
|
shift = pin & 0x1f;
|
|
|
|
spin_lock(&pmx->lock);
|
|
|
|
/* keep a record whether gpio is selected */
|
|
gpio_en = pmx->gpio_en[index];
|
|
gpio_en &= ~BIT(shift);
|
|
if (gpio_select)
|
|
gpio_en |= BIT(shift);
|
|
pmx->gpio_en[index] = gpio_en;
|
|
|
|
/* update the select bit */
|
|
tz1090_pinctrl_select(pmx, pin);
|
|
|
|
spin_unlock(&pmx->lock);
|
|
}
|
|
|
|
/**
|
|
* tz1090_pinctrl_perip_select() - enable/disable peripheral interface for a pin
|
|
* @pmx: Pinmux data
|
|
* @pin: Pin number
|
|
* @perip_select: true to enable peripheral interface when not GPIO,
|
|
* false to leave pin in GPIO mode
|
|
*
|
|
* Records that peripheral usage is enabled/disabled so that SELECT register can
|
|
* be set appropriately when GPIO is disabled.
|
|
*/
|
|
static void tz1090_pinctrl_perip_select(struct tz1090_pmx *pmx,
|
|
unsigned int pin,
|
|
bool perip_select)
|
|
{
|
|
unsigned int index, shift;
|
|
u32 pin_en;
|
|
|
|
if (pin >= NUM_GPIOS)
|
|
return;
|
|
|
|
/* uses base 32 instead of base 30 */
|
|
index = pin >> 5;
|
|
shift = pin & 0x1f;
|
|
|
|
spin_lock(&pmx->lock);
|
|
|
|
/* keep a record whether peripheral is selected */
|
|
pin_en = pmx->pin_en[index];
|
|
pin_en &= ~BIT(shift);
|
|
if (perip_select)
|
|
pin_en |= BIT(shift);
|
|
pmx->pin_en[index] = pin_en;
|
|
|
|
/* update the select bit */
|
|
tz1090_pinctrl_select(pmx, pin);
|
|
|
|
spin_unlock(&pmx->lock);
|
|
}
|
|
|
|
/**
|
|
* tz1090_pinctrl_enable_mux() - Switch a pin mux group to a function.
|
|
* @pmx: Pinmux data
|
|
* @desc: Pinmux description
|
|
* @function: Function to switch to
|
|
*
|
|
* Enable a particular function on a pin mux group. Since pin mux descriptions
|
|
* are nested this function is recursive.
|
|
*/
|
|
static int tz1090_pinctrl_enable_mux(struct tz1090_pmx *pmx,
|
|
const struct tz1090_muxdesc *desc,
|
|
unsigned int function)
|
|
{
|
|
const int *fit;
|
|
unsigned long flags;
|
|
int mux;
|
|
unsigned int func, ret;
|
|
u32 reg, mask;
|
|
|
|
/* find the mux value for this function, searching recursively */
|
|
for (mux = 0, fit = desc->funcs;
|
|
mux < ARRAY_SIZE(desc->funcs); ++mux, ++fit) {
|
|
func = *fit;
|
|
if (func == function)
|
|
goto found_mux;
|
|
|
|
/* maybe it's a sub-mux */
|
|
if (func < ARRAY_SIZE(tz1090_submux) && tz1090_submux[func]) {
|
|
ret = tz1090_pinctrl_enable_mux(pmx,
|
|
tz1090_submux[func],
|
|
function);
|
|
if (!ret)
|
|
goto found_mux;
|
|
}
|
|
}
|
|
|
|
return -EINVAL;
|
|
found_mux:
|
|
|
|
/* Set up the mux */
|
|
if (desc->width) {
|
|
mask = (BIT(desc->width) - 1) << desc->bit;
|
|
__global_lock2(flags);
|
|
reg = pmx_read(pmx, desc->reg);
|
|
reg &= ~mask;
|
|
reg |= (mux << desc->bit) & mask;
|
|
pmx_write(pmx, reg, desc->reg);
|
|
__global_unlock2(flags);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* tz1090_pinctrl_enable() - Enable a function on a pin group.
|
|
* @pctldev: Pin control data
|
|
* @function: Function index to enable
|
|
* @group: Group index to enable
|
|
*
|
|
* Enable a particular function on a group of pins. The per GPIO pin pseudo pin
|
|
* groups can be used (in which case the pin will be enabled in peripheral mode
|
|
* and if it belongs to a pin mux group the mux will be switched if it isn't
|
|
* already in use. Some convenience pin groups can also be used in which case
|
|
* the effect is the same as enabling the function on each individual pin in the
|
|
* group.
|
|
*/
|
|
static int tz1090_pinctrl_enable(struct pinctrl_dev *pctldev,
|
|
unsigned int function, unsigned int group)
|
|
{
|
|
struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
|
|
struct tz1090_pingroup *grp;
|
|
int ret;
|
|
unsigned int pin_num, mux_group, i, npins;
|
|
const unsigned int *pins;
|
|
|
|
/* group of pins? */
|
|
if (group < ARRAY_SIZE(tz1090_groups)) {
|
|
grp = &tz1090_groups[group];
|
|
npins = grp->npins;
|
|
pins = grp->pins;
|
|
/*
|
|
* All pins in the group must belong to the same mux group,
|
|
* which allows us to just use the mux group of the first pin.
|
|
* By explicitly listing permitted pingroups for each function
|
|
* the pinmux core should ensure this is always the case.
|
|
*/
|
|
} else {
|
|
pin_num = group - ARRAY_SIZE(tz1090_groups);
|
|
npins = 1;
|
|
pins = &pin_num;
|
|
}
|
|
mux_group = tz1090_mux_pins[*pins];
|
|
|
|
/* no mux group, but can still be individually muxed to peripheral */
|
|
if (mux_group >= TZ1090_MUX_GROUP_MAX) {
|
|
if (function == TZ1090_MUX_PERIP)
|
|
goto mux_pins;
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* mux group already set to a different function? */
|
|
grp = &tz1090_mux_groups[mux_group];
|
|
if (grp->func_count && grp->func != function) {
|
|
dev_err(pctldev->dev,
|
|
"%s: can't mux pin(s) to '%s', group already muxed to '%s'\n",
|
|
__func__, tz1090_functions[function].name,
|
|
tz1090_functions[grp->func].name);
|
|
return -EBUSY;
|
|
}
|
|
|
|
dev_dbg(pctldev->dev, "%s: muxing %u pin(s) in '%s' to '%s'\n",
|
|
__func__, npins, grp->name, tz1090_functions[function].name);
|
|
|
|
/* if first pin in mux group to be enabled, enable the group mux */
|
|
if (!grp->func_count) {
|
|
grp->func = function;
|
|
ret = tz1090_pinctrl_enable_mux(pmx, &grp->mux, function);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
/* add pins to ref count and mux individually to peripheral */
|
|
grp->func_count += npins;
|
|
mux_pins:
|
|
for (i = 0; i < npins; ++i)
|
|
tz1090_pinctrl_perip_select(pmx, pins[i], true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* tz1090_pinctrl_gpio_request_enable() - Put pin in GPIO mode.
|
|
* @pctldev: Pin control data
|
|
* @range: GPIO range
|
|
* @pin: Pin number
|
|
*
|
|
* Puts a particular pin into GPIO mode, disabling peripheral control until it's
|
|
* disabled again.
|
|
*/
|
|
static int tz1090_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev,
|
|
struct pinctrl_gpio_range *range,
|
|
unsigned int pin)
|
|
{
|
|
struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
|
|
tz1090_pinctrl_gpio_select(pmx, pin, true);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* tz1090_pinctrl_gpio_disable_free() - Take pin out of GPIO mode.
|
|
* @pctldev: Pin control data
|
|
* @range: GPIO range
|
|
* @pin: Pin number
|
|
*
|
|
* Take a particular pin out of GPIO mode. If the pin is enabled for a
|
|
* peripheral it will return to peripheral mode.
|
|
*/
|
|
static void tz1090_pinctrl_gpio_disable_free(struct pinctrl_dev *pctldev,
|
|
struct pinctrl_gpio_range *range,
|
|
unsigned int pin)
|
|
{
|
|
struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
|
|
tz1090_pinctrl_gpio_select(pmx, pin, false);
|
|
}
|
|
|
|
static struct pinmux_ops tz1090_pinmux_ops = {
|
|
.get_functions_count = tz1090_pinctrl_get_funcs_count,
|
|
.get_function_name = tz1090_pinctrl_get_func_name,
|
|
.get_function_groups = tz1090_pinctrl_get_func_groups,
|
|
.enable = tz1090_pinctrl_enable,
|
|
.gpio_request_enable = tz1090_pinctrl_gpio_request_enable,
|
|
.gpio_disable_free = tz1090_pinctrl_gpio_disable_free,
|
|
};
|
|
|
|
/*
|
|
* Pin config operations
|
|
*/
|
|
|
|
struct tz1090_pinconf_pullup {
|
|
unsigned char index;
|
|
unsigned char shift;
|
|
};
|
|
|
|
/* The mapping of pin to pull up/down register index and shift */
|
|
static struct tz1090_pinconf_pullup tz1090_pinconf_pullup[] = {
|
|
{5, 22}, /* 0 - TZ1090_PIN_SDIO_CLK */
|
|
{0, 14}, /* 1 - TZ1090_PIN_SDIO_CMD */
|
|
{0, 6}, /* 2 - TZ1090_PIN_SDIO_D0 */
|
|
{0, 8}, /* 3 - TZ1090_PIN_SDIO_D1 */
|
|
{0, 10}, /* 4 - TZ1090_PIN_SDIO_D2 */
|
|
{0, 12}, /* 5 - TZ1090_PIN_SDIO_D3 */
|
|
{0, 2}, /* 6 - TZ1090_PIN_SDH_CD */
|
|
{0, 4}, /* 7 - TZ1090_PIN_SDH_WP */
|
|
{0, 16}, /* 8 - TZ1090_PIN_SPI0_MCLK */
|
|
{0, 18}, /* 9 - TZ1090_PIN_SPI0_CS0 */
|
|
{0, 20}, /* 10 - TZ1090_PIN_SPI0_CS1 */
|
|
{0, 22}, /* 11 - TZ1090_PIN_SPI0_CS2 */
|
|
{0, 24}, /* 12 - TZ1090_PIN_SPI0_DOUT */
|
|
{0, 26}, /* 13 - TZ1090_PIN_SPI0_DIN */
|
|
{0, 28}, /* 14 - TZ1090_PIN_SPI1_MCLK */
|
|
{0, 30}, /* 15 - TZ1090_PIN_SPI1_CS0 */
|
|
{1, 0}, /* 16 - TZ1090_PIN_SPI1_CS1 */
|
|
{1, 2}, /* 17 - TZ1090_PIN_SPI1_CS2 */
|
|
{1, 4}, /* 18 - TZ1090_PIN_SPI1_DOUT */
|
|
{1, 6}, /* 19 - TZ1090_PIN_SPI1_DIN */
|
|
{1, 8}, /* 20 - TZ1090_PIN_UART0_RXD */
|
|
{1, 10}, /* 21 - TZ1090_PIN_UART0_TXD */
|
|
{1, 12}, /* 22 - TZ1090_PIN_UART0_CTS */
|
|
{1, 14}, /* 23 - TZ1090_PIN_UART0_RTS */
|
|
{1, 16}, /* 24 - TZ1090_PIN_UART1_RXD */
|
|
{1, 18}, /* 25 - TZ1090_PIN_UART1_TXD */
|
|
{1, 20}, /* 26 - TZ1090_PIN_SCB0_SDAT */
|
|
{1, 22}, /* 27 - TZ1090_PIN_SCB0_SCLK */
|
|
{1, 24}, /* 28 - TZ1090_PIN_SCB1_SDAT */
|
|
{1, 26}, /* 29 - TZ1090_PIN_SCB1_SCLK */
|
|
|
|
{1, 28}, /* 30 - TZ1090_PIN_SCB2_SDAT */
|
|
{1, 30}, /* 31 - TZ1090_PIN_SCB2_SCLK */
|
|
{2, 0}, /* 32 - TZ1090_PIN_I2S_MCLK */
|
|
{2, 2}, /* 33 - TZ1090_PIN_I2S_BCLK_OUT */
|
|
{2, 4}, /* 34 - TZ1090_PIN_I2S_LRCLK_OUT */
|
|
{2, 6}, /* 35 - TZ1090_PIN_I2S_DOUT0 */
|
|
{2, 8}, /* 36 - TZ1090_PIN_I2S_DOUT1 */
|
|
{2, 10}, /* 37 - TZ1090_PIN_I2S_DOUT2 */
|
|
{2, 12}, /* 38 - TZ1090_PIN_I2S_DIN */
|
|
{4, 12}, /* 39 - TZ1090_PIN_PDM_A */
|
|
{4, 14}, /* 40 - TZ1090_PIN_PDM_B */
|
|
{4, 18}, /* 41 - TZ1090_PIN_PDM_C */
|
|
{4, 20}, /* 42 - TZ1090_PIN_PDM_D */
|
|
{2, 14}, /* 43 - TZ1090_PIN_TFT_RED0 */
|
|
{2, 16}, /* 44 - TZ1090_PIN_TFT_RED1 */
|
|
{2, 18}, /* 45 - TZ1090_PIN_TFT_RED2 */
|
|
{2, 20}, /* 46 - TZ1090_PIN_TFT_RED3 */
|
|
{2, 22}, /* 47 - TZ1090_PIN_TFT_RED4 */
|
|
{2, 24}, /* 48 - TZ1090_PIN_TFT_RED5 */
|
|
{2, 26}, /* 49 - TZ1090_PIN_TFT_RED6 */
|
|
{2, 28}, /* 50 - TZ1090_PIN_TFT_RED7 */
|
|
{2, 30}, /* 51 - TZ1090_PIN_TFT_GREEN0 */
|
|
{3, 0}, /* 52 - TZ1090_PIN_TFT_GREEN1 */
|
|
{3, 2}, /* 53 - TZ1090_PIN_TFT_GREEN2 */
|
|
{3, 4}, /* 54 - TZ1090_PIN_TFT_GREEN3 */
|
|
{3, 6}, /* 55 - TZ1090_PIN_TFT_GREEN4 */
|
|
{3, 8}, /* 56 - TZ1090_PIN_TFT_GREEN5 */
|
|
{3, 10}, /* 57 - TZ1090_PIN_TFT_GREEN6 */
|
|
{3, 12}, /* 58 - TZ1090_PIN_TFT_GREEN7 */
|
|
{3, 14}, /* 59 - TZ1090_PIN_TFT_BLUE0 */
|
|
|
|
{3, 16}, /* 60 - TZ1090_PIN_TFT_BLUE1 */
|
|
{3, 18}, /* 61 - TZ1090_PIN_TFT_BLUE2 */
|
|
{3, 20}, /* 62 - TZ1090_PIN_TFT_BLUE3 */
|
|
{3, 22}, /* 63 - TZ1090_PIN_TFT_BLUE4 */
|
|
{3, 24}, /* 64 - TZ1090_PIN_TFT_BLUE5 */
|
|
{3, 26}, /* 65 - TZ1090_PIN_TFT_BLUE6 */
|
|
{3, 28}, /* 66 - TZ1090_PIN_TFT_BLUE7 */
|
|
{3, 30}, /* 67 - TZ1090_PIN_TFT_VDDEN_GD */
|
|
{4, 0}, /* 68 - TZ1090_PIN_TFT_PANELCLK */
|
|
{4, 2}, /* 69 - TZ1090_PIN_TFT_BLANK_LS */
|
|
{4, 4}, /* 70 - TZ1090_PIN_TFT_VSYNC_NS */
|
|
{4, 6}, /* 71 - TZ1090_PIN_TFT_HSYNC_NR */
|
|
{4, 8}, /* 72 - TZ1090_PIN_TFT_VD12ACB */
|
|
{4, 10}, /* 73 - TZ1090_PIN_TFT_PWRSAVE */
|
|
{4, 24}, /* 74 - TZ1090_PIN_TX_ON */
|
|
{4, 26}, /* 75 - TZ1090_PIN_RX_ON */
|
|
{4, 28}, /* 76 - TZ1090_PIN_PLL_ON */
|
|
{4, 30}, /* 77 - TZ1090_PIN_PA_ON */
|
|
{5, 0}, /* 78 - TZ1090_PIN_RX_HP */
|
|
{5, 6}, /* 79 - TZ1090_PIN_GAIN0 */
|
|
{5, 8}, /* 80 - TZ1090_PIN_GAIN1 */
|
|
{5, 10}, /* 81 - TZ1090_PIN_GAIN2 */
|
|
{5, 12}, /* 82 - TZ1090_PIN_GAIN3 */
|
|
{5, 14}, /* 83 - TZ1090_PIN_GAIN4 */
|
|
{5, 16}, /* 84 - TZ1090_PIN_GAIN5 */
|
|
{5, 18}, /* 85 - TZ1090_PIN_GAIN6 */
|
|
{5, 20}, /* 86 - TZ1090_PIN_GAIN7 */
|
|
{5, 2}, /* 87 - TZ1090_PIN_ANT_SEL0 */
|
|
{5, 4}, /* 88 - TZ1090_PIN_ANT_SEL1 */
|
|
{0, 0}, /* 89 - TZ1090_PIN_SDH_CLK_IN */
|
|
|
|
{5, 24}, /* 90 - TZ1090_PIN_TCK */
|
|
{5, 26}, /* 91 - TZ1090_PIN_TRST */
|
|
{5, 28}, /* 92 - TZ1090_PIN_TDI */
|
|
{5, 30}, /* 93 - TZ1090_PIN_TDO */
|
|
{6, 0}, /* 94 - TZ1090_PIN_TMS */
|
|
{4, 16}, /* 95 - TZ1090_PIN_CLK_OUT0 */
|
|
{4, 22}, /* 96 - TZ1090_PIN_CLK_OUT1 */
|
|
};
|
|
|
|
static int tz1090_pinconf_reg(struct pinctrl_dev *pctldev,
|
|
unsigned int pin,
|
|
enum pin_config_param param,
|
|
bool report_err,
|
|
u32 *reg, u32 *width, u32 *mask, u32 *shift,
|
|
u32 *val)
|
|
{
|
|
struct tz1090_pinconf_pullup *pu;
|
|
|
|
/* All supported pins have controllable input bias */
|
|
switch (param) {
|
|
case PIN_CONFIG_BIAS_DISABLE:
|
|
case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
|
|
*val = REG_PU_PD_TRISTATE;
|
|
break;
|
|
case PIN_CONFIG_BIAS_PULL_UP:
|
|
*val = REG_PU_PD_UP;
|
|
break;
|
|
case PIN_CONFIG_BIAS_PULL_DOWN:
|
|
*val = REG_PU_PD_DOWN;
|
|
break;
|
|
case PIN_CONFIG_BIAS_BUS_HOLD:
|
|
*val = REG_PU_PD_REPEATER;
|
|
break;
|
|
default:
|
|
return -ENOTSUPP;
|
|
};
|
|
|
|
/* Only input bias parameters supported */
|
|
pu = &tz1090_pinconf_pullup[pin];
|
|
*reg = REG_PINCTRL_PU_PD + 4*pu->index;
|
|
*shift = pu->shift;
|
|
*width = 2;
|
|
|
|
/* Calculate field information */
|
|
*mask = (BIT(*width) - 1) << *shift;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tz1090_pinconf_get(struct pinctrl_dev *pctldev,
|
|
unsigned int pin, unsigned long *config)
|
|
{
|
|
struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
|
|
enum pin_config_param param = pinconf_to_config_param(*config);
|
|
int ret;
|
|
u32 reg, width, mask, shift, val, tmp, arg;
|
|
|
|
/* Get register information */
|
|
ret = tz1090_pinconf_reg(pctldev, pin, param, true,
|
|
®, &width, &mask, &shift, &val);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Extract field from register */
|
|
tmp = pmx_read(pmx, reg);
|
|
arg = ((tmp & mask) >> shift) == val;
|
|
|
|
/* Config not active */
|
|
if (!arg)
|
|
return -EINVAL;
|
|
|
|
/* And pack config */
|
|
*config = pinconf_to_config_packed(param, arg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tz1090_pinconf_set(struct pinctrl_dev *pctldev,
|
|
unsigned int pin, unsigned long *configs,
|
|
unsigned num_configs)
|
|
{
|
|
struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
|
|
enum pin_config_param param;
|
|
unsigned int arg;
|
|
int ret;
|
|
u32 reg, width, mask, shift, val, tmp;
|
|
unsigned long flags;
|
|
int i;
|
|
|
|
for (i = 0; i < num_configs; i++) {
|
|
param = pinconf_to_config_param(configs[i]);
|
|
arg = pinconf_to_config_argument(configs[i]);
|
|
|
|
dev_dbg(pctldev->dev, "%s(pin=%s, config=%#lx)\n",
|
|
__func__, tz1090_pins[pin].name, configs[i]);
|
|
|
|
/* Get register information */
|
|
ret = tz1090_pinconf_reg(pctldev, pin, param, true,
|
|
®, &width, &mask, &shift, &val);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Unpack argument and range check it */
|
|
if (arg > 1) {
|
|
dev_dbg(pctldev->dev, "%s: arg %u out of range\n",
|
|
__func__, arg);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Write register field */
|
|
__global_lock2(flags);
|
|
tmp = pmx_read(pmx, reg);
|
|
tmp &= ~mask;
|
|
if (arg)
|
|
tmp |= val << shift;
|
|
pmx_write(pmx, tmp, reg);
|
|
__global_unlock2(flags);
|
|
} /* for each config */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const int tz1090_boolean_map[] = {
|
|
[0] = -EINVAL,
|
|
[1] = 1,
|
|
};
|
|
|
|
static const int tz1090_dr_map[] = {
|
|
[REG_DR_2mA] = 2,
|
|
[REG_DR_4mA] = 4,
|
|
[REG_DR_8mA] = 8,
|
|
[REG_DR_12mA] = 12,
|
|
};
|
|
|
|
static int tz1090_pinconf_group_reg(struct pinctrl_dev *pctldev,
|
|
const struct tz1090_pingroup *g,
|
|
enum pin_config_param param,
|
|
bool report_err,
|
|
u32 *reg, u32 *width, u32 *mask, u32 *shift,
|
|
const int **map)
|
|
{
|
|
/* Drive configuration applies in groups, but not to all groups. */
|
|
if (!g->drv) {
|
|
if (report_err)
|
|
dev_dbg(pctldev->dev,
|
|
"%s: group %s has no drive control\n",
|
|
__func__, g->name);
|
|
return -ENOTSUPP;
|
|
}
|
|
|
|
/* Find information about drive parameter's register */
|
|
switch (param) {
|
|
case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
|
|
*reg = REG_PINCTRL_SCHMITT;
|
|
*width = 1;
|
|
*map = tz1090_boolean_map;
|
|
break;
|
|
case PIN_CONFIG_DRIVE_STRENGTH:
|
|
*reg = REG_PINCTRL_DR;
|
|
*width = 2;
|
|
*map = tz1090_dr_map;
|
|
break;
|
|
default:
|
|
return -ENOTSUPP;
|
|
};
|
|
|
|
/* Calculate field information */
|
|
*shift = g->slw_bit * *width;
|
|
*mask = (BIT(*width) - 1) << *shift;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tz1090_pinconf_group_get(struct pinctrl_dev *pctldev,
|
|
unsigned int group,
|
|
unsigned long *config)
|
|
{
|
|
struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
|
|
const struct tz1090_pingroup *g;
|
|
enum pin_config_param param = pinconf_to_config_param(*config);
|
|
int ret, arg;
|
|
unsigned int pin;
|
|
u32 reg, width, mask, shift, val;
|
|
const int *map;
|
|
|
|
if (group >= ARRAY_SIZE(tz1090_groups)) {
|
|
pin = group - ARRAY_SIZE(tz1090_groups);
|
|
return tz1090_pinconf_get(pctldev, pin, config);
|
|
}
|
|
|
|
g = &tz1090_groups[group];
|
|
if (g->npins == 1) {
|
|
pin = g->pins[0];
|
|
ret = tz1090_pinconf_get(pctldev, pin, config);
|
|
if (ret != -ENOTSUPP)
|
|
return ret;
|
|
}
|
|
|
|
/* Get register information */
|
|
ret = tz1090_pinconf_group_reg(pctldev, g, param, true,
|
|
®, &width, &mask, &shift, &map);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Extract field from register */
|
|
val = pmx_read(pmx, reg);
|
|
arg = map[(val & mask) >> shift];
|
|
if (arg < 0)
|
|
return arg;
|
|
|
|
/* And pack config */
|
|
*config = pinconf_to_config_packed(param, arg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tz1090_pinconf_group_set(struct pinctrl_dev *pctldev,
|
|
unsigned int group, unsigned long *configs,
|
|
unsigned num_configs)
|
|
{
|
|
struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
|
|
const struct tz1090_pingroup *g;
|
|
enum pin_config_param param;
|
|
unsigned int arg, pin, i;
|
|
const unsigned int *pit;
|
|
int ret;
|
|
u32 reg, width, mask, shift, val;
|
|
unsigned long flags;
|
|
const int *map;
|
|
int j;
|
|
|
|
if (group >= ARRAY_SIZE(tz1090_groups)) {
|
|
pin = group - ARRAY_SIZE(tz1090_groups);
|
|
return tz1090_pinconf_set(pctldev, pin, configs, num_configs);
|
|
}
|
|
|
|
g = &tz1090_groups[group];
|
|
if (g->npins == 1) {
|
|
pin = g->pins[0];
|
|
ret = tz1090_pinconf_set(pctldev, pin, configs, num_configs);
|
|
if (ret != -ENOTSUPP)
|
|
return ret;
|
|
}
|
|
|
|
for (j = 0; j < num_configs; j++) {
|
|
param = pinconf_to_config_param(configs[j]);
|
|
|
|
dev_dbg(pctldev->dev, "%s(group=%s, config=%#lx)\n",
|
|
__func__, g->name, configs[j]);
|
|
|
|
/* Get register information */
|
|
ret = tz1090_pinconf_group_reg(pctldev, g, param, true, ®,
|
|
&width, &mask, &shift, &map);
|
|
if (ret < 0) {
|
|
/*
|
|
* Maybe we're trying to set a per-pin configuration
|
|
* of a group, so do the pins one by one. This is
|
|
* mainly as a convenience.
|
|
*/
|
|
for (i = 0, pit = g->pins; i < g->npins; ++i, ++pit) {
|
|
ret = tz1090_pinconf_set(pctldev, *pit, configs,
|
|
num_configs);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Unpack argument and map it to register value */
|
|
arg = pinconf_to_config_argument(configs[j]);
|
|
for (i = 0; i < BIT(width); ++i) {
|
|
if (map[i] == arg || (map[i] == -EINVAL && !arg)) {
|
|
/* Write register field */
|
|
__global_lock2(flags);
|
|
val = pmx_read(pmx, reg);
|
|
val &= ~mask;
|
|
val |= i << shift;
|
|
pmx_write(pmx, val, reg);
|
|
__global_unlock2(flags);
|
|
goto next_config;
|
|
}
|
|
}
|
|
|
|
dev_dbg(pctldev->dev, "%s: arg %u not supported\n",
|
|
__func__, arg);
|
|
return -EINVAL;
|
|
|
|
next_config:
|
|
;
|
|
} /* for each config */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct pinconf_ops tz1090_pinconf_ops = {
|
|
.is_generic = true,
|
|
.pin_config_get = tz1090_pinconf_get,
|
|
.pin_config_set = tz1090_pinconf_set,
|
|
.pin_config_group_get = tz1090_pinconf_group_get,
|
|
.pin_config_group_set = tz1090_pinconf_group_set,
|
|
.pin_config_config_dbg_show = pinconf_generic_dump_config,
|
|
};
|
|
|
|
/*
|
|
* Pin control driver setup
|
|
*/
|
|
|
|
static struct pinctrl_desc tz1090_pinctrl_desc = {
|
|
.pctlops = &tz1090_pinctrl_ops,
|
|
.pmxops = &tz1090_pinmux_ops,
|
|
.confops = &tz1090_pinconf_ops,
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
static int tz1090_pinctrl_probe(struct platform_device *pdev)
|
|
{
|
|
struct tz1090_pmx *pmx;
|
|
struct resource *res;
|
|
|
|
pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL);
|
|
if (!pmx) {
|
|
dev_err(&pdev->dev, "Can't alloc tz1090_pmx\n");
|
|
return -ENOMEM;
|
|
}
|
|
pmx->dev = &pdev->dev;
|
|
spin_lock_init(&pmx->lock);
|
|
|
|
tz1090_pinctrl_desc.name = dev_name(&pdev->dev);
|
|
tz1090_pinctrl_desc.pins = tz1090_pins;
|
|
tz1090_pinctrl_desc.npins = ARRAY_SIZE(tz1090_pins);
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
pmx->regs = devm_ioremap_resource(&pdev->dev, res);
|
|
if (IS_ERR(pmx->regs))
|
|
return PTR_ERR(pmx->regs);
|
|
|
|
pmx->pctl = pinctrl_register(&tz1090_pinctrl_desc, &pdev->dev, pmx);
|
|
if (!pmx->pctl) {
|
|
dev_err(&pdev->dev, "Couldn't register pinctrl driver\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, pmx);
|
|
|
|
dev_info(&pdev->dev, "TZ1090 pinctrl driver initialised\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tz1090_pinctrl_remove(struct platform_device *pdev)
|
|
{
|
|
struct tz1090_pmx *pmx = platform_get_drvdata(pdev);
|
|
|
|
pinctrl_unregister(pmx->pctl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct of_device_id tz1090_pinctrl_of_match[] = {
|
|
{ .compatible = "img,tz1090-pinctrl", },
|
|
{ },
|
|
};
|
|
|
|
static struct platform_driver tz1090_pinctrl_driver = {
|
|
.driver = {
|
|
.name = "tz1090-pinctrl",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = tz1090_pinctrl_of_match,
|
|
},
|
|
.probe = tz1090_pinctrl_probe,
|
|
.remove = tz1090_pinctrl_remove,
|
|
};
|
|
|
|
static int __init tz1090_pinctrl_init(void)
|
|
{
|
|
tz1090_init_mux_pins();
|
|
return platform_driver_register(&tz1090_pinctrl_driver);
|
|
}
|
|
arch_initcall(tz1090_pinctrl_init);
|
|
|
|
static void __exit tz1090_pinctrl_exit(void)
|
|
{
|
|
platform_driver_unregister(&tz1090_pinctrl_driver);
|
|
}
|
|
module_exit(tz1090_pinctrl_exit);
|
|
|
|
MODULE_AUTHOR("Imagination Technologies Ltd.");
|
|
MODULE_DESCRIPTION("Toumaz Xenif TZ1090 pinctrl driver");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_DEVICE_TABLE(of, tz1090_pinctrl_of_match);
|