drivers/gpio/gpio-siox.c

Source file repositories/reference/linux-study-clean/drivers/gpio/gpio-siox.c

File Facts

System
Linux kernel
Corpus path
drivers/gpio/gpio-siox.c
Extension
.c
Size
6432 bytes
Lines
272
Domain
Driver Families
Bucket
drivers/gpio
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct gpio_siox_ddata {
	struct gpio_chip gchip;
	struct mutex lock;
	u8 setdata[1];
	u8 getdata[3];

	raw_spinlock_t irqlock;
	u32 irq_enable;
	u32 irq_status;
	u32 irq_type[20];
};

/*
 * Note that this callback only sets the value that is clocked out in the next
 * cycle.
 */
static int gpio_siox_set_data(struct siox_device *sdevice, u8 status, u8 buf[])
{
	struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev);

	mutex_lock(&ddata->lock);
	buf[0] = ddata->setdata[0];
	mutex_unlock(&ddata->lock);

	return 0;
}

static int gpio_siox_get_data(struct siox_device *sdevice, const u8 buf[])
{
	struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev);
	size_t offset;
	u32 trigger;

	mutex_lock(&ddata->lock);

	raw_spin_lock_irq(&ddata->irqlock);

	for (offset = 0; offset < 12; ++offset) {
		unsigned int bitpos = 11 - offset;
		unsigned int gpiolevel = buf[bitpos / 8] & (1 << bitpos % 8);
		unsigned int prev_level =
			ddata->getdata[bitpos / 8] & (1 << (bitpos % 8));
		u32 irq_type = ddata->irq_type[offset];

		if (gpiolevel) {
			if ((irq_type & IRQ_TYPE_LEVEL_HIGH) ||
			    ((irq_type & IRQ_TYPE_EDGE_RISING) && !prev_level))
				ddata->irq_status |= 1 << offset;
		} else {
			if ((irq_type & IRQ_TYPE_LEVEL_LOW) ||
			    ((irq_type & IRQ_TYPE_EDGE_FALLING) && prev_level))
				ddata->irq_status |= 1 << offset;
		}
	}

	trigger = ddata->irq_status & ddata->irq_enable;

	raw_spin_unlock_irq(&ddata->irqlock);

	ddata->getdata[0] = buf[0];
	ddata->getdata[1] = buf[1];
	ddata->getdata[2] = buf[2];

	mutex_unlock(&ddata->lock);

	for (offset = 0; offset < 12; ++offset) {
		if (trigger & (1 << offset)) {
			struct irq_domain *irqdomain = ddata->gchip.irq.domain;
			unsigned int irq = irq_find_mapping(irqdomain, offset);

			/*
			 * Conceptually handle_nested_irq should call the flow
			 * handler of the irq chip. But it doesn't, so we have
			 * to clean the irq_status here.
			 */
			raw_spin_lock_irq(&ddata->irqlock);
			ddata->irq_status &= ~(1 << offset);
			raw_spin_unlock_irq(&ddata->irqlock);

			handle_nested_irq(irq);
		}
	}

	return 0;
}

static void gpio_siox_irq_ack(struct irq_data *d)
{
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct gpio_siox_ddata *ddata = gpiochip_get_data(gc);

Annotation

Implementation Notes