// 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. Load more
// 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) {