blob: c850df3eee919fcb7d875c915cd8a5cbc1908637 [file] [log] [blame]
/* gpio-cherryview.c Cherrytrail platform GPIO driver
*
* Copyright (c) 2013, Intel Corporation.
*
* 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.
*
* This program is distributed in the hope that 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/gpio.h>
#include <linux/acpi.h>
#include <linux/acpi_gpio.h>
#include <linux/seq_file.h>
#include <linux/io.h>
#include <asm/intel_chv.h>
#include <linux/pnp.h>
#include "gpiodebug.h"
#define GPIO_PATH_MAX 64
#define FAMILY0_PAD_REGS_OFF 0x4400
#define FAMILY_PAD_REGS_SIZE 0x400
#define MAX_FAMILY_PAD_GPIO_NO 15
#define GPIO_REGS_SIZE 8
#define CV_PADCTRL0_REG 0x000
#define CV_PADCTRL1_REG 0x004
#define CV_INT_STAT_REG 0x300
#define CV_INT_MASK_REG 0x380
#define CV_GPIO_RX_STAT BIT(0)
#define CV_GPIO_TX_STAT BIT(1)
#define CV_GPIO_EN BIT(15)
#define CV_GPIO_PULL BIT(23)
#define CV_CFG_LOCK_MASK BIT(31)
#define CV_INT_CFG_MASK (BIT(0) | BIT(1) | BIT(2))
#define CV_PAD_MODE_MASK (0xF << 16)
#define CV_GPIO_CFG_MASK (BIT(8) | BIT(9) | BIT(10))
#define CV_GPIO_TX_EN (1 << 8)
#define CV_GPIO_RX_EN (2 << 8)
#define CV_INV_RX_DATA BIT(6)
#define CV_INT_SEL_MASK (0xF << 28)
#define CV_PAD_MODE_MASK (0xF << 16)
#define CV_GPIO_PULL_MODE (0xF << 20)
#define CV_GPIO_PULL_STRENGTH_MASK (0x7 << 20)
#define MAX_INTR_LINE_NUM 16
/* When Pad Cfg is locked, driver can only change GPIOTXState or GPIORXState */
#define PAD_CFG_LOCKED(offset) (chv_readl(chv_gpio_reg(&cg->chip, \
offset, CV_PADCTRL1_REG)) & CV_CFG_LOCK_MASK)
enum INTR_CFG {
CV_INTR_DISABLE,
CV_TRIG_EDGE_FALLING,
CV_TRIG_EDGE_RISING,
CV_TRIG_EDGE_BOTH,
CV_TRIG_LEVEL,
};
struct gpio_pad_info {
int family; /* Family ID */
int pad; /* Pad ID in this family */
/* Interrupt line selected (0~15), -1 if not interruptible. */
int interrupt_line;
};
struct gpio_bank_pnp {
char *name;
int gpio_base;
int irq_base;
int ngpio;
struct gpio_pad_info *pads_info;
struct chv_gpio *cg;
};
/* For invalid GPIO number(not found in GPIO list),
* initialize all fields as -1.
*/
static struct gpio_pad_info north_pads_info[CV_NGPIO_NORTH] = {
[GPIO_DFX_0] = { 0, 0, -1 },
[GPIO_DFX_3] = { 0, 1, -1 },
[GPIO_DFX_7] = { 0, 2, -1 },
[GPIO_DFX_1] = { 0, 3, -1 },
[GPIO_DFX_5] = { 0, 4, -1 },
[GPIO_DFX_4] = { 0, 5, -1 },
[GPIO_DFX_8] = { 0, 6, -1 },
[GPIO_DFX_2] = { 0, 7, -1 },
[GPIO_DFX_6] = { 0, 8, -1 },
[9] = { -1, -1, -1 },
[10] = { -1, -1, -1 },
[11] = { -1, -1, -1 },
[12] = { -1, -1, -1 },
[13] = { -1, -1, -1 },
[14] = { -1, -1, -1 },
[GPIO_SUS0] = { 1, 0, -1 },
[SEC_GPIO_SUS10] = { 1, 1, -1 },
[GPIO_SUS3] = { 1, 2, -1 },
[GPIO_SUS7] = { 1, 3, -1 },
[GPIO_SUS1] = { 1, 4, -1 },
[GPIO_SUS5] = { 1, 5, -1 },
[SEC_GPIO_SUS11] = { 1, 6, -1 },
[GPIO_SUS4] = { 1, 7, -1 },
[SEC_GPIO_SUS8] = { 1, 8, -1 },
[GPIO_SUS2] = { 1, 9, -1 },
[GPIO_SUS6] = { 1, 10, -1 },
[CX_PREQ_B] = { 1, 11, -1 },
[SEC_GPIO_SUS9] = { 1, 12, -1 },
[28] = { -1, -1, -1 },
[29] = { -1, -1, -1 },
[TRST_B] = { 2, 0, -1 },
[TCK] = { 2, 1, -1 },
[PROCHOT_B] = { 2, 2, -1 },
[SVIDO_DATA] = { 2, 3, -1 },
[TMS] = { 2, 4, -1 },
[CX_PRDY_B_2] = { 2, 5, -1 },
[TDO_2] = { 2, 6, -1 },
[CX_PRDY_B] = { 2, 7, -1 },
[SVIDO_ALERT_B] = { 2, 8, -1 },
[TDO] = { 2, 9, -1 },
[SVIDO_CLK] = { 2, 10, -1 },
[TDI] = { 2, 11, -1 },
[42] = { -1, -1, -1 },
[43] = { -1, -1, -1 },
[44] = { -1, -1, -1 },
[GP_CAMERASB_05] = { 3, 0, -1 },
[GP_CAMERASB_02] = { 3, 1, -1 },
[GP_CAMERASB_08] = { 3, 2, -1 },
[GP_CAMERASB_00] = { 3, 3, -1 },
[GP_CAMERASB_06] = { 3, 4, -1 },
[GP_CAMERASB_10] = { 3, 5, -1 },
[GP_CAMERASB_03] = { 3, 6, -1 },
[GP_CAMERASB_09] = { 3, 7, -1 },
[GP_CAMERASB_01] = { 3, 8, -1 },
[GP_CAMERASB_07] = { 3, 9, -1 },
[GP_CAMERASB_11] = { 3, 10, -1 },
[GP_CAMERASB_04] = { 3, 11, -1 },
[57] = { -1, -1, -1 },
[58] = { -1, -1, -1 },
[59] = { -1, -1, -1 },
[PANEL0_BKLTEN] = { 4, 0, -1 },
[HV_DDI0_HPD] = { 4, 1, -1 },
[HV_DDI2_DDC_SDA] = { 4, 2, -1 },
[PANEL1_BKLTCTL] = { 4, 3, -1 },
[HV_DDI1_HPD] = { 4, 4, -1 },
[PANEL0_BKLTCTL] = { 4, 5, -1 },
[HV_DDI0_DDC_SDA] = { 4, 6, -1 },
[HV_DDI2_DDC_SCL] = { 4, 7, -1 },
[HV_DDI2_HPD] = { 4, 8, -1 },
[PANEL1_VDDEN] = { 4, 9, -1 },
[PANEL1_BKLTEN] = { 4, 10, -1 },
[HV_DDI0_DDC_SCL] = { 4, 11, -1 },
[PANEL0_VDDEN] = { 4, 12, -1 },
};
static struct gpio_pad_info southeast_pads_info[CV_NGPIO_SOUTHEAST] = {
[MF_PLT_CLK0] = { 0, 0, -1 },
[PWM1] = { 0, 1, -1 },
[MF_PLT_CLK1] = { 0, 2, -1 },
[MF_PLT_CLK4] = { 0, 3, -1 },
[MF_PLT_CLK3] = { 0, 4, -1 },
[PWM0] = { 0, 5, -1 },
[MF_PLT_CLK5] = { 0, 6, -1 },
[MF_PLT_CLK2] = { 0, 7, -1 },
[9] = { -1, -1, -1 },
[10] = { -1, -1, -1 },
[11] = { -1, -1, -1 },
[12] = { -1, -1, -1 },
[13] = { -1, -1, -1 },
[14] = { -1, -1, -1 },
[SDMMC2_D3_CD_B] = { 1, 0, -1 },
[SDMMC1_CLK] = { 1, 1, -1 },
[SDMMC1_D0] = { 1, 2, -1 },
[SDMMC2_D1] = { 1, 3, -1 },
[SDMMC2_CLK] = { 1, 4, -1 },
[SDMMC1_D2] = { 1, 5, -1 },
[SDMMC2_D2] = { 1, 6, -1 },
[SDMMC2_CMD] = { 1, 7, -1 },
[SDMMC1_CMD] = { 1, 8, -1 },
[SDMMC1_D1] = { 1, 9, -1 },
[SDMMC2_D0] = { 1, 10, -1 },
[SDMMC1_D3_CD_B] = { 1, 11, -1 },
[27] = { -1, -1, -1 },
[28] = { -1, -1, -1 },
[29] = { -1, -1, -1 },
[SDMMC3_D1] = { 2, 0, -1 },
[SDMMC3_CLK] = { 2, 1, -1 },
[SDMMC3_D3] = { 2, 2, -1 },
[SDMMC3_D2] = { 2, 3, -1 },
[SDMMC3_CMD] = { 2, 4, -1 },
[SDMMC3_D0] = { 2, 5, -1 },
[36] = { -1, -1, -1 },
[37] = { -1, -1, -1 },
[38] = { -1, -1, -1 },
[39] = { -1, -1, -1 },
[40] = { -1, -1, -1 },
[41] = { -1, -1, -1 },
[42] = { -1, -1, -1 },
[43] = { -1, -1, -1 },
[44] = { -1, -1, -1 },
[MF_LPC_AD2] = { 3, 0, -1 },
[LPC_CLKRUNB] = { 3, 1, -1 },
[MF_LPC_AD0] = { 3, 2, -1 },
[LPC_FRAMEB] = { 3, 3, -1 },
[MF_LPC_CLKOUT1] = { 3, 4, -1 },
[MF_LPC_AD3] = { 3, 5, -1 },
[MF_LPC_CLKOUT0] = { 3, 6, -1 },
[MF_LPC_AD1] = { 3, 7, -1 },
[53] = { -1, -1, -1 },
[54] = { -1, -1, -1 },
[55] = { -1, -1, -1 },
[56] = { -1, -1, -1 },
[57] = { -1, -1, -1 },
[58] = { -1, -1, -1 },
[59] = { -1, -1, -1 },
[SPI1_MISO] = { 4, 0, -1 },
[SPI1_CSO_B] = { 4, 1, -1 },
[SPI1_CLK] = { 4, 2, -1 },
[MMC1_D6] = { 4, 3, -1 },
[SPI1_MOSI] = { 4, 4, -1 },
[MMC1_D5] = { 4, 5, -1 },
[SPI1_CS1_B] = { 4, 6, -1 },
[MMC1_D4_SD_WE] = { 4, 7, -1 },
[MMC1_D7] = { 4, 8, -1 },
[MMC1_RCLK] = { 4, 9, -1 },
[70] = { -1, -1, -1 },
[71] = { -1, -1, -1 },
[72] = { -1, -1, -1 },
[73] = { -1, -1, -1 },
[74] = { -1, -1, -1 },
[USB_OC1_B] = { 5, 0, -1 },
[PMU_RESETBUTTON_B] = { 5, 1, -1 },
[GPIO_ALERT] = { 5, 2, -1 },
[SDMMC3_PWR_EN_B] = { 5, 3, -1 },
[ILB_SERIRQ] = { 5, 4, -1 },
[USB_OC0_B] = { 5, 5, -1 },
[SDMMC3_CD_B] = { 5, 6, -1 },
[SPKR] = { 5, 7, -1 },
[SUSPWRDNACK] = { 5, 8, -1 },
[SPARE_PIN] = { 5, 9, -1 },
[SDMMC3_1P8_EN] = { 5, 10, -1 },
};
static struct gpio_pad_info east_pads_info[CV_NGPIO_EAST] = {
[PMU_SLP_S3_B] = { 0, 0, -1 },
[PMU_BATLOW_B] = { 0, 1, -1 },
[SUS_STAT_B] = { 0, 2, -1 },
[PMU_SLP_S0IX_B] = { 0, 3, -1 },
[PMU_AC_PRESENT] = { 0, 4, -1 },
[PMU_PLTRST_B] = { 0, 5, -1 },
[PMU_SUSCLK] = { 0, 6, -1 },
[PMU_SLP_LAN_B] = { 0, 7, -1 },
[PMU_PWRBTN_B] = { 0, 8, -1 },
[PMU_SLP_S4_B] = { 0, 9, -1 },
[PMU_WAKE_B] = { 0, 10, -1 },
[PMU_WAKE_LAN_B] = { 0, 11, -1 },
[12] = { -1, -1, -1 },
[13] = { -1, -1, -1 },
[14] = { -1, -1, -1 },
[MF_ISH_GPIO_3] = { 1, 0, -1 },
[MF_ISH_GPIO_7] = { 1, 1, -1 },
[MF_ISH_I2C1_SCL] = { 1, 2, -1 },
[MF_ISH_GPIO_1] = { 1, 3, -1 },
[MF_ISH_GPIO_5] = { 1, 4, -1 },
[MF_ISH_GPIO_9] = { 1, 5, -1 },
[MF_ISH_GPIO_0] = { 1, 6, -1 },
[MF_ISH_GPIO_4] = { 1, 7, -1 },
[MF_ISH_GPIO_8] = { 1, 8, -1 },
[MF_ISH_GPIO_2] = { 1, 9, -1 },
[MF_ISH_GPIO_6] = { 1, 10, -1 },
[MF_ISH_I2C1_SDA] = { 1, 11, -1 },
};
static struct gpio_pad_info southwest_pads_info[CV_NGPIO_SOUTHWEST] = {
[FST_SPI_D2] = { 0, 0, -1 },
[FST_SPI_D0] = { 0, 1, -1 },
[FST_SPI_CLK] = { 0, 2, -1 },
[FST_SPI_D3] = { 0, 3, -1 },
[FST_SPI_CS1_B] = { 0, 4, -1 },
[FST_SPI_D1] = { 0, 5, -1 },
[FST_SPI_CS0_B] = { 0, 6, -1 },
[FST_SPI_CS2_B] = { 0, 7, -1 },
[8] = { -1, -1, -1 },
[9] = { -1, -1, -1 },
[10] = { -1, -1, -1 },
[11] = { -1, -1, -1 },
[12] = { -1, -1, -1 },
[13] = { -1, -1, -1 },
[14] = { -1, -1, -1 },
[UART1_RTS_B] = { 1, 0, -1 },
[UART1_RXD] = { 1, 1, -1 },
[UART2_RXD] = { 1, 2, -1 },
[UART1_CTS_B] = { 1, 3, -1 },
[UART2_RTS_B] = { 1, 4, -1 },
[UART1_TXD] = { 1, 5, -1 },
[UART2_TXD] = { 1, 6, -1 },
[UART2_CTS_B] = { 1, 7, -1 },
[23] = { -1, -1, -1 },
[24] = { -1, -1, -1 },
[25] = { -1, -1, -1 },
[26] = { -1, -1, -1 },
[27] = { -1, -1, -1 },
[28] = { -1, -1, -1 },
[29] = { -1, -1, -1 },
[MF_HDA_CLK] = { 2, 0, -1 },
[MF_HDA_RSTB] = { 2, 1, -1 },
[MF_HDA_SDIO] = { 2, 2, -1 },
[MF_HDA_SDO] = { 2, 3, -1 },
[MF_HDA_DOCKRSTB] = { 2, 4, -1 },
[MF_HDA_SYNC] = { 2, 5, -1 },
[MF_HDA_SDI1] = { 2, 6, -1 },
[MF_HDA_DOCKENB] = { 2, 7, -1 },
[38] = { -1, -1, -1 },
[39] = { -1, -1, -1 },
[40] = { -1, -1, -1 },
[41] = { -1, -1, -1 },
[42] = { -1, -1, -1 },
[43] = { -1, -1, -1 },
[44] = { -1, -1, -1 },
[I2C5_SDA] = { 3, 0, -1 },
[I2C4_SDA] = { 3, 1, -1 },
[I2C6_SDA] = { 3, 2, -1 },
[I2C5_SCL] = { 3, 3, -1 },
[I2C_NFC_SDA] = { 3, 4, -1 },
[I2C4_SCL] = { 3, 5, -1 },
[I2C6_SCL] = { 3, 6, -1 },
[I2C_NFC_SCL] = { 3, 7, -1 },
[53] = { -1, -1, -1 },
[54] = { -1, -1, -1 },
[55] = { -1, -1, -1 },
[56] = { -1, -1, -1 },
[57] = { -1, -1, -1 },
[58] = { -1, -1, -1 },
[59] = { -1, -1, -1 },
[I2C1_SDA] = { 4, 0, -1 },
[I2C0_SDA] = { 4, 1, -1 },
[I2C2_SDA] = { 4, 2, -1 },
[I2C1_SCL] = { 4, 3, -1 },
[I2C3_SDA] = { 4, 4, -1 },
[I2C0_SCL] = { 4, 5, -1 },
[I2C2_SCL] = { 4, 6, -1 },
[I2C3_SCL] = { 4, 7, -1 },
[68] = { -1, -1, -1 },
[69] = { -1, -1, -1 },
[70] = { -1, -1, -1 },
[71] = { -1, -1, -1 },
[72] = { -1, -1, -1 },
[73] = { -1, -1, -1 },
[74] = { -1, -1, -1 },
[SATA_GP0] = { 5, 0, -1 },
[SATA_GP1] = { 5, 1, -1 },
[SATA_LEDN] = { 5, 2, -1 },
[SATA_GP2] = { 5, 3, -1 },
[MF_SMB_ALERTB] = { 5, 4, -1 },
[SATA_GP3] = { 5, 5, -1 },
[MF_SMB_CLK] = { 5, 6, -1 },
[MF_SMB_DATA] = { 5, 7, -1 },
[83] = { -1, -1, -1 },
[84] = { -1, -1, -1 },
[85] = { -1, -1, -1 },
[86] = { -1, -1, -1 },
[87] = { -1, -1, -1 },
[88] = { -1, -1, -1 },
[89] = { -1, -1, -1 },
[PCIE_CLKREQ0B] = { 6, 0, -1 },
[PCIE_CLKREQ1B] = { 6, 1, -1 },
[GP_SSP_2_CLK] = { 6, 2, -1 },
[PCIE_CLKREQ2B] = { 6, 3, -1 },
[GP_SSP_2_RXD] = { 6, 4, -1 },
[PCIE_CLKREQ3B] = { 6, 5, -1 },
[GP_SSP_2_FS] = { 6, 6, -1 },
[GP_SSP_2_TXD] = { 6, 7, -1 },
};
static struct gpio_bank_pnp chv_banks_pnp[] = {
{
.name = "GPO0",
.gpio_base = CV_GPIO_SOUTHWEST_BASE,
.irq_base = CV_GPIO_SOUTHWEST_IRQBASE,
.ngpio = CV_NGPIO_SOUTHWEST,
.pads_info = southwest_pads_info,
},
{
.name = "GPO1",
.gpio_base = CV_GPIO_NORTH_BASE,
.irq_base = CV_GPIO_NORTH_IRQBASE,
.ngpio = CV_NGPIO_NORTH,
.pads_info = north_pads_info,
},
{
.name = "GPO2",
.gpio_base = CV_GPIO_EAST_BASE,
.irq_base = CV_GPIO_EAST_IRQBASE,
.ngpio = CV_NGPIO_EAST,
.pads_info = east_pads_info,
},
{
.name = "GPO3",
.gpio_base = CV_GPIO_SOUTHEAST_BASE,
.irq_base = CV_GPIO_SOUTHEAST_IRQBASE,
.ngpio = CV_NGPIO_SOUTHEAST,
.pads_info = southeast_pads_info,
},
};
struct chv_gpio {
struct gpio_chip chip;
struct pnp_dev *pdev;
spinlock_t lock;
void __iomem *reg_base;
struct gpio_pad_info *pad_info;
struct irq_domain *domain;
int irq_base;
int intr_lines[MAX_INTR_LINE_NUM];
struct gpio_debug *debug;
};
static DEFINE_SPINLOCK(chv_reg_access_lock);
#define to_chv_priv(chip) container_of(chip, struct chv_gpio, chip)
static void __iomem *chv_gpio_reg(struct gpio_chip *chip, unsigned offset,
int reg)
{
struct chv_gpio *cg = to_chv_priv(chip);
u32 reg_offset;
void __iomem *ptr;
if (reg == CV_INT_STAT_REG || reg == CV_INT_MASK_REG)
reg_offset = 0;
else
reg_offset = FAMILY0_PAD_REGS_OFF +
FAMILY_PAD_REGS_SIZE * (offset / MAX_FAMILY_PAD_GPIO_NO) +
GPIO_REGS_SIZE * (offset % MAX_FAMILY_PAD_GPIO_NO);
ptr = (void __iomem *) (cg->reg_base + reg_offset + reg);
return ptr;
}
static u32 chv_readl(void __iomem *reg)
{
u32 value;
unsigned long flags;
spin_lock_irqsave(&chv_reg_access_lock, flags);
value = readl(reg);
spin_unlock_irqrestore(&chv_reg_access_lock, flags);
return value;
}
static void chv_writel(u32 value, void __iomem *reg)
{
unsigned long flags;
spin_lock_irqsave(&chv_reg_access_lock, flags);
writel(value, reg);
/* simple readback to confirm the bus transferring done */
readl(reg);
spin_unlock_irqrestore(&chv_reg_access_lock, flags);
}
static int chv_gpio_request(struct gpio_chip *chip, unsigned offset)
{
struct chv_gpio *cg = to_chv_priv(chip);
if (cg->pad_info[offset].family < 0)
return -EINVAL;
return 0;
}
static void chv_gpio_free(struct gpio_chip *chip, unsigned offset)
{
return;
}
void lnw_gpio_set_alt(int gpio, int alt)
{
struct gpio_bank_pnp *bank;
struct chv_gpio *cg = NULL;
void __iomem *reg;
unsigned long flags;
int value;
u32 offset;
int i;
int nbanks = sizeof(chv_banks_pnp) / sizeof(struct gpio_bank_pnp);
for (i = 0; i < nbanks; i++) {
bank = chv_banks_pnp + i;
if (gpio >= bank->gpio_base &&
gpio < (bank->gpio_base + bank->ngpio)) {
cg = bank->cg;
offset = gpio - bank->gpio_base;
break;
}
}
if (!cg) {
pr_err("chv_gpio: can not find pin %d\n", gpio);
return;
}
if (cg->pad_info[offset].family < 0)
return;
if (PAD_CFG_LOCKED(offset))
return;
reg = chv_gpio_reg(&cg->chip, offset, CV_PADCTRL0_REG);
spin_lock_irqsave(&cg->lock, flags);
value = chv_readl(reg) & (~CV_PAD_MODE_MASK);
value = value | ((alt & 0xF) << 16);
chv_writel(value, reg);
spin_unlock_irqrestore(&cg->lock, flags);
}
EXPORT_SYMBOL_GPL(lnw_gpio_set_alt);
int gpio_get_alt(int gpio)
{
struct gpio_bank_pnp *bank;
struct chv_gpio *cg = NULL;
void __iomem *reg;
int value;
u32 offset;
int i;
int nbanks = sizeof(chv_banks_pnp) / sizeof(struct gpio_bank_pnp);
for (i = 0; i < nbanks; i++) {
bank = chv_banks_pnp + i;
if (gpio >= bank->gpio_base &&
gpio < (bank->gpio_base + bank->ngpio)) {
cg = bank->cg;
offset = gpio - bank->gpio_base;
break;
}
}
if (!cg) {
pr_err("chv_gpio: can not find pin %d\n", gpio);
return -1;
}
if (cg->pad_info[offset].family < 0)
return -1;
reg = chv_gpio_reg(&cg->chip, offset, CV_PADCTRL0_REG);
value = (chv_readl(reg) & CV_PAD_MODE_MASK) >> 16;
return value;
}
EXPORT_SYMBOL_GPL(gpio_get_alt);
static void chv_update_irq_type(struct chv_gpio *cg, unsigned type,
void __iomem *reg)
{
u32 value;
value = chv_readl(reg);
value &= ~CV_INT_CFG_MASK;
value &= ~CV_INV_RX_DATA;
if (type & IRQ_TYPE_EDGE_BOTH) {
if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
value |= CV_TRIG_EDGE_BOTH;
else if (type & IRQ_TYPE_EDGE_RISING)
value |= CV_TRIG_EDGE_RISING;
else if (type & IRQ_TYPE_EDGE_FALLING)
value |= CV_TRIG_EDGE_FALLING;
} else if (type & IRQ_TYPE_LEVEL_MASK) {
value |= CV_TRIG_LEVEL;
if (type & IRQ_TYPE_LEVEL_LOW)
value |= CV_INV_RX_DATA;
}
chv_writel(value, reg);
}
/* BIOS programs IntSel bits for shared interrupt.
* GPIO driver follows it.
*/
static void pad_intr_line_save(struct chv_gpio *cg, unsigned offset)
{
u32 value;
u32 intr_line;
void __iomem *reg = chv_gpio_reg(&cg->chip, offset, CV_PADCTRL0_REG);
struct gpio_pad_info *pad_info = cg->pad_info + offset;
value = chv_readl(reg);
intr_line = (value & CV_INT_SEL_MASK) >> 28;
pad_info->interrupt_line = intr_line;
cg->intr_lines[intr_line] = offset;
}
static int chv_irq_type(struct irq_data *d, unsigned type)
{
struct chv_gpio *cg = irq_data_get_irq_chip_data(d);
u32 offset = irqd_to_hwirq(d);
void __iomem *reg;
unsigned long flags;
int ret = 0;
if (offset >= cg->chip.ngpio)
return -EINVAL;
if (cg->pad_info[offset].family < 0)
return -EINVAL;
spin_lock_irqsave(&cg->lock, flags);
/* Pins which can be used as shared interrupt are configured in BIOS.
* Driver trusts BIOS configurations and assigns different handler
* according to the irq type.
*
* Driver needs to save the mapping between each pin and
* its interrupt line.
* 1. If the pin cfg is locked in BIOS:
* Trust BIOS has programmed IntWakeCfg bits correctly,
* driver just needs to save the mapping.
* 2. If the pin cfg is not locked in BIOS:
* Driver programs the IntWakeCfg bits and save the mapping.
*
*/
if (!PAD_CFG_LOCKED(offset)) {
reg = chv_gpio_reg(&cg->chip, offset, CV_PADCTRL1_REG);
chv_update_irq_type(cg, type, reg);
}
pad_intr_line_save(cg, offset);
if (type & IRQ_TYPE_EDGE_BOTH)
__irq_set_handler_locked(d->irq, handle_edge_irq);
else if (type & IRQ_TYPE_LEVEL_MASK)
__irq_set_handler_locked(d->irq, handle_level_irq);
spin_unlock_irqrestore(&cg->lock, flags);
return ret;
}
static int chv_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct chv_gpio *cg = to_chv_priv(chip);
void __iomem *reg;
u32 value;
if (cg->pad_info[offset].family < 0)
return -EINVAL;
reg = chv_gpio_reg(chip, offset, CV_PADCTRL0_REG);
value = chv_readl(reg);
return value & CV_GPIO_RX_STAT;
}
static void chv_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct chv_gpio *cg = to_chv_priv(chip);
void __iomem *reg;
unsigned long flags;
u32 old_val;
if (cg->pad_info[offset].family < 0)
return;
reg = chv_gpio_reg(chip, offset, CV_PADCTRL0_REG);
spin_lock_irqsave(&cg->lock, flags);
old_val = chv_readl(reg);
if (value)
chv_writel(old_val | CV_GPIO_TX_STAT, reg);
else
chv_writel(old_val & ~CV_GPIO_TX_STAT, reg);
spin_unlock_irqrestore(&cg->lock, flags);
}
static int chv_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
struct chv_gpio *cg = to_chv_priv(chip);
void __iomem *reg;
unsigned long flags;
u32 value;
if (cg->pad_info[offset].family < 0)
return -EINVAL;
if (PAD_CFG_LOCKED(offset))
return 0;
reg = chv_gpio_reg(chip, offset, CV_PADCTRL0_REG);
spin_lock_irqsave(&cg->lock, flags);
value = chv_readl(reg) & (~CV_GPIO_CFG_MASK);
/* Disable TX and Enable RX */
value |= CV_GPIO_RX_EN;
chv_writel(value, reg);
spin_unlock_irqrestore(&cg->lock, flags);
return 0;
}
static int chv_gpio_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
{
struct chv_gpio *cg = to_chv_priv(chip);
void __iomem *ctrl0, *ctrl1;
unsigned long flags;
u32 reg_val;
if (cg->pad_info[offset].family < 0)
return -EINVAL;
if (PAD_CFG_LOCKED(offset))
return 0;
ctrl0 = chv_gpio_reg(chip, offset, CV_PADCTRL0_REG);
ctrl1 = chv_gpio_reg(chip, offset, CV_PADCTRL1_REG);
spin_lock_irqsave(&cg->lock, flags);
/* Make sure interrupt of this pad is disabled */
chv_update_irq_type(cg, IRQ_TYPE_NONE, ctrl1);
reg_val = chv_readl(ctrl0) & (~CV_GPIO_CFG_MASK);
/* Enable both RX and TX, control TX State */
if (value)
reg_val |= CV_GPIO_TX_STAT;
else
reg_val &= ~CV_GPIO_TX_STAT;
chv_writel(reg_val, ctrl0);
spin_unlock_irqrestore(&cg->lock, flags);
return 0;
}
static void chv_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
struct chv_gpio *cg = container_of(chip, struct chv_gpio, chip);
int i;
unsigned long flags;
u32 ctrl0, ctrl1, offs;
void __iomem *reg;
spin_lock_irqsave(&cg->lock, flags);
reg = chv_gpio_reg(&cg->chip, 0, CV_INT_STAT_REG);
seq_printf(s, "CV_INT_STAT_REG: 0x%x\n", chv_readl(reg));
reg = chv_gpio_reg(&cg->chip, 0, CV_INT_MASK_REG);
seq_printf(s, "CV_INT_MASK_REG: 0x%x\n", chv_readl(reg));
for (i = 0; i < 16; i++)
seq_printf(s, "intline: %d, offset: %d\n",
i, cg->intr_lines[i]);
for (i = 0; i < cg->chip.ngpio; i++) {
if (cg->pad_info[i].family < 0) {
seq_printf(s, "gpio-%d\t\tInvalid\n", i);
continue;
}
offs = FAMILY0_PAD_REGS_OFF +
FAMILY_PAD_REGS_SIZE * (i / MAX_FAMILY_PAD_GPIO_NO) +
GPIO_REGS_SIZE * (i % MAX_FAMILY_PAD_GPIO_NO);
ctrl0 = chv_readl(chv_gpio_reg(&cg->chip, i, CV_PADCTRL0_REG));
ctrl1 = chv_readl(chv_gpio_reg(&cg->chip, i, CV_PADCTRL1_REG));
seq_printf(s, " gpio-%-3d %s %s %s pad-%-3d offset:0x%03x "
"mux:%d %s %s %s %s %s "
"IntSel:%d ctrl0: 0x%x ctrl1: 0x%x\n",
i,
((ctrl0 & CV_GPIO_CFG_MASK) < CV_GPIO_RX_EN) ? "out" : " ",
((ctrl0 & CV_GPIO_CFG_MASK) == CV_GPIO_RX_EN) ? "in" : " ",
(ctrl0 & CV_GPIO_RX_STAT) ? "high" : " low ",
cg->pad_info[i].pad,
offs,
(ctrl0 & CV_PAD_MODE_MASK) >> 16,
(ctrl1 & CV_INT_CFG_MASK) == 0x0 ? "disabled" : "",
(ctrl1 & CV_INT_CFG_MASK) == 0x1 ? "fall" : "",
(ctrl1 & CV_INT_CFG_MASK) == 0x2 ? "rise" : "",
(ctrl1 & CV_INT_CFG_MASK) == 0x3 ? "both" : "",
(ctrl1 & CV_INT_CFG_MASK) == 0x4 ?
((ctrl1 & CV_INV_RX_DATA) ? "level-low" : "level-high") : "",
(ctrl0 & CV_INT_SEL_MASK) >> 28,
ctrl0,
ctrl1);
}
spin_unlock_irqrestore(&cg->lock, flags);
}
static void chv_irq_unmask(struct irq_data *d)
{
struct chv_gpio *cg = irq_data_get_irq_chip_data(d);
u32 offset = irqd_to_hwirq(d);
int interrupt_line;
u32 value;
void __iomem *reg = chv_gpio_reg(&cg->chip, 0, CV_INT_MASK_REG);
unsigned long flags;
struct gpio_pad_info *pad_info = cg->pad_info + offset;
if (cg->pad_info[offset].family < 0)
return;
if (offset >= cg->chip.ngpio)
return;
spin_lock_irqsave(&cg->lock, flags);
interrupt_line = pad_info->interrupt_line;
/* Unmask if this GPIO has valid interrupt line */
if (interrupt_line >= 0) {
value = chv_readl(reg);
value |= (1 << interrupt_line);
chv_writel(value, reg);
} else {
dev_warn(&cg->pdev->dev,
"Trying to unmask GPIO intr which is not allocated\n");
}
spin_unlock_irqrestore(&cg->lock, flags);
}
static void chv_irq_mask(struct irq_data *d)
{
struct chv_gpio *cg = irq_data_get_irq_chip_data(d);
u32 offset = irqd_to_hwirq(d);
int interrupt_line;
u32 value;
unsigned long flags;
void __iomem *reg = chv_gpio_reg(&cg->chip, 0, CV_INT_MASK_REG);
struct gpio_pad_info *pad_info = cg->pad_info + offset;
if (cg->pad_info[offset].family < 0)
return;
if (offset >= cg->chip.ngpio)
return;
spin_lock_irqsave(&cg->lock, flags);
interrupt_line = pad_info->interrupt_line;
/* Mask if this GPIO has valid interrupt line */
if (interrupt_line >= 0) {
value = chv_readl(reg);
value &= (~(1 << interrupt_line));
chv_writel(value, reg);
} else {
dev_warn(&cg->pdev->dev,
"Trying to mask GPIO intr which is not allocated\n");
}
spin_unlock_irqrestore(&cg->lock, flags);
}
static int chv_irq_wake(struct irq_data *d, unsigned on)
{
return 0;
}
static void chv_irq_ack(struct irq_data *d)
{
}
static void chv_irq_shutdown(struct irq_data *d)
{
struct chv_gpio *cg = irq_data_get_irq_chip_data(d);
u32 offset = irqd_to_hwirq(d);
void __iomem *reg;
unsigned long flags;
if (cg->pad_info[offset].family < 0)
return;
reg = chv_gpio_reg(&cg->chip, offset, CV_PADCTRL1_REG);
chv_irq_mask(d);
if (!PAD_CFG_LOCKED(offset)) {
spin_lock_irqsave(&cg->lock, flags);
chv_update_irq_type(cg, IRQ_TYPE_NONE, reg);
spin_unlock_irqrestore(&cg->lock, flags);
}
}
static struct irq_chip chv_irqchip = {
.name = "LNW-GPIO",
.irq_mask = chv_irq_mask,
.irq_unmask = chv_irq_unmask,
.irq_set_type = chv_irq_type,
.irq_set_wake = chv_irq_wake,
.irq_ack = chv_irq_ack,
.irq_shutdown = chv_irq_shutdown,
};
static void chv_gpio_irq_dispatch(struct chv_gpio *cg)
{
u32 intr_line, mask, offset;
void __iomem *reg, *mask_reg;
u32 pending;
struct gpio_debug *debug = cg->debug;
/* each GPIO controller has one INT_STAT reg */
reg = chv_gpio_reg(&cg->chip, 0, CV_INT_STAT_REG);
mask_reg = chv_gpio_reg(&cg->chip, 0, CV_INT_MASK_REG);
while ((pending = (chv_readl(reg) & chv_readl(mask_reg) & 0xFFFF))) {
intr_line = __ffs(pending);
mask = BIT(intr_line);
chv_writel(mask, reg);
offset = cg->intr_lines[intr_line];
if (unlikely(offset < 0)) {
dev_warn(&cg->pdev->dev, "unregistered shared irq\n");
continue;
}
DEFINE_DEBUG_IRQ_CONUNT_INCREASE(cg->chip.base + offset);
generic_handle_irq(irq_find_mapping(cg->domain, offset));
}
}
static void chv_gpio_irq_handler(unsigned irq, struct irq_desc *desc)
{
struct irq_data *data = irq_desc_get_irq_data(desc);
struct chv_gpio *cg = irq_data_get_irq_handler_data(data);
struct irq_chip *chip = irq_data_get_irq_chip(data);
chv_gpio_irq_dispatch(cg);
chip->irq_eoi(data);
}
static void chv_irq_init_hw(struct chv_gpio *cg)
{
void __iomem *reg;
reg = chv_gpio_reg(&cg->chip, 0, CV_INT_STAT_REG);
chv_writel(0xffff, reg);
}
static char conf_reg_msg[] =
"\nGPIO Pad Ctrl0 register:\n"
"\t[ 0: 0]\tGPIO RX State\n"
"\t[ 1: 1]\tGPIO TX State\n"
"\t[ 7: 2]\tReserved\n"
"\t[10: 8]\tGPIO Config\n"
"\t[14:11]\tReserved\n"
"\t[15:15]\tGPIO Enable\n"
"\t[19:16]\tPad Mode\n"
"\t[23:20]\tTermination\n"
"\t[25:24]\tRX/TX Enable Config\n"
"\t[27:26]\tGlitch Filter Config\n"
"\t[31:28]\tInterrupt Select\n";
static char *pinvalue[] = {"low", "high"};
static char *pindirection[] = {"Both", "Out", "In", "HiZ"};
static char *irqtype[] = {"irq_none", "edge_falling", "edge_rising",
"edge_both", "level_high", "level_low"};
static char *pinmux[] = {"mode0", "mode1", "mode2", "mode3", "mode4", "mode5",
"mode6", "mode7", "mode8", "mode9", "mode10", "mode11",
"mode12", "mode13", "mode14", "mode15", "gpio"};
static char *pullmode[] = {"pull_none", "pulldown_20k", "pulldown_5k",
"pulldown_1k", "pullup_20k", "pullup_5k", "pullup_1k"};
static char *odstate[] = {"od_disable", "od_enable"};
static char *irqline[] = {"line0", "line1", "line2", "line3", "line4",
"line5", "line6", "line7", "line8", "line9", "line10",
"line11", "line12", "line13", "line14", "line15"};
static int chv_get_generic(struct gpio_control *control, void *private_data,
unsigned gpio)
{
struct chv_gpio *cg = private_data;
void __iomem *reg;
u32 offset = gpio - cg->chip.base;
u32 value;
u32 shift = control->shift;
u32 mask = control->mask;
int num;
reg = chv_gpio_reg(&cg->chip, offset, control->reg);
value = chv_readl(reg);
if (control->get_handle)
num = control->get_handle(value);
else
num = (value & (mask << shift)) >> shift;
if (num < control->num)
return num;
return -1;
}
static int chv_set_generic(struct gpio_control *control, void *private_data,
unsigned gpio, unsigned int num)
{
struct chv_gpio *cg = private_data;
void __iomem *reg;
unsigned long flags;
u32 offset = gpio - cg->chip.base;
u32 value;
u32 shift = control->shift;
u32 mask = control->mask;
reg = chv_gpio_reg(&cg->chip, offset, control->reg);
spin_lock_irqsave(&cg->lock, flags);
value = chv_readl(reg);
if (control->set_handle)
control->set_handle(num, &value);
else {
value &= ~(mask << shift);
value |= (num & mask) << shift;
}
chv_writel(value, reg);
spin_unlock_irqrestore(&cg->lock, flags);
return 0;
}
static int pinmux_get_handle(int value)
{
int num;
if (value & CV_GPIO_EN)
num = 16;
else
num = (value & CV_PAD_MODE_MASK) >> 16;
return num;
}
static void pinmux_set_handle(unsigned int num, int *value)
{
if (num == 16)
*value |= CV_GPIO_EN;
else if (num < 16) {
*value &= ~(CV_GPIO_EN | CV_PAD_MODE_MASK);
*value |= (num << 16);
}
}
static int pullmode_get_handle(int value)
{
int num;
if (!(value & CV_GPIO_PULL_MODE))
return 0;
num = (value & CV_GPIO_PULL) ? 4 : 1;
num += __ffs((value & CV_GPIO_PULL_STRENGTH_MASK) >> 20);
return num;
}
static void pullmode_set_handle(unsigned int num, int *value)
{
*value &= ~CV_GPIO_PULL_MODE;
if (num == 0)
return;
else if (num >= 4 && num < 7) {
*value |= CV_GPIO_PULL;
num -= 3;
} else if (num >= 7)
return;
*value |= (1 << ((num - 1) + 20));
}
static int pinvalue_get_handle(int value)
{
return value & CV_GPIO_RX_STAT;
}
static void pinvalue_set_handle(unsigned int num, int *value)
{
/* change pin value if output enabled */
if ((*value & CV_GPIO_CFG_MASK) >= CV_GPIO_RX_EN)
return;
if (num)
*value |= CV_GPIO_TX_STAT;
else
*value &= ~CV_GPIO_TX_STAT;
}
static int irqtype_get_handle(int value)
{
int num;
num = (value & CV_INT_CFG_MASK);
if (num == 4 && (value & CV_INV_RX_DATA))
num = 5;
return num;
}
static void irqtype_set_handle(unsigned int num, int *value)
{
*value &= ~CV_INT_CFG_MASK;
if (num == 4)
*value &= ~CV_INV_RX_DATA;
if (num == 5)
*value |= CV_INV_RX_DATA;
*value |= num;
}
#define CHV_NORMAL_CONTROL(xtype, xinfo, xnum, xreg, xshift, xmask) \
{ .type = xtype, .pininfo = xinfo, .num = xnum, .reg = xreg, \
.shift = xshift, .mask = xmask, .get = chv_get_generic, \
.set = chv_set_generic, .get_handle = NULL, .set_handle = NULL}
#define CHV_SPECIAL_CONTROL(xtype, xinfo, xnum, xreg, get_hdl, set_hdl) \
{ .type = xtype, .pininfo = xinfo, .num = xnum, .reg = xreg, \
.get = chv_get_generic, .set = chv_set_generic, \
.get_handle = get_hdl, .set_handle = set_hdl}
static struct gpio_control chv_gpio_controls[] = {
CHV_NORMAL_CONTROL(TYPE_DIRECTION, pindirection, 4, CV_PADCTRL0_REG, 8, 0x7),
CHV_NORMAL_CONTROL(TYPE_OPEN_DRAIN, odstate, 2, CV_PADCTRL1_REG, 3, 0x1),
CHV_NORMAL_CONTROL(TYPE_IRQ_LINE, irqline, 16, CV_PADCTRL0_REG, 28, 0xF),
CHV_SPECIAL_CONTROL(TYPE_PINMUX, pinmux, 17, CV_PADCTRL0_REG,
pinmux_get_handle, pinmux_set_handle),
CHV_SPECIAL_CONTROL(TYPE_PULLMODE, pullmode, 7, CV_PADCTRL0_REG,
pullmode_get_handle, pullmode_set_handle),
CHV_SPECIAL_CONTROL(TYPE_PIN_VALUE, pinvalue, 2, CV_PADCTRL0_REG,
pinvalue_get_handle, pinvalue_set_handle),
CHV_SPECIAL_CONTROL(TYPE_IRQ_TYPE, irqtype, 6, CV_PADCTRL1_REG,
irqtype_get_handle, irqtype_set_handle),
};
static unsigned int chv_get_conf_reg(struct gpio_debug *debug, unsigned gpio)
{
struct chv_gpio *cg = debug->private_data;
void __iomem *reg;
u32 offset = gpio - cg->chip.base;
u32 value;
reg = chv_gpio_reg(&cg->chip, offset, CV_PADCTRL0_REG);
value = chv_readl(reg);
return value;
}
static void chv_set_conf_reg(struct gpio_debug *debug, unsigned gpio,
unsigned int value)
{
struct chv_gpio *cg = debug->private_data;
void __iomem *reg;
u32 offset = gpio - cg->chip.base;
unsigned long flags;
reg = chv_gpio_reg(&cg->chip, offset, CV_PADCTRL0_REG);
spin_lock_irqsave(&cg->lock, flags);
chv_writel(value, reg);
spin_unlock_irqrestore(&cg->lock, flags);
}
static char **chv_get_avl_pininfo(struct gpio_debug *debug, unsigned gpio,
unsigned int type, unsigned *num)
{
struct gpio_control *control;
control = find_gpio_control(chv_gpio_controls,
ARRAY_SIZE(chv_gpio_controls), type);
if (control == NULL)
return NULL;
*num = control->num;
return control->pininfo;
}
static char *chv_get_cul_pininfo(struct gpio_debug *debug, unsigned gpio,
unsigned int type)
{
struct chv_gpio *cg = debug->private_data;
struct gpio_control *control;
int num;
control = find_gpio_control(chv_gpio_controls,
ARRAY_SIZE(chv_gpio_controls), type);
if (control == NULL)
return NULL;
num = control->get(control, cg, gpio);
if (num == -1)
return NULL;
return *(control->pininfo + num);
}
static void chv_set_pininfo(struct gpio_debug *debug, unsigned gpio,
unsigned int type, const char *info)
{
struct chv_gpio *cg = debug->private_data;
struct gpio_control *control;
int num;
control = find_gpio_control(chv_gpio_controls,
ARRAY_SIZE(chv_gpio_controls), type);
if (control == NULL)
return;
num = find_pininfo_num(control, info);
if (num == -1)
return;
if (control->set)
control->set(control, cg, gpio, num);
}
static int chv_get_register_msg(char **buf, unsigned long *size)
{
*buf = conf_reg_msg;
*size = strlen(conf_reg_msg);
return 0;
}
static struct gpio_debug_ops chv_gpio_debug_ops = {
.get_conf_reg = chv_get_conf_reg,
.set_conf_reg = chv_set_conf_reg,
.get_avl_pininfo = chv_get_avl_pininfo,
.get_cul_pininfo = chv_get_cul_pininfo,
.set_pininfo = chv_set_pininfo,
.get_register_msg = chv_get_register_msg,
};
static int chv_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct chv_gpio *cg = to_chv_priv(chip);
return irq_create_mapping(cg->domain, offset);
}
static int chv_gpio_irq_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hw)
{
struct chv_gpio *cg = d->host_data;
irq_set_chip_and_handler_name(virq, &chv_irqchip, handle_simple_irq,
"demux");
irq_set_chip_data(virq, cg);
return 0;
}
static const struct irq_domain_ops chv_gpio_irq_ops = {
.map = chv_gpio_irq_map,
.xlate = irq_domain_xlate_twocell,
};
static int
chv_gpio_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *id)
{
int i;
struct chv_gpio *cg;
struct gpio_chip *gc;
struct resource *mem_rc, *irq_rc;
struct device *dev = &pdev->dev;
struct gpio_bank_pnp *bank;
int ret = 0;
int nbanks = sizeof(chv_banks_pnp) / sizeof(struct gpio_bank_pnp);
struct gpio_debug *debug;
cg = devm_kzalloc(dev, sizeof(struct chv_gpio), GFP_KERNEL);
if (!cg) {
dev_err(dev, "can't allocate chv_gpio chip data\n");
return -ENOMEM;
}
cg->pdev = pdev;
for (i = 0; i < nbanks; i++) {
bank = chv_banks_pnp + i;
if (!strcmp(pdev->name, bank->name)) {
cg->chip.ngpio = bank->ngpio;
cg->pad_info = bank->pads_info;
cg->irq_base = bank->irq_base;
bank->cg = cg;
break;
}
}
if (!bank) {
dev_err(dev, "can't find %s\n", pdev->name);
ret = -ENODEV;
goto err;
}
mem_rc = pnp_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem_rc) {
dev_err(dev, "missing MEM resource\n");
ret = -EINVAL;
goto err;
}
irq_rc = pnp_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq_rc) {
dev_err(dev, "missing IRQ resource\n");
ret = -EINVAL;
goto err;
}
cg->reg_base = devm_request_and_ioremap(dev, mem_rc);
if (cg->reg_base == NULL) {
dev_err(dev, "error mapping resource\n");
ret = -EINVAL;
goto err;
}
spin_lock_init(&cg->lock);
gc = &cg->chip;
gc->label = dev_name(&pdev->dev);
gc->owner = THIS_MODULE;
gc->request = chv_gpio_request;
gc->free = chv_gpio_free;
gc->direction_input = chv_gpio_direction_input;
gc->direction_output = chv_gpio_direction_output;
gc->set_pinmux = lnw_gpio_set_alt;
gc->get_pinmux = gpio_get_alt;
gc->get = chv_gpio_get;
gc->set = chv_gpio_set;
gc->to_irq = chv_gpio_to_irq;
gc->dbg_show = chv_gpio_dbg_show;
gc->base = bank->gpio_base;
gc->can_sleep = 0;
gc->dev = dev;
/* Initialize interrupt lines array with negative value */
for (i = 0; i < MAX_INTR_LINE_NUM; i++)
cg->intr_lines[i] = -1;
cg->domain = irq_domain_add_simple(pdev->dev.of_node,
cg->chip.ngpio, cg->irq_base,
&chv_gpio_irq_ops, cg);
if (!cg->domain) {
ret = -ENOMEM;
goto err;
}
ret = gpiochip_add(gc);
if (ret) {
dev_err(dev, "failed adding chv-gpio chip\n");
goto err;
}
debug = gpio_debug_alloc();
if (debug) {
__set_bit(TYPE_IRQ_LINE, debug->typebit);
__clear_bit(TYPE_PULLSTRENGTH, debug->typebit);
__clear_bit(TYPE_DEBOUNCE, debug->typebit);
debug->chip = gc;
debug->ops = &chv_gpio_debug_ops;
debug->private_data = cg;
cg->debug = debug;
ret = gpio_debug_register(debug);
if (ret) {
dev_err(&pdev->dev, "gpio_add_debug_debugfs error %d\n",
ret);
gpio_debug_remove(debug);
}
}
chv_irq_init_hw(cg);
if (irq_rc && irq_rc->start) {
irq_set_handler_data(irq_rc->start, cg);
irq_set_chained_handler(irq_rc->start, chv_gpio_irq_handler);
}
pr_info("Cherryview GPIO %s probed\n", pdev->name);
return 0;
err:
return ret;
}
static const struct pnp_device_id chv_gpio_pnp_match[] = {
{ "INT33B2", 0 },
{ }
};
MODULE_DEVICE_TABLE(pnp, chv_gpio_pnp_match);
static struct pnp_driver chv_gpio_pnp_driver = {
.name = "chv_gpio",
.id_table = chv_gpio_pnp_match,
.probe = chv_gpio_pnp_probe,
};
static int __init chv_gpio_init(void)
{
return pnp_register_driver(&chv_gpio_pnp_driver);
}
fs_initcall(chv_gpio_init);