Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4f47643d authored by Ben Skeggs's avatar Ben Skeggs
Browse files

drm/nouveau/gpio: use event interfaces for interrupt signalling



Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 0f080066
Loading
Loading
Loading
Loading
+13 −18
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@

#include <core/subdev.h>
#include <core/device.h>
#include <core/event.h>

#include <subdev/bios.h>
#include <subdev/bios/gpio.h>
@@ -10,28 +11,18 @@
struct nouveau_gpio {
	struct nouveau_subdev base;

	struct nouveau_event *events;

	/* hardware interfaces */
	void (*reset)(struct nouveau_gpio *, u8 func);
	int  (*drive)(struct nouveau_gpio *, int line, int dir, int out);
	int  (*sense)(struct nouveau_gpio *, int line);
	void (*irq_enable)(struct nouveau_gpio *, int line, bool);

	/* software interfaces */
	int  (*find)(struct nouveau_gpio *, int idx, u8 tag, u8 line,
		     struct dcb_gpio_func *);
	int  (*set)(struct nouveau_gpio *, int idx, u8 tag, u8 line, int state);
	int  (*get)(struct nouveau_gpio *, int idx, u8 tag, u8 line);
	int  (*irq)(struct nouveau_gpio *, int idx, u8 tag, u8 line, bool on);

	/* interrupt handling */
	struct list_head isr;
	spinlock_t lock;

	void (*isr_run)(struct nouveau_gpio *, int idx, u32 mask);
	int  (*isr_add)(struct nouveau_gpio *, int idx, u8 tag, u8 line,
			void (*)(void *, int state), void *data);
	void (*isr_del)(struct nouveau_gpio *, int idx, u8 tag, u8 line,
			void (*)(void *, int state), void *data);
};

static inline struct nouveau_gpio *
@@ -42,13 +33,16 @@ nouveau_gpio(void *obj)

#define nouveau_gpio_create(p,e,o,l,d)                                         \
	nouveau_gpio_create_((p), (e), (o), (l), sizeof(**d), (void **)d)
#define nouveau_gpio_destroy(p)                                                \
	nouveau_subdev_destroy(&(p)->base)
#define nouveau_gpio_destroy(p) ({                                             \
	struct nouveau_gpio *gpio = (p);                                       \
	_nouveau_gpio_dtor(nv_object(gpio));                                   \
})
#define nouveau_gpio_fini(p,s)                                                 \
	nouveau_subdev_fini(&(p)->base, (s))

int  nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *,
			  struct nouveau_oclass *, int, int, void **);
void _nouveau_gpio_dtor(struct nouveau_object *);
int  nouveau_gpio_init(struct nouveau_gpio *);

extern struct nouveau_oclass nv10_gpio_oclass;
@@ -59,6 +53,7 @@ void nv50_gpio_dtor(struct nouveau_object *);
int  nv50_gpio_init(struct nouveau_object *);
int  nv50_gpio_fini(struct nouveau_object *, bool);
void nv50_gpio_intr(struct nouveau_subdev *);
void nv50_gpio_irq_enable(struct nouveau_gpio *, int line, bool);
void nv50_gpio_intr_enable(struct nouveau_event *, int line);
void nv50_gpio_intr_disable(struct nouveau_event *, int line);

#endif
+9 −128
Original line number Diff line number Diff line
@@ -102,129 +102,12 @@ nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line)
	return ret;
}

static int
nouveau_gpio_irq(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, bool on)
{
	struct dcb_gpio_func func;
	int ret;

	ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
	if (ret == 0) {
		if (idx == 0 && gpio->irq_enable)
			gpio->irq_enable(gpio, func.line, on);
		else
			ret = -ENODEV;
	}

	return ret;
}

struct gpio_isr {
	struct nouveau_gpio *gpio;
	struct list_head head;
	struct work_struct work;
	int idx;
	struct dcb_gpio_func func;
	void (*handler)(void *, int);
	void *data;
	bool inhibit;
};

static void
nouveau_gpio_isr_bh(struct work_struct *work)
{
	struct gpio_isr *isr = container_of(work, struct gpio_isr, work);
	struct nouveau_gpio *gpio = isr->gpio;
	unsigned long flags;
	int state;

	state = nouveau_gpio_get(gpio, isr->idx, isr->func.func,
						 isr->func.line);
	if (state >= 0)
		isr->handler(isr->data, state);

	spin_lock_irqsave(&gpio->lock, flags);
	isr->inhibit = false;
	spin_unlock_irqrestore(&gpio->lock, flags);
}

static void
nouveau_gpio_isr_run(struct nouveau_gpio *gpio, int idx, u32 line_mask)
{
	struct gpio_isr *isr;

	if (idx != 0)
		return;

	spin_lock(&gpio->lock);
	list_for_each_entry(isr, &gpio->isr, head) {
		if (line_mask & (1 << isr->func.line)) {
			if (isr->inhibit)
				continue;
			isr->inhibit = true;
			schedule_work(&isr->work);
		}
	}
	spin_unlock(&gpio->lock);
}

static int
nouveau_gpio_isr_add(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line,
		     void (*handler)(void *, int), void *data)
{
	struct gpio_isr *isr;
	unsigned long flags;
	int ret;

	isr = kzalloc(sizeof(*isr), GFP_KERNEL);
	if (!isr)
		return -ENOMEM;

	ret = nouveau_gpio_find(gpio, idx, tag, line, &isr->func);
	if (ret) {
		kfree(isr);
		return ret;
	}

	INIT_WORK(&isr->work, nouveau_gpio_isr_bh);
	isr->gpio = gpio;
	isr->handler = handler;
	isr->data = data;
	isr->idx = idx;

	spin_lock_irqsave(&gpio->lock, flags);
	list_add(&isr->head, &gpio->isr);
	spin_unlock_irqrestore(&gpio->lock, flags);
	return 0;
}

static void
nouveau_gpio_isr_del(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line,
		     void (*handler)(void *, int), void *data)
void
_nouveau_gpio_dtor(struct nouveau_object *object)
{
	struct gpio_isr *isr, *tmp;
	struct dcb_gpio_func func;
	unsigned long flags;
	LIST_HEAD(tofree);
	int ret;

	ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
	if (ret == 0) {
		spin_lock_irqsave(&gpio->lock, flags);
		list_for_each_entry_safe(isr, tmp, &gpio->isr, head) {
			if (memcmp(&isr->func, &func, sizeof(func)) ||
			    isr->idx != idx ||
			    isr->handler != handler || isr->data != data)
				continue;
			list_move_tail(&isr->head, &tofree);
		}
		spin_unlock_irqrestore(&gpio->lock, flags);

		list_for_each_entry_safe(isr, tmp, &tofree, head) {
			flush_work(&isr->work);
			kfree(isr);
		}
	}
	struct nouveau_gpio *gpio = (void *)object;
	nouveau_event_destroy(&gpio->events);
	nouveau_subdev_destroy(&gpio->base);
}

int
@@ -242,15 +125,13 @@ nouveau_gpio_create_(struct nouveau_object *parent,
	if (ret)
		return ret;

	ret = nouveau_event_create(lines, &gpio->events);
	if (ret)
		return ret;

	gpio->find = nouveau_gpio_find;
	gpio->set  = nouveau_gpio_set;
	gpio->get  = nouveau_gpio_get;
	gpio->irq  = nouveau_gpio_irq;
	gpio->isr_run = nouveau_gpio_isr_run;
	gpio->isr_add = nouveau_gpio_isr_add;
	gpio->isr_del = nouveau_gpio_isr_del;
	INIT_LIST_HEAD(&gpio->isr);
	spin_lock_init(&gpio->lock);
	return 0;
}

+22 −11
Original line number Diff line number Diff line
@@ -82,15 +82,6 @@ nv10_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out)
	return 0;
}

static void
nv10_gpio_irq_enable(struct nouveau_gpio *gpio, int line, bool on)
{
	u32 mask = 0x00010001 << line;

	nv_wr32(gpio, 0x001104, mask);
	nv_mask(gpio, 0x001144, mask, on ? mask : 0);
}

static void
nv10_gpio_intr(struct nouveau_subdev *subdev)
{
@@ -98,12 +89,30 @@ nv10_gpio_intr(struct nouveau_subdev *subdev)
	u32 intr = nv_rd32(priv, 0x001104);
	u32 hi = (intr & 0x0000ffff) >> 0;
	u32 lo = (intr & 0xffff0000) >> 16;
	int i;

	priv->base.isr_run(&priv->base, 0, hi | lo);
	for (i = 0; (hi | lo) && i < 32; i++) {
		if ((hi | lo) & (1 << i))
			nouveau_event_trigger(priv->base.events, i);
	}

	nv_wr32(priv, 0x001104, intr);
}

static void
nv10_gpio_intr_enable(struct nouveau_event *event, int line)
{
	nv_wr32(event->priv, 0x001104, 0x00010001 << line);
	nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00010001 << line);
}

static void
nv10_gpio_intr_disable(struct nouveau_event *event, int line)
{
	nv_wr32(event->priv, 0x001104, 0x00010001 << line);
	nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00000000);
}

static int
nv10_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
	       struct nouveau_oclass *oclass, void *data, u32 size,
@@ -119,7 +128,9 @@ nv10_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,

	priv->base.drive = nv10_gpio_drive;
	priv->base.sense = nv10_gpio_sense;
	priv->base.irq_enable = nv10_gpio_irq_enable;
	priv->base.events->priv = priv;
	priv->base.events->enable = nv10_gpio_intr_enable;
	priv->base.events->disable = nv10_gpio_intr_disable;
	nv_subdev(priv)->intr = nv10_gpio_intr;
	return 0;
}
+27 −12
Original line number Diff line number Diff line
@@ -94,22 +94,13 @@ nv50_gpio_sense(struct nouveau_gpio *gpio, int line)
	return !!(nv_rd32(gpio, reg) & (4 << shift));
}

void
nv50_gpio_irq_enable(struct nouveau_gpio *gpio, int line, bool on)
{
	u32 reg  = line < 16 ? 0xe050 : 0xe070;
	u32 mask = 0x00010001 << (line & 0xf);

	nv_wr32(gpio, reg + 4, mask);
	nv_mask(gpio, reg + 0, mask, on ? mask : 0);
}

void
nv50_gpio_intr(struct nouveau_subdev *subdev)
{
	struct nv50_gpio_priv *priv = (void *)subdev;
	u32 intr0, intr1 = 0;
	u32 hi, lo;
	int i;

	intr0 = nv_rd32(priv, 0xe054) & nv_rd32(priv, 0xe050);
	if (nv_device(priv)->chipset >= 0x90)
@@ -117,13 +108,35 @@ nv50_gpio_intr(struct nouveau_subdev *subdev)

	hi = (intr0 & 0x0000ffff) | (intr1 << 16);
	lo = (intr0 >> 16) | (intr1 & 0xffff0000);
	priv->base.isr_run(&priv->base, 0, hi | lo);

	for (i = 0; (hi | lo) && i < 32; i++) {
		if ((hi | lo) & (1 << i))
			nouveau_event_trigger(priv->base.events, i);
	}

	nv_wr32(priv, 0xe054, intr0);
	if (nv_device(priv)->chipset >= 0x90)
		nv_wr32(priv, 0xe074, intr1);
}

void
nv50_gpio_intr_enable(struct nouveau_event *event, int line)
{
	const u32 addr = line < 16 ? 0xe050 : 0xe070;
	const u32 mask = 0x00010001 << (line & 0xf);
	nv_wr32(event->priv, addr + 0x04, mask);
	nv_mask(event->priv, addr + 0x00, mask, mask);
}

void
nv50_gpio_intr_disable(struct nouveau_event *event, int line)
{
	const u32 addr = line < 16 ? 0xe050 : 0xe070;
	const u32 mask = 0x00010001 << (line & 0xf);
	nv_wr32(event->priv, addr + 0x04, mask);
	nv_mask(event->priv, addr + 0x00, mask, 0x00000000);
}

static int
nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
	       struct nouveau_oclass *oclass, void *data, u32 size,
@@ -142,7 +155,9 @@ nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
	priv->base.reset = nv50_gpio_reset;
	priv->base.drive = nv50_gpio_drive;
	priv->base.sense = nv50_gpio_sense;
	priv->base.irq_enable = nv50_gpio_irq_enable;
	priv->base.events->priv = priv;
	priv->base.events->enable = nv50_gpio_intr_enable;
	priv->base.events->disable = nv50_gpio_intr_disable;
	nv_subdev(priv)->intr = nv50_gpio_intr;
	return 0;
}
+3 −1
Original line number Diff line number Diff line
@@ -88,7 +88,9 @@ nvd0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
	priv->base.reset = nvd0_gpio_reset;
	priv->base.drive = nvd0_gpio_drive;
	priv->base.sense = nvd0_gpio_sense;
	priv->base.irq_enable = nv50_gpio_irq_enable;
	priv->base.events->priv = priv;
	priv->base.events->enable = nv50_gpio_intr_enable;
	priv->base.events->disable = nv50_gpio_intr_disable;
	nv_subdev(priv)->intr = nv50_gpio_intr;
	return 0;
}
Loading