/*
* 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>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include "pinctrl-intel.h"
/* Maximum number of pads in each group */
#define NPADS_IN_GPP 24
/* Offset from regs */
#define PADBAR 0x00c
#define GPI_IS 0x100
#define GPI_GPE_STS 0x140
#define GPI_GPE_EN 0x160
#define PADOWN_BITS 4
#define PADOWN_SHIFT(p) ((p) % 8 * PADOWN_BITS)
#define PADOWN_MASK(p) (0xf << PADOWN_SHIFT(p))
/* Offset from pad_regs */
#define PADCFG0 0x000
#define PADCFG0_RXEVCFG_SHIFT 25
#define PADCFG0_RXEVCFG_MASK (3 << PADCFG0_RXEVCFG_SHIFT)
#define PADCFG0_RXEVCFG_LEVEL 0
#define PADCFG0_RXEVCFG_EDGE 1
#define PADCFG0_RXEVCFG_DISABLED 2
#define PADCFG0_RXEVCFG_EDGE_BOTH 3
#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 (0xf << PADCFG0_PMODE_SHIFT)
#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 (7 << PADCFG1_TERM_SHIFT)
#define PADCFG1_TERM_20K 4
#define PADCFG1_TERM_2K 3
#define PADCFG1_TERM_5K 2
#define PADCFG1_TERM_1K 1
struct intel_pad_context {
u32 padcfg0;
u32 padcfg1;
};
struct intel_community_context {
u32 *intmask;
};
struct intel_pinctrl_context {
struct intel_pad_context *pads;
struct intel_community_context *communities;
};
/**
* struct intel_pinctrl - Intel pinctrl private structure
* @dev: Pointer to the device structure
* @lock: Lock to serialize register access
* @pctldesc: Pin controller description
* @pctldev: Pointer to the pin controller device
* @chip: GPIO chip in this pin controller
* @soc: SoC/PCH specific pin configuration data
* @communities: All communities in this pin controller
* @ncommunities: Number of communities in this pin controller
* @context: Configuration saved over system sleep
*/
struct intel_pinctrl {
struct device *dev;
spinlock_t lock;
struct pinctrl_desc pctldesc;
struct pinctrl_dev *pctldev;
struct gpio_chip chip;
const struct intel_pinctrl_soc_data *soc;
struct intel_community *communities;
size_t ncommunities;
struct intel_pinctrl_context context;
};
#define gpiochip_to_pinctrl(c) container_of(c, struct intel_pinctrl, chip)
#define pin_to_padno(c, p) ((p) - (c)->pin_base)
static struct intel_community *intel_get_community(struct intel_pinctrl *pctrl,
unsigned pin)
{
struct intel_community *community;
int i;
for (i = 0; i < pctrl->ncommunities; i++) {
community = &pctrl->communities[i];
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;
}
static void __iomem *intel_get_padcfg(struct intel_pinctrl *pctrl, unsigned pin,
unsigned reg)
{
const struct intel_community *community;
unsigned padno;
community = intel_get_community(pctrl, pin);
if (!community)
return NULL;
padno = pin_to_padno(community, pin);
return community->pad_regs + reg + padno * 8;
}
static bool intel_pad_owned_by_host(struct intel_pinctrl *pctrl, unsigned pin)
{
const struct intel_community *community;
unsigned padno, gpp, gpp_offset, offset;
void __iomem *padown;
community = intel_get_community(pctrl, pin);
if (!community)
return false;
if (!community->padown_offset)
return true;
padno = pin_to_padno(community, pin);
gpp =