Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 88 additions & 11 deletions src/gpio/driver/linux/gpio-oh.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,25 @@ static inline u64 oh_gpio_reg_read(struct oh_gpio *gpio, unsigned long offset)
return (u64) readl((u8 __iomem *) gpio->base_addr + offset);
}

/**
* oh_gpio_get_value_locked - Get value of the specified pin
* @chip: gpio chip device
* @pin: gpio pin number
*
* Note: Caller must hold gpio->lock.
*
* Return: 0 if the pin is low, 1 if pin is high.
*/
static int oh_gpio_get_value_locked(struct gpio_chip *chip, unsigned pin)
{
u64 data;
struct oh_gpio *gpio = to_oh_gpio(chip);

data = oh_gpio_reg_read(gpio, OH_GPIO_IN);

return (data >> pin) & 1;
}

/**
* oh_gpio_get_value - Get value of the specified pin
* @chip: gpio chip device
Expand All @@ -98,15 +117,15 @@ static inline u64 oh_gpio_reg_read(struct oh_gpio *gpio, unsigned long offset)
*/
static int oh_gpio_get_value(struct gpio_chip *chip, unsigned pin)
{
u64 data;
int ret;
unsigned long flags;
struct oh_gpio *gpio = to_oh_gpio(chip);

spin_lock_irqsave(&gpio->lock, flags);
data = oh_gpio_reg_read(gpio, OH_GPIO_IN);
ret = oh_gpio_get_value_locked(chip, pin);
spin_unlock_irqrestore(&gpio->lock, flags);

return (data >> pin) & 1;
return ret;
}

/**
Expand Down Expand Up @@ -266,6 +285,29 @@ static void oh_gpio_irq_ack(struct irq_data *irq_data)
spin_unlock_irqrestore(&gpio->lock, flags);
}

/**
* oh_gpio_irq_next_edge_locked - Configure IPOL for next edge
* @gpio: oh gpio structure
* @pin: gpio pin number
*
* Note: Caller must hold gpio->lock.
*/
static void oh_gpio_irq_next_edge_locked(struct oh_gpio *gpio, unsigned pin)
{
u64 ipol, mask;

mask = BIT_ULL(pin);

ipol = oh_gpio_reg_read(gpio, OH_GPIO_IPOL);

if (oh_gpio_get_value_locked(&gpio->chip, pin))
ipol &= ~mask;
else
ipol |= mask;

oh_gpio_reg_write(ipol, gpio, OH_GPIO_IPOL);
}

/**
* oh_gpio_irq_set_type - Set the irq type for a gpio pin
* @irq_data: irq data containing irq number of gpio pin
Expand All @@ -290,6 +332,8 @@ static int oh_gpio_irq_set_type(struct irq_data *irq_data, unsigned type)
ipol = oh_gpio_reg_read(gpio, OH_GPIO_IPOL);

switch (type) {
case IRQ_TYPE_EDGE_BOTH:
/* fall through, handle below */
case IRQ_TYPE_EDGE_RISING:
itype &= ~mask;
ipol |= mask;
Expand All @@ -312,7 +356,11 @@ static int oh_gpio_irq_set_type(struct irq_data *irq_data, unsigned type)
}

oh_gpio_reg_write(itype, gpio, OH_GPIO_ITYPE);
oh_gpio_reg_write(ipol, gpio, OH_GPIO_IPOL);

if (type == IRQ_TYPE_EDGE_BOTH)
oh_gpio_irq_next_edge_locked(gpio, pin);
else
oh_gpio_reg_write(ipol, gpio, OH_GPIO_IPOL);

spin_unlock_irqrestore(&gpio->lock, flags);

Expand All @@ -331,6 +379,40 @@ static struct irq_chip oh_gpio_irqchip = {
.irq_set_type = oh_gpio_irq_set_type,
};

/**
* oh_gpio_irq_handler_for_each - IRQ handler helper
* @gpio: oh gpio structure
* @ilat: portion of ilat register
* @base: base pin number
*
* Calls the generic irq handlers for gpio pins with pending interrupts.
*/
static void oh_gpio_irq_handler_for_each(struct oh_gpio *gpio,
unsigned long *ilat, int base)
{
int offset;
unsigned long flags;
unsigned child_irq_no;
struct irq_desc *child_desc;
u32 child_type;
struct irq_domain *irqdomain = gpio->chip.irqdomain;

for_each_set_bit(offset, ilat, 32) {
child_irq_no = irq_find_mapping(irqdomain, base + offset);
child_desc = irq_to_desc(child_irq_no);
child_type = irqd_get_trigger_type(&child_desc->irq_data);

/* Toggle edge for pin with both edges triggering enabled */
if (child_type == IRQ_TYPE_EDGE_BOTH) {
spin_lock_irqsave(&gpio->lock, flags);
oh_gpio_irq_next_edge_locked(gpio, base + offset);
spin_unlock_irqrestore(&gpio->lock, flags);
}

generic_handle_irq_desc(child_desc);
}
}

/**
* oh_gpio_irq_handler - IRQ handler
* @irq: oh_gpio irq number
Expand All @@ -346,9 +428,7 @@ static irqreturn_t oh_gpio_irq_handler(int irq, void *dev_id)
{
u64 ilat;
unsigned long flags, ilat_lo, ilat_hi;
int offset;
struct oh_gpio *gpio = dev_id;
struct irq_domain *irqdomain = gpio->chip.irqdomain;

spin_lock_irqsave(&gpio->lock, flags);
ilat = oh_gpio_reg_read(gpio, OH_GPIO_ILAT);
Expand All @@ -361,11 +441,8 @@ static irqreturn_t oh_gpio_irq_handler(int irq, void *dev_id)
ilat_lo = (unsigned long) ((ilat >> 0) & 0xffffffff);
ilat_hi = (unsigned long) ((ilat >> 32) & 0xffffffff);

for_each_set_bit(offset, &ilat_lo, 32)
generic_handle_irq(irq_find_mapping(irqdomain, offset));

for_each_set_bit(offset, &ilat_hi, 32)
generic_handle_irq(irq_find_mapping(irqdomain, 32 + offset));
oh_gpio_irq_handler_for_each(gpio, &ilat_lo, 0);
oh_gpio_irq_handler_for_each(gpio, &ilat_hi, 32);

return IRQ_HANDLED;
}
Expand Down