/* * These two headers aren't meant to be used by GPIO drivers. We need * them in order to access gpio_chip_hwgpio() which we need to implement * the aspeed specific API which allows the coprocessor to request * access to some GPIOs and to arbitrate between coprocessor and ARM.
*/ #include <linux/gpio/consumer.h> #include"gpiolib.h"
/* * @offset_timer: Maps an offset to an @timer_users index, or zero if disabled * @timer_users: Tracks the number of users for each timer * * The @timer_users has four elements but the first element is unused. This is * to simplify accounting and indexing, as a zero value in @offset_timer * represents disabled debouncing for the GPIO. Any other value for an element * of @offset_timer is used as an index into @timer_users. This behaviour of * the zero value aligns with the behaviour of zero built from the timer * configuration registers (i.e. debouncing is disabled).
*/ struct aspeed_gpio { struct gpio_chip chip; struct device *dev;
raw_spinlock_t lock; void __iomem *base; int irq; conststruct aspeed_gpio_config *config;
/* * Note: The "value" register returns the input value sampled on the * line even when the GPIO is configured as an output. Since * that input goes through synchronizers, writing, then reading * back may not return the written value right away. * * The "rdata" register returns the content of the write latch * and thus can be used to read back what was last written * reliably.
*/
/* * The debounce timers array is used to configure the debounce timer settings.Here’s how it works: * Array Value: Indicates the offset for configuring the debounce timer. * Array Index: Corresponds to the debounce setting register. * The debounce timers array follows this pattern for configuring the debounce setting registers: * Array Index 0: No debounce timer is set; * Array Value is irrelevant (don’t care). * Array Index 1: Debounce setting #2 is set to 1, and debounce setting #1 is set to 0. * Array Value: offset for configuring debounce timer 0 (g4: 0x50, g7: 0x00) * Array Index 2: Debounce setting #2 is set to 0, and debounce setting #1 is set to 1. * Array Value: offset for configuring debounce timer 1 (g4: 0x54, g7: 0x04) * Array Index 3: Debounce setting #2 is set to 1, and debounce setting #1 is set to 1. * Array Value: offset for configuring debounce timer 2 (g4: 0x58, g7: 0x8)
*/
/* This will be resolved at compile time */ staticvoid __iomem *aspeed_gpio_g4_bank_reg(struct aspeed_gpio *gpio, conststruct aspeed_gpio_bank *bank, constenum aspeed_gpio_reg reg)
{ switch (reg) { case reg_val: return gpio->base + bank->val_regs + GPIO_VAL_VALUE; case reg_rdata: return gpio->base + bank->rdata_reg; case reg_dir: return gpio->base + bank->val_regs + GPIO_VAL_DIR; case reg_irq_enable: return gpio->base + bank->irq_regs + GPIO_IRQ_ENABLE; case reg_irq_type0: return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE0; case reg_irq_type1: return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE1; case reg_irq_type2: return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE2; case reg_irq_status: return gpio->base + bank->irq_regs + GPIO_IRQ_STATUS; case reg_debounce_sel1: return gpio->base + bank->debounce_regs + GPIO_DEBOUNCE_SEL1; case reg_debounce_sel2: return gpio->base + bank->debounce_regs + GPIO_DEBOUNCE_SEL2; case reg_tolerance: return gpio->base + bank->tolerance_regs; case reg_cmdsrc0: return gpio->base + bank->cmdsrc_regs + GPIO_CMDSRC_0; case reg_cmdsrc1: return gpio->base + bank->cmdsrc_regs + GPIO_CMDSRC_1;
}
BUG();
}
static u32 aspeed_gpio_g7_reg_mask(constenum aspeed_gpio_reg reg)
{ switch (reg) { case reg_val: return GPIO_G7_CTRL_OUT_DATA; case reg_dir: return GPIO_G7_CTRL_DIR; case reg_irq_enable: return GPIO_G7_CTRL_IRQ_EN; case reg_irq_type0: return GPIO_G7_CTRL_IRQ_TYPE0; case reg_irq_type1: return GPIO_G7_CTRL_IRQ_TYPE1; case reg_irq_type2: return GPIO_G7_CTRL_IRQ_TYPE2; case reg_tolerance: return GPIO_G7_CTRL_RST_TOLERANCE; case reg_debounce_sel1: return GPIO_G7_CTRL_DEBOUNCE_SEL1; case reg_debounce_sel2: return GPIO_G7_CTRL_DEBOUNCE_SEL2; case reg_rdata: return GPIO_G7_CTRL_OUT_DATA; case reg_irq_status: return GPIO_G7_CTRL_IRQ_STS; case reg_cmdsrc0: case reg_cmdsrc1: default:
WARN_ON_ONCE(1); return 0;
}
}
/* Call under gpio->lock */ staticvoid configure_timer(struct aspeed_gpio *gpio, unsignedint offset, unsignedint timer)
{ /* Note: Debounce timer isn't under control of the command * source registers, so no need to sync with the coprocessor
*/
gpio->config->llops->reg_bit_set(gpio, offset, reg_debounce_sel1, !!(timer & BIT(1)));
gpio->config->llops->reg_bit_set(gpio, offset, reg_debounce_sel2, !!(timer & BIT(0)));
}
staticint enable_debounce(struct gpio_chip *chip, unsignedint offset, unsignedlong usecs)
{ struct aspeed_gpio *gpio = gpiochip_get_data(chip);
u32 requested_cycles; int rc; int i;
if (!gpio->clk) return -EINVAL;
rc = usecs_to_cycles(gpio, usecs, &requested_cycles); if (rc < 0) {
dev_warn(chip->parent, "Failed to convert %luus to cycles at %luHz: %d\n",
usecs, clk_get_rate(gpio->clk), rc); return rc;
}
guard(raw_spinlock_irqsave)(&gpio->lock);
if (timer_allocation_registered(gpio, offset)) {
rc = unregister_allocated_timer(gpio, offset); if (rc < 0) return rc;
}
/* Try to find a timer already configured for the debounce period */ for (i = 1; i < gpio->config->debounce_timers_num; i++) {
u32 cycles;
if (i == gpio->config->debounce_timers_num) { int j;
/* * As there are no timers configured for the requested debounce * period, find an unused timer instead
*/ for (j = 1; j < ARRAY_SIZE(gpio->timer_users); j++) { if (gpio->timer_users[j] == 0) break;
}
if (j == ARRAY_SIZE(gpio->timer_users)) {
dev_warn(chip->parent, "Debounce timers exhausted, cannot debounce for period %luus\n",
usecs);
rc = -EPERM;
/* * We already adjusted the accounting to remove @offset * as a user of its previous timer, so also configure * the hardware so @offset has timers disabled for * consistency.
*/
configure_timer(gpio, offset, 0); return rc;
}
/** * aspeed_gpio_copro_set_ops - Sets the callbacks used for handshaking with * the coprocessor for shared GPIO banks * @ops: The callbacks * @data: Pointer passed back to the callbacks
*/ int aspeed_gpio_copro_set_ops(conststruct aspeed_gpio_copro_ops *ops, void *data)
{
copro_data = data;
copro_ops = ops;
/** * aspeed_gpio_copro_grab_gpio - Mark a GPIO used by the coprocessor. The entire * bank gets marked and any access from the ARM will * result in handshaking via callbacks. * @desc: The GPIO to be marked * @vreg_offset: If non-NULL, returns the value register offset in the GPIO space * @dreg_offset: If non-NULL, returns the data latch register offset in the GPIO space * @bit: If non-NULL, returns the bit number of the GPIO in the registers
*/ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc,
u16 *vreg_offset, u16 *dreg_offset, u8 *bit)
{ struct gpio_chip *chip = gpiod_to_chip(desc); struct aspeed_gpio *gpio = gpiochip_get_data(chip); int rc = 0, bindex, offset = gpio_chip_hwgpio(desc); conststruct aspeed_gpio_bank *bank = to_bank(offset);
if (!aspeed_gpio_support_copro(gpio)) return -EOPNOTSUPP;
if (!gpio->cf_copro_bankmap)
gpio->cf_copro_bankmap = kzalloc(gpio->chip.ngpio >> 3, GFP_KERNEL); if (!gpio->cf_copro_bankmap) return -ENOMEM; if (offset < 0 || offset > gpio->chip.ngpio) return -EINVAL;
bindex = offset >> 3;
guard(raw_spinlock_irqsave)(&gpio->lock);
/* Sanity check, this shouldn't happen */ if (gpio->cf_copro_bankmap[bindex] == 0xff) return -EIO;
if (vreg_offset)
*vreg_offset = bank->val_regs; if (dreg_offset)
*dreg_offset = bank->rdata_reg; if (bit)
*bit = GPIO_OFFSET(offset); return rc;
}
EXPORT_SYMBOL_GPL(aspeed_gpio_copro_grab_gpio);
/** * aspeed_gpio_copro_release_gpio - Unmark a GPIO used by the coprocessor. * @desc: The GPIO to be marked
*/ int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc)
{ struct gpio_chip *chip = gpiod_to_chip(desc); struct aspeed_gpio *gpio = gpiochip_get_data(chip); int rc = 0, bindex, offset = gpio_chip_hwgpio(desc);
if (!aspeed_gpio_support_copro(gpio)) return -EOPNOTSUPP;
staticvoid aspeed_g4_privilege_ctrl(struct aspeed_gpio *gpio, unsignedint offset, int cmdsrc)
{ /* * The command source register is only valid in bits 0, 8, 16, and 24, so we use * (offset & ~(0x7)) to ensure that reg_bits_set always targets a valid bit.
*/ /* Source 1 first to avoid illegal 11 combination */
aspeed_g4_reg_bit_set(gpio, offset & ~(0x7), reg_cmdsrc1, !!(cmdsrc & BIT(1))); /* Then Source 0 */
aspeed_g4_reg_bit_set(gpio, offset & ~(0x7), reg_cmdsrc0, !!(cmdsrc & BIT(0)));
}
/* Switch all command sources to the ARM by default */ for (i = 0; i < DIV_ROUND_UP(gpio->chip.ngpio, 32); i++) {
aspeed_g4_privilege_ctrl(gpio, (i << 5) + 0, GPIO_CMDSRC_ARM);
aspeed_g4_privilege_ctrl(gpio, (i << 5) + 8, GPIO_CMDSRC_ARM);
aspeed_g4_privilege_ctrl(gpio, (i << 5) + 16, GPIO_CMDSRC_ARM);
aspeed_g4_privilege_ctrl(gpio, (i << 5) + 24, GPIO_CMDSRC_ARM);
}
}
staticbool aspeed_g4_copro_request(struct aspeed_gpio *gpio, unsignedint offset)
{ if (!copro_ops || !gpio->cf_copro_bankmap) returnfalse; if (!gpio->cf_copro_bankmap[offset >> 3]) returnfalse; if (!copro_ops->request_access) returnfalse;
/* Pause the coprocessor */
copro_ops->request_access(copro_data);
/* Change command source back to ARM */
aspeed_g4_privilege_ctrl(gpio, offset, GPIO_CMDSRC_ARM);
/* * Any banks not specified in a struct aspeed_bank_props array are assumed to * have the properties: * * { .input = 0xffffffff, .output = 0xffffffff }
*/
staticconststruct aspeed_gpio_config ast2600_config = /* * ast2600 has two controllers one with 208 GPIOs and one with 36 GPIOs. * We expect ngpio being set in the device tree and this is a fallback * option.
*/
{
.nr_gpios = 208,
.props = ast2600_bank_props,
.llops = &aspeed_g4_llops,
.debounce_timers_array = debounce_timers,
.debounce_timers_num = ARRAY_SIZE(debounce_timers),
.require_dcache = true,
};
staticconststruct aspeed_gpio_config ast2700_config = /* * ast2700 has two controllers one with 212 GPIOs and one with 16 GPIOs. * 216 for simplicity, actual number is 212 (4-GPIO hole in GPIOH) * We expect ngpio being set in the device tree and this is a fallback * option.
*/
{
.nr_gpios = 216,
.props = ast2700_bank_props,
.llops = &aspeed_g7_llops,
.debounce_timers_array = g7_debounce_timers,
.debounce_timers_num = ARRAY_SIZE(g7_debounce_timers),
.require_dcache = false,
};
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); if (!gpio) return -ENOMEM;
gpio->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(gpio->base)) return PTR_ERR(gpio->base);
gpio->dev = &pdev->dev;
raw_spin_lock_init(&gpio->lock);
gpio_id = of_match_node(aspeed_gpio_of_table, pdev->dev.of_node); if (!gpio_id) return -EINVAL;
gpio->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(gpio->clk)) {
dev_warn(&pdev->dev, "Failed to get clock from devicetree, debouncing disabled\n");
gpio->clk = NULL;
}
gpio->config = gpio_id->data;
if (!gpio->config->llops->reg_bit_set || !gpio->config->llops->reg_bit_get ||
!gpio->config->llops->reg_bank_get) return -EINVAL;
if (gpio->config->require_dcache) { /* Allocate a cache of the output registers */
banks = DIV_ROUND_UP(gpio->chip.ngpio, 32);
gpio->dcache = devm_kcalloc(&pdev->dev, banks, sizeof(u32), GFP_KERNEL); if (!gpio->dcache) return -ENOMEM; /* * Populate it with initial values read from the HW
*/ for (i = 0; i < banks; i++)
gpio->dcache[i] =
gpio->config->llops->reg_bank_get(gpio, (i << 5), reg_rdata);
}
if (gpio->config->llops->privilege_init)
gpio->config->llops->privilege_init(gpio);
/* Set up an irqchip */
irq = platform_get_irq(pdev, 0); if (irq < 0) return irq;
gpio->irq = irq;
girq = &gpio->chip.irq;
gpio_irq_chip_set_chip(girq, &aspeed_gpio_irq_chip);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.