Contributors: 3
Author Tokens Token Proportion Commits Commit Proportion
Lakshmi Sowjanya D 6555 98.26% 1 25.00%
Rafał Miłecki 113 1.69% 2 50.00%
Lv Ruyi 3 0.04% 1 25.00%
Total 6671 4

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297
// SPDX-License-Identifier: GPL-2.0
/*
 * Intel Thunder Bay SOC pinctrl/GPIO driver
 *
 * Copyright (C) 2021 Intel Corporation
 */

#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>

#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>

#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>

#include "core.h"
#include "pinconf.h"
#include "pinctrl-utils.h"
#include "pinmux.h"

/* Bit 0:2 and 4:6 should be used for mode selection */
#define THB_GPIO_PINMUX_MODE_0			0x00
#define THB_GPIO_PINMUX_MODE_1			0x11
#define THB_GPIO_PINMUX_MODE_2			0x22
#define THB_GPIO_PINMUX_MODE_3			0x33
#define THB_GPIO_PINMUX_MODE_4			0x44

#define THB_GPIO_PORT_SELECT_MASK		BIT(8)
#define THB_GPIO_PAD_DIRECTION_MASK		BIT(10)
#define THB_GPIO_SPU_MASK			BIT(11)
#define THB_GPIO_PULL_ENABLE_MASK		BIT(12)
#define THB_GPIO_PULL_UP_MASK			BIT(13)
#define THB_GPIO_PULL_DOWN_MASK			BIT(14)
#define THB_GPIO_ENAQ_MASK			BIT(15)
/* bit 16-19: Drive Strength for the Pad */
#define THB_GPIO_DRIVE_STRENGTH_MASK		(0xF0000)
#define THB_GPIO_SLEW_RATE_MASK			BIT(20)
#define THB_GPIO_SCHMITT_TRIGGER_MASK		BIT(21)

#define THB_GPIO_REG_OFFSET(pin_num)			((pin_num) * (0x4))
#define THB_MAX_MODE_SUPPORTED				(5u)
#define THB_MAX_NPINS_SUPPORTED				(67u)

/* store Pin status */
static u32 thb_pinx_status[THB_MAX_NPINS_SUPPORTED];

struct thunderbay_mux_desc {
	u8 mode;
	const char *name;
};

#define THUNDERBAY_PIN_DESC(pin_number, pin_name, ...) {        \
	.number = pin_number,                           \
	.name = pin_name,                               \
	.drv_data = &(struct thunderbay_mux_desc[]) {   \
			__VA_ARGS__, { } },             \
}

#define THUNDERBAY_MUX(pin_mode, pin_function) {                \
	.mode = pin_mode,                               \
	.name = pin_function,                           \
}

struct thunderbay_pin_soc {
	const struct pinctrl_pin_desc           *pins;
	unsigned int                            npins;
};

/**
 * struct thunderbay_pinctrl - Intel Thunderbay pinctrl structure
 * @pctrl: Pointer to the pin controller device
 * @base0: First register base address
 * @dev: Pointer to the device structure
 * @chip: GPIO chip used by this pin controller
 * @soc: Pin control configuration data based on SoC
 * @ngroups: Number of pin groups available
 * @nfuncs: Number of pin functions available
 */
struct thunderbay_pinctrl {
	struct pinctrl_dev              *pctrl;
	void __iomem                    *base0;
	struct device                   *dev;
	struct gpio_chip                chip;
	const struct thunderbay_pin_soc *soc;
	unsigned int                    ngroups;
	unsigned int                    nfuncs;
};

static const struct pinctrl_pin_desc thunderbay_pins[] = {
	THUNDERBAY_PIN_DESC(0, "GPIO0",
			    THUNDERBAY_MUX(0X0, "I2C0_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(1, "GPIO1",
			    THUNDERBAY_MUX(0X0, "I2C0_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(2, "GPIO2",
			    THUNDERBAY_MUX(0X0, "I2C1_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(3, "GPIO3",
			    THUNDERBAY_MUX(0X0, "I2C1_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(4, "GPIO4",
			    THUNDERBAY_MUX(0X0, "I2C2_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(5, "GPIO5",
			    THUNDERBAY_MUX(0X0, "I2C2_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(6, "GPIO6",
			    THUNDERBAY_MUX(0X0, "I2C3_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(7, "GPIO7",
			    THUNDERBAY_MUX(0X0, "I2C3_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(8, "GPIO8",
			    THUNDERBAY_MUX(0X0, "I2C4_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(9, "GPIO9",
			    THUNDERBAY_MUX(0X0, "I2C4_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(10, "GPIO10",
			    THUNDERBAY_MUX(0X0, "UART0_M0"),
			    THUNDERBAY_MUX(0X1, "RT0_DSU_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(11, "GPIO11",
			    THUNDERBAY_MUX(0X0, "UART0_M0"),
			    THUNDERBAY_MUX(0X1, "RT0_DSU_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(12, "GPIO12",
			    THUNDERBAY_MUX(0X0, "UART0_M0"),
			    THUNDERBAY_MUX(0X1, "RT1_DSU_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(13, "GPIO13",
			    THUNDERBAY_MUX(0X0, "UART0_M0"),
			    THUNDERBAY_MUX(0X1, "RT1_DSU_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(14, "GPIO14",
			    THUNDERBAY_MUX(0X0, "UART1_M0"),
			    THUNDERBAY_MUX(0X1, "RT2_DSU_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "TRIGGER_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(15, "GPIO15",
			    THUNDERBAY_MUX(0X0, "UART1_M0"),
			    THUNDERBAY_MUX(0X1, "RT2_DSU_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "TRIGGER_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(16, "GPIO16",
			    THUNDERBAY_MUX(0X0, "UART1_M0"),
			    THUNDERBAY_MUX(0X1, "RT3_DSU_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(17, "GPIO17",
			    THUNDERBAY_MUX(0X0, "UART1_M0"),
			    THUNDERBAY_MUX(0X1, "RT3_DSU_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(18, "GPIO18",
			    THUNDERBAY_MUX(0X0, "SPI0_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(19, "GPIO19",
			    THUNDERBAY_MUX(0X0, "SPI0_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(20, "GPIO20",
			    THUNDERBAY_MUX(0X0, "SPI0_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_TRACE_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(21, "GPIO21",
			    THUNDERBAY_MUX(0X0, "SPI0_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_TRACE_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(22, "GPIO22",
			    THUNDERBAY_MUX(0X0, "SPI1_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M0"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(23, "GPIO23",
			    THUNDERBAY_MUX(0X0, "SPI1_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(24, "GPIO24",
			    THUNDERBAY_MUX(0X0, "SPI1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_TRACE_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(25, "GPIO25",
			    THUNDERBAY_MUX(0X0, "SPI1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_TRACE_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(26, "GPIO26",
			    THUNDERBAY_MUX(0X0, "ETHER0_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DEBUG_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(27, "GPIO27",
			    THUNDERBAY_MUX(0X0, "ETHER0_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DEBUG_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(28, "GPIO28",
			    THUNDERBAY_MUX(0X0, "ETHER0_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DEBUG_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(29, "GPIO29",
			    THUNDERBAY_MUX(0X0, "ETHER0_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DEBUG_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(30, "GPIO30",
			    THUNDERBAY_MUX(0X0, "ETHER0_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DEBUG_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(31, "GPIO31",
			    THUNDERBAY_MUX(0X0, "ETHER0_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DEBUG_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(32, "GPIO32",
			    THUNDERBAY_MUX(0X0, "ETHER0_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DEBUG_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(33, "GPIO33",
			    THUNDERBAY_MUX(0X0, "ETHER0_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DEBUG_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(34, "GPIO34",
			    THUNDERBAY_MUX(0X0, "ETHER0_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DIG_VIEW_0"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(35, "GPIO35",
			    THUNDERBAY_MUX(0X0, "ETHER0_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DIG_VIEW_1"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(36, "GPIO36",
			    THUNDERBAY_MUX(0X0, "ETHER0_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "CPR_IO_OUT_CLK_0"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(37, "GPIO37",
			    THUNDERBAY_MUX(0X0, "ETHER0_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "CPR_IO_OUT_CLK_1"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(38, "GPIO38",
			    THUNDERBAY_MUX(0X0, "ETHER0_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "CPR_IO_OUT_CLK_2"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(39, "GPIO39",
			    THUNDERBAY_MUX(0X0, "ETHER0_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "CPR_IO_OUT_CLK_3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(40, "GPIO40",
			    THUNDERBAY_MUX(0X0, "ETHER0_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(41, "GPIO41",
			    THUNDERBAY_MUX(0X0, "POWER_INTERRUPT_MAX_PLATFORM_POWER_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(42, "GPIO42",
			    THUNDERBAY_MUX(0X0, "ETHER1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DEBUG_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(43, "GPIO43",
			    THUNDERBAY_MUX(0X0, "ETHER1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DEBUG_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(44, "GPIO44",
			    THUNDERBAY_MUX(0X0, "ETHER1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DEBUG_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(45, "GPIO45",
			    THUNDERBAY_MUX(0X0, "ETHER1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DEBUG_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(46, "GPIO46",
			    THUNDERBAY_MUX(0X0, "ETHER1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DEBUG_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(47, "GPIO47",
			    THUNDERBAY_MUX(0X0, "ETHER1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DEBUG_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(48, "GPIO48",
			    THUNDERBAY_MUX(0X0, "ETHER1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DEBUG_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(49, "GPIO49",
			    THUNDERBAY_MUX(0X0, "ETHER1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DEBUG_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(50, "GPIO50",
			    THUNDERBAY_MUX(0X0, "ETHER1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DIG_VIEW_0"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(51, "GPIO51",
			    THUNDERBAY_MUX(0X0, "ETHER1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "DIG_VIEW_1"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(52, "GPIO52",
			    THUNDERBAY_MUX(0X0, "ETHER1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "CPR_IO_OUT_CLK_0"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(53, "GPIO53",
			    THUNDERBAY_MUX(0X0, "ETHER1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "CPR_IO_OUT_CLK_1"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(54, "GPIO54",
			    THUNDERBAY_MUX(0X0, "ETHER1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "CPR_IO_OUT_CLK_2"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(55, "GPIO55",
			    THUNDERBAY_MUX(0X0, "ETHER1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "CPR_IO_OUT_CLK_3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(56, "GPIO56",
			    THUNDERBAY_MUX(0X0, "ETHER1_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "POWER_INTERRUPT_ICCMAX_VDDD_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(57, "GPIO57",
			    THUNDERBAY_MUX(0X0, "POWER_INTERRUPT_ICCMAX_VPU_M0"),
			    THUNDERBAY_MUX(0X1, "TPIU_DATA_M1"),
			    THUNDERBAY_MUX(0X2, "TPIU_DATA_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(58, "GPIO58",
			    THUNDERBAY_MUX(0X0, "THERMTRIP_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(59, "GPIO59",
			    THUNDERBAY_MUX(0X0, "THERMTRIP_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(60, "GPIO60",
			    THUNDERBAY_MUX(0X0, "SMBUS_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(61, "GPIO61",
			    THUNDERBAY_MUX(0X0, "SMBUS_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "POWER_INTERRUPT_ICCMAX_VDDD_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(62, "GPIO62",
			    THUNDERBAY_MUX(0X0, "PLATFORM_RESET_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(63, "GPIO63",
			    THUNDERBAY_MUX(0X0, "PLATFORM_RESET_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(64, "GPIO64",
			    THUNDERBAY_MUX(0X0, "PLATFORM_SHUTDOWN_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(65, "GPIO65",
			    THUNDERBAY_MUX(0X0, "PLATFORM_SHUTDOWN_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
	THUNDERBAY_PIN_DESC(66, "GPIO66",
			    THUNDERBAY_MUX(0X0, "POWER_INTERRUPT_ICCMAX_MEDIA_M0"),
			    THUNDERBAY_MUX(0X1, "EMPTY_M1"),
			    THUNDERBAY_MUX(0X2, "EMPTY_M2"),
			    THUNDERBAY_MUX(0X3, "EMPTY_M3"),
			    THUNDERBAY_MUX(0X4, "GPIO_M4")),
};

static const struct thunderbay_pin_soc thunderbay_data = {
	.pins	= thunderbay_pins,
	.npins  = ARRAY_SIZE(thunderbay_pins),
};

static u32 thb_gpio_read_reg(struct gpio_chip *chip, unsigned int pinnr)
{
	struct thunderbay_pinctrl *tpc = gpiochip_get_data(chip);

	return readl(tpc->base0 + THB_GPIO_REG_OFFSET(pinnr));
}

static u32 thb_gpio_write_reg(struct gpio_chip *chip, unsigned int pinnr, u32 value)
{
	struct thunderbay_pinctrl *tpc = gpiochip_get_data(chip);

	writel(value, (tpc->base0 + THB_GPIO_REG_OFFSET(pinnr)));
	return 0;
}

static int thb_read_gpio_data(struct gpio_chip *chip, unsigned int offset, unsigned int pad_dir)
{
	int data_offset;
	u32 data_reg;

	/* as per GPIO Spec = pad_dir 0:input, 1:output */
	data_offset = 0x2000u + (offset / 32);
	if (!pad_dir)
		data_offset += 4;
	data_reg = thb_gpio_read_reg(chip, data_offset);

	return data_reg & BIT(offset % 32);
}

static int thb_write_gpio_data(struct gpio_chip *chip, unsigned int offset, unsigned int value)
{
	int data_offset;
	u32 data_reg;

	data_offset = 0x2000u + (offset / 32);

	data_reg = thb_gpio_read_reg(chip, data_offset);

	if (value > 0)
		data_reg |= BIT(offset % 32);
	else
		data_reg &= ~BIT(offset % 32);

	return thb_gpio_write_reg(chip, data_offset, data_reg);
}

static int thunderbay_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
{
	u32 reg = thb_gpio_read_reg(chip, offset);

	/* Return direction only if configured as GPIO else negative error */
	if (reg & THB_GPIO_PORT_SELECT_MASK)
		return !(reg & THB_GPIO_PAD_DIRECTION_MASK);
	return -EINVAL;
}

static int thunderbay_gpio_set_direction_input(struct gpio_chip *chip, unsigned int offset)
{
	u32 reg = thb_gpio_read_reg(chip, offset);

	/* set pin as input only if it is GPIO else error */
	if (reg & THB_GPIO_PORT_SELECT_MASK) {
		reg &= (~THB_GPIO_PAD_DIRECTION_MASK);
		thb_gpio_write_reg(chip, offset, reg);
		return 0;
	}
	return -EINVAL;
}

static void thunderbay_gpio_set_value(struct gpio_chip *chip, unsigned int offset, int value)
{
	u32 reg = thb_gpio_read_reg(chip, offset);

	/* update pin value only if it is GPIO-output else error */
	if ((reg & THB_GPIO_PORT_SELECT_MASK) && (reg & THB_GPIO_PAD_DIRECTION_MASK))
		thb_write_gpio_data(chip, offset, value);
}

static int thunderbay_gpio_set_direction_output(struct gpio_chip *chip,
						unsigned int offset, int value)
{
	u32 reg = thb_gpio_read_reg(chip, offset);

	/* set pin as output only if it is GPIO else error */
	if (reg & THB_GPIO_PORT_SELECT_MASK) {
		reg |= THB_GPIO_PAD_DIRECTION_MASK;
		thb_gpio_write_reg(chip, offset, reg);
		thunderbay_gpio_set_value(chip, offset, value);
		return 0;
	}
	return -EINVAL;
}

static int thunderbay_gpio_get_value(struct gpio_chip *chip, unsigned int offset)
{
	u32 reg = thb_gpio_read_reg(chip, offset);
	int gpio_dir = 0;

	/* Read pin value only if it is GPIO else error */
	if (reg & THB_GPIO_PORT_SELECT_MASK) {
		/* 0=in, 1=out */
		gpio_dir = (reg & THB_GPIO_PAD_DIRECTION_MASK) > 0;

		/* Returns negative value when pin is configured as PORT */
		return thb_read_gpio_data(chip, offset, gpio_dir);
	}
	return -EINVAL;
}

static int thunderbay_gpiochip_probe(struct thunderbay_pinctrl *tpc)
{
	struct gpio_chip *chip = &tpc->chip;
	int ret;

	chip->label		= dev_name(tpc->dev);
	chip->parent		= tpc->dev;
	chip->request		= gpiochip_generic_request;
	chip->free		= gpiochip_generic_free;
	chip->get_direction	= thunderbay_gpio_get_direction;
	chip->direction_input	= thunderbay_gpio_set_direction_input;
	chip->direction_output  = thunderbay_gpio_set_direction_output;
	chip->get		= thunderbay_gpio_get_value;
	chip->set               = thunderbay_gpio_set_value;
	chip->set_config	= gpiochip_generic_config;
	/* identifies the first GPIO number handled by this chip; or,
	 * if negative during registration, requests dynamic ID allocation.
	 * Please pass -1 as base to let gpiolib select the chip base in all possible cases.
	 * We want to get rid of the static GPIO number space in the long run.
	 */
	chip->base		= -1;
	/* Number of GPIOs handled by this controller; the last GPIO handled is (base + ngpio - 1)*/
	chip->ngpio		= THB_MAX_NPINS_SUPPORTED;

	/* Register/add Thunder Bay GPIO chip with Linux framework */
	ret = gpiochip_add_data(chip, tpc);
	if (ret)
		dev_err(tpc->dev, "Failed to add gpiochip\n");
	return ret;
}

static int thunderbay_request_gpio(struct pinctrl_dev *pctldev,
				   struct pinctrl_gpio_range *range,
				   unsigned int pin)
{
	struct thunderbay_pinctrl *tpc = pinctrl_dev_get_drvdata(pctldev);
	struct gpio_chip *chip = &tpc->chip;
	u32 reg = 0;

	if (thb_pinx_status[pin] == 0u) {
		reg = thb_gpio_read_reg(chip, pin);
		/* Updates PIN configuration as GPIO and sets GPIO to MODE-4*/
		reg |= (THB_GPIO_PORT_SELECT_MASK | THB_GPIO_PINMUX_MODE_4);
		thb_gpio_write_reg(chip, pin, reg);

		/* update pin status as busy */
		thb_pinx_status[pin] = 1u;

		return 0;
	}
	return -EINVAL;
}

static void thunderbay_free_gpio(struct pinctrl_dev *pctldev,
				 struct pinctrl_gpio_range *range,
				 unsigned int pin)
{
	struct thunderbay_pinctrl *tpc = pinctrl_dev_get_drvdata(pctldev);
	struct gpio_chip *chip = &tpc->chip;
	u32 reg = 0;

	if (thb_pinx_status[pin] == 1u) {
		reg = thb_gpio_read_reg(chip, pin);

		/* Updates PIN configuration from GPIO to PORT */
		reg &= (~THB_GPIO_PORT_SELECT_MASK);

		/* Change Port/gpio mode to default mode-0 */
		reg &= (~THB_GPIO_PINMUX_MODE_4);

		thb_gpio_write_reg(chip, pin, reg);

		/* update pin status as free */
		thb_pinx_status[pin] = 0u;
	}
}

static int thb_pinctrl_set_mux(struct pinctrl_dev *pctldev,
			       unsigned int func_select, unsigned int group_select)
{
	struct thunderbay_pinctrl *tpc = pinctrl_dev_get_drvdata(pctldev);
	struct gpio_chip *chip = &tpc->chip;
	struct function_desc *function;
	unsigned int i, pin_mode;
	struct group_desc *group;
	int ret = -EINVAL;
	u32 reg = 0u;

	group = pinctrl_generic_get_group(pctldev, group_select);
	if (!group)
		return -EINVAL;

	function = pinmux_generic_get_function(pctldev, func_select);
	if (!function)
		return -EINVAL;

	pin_mode = *(unsigned int *)(function->data);

	/* Change modes for pins in the selected group */
	for (i = 0; i < group->num_pins; i++) {
		reg = thb_gpio_read_reg(chip, group->pins[i]);

		switch (pin_mode) {
		case 0u:
			reg |= THB_GPIO_PINMUX_MODE_0;
			break;
		case 1u:
			reg |= THB_GPIO_PINMUX_MODE_1;
			break;
		case 2u:
			reg |= THB_GPIO_PINMUX_MODE_2;
			break;
		case 3u:
			reg |= THB_GPIO_PINMUX_MODE_3;
			break;
		case 4u:
			reg |= THB_GPIO_PINMUX_MODE_4;
			break;
		default:
			return -EINVAL;
		}

		ret = thb_gpio_write_reg(chip, group->pins[i], reg);
		if (~ret) {
			/* update pin status as busy */
			thb_pinx_status[group->pins[i]] = 1u;
		}
	}
	return ret;
}

static int thunderbay_build_groups(struct thunderbay_pinctrl *tpc)
{
	struct group_desc *thunderbay_groups;
	int i;

	tpc->ngroups = tpc->soc->npins;
	thunderbay_groups = devm_kcalloc(tpc->dev, tpc->ngroups,
					 sizeof(*thunderbay_groups), GFP_KERNEL);
	if (!thunderbay_groups)
		return -ENOMEM;

	for (i = 0; i < tpc->ngroups; i++) {
		struct group_desc *group = thunderbay_groups + i;
		const struct pinctrl_pin_desc *pin_info = thunderbay_pins + i;

		group->name = pin_info->name;
		group->pins = (int *)&pin_info->number;
		pinctrl_generic_add_group(tpc->pctrl, group->name,
					  group->pins, 1, NULL);
	}
	return 0;
}

static int thunderbay_add_functions(struct thunderbay_pinctrl *tpc, struct function_desc *funcs)
{
	int i;

	/* Assign the groups for each function */
	for (i = 0; i < tpc->nfuncs; i++) {
		struct function_desc *func = &funcs[i];
		const char **group_names;
		unsigned int grp_idx = 0;
		int j;

		group_names = devm_kcalloc(tpc->dev, func->num_group_names,
					   sizeof(*group_names), GFP_KERNEL);
		if (!group_names)
			return -ENOMEM;

		for (j = 0; j < tpc->soc->npins; j++) {
			const struct pinctrl_pin_desc *pin_info = &thunderbay_pins[j];
			struct thunderbay_mux_desc *pin_mux;

			for (pin_mux = pin_info->drv_data; pin_mux->name; pin_mux++) {
				if (!strcmp(pin_mux->name, func->name))
					group_names[grp_idx++] = pin_info->name;
			}
		}

		func->group_names = group_names;
	}

	/* Add all functions */
	for (i = 0; i < tpc->nfuncs; i++) {
		pinmux_generic_add_function(tpc->pctrl,
					    funcs[i].name,
					    funcs[i].group_names,
					    funcs[i].num_group_names,
					    funcs[i].data);
	}
	kfree(funcs);
	return 0;
}

static int thunderbay_build_functions(struct thunderbay_pinctrl *tpc)
{
	struct function_desc *thunderbay_funcs;
	void *ptr;
	int pin;

	/*
	 * Allocate maximum possible number of functions. Assume every pin
	 * being part of 8 (hw maximum) globally unique muxes.
	 */
	tpc->nfuncs = 0;
	thunderbay_funcs = kcalloc(tpc->soc->npins * 8,
				   sizeof(*thunderbay_funcs), GFP_KERNEL);
	if (!thunderbay_funcs)
		return -ENOMEM;

	/* Setup 1 function for each unique mux */
	for (pin = 0; pin < tpc->soc->npins; pin++) {
		const struct pinctrl_pin_desc *pin_info = thunderbay_pins + pin;
		struct thunderbay_mux_desc *pin_mux;

		for (pin_mux = pin_info->drv_data; pin_mux->name; pin_mux++) {
			struct function_desc *func;

			/* Check if we already have function for this mux */
			for (func = thunderbay_funcs; func->name; func++) {
				if (!strcmp(pin_mux->name, func->name)) {
					func->num_group_names++;
					break;
				}
			}

			if (!func->name) {
				func->name = pin_mux->name;
				func->num_group_names = 1;
				func->data = (int *)&pin_mux->mode;
				tpc->nfuncs++;
			}
		}
	}

	/* Reallocate memory based on actual number of functions */
	ptr = krealloc(thunderbay_funcs,
		       tpc->nfuncs * sizeof(*thunderbay_funcs), GFP_KERNEL);
	if (!ptr)
		return -ENOMEM;

	thunderbay_funcs = ptr;
	return thunderbay_add_functions(tpc, thunderbay_funcs);
}

static int thunderbay_pinconf_set_tristate(struct thunderbay_pinctrl *tpc,
					   unsigned int pin, u32 config)
{
	struct gpio_chip *chip = &tpc->chip;
	u32 reg;

	reg = thb_gpio_read_reg(chip, pin);
	if (config > 0)
		reg |= THB_GPIO_ENAQ_MASK;
	else
		reg &= ~THB_GPIO_ENAQ_MASK;

	return thb_gpio_write_reg(chip, pin, reg);
}

static int thunderbay_pinconf_get_tristate(struct thunderbay_pinctrl *tpc,
					   unsigned int pin, u32 *config)
{
	struct gpio_chip *chip = &tpc->chip;
	u32 reg;

	reg = thb_gpio_read_reg(chip, pin);
	*config = (reg & THB_GPIO_ENAQ_MASK) > 0;

	return 0;
}

static int thunderbay_pinconf_set_pulldown(struct thunderbay_pinctrl *tpc,
					   unsigned int pin, u32 config)
{
	struct gpio_chip *chip = &tpc->chip;
	u32 reg;

	reg = thb_gpio_read_reg(chip, pin);
	if (config > 0)
		reg |= THB_GPIO_PULL_DOWN_MASK;
	else
		reg &= ~THB_GPIO_PULL_DOWN_MASK;

	return thb_gpio_write_reg(chip, pin, reg);
}

static int thunderbay_pinconf_get_pulldown(struct thunderbay_pinctrl *tpc,
					   unsigned int pin, u32 *config)
{
	struct gpio_chip *chip = &tpc->chip;
	u32 reg = 0;

	reg = thb_gpio_read_reg(chip, pin);
	*config = ((reg & THB_GPIO_PULL_DOWN_MASK) > 0) ? 1 : 0;

	return 0;
}

static int thunderbay_pinconf_set_pullup(struct thunderbay_pinctrl *tpc,
					 unsigned int pin, u32 config)
{
	struct gpio_chip *chip = &tpc->chip;
	u32 reg;

	reg = thb_gpio_read_reg(chip, pin);
	if (config > 0)
		reg &= ~THB_GPIO_PULL_UP_MASK;
	else
		reg |= THB_GPIO_PULL_UP_MASK;

	return thb_gpio_write_reg(chip, pin, reg);
}

static int thunderbay_pinconf_get_pullup(struct thunderbay_pinctrl *tpc,
					 unsigned int pin, u32 *config)
{
	struct gpio_chip *chip = &tpc->chip;
	u32 reg;

	reg = thb_gpio_read_reg(chip, pin);
	*config = ((reg & THB_GPIO_PULL_UP_MASK) == 0) ? 1 : 0;

	return 0;
}

static int thunderbay_pinconf_set_opendrain(struct thunderbay_pinctrl *tpc,
					    unsigned int pin, u32 config)
{
	struct gpio_chip *chip = &tpc->chip;
	u32 reg;

	reg = thb_gpio_read_reg(chip, pin);
	if (config > 0)
		reg &= ~THB_GPIO_PULL_ENABLE_MASK;
	else
		reg |= THB_GPIO_PULL_ENABLE_MASK;

	return thb_gpio_write_reg(chip, pin, reg);
}

static int thunderbay_pinconf_get_opendrain(struct thunderbay_pinctrl *tpc,
					    unsigned int pin, u32 *config)
{
	struct gpio_chip *chip = &tpc->chip;
	u32 reg;

	reg = thb_gpio_read_reg(chip, pin);
	*config = ((reg & THB_GPIO_PULL_ENABLE_MASK) == 0) ? 1 : 0;

	return 0;
}

static int thunderbay_pinconf_set_pushpull(struct thunderbay_pinctrl *tpc,
					   unsigned int pin, u32 config)
{
	struct gpio_chip *chip = &tpc->chip;
	u32 reg;

	reg = thb_gpio_read_reg(chip, pin);
	if (config > 0)
		reg |= THB_GPIO_PULL_ENABLE_MASK;
	else
		reg &= ~THB_GPIO_PULL_ENABLE_MASK;

	return thb_gpio_write_reg(chip, pin, reg);
}

static int thunderbay_pinconf_get_pushpull(struct thunderbay_pinctrl *tpc,
					   unsigned int pin, u32 *config)
{
	struct gpio_chip *chip = &tpc->chip;
	u32 reg;

	reg = thb_gpio_read_reg(chip, pin);
	*config = ((reg & THB_GPIO_PULL_ENABLE_MASK) > 0) ? 1 : 0;

	return 0;
}

static int thunderbay_pinconf_set_drivestrength(struct thunderbay_pinctrl *tpc,
						unsigned int pin, u32 config)
{
	struct gpio_chip *chip = &tpc->chip;
	u32 reg;

	reg = thb_gpio_read_reg(chip, pin);

	/* Drive Strength: 0x0 to 0xF */
	if (config <= 0xF) {
		reg = (reg | config);
		return thb_gpio_write_reg(chip, pin, reg);
	}

	return -EINVAL;
}

static int thunderbay_pinconf_get_drivestrength(struct thunderbay_pinctrl *tpc,
						unsigned int pin, u32 *config)
{
	struct gpio_chip *chip = &tpc->chip;
	u32 reg;

	reg = thb_gpio_read_reg(chip, pin);
	reg = (reg & THB_GPIO_DRIVE_STRENGTH_MASK) >> 16;
	*config = (reg > 0) ? reg : 0;

	return 0;
}

static int thunderbay_pinconf_set_schmitt(struct thunderbay_pinctrl *tpc,
					  unsigned int pin, u32 config)
{
	struct gpio_chip *chip = &tpc->chip;
	u32 reg;

	reg = thb_gpio_read_reg(chip, pin);
	if (config > 0)
		reg |= THB_GPIO_SCHMITT_TRIGGER_MASK;
	else
		reg &= ~THB_GPIO_SCHMITT_TRIGGER_MASK;

	return thb_gpio_write_reg(chip, pin, reg);
}

static int thunderbay_pinconf_get_schmitt(struct thunderbay_pinctrl *tpc,
					  unsigned int pin, u32 *config)
{
	struct gpio_chip *chip = &tpc->chip;
	u32 reg;

	reg = thb_gpio_read_reg(chip, pin);
	*config = ((reg & THB_GPIO_SCHMITT_TRIGGER_MASK) > 0) ? 1 : 0;

	return 0;
}

static int thunderbay_pinconf_set_slew_rate(struct thunderbay_pinctrl *tpc,
					    unsigned int pin, u32 config)
{
	struct gpio_chip *chip = &tpc->chip;
	u32 reg = 0;

	reg = thb_gpio_read_reg(chip, pin);
	if (config > 0)
		reg |= THB_GPIO_SLEW_RATE_MASK;
	else
		reg &= ~THB_GPIO_SLEW_RATE_MASK;

	return thb_gpio_write_reg(chip, pin, reg);
}

static int thunderbay_pinconf_get_slew_rate(struct thunderbay_pinctrl *tpc,
					    unsigned int pin, u32 *config)
{
	struct gpio_chip *chip = &tpc->chip;
	u32 reg;

	reg = thb_gpio_read_reg(chip, pin);
	*config = ((reg & THB_GPIO_SLEW_RATE_MASK) > 0) ? 1 : 0;

	return 0;
}

static int thunderbay_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
				  unsigned long *config)
{
	struct thunderbay_pinctrl *tpc = pinctrl_dev_get_drvdata(pctldev);
	enum pin_config_param param = pinconf_to_config_param(*config);
	u32 arg;
	int ret;

	switch (param) {
	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
		ret = thunderbay_pinconf_get_tristate(tpc, pin, &arg);
		break;

	case PIN_CONFIG_BIAS_PULL_DOWN:
		ret = thunderbay_pinconf_get_pulldown(tpc, pin, &arg);
		break;

	case PIN_CONFIG_BIAS_PULL_UP:
		ret = thunderbay_pinconf_get_pullup(tpc, pin, &arg);
		break;

	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
		ret = thunderbay_pinconf_get_opendrain(tpc, pin, &arg);
		break;

	case PIN_CONFIG_DRIVE_PUSH_PULL:
		ret = thunderbay_pinconf_get_pushpull(tpc, pin, &arg);
		break;

	case PIN_CONFIG_DRIVE_STRENGTH:
		ret = thunderbay_pinconf_get_drivestrength(tpc, pin, &arg);
		break;

	case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
		ret = thunderbay_pinconf_get_schmitt(tpc, pin, &arg);
		break;

	case PIN_CONFIG_SLEW_RATE:
		ret = thunderbay_pinconf_get_slew_rate(tpc, pin, &arg);
		break;

	default:
		return -ENOTSUPP;
	}

	*config = pinconf_to_config_packed(param, arg);

	return ret;
}

static int thunderbay_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
				  unsigned long *configs, unsigned int num_configs)
{
	struct thunderbay_pinctrl *tpc = pinctrl_dev_get_drvdata(pctldev);
	enum pin_config_param param;
	unsigned int pinconf;
	int ret = 0;
	u32 arg;

	for (pinconf = 0; pinconf < num_configs; pinconf++) {
		param = pinconf_to_config_param(configs[pinconf]);
		arg = pinconf_to_config_argument(configs[pinconf]);

		switch (param) {
		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
			ret = thunderbay_pinconf_set_tristate(tpc, pin, arg);
			break;

		case PIN_CONFIG_BIAS_PULL_DOWN:
			ret = thunderbay_pinconf_set_pulldown(tpc, pin, arg);
			break;

		case PIN_CONFIG_BIAS_PULL_UP:
			ret = thunderbay_pinconf_set_pullup(tpc, pin, arg);
			break;

		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
			ret = thunderbay_pinconf_set_opendrain(tpc, pin, arg);
			break;

		case PIN_CONFIG_DRIVE_PUSH_PULL:
			ret = thunderbay_pinconf_set_pushpull(tpc, pin, arg);
			break;

		case PIN_CONFIG_DRIVE_STRENGTH:
			ret = thunderbay_pinconf_set_drivestrength(tpc, pin, arg);
			break;

		case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
			ret = thunderbay_pinconf_set_schmitt(tpc, pin, arg);
			break;

		case PIN_CONFIG_SLEW_RATE:
			ret = thunderbay_pinconf_set_slew_rate(tpc, pin, arg);
			break;

		default:
			return -ENOTSUPP;
		}
	}
	return ret;
}

static const struct pinctrl_ops thunderbay_pctlops = {
	.get_groups_count = pinctrl_generic_get_group_count,
	.get_group_name   = pinctrl_generic_get_group_name,
	.get_group_pins   = pinctrl_generic_get_group_pins,
	.dt_node_to_map   = pinconf_generic_dt_node_to_map_all,
	.dt_free_map	  = pinconf_generic_dt_free_map,
};

static const struct pinmux_ops thunderbay_pmxops = {
	.get_functions_count	= pinmux_generic_get_function_count,
	.get_function_name	= pinmux_generic_get_function_name,
	.get_function_groups	= pinmux_generic_get_function_groups,
	.set_mux		= thb_pinctrl_set_mux,
	.gpio_request_enable	= thunderbay_request_gpio,
	.gpio_disable_free	= thunderbay_free_gpio,
};

static const struct pinconf_ops thunderbay_confops = {
	.is_generic		= true,
	.pin_config_get		= thunderbay_pinconf_get,
	.pin_config_set		= thunderbay_pinconf_set,
};

static struct pinctrl_desc thunderbay_pinctrl_desc = {
	.name		= "thunderbay-pinmux",
	.pctlops	= &thunderbay_pctlops,
	.pmxops		= &thunderbay_pmxops,
	.confops	= &thunderbay_confops,
	.owner		= THIS_MODULE,
};

static const struct of_device_id thunderbay_pinctrl_match[] = {
	{
		.compatible = "intel,thunderbay-pinctrl",
		.data = &thunderbay_data
	},
	{}
};

static int thunderbay_pinctrl_probe(struct platform_device *pdev)
{
	const struct of_device_id *of_id;
	struct device *dev = &pdev->dev;
	struct thunderbay_pinctrl *tpc;
	int ret;

	of_id = of_match_node(thunderbay_pinctrl_match, pdev->dev.of_node);
	if (!of_id)
		return -ENODEV;

	tpc = devm_kzalloc(dev, sizeof(*tpc), GFP_KERNEL);
	if (!tpc)
		return -ENOMEM;

	tpc->dev = dev;
	tpc->soc = of_id->data;

	tpc->base0 = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(tpc->base0))
		return PTR_ERR(tpc->base0);

	thunderbay_pinctrl_desc.pins = tpc->soc->pins;
	thunderbay_pinctrl_desc.npins = tpc->soc->npins;

	/* Register pinctrl */
	tpc->pctrl = devm_pinctrl_register(dev, &thunderbay_pinctrl_desc, tpc);
	if (IS_ERR(tpc->pctrl))
		return PTR_ERR(tpc->pctrl);

	/* Setup pinmux groups */
	ret = thunderbay_build_groups(tpc);
	if (ret)
		return ret;

	/* Setup pinmux functions */
	ret = thunderbay_build_functions(tpc);
	if (ret)
		return ret;

	/* Setup GPIO */
	ret = thunderbay_gpiochip_probe(tpc);
	if (ret < 0)
		return ret;

	platform_set_drvdata(pdev, tpc);

	return 0;
}

static int thunderbay_pinctrl_remove(struct platform_device *pdev)
{
	/* thunderbay_pinctrl_remove function to clear the assigned memory */
	return 0;
}

static struct platform_driver thunderbay_pinctrl_driver = {
	.driver = {
		.name = "thunderbay-pinctrl",
		.of_match_table = thunderbay_pinctrl_match,
	},
	.probe = thunderbay_pinctrl_probe,
	.remove = thunderbay_pinctrl_remove,
};

builtin_platform_driver(thunderbay_pinctrl_driver);

MODULE_AUTHOR("Lakshmi Sowjanya D <lakshmi.sowjanya.d@intel.com>");
MODULE_AUTHOR("Kiran Kumar S <kiran.kumar1.s@intel.com>");
MODULE_DESCRIPTION("Intel Thunder Bay Pinctrl/GPIO Driver");
MODULE_LICENSE("GPL v2");