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

Commit 628c534a authored by Chris Brandt's avatar Chris Brandt Committed by Greg Kroah-Hartman
Browse files

serial: sh-sci: Improve support for separate TEI and DRI interrupts



Some SCIF versions mux error and break interrupts together and then provide
a separate interrupt ID for just TEI/DRI.

Allow all 6 types of interrupts to be specified via platform data (or DT)
and for any signals that are muxed together (have the same interrupt
number) simply register one handler.

Signed-off-by: default avatarChris Brandt <chris.brandt@renesas.com>
Reviewed-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 7acece71
Loading
Loading
Loading
Loading
+40 −51
Original line number Diff line number Diff line
@@ -65,7 +65,8 @@ enum {
	SCIx_RXI_IRQ,
	SCIx_TXI_IRQ,
	SCIx_BRI_IRQ,
	SCIx_TEIDRI_IRQ,
	SCIx_DRI_IRQ,
	SCIx_TEI_IRQ,
	SCIx_NR_IRQS,

	SCIx_MUX_IRQ = SCIx_NR_IRQS,	/* special case */
@@ -77,9 +78,6 @@ enum {
	((port)->irqs[SCIx_ERI_IRQ] &&	\
	 ((port)->irqs[SCIx_RXI_IRQ] < 0))

#define SCIx_TEIDRI_IRQ_EXISTS(port)		\
	((port)->irqs[SCIx_TEIDRI_IRQ] > 0)

enum SCI_CLKS {
	SCI_FCK,		/* Functional Clock */
	SCI_SCK,		/* Optional External Clock */
@@ -1685,14 +1683,23 @@ static irqreturn_t sci_tx_interrupt(int irq, void *ptr)
	return IRQ_HANDLED;
}

static irqreturn_t sci_br_interrupt(int irq, void *ptr);
static irqreturn_t sci_br_interrupt(int irq, void *ptr)
{
	struct uart_port *port = ptr;

	/* Handle BREAKs */
	sci_handle_breaks(port);
	sci_clear_SCxSR(port, SCxSR_BREAK_CLEAR(port));

	return IRQ_HANDLED;
}

static irqreturn_t sci_er_interrupt(int irq, void *ptr)
{
	struct uart_port *port = ptr;
	struct sci_port *s = to_sci_port(port);

	if (SCIx_TEIDRI_IRQ_EXISTS(s)) {
	if (s->irqs[SCIx_ERI_IRQ] == s->irqs[SCIx_BRI_IRQ]) {
		/* Break and Error interrupts are muxed */
		unsigned short ssr_status = serial_port_in(port, SCxSR);

@@ -1727,17 +1734,6 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr)
	return IRQ_HANDLED;
}

static irqreturn_t sci_br_interrupt(int irq, void *ptr)
{
	struct uart_port *port = ptr;

	/* Handle BREAKs */
	sci_handle_breaks(port);
	sci_clear_SCxSR(port, SCxSR_BREAK_CLEAR(port));

	return IRQ_HANDLED;
}

static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
{
	unsigned short ssr_status, scr_status, err_enabled, orer_status = 0;
@@ -1811,6 +1807,16 @@ static const struct sci_irq_desc {
		.handler = sci_br_interrupt,
	},

	[SCIx_DRI_IRQ] = {
		.desc = "rx ready",
		.handler = sci_rx_interrupt,
	},

	[SCIx_TEI_IRQ] = {
		.desc = "tx end",
		.handler = sci_tx_interrupt,
	},

	/*
	 * Special muxed handler.
	 */
@@ -1823,12 +1829,19 @@ static const struct sci_irq_desc {
static int sci_request_irq(struct sci_port *port)
{
	struct uart_port *up = &port->port;
	int i, j, ret = 0;
	int i, j, w, ret = 0;

	for (i = j = 0; i < SCIx_NR_IRQS; i++, j++) {
		const struct sci_irq_desc *desc;
		int irq;

		/* Check if already registered (muxed) */
		for (w = 0; w < i; w++)
			if (port->irqs[w] == port->irqs[i])
				w = i + 1;
		if (w > i)
			continue;

		if (SCIx_IRQ_IS_MUXED(port)) {
			i = SCIx_MUX_IRQ;
			irq = up->irq;
@@ -1844,32 +1857,8 @@ static int sci_request_irq(struct sci_port *port)
		}

		desc = sci_irq_desc + i;
		port->irqstr[j] = NULL;
		if (SCIx_TEIDRI_IRQ_EXISTS(port)) {
			/*
			 * ERI and BRI are muxed, just register ERI and
			 * ignore BRI.
			 * TEI and DRI are muxed, but only DRI
			 * is enabled, so use RXI handler
			 */
			if (i == SCIx_ERI_IRQ)
				port->irqstr[j] = kasprintf(GFP_KERNEL,
							    "%s:err + break",
							    dev_name(up->dev));
			if (i == SCIx_BRI_IRQ)
				continue;
			if (i == SCIx_TEIDRI_IRQ) {
				port->irqstr[j] = kasprintf(GFP_KERNEL,
							    "%s:tx end + rx ready",
							    dev_name(up->dev));
				desc = sci_irq_desc + SCIx_RXI_IRQ;
			}
		}

		if (!port->irqstr[j])
		port->irqstr[j] = kasprintf(GFP_KERNEL, "%s:%s",
						    dev_name(up->dev),
						    desc->desc);
					    dev_name(up->dev), desc->desc);
		if (!port->irqstr[j]) {
			ret = -ENOMEM;
			goto out_nomem;
@@ -2842,17 +2831,17 @@ static int sci_init_single(struct platform_device *dev,

	/* The SCI generates several interrupts. They can be muxed together or
	 * connected to different interrupt lines. In the muxed case only one
	 * interrupt resource is specified. In the non-muxed case three or four
	 * interrupt resources are specified, as the BRI interrupt is optional.
	 * interrupt resource is specified as there is only one interrupt ID.
	 * In the non-muxed case, up to 6 interrupt signals might be generated
	 * from the SCI, however those signals might have their own individual
	 * interrupt ID numbers, or muxed together with another interrupt.
	 */
	if (sci_port->irqs[0] < 0)
		return -ENXIO;

	if (sci_port->irqs[1] < 0) {
		sci_port->irqs[1] = sci_port->irqs[0];
		sci_port->irqs[2] = sci_port->irqs[0];
		sci_port->irqs[3] = sci_port->irqs[0];
	}
	if (sci_port->irqs[1] < 0)
		for (i = 1; i < ARRAY_SIZE(sci_port->irqs); i++)
			sci_port->irqs[i] = sci_port->irqs[0];

	sci_port->params = sci_probe_regmap(p);
	if (unlikely(sci_port->params == NULL))