1968 lines · 50.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Intel pinctrl/GPIO core driver. * * Copyright (C) 2015 Intel Corporation * Authors: Mathias Nyman <mathias.nyman@linux.intel.com> *          Mika Westerberg <mika.westerberg@linux.intel.com> */ #include <linux/acpi.h>#include <linux/cleanup.h>#include <linux/export.h>#include <linux/gpio/driver.h>#include <linux/interrupt.h>#include <linux/log2.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/property.h>#include <linux/seq_file.h>#include <linux/string_helpers.h>#include <linux/time.h> #include <linux/pinctrl/consumer.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_data/x86/pwm-lpss.h> #include "../core.h"#include "pinctrl-intel.h" /* Offset from regs */#define REVID				0x000#define REVID_SHIFT			16#define REVID_MASK			GENMASK(31, 16) #define CAPLIST				0x004#define CAPLIST_ID_SHIFT		16#define CAPLIST_ID_MASK			GENMASK(23, 16)#define CAPLIST_ID_GPIO_HW_INFO		1#define CAPLIST_ID_PWM			2#define CAPLIST_ID_BLINK		3#define CAPLIST_ID_EXP			4#define CAPLIST_NEXT_SHIFT		0#define CAPLIST_NEXT_MASK		GENMASK(15, 0) #define PADBAR				0x00c #define PADOWN_BITS			4#define PADOWN_SHIFT(p)			((p) % 8 * PADOWN_BITS)#define PADOWN_MASK(p)			(GENMASK(3, 0) << PADOWN_SHIFT(p))#define PADOWN_GPP(p)			((p) / 8) /* Offset from pad_regs */#define PADCFG0				0x000#define PADCFG0_RXEVCFG_MASK		GENMASK(26, 25)#define PADCFG0_RXEVCFG_LEVEL		(0 << 25)#define PADCFG0_RXEVCFG_EDGE		(1 << 25)#define PADCFG0_RXEVCFG_DISABLED	(2 << 25)#define PADCFG0_RXEVCFG_EDGE_BOTH	(3 << 25)#define PADCFG0_PREGFRXSEL		BIT(24)#define PADCFG0_RXINV			BIT(23)#define PADCFG0_GPIROUTIOXAPIC		BIT(20)#define PADCFG0_GPIROUTSCI		BIT(19)#define PADCFG0_GPIROUTSMI		BIT(18)#define PADCFG0_GPIROUTNMI		BIT(17)#define PADCFG0_PMODE_SHIFT		10#define PADCFG0_PMODE_MASK		GENMASK(13, 10)#define PADCFG0_PMODE_GPIO		0#define PADCFG0_GPIODIS_SHIFT		8#define PADCFG0_GPIODIS_MASK		GENMASK(9, 8)#define PADCFG0_GPIODIS_NONE		0#define PADCFG0_GPIODIS_OUTPUT		1#define PADCFG0_GPIODIS_INPUT		2#define PADCFG0_GPIODIS_FULL		3#define PADCFG0_GPIORXDIS		BIT(9)#define PADCFG0_GPIOTXDIS		BIT(8)#define PADCFG0_GPIORXSTATE		BIT(1)#define PADCFG0_GPIOTXSTATE		BIT(0) #define PADCFG1				0x004#define PADCFG1_TERM_UP			BIT(13)#define PADCFG1_TERM_SHIFT		10#define PADCFG1_TERM_MASK		GENMASK(12, 10)/* * Bit 0  Bit 1  Bit 2			Value, Ohms * *   0      0      0			- *   0      0      1			20000 *   0      1      0			5000 *   0      1      1			~4000 *   1      0      0			1000 (if supported) *   1      0      1			~952 (if supported) *   1      1      0			~833 (if supported) *   1      1      1			~800 (if supported) */#define PADCFG1_TERM_20K		BIT(2)#define PADCFG1_TERM_5K			BIT(1)#define PADCFG1_TERM_4K			(BIT(2) | BIT(1))#define PADCFG1_TERM_1K			BIT(0)#define PADCFG1_TERM_952		(BIT(2) | BIT(0))#define PADCFG1_TERM_833		(BIT(1) | BIT(0))#define PADCFG1_TERM_800		(BIT(2) | BIT(1) | BIT(0)) #define PADCFG2				0x008#define PADCFG2_DEBOUNCE_SHIFT		1#define PADCFG2_DEBOUNCE_MASK		GENMASK(4, 1)#define PADCFG2_DEBEN			BIT(0) #define DEBOUNCE_PERIOD_NSEC		31250 struct intel_pad_context {	u32 padcfg0;	u32 padcfg1;	u32 padcfg2;}; struct intel_community_context {	u32 *intmask;	u32 *hostown;}; #define pin_to_padno(c, p)	((p) - (c)->pin_base)#define padgroup_offset(g, p)	((p) - (g)->base) #define for_each_intel_pin_community(pctrl, community)					\	for (unsigned int __ci = 0;							\	     __ci < pctrl->ncommunities && (community = &pctrl->communities[__ci]);	\	     __ci++)									\ #define for_each_intel_community_pad_group(community, grp)			\	for (unsigned int __gi = 0;						\	     __gi < community->ngpps && (grp = &community->gpps[__gi]);		\	     __gi++)								\ #define for_each_intel_pad_group(pctrl, community, grp)			\	for_each_intel_pin_community(pctrl, community)			\		for_each_intel_community_pad_group(community, grp) #define for_each_intel_gpio_group(pctrl, community, grp)		\	for_each_intel_pad_group(pctrl, community, grp)			\		if (grp->gpio_base == INTEL_GPIO_BASE_NOMAP) {} else const struct intel_community *intel_get_community(const struct intel_pinctrl *pctrl,						  unsigned int pin){	const struct intel_community *community; 	for_each_intel_pin_community(pctrl, community) {		if (pin >= community->pin_base &&		    pin < community->pin_base + community->npins)			return community;	} 	dev_warn(pctrl->dev, "failed to find community for pin %u\n", pin);	return NULL;}EXPORT_SYMBOL_NS_GPL(intel_get_community, "PINCTRL_INTEL"); static const struct intel_padgroup *intel_community_get_padgroup(const struct intel_community *community,			     unsigned int pin){	const struct intel_padgroup *padgrp; 	for_each_intel_community_pad_group(community, padgrp) {		if (pin >= padgrp->base && pin < padgrp->base + padgrp->size)			return padgrp;	} 	return NULL;} static void __iomem *intel_get_padcfg(struct intel_pinctrl *pctrl,				      unsigned int pin, unsigned int reg){	const struct intel_community *community;	unsigned int padno;	size_t nregs; 	community = intel_get_community(pctrl, pin);	if (!community)		return NULL; 	padno = pin_to_padno(community, pin);	nregs = (community->features & PINCTRL_FEATURE_DEBOUNCE) ? 4 : 2; 	if (reg >= nregs * 4)		return NULL; 	return community->pad_regs + reg + padno * nregs * 4;} static bool intel_pad_owned_by_host(const struct intel_pinctrl *pctrl, unsigned int pin){	const struct intel_community *community;	const struct intel_padgroup *padgrp;	unsigned int gpp, offset, gpp_offset;	void __iomem *padown; 	community = intel_get_community(pctrl, pin);	if (!community)		return false; 	/* If padown_offset is not provided, assume host ownership */	padown = community->regs + community->padown_offset;	if (padown == community->regs)		return true; 	/* New HW generations have extended PAD_OWN registers */	if (community->features & PINCTRL_FEATURE_3BIT_PAD_OWN)		return !(readl(padown + pin_to_padno(community, pin) * 4) & 7); 	padgrp = intel_community_get_padgroup(community, pin);	if (!padgrp)		return false; 	gpp_offset = padgroup_offset(padgrp, pin);	gpp = PADOWN_GPP(gpp_offset);	offset = padgrp->padown_num * 4 + gpp * 4; 	return !(readl(padown + offset) & PADOWN_MASK(gpp_offset));} static bool intel_pad_acpi_mode(const struct intel_pinctrl *pctrl, unsigned int pin){	const struct intel_community *community;	const struct intel_padgroup *padgrp;	unsigned int offset, gpp_offset;	void __iomem *hostown; 	community = intel_get_community(pctrl, pin);	if (!community)		return true;	if (!community->hostown_offset)		return false; 	padgrp = intel_community_get_padgroup(community, pin);	if (!padgrp)		return true; 	gpp_offset = padgroup_offset(padgrp, pin);	offset = community->hostown_offset + padgrp->reg_num * 4;	hostown = community->regs + offset; 	return !(readl(hostown) & BIT(gpp_offset));} /** * enum - Locking variants of the pad configuration * @PAD_UNLOCKED:	pad is fully controlled by the configuration registers * @PAD_LOCKED:		pad configuration registers, except TX state, are locked * @PAD_LOCKED_TX:	pad configuration TX state is locked * @PAD_LOCKED_FULL:	pad configuration registers are locked completely * * Locking is considered as read-only mode for corresponding registers and * their respective fields. That said, TX state bit is locked separately from * the main locking scheme. */enum {	PAD_UNLOCKED	= 0,	PAD_LOCKED	= 1,	PAD_LOCKED_TX	= 2,	PAD_LOCKED_FULL	= PAD_LOCKED | PAD_LOCKED_TX,}; static int intel_pad_locked(const struct intel_pinctrl *pctrl, unsigned int pin){	const struct intel_community *community;	const struct intel_padgroup *padgrp;	unsigned int offset, gpp_offset;	u32 value;	int ret = PAD_UNLOCKED; 	community = intel_get_community(pctrl, pin);	if (!community)		return PAD_LOCKED_FULL;	if (!community->padcfglock_offset)		return PAD_UNLOCKED; 	padgrp = intel_community_get_padgroup(community, pin);	if (!padgrp)		return PAD_LOCKED_FULL; 	gpp_offset = padgroup_offset(padgrp, pin); 	/*	 * If PADCFGLOCK and PADCFGLOCKTX bits are both clear for this pad,	 * the pad is considered unlocked. Any other case means that it is	 * either fully or partially locked.	 */	offset = community->padcfglock_offset + 0 + padgrp->reg_num * 8;	value = readl(community->regs + offset);	if (value & BIT(gpp_offset))		ret |= PAD_LOCKED; 	offset = community->padcfglock_offset + 4 + padgrp->reg_num * 8;	value = readl(community->regs + offset);	if (value & BIT(gpp_offset))		ret |= PAD_LOCKED_TX; 	return ret;} static bool intel_pad_is_unlocked(const struct intel_pinctrl *pctrl, unsigned int pin){	return (intel_pad_locked(pctrl, pin) & PAD_LOCKED) == PAD_UNLOCKED;} static bool intel_pad_usable(const struct intel_pinctrl *pctrl, unsigned int pin){	return intel_pad_owned_by_host(pctrl, pin) && intel_pad_is_unlocked(pctrl, pin);} int intel_get_groups_count(struct pinctrl_dev *pctldev){	const struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); 	return pctrl->soc->ngroups;}EXPORT_SYMBOL_NS_GPL(intel_get_groups_count, "PINCTRL_INTEL"); const char *intel_get_group_name(struct pinctrl_dev *pctldev, unsigned int group){	const struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); 	return pctrl->soc->groups[group].grp.name;}EXPORT_SYMBOL_NS_GPL(intel_get_group_name, "PINCTRL_INTEL"); int intel_get_group_pins(struct pinctrl_dev *pctldev, unsigned int group,			 const unsigned int **pins, unsigned int *npins){	const struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); 	*pins = pctrl->soc->groups[group].grp.pins;	*npins = pctrl->soc->groups[group].grp.npins;	return 0;}EXPORT_SYMBOL_NS_GPL(intel_get_group_pins, "PINCTRL_INTEL"); static void intel_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,			       unsigned int pin){	struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);	void __iomem *padcfg;	u32 cfg0, cfg1, mode;	int locked;	bool acpi; 	if (!intel_pad_owned_by_host(pctrl, pin)) {		seq_puts(s, "not available");		return;	} 	cfg0 = readl(intel_get_padcfg(pctrl, pin, PADCFG0));	cfg1 = readl(intel_get_padcfg(pctrl, pin, PADCFG1)); 	mode = (cfg0 & PADCFG0_PMODE_MASK) >> PADCFG0_PMODE_SHIFT;	if (mode == PADCFG0_PMODE_GPIO)		seq_puts(s, "GPIO ");	else		seq_printf(s, "mode %d ", mode); 	seq_printf(s, "0x%08x 0x%08x", cfg0, cfg1); 	/* Dump the additional PADCFG registers if available */	padcfg = intel_get_padcfg(pctrl, pin, PADCFG2);	if (padcfg)		seq_printf(s, " 0x%08x", readl(padcfg)); 	locked = intel_pad_locked(pctrl, pin);	acpi = intel_pad_acpi_mode(pctrl, pin); 	if (locked || acpi) {		seq_puts(s, " [");		if (locked)			seq_puts(s, "LOCKED");		if ((locked & PAD_LOCKED_FULL) == PAD_LOCKED_TX)			seq_puts(s, " tx");		else if ((locked & PAD_LOCKED_FULL) == PAD_LOCKED_FULL)			seq_puts(s, " full"); 		if (locked && acpi)			seq_puts(s, ", "); 		if (acpi)			seq_puts(s, "ACPI");		seq_puts(s, "]");	}} static const struct pinctrl_ops intel_pinctrl_ops = {	.get_groups_count = intel_get_groups_count,	.get_group_name = intel_get_group_name,	.get_group_pins = intel_get_group_pins,	.pin_dbg_show = intel_pin_dbg_show,}; int intel_get_functions_count(struct pinctrl_dev *pctldev){	const struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); 	return pctrl->soc->nfunctions;}EXPORT_SYMBOL_NS_GPL(intel_get_functions_count, "PINCTRL_INTEL"); const char *intel_get_function_name(struct pinctrl_dev *pctldev, unsigned int function){	const struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); 	return pctrl->soc->functions[function].func.name;}EXPORT_SYMBOL_NS_GPL(intel_get_function_name, "PINCTRL_INTEL"); int intel_get_function_groups(struct pinctrl_dev *pctldev, unsigned int function,			      const char * const **groups, unsigned int * const ngroups){	const struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); 	*groups = pctrl->soc->functions[function].func.groups;	*ngroups = pctrl->soc->functions[function].func.ngroups;	return 0;}EXPORT_SYMBOL_NS_GPL(intel_get_function_groups, "PINCTRL_INTEL"); static int intel_pinmux_set_mux(struct pinctrl_dev *pctldev,				unsigned int function, unsigned int group){	struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);	const struct intel_pingroup *grp = &pctrl->soc->groups[group];	int i; 	guard(raw_spinlock_irqsave)(&pctrl->lock); 	/*	 * All pins in the groups needs to be accessible and writable	 * before we can enable the mux for this group.	 */	for (i = 0; i < grp->grp.npins; i++) {		if (!intel_pad_usable(pctrl, grp->grp.pins[i]))			return -EBUSY;	} 	/* Now enable the mux setting for each pin in the group */	for (i = 0; i < grp->grp.npins; i++) {		void __iomem *padcfg0;		u32 value, pmode; 		padcfg0 = intel_get_padcfg(pctrl, grp->grp.pins[i], PADCFG0); 		value = readl(padcfg0);		value &= ~PADCFG0_PMODE_MASK; 		if (grp->modes)			pmode = grp->modes[i];		else			pmode = grp->mode; 		value |= pmode << PADCFG0_PMODE_SHIFT;		writel(value, padcfg0);	} 	return 0;} /** * enum - Possible pad physical connections * @PAD_CONNECT_NONE:	pad is fully disconnected * @PAD_CONNECT_INPUT:	pad is in input only mode * @PAD_CONNECT_OUTPUT:	pad is in output only mode * @PAD_CONNECT_FULL:	pad is fully connected */enum {	PAD_CONNECT_NONE	= 0,	PAD_CONNECT_INPUT	= 1,	PAD_CONNECT_OUTPUT	= 2,	PAD_CONNECT_FULL	= PAD_CONNECT_INPUT | PAD_CONNECT_OUTPUT,}; static int __intel_gpio_get_direction(u32 value){	switch ((value & PADCFG0_GPIODIS_MASK) >> PADCFG0_GPIODIS_SHIFT) {	case PADCFG0_GPIODIS_FULL:		return PAD_CONNECT_NONE;	case PADCFG0_GPIODIS_OUTPUT:		return PAD_CONNECT_INPUT;	case PADCFG0_GPIODIS_INPUT:		return PAD_CONNECT_OUTPUT;	case PADCFG0_GPIODIS_NONE:		return PAD_CONNECT_FULL;	default:		return -ENOTSUPP;	};} static u32 __intel_gpio_set_direction(u32 value, bool input, bool output){

Lines 1–500 of 1968.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t