Commit 92af5d47 authored by Marc Zyngier's avatar Marc Zyngier
Browse files

Merge branch irq/meson-gpio into irq/irqchip-next



* irq/meson-gpio:
  : .
  : Expand meson-gpio support to deal with the new Meson-S4 SoC
  : .
  irqchip/meson-gpio: Add support for meson s4 SoCs
  irqchip/meson-gpio: add select trigger type callback
  irqchip/meson-gpio: support more than 8 channels gpio irq
  dt-bindings: interrupt-controller: New binding for Meson-S4 SoCs

Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parents 8e6958c8 d6c47d21
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ Required properties:
    "amlogic,meson-g12a-gpio-intc" for G12A SoCs (S905D2, S905X2, S905Y2)
    "amlogic,meson-sm1-gpio-intc" for SM1 SoCs (S905D3, S905X3, S905Y3)
    "amlogic,meson-a1-gpio-intc" for A1 SoCs (A113L)
    "amlogic,meson-s4-gpio-intc" for S4 SoCs (S802X2, S905Y4, S805X2G, S905W2)
- reg : Specifies base physical address and size of the registers.
- interrupt-controller : Identifies the node as an interrupt controller.
- #interrupt-cells : Specifies the number of cells needed to encode an
+90 −16
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@
#include <linux/of.h>
#include <linux/of_address.h>

#define NUM_CHANNEL 8
#define MAX_NUM_CHANNEL 64
#define MAX_INPUT_MUX 256

#define REG_EDGE_POL	0x00
@@ -26,6 +26,8 @@

/* use for A1 like chips */
#define REG_PIN_A1_SEL	0x04
/* Used for s4 chips */
#define REG_EDGE_POL_S4	0x1c

/*
 * Note: The S905X3 datasheet reports that BOTH_EDGE is controlled by
@@ -51,15 +53,22 @@ static void meson_a1_gpio_irq_sel_pin(struct meson_gpio_irq_controller *ctl,
				      unsigned int channel,
				      unsigned long hwirq);
static void meson_a1_gpio_irq_init(struct meson_gpio_irq_controller *ctl);
static int meson8_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl,
				    unsigned int type, u32 *channel_hwirq);
static int meson_s4_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl,
				      unsigned int type, u32 *channel_hwirq);

struct irq_ctl_ops {
	void (*gpio_irq_sel_pin)(struct meson_gpio_irq_controller *ctl,
				 unsigned int channel, unsigned long hwirq);
	void (*gpio_irq_init)(struct meson_gpio_irq_controller *ctl);
	int (*gpio_irq_set_type)(struct meson_gpio_irq_controller *ctl,
				 unsigned int type, u32 *channel_hwirq);
};

struct meson_gpio_irq_params {
	unsigned int nr_hwirq;
	unsigned int nr_channels;
	bool support_edge_both;
	unsigned int edge_both_offset;
	unsigned int edge_single_offset;
@@ -68,28 +77,44 @@ struct meson_gpio_irq_params {
	struct irq_ctl_ops ops;
};

#define INIT_MESON_COMMON(irqs, init, sel)			\
#define INIT_MESON_COMMON(irqs, init, sel, type)		\
	.nr_hwirq = irqs,					\
	.ops = {						\
		.gpio_irq_init = init,				\
		.gpio_irq_sel_pin = sel,			\
		.gpio_irq_set_type = type,			\
	},

#define INIT_MESON8_COMMON_DATA(irqs)				\
	INIT_MESON_COMMON(irqs, meson_gpio_irq_init_dummy,	\
			  meson8_gpio_irq_sel_pin)		\
			  meson8_gpio_irq_sel_pin,		\
			  meson8_gpio_irq_set_type)		\
	.edge_single_offset = 0,				\
	.pol_low_offset = 16,					\
	.pin_sel_mask = 0xff,					\
	.nr_channels = 8,					\

#define INIT_MESON_A1_COMMON_DATA(irqs)				\
	INIT_MESON_COMMON(irqs, meson_a1_gpio_irq_init,		\
			  meson_a1_gpio_irq_sel_pin)		\
			  meson_a1_gpio_irq_sel_pin,		\
			  meson8_gpio_irq_set_type)		\
	.support_edge_both = true,				\
	.edge_both_offset = 16,					\
	.edge_single_offset = 8,				\
	.pol_low_offset = 0,					\
	.pin_sel_mask = 0x7f,					\
	.nr_channels = 8,					\

#define INIT_MESON_S4_COMMON_DATA(irqs)				\
	INIT_MESON_COMMON(irqs, meson_a1_gpio_irq_init,		\
			  meson_a1_gpio_irq_sel_pin,		\
			  meson_s4_gpio_irq_set_type)		\
	.support_edge_both = true,				\
	.edge_both_offset = 0,					\
	.edge_single_offset = 12,				\
	.pol_low_offset = 0,					\
	.pin_sel_mask = 0xff,					\
	.nr_channels = 12,					\

static const struct meson_gpio_irq_params meson8_params = {
	INIT_MESON8_COMMON_DATA(134)
@@ -121,6 +146,10 @@ static const struct meson_gpio_irq_params a1_params = {
	INIT_MESON_A1_COMMON_DATA(62)
};

static const struct meson_gpio_irq_params s4_params = {
	INIT_MESON_S4_COMMON_DATA(82)
};

static const struct of_device_id meson_irq_gpio_matches[] = {
	{ .compatible = "amlogic,meson8-gpio-intc", .data = &meson8_params },
	{ .compatible = "amlogic,meson8b-gpio-intc", .data = &meson8b_params },
@@ -130,14 +159,15 @@ static const struct of_device_id meson_irq_gpio_matches[] = {
	{ .compatible = "amlogic,meson-g12a-gpio-intc", .data = &axg_params },
	{ .compatible = "amlogic,meson-sm1-gpio-intc", .data = &sm1_params },
	{ .compatible = "amlogic,meson-a1-gpio-intc", .data = &a1_params },
	{ .compatible = "amlogic,meson-s4-gpio-intc", .data = &s4_params },
	{ }
};

struct meson_gpio_irq_controller {
	const struct meson_gpio_irq_params *params;
	void __iomem *base;
	u32 channel_irqs[NUM_CHANNEL];
	DECLARE_BITMAP(channel_map, NUM_CHANNEL);
	u32 channel_irqs[MAX_NUM_CHANNEL];
	DECLARE_BITMAP(channel_map, MAX_NUM_CHANNEL);
	spinlock_t lock;
};

@@ -207,8 +237,8 @@ meson_gpio_irq_request_channel(struct meson_gpio_irq_controller *ctl,
	spin_lock_irqsave(&ctl->lock, flags);

	/* Find a free channel */
	idx = find_first_zero_bit(ctl->channel_map, NUM_CHANNEL);
	if (idx >= NUM_CHANNEL) {
	idx = find_first_zero_bit(ctl->channel_map, ctl->params->nr_channels);
	if (idx >= ctl->params->nr_channels) {
		spin_unlock_irqrestore(&ctl->lock, flags);
		pr_err("No channel available\n");
		return -ENOSPC;
@@ -256,9 +286,8 @@ meson_gpio_irq_release_channel(struct meson_gpio_irq_controller *ctl,
	clear_bit(idx, ctl->channel_map);
}

static int meson_gpio_irq_type_setup(struct meson_gpio_irq_controller *ctl,
				     unsigned int type,
				     u32 *channel_hwirq)
static int meson8_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl,
				    unsigned int type, u32 *channel_hwirq)
{
	u32 val = 0;
	unsigned int idx;
@@ -299,6 +328,51 @@ static int meson_gpio_irq_type_setup(struct meson_gpio_irq_controller *ctl,
	return 0;
}

/*
 * gpio irq relative registers for s4
 * -PADCTRL_GPIO_IRQ_CTRL0
 * bit[31]:    enable/disable all the irq lines
 * bit[12-23]: single edge trigger
 * bit[0-11]:  polarity trigger
 *
 * -PADCTRL_GPIO_IRQ_CTRL[X]
 * bit[0-16]: 7 bits to choose gpio source for irq line 2*[X] - 2
 * bit[16-22]:7 bits to choose gpio source for irq line 2*[X] - 1
 * where X = 1-6
 *
 * -PADCTRL_GPIO_IRQ_CTRL[7]
 * bit[0-11]: both edge trigger
 */
static int meson_s4_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl,
				      unsigned int type, u32 *channel_hwirq)
{
	u32 val = 0;
	unsigned int idx;

	idx = meson_gpio_irq_get_channel_idx(ctl, channel_hwirq);

	type &= IRQ_TYPE_SENSE_MASK;

	meson_gpio_irq_update_bits(ctl, REG_EDGE_POL_S4, BIT(idx), 0);

	if (type == IRQ_TYPE_EDGE_BOTH) {
		val |= BIT(ctl->params->edge_both_offset + idx);
		meson_gpio_irq_update_bits(ctl, REG_EDGE_POL_S4,
					   BIT(ctl->params->edge_both_offset + idx), val);
		return 0;
	}

	if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING))
		val |= BIT(ctl->params->pol_low_offset + idx);

	if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
		val |= BIT(ctl->params->edge_single_offset + idx);

	meson_gpio_irq_update_bits(ctl, REG_EDGE_POL,
				   BIT(idx) | BIT(12 + idx), val);
	return 0;
};

static unsigned int meson_gpio_irq_type_output(unsigned int type)
{
	unsigned int sense = type & IRQ_TYPE_SENSE_MASK;
@@ -323,7 +397,7 @@ static int meson_gpio_irq_set_type(struct irq_data *data, unsigned int type)
	u32 *channel_hwirq = irq_data_get_irq_chip_data(data);
	int ret;

	ret = meson_gpio_irq_type_setup(ctl, type, channel_hwirq);
	ret = ctl->params->ops.gpio_irq_set_type(ctl, type, channel_hwirq);
	if (ret)
		return ret;

@@ -450,10 +524,10 @@ static int meson_gpio_irq_parse_dt(struct device_node *node, struct meson_gpio_i
	ret = of_property_read_variable_u32_array(node,
						  "amlogic,channel-interrupts",
						  ctl->channel_irqs,
						  NUM_CHANNEL,
						  NUM_CHANNEL);
						  ctl->params->nr_channels,
						  ctl->params->nr_channels);
	if (ret < 0) {
		pr_err("can't get %d channel interrupts\n", NUM_CHANNEL);
		pr_err("can't get %d channel interrupts\n", ctl->params->nr_channels);
		return ret;
	}

@@ -507,7 +581,7 @@ static int meson_gpio_irq_of_init(struct device_node *node, struct device_node *
	}

	pr_info("%d to %d gpio interrupt mux initialized\n",
		ctl->params->nr_hwirq, NUM_CHANNEL);
		ctl->params->nr_hwirq, ctl->params->nr_channels);

	return 0;