1297 lines · 34.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * GPIO driver for AMD * * Copyright (c) 2014,2015 AMD Corporation. * Authors: Ken Xue <Ken.Xue@amd.com> *      Wu, Jeff <Jeff.Wu@amd.com> * */ #include <linux/err.h>#include <linux/bug.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/spinlock.h>#include <linux/compiler.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/log2.h>#include <linux/io.h>#include <linux/gpio/driver.h>#include <linux/slab.h>#include <linux/platform_device.h>#include <linux/acpi.h>#include <linux/seq_file.h>#include <linux/interrupt.h>#include <linux/bitops.h>#include <linux/pinctrl/pinconf.h>#include <linux/pinctrl/pinconf-generic.h>#include <linux/pinctrl/pinmux.h>#include <linux/string_choices.h>#include <linux/suspend.h> #include "core.h"#include "pinctrl-utils.h"#include "pinctrl-amd.h" #ifdef CONFIG_SUSPENDstatic struct amd_gpio *pinctrl_dev;#endif static int amd_gpio_get_direction(struct gpio_chip *gc, unsigned offset){	unsigned long flags;	u32 pin_reg;	struct amd_gpio *gpio_dev = gpiochip_get_data(gc); 	raw_spin_lock_irqsave(&gpio_dev->lock, flags);	pin_reg = readl(gpio_dev->base + offset * 4);	raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); 	if (pin_reg & BIT(OUTPUT_ENABLE_OFF))		return GPIO_LINE_DIRECTION_OUT; 	return GPIO_LINE_DIRECTION_IN;} static int amd_gpio_direction_input(struct gpio_chip *gc, unsigned offset){	unsigned long flags;	u32 pin_reg;	struct amd_gpio *gpio_dev = gpiochip_get_data(gc); 	raw_spin_lock_irqsave(&gpio_dev->lock, flags);	pin_reg = readl(gpio_dev->base + offset * 4);	pin_reg &= ~BIT(OUTPUT_ENABLE_OFF);	writel(pin_reg, gpio_dev->base + offset * 4);	raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); 	return 0;} static int amd_gpio_direction_output(struct gpio_chip *gc, unsigned offset,		int value){	u32 pin_reg;	unsigned long flags;	struct amd_gpio *gpio_dev = gpiochip_get_data(gc); 	raw_spin_lock_irqsave(&gpio_dev->lock, flags);	pin_reg = readl(gpio_dev->base + offset * 4);	pin_reg |= BIT(OUTPUT_ENABLE_OFF);	if (value)		pin_reg |= BIT(OUTPUT_VALUE_OFF);	else		pin_reg &= ~BIT(OUTPUT_VALUE_OFF);	writel(pin_reg, gpio_dev->base + offset * 4);	raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); 	return 0;} static int amd_gpio_get_value(struct gpio_chip *gc, unsigned offset){	u32 pin_reg;	unsigned long flags;	struct amd_gpio *gpio_dev = gpiochip_get_data(gc); 	raw_spin_lock_irqsave(&gpio_dev->lock, flags);	pin_reg = readl(gpio_dev->base + offset * 4);	raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); 	return !!(pin_reg & BIT(PIN_STS_OFF));} static int amd_gpio_set_value(struct gpio_chip *gc, unsigned int offset,			      int value){	u32 pin_reg;	unsigned long flags;	struct amd_gpio *gpio_dev = gpiochip_get_data(gc); 	raw_spin_lock_irqsave(&gpio_dev->lock, flags);	pin_reg = readl(gpio_dev->base + offset * 4);	if (value)		pin_reg |= BIT(OUTPUT_VALUE_OFF);	else		pin_reg &= ~BIT(OUTPUT_VALUE_OFF);	writel(pin_reg, gpio_dev->base + offset * 4);	raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); 	return 0;} static int amd_gpio_set_debounce(struct amd_gpio *gpio_dev, unsigned int offset,				 unsigned int debounce){	u32 time;	u32 pin_reg;	int ret = 0; 	/* Use special handling for Pin0 debounce */	if (offset == 0) {		pin_reg = readl(gpio_dev->base + WAKE_INT_MASTER_REG);		if (pin_reg & INTERNAL_GPIO0_DEBOUNCE)			debounce = 0;	} 	pin_reg = readl(gpio_dev->base + offset * 4); 	if (debounce) {		pin_reg |= DB_TYPE_REMOVE_GLITCH << DB_CNTRL_OFF;		pin_reg &= ~DB_TMR_OUT_MASK;		/*		Debounce	Debounce	Timer	Max		TmrLarge	TmrOutUnit	Unit	Debounce							Time		0	0	61 usec (2 RtcClk)	976 usec		0	1	244 usec (8 RtcClk)	3.9 msec		1	0	15.6 msec (512 RtcClk)	250 msec		1	1	62.5 msec (2048 RtcClk)	1 sec		*/ 		if (debounce < 61) {			pin_reg |= 1;			pin_reg &= ~BIT(DB_TMR_OUT_UNIT_OFF);			pin_reg &= ~BIT(DB_TMR_LARGE_OFF);		} else if (debounce < 976) {			time = debounce / 61;			pin_reg |= time & DB_TMR_OUT_MASK;			pin_reg &= ~BIT(DB_TMR_OUT_UNIT_OFF);			pin_reg &= ~BIT(DB_TMR_LARGE_OFF);		} else if (debounce < 3900) {			time = debounce / 244;			pin_reg |= time & DB_TMR_OUT_MASK;			pin_reg |= BIT(DB_TMR_OUT_UNIT_OFF);			pin_reg &= ~BIT(DB_TMR_LARGE_OFF);		} else if (debounce < 250000) {			time = debounce / 15625;			pin_reg |= time & DB_TMR_OUT_MASK;			pin_reg &= ~BIT(DB_TMR_OUT_UNIT_OFF);			pin_reg |= BIT(DB_TMR_LARGE_OFF);		} else if (debounce < 1000000) {			time = debounce / 62500;			pin_reg |= time & DB_TMR_OUT_MASK;			pin_reg |= BIT(DB_TMR_OUT_UNIT_OFF);			pin_reg |= BIT(DB_TMR_LARGE_OFF);		} else {			pin_reg &= ~(DB_CNTRl_MASK << DB_CNTRL_OFF);			ret = -EINVAL;		}	} else {		pin_reg &= ~BIT(DB_TMR_OUT_UNIT_OFF);		pin_reg &= ~BIT(DB_TMR_LARGE_OFF);		pin_reg &= ~DB_TMR_OUT_MASK;		pin_reg &= ~(DB_CNTRl_MASK << DB_CNTRL_OFF);	}	writel(pin_reg, gpio_dev->base + offset * 4); 	return ret;} #ifdef CONFIG_DEBUG_FSstatic void amd_gpio_dbg_show(struct seq_file *s, struct gpio_chip *gc){	u32 pin_reg;	u32 db_cntrl;	unsigned long flags;	unsigned int bank, i, pin_num;	struct amd_gpio *gpio_dev = gpiochip_get_data(gc); 	bool tmr_out_unit;	bool tmr_large; 	char *level_trig;	char *active_level;	char *interrupt_mask;	char *wake_cntrl0;	char *wake_cntrl1;	char *wake_cntrl2;	char *pin_sts;	char *interrupt_sts;	char *wake_sts;	char *orientation;	char debounce_value[40];	char *debounce_enable;	char *wake_cntrlz; 	seq_printf(s, "WAKE_INT_MASTER_REG: 0x%08x\n", readl(gpio_dev->base + WAKE_INT_MASTER_REG));	for (bank = 0; bank < gpio_dev->hwbank_num; bank++) {		unsigned int time = 0;		unsigned int unit = 0; 		switch (bank) {		case 0:			i = 0;			pin_num = AMD_GPIO_PINS_BANK0;			break;		case 1:			i = 64;			pin_num = AMD_GPIO_PINS_BANK1 + i;			break;		case 2:			i = 128;			pin_num = AMD_GPIO_PINS_BANK2 + i;			break;		case 3:			i = 192;			pin_num = AMD_GPIO_PINS_BANK3 + i;			break;		default:			/* Illegal bank number, ignore */			continue;		}		seq_printf(s, "GPIO bank%d\n", bank);		seq_puts(s, "gpio\t  int|active|trigger|S0i3| S3|S4/S5| Z|wake|pull|  orient|       debounce|reg\n");		for (; i < pin_num; i++) {			seq_printf(s, "#%d\t", i);			raw_spin_lock_irqsave(&gpio_dev->lock, flags);			pin_reg = readl(gpio_dev->base + i * 4);			raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); 			if (pin_reg & BIT(INTERRUPT_ENABLE_OFF)) {				u8 level = (pin_reg >> ACTIVE_LEVEL_OFF) &						ACTIVE_LEVEL_MASK; 				if (level == ACTIVE_LEVEL_HIGH)					active_level = "↑";				else if (level == ACTIVE_LEVEL_LOW)					active_level = "↓";				else if (!(pin_reg & BIT(LEVEL_TRIG_OFF)) &&					 level == ACTIVE_LEVEL_BOTH)					active_level = "b";				else					active_level = "?"; 				if (pin_reg & BIT(LEVEL_TRIG_OFF))					level_trig = "level";				else					level_trig = " edge"; 				if (pin_reg & BIT(INTERRUPT_MASK_OFF))					interrupt_mask = "😛";				else					interrupt_mask = "😷"; 				if (pin_reg & BIT(INTERRUPT_STS_OFF))					interrupt_sts = "🔥";				else					interrupt_sts = "  "; 				seq_printf(s, "%s %s|     %s|  %s|",				   interrupt_sts,				   interrupt_mask,				   active_level,				   level_trig);			} else				seq_puts(s, "    ∅|      |       |"); 			if (pin_reg & BIT(WAKE_CNTRL_OFF_S0I3))				wake_cntrl0 = "⏰";			else				wake_cntrl0 = "  ";			seq_printf(s, "  %s| ", wake_cntrl0); 			if (pin_reg & BIT(WAKE_CNTRL_OFF_S3))				wake_cntrl1 = "⏰";			else				wake_cntrl1 = "  ";			seq_printf(s, "%s|", wake_cntrl1); 			if (pin_reg & BIT(WAKE_CNTRL_OFF_S4))				wake_cntrl2 = "⏰";			else				wake_cntrl2 = "  ";			seq_printf(s, "   %s|", wake_cntrl2); 			if (pin_reg & BIT(WAKECNTRL_Z_OFF))				wake_cntrlz = "⏰";			else				wake_cntrlz = "  ";			seq_printf(s, "%s|", wake_cntrlz); 			if (pin_reg & BIT(WAKE_STS_OFF))				wake_sts = "🔥";			else				wake_sts = " ";			seq_printf(s, "   %s|", wake_sts); 			if (pin_reg & BIT(PULL_UP_ENABLE_OFF)) {				seq_puts(s, "  ↑ |");			} else if (pin_reg & BIT(PULL_DOWN_ENABLE_OFF)) {				seq_puts(s, "  ↓ |");			} else  {				seq_puts(s, "    |");			} 			if (pin_reg & BIT(OUTPUT_ENABLE_OFF)) {				pin_sts = "output";				if (pin_reg & BIT(OUTPUT_VALUE_OFF))					orientation = "↑";				else					orientation = "↓";			} else {				pin_sts = "input ";				if (pin_reg & BIT(PIN_STS_OFF))					orientation = "↑";				else					orientation = "↓";			}			seq_printf(s, "%s %s|", pin_sts, orientation); 			db_cntrl = (DB_CNTRl_MASK << DB_CNTRL_OFF) & pin_reg;			if (db_cntrl) {				tmr_out_unit = pin_reg & BIT(DB_TMR_OUT_UNIT_OFF);				tmr_large = pin_reg & BIT(DB_TMR_LARGE_OFF);				time = pin_reg & DB_TMR_OUT_MASK;				if (tmr_large) {					if (tmr_out_unit)						unit = 62500;					else						unit = 15625;				} else {					if (tmr_out_unit)						unit = 244;					else						unit = 61;				}				if ((DB_TYPE_REMOVE_GLITCH << DB_CNTRL_OFF) == db_cntrl)					debounce_enable = "b";				else if ((DB_TYPE_PRESERVE_LOW_GLITCH << DB_CNTRL_OFF) == db_cntrl)					debounce_enable = "↓";				else					debounce_enable = "↑";				snprintf(debounce_value, sizeof(debounce_value), "%06u", time * unit);				seq_printf(s, "%s (🕑 %sus)|", debounce_enable, debounce_value);			} else {				seq_puts(s, "               |");			}			seq_printf(s, "0x%x\n", pin_reg);		}	}}#else#define amd_gpio_dbg_show NULL#endif static void amd_gpio_irq_enable(struct irq_data *d){	u32 pin_reg;	unsigned long flags;	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);	struct amd_gpio *gpio_dev = gpiochip_get_data(gc);	irq_hw_number_t hwirq = irqd_to_hwirq(d); 	gpiochip_enable_irq(gc, hwirq); 	raw_spin_lock_irqsave(&gpio_dev->lock, flags);	pin_reg = readl(gpio_dev->base + hwirq * 4);	pin_reg |= BIT(INTERRUPT_ENABLE_OFF);	pin_reg |= BIT(INTERRUPT_MASK_OFF);	writel(pin_reg, gpio_dev->base + hwirq * 4);	raw_spin_unlock_irqrestore(&gpio_dev->lock, flags);} static void amd_gpio_irq_disable(struct irq_data *d){	u32 pin_reg;	unsigned long flags;	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);	struct amd_gpio *gpio_dev = gpiochip_get_data(gc);	irq_hw_number_t hwirq = irqd_to_hwirq(d); 	raw_spin_lock_irqsave(&gpio_dev->lock, flags);	pin_reg = readl(gpio_dev->base + hwirq * 4);	pin_reg &= ~BIT(INTERRUPT_ENABLE_OFF);	pin_reg &= ~BIT(INTERRUPT_MASK_OFF);	writel(pin_reg, gpio_dev->base + hwirq * 4);	raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); 	gpiochip_disable_irq(gc, hwirq);} static void amd_gpio_irq_mask(struct irq_data *d){	u32 pin_reg;	unsigned long flags;	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);	struct amd_gpio *gpio_dev = gpiochip_get_data(gc);	irq_hw_number_t hwirq = irqd_to_hwirq(d); 	raw_spin_lock_irqsave(&gpio_dev->lock, flags);	pin_reg = readl(gpio_dev->base + hwirq * 4);	pin_reg &= ~BIT(INTERRUPT_MASK_OFF);	writel(pin_reg, gpio_dev->base + hwirq * 4);	raw_spin_unlock_irqrestore(&gpio_dev->lock, flags);} static void amd_gpio_irq_unmask(struct irq_data *d){	u32 pin_reg;	unsigned long flags;	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);	struct amd_gpio *gpio_dev = gpiochip_get_data(gc);	irq_hw_number_t hwirq = irqd_to_hwirq(d); 	raw_spin_lock_irqsave(&gpio_dev->lock, flags);	pin_reg = readl(gpio_dev->base + hwirq * 4);	pin_reg |= BIT(INTERRUPT_MASK_OFF);	writel(pin_reg, gpio_dev->base + hwirq * 4);	raw_spin_unlock_irqrestore(&gpio_dev->lock, flags);} static int amd_gpio_irq_set_wake(struct irq_data *d, unsigned int on){	u32 pin_reg;	unsigned long flags;	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);	struct amd_gpio *gpio_dev = gpiochip_get_data(gc);	u32 wake_mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3);	irq_hw_number_t hwirq = irqd_to_hwirq(d);	int err; 	pm_pr_dbg("Setting wake for GPIO %lu to %s\n",		   hwirq, str_enable_disable(on)); 	raw_spin_lock_irqsave(&gpio_dev->lock, flags);	pin_reg = readl(gpio_dev->base + hwirq * 4); 	if (on)		pin_reg |= wake_mask;	else		pin_reg &= ~wake_mask; 	writel(pin_reg, gpio_dev->base + hwirq * 4);	raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); 	if (on)		err = enable_irq_wake(gpio_dev->irq);	else		err = disable_irq_wake(gpio_dev->irq); 	if (err)		dev_err(&gpio_dev->pdev->dev, "failed to %s wake-up interrupt\n",			str_enable_disable(on)); 	return 0;} static void amd_gpio_irq_eoi(struct irq_data *d){	u32 reg;	unsigned long flags;	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);	struct amd_gpio *gpio_dev = gpiochip_get_data(gc); 	raw_spin_lock_irqsave(&gpio_dev->lock, flags);	reg = readl(gpio_dev->base + WAKE_INT_MASTER_REG);	reg |= EOI_MASK;	writel(reg, gpio_dev->base + WAKE_INT_MASTER_REG);	raw_spin_unlock_irqrestore(&gpio_dev->lock, flags);} static int amd_gpio_irq_set_type(struct irq_data *d, unsigned int type){	int ret = 0;	u32 pin_reg, pin_reg_irq_en, mask;	unsigned long flags;	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);	struct amd_gpio *gpio_dev = gpiochip_get_data(gc);

Lines 1–500 of 1297.

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