linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 00/14] Initial Microchip PIC32MZDA Support
@ 2015-12-14 22:42 Joshua Henderson
  2015-12-14 22:42 ` [PATCH v2 01/14] DEVICETREE: Add bindings for PIC32 interrupt controller Joshua Henderson
                   ` (13 more replies)
  0 siblings, 14 replies; 33+ messages in thread
From: Joshua Henderson @ 2015-12-14 22:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, ralf, Joshua Henderson, Andrei Pistirica,
	Andrew Bresticker, Andy Green, Ben Hutchings, Chaotian Jing,
	Corneliu Doban, Cristian Birsan, devicetree, Geert Uytterhoeven,
	Haojian Zhuang, Jean Delvare, Kevin Hao, linux-api, linux-clk,
	linux-gpio, linux-mmc, linux-serial, Lokesh Vutla,
	ludovic.desroches, Luis de Bethencourt, Paul Burton,
	Purna Chandra Mandal, Scott Branden, Shawn Lin, Stephen Boyd,
	Ulf Hansson, Vincent Yang, Weijun Yang, yangbo lu

This patch series adds support for the Microchip PIC32MZDA MIPS platform.
All drivers required to boot from a MMC uSD card are included. Clock,
external interrupt controller, serial, SDHCI, and pinctrl drivers are
included. This has been tested on a PIC32MZDA Starter Kit. A tree with
these changes is available at [0].

[0] https://github.com/joshua-henderson/linux/tree/pic32-upstream-v2

Changes since v1 (https://lkml.org/lkml/2015/11/20/848):

	+ Rename all DT compatible properties to be chip specific.
	+ Remove hardware interrupt priorities from interrupt controller DT
	  bindings.
	+ Remove all dependencies on include headers used by PIC32 DTS
	  files.
	+ Remove arch/mips/include/asm/mach-pic32/gpio.h
	+ Drop usage of the following, mostly non-standard, properties in
	  DT bindings:
		device_type
		piomode
		no-1-8-v
		uart-has-rtscts
		clock-frequency => assigned-clock-rate
	+ Remove PIC32 memory PLL support from DT.
	+ Replace empty 'ranges' with populated one for clock tree node.
	+ Rename all instances of "USART" to "UART".
	+ Remove 'interrupts' property from FSCM of PIC32 clock tree node.
	+ Add default REFCLK rate initialization required for SDHCI in DTS.
	+ Remove default frequency setup for REFOSC clocks in -clk DTS.
	+ Address missing static on local functions and other sparse
	  warnings in several drivers.
	+ Update pinctrl driver to address major binding and architectural
	  issues.
	+ Remove redundant probing 'pb7_clk' to find CPU clock.
	+ Remove unused PIC32 MPLL support.
	+ Remove support for initializing default parent/rate for REFOSC
	  clocks.
	+ Be consistent and use only "SDHCI" when refering to SD host
	  controller
	+ Remove unecessary PIC32 sdhci_ops min clock function.
	+ Make platform PIC32[_CLR|_SET|_INV] register macros safer.

Andrei Pistirica (4):
  DEVICETREE: Add bindings for PIC32 UART driver
  serial: pic32_uart: Add PIC32 UART driver
  DEVICETREE: Add bindings for PIC32 SDHCI host controller
  mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver

Cristian Birsan (2):
  DEVICETREE: Add bindings for PIC32 interrupt controller
  irqchip: irq-pic32-evic: Add support for PIC32 interrupt controller

Joshua Henderson (6):
  DEVICETREE: Add bindings for PIC32/MZDA platforms
  MIPS: Add support for PIC32MZDA platform
  DEVICETREE: Add bindings for PIC32 pin control and GPIO
  pinctrl: pinctrl-pic32: Add PIC32 pin control driver
  MIPS: dts: Add initial DTS for the PIC32MZDA Starter Kit
  MIPS: pic32mzda: Add initial PIC32MZDA Starter Kit defconfig

Purna Chandra Mandal (2):
  DEVICETREE: Add PIC32 clock binding documentation
  clk: clk-pic32: Add PIC32 clock driver

 .../devicetree/bindings/clock/microchip,pic32.txt  |  256 +++
 .../bindings/gpio/microchip,pic32-gpio.txt         |   32 +
 .../interrupt-controller/microchip,pic32-evic.txt  |   58 +
 .../bindings/mips/pic32/microchip,pic32mzda.txt    |   33 +
 .../bindings/mmc/microchip,sdhci-pic32.txt         |   29 +
 .../bindings/pinctrl/microchip,pic32-pinctrl.txt   |   93 +
 .../bindings/serial/microchip,pic32-uart.txt       |   29 +
 arch/mips/Kbuild.platforms                         |    1 +
 arch/mips/Kconfig                                  |    9 +
 arch/mips/boot/dts/Makefile                        |    1 +
 arch/mips/boot/dts/pic32/Makefile                  |   12 +
 arch/mips/boot/dts/pic32/pic32mzda-clk.dtsi        |  235 ++
 arch/mips/boot/dts/pic32/pic32mzda.dtsi            |  275 +++
 arch/mips/boot/dts/pic32/pic32mzda_sk.dts          |  151 ++
 arch/mips/configs/pic32mzda_defconfig              |   88 +
 .../include/asm/mach-pic32/cpu-feature-overrides.h |   32 +
 arch/mips/include/asm/mach-pic32/irq.h             |   22 +
 arch/mips/include/asm/mach-pic32/pic32.h           |   44 +
 arch/mips/include/asm/mach-pic32/spaces.h          |   24 +
 arch/mips/pic32/Kconfig                            |   50 +
 arch/mips/pic32/Makefile                           |    6 +
 arch/mips/pic32/Platform                           |    7 +
 arch/mips/pic32/common/Makefile                    |    5 +
 arch/mips/pic32/common/irq.c                       |   21 +
 arch/mips/pic32/common/reset.c                     |   62 +
 arch/mips/pic32/pic32mzda/Makefile                 |    9 +
 arch/mips/pic32/pic32mzda/config.c                 |  126 ++
 arch/mips/pic32/pic32mzda/early_clk.c              |  106 +
 arch/mips/pic32/pic32mzda/early_console.c          |  171 ++
 arch/mips/pic32/pic32mzda/early_pin.c              |  275 +++
 arch/mips/pic32/pic32mzda/early_pin.h              |  241 ++
 arch/mips/pic32/pic32mzda/init.c                   |  156 ++
 arch/mips/pic32/pic32mzda/pic32mzda.h              |   29 +
 arch/mips/pic32/pic32mzda/time.c                   |   44 +
 drivers/clk/Kconfig                                |    3 +
 drivers/clk/Makefile                               |    1 +
 drivers/clk/clk-pic32.c                            | 1804 +++++++++++++++
 drivers/irqchip/Makefile                           |    1 +
 drivers/irqchip/irq-pic32-evic.c                   |  321 +++
 drivers/mmc/host/Kconfig                           |   11 +
 drivers/mmc/host/Makefile                          |    1 +
 drivers/mmc/host/sdhci-pic32.c                     |  291 +++
 drivers/pinctrl/Kconfig                            |   17 +
 drivers/pinctrl/Makefile                           |    1 +
 drivers/pinctrl/pinctrl-pic32.c                    | 2339 ++++++++++++++++++++
 drivers/pinctrl/pinctrl-pic32.h                    |  141 ++
 drivers/tty/serial/Kconfig                         |   21 +
 drivers/tty/serial/Makefile                        |    1 +
 drivers/tty/serial/pic32_uart.c                    |  927 ++++++++
 drivers/tty/serial/pic32_uart.h                    |  198 ++
 include/linux/irqchip/pic32-evic.h                 |   19 +
 include/linux/platform_data/sdhci-pic32.h          |   22 +
 include/uapi/linux/serial_core.h                   |    3 +
 53 files changed, 8854 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/microchip,pic32.txt
 create mode 100644 Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/microchip,pic32-evic.txt
 create mode 100644 Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt
 create mode 100644 Documentation/devicetree/bindings/mmc/microchip,sdhci-pic32.txt
 create mode 100644 Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt
 create mode 100644 Documentation/devicetree/bindings/serial/microchip,pic32-uart.txt
 create mode 100644 arch/mips/boot/dts/pic32/Makefile
 create mode 100644 arch/mips/boot/dts/pic32/pic32mzda-clk.dtsi
 create mode 100644 arch/mips/boot/dts/pic32/pic32mzda.dtsi
 create mode 100644 arch/mips/boot/dts/pic32/pic32mzda_sk.dts
 create mode 100644 arch/mips/configs/pic32mzda_defconfig
 create mode 100644 arch/mips/include/asm/mach-pic32/cpu-feature-overrides.h
 create mode 100644 arch/mips/include/asm/mach-pic32/irq.h
 create mode 100644 arch/mips/include/asm/mach-pic32/pic32.h
 create mode 100644 arch/mips/include/asm/mach-pic32/spaces.h
 create mode 100644 arch/mips/pic32/Kconfig
 create mode 100644 arch/mips/pic32/Makefile
 create mode 100644 arch/mips/pic32/Platform
 create mode 100644 arch/mips/pic32/common/Makefile
 create mode 100644 arch/mips/pic32/common/irq.c
 create mode 100644 arch/mips/pic32/common/reset.c
 create mode 100644 arch/mips/pic32/pic32mzda/Makefile
 create mode 100644 arch/mips/pic32/pic32mzda/config.c
 create mode 100644 arch/mips/pic32/pic32mzda/early_clk.c
 create mode 100644 arch/mips/pic32/pic32mzda/early_console.c
 create mode 100644 arch/mips/pic32/pic32mzda/early_pin.c
 create mode 100644 arch/mips/pic32/pic32mzda/early_pin.h
 create mode 100644 arch/mips/pic32/pic32mzda/init.c
 create mode 100644 arch/mips/pic32/pic32mzda/pic32mzda.h
 create mode 100644 arch/mips/pic32/pic32mzda/time.c
 create mode 100644 drivers/clk/clk-pic32.c
 create mode 100644 drivers/irqchip/irq-pic32-evic.c
 create mode 100644 drivers/mmc/host/sdhci-pic32.c
 create mode 100644 drivers/pinctrl/pinctrl-pic32.c
 create mode 100644 drivers/pinctrl/pinctrl-pic32.h
 create mode 100644 drivers/tty/serial/pic32_uart.c
 create mode 100644 drivers/tty/serial/pic32_uart.h
 create mode 100644 include/linux/irqchip/pic32-evic.h
 create mode 100644 include/linux/platform_data/sdhci-pic32.h

--
1.7.9.5


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v2 01/14] DEVICETREE: Add bindings for PIC32 interrupt controller
  2015-12-14 22:42 [PATCH v2 00/14] Initial Microchip PIC32MZDA Support Joshua Henderson
@ 2015-12-14 22:42 ` Joshua Henderson
  2015-12-14 23:34   ` Rob Herring
  2015-12-14 22:42 ` [PATCH v2 02/14] irqchip: irq-pic32-evic: Add support " Joshua Henderson
                   ` (12 subsequent siblings)
  13 siblings, 1 reply; 33+ messages in thread
From: Joshua Henderson @ 2015-12-14 22:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, ralf, Cristian Birsan, Joshua Henderson,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, devicetree

From: Cristian Birsan <cristian.birsan@microchip.com>

Document the devicetree bindings for the interrupt controller on
Microchip PIC32 class devices.

Signed-off-by: Cristian Birsan <cristian.birsan@microchip.com>
Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
---
 .../interrupt-controller/microchip,pic32-evic.txt  |   58 ++++++++++++++++++++
 1 file changed, 58 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/microchip,pic32-evic.txt

diff --git a/Documentation/devicetree/bindings/interrupt-controller/microchip,pic32-evic.txt b/Documentation/devicetree/bindings/interrupt-controller/microchip,pic32-evic.txt
new file mode 100644
index 0000000..6f4389a
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/microchip,pic32-evic.txt
@@ -0,0 +1,58 @@
+Microchip PIC32 Interrupt Controller
+====================================
+
+The Microchip PIC32 contains an Enhanced Vectored Interrupt Controller
+(EVIC). It handles internal and external interrupts and provides support for
+irq type and polarity.
+
+Required properties
+-------------------
+
+- compatible: Should be "microchip,pic32mzda-evic"
+
+- reg: Specifies physical base address and size of register range.
+
+- interrupt-controller: Identifies the node as an interrupt controller.
+
+- #interrupt cells: Specifies the number of cells used to encode an interrupt
+source connected to this controller. The value shall be 2 and interrupt
+descriptor shall have the following format:
+	<hw_irq irq_type>
+
+hw_irq - represents the hardware interrupt number as in the data sheet.
+
+irq_type - is used to describe the type and polarity of an interrupt. For
+internal interrupts use IRQ_TYPE_EDGE_RISING for non persistent interrupts and
+IRQ_TYPE_LEVEL_HIGH for persistent interrupts. For external interrupts use
+IRQ_TYPE_EDGE_RISING or IRQ_TYPE_EDGE_FALLING to select the desired polarity.
+
+Example
+-------
+
+evic: interrupt-controller@1f810000 {
+        compatible = "microchip,pic32mzda-evic";
+        interrupt-controller;
+        #interrupt-cells = <2>;
+        reg = <0x1f810000 0x1000>;
+};
+
+Each device must request his interrupt line with the associated priority and
+polarity
+
+Internal interrupt DTS snippet
+------------------------------
+
+device@1f800000 {
+	...
+	interrupts = <113 IRQ_TYPE_LEVEL_HIGH>;
+	...
+};
+
+External interrupt DTS snippet
+------------------------------
+
+device@1f800000 {
+	...
+	interrupts = <3 IRQ_TYPE_EDGE_RISING>;
+	...
+};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 02/14] irqchip: irq-pic32-evic: Add support for PIC32 interrupt controller
  2015-12-14 22:42 [PATCH v2 00/14] Initial Microchip PIC32MZDA Support Joshua Henderson
  2015-12-14 22:42 ` [PATCH v2 01/14] DEVICETREE: Add bindings for PIC32 interrupt controller Joshua Henderson
@ 2015-12-14 22:42 ` Joshua Henderson
  2015-12-15 17:00   ` Marc Zyngier
  2015-12-14 22:42 ` [PATCH v2 03/14] DEVICETREE: Add PIC32 clock binding documentation Joshua Henderson
                   ` (11 subsequent siblings)
  13 siblings, 1 reply; 33+ messages in thread
From: Joshua Henderson @ 2015-12-14 22:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, ralf, Cristian Birsan, Joshua Henderson,
	Thomas Gleixner, Jason Cooper, Marc Zyngier

From: Cristian Birsan <cristian.birsan@microchip.com>

This adds support for the interrupt controller present on PIC32 class
devices.

The following features are supported:
 - DT properties for EVIC and for devices that use interrupt lines
 - Persistent and non-persistent interrupt handling
 - irqdomain support

Signed-off-by: Cristian Birsan <cristian.birsan@microchip.com>
Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
---
 drivers/irqchip/Makefile           |    1 +
 drivers/irqchip/irq-pic32-evic.c   |  321 ++++++++++++++++++++++++++++++++++++
 include/linux/irqchip/pic32-evic.h |   19 +++
 3 files changed, 341 insertions(+)
 create mode 100644 drivers/irqchip/irq-pic32-evic.c
 create mode 100644 include/linux/irqchip/pic32-evic.h

diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 177f78f..e3608fc 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -55,3 +55,4 @@ obj-$(CONFIG_RENESAS_H8S_INTC)		+= irq-renesas-h8s.o
 obj-$(CONFIG_ARCH_SA1100)		+= irq-sa11x0.o
 obj-$(CONFIG_INGENIC_IRQ)		+= irq-ingenic.o
 obj-$(CONFIG_IMX_GPCV2)			+= irq-imx-gpcv2.o
+obj-$(CONFIG_MACH_PIC32)		+= irq-pic32-evic.o
diff --git a/drivers/irqchip/irq-pic32-evic.c b/drivers/irqchip/irq-pic32-evic.c
new file mode 100644
index 0000000..6a7747c
--- /dev/null
+++ b/drivers/irqchip/irq-pic32-evic.c
@@ -0,0 +1,321 @@
+/*
+ * Cristian Birsan <cristian.birsan@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/pic32-evic.h>
+
+#include <asm/irq.h>
+#include <asm/traps.h>
+
+#define CORE_TIMER_INTERRUPT 0
+#define EXTERNAL_INTERRUPT_0 3
+#define EXTERNAL_INTERRUPT_1 8
+#define EXTERNAL_INTERRUPT_2 13
+#define EXTERNAL_INTERRUPT_3 18
+#define EXTERNAL_INTERRUPT_4 23
+
+#define PRI_MASK	0x7	/* 3 bit priority mask */
+#define SUBPRI_MASK	0x3	/* 2 bit subpriority mask */
+#define INT_MASK	0x1F	/* 5 bit pri and subpri mask */
+#define NR_EXT_IRQS	5	/* 5 external interrupts sources */
+
+#define PIC32_INT_PRI(pri, subpri)	\
+	(((pri & PRI_MASK) << 2) | (subpri & SUBPRI_MASK))
+#define DEFAULT_PIC32_INT_PRI PIC32_INT_PRI(2, 0)
+
+static struct irq_domain *evic_irq_domain;
+static struct evic __iomem *evic_base;
+
+static unsigned int *evic_irq_prio;
+
+struct pic_reg {
+	u32 val; /* value register*/
+	u32 clr; /* clear register */
+	u32 set; /* set register */
+	u32 inv; /* inv register */
+} __packed;
+
+struct evic {
+	struct pic_reg intcon;
+	struct pic_reg priss;
+	struct pic_reg intstat;
+	struct pic_reg iptmr;
+	struct pic_reg ifs[6];
+	u32 reserved1[8];
+	struct pic_reg iec[6];
+	u32 reserved2[8];
+	struct pic_reg ipc[48];
+	u32 reserved3[64];
+	u32 off[191];
+} __packed;
+
+static int get_ext_irq_index(irq_hw_number_t hw);
+static void evic_set_ext_irq_polarity(int ext_irq, u32 type);
+
+#define BIT_REG_MASK(bit, reg, mask)		\
+	do {					\
+		reg = bit/32;			\
+		mask = 1 << (bit % 32);		\
+	} while (0)
+
+asmlinkage void __weak plat_irq_dispatch(void)
+{
+	unsigned int irq, hwirq;
+	u32 reg, mask;
+
+	hwirq = readl(&evic_base->intstat.val) & 0xFF;
+
+	/* Check if the interrupt was really triggered by hardware*/
+	BIT_REG_MASK(hwirq, reg, mask);
+	if (likely(readl(&evic_base->ifs[reg].val) &
+			readl(&evic_base->iec[reg].val) & mask)) {
+		irq = irq_linear_revmap(evic_irq_domain, hwirq);
+		do_IRQ(irq);
+	} else
+		spurious_interrupt();
+}
+
+/* mask off an interrupt */
+static inline void mask_pic32_irq(struct irq_data *irqd)
+{
+	u32 reg, mask;
+	unsigned int hwirq = irqd_to_hwirq(irqd);
+
+	BIT_REG_MASK(hwirq, reg, mask);
+	writel(mask, &evic_base->iec[reg].clr);
+}
+
+/* unmask an interrupt */
+static inline void unmask_pic32_irq(struct irq_data *irqd)
+{
+	u32 reg, mask;
+	unsigned int hwirq = irqd_to_hwirq(irqd);
+
+	BIT_REG_MASK(hwirq, reg, mask);
+	writel(mask, &evic_base->iec[reg].set);
+}
+
+/* acknowledge an interrupt */
+static void ack_pic32_irq(struct irq_data *irqd)
+{
+	u32 reg, mask;
+	unsigned int hwirq = irqd_to_hwirq(irqd);
+
+	BIT_REG_MASK(hwirq, reg, mask);
+	writel(mask, &evic_base->ifs[reg].clr);
+}
+
+/* mask off and acknowledge an interrupt */
+static inline void mask_ack_pic32_irq(struct irq_data *irqd)
+{
+	u32 reg, mask;
+	unsigned int hwirq = irqd_to_hwirq(irqd);
+
+	BIT_REG_MASK(hwirq, reg, mask);
+	writel(mask, &evic_base->iec[reg].clr);
+	writel(mask, &evic_base->ifs[reg].clr);
+}
+
+static int set_type_pic32_irq(struct irq_data *data, unsigned int flow_type)
+{
+	int index;
+
+	switch (flow_type) {
+
+	case IRQ_TYPE_EDGE_RISING:
+	case IRQ_TYPE_EDGE_FALLING:
+		irq_set_handler_locked(data, handle_edge_irq);
+		break;
+
+	case IRQ_TYPE_LEVEL_HIGH:
+	case IRQ_TYPE_LEVEL_LOW:
+		irq_set_handler_locked(data, handle_fasteoi_irq);
+		break;
+
+	default:
+		pr_err("Invalid interrupt type !\n");
+		return -EINVAL;
+	}
+
+	/* set polarity for external interrupts only */
+	index = get_ext_irq_index(data->hwirq);
+	if (index >= 0)
+		evic_set_ext_irq_polarity(index, flow_type);
+
+	return IRQ_SET_MASK_OK;
+}
+
+static void pic32_bind_evic_interrupt(int irq, int set)
+{
+	writel(set, &evic_base->off[irq]);
+}
+
+int pic32_get_c0_compare_int(void)
+{
+	int virq;
+
+	virq = irq_create_mapping(evic_irq_domain, CORE_TIMER_INTERRUPT);
+	irq_set_irq_type(virq, IRQ_TYPE_EDGE_RISING);
+	return virq;
+}
+
+static struct irq_chip pic32_irq_chip = {
+	.name = "PIC32-EVIC",
+	.irq_ack = ack_pic32_irq,
+	.irq_mask = mask_pic32_irq,
+	.irq_mask_ack = mask_ack_pic32_irq,
+	.irq_unmask = unmask_pic32_irq,
+	.irq_eoi = ack_pic32_irq,
+	.irq_set_type = set_type_pic32_irq,
+	.irq_enable = unmask_pic32_irq,
+	.irq_disable = mask_pic32_irq,
+};
+
+static void evic_set_irq_priority(int irq, int priority)
+{
+	u32 reg, shift;
+
+	reg = irq / 4;
+	shift = (irq % 4) * 8;
+
+	/* set priority */
+	writel(INT_MASK << shift, &evic_base->ipc[reg].clr);
+	writel(priority << shift, &evic_base->ipc[reg].set);
+}
+
+static void evic_set_ext_irq_polarity(int ext_irq, u32 type)
+{
+	if (WARN_ON(ext_irq >= NR_EXT_IRQS))
+		return;
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+		writel(1 << ext_irq, &evic_base->intcon.set);
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		writel(1 << ext_irq, &evic_base->intcon.clr);
+		break;
+	default:
+		pr_err("Invalid external interrupt polarity !\n");
+	}
+}
+
+static int get_ext_irq_index(irq_hw_number_t hw)
+{
+	switch (hw) {
+	case EXTERNAL_INTERRUPT_0:
+		return 0;
+	case EXTERNAL_INTERRUPT_1:
+		return 1;
+	case EXTERNAL_INTERRUPT_2:
+		return 2;
+	case EXTERNAL_INTERRUPT_3:
+		return 3;
+	case EXTERNAL_INTERRUPT_4:
+		return 4;
+	default:
+		return -1;
+	}
+}
+
+static int evic_intc_map(struct irq_domain *irqd, unsigned int virq,
+			irq_hw_number_t hw)
+{
+	u32 reg, mask;
+
+	irq_set_chip(virq, &pic32_irq_chip);
+
+	BIT_REG_MASK(hw, reg, mask);
+
+	/* disable */
+	writel(mask, &evic_base->iec[reg].clr);
+
+	/* clear flag */
+	writel(mask, &evic_base->ifs[reg].clr);
+
+	evic_set_irq_priority(hw, evic_irq_prio[hw]);
+
+	return 0;
+}
+
+static int evic_irq_domain_xlate(struct irq_domain *d,
+				struct device_node *ctrlr,
+				const u32 *intspec,
+				unsigned int intsize,
+				irq_hw_number_t *out_hwirq,
+				unsigned int *out_type)
+{
+	/* Check for number of params */
+	if (WARN_ON(intsize < 2))
+		return -EINVAL;
+	if (WARN_ON(intspec[0] >= NR_IRQS))
+		return -EINVAL;
+
+	*out_hwirq = intspec[0];
+
+	evic_irq_prio[*out_hwirq] = DEFAULT_PIC32_INT_PRI;
+
+	*out_type = intspec[1];
+
+	return 0;
+}
+
+static const struct irq_domain_ops evic_intc_irq_domain_ops = {
+		.map = evic_intc_map,
+		.xlate = evic_irq_domain_xlate,
+};
+
+#ifdef CONFIG_OF
+static int __init
+microchip_evic_of_init(struct device_node *node, struct device_node *parent)
+{
+	struct resource res;
+
+	if (WARN_ON(!node))
+		return -ENODEV;
+
+	evic_irq_prio = kcalloc(NR_IRQS, sizeof(*evic_irq_prio),
+				GFP_KERNEL);
+	if (!evic_irq_prio)
+		return -ENOMEM;
+
+	evic_irq_prio[CORE_TIMER_INTERRUPT] = DEFAULT_PIC32_INT_PRI;
+
+	if (of_address_to_resource(node, 0, &res))
+		panic("Failed to get evic memory range");
+
+	if (request_mem_region(res.start, resource_size(&res),
+				res.name) == NULL)
+		panic("Failed to request evic memory");
+
+	evic_base = ioremap_nocache(res.start, resource_size(&res));
+	if (!evic_base)
+		panic("Failed to remap evic memory");
+
+	board_bind_eic_interrupt = &pic32_bind_evic_interrupt;
+
+	evic_irq_domain = irq_domain_add_linear(node, NR_IRQS,
+			&evic_intc_irq_domain_ops, NULL);
+	if (!evic_irq_domain)
+		panic("Failed to add linear irqdomain for EVIC");
+
+	irq_set_default_host(evic_irq_domain);
+
+	return 0;
+}
+
+IRQCHIP_DECLARE(microchip_evic, "microchip,pic32mzda-evic",
+		microchip_evic_of_init);
+#endif
diff --git a/include/linux/irqchip/pic32-evic.h b/include/linux/irqchip/pic32-evic.h
new file mode 100644
index 0000000..c514bae
--- /dev/null
+++ b/include/linux/irqchip/pic32-evic.h
@@ -0,0 +1,19 @@
+/*
+ * Joshua Henderson, <joshua.henderson@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ */
+#ifndef __LINUX_IRQCHIP_PIC32_EVIC_H
+#define __LINUX_IRQCHIP_PIC32_EVIC_H
+
+extern int pic32_get_c0_compare_int(void);
+
+#endif /* __LINUX_IRQCHIP_PIC32_EVIC_H */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 03/14] DEVICETREE: Add PIC32 clock binding documentation
  2015-12-14 22:42 [PATCH v2 00/14] Initial Microchip PIC32MZDA Support Joshua Henderson
  2015-12-14 22:42 ` [PATCH v2 01/14] DEVICETREE: Add bindings for PIC32 interrupt controller Joshua Henderson
  2015-12-14 22:42 ` [PATCH v2 02/14] irqchip: irq-pic32-evic: Add support " Joshua Henderson
@ 2015-12-14 22:42 ` Joshua Henderson
  2015-12-18 15:44   ` Rob Herring
  2015-12-14 22:42 ` [PATCH v2 04/14] clk: clk-pic32: Add PIC32 clock driver Joshua Henderson
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 33+ messages in thread
From: Joshua Henderson @ 2015-12-14 22:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, ralf, Purna Chandra Mandal, Joshua Henderson,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree

From: Purna Chandra Mandal <purna.mandal@microchip.com>

Document the devicetree bindings for the clock driver found on Microchip
PIC32 class devices.

Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com>
Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
---
 .../devicetree/bindings/clock/microchip,pic32.txt  |  256 ++++++++++++++++++++
 1 file changed, 256 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/microchip,pic32.txt

diff --git a/Documentation/devicetree/bindings/clock/microchip,pic32.txt b/Documentation/devicetree/bindings/clock/microchip,pic32.txt
new file mode 100644
index 0000000..f50c653
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/microchip,pic32.txt
@@ -0,0 +1,256 @@
+Binding for a Clock hardware block found on
+certain Microchip PIC32 MCU devices.
+
+Microchip SoC clocks-node consists of few oscillators, PLL, multiplexer
+and few divider nodes.
+
+We will find only the base address of the clock tree, this base
+address is common for some of the subnodes, not all. If no address is
+specified for any of subnode base address of the clock tree will be
+treated as its base. Each of subnodes follow the same common clock
+binding with some additional optional properties.
+
+	clocks_node {
+		reg = <>;
+
+		spll_node {
+			...
+		};
+
+		frcdiv_node {
+			...
+		};
+
+		sysclk_mux_node {
+			...
+		};
+
+		pbdiv_node {
+			...
+		};
+
+		refoclk_node {
+			...
+		};
+		...
+	};
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible : should be one of "microchip,pic32mzda-clk",
+    "microchip,pic32mzda-sosc", "microchip,pic32mzda-frcdivclk",
+    "microchip,pic32mzda-syspll", "microchip,pic32mzda-sysclk-v2",
+    "microchip,pic32mzda-pbclk", "microchip,pic32mzda-refoclk".
+- reg : A Base address and length of the register set.
+- interrupts : source of interrupt.
+
+Optional properties (for subnodes):
+- #clock-cells: From common clock binding, should be 0.
+- microchip,clock-indices: in multiplexer node clock sources always aren't linear
+    and contiguous. This property helps define clock-sources with respect to
+    the mux clock node.
+- microchip,ignore-unused : ignore gate request even if the gated clock is unused.
+- microchip,status-bit-mask: bitmask for status check. This will be used to confirm
+    particular operation by clock sub-node is completed. It is dependent sub-node.
+- microchip,bit-mask: enable mask, similar to microchip,status-bit-mask.
+- microchip,slew-step: enable frequency slewing(stepping) during rate change;
+    applicable only to sys-clock subnode.
+
+Example:
+
+/* PIC32 specific clks */
+pic32_clktree {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	reg = <0x1f801200 0x200>;
+	compatible = "microchip,pic32mzda-clk";
+	ranges = <0 0x1f801200 0x200>;
+
+	/* secondary oscillator; external input on SOSCI pin */
+	SOSC:sosc_clk {
+		#clock-cells = <0>;
+		compatible = "microchip,pic32mzda-sosc";
+		clock-frequency = <32768>;
+		reg = <0x000 0x10>, /* enable reg */
+		      <0x1d0 0x10>; /* status reg */
+		microchip,bit-mask = <0x02>; /* enable mask */
+		microchip,status-bit-mask = <0x10>; /* status-mask*/
+	};
+
+	FRCDIV:frcdiv_clk {
+		#clock-cells = <0>;
+		compatible = "microchip,pic32mzda-frcdivclk";
+		clocks = <&FRC>;
+		clock-output-names = "frcdiv_clk";
+	};
+
+	/* System PLL clock */
+	SYSPLL:spll_clk {
+		#clock-cells = <0>;
+		compatible = "microchip,pic32mzda-syspll";
+		reg = <0x020 0x10>, /* SPLL register */
+		      <0x1d0 0x10>; /* CLKSTAT register */
+		clocks = <&POSC>, <&FRC>;
+		clock-output-names = "sys_pll";
+		microchip,status-bit-mask = <0x80>; /* SPLLRDY */
+	};
+
+	/* system clock; mux with postdiv & slew */
+	SYSCLK:sys_clk {
+		#clock-cells = <0>;
+		compatible = "microchip,pic32mzda-sysclk-v2";
+		reg = <0x1c0 0x04>; /* SLEWCON */
+		clocks = <&FRCDIV>, <&SYSPLL>, <&POSC>, <&SOSC>,
+				<&LPRC>, <&FRCDIV>;
+		microchip,clock-indices = <0>, <1>, <2>, <4>, <5>, <7>;
+		clock-output-names = "sys_clk";
+	};
+
+	/* UPLL is integral part of USB PHY; UTMI clk for USBCORE */
+	UPLL:usb_phy_clk {
+		#clock-cells = <0>;
+		compatible = "fixed-clocks";
+		clock-frequency = <24000000>;
+		clock-output-names = "usbphy_clk";
+	};
+
+	/* Peripheral bus1 clock */
+	PBCLK1:pb1_clk {
+		reg = <0x140 0x10>;
+		#clock-cells = <0>;
+		compatible = "microchip,pic32mzda-pbclk";
+		clocks = <&SYSCLK>;
+		clock-output-names = "pb1_clk";
+		/* used by system modules, not gateable */
+		microchip,ignore-unused;
+	};
+
+	/* Peripheral bus2 clock */
+	PBCLK2:pb2_clk {
+		reg = <0x150 0x10>;
+		#clock-cells = <0>;
+		compatible = "microchip,pic32mzda-pbclk";
+		clocks = <&SYSCLK>;
+		clock-output-names = "pb2_clk";
+		/* avoid gating even if unused */
+		microchip,ignore-unused;
+	};
+
+	/* Peripheral bus3 clock */
+	PBCLK3:pb3_clk {
+		reg = <0x160 0x10>;
+		#clock-cells = <0>;
+		compatible = "microchip,pic32mzda-pbclk";
+		clocks = <&SYSCLK>;
+		clock-output-names = "pb3_clk";
+	};
+
+	/* Peripheral bus4 clock(I/O ports, GPIO) */
+	PBCLK4:pb4_clk {
+		reg = <0x170 0x10>;
+		#clock-cells = <0>;
+		compatible = "microchip,pic32mzda-pbclk";
+		clocks = <&SYSCLK>;
+		clock-output-names = "pb4_clk";
+	};
+
+	/* Peripheral bus clock */
+	PBCLK5:pb5_clk {
+		reg = <0x180 0x10>;
+		#clock-cells = <0>;
+		compatible = "microchip,pic32mzda-pbclk";
+		clocks = <&SYSCLK>;
+		clock-output-names = "pb5_clk";
+	};
+
+	/* Peripheral Bus6 clock; */
+	PBCLK6:pb6_clk {
+		reg = <0x190 0x10>;
+		compatible = "microchip,pic32mzda-pbclk";
+		clocks = <&SYSCLK>;
+		#clock-cells = <0>;
+	};
+
+	/* Peripheral bus7 clock */
+	PBCLK7:pb7_clk {
+		reg = <0x1A0 0x10>;
+		#clock-cells = <0>;
+		compatible = "microchip,pic32mzda-pbclk";
+		/* CPU is driven by this clock; so named */
+		clock-output-names = "cpu_clk";
+		clocks = <&SYSCLK>;
+	};
+
+	/* Reference Oscillator clock for SPI/I2S */
+	REFCLKO1:refo1_clk {
+		reg = <0x080 0x20>;
+		#clock-cells = <0>;
+		compatible = "microchip,pic32mzda-refoclk";
+		clocks = <&SYSCLK>, <&PBCLK1>, <&POSC>, <&FRC>, <&LPRC>,
+			<&SOSC>, <&SYSPLL>, <&REFIx>, <&BFRC>;
+		microchip,clock-indices = <0>, <1>, <2>, <3>, <4>, <5>,
+						<7>, <8>, <9>;
+		clock-output-names = "refo1_clk";
+	};
+
+	/* Reference Oscillator clock for SQI */
+	REFCLKO2:refo2_clk {
+		reg = <0x0A0 0x20>;
+		#clock-cells = <0>;
+		compatible = "microchip,pic32mzda-refoclk";
+		clocks = <&SYSCLK>, <&PBCLK1>, <&POSC>, <&FRC>, <&LPRC>,
+			<&SOSC>, <&SYSPLL>, <&REFIx>, <&BFRC>;
+		microchip,clock-indices = <0>, <1>, <2>, <3>, <4>, <5>,
+						<7>, <8>, <9>;
+		clock-output-names = "refo2_clk";
+	};
+
+	/* Reference Oscillator clock, ADC */
+	REFCLKO3:refo3_clk {
+		reg = <0x0C0 0x20>;
+		compatible = "microchip,pic32mzda-refoclk";
+		clocks = <&SYSCLK>, <&PBCLK1>, <&POSC>, <&FRC>, <&LPRC>,
+			<&SOSC>, <&SYSPLL>, <&REFIx>, <&BFRC>;
+		microchip,clock-indices = <0>, <1>, <2>, <3>, <4>, <5>,
+						<7>, <8>, <9>;
+		#clock-cells = <0>;
+		clock-output-names = "refo3_clk";
+	};
+
+	/* Reference Oscillator clock */
+	REFCLKO4:refo4_clk {
+		reg = <0x0E0 0x20>;
+		compatible = "microchip,pic32mzda-refoclk";
+		clocks = <&SYSCLK>, <&PBCLK1>, <&POSC>, <&FRC>, <&LPRC>,
+				<&SOSC>, <&SYSPLL>, <&REFIx>, <&BFRC>;
+		microchip,clock-indices = <0>,<1>,<2>,<3>,<4>,<5>,<7>,
+						<8>,<9>;
+		#clock-cells = <0>;
+		clock-output-names = "refo4_clk";
+	};
+
+	/* Reference Oscillator clock, LCD */
+	REFCLKO5:refo5_clk {
+		reg = <0x100 0x20>;
+		compatible = "microchip,pic32mzda-refoclk";
+		clocks = <&SYSCLK>,<&PBCLK1>,<&POSC>,<&FRC>,<&LPRC>,
+			<&SOSC>,<&SYSPLL>,<&REFIx>,<&BFRC>;
+		microchip,clock-indices = <0>, <1>, <2>, <3>, <4>, <5>,
+					<7>, <8>,<9>;
+		#clock-cells = <0>;
+		clock-output-names = "refo5_clk";
+	};
+};
+
+The clock consumer should specify the desired clock by having the clocks in its
+"clock" phandle cell. For example for UART:
+
+uart2: serial@<> {
+	compatible = "microchip,pic32mzda-uart";
+	reg = <>;
+	interrupts = <>;
+	clocks = <&PBCLK2>;
+}
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 04/14] clk: clk-pic32: Add PIC32 clock driver
  2015-12-14 22:42 [PATCH v2 00/14] Initial Microchip PIC32MZDA Support Joshua Henderson
                   ` (2 preceding siblings ...)
  2015-12-14 22:42 ` [PATCH v2 03/14] DEVICETREE: Add PIC32 clock binding documentation Joshua Henderson
@ 2015-12-14 22:42 ` Joshua Henderson
  2015-12-14 22:42 ` [PATCH v2 05/14] DEVICETREE: Add bindings for PIC32/MZDA platforms Joshua Henderson
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 33+ messages in thread
From: Joshua Henderson @ 2015-12-14 22:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, ralf, Purna Chandra Mandal, Joshua Henderson,
	Michael Turquette, Stephen Boyd, linux-clk

From: Purna Chandra Mandal <purna.mandal@microchip.com>

This clock driver implements PIC32 specific clock-tree. clock-tree
entities can only be configured through device-tree file (OF).

Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com>
Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
---
 drivers/clk/Kconfig     |    3 +
 drivers/clk/Makefile    |    1 +
 drivers/clk/clk-pic32.c | 1804 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1808 insertions(+)
 create mode 100644 drivers/clk/clk-pic32.c

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index c3e3a02..c05f44d 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -188,6 +188,9 @@ config COMMON_CLK_CDCE706
 	---help---
 	  This driver supports TI CDCE706 programmable 3-PLL clock synthesizer.
 
+config COMMON_CLK_PIC32
+	def_bool COMMON_CLK && MACH_PIC32
+
 source "drivers/clk/bcm/Kconfig"
 source "drivers/clk/hisilicon/Kconfig"
 source "drivers/clk/qcom/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 820714c..af737ab 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_ARCH_MOXART)		+= clk-moxart.o
 obj-$(CONFIG_ARCH_NOMADIK)		+= clk-nomadik.o
 obj-$(CONFIG_ARCH_NSPIRE)		+= clk-nspire.o
 obj-$(CONFIG_COMMON_CLK_PALMAS)		+= clk-palmas.o
+obj-$(CONFIG_COMMON_CLK_PIC32)		+= clk-pic32.o
 obj-$(CONFIG_CLK_QORIQ)			+= clk-qoriq.o
 obj-$(CONFIG_COMMON_CLK_RK808)		+= clk-rk808.o
 obj-$(CONFIG_COMMON_CLK_S2MPS11)	+= clk-s2mps11.o
diff --git a/drivers/clk/clk-pic32.c b/drivers/clk/clk-pic32.c
new file mode 100644
index 0000000..ec67766
--- /dev/null
+++ b/drivers/clk/clk-pic32.c
@@ -0,0 +1,1804 @@
+/*
+ * Purna Chandra Mandal,<purna.mandal@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ * This program is free software; you can distribute it and/or modify it
+ * under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <asm/traps.h>
+#include <linux/slab.h>
+
+#include <asm/mach-pic32/pic32.h>
+
+/* OSCCON Reg fields */
+#define OSC_CUR_MASK		0x07
+#define OSC_CUR_SHIFT		12
+#define OSC_NEW_MASK		0x07
+#define OSC_NEW_SHIFT		8
+#define OSC_SWEN		0x01
+#define OSC_CLK_FAILED		0x04
+
+/* SPLLCON Reg fields */
+#define PLL_RANGE_MASK		0x07
+#define PLL_RANGE_SHIFT		0
+#define PLL_ICLK_MASK		0x01
+#define PLL_ICLK_SHIFT		7
+#define PLL_IDIV_MASK		0x07
+#define PLL_IDIV_SHIFT		8
+#define PLL_ODIV_MASK		0x07
+#define PLL_ODIV_SHIFT		24
+#define PLL_MULT_MASK		0x7F
+#define PLL_MULT_SHIFT		16
+#define PLL_MULT_MAX		128
+#define PLL_ODIV_MIN		1
+#define PLL_ODIV_MAX		5
+
+/* Peripheral Bus Clock Reg Fields */
+#define PB_DIV_MASK		0x7f
+#define PB_DIV_SHIFT		0
+#define PB_DIV_READY		BIT(11)
+#define PB_DIV_ENABLED		BIT(15)
+#define PB_DIV_MAX		128
+#define PB_DIV_MIN		0
+
+/* Reference Oscillator Control Reg fields */
+#define REFO_SEL_MASK		0x0f
+#define REFO_SEL_SHIFT		0
+#define REFO_ACTIVE		BIT(8)
+#define REFO_DIVSW_EN		BIT(9)
+#define REFO_OE			BIT(12)
+#define REFO_ON			BIT(15)
+#define REFO_DIV_SHIFT		16
+#define REFO_DIV_MASK		0x7fff
+
+/* Reference Oscillator Trim Register Fields */
+#define REFO_TRIM_REG		0x10 /* Register offset w.r.t. REFO_CON_REG */
+#define REFO_TRIM_MASK		0x1ff
+#define REFO_TRIM_SHIFT		23
+#define REFO_TRIM_MAX		511
+
+/* FRC postscaler */
+#define OSC_FRCDIV_MASK		0x07
+#define OSC_FRCDIV_SHIFT	24
+
+/* FRC tuning */
+#define OSC_FRCTUN_MASK		0x3F
+#define OSC_FRCTUN_SHIFT	0
+
+/* SLEW Control Register fields */
+#define SLEW_BUSY		0x01
+#define SLEW_DOWNEN		0x02
+#define SLEW_UPEN		0x04
+#define SLEW_DIV		0x07
+#define SLEW_DIV_SHIFT		8
+#define SLEW_SYSDIV		0x0f
+#define SLEW_SYSDIV_SHIFT	20
+
+/* Common clock flags */
+#define CLK_ENABLED_ALWAYS	CLK_IGNORE_UNUSED
+#define CLK_DIV_FIXED		BIT(20)
+
+/* Sys Mux clock flags */
+#define SYS_MUX_POSTDIV		0x1
+#define SYS_MUX_SLEW		0x2
+
+#define LOCK_TIMEOUT_NS		(100 * NSEC_PER_MSEC)
+
+/* System PLL clk */
+struct pic32_spll {
+	struct clk_hw hw;
+	void __iomem *regs;
+	void __iomem *status_reg;
+	u32 pll_locked;
+	u8 idiv; /* pll-iclk divider, treating fixed */
+};
+
+/* System Clk */
+struct pic32_sclk {
+	struct clk_hw hw;
+	void __iomem *regs;
+	void __iomem *slwreg;
+	unsigned long flags;
+	u32 *parent_idx;
+	struct debugfs_regset32	regset;
+};
+
+/* Reference Oscillator */
+struct pic32_refosc {
+	struct clk_hw hw;
+	void __iomem *regs;
+	u32 *parent_idx;
+	struct debugfs_regset32	regset;
+};
+
+/* Peripheral Bus Clock */
+struct pic32_pbclk {
+	struct clk_hw hw;
+	void __iomem *regs;
+	u32 flags;
+	struct debugfs_regset32	regset;
+};
+
+/* External SOSC(fixed gated) clock  */
+struct pic32_sosc {
+	struct clk_hw hw;
+	void __iomem *regs;
+	void __iomem *status_reg;
+	unsigned long fixed_rate;
+	int bitmask;
+	int status_bitmask;
+};
+
+/* Soc specific clock reg-base */
+static void __iomem *pic32_clk_regbase;
+static struct clk *pic32_sys_clk;
+
+static DEFINE_SPINLOCK(lock);
+
+#define __clk_lock(flags)	spin_lock_irqsave(&lock, flags)
+#define __clk_unlock(flags)	spin_unlock_irqrestore(&lock, flags)
+
+/* execute unlock-sequence before writing to system registers */
+#define pic32_devcon_sysunlock()	pic32_syskey_unlock()
+#define pic32_devcon_syslock()
+
+/* add instruction pipeline delay while CPU clock is in-transition. */
+#define cpu_nop5()			\
+do {					\
+	__asm__ __volatile__("nop");	\
+	__asm__ __volatile__("nop");	\
+	__asm__ __volatile__("nop");	\
+	__asm__ __volatile__("nop");	\
+	__asm__ __volatile__("nop");	\
+} while (0)
+
+#define clkhw_to_spll(_hw)	container_of(_hw, struct pic32_spll, hw)
+#define clkhw_to_refosc(_hw)	container_of(_hw, struct pic32_refosc, hw)
+#define clkhw_to_pbclk(_hw)	container_of(_hw, struct pic32_pbclk, hw)
+#define clkhw_to_sys_clk(_hw)	container_of(_hw, struct pic32_sclk, hw)
+#define clkhw_to_sosc(_hw)	container_of(_hw, struct pic32_sosc, hw)
+
+/* pic32_of_clk_get_parent_indices - get parent clk hardware indices.
+ *
+ * This is useful specifically for mux clocks where some of possible parent-
+ * clocks logically been dropped thereby creating discontinuous linear
+ * sequence. This API refers OF property "microchip,clock-indices" of the
+ * device node to find h/w id(s) corresponding to each input clock source.
+ */
+int pic32_of_clk_get_parent_indices(struct device_node *np,
+				    u32 **table_p,
+				    int count)
+{
+	struct property *prop;
+	const __be32 *pv;
+	u32 i, *array, ret;
+
+	if ((!table_p) || (!count))
+		return -EINVAL;
+
+	prop = of_find_property(np, "microchip,clock-indices", NULL);
+	if (!prop) {
+		ret = 0;
+		goto out_err;
+	}
+
+	array = kzalloc((sizeof(u32) * count), GFP_KERNEL);
+	if (!array) {
+		ret = -ENOMEM;
+		goto out_err;
+	}
+
+	for (i = 0, pv = NULL; i < count; i++) {
+		pv = of_prop_next_u32(prop, pv, &array[i]);
+		if (!pv) {
+			kfree(array);
+			ret = -EINVAL;
+			goto out_err;
+		}
+	}
+
+	*table_p = array;
+	return 0;
+out_err:
+	*table_p = NULL;
+	return ret;
+}
+EXPORT_SYMBOL(pic32_of_clk_get_parent_indices);
+
+static int pic32_of_clk_register_clkdev(struct device_node *np, struct clk *clk)
+{
+	int ret;
+
+	ret = clk_register_clkdev(clk, NULL, __clk_get_name(clk));
+	if (ret) {
+		pr_err("%s: clkdev register failed, ret %d\n",
+		       __clk_get_name(clk), ret);
+		goto out_err;
+	}
+
+	ret = of_clk_add_provider(np, of_clk_src_simple_get, clk);
+
+out_err:
+	return ret;
+}
+
+static int pbclk_endisable(struct clk_hw *hw, int enable)
+{
+	struct pic32_pbclk *pb = clkhw_to_pbclk(hw);
+
+	if (enable)
+		clk_writel(PB_DIV_ENABLED, PIC32_SET(pb->regs));
+	else
+		clk_writel(PB_DIV_ENABLED, PIC32_CLR(pb->regs));
+	return 0;
+}
+
+static int pbclk_is_enabled(struct clk_hw *hw)
+{
+	u32 v;
+	struct pic32_pbclk *pb = clkhw_to_pbclk(hw);
+
+	v = clk_readl(pb->regs) & PB_DIV_ENABLED;
+	return !!v;
+}
+
+static int pbclk_enable(struct clk_hw *hw)
+{
+	return pbclk_endisable(hw, 1);
+}
+
+static void pbclk_disable(struct clk_hw *hw)
+{
+	struct pic32_pbclk *pb = clkhw_to_pbclk(hw);
+
+	if (pb->flags & CLK_ENABLED_ALWAYS)
+		return;
+
+	pbclk_endisable(hw, 0);
+	cpu_relax();
+}
+
+static unsigned long calc_best_divided_rate(unsigned long rate,
+					    unsigned long parent_rate,
+					    u32 divider_max,
+					    u32 divider_min)
+{
+	u32 divided_rate_up, divided_rate_down, best_rate;
+	u32 divider_down, divider_up;
+
+	/* eq. clk_rate = parent_rate / divider.
+	 *
+	 * Find best divider to produce closest of target divided rate.
+	 */
+
+	divider_down = parent_rate / rate;
+	divider_up = divider_down + 1;
+	if (divider_down >= divider_max) {
+		divider_down = divider_max;
+		divider_up = divider_down;
+	} else if (divider_down < divider_min) {
+		divider_down = divider_min;
+	}
+	divided_rate_up = parent_rate / divider_down;
+	divided_rate_down = parent_rate / divider_up;
+	if (abs(rate - divided_rate_down) < abs(rate - divided_rate_up))
+		best_rate = divided_rate_down;
+	else
+		best_rate = divided_rate_up;
+
+	return best_rate;
+}
+
+static inline u16 pbclk_read_pbdiv(struct pic32_pbclk *pb)
+{
+	u32 v = clk_readl(pb->regs);
+
+	return ((v >> PB_DIV_SHIFT) & PB_DIV_MASK) + 1;
+}
+
+static unsigned long pbclk_recalc_rate(struct clk_hw *hw,
+				       unsigned long parent_rate)
+{
+	struct pic32_pbclk *pb = clkhw_to_pbclk(hw);
+	unsigned long div, rate;
+
+	div = pbclk_read_pbdiv(pb);
+	rate = parent_rate / div;
+
+	return rate;
+}
+
+static long pbclk_round_rate(struct clk_hw *hw, unsigned long rate,
+			     unsigned long *parent_rate)
+{
+	long best_rate = calc_best_divided_rate(rate, *parent_rate,
+						PB_DIV_MAX, PB_DIV_MIN);
+	return best_rate;
+}
+
+static int pbclk_set_rate(struct clk_hw *hw, unsigned long rate,
+			  unsigned long parent_rate)
+{
+	struct pic32_pbclk *pb = clkhw_to_pbclk(hw);
+	u16 div, new_div;
+	unsigned long pbclk, flags, v;
+	ktime_t timeout;
+
+	/* fixed-div clk ? */
+	if (pb->flags & CLK_DIV_FIXED)
+		return -EINVAL;
+
+	/* calculate clkdiv and best rate */
+	new_div = parent_rate / rate;
+	pbclk = parent_rate / new_div;
+
+	/* check & wait for PBDIVRDY */
+	timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS);
+	for (;;) {
+		v = clk_readl(pb->regs);
+		if (v & PB_DIV_READY)
+			break;
+
+		if (ktime_after(ktime_get(), timeout)) {
+			pr_err("%s: pre_rate, busy while timeout\n",
+			       clk_hw_get_name(hw));
+			return -EPERM;
+		}
+		cpu_relax();
+	}
+
+	__clk_lock(flags);
+
+	/* apply new pbdiv */
+	v = clk_readl(pb->regs);
+	v &= ~PB_DIV_MASK;
+	v |= (new_div - 1);
+
+	/* sys unlock */
+	pic32_devcon_sysunlock();
+
+	clk_writel(v, pb->regs);
+
+	/* sys lock */
+	pic32_devcon_syslock();
+
+	__clk_unlock(flags);
+
+	/* wait again, for pbdivready */
+	timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS);
+	for (;;) {
+		v = clk_readl(pb->regs);
+		if (v & PB_DIV_READY)
+			break;
+
+		if (ktime_after(ktime_get(), timeout)) {
+			pr_err("%s: post_rate, busy while timeout\n",
+			       clk_hw_get_name(hw));
+			break;
+		}
+	}
+
+	/* confirm that new div is applied correctly */
+	div = pbclk_read_pbdiv(pb);
+	return (div == new_div) ? 0 : -EPERM;
+}
+
+static struct debugfs_reg32 pbclk_regs_debug[] = {
+	{ .name = "PBxDIV", .offset = 0,},
+};
+
+static int pbclk_debug_init(struct clk_hw *hw, struct dentry *dentry)
+{
+	struct pic32_pbclk *pb = clkhw_to_pbclk(hw);
+	struct dentry *file;
+
+	pb->regset.base = pb->regs;
+	pb->regset.regs = pbclk_regs_debug;
+	pb->regset.nregs = ARRAY_SIZE(pbclk_regs_debug);
+
+	file = debugfs_create_regset32("regdump", S_IRUGO, dentry, &pb->regset);
+	if (IS_ERR(file))
+		return PTR_ERR(file);
+
+	return 0;
+}
+
+/* Reference Oscillator operations */
+static int roclk_endisable(struct clk_hw *hw, int enable)
+{
+	struct pic32_refosc *refo = clkhw_to_refosc(hw);
+
+	if (enable)
+		clk_writel(REFO_ON | REFO_OE, PIC32_SET(refo->regs));
+	else
+		clk_writel(REFO_ON | REFO_OE, PIC32_CLR(refo->regs));
+	return 0;
+}
+
+static int roclk_is_enabled(struct clk_hw *hw)
+{
+	struct pic32_refosc *refo = clkhw_to_refosc(hw);
+
+	return clk_readl(refo->regs) & REFO_ON;
+}
+
+static int roclk_enable(struct clk_hw *hw)
+{
+	return roclk_endisable(hw, 1);
+}
+
+static void roclk_disable(struct clk_hw *hw)
+{
+	roclk_endisable(hw, 0);
+	cpu_relax();
+}
+
+static void roclk_init(struct clk_hw *hw)
+{
+	roclk_disable(hw);
+}
+
+static u8 roclk_get_parent(struct clk_hw *hw)
+{
+	u8 i = 0;
+	struct pic32_refosc *refo = clkhw_to_refosc(hw);
+	unsigned long v;
+
+	v = clk_readl(refo->regs);
+	v = (v >> REFO_SEL_SHIFT) & REFO_SEL_MASK;
+
+	if (!refo->parent_idx)
+		goto done;
+
+	for (i = 0; i < clk_hw_get_num_parents(hw); i++)
+		if (refo->parent_idx[i] == v)
+			return (u8)i;
+done:
+	return (u8)v;
+}
+
+static int roclk_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct pic32_refosc *refo = clkhw_to_refosc(hw);
+	unsigned long v, flags;
+	u8 new_idx, cur_idx, was_disabled = 1;
+
+	new_idx = index;
+	if (refo->parent_idx && (index < clk_hw_get_num_parents(hw)))
+		new_idx = refo->parent_idx[index];
+
+	/* sanity */
+	v = clk_readl(refo->regs);
+	cur_idx = v & REFO_SEL_MASK;
+
+	if (unlikely(cur_idx == new_idx))
+		return 0;
+
+	/*
+	 * Note: clk-src switching is allowed only when module is not ACTIVE.
+	 * Module gets ACTIVE when enabled. So it meant set_parent() needs
+	 * clk-gating across the call.
+	 */
+	if (roclk_is_enabled(hw)) {
+		pr_warn("%s needs gated clock. Forcing.\n", __func__);
+		roclk_disable(hw);
+		was_disabled = 0;
+	}
+
+	/* wait until ACTIVE bit is zero */
+	for (;;) {
+		v = clk_readl(refo->regs);
+		if ((v & REFO_ACTIVE) == 0)
+			break;
+	}
+
+	__clk_lock(flags);
+
+	/* sysunlock */
+	pic32_devcon_sysunlock();
+
+	/* Calculate REFOCON register value */
+	v = clk_readl(refo->regs);
+	v &= ~(REFO_SEL_MASK << REFO_SEL_SHIFT);
+	v |= (new_idx << REFO_SEL_SHIFT);
+
+	/* Apply */
+	clk_writel(v, refo->regs);
+
+	/* syslock */
+	pic32_devcon_syslock();
+
+	/* enable module */
+	clk_writel(REFO_ON | REFO_OE, PIC32_SET(refo->regs));
+
+	__clk_unlock(flags);
+
+	/* keep it disabled, if it was */
+	if (was_disabled)
+		clk_writel(REFO_ON, PIC32_CLR(refo->regs));
+
+	return 0;
+}
+
+static unsigned long roclk_calc_rate(unsigned long parent_rate,
+				     u16 rodiv, u16 rotrim)
+{
+	u64 rate64;
+	u32 N;
+
+	N = rodiv;
+	/* fout = fin / [2 * {N + (M / 512)}]
+	 *	= fin * 512 / [1024 * N + 2 * M]
+	 *	= fin * 256 / (512 * N + M)
+	 *	= (fin << 8) / ((N << 9) + M)
+	 */
+	if (rotrim) {
+		N = (N << 9) + rotrim;
+		rate64 = parent_rate;
+		rate64 <<= 8;
+		do_div(rate64, N);
+	} else {
+		rate64 = parent_rate / (N << 1);
+	}
+	return (unsigned long)rate64;
+}
+
+static void roclk_calc_div_trim(unsigned long rate,
+				unsigned long parent_rate,
+				u16 *rodiv_p, u16 *rotrim_p)
+{
+	u16 div, rotrim, rodiv;
+	u64 frac;
+
+	/* Find integer approximation of floating-point arithmatic.
+	 *      fout = fin / [2 * {rodiv + (rotrim / 512)}] ... (1)
+	 * i.e. fout = fin / 2 * DIV
+	 *      whereas DIV = rodiv + (rotrim / 512)
+	 *
+	 * Since kernel does not perform floating-point arithmatic so
+	 * (rotrim/512) will be zero. And DIV & rodiv will result same.
+	 *
+	 * ie. fout = (fin * 256) / [(512 * rodiv) + rotrim]  ... from (1)
+	 * ie. rotrim = ((fin * 256) / fout) - (512 * DIV)
+	 */
+	if (parent_rate <= rate) {
+		div = 0;
+		frac = 0;
+		rodiv = 0;
+		rotrim = 0;
+	} else {
+		div = parent_rate / (rate << 1);
+		frac = parent_rate;
+		frac <<= 8;
+		do_div(frac, rate);
+		frac -= (u64)(div << 9);
+
+		rodiv = (div > REFO_DIV_MASK) ? REFO_DIV_MASK : div;
+		rotrim = (frac >= REFO_TRIM_MAX) ? REFO_TRIM_MAX : (u16)frac;
+	}
+
+	if (rodiv_p)
+		*rodiv_p = rodiv;
+
+	if (rotrim_p)
+		*rotrim_p = rotrim;
+}
+
+static unsigned long roclk_recalc_rate(struct clk_hw *hw,
+				       unsigned long parent_rate)
+{
+	struct pic32_refosc *refo = clkhw_to_refosc(hw);
+	unsigned long v;
+	u16 rodiv, rotrim;
+
+	/* get rodiv */
+	v = clk_readl(refo->regs);
+	rodiv = (v >> REFO_DIV_SHIFT) & REFO_DIV_MASK;
+
+	/* get trim */
+	v = clk_readl(refo->regs + REFO_TRIM_REG);
+	rotrim = (v >> REFO_TRIM_SHIFT) & REFO_TRIM_MASK;
+
+	v = roclk_calc_rate(parent_rate, rodiv, rotrim);
+	return v;
+}
+
+static long roclk_round_rate(struct clk_hw *hw, unsigned long rate,
+			     unsigned long *parent_rate)
+{
+	u16 rotrim, rodiv;
+
+	/* calculate dividers for new rate */
+	roclk_calc_div_trim(rate, *parent_rate, &rodiv, &rotrim);
+
+	/* caclulate new rate (rounding) based on new rodiv & rotrim */
+	return roclk_calc_rate(*parent_rate, rodiv, rotrim);
+}
+
+static int roclk_determine_rate(struct clk_hw *hw,
+				struct clk_rate_request *req)
+{
+	struct clk_hw *parent_clk, *best_parent_clk = NULL;
+	unsigned int i, delta, best_delta = -1;
+	unsigned long parent_rate, best_parent_rate = 0;
+	unsigned long best = 0, nearest_rate;
+
+	/* find a parent which can generate nearest clkrate >= rate */
+	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+		/* get parent */
+		parent_clk = clk_hw_get_parent_by_index(hw, i);
+		if (!parent_clk)
+			continue;
+
+		/* skip if parent runs slower than target rate */
+		parent_rate = clk_hw_get_rate(parent_clk);
+		if (req->rate > parent_rate)
+			continue;
+
+		nearest_rate = roclk_round_rate(hw, req->rate, &parent_rate);
+		delta = abs(nearest_rate - req->rate);
+		if ((nearest_rate >= req->rate) && (delta < best_delta)) {
+			best_parent_clk = parent_clk;
+			best_parent_rate = parent_rate;
+			best = nearest_rate;
+			best_delta = delta;
+
+			if (delta == 0)
+				break;
+		}
+	}
+
+	/* if no match found, retain old rate */
+	if (!best_parent_clk) {
+		pr_err("%s:%s, no parent found for rate %lu.\n",
+		       __func__, clk_hw_get_name(hw), req->rate);
+		best_parent_clk = clk_hw_get_parent(hw);
+		best_parent_rate = clk_hw_get_rate(best_parent_clk);
+		best = clk_hw_get_rate(hw);
+	}
+
+	pr_debug("%s,rate %lu /best_parent(%s, %lu) /best %lu /delta %d\n",
+		 clk_hw_get_name(hw), req->rate,
+		 clk_hw_get_name(best_parent_clk), best_parent_rate,
+		 best, best_delta);
+
+	if (req->best_parent_rate)
+		req->best_parent_rate = best_parent_rate;
+
+	if (req->best_parent_hw)
+		req->best_parent_hw = best_parent_clk;
+
+	return best;
+}
+
+static int roclk_set_rate_and_parent(struct clk_hw *hw,
+				     unsigned long rate,
+				     unsigned long parent_rate,
+				     u8 index)
+{
+	struct pic32_refosc *refo = clkhw_to_refosc(hw);
+	u16 trim, rodiv, parent_id, was_disabled = 1;
+	unsigned long flags, v;
+
+	if (unlikely(clk_hw_get_rate(hw) == rate))
+		return 0;
+
+	/* calculate new rodiv & rotrim for new rate */
+	roclk_calc_div_trim(rate, parent_rate, &rodiv, &trim);
+
+	pr_debug("parent_rate = %lu, rate = %lu, div = %d, trim = %d\n",
+		 parent_rate, rate, rodiv, trim);
+
+	/* Note: rosel can only be programmed when module is INACTIVE.
+	 * i.e gating is required across set_parent.
+	 * So disable clk, if required.
+	 */
+	if (roclk_is_enabled(hw)) {
+		pr_err("%s: needs gating. Forcing.\n", __func__);
+		roclk_disable(hw);
+		was_disabled = 0;
+	}
+
+	/* check current source */
+	if (refo->parent_idx)
+		index = refo->parent_idx[index];
+
+	parent_id = roclk_get_parent(hw);
+	if (parent_id == index)
+		goto clk_rosel_ready;
+
+	/* wait till source change is active */
+	for (;;) {
+		v = clk_readl(refo->regs);
+		if ((v & (REFO_DIVSW_EN | REFO_ACTIVE)) == 0)
+			break;
+	}
+
+clk_rosel_ready:
+	/* spinlock */
+	__clk_lock(flags);
+	v = clk_readl(refo->regs);
+
+	/* sysunlock */
+	pic32_devcon_sysunlock();
+
+	/* apply parent, if required */
+	if (parent_id != index) {
+		v &= ~(REFO_SEL_MASK << REFO_SEL_SHIFT);
+		v |= (index << REFO_SEL_SHIFT);
+	}
+
+	/* apply RODIV */
+	v &= ~(REFO_DIV_MASK << REFO_DIV_SHIFT);
+	v |= (rodiv << REFO_DIV_SHIFT);
+	clk_writel(v, refo->regs);
+
+	/* apply ROTRIM */
+	v = clk_readl(refo->regs + REFO_TRIM_REG);
+	v &= ~(REFO_TRIM_MASK << REFO_TRIM_SHIFT);
+	v |= (trim << REFO_TRIM_SHIFT);
+	clk_writel(v, refo->regs + REFO_TRIM_REG);
+
+	/* enable refo module */
+	clk_writel(REFO_ON | REFO_OE, PIC32_SET(refo->regs));
+
+	/* activate divider switching */
+	clk_writel(REFO_DIVSW_EN, PIC32_SET(refo->regs));
+
+	/* syslock */
+	pic32_devcon_syslock();
+
+	/* wait till divswen is in-progress */
+	for (;;) {
+		v = clk_readl(refo->regs);
+		if ((v & REFO_DIVSW_EN) == 0)
+			break;
+	}
+
+	__clk_unlock(flags);
+
+	/* keep it disabled if it was */
+	if (was_disabled)
+		clk_writel(REFO_ON, PIC32_CLR(refo->regs));
+
+	return 0;
+}
+
+static int roclk_set_rate(struct clk_hw *hw, unsigned long rate,
+			  unsigned long parent_rate)
+{
+	u8 index = roclk_get_parent(hw);
+
+	return roclk_set_rate_and_parent(hw, rate, parent_rate, index);
+}
+
+static struct debugfs_reg32 roclk_regs_debug[] = {
+	{ .name = "REFOxCON", .offset = 0,},
+	{ .name = "REFOxTRIM", .offset = REFO_TRIM_REG,},
+};
+
+static int roclk_debug_init(struct clk_hw *hw, struct dentry *dentry)
+{
+	struct pic32_refosc *ro = clkhw_to_refosc(hw);
+	struct dentry *file;
+
+	ro->regset.base = ro->regs;
+	ro->regset.regs = roclk_regs_debug;
+	ro->regset.nregs = ARRAY_SIZE(roclk_regs_debug);
+
+	file = debugfs_create_regset32("regdump", S_IRUGO, dentry, &ro->regset);
+
+	return IS_ERR(file) ? PTR_ERR(file) : 0;
+}
+
+static inline u8 spll_odiv_to_divider(u8 odiv)
+{
+	if (odiv <= PLL_ODIV_MIN)
+		odiv = PLL_ODIV_MIN;
+	else if (odiv >= PLL_ODIV_MAX)
+		odiv = PLL_ODIV_MAX;
+
+	return 1 << odiv;
+}
+
+static unsigned long spll_calc_mult_div(struct pic32_spll *pll,
+					unsigned long rate,
+					unsigned long parent_rate,
+					u8 *mult_p, u8 *odiv_p)
+{
+	u8 mul, div, best_mul = 1, best_div = 1;
+	unsigned long new_rate, best_rate = rate;
+	unsigned int best_delta = -1, delta, match_found = 0;
+	u64 rate64;
+
+	parent_rate /= pll->idiv;
+
+	for (mul = 1; mul <= PLL_MULT_MAX; mul++) {
+		for (div = PLL_ODIV_MIN; div <= PLL_ODIV_MAX; div++) {
+			rate64 = parent_rate;
+			rate64 *= mul;
+			do_div(rate64, 1 << div);
+			new_rate = (u32)rate64;
+			delta = abs(rate - new_rate);
+			if ((new_rate >= rate) && (delta < best_delta)) {
+				best_delta = delta;
+				best_rate = new_rate;
+				best_mul = mul;
+				best_div = div;
+				match_found = 1;
+			}
+		}
+	}
+
+	if (!match_found) {
+		pr_warn("spll: no match found\n");
+		return 0;
+	}
+
+	pr_debug("rate %lu, par_rate %lu/mult %u, div %u, best_rate %lu\n",
+		 rate, parent_rate, best_mul, best_div, best_rate);
+
+	if (mult_p)
+		*mult_p = best_mul - 1;
+
+	if (odiv_p)
+		*odiv_p = best_div;
+
+	return best_rate;
+}
+
+static unsigned long spll_clk_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	struct pic32_spll *pll = clkhw_to_spll(hw);
+	unsigned long pll_in_rate, v;
+	u8 mult, odiv, div;
+	u64 rate64;
+
+	v = clk_readl(pll->regs);
+	odiv = ((v >> PLL_ODIV_SHIFT) & PLL_ODIV_MASK);
+	mult = ((v >> PLL_MULT_SHIFT) & PLL_MULT_MASK) + 1;
+	div = spll_odiv_to_divider(odiv);
+
+	/* pll_in = parent_rate / idiv
+	 * pll_out = pll_in * mult / div;
+	 */
+	pll_in_rate = parent_rate / pll->idiv;
+	rate64 = pll_in_rate;
+	rate64 *= mult;
+	do_div(rate64, div);
+
+	return (unsigned long)rate64;
+}
+
+static long spll_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long *parent_rate)
+{
+	struct pic32_spll *pll = clkhw_to_spll(hw);
+
+	return spll_calc_mult_div(pll, rate, *parent_rate, NULL, NULL);
+}
+
+static int spll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+			     unsigned long parent_rate)
+{
+	struct pic32_spll *pll = clkhw_to_spll(hw);
+	u8 mult, odiv;
+	unsigned long ret, v, loop = 1000;
+	struct clk *sclk_parent;
+	unsigned long flags;
+
+	ret = spll_calc_mult_div(pll, rate, parent_rate, &mult, &odiv);
+	if (!ret || (ret == rate))
+		return 0;
+
+	/* To change frequency
+	 * - (a) check whether this clk is active parent of SYSCLK.
+	 * - (b) apply new mult & odiv.
+	 * - (c) switch back to PLL
+	 * - (d) wait until PLL settles down / locked.
+	 */
+
+	/* To check whether rate change is allowed we will have to ensure
+	 * spll_clk is not active parent of sys_clk.
+	 */
+	if (WARN_ON(IS_ERR_OR_NULL(pic32_sys_clk)))
+		return -EPERM;
+
+	/* get sysclk parent */
+	sclk_parent = clk_get_parent(pic32_sys_clk);
+
+	/* does sys_clk using spll_clk as parent ? */
+	if (unlikely(__clk_get_hw(sclk_parent) == hw)) {
+		pr_err("spll: set_rate() is not allowed when spll is parent of sys_clk.");
+		pr_err("First reparent sys_clk to frcdiv-clk and then try.\n");
+		return -EPERM;
+	}
+
+	/* lock */
+	__clk_lock(flags);
+
+	/* apply new multiplier & divisor (read-modify-write) */
+	v = clk_readl(pll->regs);
+	v &= ~(PLL_MULT_MASK << PLL_MULT_SHIFT);
+	v &= ~(PLL_ODIV_MASK << PLL_ODIV_SHIFT);
+	v |= (mult << PLL_MULT_SHIFT) | (odiv << PLL_ODIV_SHIFT);
+
+	/* sysunlock before writing to SPLLCON register */
+	pic32_devcon_sysunlock();
+
+	clk_writel(v, pll->regs);
+	cpu_relax();
+
+	/* insert few nops (5-stage) to ensure CPU does not hang */
+	cpu_nop5();
+	cpu_nop5();
+
+	/* syslock*/
+	pic32_devcon_syslock();
+
+	/* Wait until PLL is locked (maximum 100 usecs). */
+	for (;;) {
+		v = clk_readl(pll->status_reg);
+		if (v & pll->pll_locked)
+			break;
+
+		if (--loop == 0)
+			break;
+
+		ndelay(100);
+	}
+
+	/* lock */
+	__clk_unlock(flags);
+
+	return 0;
+}
+
+static struct debugfs_reg32 sclk_regs_debug[] = {
+	{ .name = "OSCCON", .offset = 0,},
+	{ .name = "OSCTUN", .offset = 0x10,},
+	{ .name = "SPLLCON", .offset = 0x20,},
+};
+
+static int sclk_debug_init(struct clk_hw *hw, struct dentry *dir)
+{
+	struct pic32_sclk *sclk = clkhw_to_sys_clk(hw);
+	struct dentry *file;
+
+	sclk->regset.base = sclk->regs;
+	sclk->regset.regs = sclk_regs_debug;
+	sclk->regset.nregs = ARRAY_SIZE(sclk_regs_debug);
+
+	file = debugfs_create_regset32("regdump", S_IRUGO, dir, &sclk->regset);
+	if (IS_ERR(file))
+		return PTR_ERR(file);
+
+	return 0;
+}
+
+static long sclk_round_rate(struct clk_hw *hw, unsigned long rate,
+			    unsigned long *parent_rate)
+{
+	return calc_best_divided_rate(rate, *parent_rate, SLEW_SYSDIV, 1);
+}
+
+static unsigned long sclk_get_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+	u32 v, div;
+	struct pic32_sclk *sysclk = clkhw_to_sys_clk(hw);
+
+	v = clk_readl(sysclk->slwreg);
+	div = (v >> SLEW_SYSDIV_SHIFT) & SLEW_SYSDIV;
+	div += 1; /* sys-div to divider */
+
+	return parent_rate / div;
+}
+
+static int sclk_set_rate(struct clk_hw *hw,
+			 unsigned long rate, unsigned long parent_rate)
+{
+	u32 v, div;
+	unsigned long flags;
+	struct pic32_sclk *sysclk = clkhw_to_sys_clk(hw);
+	ktime_t timeout;
+
+	div = parent_rate / rate;
+
+	__clk_lock(flags);
+
+	/* sysunlock*/
+	pic32_devcon_sysunlock();
+
+	/* apply new div */
+	v = clk_readl(sysclk->slwreg);
+	v &= ~(SLEW_SYSDIV << SLEW_SYSDIV_SHIFT);
+	v |= ((div - 1) << SLEW_SYSDIV_SHIFT);
+	clk_writel(v, sysclk->slwreg);
+
+	/* syslock*/
+	pic32_devcon_syslock();
+
+	/* wait until BUSY is cleared */
+	timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS);
+	for (;;) {
+		v = clk_readl(sysclk->slwreg);
+		if (!(v & SLEW_BUSY))
+			break;
+
+		if (ktime_after(ktime_get(), timeout)) {
+			pr_err("%s: busy while timeout\n",
+			       clk_hw_get_name(hw));
+			break;
+		}
+	}
+	__clk_unlock(flags);
+
+	return 0;
+}
+
+static u8 sclk_get_parent(struct clk_hw *hw)
+{
+	u8 idx, i;
+	u32 v;
+	struct pic32_sclk *sysclk = clkhw_to_sys_clk(hw);
+
+	v = clk_readl(sysclk->regs);
+	idx = (v >> OSC_CUR_SHIFT) & OSC_CUR_MASK;
+
+	if (!sysclk->parent_idx)
+		goto done;
+
+	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+		if (sysclk->parent_idx[i] == idx) {
+			idx = i;
+			break;
+		}
+	}
+
+done:
+	return idx;
+}
+
+static int sclk_set_parent(struct clk_hw *hw, u8 index)
+{
+	u32 v;
+	unsigned long flags, parent_rate;
+	u8 nosc, cosc;
+	struct pic32_sclk *sysclk = clkhw_to_sys_clk(hw);
+
+	/* find new_osc */
+	nosc = sysclk->parent_idx ? sysclk->parent_idx[index] : index;
+
+	/* check cur_osc is not same as new_osc */
+	v = clk_readl(sysclk->regs);
+	cosc = (v >> OSC_CUR_SHIFT) & OSC_CUR_MASK;
+	if (unlikely(cosc == nosc))
+		return 0;
+
+	parent_rate = clk_hw_get_rate(clk_hw_get_parent_by_index(hw, index));
+
+	/* spin lock */
+	__clk_lock(flags);
+
+	/* sysunlock*/
+	pic32_devcon_sysunlock();
+
+	/* set new parent */
+	v = clk_readl(sysclk->regs);
+	v &= ~(OSC_NEW_MASK << OSC_NEW_SHIFT);
+	v |= (nosc << OSC_NEW_SHIFT);
+	clk_writel(v, sysclk->regs);
+
+	/* initate switch */
+	clk_writel(OSC_SWEN, PIC32_SET(sysclk->regs));
+	cpu_relax();
+
+	/* some nop to flush pipeline(cpu-clk is in-flux) */
+	cpu_nop5();
+
+	/* syslock */
+	pic32_devcon_syslock();
+
+	/* wait for SWEN bit to clear */
+	for (;;) {
+		v = clk_readl(sysclk->regs);
+		if (!(v & OSC_SWEN))
+			break;
+	}
+
+	/* spin unlock */
+	__clk_unlock(flags);
+
+	/* SYSCLK switch logic performs sanity and maintains state machine for
+	 * clock-switching. So h/w might reject clk-switch request if required
+	 * conditions (like clksrc not present or unstable) aren't met.
+	 * So confirm before claiming success.
+	 */
+	cosc = (v >> OSC_CUR_SHIFT) & OSC_CUR_MASK;
+	if (unlikely(cosc != nosc)) {
+		pr_err("%s: err COSC %d and NOSC %d\n",
+		       clk_hw_get_name(hw), cosc, nosc);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int sosc_clk_enable(struct clk_hw *hw)
+{
+	int loop;
+	unsigned long flags;
+	struct pic32_sosc *sosc = clkhw_to_sosc(hw);
+
+	local_irq_save(flags);
+
+	/* enable SOSC */
+	pic32_devcon_sysunlock();
+	clk_writel(sosc->bitmask, PIC32_SET(sosc->regs));
+	pic32_devcon_syslock();
+
+	/* Wait till warm-up period expires and ready-status is updated */
+	for (loop = 1024; loop; --loop) {
+		cpu_relax();
+		if (clk_readl(sosc->status_reg) & sosc->status_bitmask)
+			break;
+	}
+
+	local_irq_restore(flags);
+
+	if (!loop) {
+		pr_err("%s: possibly clk is not present or ready for ops\n",
+		       clk_hw_get_name(hw));
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static void sosc_clk_disable(struct clk_hw *hw)
+{
+	struct pic32_sosc *sosc = clkhw_to_sosc(hw);
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	pic32_devcon_sysunlock();
+	clk_writel(sosc->bitmask, PIC32_CLR(sosc->regs));
+	pic32_devcon_syslock();
+
+	local_irq_restore(flags);
+}
+
+static int sosc_clk_is_enabled(struct clk_hw *hw)
+{
+	struct pic32_sosc *sosc = clkhw_to_sosc(hw);
+	u32 enable, status;
+
+	/* check enable & ready-status */
+	enable = clk_readl(sosc->regs) & sosc->bitmask;
+	status = clk_readl(sosc->status_reg) & sosc->status_bitmask;
+
+	return enable && status;
+}
+
+static unsigned long sosc_clk_calc_rate(struct clk_hw *hw,
+					unsigned long parent_rate)
+{
+	return clkhw_to_sosc(hw)->fixed_rate;
+}
+
+static struct clk_ops pbclk_ops = {
+	.enable		= pbclk_enable,
+	.disable	= pbclk_disable,
+	.is_enabled	= pbclk_is_enabled,
+	.recalc_rate	= pbclk_recalc_rate,
+	.round_rate	= pbclk_round_rate,
+	.set_rate	= pbclk_set_rate,
+	.debug_init	= pbclk_debug_init,
+};
+
+/* sysclk is a mux with post-divider.
+ * get/set_parent &  get/set_rate are required operation.
+ */
+static struct clk_ops sclk_postdiv_ops = {
+	.get_parent	= sclk_get_parent,
+	.set_parent	= sclk_set_parent,
+	.determine_rate = __clk_mux_determine_rate,
+	.round_rate	= sclk_round_rate,
+	.set_rate	= sclk_set_rate,
+	.recalc_rate	= sclk_get_rate,
+	.debug_init	= sclk_debug_init,
+};
+
+static struct clk_ops spll_clk_ops = {
+	.recalc_rate	= spll_clk_recalc_rate,
+	.round_rate	= spll_clk_round_rate,
+	.set_rate	= spll_clk_set_rate,
+};
+
+static struct clk_ops roclk_ops = {
+	.enable			= roclk_enable,
+	.disable		= roclk_disable,
+	.is_enabled		= roclk_is_enabled,
+	.get_parent		= roclk_get_parent,
+	.set_parent		= roclk_set_parent,
+	.determine_rate		= roclk_determine_rate,
+	.recalc_rate		= roclk_recalc_rate,
+	.round_rate		= roclk_round_rate,
+	.set_rate_and_parent	= roclk_set_rate_and_parent,
+	.set_rate		= roclk_set_rate,
+	.init			= roclk_init,
+	.debug_init		= roclk_debug_init,
+};
+
+static struct clk_ops sosc_ops = {
+	.enable = sosc_clk_enable,
+	.disable = sosc_clk_disable,
+	.is_enabled = sosc_clk_is_enabled,
+	.recalc_rate = sosc_clk_calc_rate,
+};
+
+#define init_clk_data(__initdata, __clk, __parents,	\
+	__nr_parents, __flags, __ops)			\
+	__initdata.name = (__clk);			\
+	__initdata.ops = (__ops);			\
+	__initdata.flags = (__flags);			\
+	__initdata.parent_names = (__parents);		\
+	__initdata.num_parents = (__nr_parents)
+
+static struct clk *periph_clk_register(const char *name,
+				       const char **parent_name,
+				       void __iomem *regs, u32 flags)
+{
+	struct clk *clk;
+	struct pic32_pbclk *pbclk;
+	struct clk_init_data init;
+
+	init_clk_data(init, name, parent_name, 1,
+		      flags | CLK_IS_BASIC, &pbclk_ops);
+
+	pbclk = kzalloc(sizeof(*pbclk), GFP_KERNEL);
+	if (!pbclk)
+		return ERR_PTR(-ENOMEM);
+
+	/* init */
+	pbclk->regs = regs;
+	pbclk->flags = flags;
+	pbclk->hw.init = &init;
+
+	clk = clk_register(NULL, &pbclk->hw);
+	if (IS_ERR(clk))
+		kfree(pbclk);
+
+	return clk;
+}
+
+static struct clk *sys_mux_clk_register(const char *name,
+					const char **parents,
+					const int num_parents,
+					void __iomem *regs,
+					void __iomem *slew_reg,
+					u32 *parent_idx,
+					const struct clk_ops *clkop)
+{
+	struct clk *clk;
+	struct pic32_sclk *sysclk;
+	struct clk_init_data init;
+
+	init_clk_data(init, name, parents, num_parents,
+		      CLK_IS_BASIC, clkop);
+
+	sysclk = kzalloc(sizeof(*sysclk), GFP_KERNEL);
+	if (!sysclk)
+		return ERR_PTR(-ENOMEM);
+
+	/* init sysclk data */
+	sysclk->hw.init = &init;
+	sysclk->regs = regs;
+	sysclk->slwreg = slew_reg;
+	sysclk->parent_idx = parent_idx;
+
+	clk = clk_register(NULL, &sysclk->hw);
+	if (IS_ERR(clk)) {
+		kfree(sysclk);
+		return clk;
+	}
+
+	/* Maintain reference to this clock;
+	 * This clock will be needed in spll-rate-change.
+	 */
+	pic32_sys_clk = clk;
+
+	return clk;
+}
+
+static struct clk *spll_clk_register(const char *name, const char *parents,
+				     void __iomem *regs,
+				     void __iomem *status_reg,
+				     u32 lock_bitmask)
+{
+	u32 v;
+	struct pic32_spll *pll;
+	struct clk_init_data init;
+	struct clk *clk;
+
+	init_clk_data(init, name, &parents, 1, CLK_IS_BASIC, &spll_clk_ops);
+
+	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	/* initialize configuration */
+	pll->regs = regs;
+	pll->status_reg = status_reg;
+	pll->pll_locked = lock_bitmask;
+	pll->hw.init = &init;
+
+	/* read and cache pll_idiv; we will use it as constant.*/
+	v = clk_readl(pll->regs);
+	pll->idiv = ((v >> PLL_IDIV_SHIFT) & PLL_IDIV_MASK) + 1;
+
+	clk = clk_register(NULL, &pll->hw);
+	if (IS_ERR(clk))
+		kfree(pll);
+
+	return clk;
+}
+
+static struct clk *refo_clk_register(const char *name,
+				     const char **parents,
+				     u32 nr_parents,
+				     void __iomem *regs,
+				     u32 *parent_idx)
+{
+	struct pic32_refosc *refo;
+	struct clk_init_data init;
+	struct clk *clk;
+	int clk_flags = CLK_IS_BASIC;
+
+	init_clk_data(init, name, parents, nr_parents, clk_flags, &roclk_ops);
+
+	refo = kmalloc(sizeof(*refo), GFP_KERNEL);
+	if (!refo)
+		return ERR_PTR(-ENOMEM);
+
+	/* initialize configuration */
+	refo->regs = regs;
+	refo->hw.init = &init;
+	refo->parent_idx = parent_idx;
+
+	clk = clk_register(NULL, &refo->hw);
+	if (IS_ERR(clk))
+		kfree(refo);
+
+	return clk;
+}
+
+static void __init of_sosc_clk_setup(struct device_node *np)
+{
+	u32 rate, stsmask, bitmask;
+	struct pic32_sosc *sosc;
+	struct clk *clk;
+	struct clk_init_data init;
+	void __iomem *regs, *status_reg;
+	const char *name = np->name;
+
+	if (of_property_read_u32(np, "clock-frequency", &rate))
+		return;
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regs = of_iomap(np, 0);
+	if (!regs)
+		regs = pic32_clk_regbase;
+
+	status_reg = of_iomap(np, 1);
+	if (!status_reg)
+		status_reg = regs;
+
+	of_property_read_u32(np, "microchip,bit-mask", &bitmask);
+
+	of_property_read_u32(np, "microchip,status-bit-mask", &stsmask);
+
+	/* allocate fixed-rate clock */
+	sosc = kzalloc(sizeof(*sosc), GFP_KERNEL);
+	if (!sosc)
+		return;
+
+	init_clk_data(init, name, NULL, 0,
+		      CLK_IS_BASIC | CLK_IS_ROOT, &sosc_ops);
+
+	/* struct clk assignments */
+	sosc->fixed_rate = rate;
+	sosc->hw.init = &init;
+	sosc->regs = regs;
+	sosc->status_reg = status_reg;
+	sosc->bitmask = bitmask;
+	sosc->status_bitmask = stsmask;
+
+	/* register the clock */
+	clk = clk_register(NULL, &sosc->hw);
+	if (IS_ERR(clk))
+		kfree(sosc);
+	else
+		pic32_of_clk_register_clkdev(np, clk);
+}
+
+static void __init of_periph_clk_setup(struct device_node *np)
+{
+	const char *parent_name;
+	const char *name = np->name;
+	struct clk *clk;
+	u32 flags = 0;
+	void __iomem *regs;
+
+	regs = of_iomap(np, 0);
+	if (!regs) {
+		pr_err("%s: could not get reg property\n", name);
+		return;
+	}
+
+	parent_name = of_clk_get_parent_name(np, 0);
+	if (!parent_name) {
+		pr_err("pbclk: %s must have a parent\n", name);
+		goto err_map;
+	}
+
+	if (of_find_property(np, "microchip,ignore-unused", NULL)) {
+		flags |= CLK_IGNORE_UNUSED;
+		pr_info("%s: ignore gating even if unused\n", name);
+	}
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	/* register peripheral clock */
+	clk = periph_clk_register(name, &parent_name, regs, flags);
+	if (IS_ERR(clk)) {
+		pr_err("%s: could not register clock\n", name);
+		goto err_map;
+	}
+
+	pic32_of_clk_register_clkdev(np, clk);
+
+	return;
+
+err_map:
+	iounmap(regs);
+}
+
+static void __init of_refo_clk_setup(struct device_node *np)
+{
+	struct clk *clk;
+	int ret, i, count;
+	const char **parents;
+	const char *clk_name = np->name;
+	void __iomem *regs;
+	u32 *parent_idx;
+
+	/* get the input clock source count */
+	count = of_clk_get_parent_count(np);
+	if (count < 0) {
+		pr_err("%s: get clock count error\n", np->name);
+		return;
+	}
+
+	parents = kzalloc((sizeof(char *) * count), GFP_KERNEL);
+	if (!parents)
+		return;
+
+	ret = pic32_of_clk_get_parent_indices(np, &parent_idx, count);
+	if (ret)
+		goto err_parent;
+
+	for (i = 0; i < count; i++)
+		parents[i] = of_clk_get_parent_name(np, i);
+
+	/* get iobase */
+	regs = of_iomap(np, 0);
+	if (!regs) {
+		pr_err("%s: could not get reg property\n", np->name);
+		goto err_parent_idx;
+	}
+
+	of_property_read_string(np, "clock-output-names", &clk_name);
+
+	clk = refo_clk_register(clk_name, parents, count, regs, parent_idx);
+	if (IS_ERR(clk)) {
+		pr_err("%s: could not register clock\n", clk_name);
+		goto err_map;
+	}
+
+	pic32_of_clk_register_clkdev(np, clk);
+
+	goto err_parent;
+
+err_map:
+	iounmap(regs);
+err_parent_idx:
+	kfree(parent_idx);
+err_parent:
+	kfree(parents);
+}
+
+static void __init of_sys_mux_slew_setup(struct device_node *np)
+{
+	struct clk *clk;
+	int ret, i, count;
+	const char *clk_name;
+	const char **parents;
+	u32 *parent_idx, slew, v;
+	unsigned long flags;
+	void __iomem *slew_reg;
+
+	/* get the input clock source count */
+	count = of_clk_get_parent_count(np);
+	if (count < 0) {
+		pr_err("%s: get clock count error\n", np->name);
+		return;
+	}
+
+	parents = kzalloc((sizeof(char *) * count), GFP_KERNEL);
+	if (!parents)
+		return;
+
+	ret = pic32_of_clk_get_parent_indices(np, &parent_idx, count);
+	if (ret)
+		goto err_name;
+
+	for (i = 0; i < count; i++)
+		parents[i] = of_clk_get_parent_name(np, i);
+
+	ret = of_property_read_string_index(np, "clock-output-names",
+					    0, &clk_name);
+	if (ret)
+		clk_name = np->name;
+
+	/* get slew base */
+	slew_reg = of_iomap(np, 0);
+	if (!slew_reg) {
+		pr_warn("%s: no slew register ?\n", clk_name);
+		goto err_name;
+	}
+
+	/* register mux clk */
+	clk = sys_mux_clk_register(clk_name, parents, count, pic32_clk_regbase,
+				   slew_reg, parent_idx, &sclk_postdiv_ops);
+	if (IS_ERR(clk)) {
+		pr_err("%s: could not register clock\n", clk_name);
+		goto err_parent_idx;
+	}
+
+	/* enable slew, if asked */
+	if (!of_property_read_u32(np, "microchip,slew-step", &slew)) {
+		__clk_lock(flags);
+
+		v = clk_readl(slew_reg);
+		/* Apply new slew-div and enable up/down slewing */
+		v &= ~(SLEW_DIV << SLEW_DIV_SHIFT);
+		v |= (slew << SLEW_DIV_SHIFT);
+		v |= SLEW_DOWNEN | SLEW_UPEN;
+		clk_writel(v, slew_reg);
+
+		__clk_unlock(flags);
+	}
+
+	/* register clkdev */
+	pic32_of_clk_register_clkdev(np, clk);
+
+	goto err_name;
+
+err_parent_idx:
+	iounmap(slew_reg);
+	kfree(parent_idx);
+err_name:
+	kfree(parents);
+}
+
+static void __init of_sys_pll_setup(struct device_node *np)
+{
+	int i, count;
+	const char *clk_name = np->name;
+	const char **parent_names;
+	const char *plliclk_name = "spll_mux_clk";
+	void __iomem *regs, *stat_reg;
+	struct clk *clk, *mux_clk;
+	u32 bitmask;
+
+	/* get the input clock source count */
+	count = of_clk_get_parent_count(np);
+	if (count < 0) {
+		pr_err("%s: get clock count error, %d\n", np->name, count);
+		return;
+	}
+
+	parent_names = kzalloc((sizeof(char *) * count), GFP_KERNEL);
+	if (!parent_names)
+		return;
+
+	for (i = 0; i < count; i++)
+		parent_names[i] = of_clk_get_parent_name(np, i);
+
+	/* get output name */
+	of_property_read_string(np, "clock-output-names", &clk_name);
+
+	/* get iobase */
+	regs = of_iomap(np, 0);
+	if (!regs) {
+		pr_err("%s: of_iomap failed\n", np->name);
+		goto err_name;
+	}
+
+	/* get status reg & status bitmask */
+	stat_reg = of_iomap(np, 1);
+
+	of_property_read_u32(np, "microchip,status-bit-mask", &bitmask);
+	if (!stat_reg || !bitmask)
+		pr_warn("%s: status_reg(or bit-mask) not found.\n", np->name);
+
+	/* register plliclk mux */
+	mux_clk = clk_register_mux(NULL, plliclk_name, parent_names,
+				   count, 0, regs,
+				   PLL_ICLK_SHIFT, 1, 0, &lock);
+	if (IS_ERR(mux_clk))  {
+		pr_err("splliclk_mux not registered\n");
+		goto err_unmap;
+	}
+
+	/* register sys-pll clock */
+	clk = spll_clk_register(clk_name, plliclk_name,
+				regs, stat_reg, bitmask);
+	if (IS_ERR(clk)) {
+		pr_err("spll_clk not registered\n");
+		goto err_mux;
+	}
+
+	pic32_of_clk_register_clkdev(np, clk);
+	goto err_name;
+
+err_mux:
+	clk_unregister(mux_clk);
+err_unmap:
+	iounmap(regs);
+err_name:
+	kfree(parent_names);
+}
+
+static void __init of_frcdiv_setup(struct device_node *np)
+{
+	struct clk *clk;
+	const char *clk_name = np->name;
+	const char *parent_name;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+	if (!parent_name) {
+		pr_err("frcdiv: %s must have a parent\n", np->name);
+		return;
+	}
+
+	/* clk name */
+	of_property_read_string(np, "clock-output-names", &clk_name);
+
+	/* divider clock register */
+	clk = clk_register_divider(NULL, clk_name, parent_name,
+				   0, pic32_clk_regbase,
+				   OSC_FRCDIV_SHIFT, OSC_FRCDIV_MASK,
+				   CLK_DIVIDER_POWER_OF_TWO, &lock);
+
+	if (IS_ERR_OR_NULL(clk)) {
+		pr_err("frcdiv_clk not registered\n");
+		return;
+	}
+
+	pic32_of_clk_register_clkdev(np, clk);
+}
+
+static const struct of_device_id pic32_clk_match[] __initconst = {
+	{
+		.compatible = "microchip,pic32mzda-refoclk",
+		.data = of_refo_clk_setup,
+	},
+	{
+		.compatible = "microchip,pic32mzda-pbclk",
+		.data = of_periph_clk_setup,
+	},
+	{
+		.compatible = "microchip,pic32mzda-syspll",
+		.data = of_sys_pll_setup,
+	},
+	{
+		.compatible = "microchip,pic32mzda-sosc",
+		.data = of_sosc_clk_setup,
+	},
+	{
+		.compatible = "microchip,pic32mzda-frcdivclk",
+		.data = of_frcdiv_setup,
+	},
+	{
+		.compatible = "microchip,pic32mzda-sysclk-v2",
+		.data = of_sys_mux_slew_setup,
+	},
+	{}
+};
+
+static irqreturn_t pic32_fscm_isr_handler(int irq, void *data)
+{
+	u32 v = clk_readl(pic32_clk_regbase);
+
+	if (v & OSC_CLK_FAILED)
+		pr_info("pic32-clk: FSCM detected clk failure.\n");
+
+	return IRQ_HANDLED;
+}
+
+static int pic32_fscm_nmi(struct notifier_block *nb,
+			  unsigned long action, void *data)
+{
+	pic32_fscm_isr_handler(0, NULL);
+	return NOTIFY_OK;
+}
+
+static struct notifier_block failsafe_clk_notifier = {
+	.notifier_call = pic32_fscm_nmi,
+};
+
+static void __init of_pic32_soc_clock_init(struct device_node *np)
+{
+	int ret, nmi = 0, irq;
+	struct resource r;
+	struct device_node *childnp;
+	const struct of_device_id *clk_id;
+	void (*clk_setup)(struct device_node *);
+
+	if (of_address_to_resource(np, 0, &r))
+		panic("Failed to get clk-pll memory region\n");
+
+	if (!request_mem_region(r.start, resource_size(&r), r.name))
+		panic("%s: request_region failed\n", np->name);
+
+	pic32_clk_regbase = ioremap_nocache(r.start, resource_size(&r));
+	if (!pic32_clk_regbase)
+		panic("pic32-clk: failed to map registers\n");
+
+	irq = irq_of_parse_and_map(np, 0);
+	if (!irq) {
+		pr_warn("pic32-clk: irq not provided for FSCM; use nmi.\n");
+		nmi = 1;
+	}
+
+	for_each_child_of_node(np, childnp) {
+		clk_id = of_match_node(pic32_clk_match, childnp);
+		if (!clk_id)
+			continue;
+		clk_setup = clk_id->data;
+		clk_setup(childnp);
+	}
+
+	/* register irq/nmi */
+	if (nmi) {
+		register_nmi_notifier(&failsafe_clk_notifier);
+	} else {
+		ret = request_irq(irq, pic32_fscm_isr_handler, 0, "fscm", NULL);
+		if (ret)
+			pr_err("pic32-clk: fscm_irq request failed\n");
+	}
+}
+
+CLK_OF_DECLARE(pic32_soc_clk, "microchip,pic32mzda-clk",
+	       of_pic32_soc_clock_init);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 05/14] DEVICETREE: Add bindings for PIC32/MZDA platforms
  2015-12-14 22:42 [PATCH v2 00/14] Initial Microchip PIC32MZDA Support Joshua Henderson
                   ` (3 preceding siblings ...)
  2015-12-14 22:42 ` [PATCH v2 04/14] clk: clk-pic32: Add PIC32 clock driver Joshua Henderson
@ 2015-12-14 22:42 ` Joshua Henderson
  2015-12-18 15:47   ` Rob Herring
  2015-12-14 22:42 ` [PATCH v2 06/14] MIPS: Add support for PIC32MZDA platform Joshua Henderson
                   ` (8 subsequent siblings)
  13 siblings, 1 reply; 33+ messages in thread
From: Joshua Henderson @ 2015-12-14 22:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, ralf, Joshua Henderson, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, devicetree

This adds support for the Microchip PIC32 platform along with the
specific variant PIC32MZDA on a PIC32MZDA Starter Kit.

Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
---
 .../bindings/mips/pic32/microchip,pic32mzda.txt    |   33 ++++++++++++++++++++
 1 file changed, 33 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt

diff --git a/Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt b/Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt
new file mode 100644
index 0000000..bcf3e04
--- /dev/null
+++ b/Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt
@@ -0,0 +1,33 @@
+* Microchip PIC32MZDA Platforms
+
+PIC32MZDA Starter Kit
+Required root node properties:
+    - compatible = "microchip,pic32mzda-sk", "microchip,pic32mzda"
+
+CPU nodes:
+----------
+A "cpus" node is required.  Required properties:
+ - #address-cells: Must be 1.
+ - #size-cells: Must be 0.
+A CPU sub-node is also required.  Required properties:
+ - device_type: Must be "cpu".
+ - compatible: Must be "mti,mips14KEc".
+Example:
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu0: cpu@0 {
+			device_type = "cpu";
+			compatible = "mti,mips14KEc";
+		};
+	};
+
+Boot protocol
+--------------
+In accordance with the MIPS UHI specification[1], the bootloader must pass the
+following arguments to the kernel:
+ - $a0: -2.
+ - $a1: KSEG0 address of the flattened device-tree blob.
+
+[1] http://prplfoundation.org/wiki/MIPS_documentation
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 06/14] MIPS: Add support for PIC32MZDA platform
  2015-12-14 22:42 [PATCH v2 00/14] Initial Microchip PIC32MZDA Support Joshua Henderson
                   ` (4 preceding siblings ...)
  2015-12-14 22:42 ` [PATCH v2 05/14] DEVICETREE: Add bindings for PIC32/MZDA platforms Joshua Henderson
@ 2015-12-14 22:42 ` Joshua Henderson
  2015-12-14 22:42 ` [PATCH v2 07/14] DEVICETREE: Add bindings for PIC32 pin control and GPIO Joshua Henderson
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 33+ messages in thread
From: Joshua Henderson @ 2015-12-14 22:42 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-mips, ralf, Joshua Henderson, Cristian Birsan

This adds support for the Microchip PIC32 MIPS microcontroller with the
specific variant PIC32MZDA. PIC32MZDA is based on the MIPS m14KEc core
and boots using device tree.

This includes an early pin setup and early clock setup needed prior to
device tree being initialized. In additon, an interface is provided to
synchronize access to registers shared across several peripherals.

Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
---
 arch/mips/Kbuild.platforms                         |    1 +
 arch/mips/Kconfig                                  |    9 +
 .../include/asm/mach-pic32/cpu-feature-overrides.h |   32 +++
 arch/mips/include/asm/mach-pic32/irq.h             |   22 ++
 arch/mips/include/asm/mach-pic32/pic32.h           |   44 ++++
 arch/mips/include/asm/mach-pic32/spaces.h          |   24 ++
 arch/mips/pic32/Kconfig                            |   34 +++
 arch/mips/pic32/Makefile                           |    6 +
 arch/mips/pic32/Platform                           |    7 +
 arch/mips/pic32/common/Makefile                    |    5 +
 arch/mips/pic32/common/irq.c                       |   21 ++
 arch/mips/pic32/common/reset.c                     |   62 +++++
 arch/mips/pic32/pic32mzda/Makefile                 |    9 +
 arch/mips/pic32/pic32mzda/config.c                 |  126 +++++++++
 arch/mips/pic32/pic32mzda/early_clk.c              |  106 ++++++++
 arch/mips/pic32/pic32mzda/early_console.c          |  171 ++++++++++++
 arch/mips/pic32/pic32mzda/early_pin.c              |  275 ++++++++++++++++++++
 arch/mips/pic32/pic32mzda/early_pin.h              |  241 +++++++++++++++++
 arch/mips/pic32/pic32mzda/init.c                   |  156 +++++++++++
 arch/mips/pic32/pic32mzda/pic32mzda.h              |   29 +++
 arch/mips/pic32/pic32mzda/time.c                   |   44 ++++
 include/linux/platform_data/sdhci-pic32.h          |   22 ++
 22 files changed, 1446 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-pic32/cpu-feature-overrides.h
 create mode 100644 arch/mips/include/asm/mach-pic32/irq.h
 create mode 100644 arch/mips/include/asm/mach-pic32/pic32.h
 create mode 100644 arch/mips/include/asm/mach-pic32/spaces.h
 create mode 100644 arch/mips/pic32/Kconfig
 create mode 100644 arch/mips/pic32/Makefile
 create mode 100644 arch/mips/pic32/Platform
 create mode 100644 arch/mips/pic32/common/Makefile
 create mode 100644 arch/mips/pic32/common/irq.c
 create mode 100644 arch/mips/pic32/common/reset.c
 create mode 100644 arch/mips/pic32/pic32mzda/Makefile
 create mode 100644 arch/mips/pic32/pic32mzda/config.c
 create mode 100644 arch/mips/pic32/pic32mzda/early_clk.c
 create mode 100644 arch/mips/pic32/pic32mzda/early_console.c
 create mode 100644 arch/mips/pic32/pic32mzda/early_pin.c
 create mode 100644 arch/mips/pic32/pic32mzda/early_pin.h
 create mode 100644 arch/mips/pic32/pic32mzda/init.c
 create mode 100644 arch/mips/pic32/pic32mzda/pic32mzda.h
 create mode 100644 arch/mips/pic32/pic32mzda/time.c
 create mode 100644 include/linux/platform_data/sdhci-pic32.h

diff --git a/arch/mips/Kbuild.platforms b/arch/mips/Kbuild.platforms
index a96c81d..c5cd63a 100644
--- a/arch/mips/Kbuild.platforms
+++ b/arch/mips/Kbuild.platforms
@@ -21,6 +21,7 @@ platforms += mti-malta
 platforms += mti-sead3
 platforms += netlogic
 platforms += paravirt
+platforms += pic32
 platforms += pistachio
 platforms += pmcs-msp71xx
 platforms += pnx833x
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 71683a8..a989e76 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -481,6 +481,14 @@ config MIPS_MALTA
 	  This enables support for the MIPS Technologies Malta evaluation
 	  board.
 
+config MACH_PIC32
+	bool "Microchip PIC32 Family"
+	help
+	  This enables support for the Microchip PIC32 family of platforms.
+
+	  Microchip PIC32 is a family of general-purpose 32 bit MIPS core
+	  microcontrollers.
+
 config MIPS_SEAD3
 	bool "MIPS SEAD3 board"
 	select BOOT_ELF32
@@ -980,6 +988,7 @@ source "arch/mips/jazz/Kconfig"
 source "arch/mips/jz4740/Kconfig"
 source "arch/mips/lantiq/Kconfig"
 source "arch/mips/lasat/Kconfig"
+source "arch/mips/pic32/Kconfig"
 source "arch/mips/pistachio/Kconfig"
 source "arch/mips/pmcs-msp71xx/Kconfig"
 source "arch/mips/ralink/Kconfig"
diff --git a/arch/mips/include/asm/mach-pic32/cpu-feature-overrides.h b/arch/mips/include/asm/mach-pic32/cpu-feature-overrides.h
new file mode 100644
index 0000000..4682308
--- /dev/null
+++ b/arch/mips/include/asm/mach-pic32/cpu-feature-overrides.h
@@ -0,0 +1,32 @@
+/*
+ * Joshua Henderson <joshua.henderson@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#ifndef __ASM_MACH_PIC32_CPU_FEATURE_OVERRIDES_H
+#define __ASM_MACH_PIC32_CPU_FEATURE_OVERRIDES_H
+
+/*
+ * CPU feature overrides for PIC32 boards
+ */
+#ifdef CONFIG_CPU_MIPS32
+#define cpu_has_vint		1
+#define cpu_has_veic		0
+#define cpu_has_tlb		1
+#define cpu_has_4kex		1
+#define cpu_has_4k_cache	1
+#define cpu_has_fpu		0
+#define cpu_has_counter		1
+#define cpu_has_llsc		1
+#define cpu_has_nofpuex		0
+#define cpu_icache_snoops_remote_store 1
+#endif
+
+#ifdef CONFIG_CPU_MIPS64
+#error This platform does not support 64bit.
+#endif
+
+#endif /* __ASM_MACH_PIC32_CPU_FEATURE_OVERRIDES_H */
diff --git a/arch/mips/include/asm/mach-pic32/irq.h b/arch/mips/include/asm/mach-pic32/irq.h
new file mode 100644
index 0000000..864330c
--- /dev/null
+++ b/arch/mips/include/asm/mach-pic32/irq.h
@@ -0,0 +1,22 @@
+/*
+ * Joshua Henderson <joshua.henderson@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ * This program is free software; you can distribute it and/or modify it
+ * under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+#ifndef __ASM_MACH_PIC32_IRQ_H
+#define __ASM_MACH_PIC32_IRQ_H
+
+#define NR_IRQS	256
+#define MIPS_CPU_IRQ_BASE 0
+
+#include_next <irq.h>
+
+#endif /* __ASM_MACH_PIC32_IRQ_H */
diff --git a/arch/mips/include/asm/mach-pic32/pic32.h b/arch/mips/include/asm/mach-pic32/pic32.h
new file mode 100644
index 0000000..ce52e91
--- /dev/null
+++ b/arch/mips/include/asm/mach-pic32/pic32.h
@@ -0,0 +1,44 @@
+/*
+ * Joshua Henderson <joshua.henderson@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ * This program is free software; you can distribute it and/or modify it
+ * under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+#ifndef _ASM_MACH_PIC32_H
+#define _ASM_MACH_PIC32_H
+
+#include <linux/io.h>
+
+/*
+ * PIC32 register offsets for SET/CLR/INV where supported.
+ */
+#define PIC32_CLR(_reg)		((_reg) + 0x04)
+#define PIC32_SET(_reg)		((_reg) + 0x08)
+#define PIC32_INV(_reg)		((_reg) + 0x0C)
+
+/*
+ * PIC32 Base Register Offsets
+ */
+#define PIC32_BASE_CONFIG	0x1f800000
+#define PIC32_BASE_OSC		0x1f801200
+#define PIC32_BASE_RESET	0x1f801240
+#define PIC32_BASE_PPS		0x1f801400
+#define PIC32_BASE_UART		0x1f822000
+#define PIC32_BASE_PORT		0x1f860000
+#define PIC32_BASE_DEVCFG2	0x1fc4ff44
+
+/*
+ * Register unlock sequence required for some register access.
+ */
+void pic32_syskey_unlock_debug(const char *fn, const ulong ln);
+#define pic32_syskey_unlock()	\
+	pic32_syskey_unlock_debug(__func__, __LINE__)
+
+#endif /* _ASM_MACH_PIC32_H */
diff --git a/arch/mips/include/asm/mach-pic32/spaces.h b/arch/mips/include/asm/mach-pic32/spaces.h
new file mode 100644
index 0000000..046a0a9
--- /dev/null
+++ b/arch/mips/include/asm/mach-pic32/spaces.h
@@ -0,0 +1,24 @@
+/*
+ * Joshua Henderson <joshua.henderson@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ * This program is free software; you can distribute it and/or modify it
+ * under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+#ifndef _ASM_MACH_PIC32_SPACES_H
+#define _ASM_MACH_PIC32_SPACES_H
+
+#ifdef CONFIG_PIC32MZDA
+#define PHYS_OFFSET	_AC(0x08000000, UL)
+#define UNCAC_BASE	_AC(0xa8000000, UL)
+#endif
+
+#include <asm/mach-generic/spaces.h>
+
+#endif /* __ASM_MACH_PIC32_SPACES_H */
diff --git a/arch/mips/pic32/Kconfig b/arch/mips/pic32/Kconfig
new file mode 100644
index 0000000..0161f09
--- /dev/null
+++ b/arch/mips/pic32/Kconfig
@@ -0,0 +1,34 @@
+if MACH_PIC32
+
+choice
+	prompt "Machine Type"
+
+config PIC32MZDA
+	bool "Microchip PIC32MZDA Platform"
+	select BOOT_ELF32
+	select BOOT_RAW
+	select CEVT_R4K
+	select CSRC_R4K
+	select DMA_NONCOHERENT
+	select SYS_HAS_CPU_MIPS32_R2
+	select SYS_HAS_EARLY_PRINTK
+	select SYS_SUPPORTS_32BIT_KERNEL
+	select SYS_SUPPORTS_LITTLE_ENDIAN
+	select ARCH_REQUIRE_GPIOLIB
+	select HAVE_MACH_CLKDEV
+	select COMMON_CLK
+	select CLKDEV_LOOKUP
+	select LIBFDT
+	select USE_OF
+	select PINCTRL
+	help
+	  Support for the Microchip PIC32MZDA microcontroller.
+
+	  This is a 32-bit microcontroller with support for external or
+	  internally packaged DDR2 memory up to 128MB.
+
+	  For more information, see <http://www.microchip.com/>.
+
+endchoice
+
+endif # MACH_PIC32
diff --git a/arch/mips/pic32/Makefile b/arch/mips/pic32/Makefile
new file mode 100644
index 0000000..fd357f4
--- /dev/null
+++ b/arch/mips/pic32/Makefile
@@ -0,0 +1,6 @@
+#
+# Joshua Henderson, <joshua.henderson@microchip.com>
+# Copyright (C) 2015 Microchip Technology, Inc.  All rights reserved.
+#
+obj-$(CONFIG_MACH_PIC32) += common/
+obj-$(CONFIG_PIC32MZDA) += pic32mzda/
diff --git a/arch/mips/pic32/Platform b/arch/mips/pic32/Platform
new file mode 100644
index 0000000..cd2084f
--- /dev/null
+++ b/arch/mips/pic32/Platform
@@ -0,0 +1,7 @@
+#
+# PIC32MZDA
+#
+platform-$(CONFIG_PIC32MZDA)	+= pic32/
+cflags-$(CONFIG_PIC32MZDA)	+= -I$(srctree)/arch/mips/include/asm/mach-pic32
+load-$(CONFIG_PIC32MZDA)	+= 0xffffffff88000000
+all-$(CONFIG_PIC32MZDA)		:= $(COMPRESSION_FNAME).bin
diff --git a/arch/mips/pic32/common/Makefile b/arch/mips/pic32/common/Makefile
new file mode 100644
index 0000000..be1909c
--- /dev/null
+++ b/arch/mips/pic32/common/Makefile
@@ -0,0 +1,5 @@
+#
+# Joshua Henderson, <joshua.henderson@microchip.com>
+# Copyright (C) 2015 Microchip Technology, Inc.  All rights reserved.
+#
+obj-y = reset.o irq.o
diff --git a/arch/mips/pic32/common/irq.c b/arch/mips/pic32/common/irq.c
new file mode 100644
index 0000000..6df347e
--- /dev/null
+++ b/arch/mips/pic32/common/irq.c
@@ -0,0 +1,21 @@
+/*
+ * Joshua Henderson <joshua.henderson@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ */
+#include <linux/init.h>
+#include <linux/irqchip.h>
+#include <asm/irq.h>
+
+void __init arch_init_irq(void)
+{
+	irqchip_init();
+}
diff --git a/arch/mips/pic32/common/reset.c b/arch/mips/pic32/common/reset.c
new file mode 100644
index 0000000..8334575
--- /dev/null
+++ b/arch/mips/pic32/common/reset.c
@@ -0,0 +1,62 @@
+/*
+ * Joshua Henderson <joshua.henderson@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ */
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <asm/reboot.h>
+#include <asm/mach-pic32/pic32.h>
+
+#define PIC32_RSWRST		0x10
+
+static void pic32_halt(void)
+{
+	while (1) {
+		__asm__(".set push;\n"
+			".set arch=r4000;\n"
+			"wait;\n"
+			".set pop;\n"
+		);
+	}
+}
+
+static void pic32_machine_restart(char *command)
+{
+	void __iomem *reg =
+		ioremap(PIC32_BASE_RESET + PIC32_RSWRST, sizeof(u32));
+
+	pic32_syskey_unlock();
+
+	/* magic write/read */
+	__raw_writel(1, reg);
+	(void)__raw_readl(reg);
+
+	pic32_halt();
+}
+
+static void pic32_machine_halt(void)
+{
+	local_irq_disable();
+
+	pic32_halt();
+}
+
+static int __init mips_reboot_setup(void)
+{
+	_machine_restart = pic32_machine_restart;
+	_machine_halt = pic32_machine_halt;
+	pm_power_off = pic32_machine_halt;
+
+	return 0;
+}
+
+arch_initcall(mips_reboot_setup);
diff --git a/arch/mips/pic32/pic32mzda/Makefile b/arch/mips/pic32/pic32mzda/Makefile
new file mode 100644
index 0000000..4a4c272
--- /dev/null
+++ b/arch/mips/pic32/pic32mzda/Makefile
@@ -0,0 +1,9 @@
+#
+# Joshua Henderson, <joshua.henderson@microchip.com>
+# Copyright (C) 2015 Microchip Technology, Inc.  All rights reserved.
+#
+obj-y			:= init.o time.o config.o
+
+obj-$(CONFIG_EARLY_PRINTK)	+= early_console.o      \
+				   early_pin.o		\
+				   early_clk.o
diff --git a/arch/mips/pic32/pic32mzda/config.c b/arch/mips/pic32/pic32mzda/config.c
new file mode 100644
index 0000000..fe293a0
--- /dev/null
+++ b/arch/mips/pic32/pic32mzda/config.c
@@ -0,0 +1,126 @@
+/*
+ * Purna Chandra Mandal, purna.mandal@microchip.com
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ */
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+
+#include <asm/mach-pic32/pic32.h>
+
+#include "pic32mzda.h"
+
+#define PIC32_CFGCON	0x0000
+#define PIC32_DEVID	0x0020
+#define PIC32_SYSKEY	0x0030
+#define PIC32_CFGEBIA	0x00c0
+#define PIC32_CFGEBIC	0x00d0
+#define PIC32_CFGCON2	0x00f0
+#define PIC32_RCON	0x1240
+
+static void __iomem *pic32_conf_base;
+static DEFINE_SPINLOCK(config_lock);
+static u32 pic32_reset_status;
+
+static u32 pic32_conf_get_reg_field(u32 offset, u32 rshift, u32 mask)
+{
+	u32 v;
+
+	v = readl(pic32_conf_base + offset);
+	v >>= rshift;
+	v &= mask;
+
+	return v;
+}
+
+static u32 pic32_conf_modify_atomic(u32 offset, u32 mask, u32 set)
+{
+	u32 v;
+	unsigned long flags;
+
+	spin_lock_irqsave(&config_lock, flags);
+	v = readl(pic32_conf_base + offset);
+	v &= ~mask;
+	v |= (set & mask);
+	writel(v, pic32_conf_base + offset);
+	spin_unlock_irqrestore(&config_lock, flags);
+
+	return 0;
+}
+
+int pic32_enable_lcd(void)
+{
+	return pic32_conf_modify_atomic(PIC32_CFGCON2, BIT(31), BIT(31));
+}
+
+int pic32_disable_lcd(void)
+{
+	return pic32_conf_modify_atomic(PIC32_CFGCON2, BIT(31), 0);
+}
+
+int pic32_set_lcd_mode(int mode)
+{
+	u32 mask = mode ? BIT(30) : 0;
+
+	return pic32_conf_modify_atomic(PIC32_CFGCON2, BIT(30), mask);
+}
+
+int pic32_set_sdhci_adma_fifo_threshold(u32 rthrsh, u32 wthrsh)
+{
+	u32 clr, set;
+
+	clr = (0x3ff << 4) | (0x3ff << 16);
+	set = (rthrsh << 4) | (wthrsh << 16);
+	return pic32_conf_modify_atomic(PIC32_CFGCON2, clr, set);
+}
+
+void pic32_syskey_unlock_debug(const char *func, const ulong line)
+{
+	void __iomem *syskey = pic32_conf_base + PIC32_SYSKEY;
+
+	pr_debug("%s: called from %s:%lu\n", __func__, func, line);
+	writel(0x00000000, syskey);
+	writel(0xAA996655, syskey);
+	writel(0x556699AA, syskey);
+}
+
+static u32 pic32_get_device_id(void)
+{
+	return pic32_conf_get_reg_field(PIC32_DEVID, 0, 0x0fffffff);
+}
+
+static u32 pic32_get_device_version(void)
+{
+	return pic32_conf_get_reg_field(PIC32_DEVID, 28, 0xf);
+}
+
+u32 pic32_get_boot_status(void)
+{
+	return pic32_reset_status;
+}
+EXPORT_SYMBOL(pic32_get_boot_status);
+
+void __init pic32_config_init(void)
+{
+	pic32_conf_base = ioremap(PIC32_BASE_CONFIG, 0x110);
+	if (!pic32_conf_base)
+		panic("pic32: config base not mapped");
+
+	/* Boot Status */
+	pic32_reset_status = readl(pic32_conf_base + PIC32_RCON);
+	writel(-1, PIC32_CLR(pic32_conf_base + PIC32_RCON));
+
+	/* Device Inforation */
+	pr_info("Device Id: 0x%08x, Device Ver: 0x%04x\n",
+		pic32_get_device_id(),
+		pic32_get_device_version());
+}
diff --git a/arch/mips/pic32/pic32mzda/early_clk.c b/arch/mips/pic32/pic32mzda/early_clk.c
new file mode 100644
index 0000000..96c090e
--- /dev/null
+++ b/arch/mips/pic32/pic32mzda/early_clk.c
@@ -0,0 +1,106 @@
+/*
+ * Joshua Henderson <joshua.henderson@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ */
+#include <asm/mach-pic32/pic32.h>
+
+#include "pic32mzda.h"
+
+/* Oscillators, PLL & clocks */
+#define ICLK_MASK	0x00000080
+#define PLLDIV_MASK	0x00000007
+#define CUROSC_MASK	0x00000007
+#define PLLMUL_MASK	0x0000007F
+#define PB_MASK		0x00000007
+#define FRC1		0
+#define FRC2		7
+#define SPLL		1
+#define POSC		2
+#define FRC_CLK		8000000
+
+#define PIC32_POSC_FREQ	24000000
+
+#define OSCCON		0x0000
+#define SPLLCON		0x0020
+#define PB1DIV		0x0140
+
+u32 pic32_get_sysclk(void)
+{
+	u32 osc_freq = 0;
+	u32 pllclk;
+	u32 frcdivn;
+	u32 osccon;
+	u32 spllcon;
+	int curr_osc;
+
+	u32 plliclk;
+	u32 pllidiv;
+	u32 pllodiv;
+	u32 pllmult;
+	u32 frcdiv;
+
+	void __iomem *osc_base = ioremap(PIC32_BASE_OSC, 0x200);
+
+	osccon = __raw_readl(osc_base + OSCCON);
+	spllcon = __raw_readl(osc_base + SPLLCON);
+
+	plliclk = (spllcon & ICLK_MASK);
+	pllidiv = ((spllcon >> 8) & PLLDIV_MASK) + 1;
+	pllodiv = ((spllcon >> 24) & PLLDIV_MASK);
+	pllmult = ((spllcon >> 16) & PLLMUL_MASK) + 1;
+	frcdiv = ((osccon >> 24) & PLLDIV_MASK);
+
+	pllclk = plliclk ? FRC_CLK : PIC32_POSC_FREQ;
+	frcdivn = ((1 << frcdiv) + 1) + (128 * (frcdiv == 7));
+
+	if (pllodiv < 2)
+		pllodiv = 2;
+	else if (pllodiv < 5)
+		pllodiv = (1 << pllodiv);
+	else
+		pllodiv = 32;
+
+	curr_osc = (int)((osccon >> 12) & CUROSC_MASK);
+
+	switch (curr_osc) {
+	case FRC1:
+	case FRC2:
+		osc_freq = FRC_CLK / frcdivn;
+		break;
+	case SPLL:
+		osc_freq = ((pllclk / pllidiv) * pllmult) / pllodiv;
+		break;
+	case POSC:
+		osc_freq = PIC32_POSC_FREQ;
+		break;
+	default:
+		break;
+	}
+
+	iounmap(osc_base);
+
+	return osc_freq;
+}
+
+u32 pic32_get_pbclk(int bus)
+{
+	u32 clk_freq;
+	void __iomem *osc_base = ioremap(PIC32_BASE_OSC, 0x200);
+	u32 pbxdiv = PB1DIV + ((bus - 1) * 0x10);
+	u32 pbdiv = (__raw_readl(osc_base + pbxdiv) & PB_MASK) + 1;
+
+	iounmap(osc_base);
+
+	clk_freq = pic32_get_sysclk();
+
+	return clk_freq / pbdiv;
+}
diff --git a/arch/mips/pic32/pic32mzda/early_console.c b/arch/mips/pic32/pic32mzda/early_console.c
new file mode 100644
index 0000000..d7b7834
--- /dev/null
+++ b/arch/mips/pic32/pic32mzda/early_console.c
@@ -0,0 +1,171 @@
+/*
+ * Joshua Henderson <joshua.henderson@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ */
+#include <asm/mach-pic32/pic32.h>
+#include <asm/fw/fw.h>
+
+#include "pic32mzda.h"
+#include "early_pin.h"
+
+/* Default early console parameters */
+#define EARLY_CONSOLE_PORT	1
+#define EARLY_CONSOLE_BAUDRATE	115200
+
+#define UART_ENABLE		BIT(15)
+#define UART_ENABLE_RX		BIT(12)
+#define UART_ENABLE_TX		BIT(10)
+#define UART_TX_FULL		BIT(9)
+
+/* UART1(x == 0) - UART6(x == 5) */
+#define UART_BASE(x)	((x) * 0x0200)
+#define U_MODE(x)	UART_BASE(x)
+#define U_STA(x)	(UART_BASE(x) + 0x10)
+#define U_TXR(x)	(UART_BASE(x) + 0x20)
+#define U_BRG(x)	(UART_BASE(x) + 0x40)
+
+static void __iomem *uart_base;
+static char console_port = -1;
+
+static int __init configure_uart_pins(int port)
+{
+	switch (port) {
+	case 1:
+		pic32_pps_input(IN_FUNC_U2RX, IN_RPB0);
+		pic32_pps_output(OUT_FUNC_U2TX, OUT_RPG9);
+		break;
+	case 5:
+		pic32_pps_input(IN_FUNC_U6RX, IN_RPD0);
+		pic32_pps_output(OUT_FUNC_U6TX, OUT_RPB8);
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+static void __init configure_uart(char port, int baud)
+{
+	u32 pbclk;
+
+	pbclk = pic32_get_pbclk(2);
+
+	__raw_writel(0, uart_base + U_MODE(port));
+	__raw_writel(((pbclk / baud) / 16) - 1, uart_base + U_BRG(port));
+	__raw_writel(UART_ENABLE, uart_base + U_MODE(port));
+	__raw_writel(UART_ENABLE_TX | UART_ENABLE_RX,
+		     uart_base + PIC32_SET(U_STA(port)));
+}
+
+static void __init setup_early_console(char port, int baud)
+{
+	if (configure_uart_pins(port))
+		return;
+
+	console_port = port;
+	configure_uart(console_port, baud);
+}
+
+static char * __init pic32_getcmdline(void)
+{
+	/*
+	 * arch_mem_init() has not been called yet, so we don't have a real
+	 * command line setup if using CONFIG_CMDLINE_BOOL.
+	 */
+#ifdef CONFIG_CMDLINE_OVERRIDE
+	return CONFIG_CMDLINE;
+#else
+	return fw_getcmdline();
+#endif
+}
+
+static int __init get_port_from_cmdline(char *arch_cmdline)
+{
+	char *s;
+	int port = -1;
+
+	if (!arch_cmdline || *arch_cmdline == '\0')
+		goto _out;
+
+	s = strstr(arch_cmdline, "earlyprintk=");
+	if (s) {
+		s = strstr(s, "ttyS");
+		if (s)
+			s += 4;
+		else
+			goto _out;
+
+		port = (*s) - '0';
+	}
+
+_out:
+	return port;
+}
+
+static int __init get_baud_from_cmdline(char *arch_cmdline)
+{
+	char *s;
+	int baud = -1;
+
+	if (!arch_cmdline || *arch_cmdline == '\0')
+		goto _out;
+
+	s = strstr(arch_cmdline, "earlyprintk=");
+	if (s) {
+		s = strstr(s, "ttyS");
+		if (s)
+			s += 6;
+		else
+			goto _out;
+
+		baud = 0;
+		while (*s >= '0' && *s <= '9')
+			baud = baud * 10 + *s++ - '0';
+	}
+
+_out:
+	return baud;
+}
+
+void __init fw_init_early_console(char port)
+{
+	char *arch_cmdline = pic32_getcmdline();
+	int baud = -1;
+
+	uart_base = ioremap_nocache(PIC32_BASE_UART, 0xc00);
+
+	baud = get_baud_from_cmdline(arch_cmdline);
+	if (port == -1)
+		port = get_port_from_cmdline(arch_cmdline);
+
+	if (port == -1)
+		port = EARLY_CONSOLE_PORT;
+
+	if (baud == -1)
+		baud = EARLY_CONSOLE_BAUDRATE;
+
+	setup_early_console(port, baud);
+}
+
+int prom_putchar(char c)
+{
+	if (console_port >= 0) {
+		while (__raw_readl(
+				uart_base + U_STA(console_port)) & UART_TX_FULL)
+			;
+
+		__raw_writel(c, uart_base + U_TXR(console_port));
+	}
+
+	return 1;
+}
diff --git a/arch/mips/pic32/pic32mzda/early_pin.c b/arch/mips/pic32/pic32mzda/early_pin.c
new file mode 100644
index 0000000..aa673f8
--- /dev/null
+++ b/arch/mips/pic32/pic32mzda/early_pin.c
@@ -0,0 +1,275 @@
+/*
+ * Joshua Henderson <joshua.henderson@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ */
+#include <asm/io.h>
+
+#include "early_pin.h"
+
+#define PPS_BASE 0x1f800000
+
+/* Input PPS Registers */
+#define INT1R 0x1404
+#define INT2R 0x1408
+#define INT3R 0x140C
+#define INT4R 0x1410
+#define T2CKR 0x1418
+#define T3CKR 0x141C
+#define T4CKR 0x1420
+#define T5CKR 0x1424
+#define T6CKR 0x1428
+#define T7CKR 0x142C
+#define T8CKR 0x1430
+#define T9CKR 0x1434
+#define IC1R 0x1438
+#define IC2R 0x143C
+#define IC3R 0x1440
+#define IC4R 0x1444
+#define IC5R 0x1448
+#define IC6R 0x144C
+#define IC7R 0x1450
+#define IC8R 0x1454
+#define IC9R 0x1458
+#define OCFAR 0x1460
+#define U1RXR 0x1468
+#define U1CTSR 0x146C
+#define U2RXR 0x1470
+#define U2CTSR 0x1474
+#define U3RXR 0x1478
+#define U3CTSR 0x147C
+#define U4RXR 0x1480
+#define U4CTSR 0x1484
+#define U5RXR 0x1488
+#define U5CTSR 0x148C
+#define U6RXR 0x1490
+#define U6CTSR 0x1494
+#define SDI1R 0x149C
+#define SS1R 0x14A0
+#define SDI2R 0x14A8
+#define SS2R 0x14AC
+#define SDI3R 0x14B4
+#define SS3R 0x14B8
+#define SDI4R 0x14C0
+#define SS4R 0x14C4
+#define SDI5R 0x14CC
+#define SS5R 0x14D0
+#define SDI6R 0x14D8
+#define SS6R 0x14DC
+#define C1RXR 0x14E0
+#define C2RXR 0x14E4
+#define REFCLKI1R 0x14E8
+#define REFCLKI3R 0x14F0
+#define REFCLKI4R 0x14F4
+
+static const struct
+{
+	int function;
+	int reg;
+} input_pin_reg[] = {
+	{ IN_FUNC_INT3, INT3R },
+	{ IN_FUNC_T2CK, T2CKR },
+	{ IN_FUNC_T6CK, T6CKR },
+	{ IN_FUNC_IC3, IC3R  },
+	{ IN_FUNC_IC7, IC7R },
+	{ IN_FUNC_U1RX, U1RXR },
+	{ IN_FUNC_U2CTS, U2CTSR },
+	{ IN_FUNC_U5RX, U5RXR },
+	{ IN_FUNC_U6CTS, U6CTSR },
+	{ IN_FUNC_SDI1, SDI1R },
+	{ IN_FUNC_SDI3, SDI3R },
+	{ IN_FUNC_SDI5, SDI5R },
+	{ IN_FUNC_SS6, SS6R },
+	{ IN_FUNC_REFCLKI1, REFCLKI1R },
+	{ IN_FUNC_INT4, INT4R },
+	{ IN_FUNC_T5CK, T5CKR },
+	{ IN_FUNC_T7CK, T7CKR },
+	{ IN_FUNC_IC4, IC4R },
+	{ IN_FUNC_IC8, IC8R },
+	{ IN_FUNC_U3RX, U3RXR },
+	{ IN_FUNC_U4CTS, U4CTSR },
+	{ IN_FUNC_SDI2, SDI2R },
+	{ IN_FUNC_SDI4, SDI4R },
+	{ IN_FUNC_C1RX, C1RXR },
+	{ IN_FUNC_REFCLKI4, REFCLKI4R },
+	{ IN_FUNC_INT2, INT2R },
+	{ IN_FUNC_T3CK, T3CKR },
+	{ IN_FUNC_T8CK, T8CKR },
+	{ IN_FUNC_IC2, IC2R },
+	{ IN_FUNC_IC5, IC5R },
+	{ IN_FUNC_IC9, IC9R },
+	{ IN_FUNC_U1CTS, U1CTSR },
+	{ IN_FUNC_U2RX, U2RXR },
+	{ IN_FUNC_U5CTS, U5CTSR },
+	{ IN_FUNC_SS1, SS1R },
+	{ IN_FUNC_SS3, SS3R },
+	{ IN_FUNC_SS4, SS4R },
+	{ IN_FUNC_SS5, SS5R },
+	{ IN_FUNC_C2RX, C2RXR },
+	{ IN_FUNC_INT1, INT1R },
+	{ IN_FUNC_T4CK, T4CKR },
+	{ IN_FUNC_T9CK, T9CKR },
+	{ IN_FUNC_IC1, IC1R },
+	{ IN_FUNC_IC6, IC6R },
+	{ IN_FUNC_U3CTS, U3CTSR },
+	{ IN_FUNC_U4RX, U4RXR },
+	{ IN_FUNC_U6RX, U6RXR },
+	{ IN_FUNC_SS2, SS2R },
+	{ IN_FUNC_SDI6, SDI6R },
+	{ IN_FUNC_OCFA, OCFAR },
+	{ IN_FUNC_REFCLKI3, REFCLKI3R },
+};
+
+void pic32_pps_input(int function, int pin)
+{
+	void __iomem *pps_base = ioremap_nocache(PPS_BASE, 0xF4);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(input_pin_reg); i++) {
+		if (input_pin_reg[i].function == function) {
+			__raw_writel(pin, pps_base + input_pin_reg[i].reg);
+			return;
+		}
+	}
+
+	iounmap(pps_base);
+}
+
+/* Output PPS Registers */
+#define RPA14R 0x1538
+#define RPA15R 0x153C
+#define RPB0R 0x1540
+#define RPB1R 0x1544
+#define RPB2R 0x1548
+#define RPB3R 0x154C
+#define RPB5R 0x1554
+#define RPB6R 0x1558
+#define RPB7R 0x155C
+#define RPB8R 0x1560
+#define RPB9R 0x1564
+#define RPB10R 0x1568
+#define RPB14R 0x1578
+#define RPB15R 0x157C
+#define RPC1R 0x1584
+#define RPC2R 0x1588
+#define RPC3R 0x158C
+#define RPC4R 0x1590
+#define RPC13R 0x15B4
+#define RPC14R 0x15B8
+#define RPD0R 0x15C0
+#define RPD1R 0x15C4
+#define RPD2R 0x15C8
+#define RPD3R 0x15CC
+#define RPD4R 0x15D0
+#define RPD5R 0x15D4
+#define RPD6R 0x15D8
+#define RPD7R 0x15DC
+#define RPD9R 0x15E4
+#define RPD10R 0x15E8
+#define RPD11R 0x15EC
+#define RPD12R 0x15F0
+#define RPD14R 0x15F8
+#define RPD15R 0x15FC
+#define RPE3R 0x160C
+#define RPE5R 0x1614
+#define RPE8R 0x1620
+#define RPE9R 0x1624
+#define RPF0R 0x1640
+#define RPF1R 0x1644
+#define RPF2R 0x1648
+#define RPF3R 0x164C
+#define RPF4R 0x1650
+#define RPF5R 0x1654
+#define RPF8R 0x1660
+#define RPF12R 0x1670
+#define RPF13R 0x1674
+#define RPG0R 0x1680
+#define RPG1R 0x1684
+#define RPG6R 0x1698
+#define RPG7R 0x169C
+#define RPG8R 0x16A0
+#define RPG9R 0x16A4
+
+static const struct
+{
+	int pin;
+	int reg;
+} output_pin_reg[] = {
+	{ OUT_RPD2, RPD2R },
+	{ OUT_RPG8, RPG8R },
+	{ OUT_RPF4, RPF4R },
+	{ OUT_RPD10, RPD10R },
+	{ OUT_RPF1, RPF1R },
+	{ OUT_RPB9, RPB9R },
+	{ OUT_RPB10, RPB10R },
+	{ OUT_RPC14, RPC14R },
+	{ OUT_RPB5, RPB5R },
+	{ OUT_RPC1, RPC1R },
+	{ OUT_RPD14, RPD14R },
+	{ OUT_RPG1, RPG1R },
+	{ OUT_RPA14, RPA14R },
+	{ OUT_RPD6, RPD6R },
+	{ OUT_RPD3, RPD3R },
+	{ OUT_RPG7, RPG7R },
+	{ OUT_RPF5, RPF5R },
+	{ OUT_RPD11, RPD11R },
+	{ OUT_RPF0, RPF0R },
+	{ OUT_RPB1, RPB1R },
+	{ OUT_RPE5, RPE5R },
+	{ OUT_RPC13, RPC13R },
+	{ OUT_RPB3, RPB3R },
+	{ OUT_RPC4, RPC4R },
+	{ OUT_RPD15, RPD15R },
+	{ OUT_RPG0, RPG0R },
+	{ OUT_RPA15, RPA15R },
+	{ OUT_RPD7, RPD7R },
+	{ OUT_RPD9, RPD9R },
+	{ OUT_RPG6, RPG6R },
+	{ OUT_RPB8, RPB8R },
+	{ OUT_RPB15, RPB15R },
+	{ OUT_RPD4, RPD4R },
+	{ OUT_RPB0, RPB0R },
+	{ OUT_RPE3, RPE3R },
+	{ OUT_RPB7, RPB7R },
+	{ OUT_RPF12, RPF12R },
+	{ OUT_RPD12, RPD12R },
+	{ OUT_RPF8, RPF8R },
+	{ OUT_RPC3, RPC3R },
+	{ OUT_RPE9, RPE9R },
+	{ OUT_RPD1, RPD1R },
+	{ OUT_RPG9, RPG9R },
+	{ OUT_RPB14, RPB14R },
+	{ OUT_RPD0, RPD0R },
+	{ OUT_RPB6, RPB6R },
+	{ OUT_RPD5, RPD5R },
+	{ OUT_RPB2, RPB2R },
+	{ OUT_RPF3, RPF3R },
+	{ OUT_RPF13, RPF13R },
+	{ OUT_RPC2, RPC2R },
+	{ OUT_RPE8, RPE8R },
+	{ OUT_RPF2, RPF2R },
+};
+
+void pic32_pps_output(int function, int pin)
+{
+	void __iomem *pps_base = ioremap_nocache(PPS_BASE, 0x170);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(output_pin_reg); i++) {
+		if (output_pin_reg[i].pin == pin) {
+			__raw_writel(function,
+				pps_base + output_pin_reg[i].reg);
+			return;
+		}
+	}
+
+	iounmap(pps_base);
+}
diff --git a/arch/mips/pic32/pic32mzda/early_pin.h b/arch/mips/pic32/pic32mzda/early_pin.h
new file mode 100644
index 0000000..417fae9
--- /dev/null
+++ b/arch/mips/pic32/pic32mzda/early_pin.h
@@ -0,0 +1,241 @@
+/*
+ * Joshua Henderson <joshua.henderson@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ */
+#ifndef _PIC32MZDA_EARLY_PIN_H
+#define _PIC32MZDA_EARLY_PIN_H
+
+/*
+ * This is a complete, yet overly simplistic and unoptimized, PIC32MZDA PPS
+ * configuration only useful before we have full pinctrl initialized.
+ */
+
+/* Input PPS Functions */
+enum {
+	IN_FUNC_INT3,
+	IN_FUNC_T2CK,
+	IN_FUNC_T6CK,
+	IN_FUNC_IC3,
+	IN_FUNC_IC7,
+	IN_FUNC_U1RX,
+	IN_FUNC_U2CTS,
+	IN_FUNC_U5RX,
+	IN_FUNC_U6CTS,
+	IN_FUNC_SDI1,
+	IN_FUNC_SDI3,
+	IN_FUNC_SDI5,
+	IN_FUNC_SS6,
+	IN_FUNC_REFCLKI1,
+	IN_FUNC_INT4,
+	IN_FUNC_T5CK,
+	IN_FUNC_T7CK,
+	IN_FUNC_IC4,
+	IN_FUNC_IC8,
+	IN_FUNC_U3RX,
+	IN_FUNC_U4CTS,
+	IN_FUNC_SDI2,
+	IN_FUNC_SDI4,
+	IN_FUNC_C1RX,
+	IN_FUNC_REFCLKI4,
+	IN_FUNC_INT2,
+	IN_FUNC_T3CK,
+	IN_FUNC_T8CK,
+	IN_FUNC_IC2,
+	IN_FUNC_IC5,
+	IN_FUNC_IC9,
+	IN_FUNC_U1CTS,
+	IN_FUNC_U2RX,
+	IN_FUNC_U5CTS,
+	IN_FUNC_SS1,
+	IN_FUNC_SS3,
+	IN_FUNC_SS4,
+	IN_FUNC_SS5,
+	IN_FUNC_C2RX,
+	IN_FUNC_INT1,
+	IN_FUNC_T4CK,
+	IN_FUNC_T9CK,
+	IN_FUNC_IC1,
+	IN_FUNC_IC6,
+	IN_FUNC_U3CTS,
+	IN_FUNC_U4RX,
+	IN_FUNC_U6RX,
+	IN_FUNC_SS2,
+	IN_FUNC_SDI6,
+	IN_FUNC_OCFA,
+	IN_FUNC_REFCLKI3,
+};
+
+/* Input PPS Pins */
+#define IN_RPD2 0x00
+#define IN_RPG8 0x01
+#define IN_RPF4 0x02
+#define IN_RPD10 0x03
+#define IN_RPF1 0x04
+#define IN_RPB9 0x05
+#define IN_RPB10 0x06
+#define IN_RPC14 0x07
+#define IN_RPB5 0x08
+#define IN_RPC1 0x0A
+#define IN_RPD14 0x0B
+#define IN_RPG1 0x0C
+#define IN_RPA14 0x0D
+#define IN_RPD6 0x0E
+#define IN_RPD3 0x00
+#define IN_RPG7 0x01
+#define IN_RPF5 0x02
+#define IN_RPD11 0x03
+#define IN_RPF0 0x04
+#define IN_RPB1 0x05
+#define IN_RPE5 0x06
+#define IN_RPC13 0x07
+#define IN_RPB3 0x08
+#define IN_RPC4 0x0A
+#define IN_RPD15 0x0B
+#define IN_RPG0 0x0C
+#define IN_RPA15 0x0D
+#define IN_RPD7 0x0E
+#define IN_RPD9 0x00
+#define IN_RPG6 0x01
+#define IN_RPB8 0x02
+#define IN_RPB15 0x03
+#define IN_RPD4 0x04
+#define IN_RPB0 0x05
+#define IN_RPE3 0x06
+#define IN_RPB7 0x07
+#define IN_RPF12 0x09
+#define IN_RPD12 0x0A
+#define IN_RPF8 0x0B
+#define IN_RPC3 0x0C
+#define IN_RPE9 0x0D
+#define IN_RPD1 0x00
+#define IN_RPG9 0x01
+#define IN_RPB14 0x02
+#define IN_RPD0 0x03
+#define IN_RPB6 0x05
+#define IN_RPD5 0x06
+#define IN_RPB2 0x07
+#define IN_RPF3 0x08
+#define IN_RPF13 0x09
+#define IN_RPF2 0x0B
+#define IN_RPC2 0x0C
+#define IN_RPE8 0x0D
+
+/* Output PPS Pins */
+enum {
+	OUT_RPD2,
+	OUT_RPG8,
+	OUT_RPF4,
+	OUT_RPD10,
+	OUT_RPF1,
+	OUT_RPB9,
+	OUT_RPB10,
+	OUT_RPC14,
+	OUT_RPB5,
+	OUT_RPC1,
+	OUT_RPD14,
+	OUT_RPG1,
+	OUT_RPA14,
+	OUT_RPD6,
+	OUT_RPD3,
+	OUT_RPG7,
+	OUT_RPF5,
+	OUT_RPD11,
+	OUT_RPF0,
+	OUT_RPB1,
+	OUT_RPE5,
+	OUT_RPC13,
+	OUT_RPB3,
+	OUT_RPC4,
+	OUT_RPD15,
+	OUT_RPG0,
+	OUT_RPA15,
+	OUT_RPD7,
+	OUT_RPD9,
+	OUT_RPG6,
+	OUT_RPB8,
+	OUT_RPB15,
+	OUT_RPD4,
+	OUT_RPB0,
+	OUT_RPE3,
+	OUT_RPB7,
+	OUT_RPF12,
+	OUT_RPD12,
+	OUT_RPF8,
+	OUT_RPC3,
+	OUT_RPE9,
+	OUT_RPD1,
+	OUT_RPG9,
+	OUT_RPB14,
+	OUT_RPD0,
+	OUT_RPB6,
+	OUT_RPD5,
+	OUT_RPB2,
+	OUT_RPF3,
+	OUT_RPF13,
+	OUT_RPC2,
+	OUT_RPE8,
+	OUT_RPF2,
+};
+
+/* Output PPS Functions */
+#define OUT_FUNC_U3TX 0x01
+#define OUT_FUNC_U4RTS 0x02
+#define OUT_FUNC_SDO1 0x05
+#define OUT_FUNC_SDO2 0x06
+#define OUT_FUNC_SDO3 0x07
+#define OUT_FUNC_SDO5 0x09
+#define OUT_FUNC_SS6 0x0A
+#define OUT_FUNC_OC3 0x0B
+#define OUT_FUNC_OC6 0x0C
+#define OUT_FUNC_REFCLKO4 0x0D
+#define OUT_FUNC_C2OUT 0x0E
+#define OUT_FUNC_C1TX 0x0F
+#define OUT_FUNC_U1TX 0x01
+#define OUT_FUNC_U2RTS 0x02
+#define OUT_FUNC_U5TX 0x03
+#define OUT_FUNC_U6RTS 0x04
+#define OUT_FUNC_SDO1 0x05
+#define OUT_FUNC_SDO2 0x06
+#define OUT_FUNC_SDO3 0x07
+#define OUT_FUNC_SDO4 0x08
+#define OUT_FUNC_SDO5 0x09
+#define OUT_FUNC_OC4 0x0B
+#define OUT_FUNC_OC7 0x0C
+#define OUT_FUNC_REFCLKO1 0x0F
+#define OUT_FUNC_U3RTS 0x01
+#define OUT_FUNC_U4TX 0x02
+#define OUT_FUNC_U6TX 0x04
+#define OUT_FUNC_SS1 0x05
+#define OUT_FUNC_SS3 0x07
+#define OUT_FUNC_SS4 0x08
+#define OUT_FUNC_SS5 0x09
+#define OUT_FUNC_SDO6 0x0A
+#define OUT_FUNC_OC5 0x0B
+#define OUT_FUNC_OC8 0x0C
+#define OUT_FUNC_C1OUT 0x0E
+#define OUT_FUNC_REFCLKO3 0x0F
+#define OUT_FUNC_U1RTS 0x01
+#define OUT_FUNC_U2TX 0x02
+#define OUT_FUNC_U5RTS 0x03
+#define OUT_FUNC_U6TX 0x04
+#define OUT_FUNC_SS2 0x06
+#define OUT_FUNC_SDO4 0x08
+#define OUT_FUNC_SDO6 0x0A
+#define OUT_FUNC_OC2 0x0B
+#define OUT_FUNC_OC1 0x0C
+#define OUT_FUNC_OC9 0x0D
+#define OUT_FUNC_C2TX 0x0F
+
+void pic32_pps_input(int function, int pin);
+void pic32_pps_output(int function, int pin);
+
+#endif
diff --git a/arch/mips/pic32/pic32mzda/init.c b/arch/mips/pic32/pic32mzda/init.c
new file mode 100644
index 0000000..775ff90
--- /dev/null
+++ b/arch/mips/pic32/pic32mzda/init.c
@@ -0,0 +1,156 @@
+/*
+ * Joshua Henderson, joshua.henderson@microchip.com
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/of_address.h>
+#include <linux/of_fdt.h>
+#include <linux/of_platform.h>
+#include <linux/platform_data/sdhci-pic32.h>
+
+#include <asm/fw/fw.h>
+#include <asm/mips-boards/generic.h>
+#include <asm/prom.h>
+
+#include "pic32mzda.h"
+
+const char *get_system_type(void)
+{
+	return "PIC32MZDA";
+}
+
+static ulong get_fdtaddr(void)
+{
+	ulong ftaddr = 0;
+
+	if ((fw_arg0 == -2) && fw_arg1 && !fw_arg2 && !fw_arg3)
+		return (ulong)fw_arg1;
+
+	if (__dtb_start < __dtb_end)
+		ftaddr = (ulong)__dtb_start;
+
+	return ftaddr;
+}
+
+void __init plat_mem_setup(void)
+{
+	void *dtb;
+
+	dtb = (void *)get_fdtaddr();
+	if (!dtb) {
+		pr_err("pic32: no DTB found.\n");
+		return;
+	}
+
+	/*
+	 * Load the builtin device tree. This causes the chosen node to be
+	 * parsed resulting in our memory appearing.
+	 */
+	__dt_setup_arch(dtb);
+
+	pr_info("Found following command lines\n");
+	pr_info(" boot_command_line: %s\n", boot_command_line);
+	pr_info(" arcs_cmdline     : %s\n", arcs_cmdline);
+#ifdef CONFIG_CMDLINE_BOOL
+	pr_info(" builtin_cmdline  : %s\n", CONFIG_CMDLINE);
+#endif
+	if (dtb != __dtb_start)
+		strlcpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE);
+
+#ifdef CONFIG_EARLY_PRINTK
+	fw_init_early_console(-1);
+#endif
+	pic32_config_init();
+}
+
+static __init void pic32_init_cmdline(int argc, char *argv[])
+{
+	unsigned int count = COMMAND_LINE_SIZE - 1;
+	int i;
+	char *dst = &(arcs_cmdline[0]);
+	char *src;
+
+	for (i = 1; i < argc && count; ++i) {
+		src = argv[i];
+		while (*src && count) {
+			*dst++ = *src++;
+			--count;
+		}
+		*dst++ = ' ';
+	}
+	if (i > 1)
+		--dst;
+
+	*dst = 0;
+}
+
+void __init prom_init(void)
+{
+	pic32_init_cmdline((int)fw_arg0, (char **)fw_arg1);
+}
+
+void __init prom_free_prom_memory(void)
+{
+}
+
+void __init device_tree_init(void)
+{
+	if (!initial_boot_params)
+		return;
+
+	unflatten_and_copy_device_tree();
+}
+
+static struct pic32_sdhci_platform_data sdhci_data = {
+	.setup_dma = pic32_set_sdhci_adma_fifo_threshold,
+};
+
+static struct of_dev_auxdata pic32_auxdata_lookup[] __initdata = {
+	OF_DEV_AUXDATA("microchip,pic32mzda-sdhci", 0, "sdhci", &sdhci_data),
+	{ /* sentinel */}
+};
+
+static int __init pic32_of_prepare_platform_data(struct of_dev_auxdata *lookup)
+{
+	struct device_node *root, *np;
+	struct resource res;
+
+	root = of_find_node_by_path("/");
+
+	for (; lookup->compatible; lookup++) {
+		np = of_find_compatible_node(NULL, NULL, lookup->compatible);
+		if (np) {
+			lookup->name = (char *)np->name;
+			if (lookup->phys_addr)
+				continue;
+			if (!of_address_to_resource(np, 0, &res))
+				lookup->phys_addr = res.start;
+		}
+	}
+
+	return 0;
+}
+
+static int __init plat_of_setup(void)
+{
+	if (!of_have_populated_dt())
+		panic("Device tree not present");
+
+	pic32_of_prepare_platform_data(pic32_auxdata_lookup);
+	if (of_platform_populate(NULL, of_default_bus_match_table,
+				 pic32_auxdata_lookup, NULL))
+		panic("Failed to populate DT");
+
+	return 0;
+}
+arch_initcall(plat_of_setup);
diff --git a/arch/mips/pic32/pic32mzda/pic32mzda.h b/arch/mips/pic32/pic32mzda/pic32mzda.h
new file mode 100644
index 0000000..96d10e2
--- /dev/null
+++ b/arch/mips/pic32/pic32mzda/pic32mzda.h
@@ -0,0 +1,29 @@
+/*
+ * Joshua Henderson <joshua.henderson@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ */
+#ifndef PIC32MZDA_COMMON_H
+#define PIC32MZDA_COMMON_H
+
+/* early clock */
+u32 pic32_get_pbclk(int bus);
+u32 pic32_get_sysclk(void);
+
+/* Device configuration */
+void __init pic32_config_init(void);
+int pic32_set_lcd_mode(int mode);
+int pic32_set_sdhci_adma_fifo_threshold(u32 rthrs, u32 wthrs);
+u32 pic32_get_boot_status(void);
+int pic32_disable_lcd(void);
+int pic32_enable_lcd(void);
+
+#endif
diff --git a/arch/mips/pic32/pic32mzda/time.c b/arch/mips/pic32/pic32mzda/time.c
new file mode 100644
index 0000000..4c0b80f
--- /dev/null
+++ b/arch/mips/pic32/pic32mzda/time.c
@@ -0,0 +1,44 @@
+/*
+ * Joshua Henderson <joshua.henderson@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clocksource.h>
+#include <linux/init.h>
+#include <linux/irqchip/pic32-evic.h>
+#include <linux/of.h>
+
+#include <asm/time.h>
+
+#include "pic32mzda.h"
+
+unsigned int get_c0_compare_int(void)
+{
+	return pic32_get_c0_compare_int();
+}
+
+void __init plat_time_init(void)
+{
+	struct clk *clk;
+
+	of_clk_init(NULL);
+	clk = clk_get_sys("cpu_clk", NULL);
+	if (IS_ERR(clk))
+		panic("unable to get CPU clock, err=%ld", PTR_ERR(clk));
+
+	clk_prepare_enable(clk);
+	pr_info("CPU Clock: %ldMHz\n", clk_get_rate(clk) / 1000000);
+	mips_hpt_frequency = clk_get_rate(clk) / 2;
+
+	clocksource_probe();
+}
diff --git a/include/linux/platform_data/sdhci-pic32.h b/include/linux/platform_data/sdhci-pic32.h
new file mode 100644
index 0000000..7e0efe6
--- /dev/null
+++ b/include/linux/platform_data/sdhci-pic32.h
@@ -0,0 +1,22 @@
+/*
+ * Purna Chandra Mandal, purna.mandal@microchip.com
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ */
+#ifndef __PIC32_SDHCI_PDATA_H__
+#define __PIC32_SDHCI_PDATA_H__
+
+struct pic32_sdhci_platform_data {
+	/* read & write fifo threshold */
+	int (*setup_dma)(u32 rfifo, u32 wfifo);
+};
+
+#endif
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 07/14] DEVICETREE: Add bindings for PIC32 pin control and GPIO
  2015-12-14 22:42 [PATCH v2 00/14] Initial Microchip PIC32MZDA Support Joshua Henderson
                   ` (5 preceding siblings ...)
  2015-12-14 22:42 ` [PATCH v2 06/14] MIPS: Add support for PIC32MZDA platform Joshua Henderson
@ 2015-12-14 22:42 ` Joshua Henderson
  2015-12-15 19:58   ` Rob Herring
  2015-12-14 22:42 ` [PATCH v2 08/14] pinctrl: pinctrl-pic32: Add PIC32 pin control driver Joshua Henderson
                   ` (6 subsequent siblings)
  13 siblings, 1 reply; 33+ messages in thread
From: Joshua Henderson @ 2015-12-14 22:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, ralf, Joshua Henderson, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, devicetree

Document the devicetree bindings for PINCTRL and GPIO found on Microchip
PIC32 class devices.

Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
---
 .../bindings/gpio/microchip,pic32-gpio.txt         |   49 ++++++++++++++++
 .../bindings/pinctrl/microchip,pic32-pinctrl.txt   |   60 ++++++++++++++++++++
 2 files changed, 109 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt
 create mode 100644 Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt

diff --git a/Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt b/Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt
new file mode 100644
index 0000000..ef37528
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt
@@ -0,0 +1,49 @@
+* Microchip PIC32 GPIO devices (PIO).
+
+Required properties:
+ - compatible: "microchip,pic32mzda-gpio"
+ - reg: Base address and length for the device.
+ - interrupts: The port interrupt shared by all pins.
+ - gpio-controller: Marks the port as GPIO controller.
+ - #gpio-cells: Two. The first cell is the pin number and
+   the second cell is used to specify the gpio polarity as defined in
+   defined in <dt-bindings/gpio/gpio.h>:
+      0 = GPIO_ACTIVE_HIGH
+      1 = GPIO_ACTIVE_LOW
+      2 = GPIO_OPEN_DRAIN
+ - interrupt-controller: Marks the device node as an interrupt controller.
+ - #interrupt-cells: Two. The first cell is the GPIO number and second cell
+   is used to specify the trigger type as defined in
+   <dt-bindings/interrupt-controller/irq.h>:
+      IRQ_TYPE_EDGE_RISING
+      IRQ_TYPE_EDGE_FALLING
+      IRQ_TYPE_EDGE_BOTH
+ - clocks: Clock specifier (see clock bindings for details).
+ - microchip,gpio-bank: Specifies which bank a controller owns.
+ - gpio-ranges: Interaction with the PINCTRL subsystem.
+
+Example:
+
+/* PORTA */
+gpio0: gpio0@1f860000 {
+	compatible = "microchip,pic32mzda-gpio";
+	reg = <0x1f860000 0x100>;
+	interrupts = <118 IRQ_TYPE_LEVEL_HIGH>;
+	#gpio-cells = <2>;
+	gpio-controller;
+	interrupt-controller;
+	#interrupt-cells = <2>;
+	clocks = <&PBCLK4>;
+	microchip,gpio-bank = <0>;
+	gpio-ranges = <&pic32_pinctrl 0 0 16>;
+};
+
+keys {
+	...
+
+	button@sw1 {
+		label = "ESC";
+		linux,code = <1>;
+		gpios = <&gpio0 12 0>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt
new file mode 100644
index 0000000..4b5efa5
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt
@@ -0,0 +1,60 @@
+* Microchip PIC32 Pin Controller
+
+Please refer to pinctrl-bindings.txt, ../gpio/gpio.txt, and
+../interrupt-controller/interrupts.txt for generic information regarding
+pin controller, GPIO, and interrupt bindings.
+
+PIC32 'pin configuration node' is a node of a group of pins which can be
+used for a specific device or function. This node represents configuraions of
+pins, optional function, and optional mux related configuration.
+
+Required properties for pin controller node:
+ - compatible: "microchip,pic32mada-pinctrl"
+ - reg: Address range of the pinctrl registers.
+ - clocks: Clock specifier (see clock bindings for details)
+
+Required properties for pin configuration sub-nodes:
+ - pins: List of pins to which the configuration applies.
+
+Optional properties for pin configuration sub-nodes:
+----------------------------------------------------
+ - function: Mux function for the specified pins.
+ - bias-pull-up: Enable weak pull-up.
+ - bias-pull-down: Enable weak pull-down.
+ - input-enable: Set the pin as an input.
+ - output-low: Set the pin as an output level low.
+ - output-high: Set the pin as an output level high.
+ - microchip,digital: Enable digital I/O.
+ - microchip,analog: Enable analog I/O.
+
+Example:
+
+pic32_pinctrl: pinctrl@1f801400{
+	#address-cells = <1>;
+	#size-cells = <1>;
+	compatible = "microchip,pic32mzda-pinctrl";
+	reg = <0x1f801400 0x400>;
+	clocks = <&PBCLK1>;
+
+	pinctrl_uart2: pinctrl_uart2 {
+		uart2-tx {
+			pins = "G9";
+			function = "U2TX";
+			microchip,digital;
+			output-low;
+		};
+		uart2-rx {
+			pins = "B0";
+			function = "U2RX";
+			microchip,digital;
+			input-enable;
+		};
+	};
+};
+
+uart2: serial@1f822200 {
+	compatible = "microchip,pic32mzda-uart";
+	reg = <0x1f822200 0x50>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_uart2>;
+};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 08/14] pinctrl: pinctrl-pic32: Add PIC32 pin control driver
  2015-12-14 22:42 [PATCH v2 00/14] Initial Microchip PIC32MZDA Support Joshua Henderson
                   ` (6 preceding siblings ...)
  2015-12-14 22:42 ` [PATCH v2 07/14] DEVICETREE: Add bindings for PIC32 pin control and GPIO Joshua Henderson
@ 2015-12-14 22:42 ` Joshua Henderson
  2015-12-14 22:42 ` [PATCH v2 09/14] DEVICETREE: Add bindings for PIC32 UART driver Joshua Henderson
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 33+ messages in thread
From: Joshua Henderson @ 2015-12-14 22:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, ralf, Joshua Henderson, Linus Walleij, linux-gpio

Add a driver for the pin controller present on the Microchip PIC32
including the specific variant PIC32MZDA. This driver provides pinmux
and pinconfig operations as well as GPIO and IRQ chips for the GPIO
banks.

Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
---
 drivers/pinctrl/Kconfig         |   17 +
 drivers/pinctrl/Makefile        |    1 +
 drivers/pinctrl/pinctrl-pic32.c | 2339 +++++++++++++++++++++++++++++++++++++++
 drivers/pinctrl/pinctrl-pic32.h |  141 +++
 4 files changed, 2498 insertions(+)
 create mode 100644 drivers/pinctrl/pinctrl-pic32.c
 create mode 100644 drivers/pinctrl/pinctrl-pic32.h

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 312c78b..374386a 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -246,6 +246,23 @@ config PINCTRL_ZYNQ
 	help
 	  This selectes the pinctrl driver for Xilinx Zynq.
 
+config PINCTRL_PIC32
+	bool "Microchip PIC32 pin controller driver"
+	depends on OF
+	depends on MACH_PIC32
+	select PINMUX
+	select GENERIC_PINCONF
+	select GPIOLIB_IRQCHIP
+	select OF_GPIO
+	help
+	  This is the pin controller and gpio driver for Microchip PIC32
+	  microcontrollers. This option is selected automatically when specific
+	  machine and arch are selected to build.
+
+config PINCTRL_PIC32MZDA
+	def_bool y if PIC32MZDA
+	select PINCTRL_PIC32
+
 source "drivers/pinctrl/bcm/Kconfig"
 source "drivers/pinctrl/berlin/Kconfig"
 source "drivers/pinctrl/freescale/Kconfig"
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 738cb49..a512196 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_PINCTRL_DIGICOLOR)	+= pinctrl-digicolor.o
 obj-$(CONFIG_PINCTRL_FALCON)	+= pinctrl-falcon.o
 obj-$(CONFIG_PINCTRL_MESON)	+= meson/
 obj-$(CONFIG_PINCTRL_PALMAS)	+= pinctrl-palmas.o
+obj-$(CONFIG_PINCTRL_PIC32)	+= pinctrl-pic32.o
 obj-$(CONFIG_PINCTRL_PISTACHIO)	+= pinctrl-pistachio.o
 obj-$(CONFIG_PINCTRL_ROCKCHIP)	+= pinctrl-rockchip.o
 obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
diff --git a/drivers/pinctrl/pinctrl-pic32.c b/drivers/pinctrl/pinctrl-pic32.c
new file mode 100644
index 0000000..dc0c5aa
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-pic32.c
@@ -0,0 +1,2339 @@
+/*
+ * PIC32 pinctrl driver
+ *
+ * Joshua Henderson, <joshua.henderson@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ * This program is free software; you can distribute it and/or modify it
+ * under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm/mach-pic32/pic32.h>
+
+#include "pinctrl-utils.h"
+#include "pinctrl-pic32.h"
+
+#define PINS_PER_BANK		16
+
+#define PIC32_CNCON_EDGE	11
+#define PIC32_CNCON_ON		15
+
+#define PIN_CONFIG_MICROCHIP_DIGITAL	(PIN_CONFIG_END + 1)
+#define PIN_CONFIG_MICROCHIP_ANALOG	(PIN_CONFIG_END + 2)
+
+static const struct pinconf_generic_params pic32_mpp_bindings[] = {
+	{"microchip,digital",	PIN_CONFIG_MICROCHIP_DIGITAL,	0},
+	{"microchip,analog",	PIN_CONFIG_MICROCHIP_ANALOG,	0},
+};
+
+#define GPIO_BANK_START(bank)		((bank) * PINS_PER_BANK)
+
+struct pic32_function {
+	const char *name;
+	const char * const *groups;
+	unsigned int ngroups;
+};
+
+struct pic32_pin_group {
+	const char *name;
+	unsigned int pin;
+	struct pic32_desc_function *functions;
+};
+
+struct pic32_desc_function {
+	const char *name;
+	u32 muxreg;
+	u32 muxval;
+};
+
+struct pic32_gpio_bank {
+	void __iomem *reg_base;
+	struct gpio_chip gpio_chip;
+	struct irq_chip irq_chip;
+	struct clk *clk;
+};
+
+struct pic32_pinctrl {
+	void __iomem *reg_base;
+	struct device *dev;
+	struct pinctrl_dev *pctldev;
+	const struct pinctrl_pin_desc *pins;
+	unsigned int npins;
+	const struct pic32_function *functions;
+	unsigned int nfunctions;
+	const struct pic32_pin_group *groups;
+	unsigned int ngroups;
+	struct pic32_gpio_bank *gpio_banks;
+	unsigned int nbanks;
+	struct clk *clk;
+};
+
+static const struct pinctrl_pin_desc pic32_pins[] = {
+	PINCTRL_PIN(0, "A0"),
+	PINCTRL_PIN(1, "A1"),
+	PINCTRL_PIN(2, "A2"),
+	PINCTRL_PIN(3, "A3"),
+	PINCTRL_PIN(4, "A4"),
+	PINCTRL_PIN(5, "A5"),
+	PINCTRL_PIN(6, "A6"),
+	PINCTRL_PIN(7, "A7"),
+	PINCTRL_PIN(8, "A8"),
+	PINCTRL_PIN(9, "A9"),
+	PINCTRL_PIN(10, "A10"),
+	PINCTRL_PIN(11, "A11"),
+	PINCTRL_PIN(12, "A12"),
+	PINCTRL_PIN(13, "A13"),
+	PINCTRL_PIN(14, "A14"),
+	PINCTRL_PIN(15, "A15"),
+	PINCTRL_PIN(16, "B0"),
+	PINCTRL_PIN(17, "B1"),
+	PINCTRL_PIN(18, "B2"),
+	PINCTRL_PIN(19, "B3"),
+	PINCTRL_PIN(20, "B4"),
+	PINCTRL_PIN(21, "B5"),
+	PINCTRL_PIN(22, "B6"),
+	PINCTRL_PIN(23, "B7"),
+	PINCTRL_PIN(24, "B8"),
+	PINCTRL_PIN(25, "B9"),
+	PINCTRL_PIN(26, "B10"),
+	PINCTRL_PIN(27, "B11"),
+	PINCTRL_PIN(28, "B12"),
+	PINCTRL_PIN(29, "B13"),
+	PINCTRL_PIN(30, "B14"),
+	PINCTRL_PIN(31, "B15"),
+	PINCTRL_PIN(33, "C1"),
+	PINCTRL_PIN(34, "C2"),
+	PINCTRL_PIN(35, "C3"),
+	PINCTRL_PIN(36, "C4"),
+	PINCTRL_PIN(44, "C12"),
+	PINCTRL_PIN(45, "C13"),
+	PINCTRL_PIN(46, "C14"),
+	PINCTRL_PIN(47, "C15"),
+	PINCTRL_PIN(48, "D0"),
+	PINCTRL_PIN(49, "D1"),
+	PINCTRL_PIN(50, "D2"),
+	PINCTRL_PIN(51, "D3"),
+	PINCTRL_PIN(52, "D4"),
+	PINCTRL_PIN(53, "D5"),
+	PINCTRL_PIN(54, "D6"),
+	PINCTRL_PIN(55, "D7"),
+	PINCTRL_PIN(57, "D9"),
+	PINCTRL_PIN(58, "D10"),
+	PINCTRL_PIN(59, "D11"),
+	PINCTRL_PIN(60, "D12"),
+	PINCTRL_PIN(61, "D13"),
+	PINCTRL_PIN(62, "D14"),
+	PINCTRL_PIN(63, "D15"),
+	PINCTRL_PIN(64, "E0"),
+	PINCTRL_PIN(65, "E1"),
+	PINCTRL_PIN(66, "E2"),
+	PINCTRL_PIN(67, "E3"),
+	PINCTRL_PIN(68, "E4"),
+	PINCTRL_PIN(69, "E5"),
+	PINCTRL_PIN(70, "E6"),
+	PINCTRL_PIN(71, "E7"),
+	PINCTRL_PIN(72, "E8"),
+	PINCTRL_PIN(73, "E9"),
+	PINCTRL_PIN(80, "F0"),
+	PINCTRL_PIN(81, "F1"),
+	PINCTRL_PIN(82, "F2"),
+	PINCTRL_PIN(83, "F3"),
+	PINCTRL_PIN(84, "F4"),
+	PINCTRL_PIN(85, "F5"),
+	PINCTRL_PIN(88, "F8"),
+	PINCTRL_PIN(92, "F12"),
+	PINCTRL_PIN(93, "F13"),
+	PINCTRL_PIN(96, "G0"),
+	PINCTRL_PIN(97, "G1"),
+	PINCTRL_PIN(102, "G6"),
+	PINCTRL_PIN(103, "G7"),
+	PINCTRL_PIN(104, "G8"),
+	PINCTRL_PIN(105, "G9"),
+	PINCTRL_PIN(108, "G12"),
+	PINCTRL_PIN(109, "G13"),
+	PINCTRL_PIN(110, "G14"),
+	PINCTRL_PIN(111, "G15"),
+	PINCTRL_PIN(112, "H0"),
+	PINCTRL_PIN(113, "H1"),
+	PINCTRL_PIN(114, "H2"),
+	PINCTRL_PIN(115, "H3"),
+	PINCTRL_PIN(116, "H4"),
+	PINCTRL_PIN(117, "H5"),
+	PINCTRL_PIN(118, "H6"),
+	PINCTRL_PIN(119, "H7"),
+	PINCTRL_PIN(120, "H8"),
+	PINCTRL_PIN(121, "H9"),
+	PINCTRL_PIN(122, "H10"),
+	PINCTRL_PIN(123, "H11"),
+	PINCTRL_PIN(124, "H12"),
+	PINCTRL_PIN(125, "H13"),
+	PINCTRL_PIN(126, "H14"),
+	PINCTRL_PIN(127, "H15"),
+	PINCTRL_PIN(128, "J0"),
+	PINCTRL_PIN(129, "J1"),
+	PINCTRL_PIN(130, "J2"),
+	PINCTRL_PIN(131, "J3"),
+	PINCTRL_PIN(132, "J4"),
+	PINCTRL_PIN(133, "J5"),
+	PINCTRL_PIN(134, "J6"),
+	PINCTRL_PIN(135, "J7"),
+	PINCTRL_PIN(136, "J8"),
+	PINCTRL_PIN(137, "J9"),
+	PINCTRL_PIN(138, "J10"),
+	PINCTRL_PIN(139, "J11"),
+	PINCTRL_PIN(140, "J12"),
+	PINCTRL_PIN(141, "J13"),
+	PINCTRL_PIN(142, "J14"),
+	PINCTRL_PIN(143, "J15"),
+	PINCTRL_PIN(144, "K0"),
+	PINCTRL_PIN(145, "K1"),
+	PINCTRL_PIN(146, "K2"),
+	PINCTRL_PIN(147, "K3"),
+	PINCTRL_PIN(148, "K4"),
+	PINCTRL_PIN(149, "K5"),
+	PINCTRL_PIN(150, "K6"),
+	PINCTRL_PIN(151, "K7"),
+};
+
+static const char * const pic32_input0_group[] = {
+	"D2", "G8", "F4", "F1", "B9", "B10", "C14", "B5",
+	"C1", "D14", "G1", "A14", "D6",
+};
+
+static const char * const pic32_input1_group[] = {
+	"D3", "G7", "F5", "D11", "F0", "B1", "E5", "C13",
+	"B3", "C4", "G0", "A15", "D7",
+};
+
+static const char * const pic32_input2_group[] = {
+	"D9", "G6", "B8", "B15", "D4", "B0", "E3", "B7",
+	"F12", "D12", "F8", "C3", "E9",
+};
+
+static const char * const pic32_input3_group[] = {
+	"G9", "B14", "D0", "B6", "D5", "B2", "F3", "F13",
+	"F2", "C2", "E8",
+};
+
+static const char * const pic32_output0_group[] = {
+	"D2", "G8", "F4", "D10", "F1", "B9", "B10", "C14",
+	"B5", "C1", "D14", "G1", "A14", "D6",
+};
+
+static const char * const pic32_output0_1_group[] = {
+	"D2", "G8", "F4", "D10", "F1", "B9", "B10", "C14",
+	"B5", "C1", "D14", "G1", "A14", "D6",
+	"D3", "G7", "F5", "D11", "F0", "B1", "E5", "C13",
+	"B3", "C4", "D15", "G0", "A15", "D7",
+};
+
+static const char *const pic32_output1_group[] = {
+	"D3", "G7", "F5", "D11", "F0", "B1", "E5", "C13",
+	"B3", "C4", "D15", "G0", "A15", "D7",
+};
+
+static const char *const pic32_output1_3_group[] = {
+	"D3", "G7", "F5", "D11", "F0", "B1", "E5", "C13",
+	"B3", "C4", "D15", "G0", "A15", "D7",
+	"G9", "B14", "D0", "B6", "D5", "B2", "F3", "F13",
+	"C2", "E8", "F2",
+};
+
+static const char * const pic32_output2_group[] = {
+	"D9", "G6", "B8", "B15", "D4", "B0", "E3", "B7",
+	"F12", "D12", "F8", "C3", "E9",
+};
+
+static const char * const pic32_output2_3_group[] = {
+	"D9", "G6", "B8", "B15", "D4", "B0", "E3", "B7",
+	"F12", "D12", "F8", "C3", "E9",
+	"G9", "B14", "D0", "B6", "D5", "B2", "F3", "F13",
+	"C2", "E8", "F2",
+};
+
+static const char * const pic32_output3_group[] = {
+	"G9", "B14", "D0", "B6", "D5", "B2", "F3", "F13",
+	"C2", "E8", "F2",
+};
+
+#define FUNCTION(_name, _gr)					\
+	{							\
+		.name = #_name,					\
+		.groups = pic32_##_gr##_group,			\
+		.ngroups = ARRAY_SIZE(pic32_##_gr##_group),	\
+	}
+
+static const struct pic32_function pic32_functions[] = {
+	FUNCTION(INT3, input0),
+	FUNCTION(T2CK, input0),
+	FUNCTION(T6CK, input0),
+	FUNCTION(IC3, input0),
+	FUNCTION(IC7, input0),
+	FUNCTION(U1RX, input0),
+	FUNCTION(U2CTS, input0),
+	FUNCTION(U5RX, input0),
+	FUNCTION(U6CTS, input0),
+	FUNCTION(SDI1, input0),
+	FUNCTION(SDI3, input0),
+	FUNCTION(SDI5, input0),
+	FUNCTION(SS6IN, input0),
+	FUNCTION(REFCLKI1, input0),
+	FUNCTION(INT4, input1),
+	FUNCTION(T5CK, input1),
+	FUNCTION(T7CK, input1),
+	FUNCTION(IC4, input1),
+	FUNCTION(IC8, input1),
+	FUNCTION(U3RX, input1),
+	FUNCTION(U4CTS, input1),
+	FUNCTION(SDI2, input1),
+	FUNCTION(SDI4, input1),
+	FUNCTION(C1RX, input1),
+	FUNCTION(REFCLKI4, input1),
+	FUNCTION(INT2, input2),
+	FUNCTION(T3CK, input2),
+	FUNCTION(T8CK, input2),
+	FUNCTION(IC2, input2),
+	FUNCTION(IC5, input2),
+	FUNCTION(IC9, input2),
+	FUNCTION(U1CTS, input2),
+	FUNCTION(U2RX, input2),
+	FUNCTION(U5CTS, input2),
+	FUNCTION(SS1IN, input2),
+	FUNCTION(SS3IN, input2),
+	FUNCTION(SS4IN, input2),
+	FUNCTION(SS5IN, input2),
+	FUNCTION(C2RX, input2),
+	FUNCTION(INT1, input3),
+	FUNCTION(T4CK, input3),
+	FUNCTION(T9CK, input3),
+	FUNCTION(IC1, input3),
+	FUNCTION(IC6, input3),
+	FUNCTION(U3CTS, input3),
+	FUNCTION(U4RX, input3),
+	FUNCTION(U6RX, input3),
+	FUNCTION(SS2IN, input3),
+	FUNCTION(SDI6, input3),
+	FUNCTION(OCFA, input3),
+	FUNCTION(REFCLKI3, input3),
+	FUNCTION(U3TX, output0),
+	FUNCTION(U4RTS, output0),
+	FUNCTION(SDO1, output0_1),
+	FUNCTION(SDO2, output0_1),
+	FUNCTION(SDO3, output0_1),
+	FUNCTION(SDO5, output0_1),
+	FUNCTION(SS6OUT, output0),
+	FUNCTION(OC3, output0),
+	FUNCTION(OC6, output0),
+	FUNCTION(REFCLKO4, output0),
+	FUNCTION(C2OUT, output0),
+	FUNCTION(C1TX, output0),
+	FUNCTION(U1TX, output1),
+	FUNCTION(U2RTS, output1),
+	FUNCTION(U5TX, output1),
+	FUNCTION(U6RTS, output1),
+	FUNCTION(SDO4, output1_3),
+	FUNCTION(OC4, output1),
+	FUNCTION(OC7, output1),
+	FUNCTION(REFCLKO1, output1),
+	FUNCTION(U3RTS, output2),
+	FUNCTION(U4TX, output2),
+	FUNCTION(U6TX, output2_3),
+	FUNCTION(SS1OUT, output2),
+	FUNCTION(SS3OUT, output2),
+	FUNCTION(SS4OUT, output2),
+	FUNCTION(SS5OUT, output2),
+	FUNCTION(SDO6, output2_3),
+	FUNCTION(OC5, output2),
+	FUNCTION(OC8, output2),
+	FUNCTION(C1OUT, output2),
+	FUNCTION(REFCLKO3, output2),
+	FUNCTION(U1RTS, output3),
+	FUNCTION(U2TX, output3),
+	FUNCTION(U5RTS, output3),
+	FUNCTION(SS2OUT, output3),
+	FUNCTION(OC2, output3),
+	FUNCTION(OC1, output3),
+	FUNCTION(OC9, output3),
+	FUNCTION(C2TX, output3),
+};
+
+#define PIC32_PINCTRL_GROUP(_pin, _name, ...)				\
+	{								\
+		.name = #_name,						\
+		.pin = _pin,						\
+		.functions = (struct pic32_desc_function[]){		\
+			__VA_ARGS__, { } },				\
+	}
+
+#define PIC32_PINCTRL_FUNCTION(_name, _muxreg, _muxval)	\
+	{						\
+		.name = #_name,				\
+		.muxreg = _muxreg,			\
+		.muxval = _muxval,			\
+	}
+
+static const struct pic32_pin_group pic32_groups[] = {
+	PIC32_PINCTRL_GROUP(14, A14,
+			PIC32_PINCTRL_FUNCTION(INT3, INT3R, 13),
+			PIC32_PINCTRL_FUNCTION(T2CK, T2CKR, 13),
+			PIC32_PINCTRL_FUNCTION(T6CK, T6CKR, 13),
+			PIC32_PINCTRL_FUNCTION(IC3, IC3R, 13),
+			PIC32_PINCTRL_FUNCTION(IC7, IC7R, 13),
+			PIC32_PINCTRL_FUNCTION(U1RX, U1RXR, 13),
+			PIC32_PINCTRL_FUNCTION(U2CTS, U2CTSR, 13),
+			PIC32_PINCTRL_FUNCTION(U5RX, U5RXR, 13),
+			PIC32_PINCTRL_FUNCTION(U6CTS, U6CTSR, 13),
+			PIC32_PINCTRL_FUNCTION(SDI1, SDI1R, 13),
+			PIC32_PINCTRL_FUNCTION(SDI3, SDI3R, 13),
+			PIC32_PINCTRL_FUNCTION(SDI5, SDI5R, 13),
+			PIC32_PINCTRL_FUNCTION(SS6IN, SS6INR, 13),
+			PIC32_PINCTRL_FUNCTION(REFCLKI1, REFCLKI1R, 13),
+			PIC32_PINCTRL_FUNCTION(U3TX, RPA14R, 1),
+			PIC32_PINCTRL_FUNCTION(U4RTS, RPA14R, 2),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPA14R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPA14R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPA14R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPA14R, 9),
+			PIC32_PINCTRL_FUNCTION(SS6OUT, RPA14R, 10),
+			PIC32_PINCTRL_FUNCTION(OC3, RPA14R, 11),
+			PIC32_PINCTRL_FUNCTION(OC6, RPA14R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO4, RPA14R, 13),
+			PIC32_PINCTRL_FUNCTION(C2OUT, RPA14R, 14),
+			PIC32_PINCTRL_FUNCTION(C1TX, RPA14R, 15)),
+	PIC32_PINCTRL_GROUP(15, A15,
+			PIC32_PINCTRL_FUNCTION(INT4, INT4R, 13),
+			PIC32_PINCTRL_FUNCTION(T5CK, T5CKR, 13),
+			PIC32_PINCTRL_FUNCTION(T7CK, T7CKR, 13),
+			PIC32_PINCTRL_FUNCTION(IC4, IC4R, 13),
+			PIC32_PINCTRL_FUNCTION(IC8, IC8R, 13),
+			PIC32_PINCTRL_FUNCTION(U3RX, U3RXR, 13),
+			PIC32_PINCTRL_FUNCTION(U4CTS, U4CTSR, 13),
+			PIC32_PINCTRL_FUNCTION(SDI2, SDI2R, 13),
+			PIC32_PINCTRL_FUNCTION(SDI4, SDI4R, 13),
+			PIC32_PINCTRL_FUNCTION(C1RX, C1RXR, 13),
+			PIC32_PINCTRL_FUNCTION(REFCLKI4, REFCLKI4R, 13),
+			PIC32_PINCTRL_FUNCTION(U1TX, RPA15R, 1),
+			PIC32_PINCTRL_FUNCTION(U2RTS, RPA15R, 2),
+			PIC32_PINCTRL_FUNCTION(U5TX, RPA15R, 3),
+			PIC32_PINCTRL_FUNCTION(U6RTS, RPA15R, 4),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPA15R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPA15R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPA15R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPA15R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPA15R, 9),
+			PIC32_PINCTRL_FUNCTION(OC4, RPA15R, 11),
+			PIC32_PINCTRL_FUNCTION(OC7, RPA15R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO1, RPA15R, 15)),
+	PIC32_PINCTRL_GROUP(16, B0,
+			PIC32_PINCTRL_FUNCTION(INT2, INT2R, 5),
+			PIC32_PINCTRL_FUNCTION(T3CK, T3CKR, 5),
+			PIC32_PINCTRL_FUNCTION(T8CK, T8CKR, 5),
+			PIC32_PINCTRL_FUNCTION(IC2, IC2R, 5),
+			PIC32_PINCTRL_FUNCTION(IC5, IC5R, 5),
+			PIC32_PINCTRL_FUNCTION(IC9, IC9R, 5),
+			PIC32_PINCTRL_FUNCTION(U1CTS, U1CTSR, 5),
+			PIC32_PINCTRL_FUNCTION(U2RX, U2RXR, 5),
+			PIC32_PINCTRL_FUNCTION(U5CTS, U5CTSR, 5),
+			PIC32_PINCTRL_FUNCTION(SS1IN, SS1INR, 5),
+			PIC32_PINCTRL_FUNCTION(SS3IN, SS3INR, 5),
+			PIC32_PINCTRL_FUNCTION(SS4IN, SS4INR, 5),
+			PIC32_PINCTRL_FUNCTION(SS5IN, SS5INR, 5),
+			PIC32_PINCTRL_FUNCTION(C2RX, C2RXR, 5),
+			PIC32_PINCTRL_FUNCTION(U3RTS, RPB0R, 1),
+			PIC32_PINCTRL_FUNCTION(U4TX, RPB0R, 2),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPB0R, 4),
+			PIC32_PINCTRL_FUNCTION(SS1OUT, RPB0R, 5),
+			PIC32_PINCTRL_FUNCTION(SS3OUT, RPB0R, 7),
+			PIC32_PINCTRL_FUNCTION(SS4OUT, RPB0R, 8),
+			PIC32_PINCTRL_FUNCTION(SS5OUT, RPB0R, 9),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPB0R, 10),
+			PIC32_PINCTRL_FUNCTION(OC5, RPB0R, 11),
+			PIC32_PINCTRL_FUNCTION(OC8, RPB0R, 12),
+			PIC32_PINCTRL_FUNCTION(C1OUT, RPB0R, 14),
+			PIC32_PINCTRL_FUNCTION(REFCLKO3, RPB0R, 15)),
+	PIC32_PINCTRL_GROUP(17, B1,
+			PIC32_PINCTRL_FUNCTION(INT4, INT4R, 5),
+			PIC32_PINCTRL_FUNCTION(T5CK, T5CKR, 5),
+			PIC32_PINCTRL_FUNCTION(T7CK, T7CKR, 5),
+			PIC32_PINCTRL_FUNCTION(IC4, IC4R, 5),
+			PIC32_PINCTRL_FUNCTION(IC8, IC8R, 5),
+			PIC32_PINCTRL_FUNCTION(U3RX, U3RXR, 5),
+			PIC32_PINCTRL_FUNCTION(U4CTS, U4CTSR, 5),
+			PIC32_PINCTRL_FUNCTION(SDI2, SDI2R, 5),
+			PIC32_PINCTRL_FUNCTION(SDI4, SDI4R, 5),
+			PIC32_PINCTRL_FUNCTION(C1RX, C1RXR, 5),
+			PIC32_PINCTRL_FUNCTION(REFCLKI4, REFCLKI4R, 5),
+			PIC32_PINCTRL_FUNCTION(U1TX, RPB1R, 1),
+			PIC32_PINCTRL_FUNCTION(U2RTS, RPB1R, 2),
+			PIC32_PINCTRL_FUNCTION(U5TX, RPB1R, 3),
+			PIC32_PINCTRL_FUNCTION(U6RTS, RPB1R, 4),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPB1R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPB1R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPB1R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPB1R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPB1R, 9),
+			PIC32_PINCTRL_FUNCTION(OC4, RPB1R, 11),
+			PIC32_PINCTRL_FUNCTION(OC7, RPB1R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO1, RPB1R, 15)),
+	PIC32_PINCTRL_GROUP(18, B2,
+			PIC32_PINCTRL_FUNCTION(INT1, INT1R, 7),
+			PIC32_PINCTRL_FUNCTION(T4CK, T4CKR, 7),
+			PIC32_PINCTRL_FUNCTION(T9CK, T9CKR, 7),
+			PIC32_PINCTRL_FUNCTION(IC1, IC1R, 7),
+			PIC32_PINCTRL_FUNCTION(IC6, IC6R, 7),
+			PIC32_PINCTRL_FUNCTION(U3CTS, U3CTSR, 7),
+			PIC32_PINCTRL_FUNCTION(U4RX, U4RXR, 7),
+			PIC32_PINCTRL_FUNCTION(U6RX, U6RXR, 7),
+			PIC32_PINCTRL_FUNCTION(SS2IN, SS2INR, 7),
+			PIC32_PINCTRL_FUNCTION(SDI6, SDI6R, 7),
+			PIC32_PINCTRL_FUNCTION(OCFA, OCFAR, 7),
+			PIC32_PINCTRL_FUNCTION(REFCLKI3, REFCLKI3R, 7),
+			PIC32_PINCTRL_FUNCTION(U1RTS, RPB2R, 1),
+			PIC32_PINCTRL_FUNCTION(U2TX, RPB2R, 2),
+			PIC32_PINCTRL_FUNCTION(U5RTS, RPB2R, 3),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPB2R, 4),
+			PIC32_PINCTRL_FUNCTION(SS2OUT, RPB2R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPB2R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPB2R, 10),
+			PIC32_PINCTRL_FUNCTION(OC2, RPB2R, 11),
+			PIC32_PINCTRL_FUNCTION(OC1, RPB2R, 12),
+			PIC32_PINCTRL_FUNCTION(OC9, RPB2R, 13),
+			PIC32_PINCTRL_FUNCTION(C2TX, RPB2R, 15)),
+	PIC32_PINCTRL_GROUP(19, B3,
+			PIC32_PINCTRL_FUNCTION(INT4, INT4R, 8),
+			PIC32_PINCTRL_FUNCTION(T5CK, T5CKR, 8),
+			PIC32_PINCTRL_FUNCTION(T7CK, T7CKR, 8),
+			PIC32_PINCTRL_FUNCTION(IC4, IC4R, 8),
+			PIC32_PINCTRL_FUNCTION(IC8, IC8R, 8),
+			PIC32_PINCTRL_FUNCTION(U3RX, U3RXR, 8),
+			PIC32_PINCTRL_FUNCTION(U4CTS, U4CTSR, 8),
+			PIC32_PINCTRL_FUNCTION(SDI2, SDI2R, 8),
+			PIC32_PINCTRL_FUNCTION(SDI4, SDI4R, 8),
+			PIC32_PINCTRL_FUNCTION(C1RX, C1RXR, 8),
+			PIC32_PINCTRL_FUNCTION(REFCLKI4, REFCLKI4R, 8),
+			PIC32_PINCTRL_FUNCTION(U1TX, RPB3R, 1),
+			PIC32_PINCTRL_FUNCTION(U2RTS, RPB3R, 2),
+			PIC32_PINCTRL_FUNCTION(U5TX, RPB3R, 3),
+			PIC32_PINCTRL_FUNCTION(U6RTS, RPB3R, 4),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPB3R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPB3R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPB3R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPB3R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPB3R, 9),
+			PIC32_PINCTRL_FUNCTION(OC4, RPB3R, 11),
+			PIC32_PINCTRL_FUNCTION(OC7, RPB3R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO1, RPB3R, 15)),
+	PIC32_PINCTRL_GROUP(21, B5,
+			PIC32_PINCTRL_FUNCTION(INT3, INT3R, 8),
+			PIC32_PINCTRL_FUNCTION(T2CK, T2CKR, 8),
+			PIC32_PINCTRL_FUNCTION(T6CK, T6CKR, 8),
+			PIC32_PINCTRL_FUNCTION(IC3, IC3R, 8),
+			PIC32_PINCTRL_FUNCTION(IC7, IC7R, 8),
+			PIC32_PINCTRL_FUNCTION(U1RX, U1RXR, 8),
+			PIC32_PINCTRL_FUNCTION(U2CTS, U2CTSR, 8),
+			PIC32_PINCTRL_FUNCTION(U5RX, U5RXR, 8),
+			PIC32_PINCTRL_FUNCTION(U6CTS, U6CTSR, 8),
+			PIC32_PINCTRL_FUNCTION(SDI1, SDI1R, 8),
+			PIC32_PINCTRL_FUNCTION(SDI3, SDI3R, 8),
+			PIC32_PINCTRL_FUNCTION(SDI5, SDI5R, 8),
+			PIC32_PINCTRL_FUNCTION(SS6IN, SS6INR, 8),
+			PIC32_PINCTRL_FUNCTION(REFCLKI1, REFCLKI1R, 8),
+			PIC32_PINCTRL_FUNCTION(U3TX, RPB5R, 1),
+			PIC32_PINCTRL_FUNCTION(U4RTS, RPB5R, 2),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPB5R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPB5R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPB5R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPB5R, 9),
+			PIC32_PINCTRL_FUNCTION(SS6OUT, RPB5R, 10),
+			PIC32_PINCTRL_FUNCTION(OC3, RPB5R, 11),
+			PIC32_PINCTRL_FUNCTION(OC6, RPB5R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO4, RPB5R, 13),
+			PIC32_PINCTRL_FUNCTION(C2OUT, RPB5R, 14),
+			PIC32_PINCTRL_FUNCTION(C1TX, RPB5R, 15)),
+	PIC32_PINCTRL_GROUP(22, B6,
+			PIC32_PINCTRL_FUNCTION(INT1, INT1R, 4),
+			PIC32_PINCTRL_FUNCTION(T4CK, T4CKR, 4),
+			PIC32_PINCTRL_FUNCTION(T9CK, T9CKR, 4),
+			PIC32_PINCTRL_FUNCTION(IC1, IC1R, 4),
+			PIC32_PINCTRL_FUNCTION(IC6, IC6R, 4),
+			PIC32_PINCTRL_FUNCTION(U3CTS, U3CTSR, 4),
+			PIC32_PINCTRL_FUNCTION(U4RX, U4RXR, 4),
+			PIC32_PINCTRL_FUNCTION(U6RX, U6RXR, 4),
+			PIC32_PINCTRL_FUNCTION(SS2IN, SS2INR, 4),
+			PIC32_PINCTRL_FUNCTION(SDI6, SDI6R, 4),
+			PIC32_PINCTRL_FUNCTION(OCFA, OCFAR, 4),
+			PIC32_PINCTRL_FUNCTION(REFCLKI3, REFCLKI3R, 4),
+			PIC32_PINCTRL_FUNCTION(U1RTS, RPB6R, 1),
+			PIC32_PINCTRL_FUNCTION(U2TX, RPB6R, 2),
+			PIC32_PINCTRL_FUNCTION(U5RTS, RPB6R, 3),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPB6R, 4),
+			PIC32_PINCTRL_FUNCTION(SS2OUT, RPB6R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPB6R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPB6R, 10),
+			PIC32_PINCTRL_FUNCTION(OC2, RPB6R, 11),
+			PIC32_PINCTRL_FUNCTION(OC1, RPB6R, 12),
+			PIC32_PINCTRL_FUNCTION(OC9, RPB6R, 13),
+			PIC32_PINCTRL_FUNCTION(C2TX, RPB6R, 15)),
+	PIC32_PINCTRL_GROUP(23, B7,
+			PIC32_PINCTRL_FUNCTION(INT2, INT2R, 7),
+			PIC32_PINCTRL_FUNCTION(T3CK, T3CKR, 7),
+			PIC32_PINCTRL_FUNCTION(T8CK, T8CKR, 7),
+			PIC32_PINCTRL_FUNCTION(IC2, IC2R, 7),
+			PIC32_PINCTRL_FUNCTION(IC5, IC5R, 7),
+			PIC32_PINCTRL_FUNCTION(IC9, IC9R, 7),
+			PIC32_PINCTRL_FUNCTION(U1CTS, U1CTSR, 7),
+			PIC32_PINCTRL_FUNCTION(U2RX, U2RXR, 7),
+			PIC32_PINCTRL_FUNCTION(U5CTS, U5CTSR, 7),
+			PIC32_PINCTRL_FUNCTION(SS1IN, SS1INR, 7),
+			PIC32_PINCTRL_FUNCTION(SS3IN, SS3INR, 7),
+			PIC32_PINCTRL_FUNCTION(SS4IN, SS4INR, 7),
+			PIC32_PINCTRL_FUNCTION(SS5IN, SS5INR, 7),
+			PIC32_PINCTRL_FUNCTION(C2RX, C2RXR, 7),
+			PIC32_PINCTRL_FUNCTION(U3RTS, RPB7R, 1),
+			PIC32_PINCTRL_FUNCTION(U4TX, RPB7R, 2),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPB7R, 4),
+			PIC32_PINCTRL_FUNCTION(SS1OUT, RPB7R, 5),
+			PIC32_PINCTRL_FUNCTION(SS3OUT, RPB7R, 7),
+			PIC32_PINCTRL_FUNCTION(SS4OUT, RPB7R, 8),
+			PIC32_PINCTRL_FUNCTION(SS5OUT, RPB7R, 9),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPB7R, 10),
+			PIC32_PINCTRL_FUNCTION(OC5, RPB7R, 11),
+			PIC32_PINCTRL_FUNCTION(OC8, RPB7R, 12),
+			PIC32_PINCTRL_FUNCTION(C1OUT, RPB7R, 14),
+			PIC32_PINCTRL_FUNCTION(REFCLKO3, RPB7R, 15)),
+	PIC32_PINCTRL_GROUP(24, B8,
+			PIC32_PINCTRL_FUNCTION(INT2, INT2R, 2),
+			PIC32_PINCTRL_FUNCTION(T3CK, T3CKR, 2),
+			PIC32_PINCTRL_FUNCTION(T8CK, T8CKR, 2),
+			PIC32_PINCTRL_FUNCTION(IC2, IC2R, 2),
+			PIC32_PINCTRL_FUNCTION(IC5, IC5R, 2),
+			PIC32_PINCTRL_FUNCTION(IC9, IC9R, 2),
+			PIC32_PINCTRL_FUNCTION(U1CTS, U1CTSR, 2),
+			PIC32_PINCTRL_FUNCTION(U2RX, U2RXR, 2),
+			PIC32_PINCTRL_FUNCTION(U5CTS, U5CTSR, 2),
+			PIC32_PINCTRL_FUNCTION(SS1IN, SS1INR, 2),
+			PIC32_PINCTRL_FUNCTION(SS3IN, SS3INR, 2),
+			PIC32_PINCTRL_FUNCTION(SS4IN, SS4INR, 2),
+			PIC32_PINCTRL_FUNCTION(SS5IN, SS5INR, 2),
+			PIC32_PINCTRL_FUNCTION(C2RX, C2RXR, 2),
+			PIC32_PINCTRL_FUNCTION(U3RTS, RPB8R, 1),
+			PIC32_PINCTRL_FUNCTION(U4TX, RPB8R, 2),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPB8R, 4),
+			PIC32_PINCTRL_FUNCTION(SS1OUT, RPB8R, 5),
+			PIC32_PINCTRL_FUNCTION(SS3OUT, RPB8R, 7),
+			PIC32_PINCTRL_FUNCTION(SS4OUT, RPB8R, 8),
+			PIC32_PINCTRL_FUNCTION(SS5OUT, RPB8R, 9),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPB8R, 10),
+			PIC32_PINCTRL_FUNCTION(OC5, RPB8R, 11),
+			PIC32_PINCTRL_FUNCTION(OC8, RPB8R, 12),
+			PIC32_PINCTRL_FUNCTION(C1OUT, RPB8R, 14),
+			PIC32_PINCTRL_FUNCTION(REFCLKO3, RPB8R, 15)),
+	PIC32_PINCTRL_GROUP(25, B9,
+			PIC32_PINCTRL_FUNCTION(INT3, INT3R, 5),
+			PIC32_PINCTRL_FUNCTION(T2CK, T2CKR, 5),
+			PIC32_PINCTRL_FUNCTION(T6CK, T6CKR, 5),
+			PIC32_PINCTRL_FUNCTION(IC3, IC3R, 5),
+			PIC32_PINCTRL_FUNCTION(IC7, IC7R, 5),
+			PIC32_PINCTRL_FUNCTION(U1RX, U1RXR, 5),
+			PIC32_PINCTRL_FUNCTION(U2CTS, U2CTSR, 5),
+			PIC32_PINCTRL_FUNCTION(U5RX, U5RXR, 5),
+			PIC32_PINCTRL_FUNCTION(U6CTS, U6CTSR, 5),
+			PIC32_PINCTRL_FUNCTION(SDI1, SDI1R, 5),
+			PIC32_PINCTRL_FUNCTION(SDI3, SDI3R, 5),
+			PIC32_PINCTRL_FUNCTION(SDI5, SDI5R, 5),
+			PIC32_PINCTRL_FUNCTION(SS6IN, SS6INR, 5),
+			PIC32_PINCTRL_FUNCTION(REFCLKI1, REFCLKI1R, 5),
+			PIC32_PINCTRL_FUNCTION(U3TX, RPB9R, 1),
+			PIC32_PINCTRL_FUNCTION(U4RTS, RPB9R, 2),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPB9R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPB9R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPB9R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPB9R, 9),
+			PIC32_PINCTRL_FUNCTION(SS6OUT, RPB9R, 10),
+			PIC32_PINCTRL_FUNCTION(OC3, RPB9R, 11),
+			PIC32_PINCTRL_FUNCTION(OC6, RPB9R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO4, RPB9R, 13),
+			PIC32_PINCTRL_FUNCTION(C2OUT, RPB9R, 14),
+			PIC32_PINCTRL_FUNCTION(C1TX, RPB9R, 15)),
+	PIC32_PINCTRL_GROUP(26, B10,
+			PIC32_PINCTRL_FUNCTION(INT3, INT3R, 6),
+			PIC32_PINCTRL_FUNCTION(T2CK, T2CKR, 6),
+			PIC32_PINCTRL_FUNCTION(T6CK, T6CKR, 6),
+			PIC32_PINCTRL_FUNCTION(IC3, IC3R, 6),
+			PIC32_PINCTRL_FUNCTION(IC7, IC7R, 6),
+			PIC32_PINCTRL_FUNCTION(U1RX, U1RXR, 6),
+			PIC32_PINCTRL_FUNCTION(U2CTS, U2CTSR, 6),
+			PIC32_PINCTRL_FUNCTION(U5RX, U5RXR, 6),
+			PIC32_PINCTRL_FUNCTION(U6CTS, U6CTSR, 6),
+			PIC32_PINCTRL_FUNCTION(SDI1, SDI1R, 6),
+			PIC32_PINCTRL_FUNCTION(SDI3, SDI3R, 6),
+			PIC32_PINCTRL_FUNCTION(SDI5, SDI5R, 6),
+			PIC32_PINCTRL_FUNCTION(SS6IN, SS6INR, 6),
+			PIC32_PINCTRL_FUNCTION(REFCLKI1, REFCLKI1R, 6),
+			PIC32_PINCTRL_FUNCTION(U3TX, RPB10R, 1),
+			PIC32_PINCTRL_FUNCTION(U4RTS, RPB10R, 2),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPB10R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPB10R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPB10R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPB10R, 9),
+			PIC32_PINCTRL_FUNCTION(SS6OUT, RPB10R, 10),
+			PIC32_PINCTRL_FUNCTION(OC3, RPB10R, 11),
+			PIC32_PINCTRL_FUNCTION(OC6, RPB10R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO4, RPB10R, 13),
+			PIC32_PINCTRL_FUNCTION(C2OUT, RPB10R, 14),
+			PIC32_PINCTRL_FUNCTION(C1TX, RPB10R, 15)),
+	PIC32_PINCTRL_GROUP(30, B14,
+			PIC32_PINCTRL_FUNCTION(INT1, INT1R, 2),
+			PIC32_PINCTRL_FUNCTION(T4CK, T4CKR, 2),
+			PIC32_PINCTRL_FUNCTION(T9CK, T9CKR, 2),
+			PIC32_PINCTRL_FUNCTION(IC1, IC1R, 2),
+			PIC32_PINCTRL_FUNCTION(IC6, IC6R, 2),
+			PIC32_PINCTRL_FUNCTION(U3CTS, U3CTSR, 2),
+			PIC32_PINCTRL_FUNCTION(U4RX, U4RXR, 2),
+			PIC32_PINCTRL_FUNCTION(U6RX, U6RXR, 2),
+			PIC32_PINCTRL_FUNCTION(SS2IN, SS2INR, 2),
+			PIC32_PINCTRL_FUNCTION(SDI6, SDI6R, 2),
+			PIC32_PINCTRL_FUNCTION(OCFA, OCFAR, 2),
+			PIC32_PINCTRL_FUNCTION(REFCLKI3, REFCLKI3R, 2),
+			PIC32_PINCTRL_FUNCTION(U1RTS, RPB14R, 1),
+			PIC32_PINCTRL_FUNCTION(U2TX, RPB14R, 2),
+			PIC32_PINCTRL_FUNCTION(U5RTS, RPB14R, 3),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPB14R, 4),
+			PIC32_PINCTRL_FUNCTION(SS2OUT, RPB14R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPB14R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPB14R, 10),
+			PIC32_PINCTRL_FUNCTION(OC2, RPB14R, 11),
+			PIC32_PINCTRL_FUNCTION(OC1, RPB14R, 12),
+			PIC32_PINCTRL_FUNCTION(OC9, RPB14R, 13),
+			PIC32_PINCTRL_FUNCTION(C2TX, RPB14R, 15)),
+	PIC32_PINCTRL_GROUP(31, B15,
+			PIC32_PINCTRL_FUNCTION(INT2, INT2R, 3),
+			PIC32_PINCTRL_FUNCTION(T3CK, T3CKR, 3),
+			PIC32_PINCTRL_FUNCTION(T8CK, T8CKR, 3),
+			PIC32_PINCTRL_FUNCTION(IC2, IC2R, 3),
+			PIC32_PINCTRL_FUNCTION(IC5, IC5R, 3),
+			PIC32_PINCTRL_FUNCTION(IC9, IC9R, 3),
+			PIC32_PINCTRL_FUNCTION(U1CTS, U1CTSR, 3),
+			PIC32_PINCTRL_FUNCTION(U2RX, U2RXR, 3),
+			PIC32_PINCTRL_FUNCTION(U5CTS, U5CTSR, 3),
+			PIC32_PINCTRL_FUNCTION(SS1IN, SS1INR, 3),
+			PIC32_PINCTRL_FUNCTION(SS3IN, SS3INR, 3),
+			PIC32_PINCTRL_FUNCTION(SS4IN, SS4INR, 3),
+			PIC32_PINCTRL_FUNCTION(SS5IN, SS5INR, 3),
+			PIC32_PINCTRL_FUNCTION(C2RX, C2RXR, 3),
+			PIC32_PINCTRL_FUNCTION(U3RTS, RPB15R, 1),
+			PIC32_PINCTRL_FUNCTION(U4TX, RPB15R, 2),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPB15R, 4),
+			PIC32_PINCTRL_FUNCTION(SS1OUT, RPB15R, 5),
+			PIC32_PINCTRL_FUNCTION(SS3OUT, RPB15R, 7),
+			PIC32_PINCTRL_FUNCTION(SS4OUT, RPB15R, 8),
+			PIC32_PINCTRL_FUNCTION(SS5OUT, RPB15R, 9),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPB15R, 10),
+			PIC32_PINCTRL_FUNCTION(OC5, RPB15R, 11),
+			PIC32_PINCTRL_FUNCTION(OC8, RPB15R, 12),
+			PIC32_PINCTRL_FUNCTION(C1OUT, RPB15R, 14),
+			PIC32_PINCTRL_FUNCTION(REFCLKO3, RPB15R, 15)),
+	PIC32_PINCTRL_GROUP(33, C1,
+			PIC32_PINCTRL_FUNCTION(INT3, INT3R, 10),
+			PIC32_PINCTRL_FUNCTION(T2CK, T2CKR, 10),
+			PIC32_PINCTRL_FUNCTION(T6CK, T6CKR, 10),
+			PIC32_PINCTRL_FUNCTION(IC3, IC3R, 10),
+			PIC32_PINCTRL_FUNCTION(IC7, IC7R, 10),
+			PIC32_PINCTRL_FUNCTION(U1RX, U1RXR, 10),
+			PIC32_PINCTRL_FUNCTION(U2CTS, U2CTSR, 10),
+			PIC32_PINCTRL_FUNCTION(U5RX, U5RXR, 10),
+			PIC32_PINCTRL_FUNCTION(U6CTS, U6CTSR, 10),
+			PIC32_PINCTRL_FUNCTION(SDI1, SDI1R, 10),
+			PIC32_PINCTRL_FUNCTION(SDI3, SDI3R, 10),
+			PIC32_PINCTRL_FUNCTION(SDI5, SDI5R, 10),
+			PIC32_PINCTRL_FUNCTION(SS6IN, SS6INR, 10),
+			PIC32_PINCTRL_FUNCTION(REFCLKI1, REFCLKI1R, 10),
+			PIC32_PINCTRL_FUNCTION(U3TX, RPC1R, 1),
+			PIC32_PINCTRL_FUNCTION(U4RTS, RPC1R, 2),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPC1R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPC1R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPC1R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPC1R, 9),
+			PIC32_PINCTRL_FUNCTION(SS6OUT, RPC1R, 10),
+			PIC32_PINCTRL_FUNCTION(OC3, RPC1R, 11),
+			PIC32_PINCTRL_FUNCTION(OC6, RPC1R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO4, RPC1R, 13),
+			PIC32_PINCTRL_FUNCTION(C2OUT, RPC1R, 14),
+			PIC32_PINCTRL_FUNCTION(C1TX, RPC1R, 15)),
+	PIC32_PINCTRL_GROUP(34, C2,
+			PIC32_PINCTRL_FUNCTION(INT1, INT1R, 12),
+			PIC32_PINCTRL_FUNCTION(T4CK, T4CKR, 12),
+			PIC32_PINCTRL_FUNCTION(T9CK, T9CKR, 12),
+			PIC32_PINCTRL_FUNCTION(IC1, IC1R, 12),
+			PIC32_PINCTRL_FUNCTION(IC6, IC6R, 12),
+			PIC32_PINCTRL_FUNCTION(U3CTS, U3CTSR, 12),
+			PIC32_PINCTRL_FUNCTION(U4RX, U4RXR, 12),
+			PIC32_PINCTRL_FUNCTION(U6RX, U6RXR, 12),
+			PIC32_PINCTRL_FUNCTION(SS2IN, SS2INR, 12),
+			PIC32_PINCTRL_FUNCTION(SDI6, SDI6R, 12),
+			PIC32_PINCTRL_FUNCTION(OCFA, OCFAR, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKI3, REFCLKI3R, 12),
+			PIC32_PINCTRL_FUNCTION(U1RTS, RPC2R, 1),
+			PIC32_PINCTRL_FUNCTION(U2TX, RPC2R, 2),
+			PIC32_PINCTRL_FUNCTION(U5RTS, RPC2R, 3),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPC2R, 4),
+			PIC32_PINCTRL_FUNCTION(SS2OUT, RPC2R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPC2R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPC2R, 10),
+			PIC32_PINCTRL_FUNCTION(OC2, RPC2R, 11),
+			PIC32_PINCTRL_FUNCTION(OC1, RPC2R, 12),
+			PIC32_PINCTRL_FUNCTION(OC9, RPC2R, 13),
+			PIC32_PINCTRL_FUNCTION(C2TX, RPC2R, 15)),
+	PIC32_PINCTRL_GROUP(35, C3,
+			PIC32_PINCTRL_FUNCTION(INT2, INT2R, 12),
+			PIC32_PINCTRL_FUNCTION(T3CK, T3CKR, 12),
+			PIC32_PINCTRL_FUNCTION(T8CK, T8CKR, 12),
+			PIC32_PINCTRL_FUNCTION(IC2, IC2R, 12),
+			PIC32_PINCTRL_FUNCTION(IC5, IC5R, 12),
+			PIC32_PINCTRL_FUNCTION(IC9, IC9R, 12),
+			PIC32_PINCTRL_FUNCTION(U1CTS, U1CTSR, 12),
+			PIC32_PINCTRL_FUNCTION(U2RX, U2RXR, 12),
+			PIC32_PINCTRL_FUNCTION(U5CTS, U5CTSR, 12),
+			PIC32_PINCTRL_FUNCTION(SS1IN, SS1INR, 12),
+			PIC32_PINCTRL_FUNCTION(SS3IN, SS3INR, 12),
+			PIC32_PINCTRL_FUNCTION(SS4IN, SS4INR, 12),
+			PIC32_PINCTRL_FUNCTION(SS5IN, SS5INR, 12),
+			PIC32_PINCTRL_FUNCTION(C2RX, C2RXR, 12),
+			PIC32_PINCTRL_FUNCTION(U3RTS, RPC3R, 1),
+			PIC32_PINCTRL_FUNCTION(U4TX, RPC3R, 2),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPC3R, 4),
+			PIC32_PINCTRL_FUNCTION(SS1OUT, RPC3R, 5),
+			PIC32_PINCTRL_FUNCTION(SS3OUT, RPC3R, 7),
+			PIC32_PINCTRL_FUNCTION(SS4OUT, RPC3R, 8),
+			PIC32_PINCTRL_FUNCTION(SS5OUT, RPC3R, 9),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPC3R, 10),
+			PIC32_PINCTRL_FUNCTION(OC5, RPC3R, 11),
+			PIC32_PINCTRL_FUNCTION(OC8, RPC3R, 12),
+			PIC32_PINCTRL_FUNCTION(C1OUT, RPC3R, 14),
+			PIC32_PINCTRL_FUNCTION(REFCLKO3, RPC3R, 15)),
+	PIC32_PINCTRL_GROUP(36, C4,
+			PIC32_PINCTRL_FUNCTION(INT4, INT4R, 10),
+			PIC32_PINCTRL_FUNCTION(T5CK, T5CKR, 10),
+			PIC32_PINCTRL_FUNCTION(T7CK, T7CKR, 10),
+			PIC32_PINCTRL_FUNCTION(IC4, IC4R, 10),
+			PIC32_PINCTRL_FUNCTION(IC8, IC8R, 10),
+			PIC32_PINCTRL_FUNCTION(U3RX, U3RXR, 10),
+			PIC32_PINCTRL_FUNCTION(U4CTS, U4CTSR, 10),
+			PIC32_PINCTRL_FUNCTION(SDI2, SDI2R, 10),
+			PIC32_PINCTRL_FUNCTION(SDI4, SDI4R, 10),
+			PIC32_PINCTRL_FUNCTION(C1RX, C1RXR, 10),
+			PIC32_PINCTRL_FUNCTION(REFCLKI4, REFCLKI4R, 10),
+			PIC32_PINCTRL_FUNCTION(U1TX, RPC4R, 1),
+			PIC32_PINCTRL_FUNCTION(U2RTS, RPC4R, 2),
+			PIC32_PINCTRL_FUNCTION(U5TX, RPC4R, 3),
+			PIC32_PINCTRL_FUNCTION(U6RTS, RPC4R, 4),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPC4R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPC4R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPC4R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPC4R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPC4R, 9),
+			PIC32_PINCTRL_FUNCTION(OC4, RPC4R, 11),
+			PIC32_PINCTRL_FUNCTION(OC7, RPC4R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO1, RPC4R, 15)),
+	PIC32_PINCTRL_GROUP(45, C13,
+			PIC32_PINCTRL_FUNCTION(INT4, INT4R, 7),
+			PIC32_PINCTRL_FUNCTION(T5CK, T5CKR, 7),
+			PIC32_PINCTRL_FUNCTION(T7CK, T7CKR, 7),
+			PIC32_PINCTRL_FUNCTION(IC4, IC4R, 7),
+			PIC32_PINCTRL_FUNCTION(IC8, IC8R, 7),
+			PIC32_PINCTRL_FUNCTION(U3RX, U3RXR, 7),
+			PIC32_PINCTRL_FUNCTION(U4CTS, U4CTSR, 7),
+			PIC32_PINCTRL_FUNCTION(SDI2, SDI2R, 7),
+			PIC32_PINCTRL_FUNCTION(SDI4, SDI4R, 7),
+			PIC32_PINCTRL_FUNCTION(C1RX, C1RXR, 7),
+			PIC32_PINCTRL_FUNCTION(REFCLKI4, REFCLKI4R, 7),
+			PIC32_PINCTRL_FUNCTION(U1TX, RPC13R, 1),
+			PIC32_PINCTRL_FUNCTION(U2RTS, RPC13R, 2),
+			PIC32_PINCTRL_FUNCTION(U5TX, RPC13R, 3),
+			PIC32_PINCTRL_FUNCTION(U6RTS, RPC13R, 4),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPC13R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPC13R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPC13R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPC13R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPC13R, 9),
+			PIC32_PINCTRL_FUNCTION(OC4, RPC13R, 11),
+			PIC32_PINCTRL_FUNCTION(OC7, RPC13R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO1, RPC13R, 15)),
+	PIC32_PINCTRL_GROUP(46, C14,
+			PIC32_PINCTRL_FUNCTION(INT3, INT3R, 7),
+			PIC32_PINCTRL_FUNCTION(T2CK, T2CKR, 7),
+			PIC32_PINCTRL_FUNCTION(T6CK, T6CKR, 7),
+			PIC32_PINCTRL_FUNCTION(IC3, IC3R, 7),
+			PIC32_PINCTRL_FUNCTION(IC7, IC7R, 7),
+			PIC32_PINCTRL_FUNCTION(U1RX, U1RXR, 7),
+			PIC32_PINCTRL_FUNCTION(U2CTS, U2CTSR, 7),
+			PIC32_PINCTRL_FUNCTION(U5RX, U5RXR, 7),
+			PIC32_PINCTRL_FUNCTION(U6CTS, U6CTSR, 7),
+			PIC32_PINCTRL_FUNCTION(SDI1, SDI1R, 7),
+			PIC32_PINCTRL_FUNCTION(SDI3, SDI3R, 7),
+			PIC32_PINCTRL_FUNCTION(SDI5, SDI5R, 7),
+			PIC32_PINCTRL_FUNCTION(SS6IN, SS6INR, 7),
+			PIC32_PINCTRL_FUNCTION(REFCLKI1, REFCLKI1R, 7),
+			PIC32_PINCTRL_FUNCTION(U3TX, RPC14R, 1),
+			PIC32_PINCTRL_FUNCTION(U4RTS, RPC14R, 2),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPC14R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPC14R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPC14R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPC14R, 9),
+			PIC32_PINCTRL_FUNCTION(SS6OUT, RPC14R, 10),
+			PIC32_PINCTRL_FUNCTION(OC3, RPC14R, 11),
+			PIC32_PINCTRL_FUNCTION(OC6, RPC14R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO4, RPC14R, 13),
+			PIC32_PINCTRL_FUNCTION(C2OUT, RPC14R, 14),
+			PIC32_PINCTRL_FUNCTION(C1TX, RPC14R, 15)),
+	PIC32_PINCTRL_GROUP(48, D0,
+			PIC32_PINCTRL_FUNCTION(INT1, INT1R, 3),
+			PIC32_PINCTRL_FUNCTION(T4CK, T4CKR, 3),
+			PIC32_PINCTRL_FUNCTION(T9CK, T9CKR, 3),
+			PIC32_PINCTRL_FUNCTION(IC1, IC1R, 3),
+			PIC32_PINCTRL_FUNCTION(IC6, IC6R, 3),
+			PIC32_PINCTRL_FUNCTION(U3CTS, U3CTSR, 3),
+			PIC32_PINCTRL_FUNCTION(U4RX, U4RXR, 3),
+			PIC32_PINCTRL_FUNCTION(U6RX, U6RXR, 3),
+			PIC32_PINCTRL_FUNCTION(SS2IN, SS2INR, 3),
+			PIC32_PINCTRL_FUNCTION(SDI6, SDI6R, 3),
+			PIC32_PINCTRL_FUNCTION(OCFA, OCFAR, 3),
+			PIC32_PINCTRL_FUNCTION(REFCLKI3, REFCLKI3R, 3),
+			PIC32_PINCTRL_FUNCTION(U1RTS, RPD0R, 1),
+			PIC32_PINCTRL_FUNCTION(U2TX, RPD0R, 2),
+			PIC32_PINCTRL_FUNCTION(U5RTS, RPD0R, 3),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPD0R, 4),
+			PIC32_PINCTRL_FUNCTION(SS2OUT, RPD0R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPD0R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPD0R, 10),
+			PIC32_PINCTRL_FUNCTION(OC2, RPD0R, 11),
+			PIC32_PINCTRL_FUNCTION(OC1, RPD0R, 12),
+			PIC32_PINCTRL_FUNCTION(OC9, RPD0R, 13),
+			PIC32_PINCTRL_FUNCTION(C2TX, RPD0R, 15)),
+	PIC32_PINCTRL_GROUP(50, D2,
+			PIC32_PINCTRL_FUNCTION(INT3, INT3R, 0),
+			PIC32_PINCTRL_FUNCTION(T2CK, T2CKR, 0),
+			PIC32_PINCTRL_FUNCTION(T6CK, T6CKR, 0),
+			PIC32_PINCTRL_FUNCTION(IC3, IC3R, 0),
+			PIC32_PINCTRL_FUNCTION(IC7, IC7R, 0),
+			PIC32_PINCTRL_FUNCTION(U1RX, U1RXR, 0),
+			PIC32_PINCTRL_FUNCTION(U2CTS, U2CTSR, 0),
+			PIC32_PINCTRL_FUNCTION(U5RX, U5RXR, 0),
+			PIC32_PINCTRL_FUNCTION(U6CTS, U6CTSR, 0),
+			PIC32_PINCTRL_FUNCTION(SDI1, SDI1R, 0),
+			PIC32_PINCTRL_FUNCTION(SDI3, SDI3R, 0),
+			PIC32_PINCTRL_FUNCTION(SDI5, SDI5R, 0),
+			PIC32_PINCTRL_FUNCTION(SS6IN, SS6INR, 0),
+			PIC32_PINCTRL_FUNCTION(REFCLKI1, REFCLKI1R, 0),
+			PIC32_PINCTRL_FUNCTION(U3TX, RPD2R, 1),
+			PIC32_PINCTRL_FUNCTION(U4RTS, RPD2R, 2),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPD2R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPD2R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPD2R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPD2R, 9),
+			PIC32_PINCTRL_FUNCTION(SS6OUT, RPD2R, 10),
+			PIC32_PINCTRL_FUNCTION(OC3, RPD2R, 11),
+			PIC32_PINCTRL_FUNCTION(OC6, RPD2R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO4, RPD2R, 13),
+			PIC32_PINCTRL_FUNCTION(C2OUT, RPD2R, 14),
+			PIC32_PINCTRL_FUNCTION(C1TX, RPD2R, 15)),
+	PIC32_PINCTRL_GROUP(51, D3,
+			PIC32_PINCTRL_FUNCTION(INT4, INT4R, 0),
+			PIC32_PINCTRL_FUNCTION(T5CK, T5CKR, 0),
+			PIC32_PINCTRL_FUNCTION(T7CK, T7CKR, 0),
+			PIC32_PINCTRL_FUNCTION(IC4, IC4R, 0),
+			PIC32_PINCTRL_FUNCTION(IC8, IC8R, 0),
+			PIC32_PINCTRL_FUNCTION(U3RX, U3RXR, 0),
+			PIC32_PINCTRL_FUNCTION(U4CTS, U4CTSR, 0),
+			PIC32_PINCTRL_FUNCTION(SDI2, SDI2R, 0),
+			PIC32_PINCTRL_FUNCTION(SDI4, SDI4R, 0),
+			PIC32_PINCTRL_FUNCTION(C1RX, C1RXR, 0),
+			PIC32_PINCTRL_FUNCTION(REFCLKI4, REFCLKI4R, 0),
+			PIC32_PINCTRL_FUNCTION(U1TX, RPD3R, 1),
+			PIC32_PINCTRL_FUNCTION(U2RTS, RPD3R, 2),
+			PIC32_PINCTRL_FUNCTION(U5TX, RPD3R, 3),
+			PIC32_PINCTRL_FUNCTION(U6RTS, RPD3R, 4),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPD3R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPD3R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPD3R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPD3R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPD3R, 9),
+			PIC32_PINCTRL_FUNCTION(OC4, RPD3R, 11),
+			PIC32_PINCTRL_FUNCTION(OC7, RPD3R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO1, RPD3R, 15)),
+	PIC32_PINCTRL_GROUP(52, D4,
+			PIC32_PINCTRL_FUNCTION(INT2, INT2R, 4),
+			PIC32_PINCTRL_FUNCTION(T3CK, T3CKR, 4),
+			PIC32_PINCTRL_FUNCTION(T8CK, T8CKR, 4),
+			PIC32_PINCTRL_FUNCTION(IC2, IC2R, 4),
+			PIC32_PINCTRL_FUNCTION(IC5, IC5R, 4),
+			PIC32_PINCTRL_FUNCTION(IC9, IC9R, 4),
+			PIC32_PINCTRL_FUNCTION(U1CTS, U1CTSR, 4),
+			PIC32_PINCTRL_FUNCTION(U2RX, U2RXR, 4),
+			PIC32_PINCTRL_FUNCTION(U5CTS, U5CTSR, 4),
+			PIC32_PINCTRL_FUNCTION(SS1IN, SS1INR, 4),
+			PIC32_PINCTRL_FUNCTION(SS3IN, SS3INR, 4),
+			PIC32_PINCTRL_FUNCTION(SS4IN, SS4INR, 4),
+			PIC32_PINCTRL_FUNCTION(SS5IN, SS5INR, 4),
+			PIC32_PINCTRL_FUNCTION(C2RX, C2RXR, 4),
+			PIC32_PINCTRL_FUNCTION(U3RTS, RPD4R, 1),
+			PIC32_PINCTRL_FUNCTION(U4TX, RPD4R, 2),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPD4R, 4),
+			PIC32_PINCTRL_FUNCTION(SS1OUT, RPD4R, 5),
+			PIC32_PINCTRL_FUNCTION(SS3OUT, RPD4R, 7),
+			PIC32_PINCTRL_FUNCTION(SS4OUT, RPD4R, 8),
+			PIC32_PINCTRL_FUNCTION(SS5OUT, RPD4R, 9),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPD4R, 10),
+			PIC32_PINCTRL_FUNCTION(OC5, RPD4R, 11),
+			PIC32_PINCTRL_FUNCTION(OC8, RPD4R, 12),
+			PIC32_PINCTRL_FUNCTION(C1OUT, RPD4R, 14),
+			PIC32_PINCTRL_FUNCTION(REFCLKO3, RPD4R, 15)),
+	PIC32_PINCTRL_GROUP(53, D5,
+			PIC32_PINCTRL_FUNCTION(INT1, INT1R, 6),
+			PIC32_PINCTRL_FUNCTION(T4CK, T4CKR, 6),
+			PIC32_PINCTRL_FUNCTION(T9CK, T9CKR, 6),
+			PIC32_PINCTRL_FUNCTION(IC1, IC1R, 6),
+			PIC32_PINCTRL_FUNCTION(IC6, IC6R, 6),
+			PIC32_PINCTRL_FUNCTION(U3CTS, U3CTSR, 6),
+			PIC32_PINCTRL_FUNCTION(U4RX, U4RXR, 6),
+			PIC32_PINCTRL_FUNCTION(U6RX, U6RXR, 6),
+			PIC32_PINCTRL_FUNCTION(SS2IN, SS2INR, 6),
+			PIC32_PINCTRL_FUNCTION(SDI6, SDI6R, 6),
+			PIC32_PINCTRL_FUNCTION(OCFA, OCFAR, 6),
+			PIC32_PINCTRL_FUNCTION(REFCLKI3, REFCLKI3R, 6),
+			PIC32_PINCTRL_FUNCTION(U1RTS, RPD5R, 1),
+			PIC32_PINCTRL_FUNCTION(U2TX, RPD5R, 2),
+			PIC32_PINCTRL_FUNCTION(U5RTS, RPD5R, 3),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPD5R, 4),
+			PIC32_PINCTRL_FUNCTION(SS2OUT, RPD5R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPD5R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPD5R, 10),
+			PIC32_PINCTRL_FUNCTION(OC2, RPD5R, 11),
+			PIC32_PINCTRL_FUNCTION(OC1, RPD5R, 12),
+			PIC32_PINCTRL_FUNCTION(OC9, RPD5R, 13),
+			PIC32_PINCTRL_FUNCTION(C2TX, RPD5R, 15)),
+	PIC32_PINCTRL_GROUP(54, D6,
+			PIC32_PINCTRL_FUNCTION(INT3, INT3R, 14),
+			PIC32_PINCTRL_FUNCTION(T2CK, T2CKR, 14),
+			PIC32_PINCTRL_FUNCTION(T6CK, T6CKR, 14),
+			PIC32_PINCTRL_FUNCTION(IC3, IC3R, 14),
+			PIC32_PINCTRL_FUNCTION(IC7, IC7R, 14),
+			PIC32_PINCTRL_FUNCTION(U1RX, U1RXR, 14),
+			PIC32_PINCTRL_FUNCTION(U2CTS, U2CTSR, 14),
+			PIC32_PINCTRL_FUNCTION(U5RX, U5RXR, 14),
+			PIC32_PINCTRL_FUNCTION(U6CTS, U6CTSR, 14),
+			PIC32_PINCTRL_FUNCTION(SDI1, SDI1R, 14),
+			PIC32_PINCTRL_FUNCTION(SDI3, SDI3R, 14),
+			PIC32_PINCTRL_FUNCTION(SDI5, SDI5R, 14),
+			PIC32_PINCTRL_FUNCTION(SS6IN, SS6INR, 14),
+			PIC32_PINCTRL_FUNCTION(REFCLKI1, REFCLKI1R, 14),
+			PIC32_PINCTRL_FUNCTION(U3TX, RPD6R, 1),
+			PIC32_PINCTRL_FUNCTION(U4RTS, RPD6R, 2),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPD6R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPD6R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPD6R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPD6R, 9),
+			PIC32_PINCTRL_FUNCTION(SS6OUT, RPD6R, 10),
+			PIC32_PINCTRL_FUNCTION(OC3, RPD6R, 11),
+			PIC32_PINCTRL_FUNCTION(OC6, RPD6R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO4, RPD6R, 13),
+			PIC32_PINCTRL_FUNCTION(C2OUT, RPD6R, 14),
+			PIC32_PINCTRL_FUNCTION(C1TX, RPD6R, 15)),
+	PIC32_PINCTRL_GROUP(55, D7,
+			PIC32_PINCTRL_FUNCTION(INT4, INT4R, 14),
+			PIC32_PINCTRL_FUNCTION(T5CK, T5CKR, 14),
+			PIC32_PINCTRL_FUNCTION(T7CK, T7CKR, 14),
+			PIC32_PINCTRL_FUNCTION(IC4, IC4R, 14),
+			PIC32_PINCTRL_FUNCTION(IC8, IC8R, 14),
+			PIC32_PINCTRL_FUNCTION(U3RX, U3RXR, 14),
+			PIC32_PINCTRL_FUNCTION(U4CTS, U4CTSR, 14),
+			PIC32_PINCTRL_FUNCTION(SDI2, SDI2R, 14),
+			PIC32_PINCTRL_FUNCTION(SDI4, SDI4R, 14),
+			PIC32_PINCTRL_FUNCTION(C1RX, C1RXR, 14),
+			PIC32_PINCTRL_FUNCTION(REFCLKI4, REFCLKI4R, 14),
+			PIC32_PINCTRL_FUNCTION(U1TX, RPD7R, 1),
+			PIC32_PINCTRL_FUNCTION(U2RTS, RPD7R, 2),
+			PIC32_PINCTRL_FUNCTION(U5TX, RPD7R, 3),
+			PIC32_PINCTRL_FUNCTION(U6RTS, RPD7R, 4),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPD7R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPD7R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPD7R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPD7R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPD7R, 9),
+			PIC32_PINCTRL_FUNCTION(OC4, RPD7R, 11),
+			PIC32_PINCTRL_FUNCTION(OC7, RPD7R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO1, RPD7R, 15)),
+	PIC32_PINCTRL_GROUP(57, D9,
+			PIC32_PINCTRL_FUNCTION(INT2, INT2R, 0),
+			PIC32_PINCTRL_FUNCTION(T3CK, T3CKR, 0),
+			PIC32_PINCTRL_FUNCTION(T8CK, T8CKR, 0),
+			PIC32_PINCTRL_FUNCTION(IC2, IC2R, 0),
+			PIC32_PINCTRL_FUNCTION(IC5, IC5R, 0),
+			PIC32_PINCTRL_FUNCTION(IC9, IC9R, 0),
+			PIC32_PINCTRL_FUNCTION(U1CTS, U1CTSR, 0),
+			PIC32_PINCTRL_FUNCTION(U2RX, U2RXR, 0),
+			PIC32_PINCTRL_FUNCTION(U5CTS, U5CTSR, 0),
+			PIC32_PINCTRL_FUNCTION(SS1IN, SS1INR, 0),
+			PIC32_PINCTRL_FUNCTION(SS3IN, SS3INR, 0),
+			PIC32_PINCTRL_FUNCTION(SS4IN, SS4INR, 0),
+			PIC32_PINCTRL_FUNCTION(SS5IN, SS5INR, 0),
+			PIC32_PINCTRL_FUNCTION(C2RX, C2RXR, 0),
+			PIC32_PINCTRL_FUNCTION(U3RTS, RPD9R, 1),
+			PIC32_PINCTRL_FUNCTION(U4TX, RPD9R, 2),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPD9R, 4),
+			PIC32_PINCTRL_FUNCTION(SS1OUT, RPD9R, 5),
+			PIC32_PINCTRL_FUNCTION(SS3OUT, RPD9R, 7),
+			PIC32_PINCTRL_FUNCTION(SS4OUT, RPD9R, 8),
+			PIC32_PINCTRL_FUNCTION(SS5OUT, RPD9R, 9),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPD9R, 10),
+			PIC32_PINCTRL_FUNCTION(OC5, RPD9R, 11),
+			PIC32_PINCTRL_FUNCTION(OC8, RPD9R, 12),
+			PIC32_PINCTRL_FUNCTION(C1OUT, RPD9R, 14),
+			PIC32_PINCTRL_FUNCTION(REFCLKO3, RPD9R, 15)),
+	PIC32_PINCTRL_GROUP(58, D10,
+			PIC32_PINCTRL_FUNCTION(U3TX, RPD10R, 1),
+			PIC32_PINCTRL_FUNCTION(U4RTS, RPD10R, 2),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPD10R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPD10R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPD10R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPD10R, 9),
+			PIC32_PINCTRL_FUNCTION(SS6OUT, RPD10R, 10),
+			PIC32_PINCTRL_FUNCTION(OC3, RPD10R, 11),
+			PIC32_PINCTRL_FUNCTION(OC6, RPD10R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO4, RPD10R, 13),
+			PIC32_PINCTRL_FUNCTION(C2OUT, RPD10R, 14),
+			PIC32_PINCTRL_FUNCTION(C1TX, RPD10R, 15)),
+	PIC32_PINCTRL_GROUP(59, D11,
+			PIC32_PINCTRL_FUNCTION(INT4, INT4R, 3),
+			PIC32_PINCTRL_FUNCTION(T5CK, T5CKR, 3),
+			PIC32_PINCTRL_FUNCTION(T7CK, T7CKR, 3),
+			PIC32_PINCTRL_FUNCTION(IC4, IC4R, 3),
+			PIC32_PINCTRL_FUNCTION(IC8, IC8R, 3),
+			PIC32_PINCTRL_FUNCTION(U3RX, U3RXR, 3),
+			PIC32_PINCTRL_FUNCTION(U4CTS, U4CTSR, 3),
+			PIC32_PINCTRL_FUNCTION(SDI2, SDI2R, 3),
+			PIC32_PINCTRL_FUNCTION(SDI4, SDI4R, 3),
+			PIC32_PINCTRL_FUNCTION(C1RX, C1RXR, 3),
+			PIC32_PINCTRL_FUNCTION(REFCLKI4, REFCLKI4R, 3),
+			PIC32_PINCTRL_FUNCTION(U1TX, RPD11R, 1),
+			PIC32_PINCTRL_FUNCTION(U2RTS, RPD11R, 2),
+			PIC32_PINCTRL_FUNCTION(U5TX, RPD11R, 3),
+			PIC32_PINCTRL_FUNCTION(U6RTS, RPD11R, 4),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPD11R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPD11R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPD11R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPD11R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPD11R, 9),
+			PIC32_PINCTRL_FUNCTION(OC4, RPD11R, 11),
+			PIC32_PINCTRL_FUNCTION(OC7, RPD11R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO1, RPD11R, 15)),
+	PIC32_PINCTRL_GROUP(60, D12,
+			PIC32_PINCTRL_FUNCTION(INT2, INT2R, 10),
+			PIC32_PINCTRL_FUNCTION(T3CK, T3CKR, 10),
+			PIC32_PINCTRL_FUNCTION(T8CK, T8CKR, 10),
+			PIC32_PINCTRL_FUNCTION(IC2, IC2R, 10),
+			PIC32_PINCTRL_FUNCTION(IC5, IC5R, 10),
+			PIC32_PINCTRL_FUNCTION(IC9, IC9R, 10),
+			PIC32_PINCTRL_FUNCTION(U1CTS, U1CTSR, 10),
+			PIC32_PINCTRL_FUNCTION(U2RX, U2RXR, 10),
+			PIC32_PINCTRL_FUNCTION(U5CTS, U5CTSR, 10),
+			PIC32_PINCTRL_FUNCTION(SS1IN, SS1INR, 10),
+			PIC32_PINCTRL_FUNCTION(SS3IN, SS3INR, 10),
+			PIC32_PINCTRL_FUNCTION(SS4IN, SS4INR, 10),
+			PIC32_PINCTRL_FUNCTION(SS5IN, SS5INR, 10),
+			PIC32_PINCTRL_FUNCTION(C2RX, C2RXR, 10),
+			PIC32_PINCTRL_FUNCTION(U3RTS, RPD12R, 1),
+			PIC32_PINCTRL_FUNCTION(U4TX, RPD12R, 2),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPD12R, 4),
+			PIC32_PINCTRL_FUNCTION(SS1OUT, RPD12R, 5),
+			PIC32_PINCTRL_FUNCTION(SS3OUT, RPD12R, 7),
+			PIC32_PINCTRL_FUNCTION(SS4OUT, RPD12R, 8),
+			PIC32_PINCTRL_FUNCTION(SS5OUT, RPD12R, 9),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPD12R, 10),
+			PIC32_PINCTRL_FUNCTION(OC5, RPD12R, 11),
+			PIC32_PINCTRL_FUNCTION(OC8, RPD12R, 12),
+			PIC32_PINCTRL_FUNCTION(C1OUT, RPD12R, 14),
+			PIC32_PINCTRL_FUNCTION(REFCLKO3, RPD12R, 15)),
+	PIC32_PINCTRL_GROUP(62, D14,
+			PIC32_PINCTRL_FUNCTION(INT3, INT3R, 11),
+			PIC32_PINCTRL_FUNCTION(T2CK, T2CKR, 11),
+			PIC32_PINCTRL_FUNCTION(T6CK, T6CKR, 11),
+			PIC32_PINCTRL_FUNCTION(IC3, IC3R, 11),
+			PIC32_PINCTRL_FUNCTION(IC7, IC7R, 11),
+			PIC32_PINCTRL_FUNCTION(U1RX, U1RXR, 11),
+			PIC32_PINCTRL_FUNCTION(U2CTS, U2CTSR, 11),
+			PIC32_PINCTRL_FUNCTION(U5RX, U5RXR, 11),
+			PIC32_PINCTRL_FUNCTION(U6CTS, U6CTSR, 11),
+			PIC32_PINCTRL_FUNCTION(SDI1, SDI1R, 11),
+			PIC32_PINCTRL_FUNCTION(SDI3, SDI3R, 11),
+			PIC32_PINCTRL_FUNCTION(SDI5, SDI5R, 11),
+			PIC32_PINCTRL_FUNCTION(SS6IN, SS6INR, 11),
+			PIC32_PINCTRL_FUNCTION(REFCLKI1, REFCLKI1R, 11),
+			PIC32_PINCTRL_FUNCTION(U3TX, RPD14R, 1),
+			PIC32_PINCTRL_FUNCTION(U4RTS, RPD14R, 2),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPD14R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPD14R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPD14R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPD14R, 9),
+			PIC32_PINCTRL_FUNCTION(SS6OUT, RPD14R, 10),
+			PIC32_PINCTRL_FUNCTION(OC3, RPD14R, 11),
+			PIC32_PINCTRL_FUNCTION(OC6, RPD14R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO4, RPD14R, 13),
+			PIC32_PINCTRL_FUNCTION(C2OUT, RPD14R, 14),
+			PIC32_PINCTRL_FUNCTION(C1TX, RPD14R, 15)),
+	PIC32_PINCTRL_GROUP(63, D15,
+			PIC32_PINCTRL_FUNCTION(U1TX, RPD15R, 1),
+			PIC32_PINCTRL_FUNCTION(U2RTS, RPD15R, 2),
+			PIC32_PINCTRL_FUNCTION(U5TX, RPD15R, 3),
+			PIC32_PINCTRL_FUNCTION(U6RTS, RPD15R, 4),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPD15R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPD15R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPD15R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPD15R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPD15R, 9),
+			PIC32_PINCTRL_FUNCTION(OC4, RPD15R, 11),
+			PIC32_PINCTRL_FUNCTION(OC7, RPD15R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO1, RPD15R, 15)),
+	PIC32_PINCTRL_GROUP(67, E3,
+			PIC32_PINCTRL_FUNCTION(INT2, INT2R, 6),
+			PIC32_PINCTRL_FUNCTION(T3CK, T3CKR, 6),
+			PIC32_PINCTRL_FUNCTION(T8CK, T8CKR, 6),
+			PIC32_PINCTRL_FUNCTION(IC2, IC2R, 6),
+			PIC32_PINCTRL_FUNCTION(IC5, IC5R, 6),
+			PIC32_PINCTRL_FUNCTION(IC9, IC9R, 6),
+			PIC32_PINCTRL_FUNCTION(U1CTS, U1CTSR, 6),
+			PIC32_PINCTRL_FUNCTION(U2RX, U2RXR, 6),
+			PIC32_PINCTRL_FUNCTION(U5CTS, U5CTSR, 6),
+			PIC32_PINCTRL_FUNCTION(SS1IN, SS1INR, 6),
+			PIC32_PINCTRL_FUNCTION(SS3IN, SS3INR, 6),
+			PIC32_PINCTRL_FUNCTION(SS4IN, SS4INR, 6),
+			PIC32_PINCTRL_FUNCTION(SS5IN, SS5INR, 6),
+			PIC32_PINCTRL_FUNCTION(C2RX, C2RXR, 6),
+			PIC32_PINCTRL_FUNCTION(U3RTS, RPE3R, 1),
+			PIC32_PINCTRL_FUNCTION(U4TX, RPE3R, 2),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPE3R, 4),
+			PIC32_PINCTRL_FUNCTION(SS1OUT, RPE3R, 5),
+			PIC32_PINCTRL_FUNCTION(SS3OUT, RPE3R, 7),
+			PIC32_PINCTRL_FUNCTION(SS4OUT, RPE3R, 8),
+			PIC32_PINCTRL_FUNCTION(SS5OUT, RPE3R, 9),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPE3R, 10),
+			PIC32_PINCTRL_FUNCTION(OC5, RPE3R, 11),
+			PIC32_PINCTRL_FUNCTION(OC8, RPE3R, 12),
+			PIC32_PINCTRL_FUNCTION(C1OUT, RPE3R, 14),
+			PIC32_PINCTRL_FUNCTION(REFCLKO3, RPE3R, 15)),
+	PIC32_PINCTRL_GROUP(69, E5,
+			PIC32_PINCTRL_FUNCTION(INT4, INT4R, 6),
+			PIC32_PINCTRL_FUNCTION(T5CK, T5CKR, 6),
+			PIC32_PINCTRL_FUNCTION(T7CK, T7CKR, 6),
+			PIC32_PINCTRL_FUNCTION(IC4, IC4R, 6),
+			PIC32_PINCTRL_FUNCTION(IC8, IC8R, 6),
+			PIC32_PINCTRL_FUNCTION(U3RX, U3RXR, 6),
+			PIC32_PINCTRL_FUNCTION(U4CTS, U4CTSR, 6),
+			PIC32_PINCTRL_FUNCTION(SDI2, SDI2R, 6),
+			PIC32_PINCTRL_FUNCTION(SDI4, SDI4R, 6),
+			PIC32_PINCTRL_FUNCTION(C1RX, C1RXR, 6),
+			PIC32_PINCTRL_FUNCTION(REFCLKI4, REFCLKI4R, 6),
+			PIC32_PINCTRL_FUNCTION(U1TX, RPE5R, 1),
+			PIC32_PINCTRL_FUNCTION(U2RTS, RPE5R, 2),
+			PIC32_PINCTRL_FUNCTION(U5TX, RPE5R, 3),
+			PIC32_PINCTRL_FUNCTION(U6RTS, RPE5R, 4),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPE5R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPE5R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPE5R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPE5R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPE5R, 9),
+			PIC32_PINCTRL_FUNCTION(OC4, RPE5R, 11),
+			PIC32_PINCTRL_FUNCTION(OC7, RPE5R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO1, RPE5R, 15)),
+	PIC32_PINCTRL_GROUP(72, E8,
+			PIC32_PINCTRL_FUNCTION(INT1, INT1R, 13),
+			PIC32_PINCTRL_FUNCTION(T4CK, T4CKR, 13),
+			PIC32_PINCTRL_FUNCTION(T9CK, T9CKR, 13),
+			PIC32_PINCTRL_FUNCTION(IC1, IC1R, 13),
+			PIC32_PINCTRL_FUNCTION(IC6, IC6R, 13),
+			PIC32_PINCTRL_FUNCTION(U3CTS, U3CTSR, 13),
+			PIC32_PINCTRL_FUNCTION(U4RX, U4RXR, 13),
+			PIC32_PINCTRL_FUNCTION(U6RX, U6RXR, 13),
+			PIC32_PINCTRL_FUNCTION(SS2IN, SS2INR, 13),
+			PIC32_PINCTRL_FUNCTION(SDI6, SDI6R, 13),
+			PIC32_PINCTRL_FUNCTION(OCFA, OCFAR, 13),
+			PIC32_PINCTRL_FUNCTION(REFCLKI3, REFCLKI3R, 13),
+			PIC32_PINCTRL_FUNCTION(U1RTS, RPE8R, 1),
+			PIC32_PINCTRL_FUNCTION(U2TX, RPE8R, 2),
+			PIC32_PINCTRL_FUNCTION(U5RTS, RPE8R, 3),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPE8R, 4),
+			PIC32_PINCTRL_FUNCTION(SS2OUT, RPE8R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPE8R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPE8R, 10),
+			PIC32_PINCTRL_FUNCTION(OC2, RPE8R, 11),
+			PIC32_PINCTRL_FUNCTION(OC1, RPE8R, 12),
+			PIC32_PINCTRL_FUNCTION(OC9, RPE8R, 13),
+			PIC32_PINCTRL_FUNCTION(C2TX, RPE8R, 15)),
+	PIC32_PINCTRL_GROUP(73, E9,
+			PIC32_PINCTRL_FUNCTION(INT2, INT2R, 13),
+			PIC32_PINCTRL_FUNCTION(T3CK, T3CKR, 13),
+			PIC32_PINCTRL_FUNCTION(T8CK, T8CKR, 13),
+			PIC32_PINCTRL_FUNCTION(IC2, IC2R, 13),
+			PIC32_PINCTRL_FUNCTION(IC5, IC5R, 13),
+			PIC32_PINCTRL_FUNCTION(IC9, IC9R, 13),
+			PIC32_PINCTRL_FUNCTION(U1CTS, U1CTSR, 13),
+			PIC32_PINCTRL_FUNCTION(U2RX, U2RXR, 13),
+			PIC32_PINCTRL_FUNCTION(U5CTS, U5CTSR, 13),
+			PIC32_PINCTRL_FUNCTION(SS1IN, SS1INR, 13),
+			PIC32_PINCTRL_FUNCTION(SS3IN, SS3INR, 13),
+			PIC32_PINCTRL_FUNCTION(SS4IN, SS4INR, 13),
+			PIC32_PINCTRL_FUNCTION(SS5IN, SS5INR, 13),
+			PIC32_PINCTRL_FUNCTION(C2RX, C2RXR, 13),
+			PIC32_PINCTRL_FUNCTION(U3RTS, RPE9R, 1),
+			PIC32_PINCTRL_FUNCTION(U4TX, RPE9R, 2),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPE9R, 4),
+			PIC32_PINCTRL_FUNCTION(SS1OUT, RPE9R, 5),
+			PIC32_PINCTRL_FUNCTION(SS3OUT, RPE9R, 7),
+			PIC32_PINCTRL_FUNCTION(SS4OUT, RPE9R, 8),
+			PIC32_PINCTRL_FUNCTION(SS5OUT, RPE9R, 9),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPE9R, 10),
+			PIC32_PINCTRL_FUNCTION(OC5, RPE9R, 11),
+			PIC32_PINCTRL_FUNCTION(OC8, RPE9R, 12),
+			PIC32_PINCTRL_FUNCTION(C1OUT, RPE9R, 14),
+			PIC32_PINCTRL_FUNCTION(REFCLKO3, RPE9R, 15)),
+	PIC32_PINCTRL_GROUP(80, F0,
+			PIC32_PINCTRL_FUNCTION(INT4, INT4R, 4),
+			PIC32_PINCTRL_FUNCTION(T5CK, T5CKR, 4),
+			PIC32_PINCTRL_FUNCTION(T7CK, T7CKR, 4),
+			PIC32_PINCTRL_FUNCTION(IC4, IC4R, 4),
+			PIC32_PINCTRL_FUNCTION(IC8, IC8R, 4),
+			PIC32_PINCTRL_FUNCTION(U3RX, U3RXR, 4),
+			PIC32_PINCTRL_FUNCTION(U4CTS, U4CTSR, 4),
+			PIC32_PINCTRL_FUNCTION(SDI2, SDI2R, 4),
+			PIC32_PINCTRL_FUNCTION(SDI4, SDI4R, 4),
+			PIC32_PINCTRL_FUNCTION(C1RX, C1RXR, 4),
+			PIC32_PINCTRL_FUNCTION(REFCLKI4, REFCLKI4R, 4),
+			PIC32_PINCTRL_FUNCTION(U1TX, RPF0R, 1),
+			PIC32_PINCTRL_FUNCTION(U2RTS, RPF0R, 2),
+			PIC32_PINCTRL_FUNCTION(U5TX, RPF0R, 3),
+			PIC32_PINCTRL_FUNCTION(U6RTS, RPF0R, 4),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPF0R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPF0R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPF0R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPF0R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPF0R, 9),
+			PIC32_PINCTRL_FUNCTION(OC4, RPF0R, 11),
+			PIC32_PINCTRL_FUNCTION(OC7, RPF0R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO1, RPF0R, 15)),
+	PIC32_PINCTRL_GROUP(81, F1,
+			PIC32_PINCTRL_FUNCTION(INT3, INT3R, 4),
+			PIC32_PINCTRL_FUNCTION(T2CK, T2CKR, 4),
+			PIC32_PINCTRL_FUNCTION(T6CK, T6CKR, 4),
+			PIC32_PINCTRL_FUNCTION(IC3, IC3R, 4),
+			PIC32_PINCTRL_FUNCTION(IC7, IC7R, 4),
+			PIC32_PINCTRL_FUNCTION(U1RX, U1RXR, 4),
+			PIC32_PINCTRL_FUNCTION(U2CTS, U2CTSR, 4),
+			PIC32_PINCTRL_FUNCTION(U5RX, U5RXR, 4),
+			PIC32_PINCTRL_FUNCTION(U6CTS, U6CTSR, 4),
+			PIC32_PINCTRL_FUNCTION(SDI1, SDI1R, 4),
+			PIC32_PINCTRL_FUNCTION(SDI3, SDI3R, 4),
+			PIC32_PINCTRL_FUNCTION(SDI5, SDI5R, 4),
+			PIC32_PINCTRL_FUNCTION(SS6IN, SS6INR, 4),
+			PIC32_PINCTRL_FUNCTION(REFCLKI1, REFCLKI1R, 4),
+			PIC32_PINCTRL_FUNCTION(U3TX, RPF1R, 1),
+			PIC32_PINCTRL_FUNCTION(U4RTS, RPF1R, 2),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPF1R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPF1R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPF1R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPF1R, 9),
+			PIC32_PINCTRL_FUNCTION(SS6OUT, RPF1R, 10),
+			PIC32_PINCTRL_FUNCTION(OC3, RPF1R, 11),
+			PIC32_PINCTRL_FUNCTION(OC6, RPF1R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO4, RPF1R, 13),
+			PIC32_PINCTRL_FUNCTION(C2OUT, RPF1R, 14),
+			PIC32_PINCTRL_FUNCTION(C1TX, RPF1R, 15)),
+	PIC32_PINCTRL_GROUP(82, F2,
+			PIC32_PINCTRL_FUNCTION(INT1, INT1R, 11),
+			PIC32_PINCTRL_FUNCTION(T4CK, T4CKR, 11),
+			PIC32_PINCTRL_FUNCTION(T9CK, T9CKR, 11),
+			PIC32_PINCTRL_FUNCTION(IC1, IC1R, 11),
+			PIC32_PINCTRL_FUNCTION(IC6, IC6R, 11),
+			PIC32_PINCTRL_FUNCTION(U3CTS, U3CTSR, 11),
+			PIC32_PINCTRL_FUNCTION(U4RX, U4RXR, 11),
+			PIC32_PINCTRL_FUNCTION(U6RX, U6RXR, 11),
+			PIC32_PINCTRL_FUNCTION(SS2IN, SS2INR, 11),
+			PIC32_PINCTRL_FUNCTION(SDI6, SDI6R, 11),
+			PIC32_PINCTRL_FUNCTION(OCFA, OCFAR, 11),
+			PIC32_PINCTRL_FUNCTION(REFCLKI3, REFCLKI3R, 11),
+			PIC32_PINCTRL_FUNCTION(U1RTS, RPF2R, 1),
+			PIC32_PINCTRL_FUNCTION(U2TX, RPF2R, 2),
+			PIC32_PINCTRL_FUNCTION(U5RTS, RPF2R, 3),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPF2R, 4),
+			PIC32_PINCTRL_FUNCTION(SS2OUT, RPF2R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPF2R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPF2R, 10),
+			PIC32_PINCTRL_FUNCTION(OC2, RPF2R, 11),
+			PIC32_PINCTRL_FUNCTION(OC1, RPF2R, 12),
+			PIC32_PINCTRL_FUNCTION(OC9, RPF2R, 13),
+			PIC32_PINCTRL_FUNCTION(C2TX, RPF2R, 15)),
+	PIC32_PINCTRL_GROUP(83, F3,
+			PIC32_PINCTRL_FUNCTION(INT1, INT1R, 8),
+			PIC32_PINCTRL_FUNCTION(T4CK, T4CKR, 8),
+			PIC32_PINCTRL_FUNCTION(T9CK, T9CKR, 8),
+			PIC32_PINCTRL_FUNCTION(IC1, IC1R, 8),
+			PIC32_PINCTRL_FUNCTION(IC6, IC6R, 8),
+			PIC32_PINCTRL_FUNCTION(U3CTS, U3CTSR, 8),
+			PIC32_PINCTRL_FUNCTION(U4RX, U4RXR, 8),
+			PIC32_PINCTRL_FUNCTION(U6RX, U6RXR, 8),
+			PIC32_PINCTRL_FUNCTION(SS2IN, SS2INR, 8),
+			PIC32_PINCTRL_FUNCTION(SDI6, SDI6R, 8),
+			PIC32_PINCTRL_FUNCTION(OCFA, OCFAR, 8),
+			PIC32_PINCTRL_FUNCTION(REFCLKI3, REFCLKI3R, 8),
+			PIC32_PINCTRL_FUNCTION(U1RTS, RPF3R, 1),
+			PIC32_PINCTRL_FUNCTION(U2TX, RPF3R, 2),
+			PIC32_PINCTRL_FUNCTION(U5RTS, RPF3R, 3),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPF3R, 4),
+			PIC32_PINCTRL_FUNCTION(SS2OUT, RPF3R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPF3R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPF3R, 10),
+			PIC32_PINCTRL_FUNCTION(OC2, RPF3R, 11),
+			PIC32_PINCTRL_FUNCTION(OC1, RPF3R, 12),
+			PIC32_PINCTRL_FUNCTION(OC9, RPF3R, 13),
+			PIC32_PINCTRL_FUNCTION(C2TX, RPF3R, 15)),
+	PIC32_PINCTRL_GROUP(84, F4,
+			PIC32_PINCTRL_FUNCTION(INT3, INT3R, 2),
+			PIC32_PINCTRL_FUNCTION(T2CK, T2CKR, 2),
+			PIC32_PINCTRL_FUNCTION(T6CK, T6CKR, 2),
+			PIC32_PINCTRL_FUNCTION(IC3, IC3R, 2),
+			PIC32_PINCTRL_FUNCTION(IC7, IC7R, 2),
+			PIC32_PINCTRL_FUNCTION(U1RX, U1RXR, 2),
+			PIC32_PINCTRL_FUNCTION(U2CTS, U2CTSR, 2),
+			PIC32_PINCTRL_FUNCTION(U5RX, U5RXR, 2),
+			PIC32_PINCTRL_FUNCTION(U6CTS, U6CTSR, 2),
+			PIC32_PINCTRL_FUNCTION(SDI1, SDI1R, 2),
+			PIC32_PINCTRL_FUNCTION(SDI3, SDI3R, 2),
+			PIC32_PINCTRL_FUNCTION(SDI5, SDI5R, 2),
+			PIC32_PINCTRL_FUNCTION(SS6IN, SS6INR, 2),
+			PIC32_PINCTRL_FUNCTION(REFCLKI1, REFCLKI1R, 2),
+			PIC32_PINCTRL_FUNCTION(U3TX, RPF4R, 1),
+			PIC32_PINCTRL_FUNCTION(U4RTS, RPF4R, 2),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPF4R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPF4R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPF4R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPF4R, 9),
+			PIC32_PINCTRL_FUNCTION(SS6OUT, RPF4R, 10),
+			PIC32_PINCTRL_FUNCTION(OC3, RPF4R, 11),
+			PIC32_PINCTRL_FUNCTION(OC6, RPF4R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO4, RPF4R, 13),
+			PIC32_PINCTRL_FUNCTION(C2OUT, RPF4R, 14),
+			PIC32_PINCTRL_FUNCTION(C1TX, RPF4R, 15)),
+	PIC32_PINCTRL_GROUP(85, F5,
+			PIC32_PINCTRL_FUNCTION(INT4, INT4R, 2),
+			PIC32_PINCTRL_FUNCTION(T5CK, T5CKR, 2),
+			PIC32_PINCTRL_FUNCTION(T7CK, T7CKR, 2),
+			PIC32_PINCTRL_FUNCTION(IC4, IC4R, 2),
+			PIC32_PINCTRL_FUNCTION(IC8, IC8R, 2),
+			PIC32_PINCTRL_FUNCTION(U3RX, U3RXR, 2),
+			PIC32_PINCTRL_FUNCTION(U4CTS, U4CTSR, 2),
+			PIC32_PINCTRL_FUNCTION(SDI2, SDI2R, 2),
+			PIC32_PINCTRL_FUNCTION(SDI4, SDI4R, 2),
+			PIC32_PINCTRL_FUNCTION(C1RX, C1RXR, 2),
+			PIC32_PINCTRL_FUNCTION(REFCLKI4, REFCLKI4R, 2),
+			PIC32_PINCTRL_FUNCTION(U1TX, RPF5R, 1),
+			PIC32_PINCTRL_FUNCTION(U2RTS, RPF5R, 2),
+			PIC32_PINCTRL_FUNCTION(U5TX, RPF5R, 3),
+			PIC32_PINCTRL_FUNCTION(U6RTS, RPF5R, 4),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPF5R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPF5R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPF5R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPF5R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPF5R, 9),
+			PIC32_PINCTRL_FUNCTION(OC4, RPF5R, 11),
+			PIC32_PINCTRL_FUNCTION(OC7, RPF5R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO1, RPF5R, 15)),
+	PIC32_PINCTRL_GROUP(88, F8,
+			PIC32_PINCTRL_FUNCTION(INT2, INT2R, 11),
+			PIC32_PINCTRL_FUNCTION(T3CK, T3CKR, 11),
+			PIC32_PINCTRL_FUNCTION(T8CK, T8CKR, 11),
+			PIC32_PINCTRL_FUNCTION(IC2, IC2R, 11),
+			PIC32_PINCTRL_FUNCTION(IC5, IC5R, 11),
+			PIC32_PINCTRL_FUNCTION(IC9, IC9R, 11),
+			PIC32_PINCTRL_FUNCTION(U1CTS, U1CTSR, 11),
+			PIC32_PINCTRL_FUNCTION(U2RX, U2RXR, 11),
+			PIC32_PINCTRL_FUNCTION(U5CTS, U5CTSR, 11),
+			PIC32_PINCTRL_FUNCTION(SS1IN, SS1INR, 11),
+			PIC32_PINCTRL_FUNCTION(SS3IN, SS3INR, 11),
+			PIC32_PINCTRL_FUNCTION(SS4IN, SS4INR, 11),
+			PIC32_PINCTRL_FUNCTION(SS5IN, SS5INR, 11),
+			PIC32_PINCTRL_FUNCTION(C2RX, C2RXR, 11),
+			PIC32_PINCTRL_FUNCTION(U3RTS, RPF8R, 1),
+			PIC32_PINCTRL_FUNCTION(U4TX, RPF8R, 2),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPF8R, 4),
+			PIC32_PINCTRL_FUNCTION(SS1OUT, RPF8R, 5),
+			PIC32_PINCTRL_FUNCTION(SS3OUT, RPF8R, 7),
+			PIC32_PINCTRL_FUNCTION(SS4OUT, RPF8R, 8),
+			PIC32_PINCTRL_FUNCTION(SS5OUT, RPF8R, 9),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPF8R, 10),
+			PIC32_PINCTRL_FUNCTION(OC5, RPF8R, 11),
+			PIC32_PINCTRL_FUNCTION(OC8, RPF8R, 12),
+			PIC32_PINCTRL_FUNCTION(C1OUT, RPF8R, 14),
+			PIC32_PINCTRL_FUNCTION(REFCLKO3, RPF8R, 15)),
+	PIC32_PINCTRL_GROUP(92, F12,
+			PIC32_PINCTRL_FUNCTION(INT2, INT2R, 9),
+			PIC32_PINCTRL_FUNCTION(T3CK, T3CKR, 9),
+			PIC32_PINCTRL_FUNCTION(T8CK, T8CKR, 9),
+			PIC32_PINCTRL_FUNCTION(IC2, IC2R, 9),
+			PIC32_PINCTRL_FUNCTION(IC5, IC5R, 9),
+			PIC32_PINCTRL_FUNCTION(IC9, IC9R, 9),
+			PIC32_PINCTRL_FUNCTION(U1CTS, U1CTSR, 9),
+			PIC32_PINCTRL_FUNCTION(U2RX, U2RXR, 9),
+			PIC32_PINCTRL_FUNCTION(U5CTS, U5CTSR, 9),
+			PIC32_PINCTRL_FUNCTION(SS1IN, SS1INR, 9),
+			PIC32_PINCTRL_FUNCTION(SS3IN, SS3INR, 9),
+			PIC32_PINCTRL_FUNCTION(SS4IN, SS4INR, 9),
+			PIC32_PINCTRL_FUNCTION(SS5IN, SS5INR, 9),
+			PIC32_PINCTRL_FUNCTION(C2RX, C2RXR, 9),
+			PIC32_PINCTRL_FUNCTION(U3RTS, RPF12R, 1),
+			PIC32_PINCTRL_FUNCTION(U4TX, RPF12R, 2),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPF12R, 4),
+			PIC32_PINCTRL_FUNCTION(SS1OUT, RPF12R, 5),
+			PIC32_PINCTRL_FUNCTION(SS3OUT, RPF12R, 7),
+			PIC32_PINCTRL_FUNCTION(SS4OUT, RPF12R, 8),
+			PIC32_PINCTRL_FUNCTION(SS5OUT, RPF12R, 9),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPF12R, 10),
+			PIC32_PINCTRL_FUNCTION(OC5, RPF12R, 11),
+			PIC32_PINCTRL_FUNCTION(OC8, RPF12R, 12),
+			PIC32_PINCTRL_FUNCTION(C1OUT, RPF12R, 14),
+			PIC32_PINCTRL_FUNCTION(REFCLKO3, RPF12R, 15)),
+	PIC32_PINCTRL_GROUP(93, F13,
+			PIC32_PINCTRL_FUNCTION(INT1, INT1R, 9),
+			PIC32_PINCTRL_FUNCTION(T4CK, T4CKR, 9),
+			PIC32_PINCTRL_FUNCTION(T9CK, T9CKR, 9),
+			PIC32_PINCTRL_FUNCTION(IC1, IC1R, 9),
+			PIC32_PINCTRL_FUNCTION(IC6, IC6R, 9),
+			PIC32_PINCTRL_FUNCTION(U3CTS, U3CTSR, 9),
+			PIC32_PINCTRL_FUNCTION(U4RX, U4RXR, 9),
+			PIC32_PINCTRL_FUNCTION(U6RX, U6RXR, 9),
+			PIC32_PINCTRL_FUNCTION(SS2IN, SS2INR, 9),
+			PIC32_PINCTRL_FUNCTION(SDI6, SDI6R, 9),
+			PIC32_PINCTRL_FUNCTION(OCFA, OCFAR, 9),
+			PIC32_PINCTRL_FUNCTION(REFCLKI3, REFCLKI3R, 9),
+			PIC32_PINCTRL_FUNCTION(U1RTS, RPF13R, 1),
+			PIC32_PINCTRL_FUNCTION(U2TX, RPF13R, 2),
+			PIC32_PINCTRL_FUNCTION(U5RTS, RPF13R, 3),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPF13R, 4),
+			PIC32_PINCTRL_FUNCTION(SS2OUT, RPF13R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPF13R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPF13R, 10),
+			PIC32_PINCTRL_FUNCTION(OC2, RPF13R, 11),
+			PIC32_PINCTRL_FUNCTION(OC1, RPF13R, 12),
+			PIC32_PINCTRL_FUNCTION(OC9, RPF13R, 13),
+			PIC32_PINCTRL_FUNCTION(C2TX, RPF13R, 15)),
+	PIC32_PINCTRL_GROUP(96, G0,
+			PIC32_PINCTRL_FUNCTION(INT4, INT4R, 12),
+			PIC32_PINCTRL_FUNCTION(T5CK, T5CKR, 12),
+			PIC32_PINCTRL_FUNCTION(T7CK, T7CKR, 12),
+			PIC32_PINCTRL_FUNCTION(IC4, IC4R, 12),
+			PIC32_PINCTRL_FUNCTION(IC8, IC8R, 12),
+			PIC32_PINCTRL_FUNCTION(U3RX, U3RXR, 12),
+			PIC32_PINCTRL_FUNCTION(U4CTS, U4CTSR, 12),
+			PIC32_PINCTRL_FUNCTION(SDI2, SDI2R, 12),
+			PIC32_PINCTRL_FUNCTION(SDI4, SDI4R, 12),
+			PIC32_PINCTRL_FUNCTION(C1RX, C1RXR, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKI4, REFCLKI4R, 12),
+			PIC32_PINCTRL_FUNCTION(U1TX, RPG0R, 1),
+			PIC32_PINCTRL_FUNCTION(U2RTS, RPG0R, 2),
+			PIC32_PINCTRL_FUNCTION(U5TX, RPG0R, 3),
+			PIC32_PINCTRL_FUNCTION(U6RTS, RPG0R, 4),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPG0R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPG0R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPG0R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPG0R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPG0R, 9),
+			PIC32_PINCTRL_FUNCTION(OC4, RPG0R, 11),
+			PIC32_PINCTRL_FUNCTION(OC7, RPG0R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO1, RPG0R, 15)),
+	PIC32_PINCTRL_GROUP(97, G1,
+			PIC32_PINCTRL_FUNCTION(INT3, INT3R, 12),
+			PIC32_PINCTRL_FUNCTION(T2CK, T2CKR, 12),
+			PIC32_PINCTRL_FUNCTION(T6CK, T6CKR, 12),
+			PIC32_PINCTRL_FUNCTION(IC3, IC3R, 12),
+			PIC32_PINCTRL_FUNCTION(IC7, IC7R, 12),
+			PIC32_PINCTRL_FUNCTION(U1RX, U1RXR, 12),
+			PIC32_PINCTRL_FUNCTION(U2CTS, U2CTSR, 12),
+			PIC32_PINCTRL_FUNCTION(U5RX, U5RXR, 12),
+			PIC32_PINCTRL_FUNCTION(U6CTS, U6CTSR, 12),
+			PIC32_PINCTRL_FUNCTION(SDI1, SDI1R, 12),
+			PIC32_PINCTRL_FUNCTION(SDI3, SDI3R, 12),
+			PIC32_PINCTRL_FUNCTION(SDI5, SDI5R, 12),
+			PIC32_PINCTRL_FUNCTION(SS6IN, SS6INR, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKI1, REFCLKI1R, 12),
+			PIC32_PINCTRL_FUNCTION(U3TX, RPG1R, 1),
+			PIC32_PINCTRL_FUNCTION(U4RTS, RPG1R, 2),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPG1R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPG1R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPG1R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPG1R, 9),
+			PIC32_PINCTRL_FUNCTION(SS6OUT, RPG1R, 10),
+			PIC32_PINCTRL_FUNCTION(OC3, RPG1R, 11),
+			PIC32_PINCTRL_FUNCTION(OC6, RPG1R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO4, RPG1R, 13),
+			PIC32_PINCTRL_FUNCTION(C2OUT, RPG1R, 14),
+			PIC32_PINCTRL_FUNCTION(C1TX, RPG1R, 15)),
+	PIC32_PINCTRL_GROUP(102, G6,
+			PIC32_PINCTRL_FUNCTION(INT2, INT2R, 1),
+			PIC32_PINCTRL_FUNCTION(T3CK, T3CKR, 1),
+			PIC32_PINCTRL_FUNCTION(T8CK, T8CKR, 1),
+			PIC32_PINCTRL_FUNCTION(IC2, IC2R, 1),
+			PIC32_PINCTRL_FUNCTION(IC5, IC5R, 1),
+			PIC32_PINCTRL_FUNCTION(IC9, IC9R, 1),
+			PIC32_PINCTRL_FUNCTION(U1CTS, U1CTSR, 1),
+			PIC32_PINCTRL_FUNCTION(U2RX, U2RXR, 1),
+			PIC32_PINCTRL_FUNCTION(U5CTS, U5CTSR, 1),
+			PIC32_PINCTRL_FUNCTION(SS1IN, SS1INR, 1),
+			PIC32_PINCTRL_FUNCTION(SS3IN, SS3INR, 1),
+			PIC32_PINCTRL_FUNCTION(SS4IN, SS4INR, 1),
+			PIC32_PINCTRL_FUNCTION(SS5IN, SS5INR, 1),
+			PIC32_PINCTRL_FUNCTION(C2RX, C2RXR, 1),
+			PIC32_PINCTRL_FUNCTION(U3RTS, RPG6R, 1),
+			PIC32_PINCTRL_FUNCTION(U4TX, RPG6R, 2),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPG6R, 4),
+			PIC32_PINCTRL_FUNCTION(SS1OUT, RPG6R, 5),
+			PIC32_PINCTRL_FUNCTION(SS3OUT, RPG6R, 7),
+			PIC32_PINCTRL_FUNCTION(SS4OUT, RPG6R, 8),
+			PIC32_PINCTRL_FUNCTION(SS5OUT, RPG6R, 9),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPG6R, 10),
+			PIC32_PINCTRL_FUNCTION(OC5, RPG6R, 11),
+			PIC32_PINCTRL_FUNCTION(OC8, RPG6R, 12),
+			PIC32_PINCTRL_FUNCTION(C1OUT, RPG6R, 14),
+			PIC32_PINCTRL_FUNCTION(REFCLKO3, RPG6R, 15)),
+	PIC32_PINCTRL_GROUP(103, G7,
+			PIC32_PINCTRL_FUNCTION(INT4, INT4R, 1),
+			PIC32_PINCTRL_FUNCTION(T5CK, T5CKR, 1),
+			PIC32_PINCTRL_FUNCTION(T7CK, T7CKR, 1),
+			PIC32_PINCTRL_FUNCTION(IC4, IC4R, 1),
+			PIC32_PINCTRL_FUNCTION(IC8, IC8R, 1),
+			PIC32_PINCTRL_FUNCTION(U3RX, U3RXR, 1),
+			PIC32_PINCTRL_FUNCTION(U4CTS, U4CTSR, 1),
+			PIC32_PINCTRL_FUNCTION(SDI2, SDI2R, 1),
+			PIC32_PINCTRL_FUNCTION(SDI4, SDI4R, 1),
+			PIC32_PINCTRL_FUNCTION(C1RX, C1RXR, 1),
+			PIC32_PINCTRL_FUNCTION(REFCLKI4, REFCLKI4R, 1),
+			PIC32_PINCTRL_FUNCTION(U1TX, RPG7R, 1),
+			PIC32_PINCTRL_FUNCTION(U2RTS, RPG7R, 2),
+			PIC32_PINCTRL_FUNCTION(U5TX, RPG7R, 3),
+			PIC32_PINCTRL_FUNCTION(U6RTS, RPG7R, 4),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPG7R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPG7R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPG7R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPG7R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPG7R, 9),
+			PIC32_PINCTRL_FUNCTION(OC4, RPG7R, 11),
+			PIC32_PINCTRL_FUNCTION(OC7, RPG7R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO1, RPG7R, 15)),
+	PIC32_PINCTRL_GROUP(104, G8,
+			PIC32_PINCTRL_FUNCTION(INT3, INT3R, 1),
+			PIC32_PINCTRL_FUNCTION(T2CK, T2CKR, 1),
+			PIC32_PINCTRL_FUNCTION(T6CK, T6CKR, 1),
+			PIC32_PINCTRL_FUNCTION(IC3, IC3R, 1),
+			PIC32_PINCTRL_FUNCTION(IC7, IC7R, 1),
+			PIC32_PINCTRL_FUNCTION(U1RX, U1RXR, 1),
+			PIC32_PINCTRL_FUNCTION(U2CTS, U2CTSR, 1),
+			PIC32_PINCTRL_FUNCTION(U5RX, U5RXR, 1),
+			PIC32_PINCTRL_FUNCTION(U6CTS, U6CTSR, 1),
+			PIC32_PINCTRL_FUNCTION(SDI1, SDI1R, 1),
+			PIC32_PINCTRL_FUNCTION(SDI3, SDI3R, 1),
+			PIC32_PINCTRL_FUNCTION(SDI5, SDI5R, 1),
+			PIC32_PINCTRL_FUNCTION(SS6IN, SS6INR, 1),
+			PIC32_PINCTRL_FUNCTION(REFCLKI1, REFCLKI1R, 1),
+			PIC32_PINCTRL_FUNCTION(U3TX, RPG8R, 1),
+			PIC32_PINCTRL_FUNCTION(U4RTS, RPG8R, 2),
+			PIC32_PINCTRL_FUNCTION(SDO1, RPG8R, 5),
+			PIC32_PINCTRL_FUNCTION(SDO2, RPG8R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO3, RPG8R, 7),
+			PIC32_PINCTRL_FUNCTION(SDO5, RPG8R, 9),
+			PIC32_PINCTRL_FUNCTION(SS6OUT, RPG8R, 10),
+			PIC32_PINCTRL_FUNCTION(OC3, RPG8R, 11),
+			PIC32_PINCTRL_FUNCTION(OC6, RPG8R, 12),
+			PIC32_PINCTRL_FUNCTION(REFCLKO4, RPG8R, 13),
+			PIC32_PINCTRL_FUNCTION(C2OUT, RPG8R, 14),
+			PIC32_PINCTRL_FUNCTION(C1TX, RPG8R, 15)),
+	PIC32_PINCTRL_GROUP(105, G9,
+			PIC32_PINCTRL_FUNCTION(INT1, INT1R, 1),
+			PIC32_PINCTRL_FUNCTION(T4CK, T4CKR, 1),
+			PIC32_PINCTRL_FUNCTION(T9CK, T9CKR, 1),
+			PIC32_PINCTRL_FUNCTION(IC1, IC1R, 1),
+			PIC32_PINCTRL_FUNCTION(IC6, IC6R, 1),
+			PIC32_PINCTRL_FUNCTION(U3CTS, U3CTSR, 1),
+			PIC32_PINCTRL_FUNCTION(U4RX, U4RXR, 1),
+			PIC32_PINCTRL_FUNCTION(U6RX, U6RXR, 1),
+			PIC32_PINCTRL_FUNCTION(SS2IN, SS2INR, 1),
+			PIC32_PINCTRL_FUNCTION(SDI6, SDI6R, 1),
+			PIC32_PINCTRL_FUNCTION(OCFA, OCFAR, 1),
+			PIC32_PINCTRL_FUNCTION(REFCLKI3, REFCLKI3R, 1),
+			PIC32_PINCTRL_FUNCTION(U1RTS, RPG9R, 1),
+			PIC32_PINCTRL_FUNCTION(U2TX, RPG9R, 2),
+			PIC32_PINCTRL_FUNCTION(U5RTS, RPG9R, 3),
+			PIC32_PINCTRL_FUNCTION(U6TX, RPG9R, 4),
+			PIC32_PINCTRL_FUNCTION(SS2OUT, RPG9R, 6),
+			PIC32_PINCTRL_FUNCTION(SDO4, RPG9R, 8),
+			PIC32_PINCTRL_FUNCTION(SDO6, RPG9R, 10),
+			PIC32_PINCTRL_FUNCTION(OC2, RPG9R, 11),
+			PIC32_PINCTRL_FUNCTION(OC1, RPG9R, 12),
+			PIC32_PINCTRL_FUNCTION(OC9, RPG9R, 13),
+			PIC32_PINCTRL_FUNCTION(C2TX, RPG9R, 15)),
+};
+
+static inline u32 pctl_readl(struct pic32_pinctrl *pctl, u32 reg)
+{
+	return readl(pctl->reg_base + reg);
+}
+
+static inline void pctl_writel(struct pic32_pinctrl *pctl, u32 val, u32 reg)
+{
+	writel(val, pctl->reg_base + reg);
+}
+
+static inline struct pic32_gpio_bank *gc_to_bank(struct gpio_chip *gc)
+{
+	return container_of(gc, struct pic32_gpio_bank, gpio_chip);
+}
+
+static inline struct pic32_gpio_bank *irqd_to_bank(struct irq_data *d)
+{
+	return gc_to_bank(irq_data_get_irq_chip_data(d));
+}
+
+static inline struct pic32_gpio_bank *pctl_to_bank(struct pic32_pinctrl *pctl,
+						unsigned pin)
+{
+	return &pctl->gpio_banks[pin / PINS_PER_BANK];
+}
+
+static inline u32 gpio_readl(struct pic32_gpio_bank *bank, u32 reg)
+{
+	return readl(bank->reg_base + reg);
+}
+
+static inline void gpio_writel(struct pic32_gpio_bank *bank, u32 val,
+			       u32 reg)
+{
+	writel(val, bank->reg_base + reg);
+}
+
+static int pic32_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+	struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+
+	return pctl->ngroups;
+}
+
+static const char *pic32_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
+						    unsigned group)
+{
+	struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+
+	return pctl->groups[group].name;
+}
+
+static int pic32_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
+					    unsigned group,
+					    const unsigned **pins,
+					    unsigned *num_pins)
+{
+	struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+
+	*pins = &pctl->groups[group].pin;
+	*num_pins = 1;
+
+	return 0;
+}
+
+static const struct pinctrl_ops pic32_pinctrl_ops = {
+	.get_groups_count = pic32_pinctrl_get_groups_count,
+	.get_group_name = pic32_pinctrl_get_group_name,
+	.get_group_pins = pic32_pinctrl_get_group_pins,
+	.dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
+	.dt_free_map = pinctrl_utils_dt_free_map,
+};
+
+static int pic32_pinmux_get_functions_count(struct pinctrl_dev *pctldev)
+{
+	struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+
+	return pctl->nfunctions;
+}
+
+static const char *
+pic32_pinmux_get_function_name(struct pinctrl_dev *pctldev, unsigned func)
+{
+	struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+
+	return pctl->functions[func].name;
+}
+
+static int pic32_pinmux_get_function_groups(struct pinctrl_dev *pctldev,
+						unsigned func,
+						const char * const **groups,
+						unsigned * const num_groups)
+{
+	struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+
+	*groups = pctl->functions[func].groups;
+	*num_groups = pctl->functions[func].ngroups;
+
+	return 0;
+}
+
+static int pic32_pinmux_enable(struct pinctrl_dev *pctldev,
+				   unsigned func, unsigned group)
+{
+	struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	const struct pic32_pin_group *pg = &pctl->groups[group];
+	const struct pic32_function *pf = &pctl->functions[func];
+	const char *fname = pf->name;
+	struct pic32_desc_function *functions = pg->functions;
+
+	while (functions->name) {
+		if (!strcmp(functions->name, fname)) {
+			dev_dbg(pctl->dev,
+				"setting function %s reg 0x%x = %d\n",
+				fname, functions->muxreg, functions->muxval);
+
+			pctl_writel(pctl, functions->muxval, functions->muxreg);
+
+			return 0;
+		}
+
+		functions++;
+	}
+
+	dev_err(pctl->dev, "cannot mux pin %u to function %u\n", group, func);
+
+	return -EINVAL;
+}
+
+static int pic32_gpio_request_enable(struct pinctrl_dev *pctldev,
+				     struct pinctrl_gpio_range *range,
+				     unsigned offset)
+{
+	struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	struct pic32_gpio_bank *bank = gc_to_bank(range->gc);
+	u32 mask = BIT(offset - bank->gpio_chip.base);
+
+	dev_dbg(pctl->dev, "requesting gpio %d in bank %d with mask 0x%x\n",
+		offset, bank->gpio_chip.base, mask);
+
+	gpio_writel(bank, mask, PIC32_CLR(ANSEL_REG));
+
+	return 0;
+}
+
+static int pic32_gpio_direction_input(struct gpio_chip *chip,
+					  unsigned offset)
+{
+	struct pic32_gpio_bank *bank = gc_to_bank(chip);
+	u32 mask = BIT(offset);
+
+	gpio_writel(bank, mask, PIC32_SET(TRIS_REG));
+
+	return 0;
+}
+
+static int pic32_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct pic32_gpio_bank *bank = gc_to_bank(chip);
+
+	return !!(gpio_readl(bank, PORT_REG) & BIT(offset));
+}
+
+static void pic32_gpio_set(struct gpio_chip *chip, unsigned offset,
+			       int value)
+{
+	struct pic32_gpio_bank *bank = gc_to_bank(chip);
+	u32 mask = BIT(offset);
+
+	if (value)
+		gpio_writel(bank, mask, PIC32_SET(PORT_REG));
+	else
+		gpio_writel(bank, mask, PIC32_CLR(PORT_REG));
+}
+
+static int pic32_gpio_direction_output(struct gpio_chip *chip,
+					   unsigned offset, int value)
+{
+	struct pic32_gpio_bank *bank = gc_to_bank(chip);
+	u32 mask = BIT(offset);
+
+	pic32_gpio_set(chip, offset, value);
+	gpio_writel(bank, mask, PIC32_CLR(TRIS_REG));
+
+	return 0;
+}
+
+static int pic32_gpio_set_direction(struct pinctrl_dev *pctldev,
+					      struct pinctrl_gpio_range *range,
+					      unsigned offset, bool input)
+{
+	struct gpio_chip *chip = range->gc;
+
+	if (input)
+		pic32_gpio_direction_input(chip, offset);
+	else
+		pic32_gpio_direction_output(chip, offset, 0);
+
+	return 0;
+}
+
+static const struct pinmux_ops pic32_pinmux_ops = {
+	.get_functions_count = pic32_pinmux_get_functions_count,
+	.get_function_name = pic32_pinmux_get_function_name,
+	.get_function_groups = pic32_pinmux_get_function_groups,
+	.set_mux = pic32_pinmux_enable,
+	.gpio_request_enable = pic32_gpio_request_enable,
+	.gpio_set_direction = pic32_gpio_set_direction,
+};
+
+static int pic32_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin,
+				 unsigned long *config)
+{
+	struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	struct pic32_gpio_bank *bank = pctl_to_bank(pctl, pin);
+	unsigned param = pinconf_to_config_param(*config);
+	u32 mask = BIT(pin - bank->gpio_chip.base);
+	u32 arg;
+
+	switch (param) {
+	case PIN_CONFIG_BIAS_PULL_UP:
+		arg = !!(gpio_readl(bank, CNPU_REG) & mask);
+		break;
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		arg = !!(gpio_readl(bank, CNPD_REG) & mask);
+		break;
+	case PIN_CONFIG_MICROCHIP_DIGITAL:
+		arg = !(gpio_readl(bank, ANSEL_REG) & mask);
+		break;
+	case PIN_CONFIG_MICROCHIP_ANALOG:
+		arg = !!(gpio_readl(bank, ANSEL_REG) & mask);
+		break;
+	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+		arg = !!(gpio_readl(bank, ODCU_REG) & mask);
+		break;
+	case PIN_CONFIG_INPUT_ENABLE:
+		arg = !!(gpio_readl(bank, TRIS_REG) & mask);
+		break;
+	case PIN_CONFIG_OUTPUT:
+		arg = !(gpio_readl(bank, TRIS_REG) & mask);
+		break;
+	default:
+		dev_err(pctl->dev, "Property %u not supported\n", param);
+		return -ENOTSUPP;
+	}
+
+	*config = pinconf_to_config_packed(param, arg);
+
+	return 0;
+}
+
+static int pic32_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin,
+				 unsigned long *configs, unsigned num_configs)
+{
+	struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	struct pic32_gpio_bank *bank = pctl_to_bank(pctl, pin);
+	unsigned param;
+	u32 arg;
+	unsigned int i;
+	u32 offset = pin - bank->gpio_chip.base;
+	u32 mask = BIT(offset);
+
+	dev_dbg(pctl->dev, "setting pin %d bank %d mask 0x%x\n",
+		pin, bank->gpio_chip.base, mask);
+
+	for (i = 0; i < num_configs; i++) {
+		param = pinconf_to_config_param(configs[i]);
+		arg = pinconf_to_config_argument(configs[i]);
+
+		switch (param) {
+		case PIN_CONFIG_BIAS_PULL_UP:
+			dev_dbg(pctl->dev, "   pullup\n");
+			gpio_writel(bank, mask, PIC32_SET(CNPU_REG));
+			break;
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+			dev_dbg(pctl->dev, "   pulldown\n");
+			gpio_writel(bank, mask, PIC32_SET(CNPD_REG));
+			break;
+		case PIN_CONFIG_MICROCHIP_DIGITAL:
+			dev_dbg(pctl->dev, "   digital\n");
+			gpio_writel(bank, mask, PIC32_CLR(ANSEL_REG));
+			break;
+		case PIN_CONFIG_MICROCHIP_ANALOG:
+			dev_dbg(pctl->dev, "   analog\n");
+			gpio_writel(bank, mask, PIC32_SET(ANSEL_REG));
+			break;
+		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+			dev_dbg(pctl->dev, "   opendrain\n");
+			gpio_writel(bank, mask, PIC32_SET(ODCU_REG));
+			break;
+		case PIN_CONFIG_INPUT_ENABLE:
+			pic32_gpio_direction_input(&bank->gpio_chip, offset);
+			break;
+		case PIN_CONFIG_OUTPUT:
+			pic32_gpio_direction_output(&bank->gpio_chip,
+						    offset, arg);
+			break;
+		default:
+			dev_err(pctl->dev, "Property %u not supported\n",
+				param);
+			return -ENOTSUPP;
+		}
+	}
+
+	return 0;
+}
+
+static const struct pinconf_ops pic32_pinconf_ops = {
+	.pin_config_get = pic32_pinconf_get,
+	.pin_config_set = pic32_pinconf_set,
+	.is_generic = true,
+};
+
+static struct pinctrl_desc pic32_pinctrl_desc = {
+	.name = "pic32-pinctrl",
+	.pctlops = &pic32_pinctrl_ops,
+	.pmxops = &pic32_pinmux_ops,
+	.confops = &pic32_pinconf_ops,
+	.owner = THIS_MODULE,
+};
+
+static int pic32_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+	struct pic32_gpio_bank *bank = gc_to_bank(chip);
+
+	return !!(gpio_readl(bank, TRIS_REG) & BIT(offset));
+}
+
+static void pic32_gpio_irq_ack(struct irq_data *data)
+{
+	struct pic32_gpio_bank *bank = irqd_to_bank(data);
+
+	gpio_writel(bank, 0, CNF_REG);
+}
+
+static void pic32_gpio_irq_mask(struct irq_data *data)
+{
+	struct pic32_gpio_bank *bank = irqd_to_bank(data);
+
+	gpio_writel(bank, BIT(PIC32_CNCON_ON), PIC32_CLR(CNCON_REG));
+}
+
+static void pic32_gpio_irq_unmask(struct irq_data *data)
+{
+	struct pic32_gpio_bank *bank = irqd_to_bank(data);
+
+	gpio_writel(bank, BIT(PIC32_CNCON_ON), PIC32_SET(CNCON_REG));
+}
+
+static unsigned int pic32_gpio_irq_startup(struct irq_data *data)
+{
+	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+
+	pic32_gpio_direction_input(chip, data->hwirq);
+	pic32_gpio_irq_unmask(data);
+
+	return 0;
+}
+
+static int pic32_gpio_irq_set_type(struct irq_data *data, unsigned int type)
+{
+	struct pic32_gpio_bank *bank = irqd_to_bank(data);
+	u32 mask = BIT(data->hwirq);
+
+	switch (type & IRQ_TYPE_SENSE_MASK) {
+	case IRQ_TYPE_EDGE_RISING:
+		/* enable RISE */
+		gpio_writel(bank, mask, PIC32_SET(CNEN_REG));
+		/* disable FALL */
+		gpio_writel(bank, mask, PIC32_CLR(CNNE_REG));
+		/* enable EDGE */
+		gpio_writel(bank, BIT(PIC32_CNCON_EDGE), PIC32_SET(CNCON_REG));
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		/* disable RISE */
+		gpio_writel(bank, mask, PIC32_CLR(CNEN_REG));
+		/* enable FALL */
+		gpio_writel(bank, mask, PIC32_SET(CNNE_REG));
+		/* enable EDGE */
+		gpio_writel(bank, BIT(PIC32_CNCON_EDGE), PIC32_SET(CNCON_REG));
+		break;
+	case IRQ_TYPE_EDGE_BOTH:
+		/* enable RISE */
+		gpio_writel(bank, mask, PIC32_SET(CNEN_REG));
+		/* enable FALL */
+		gpio_writel(bank, mask, PIC32_SET(CNNE_REG));
+		/* enable EDGE */
+		gpio_writel(bank, BIT(PIC32_CNCON_EDGE), PIC32_SET(CNCON_REG));
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	irq_set_handler_locked(data, handle_edge_irq);
+
+	return 0;
+}
+
+static u32 pic32_gpio_get_pending(struct gpio_chip *gc, unsigned long status)
+{
+	struct pic32_gpio_bank *bank = gc_to_bank(gc);
+	u32 pending = 0;
+	u32 cnen_rise, cnne_fall;
+	u32 pin;
+
+	cnen_rise = gpio_readl(bank, CNEN_REG);
+	cnne_fall = gpio_readl(bank, CNNE_REG);
+
+	for_each_set_bit(pin, &status, BITS_PER_LONG) {
+		u32 mask = BIT(pin);
+
+		if ((mask & cnen_rise) || (mask && cnne_fall))
+			pending |= mask;
+	}
+
+	return pending;
+}
+
+static void pic32_gpio_irq_handler(struct irq_desc *desc)
+{
+	struct gpio_chip *gc = irq_desc_get_handler_data(desc);
+	struct pic32_gpio_bank *bank = gc_to_bank(gc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	unsigned long pending;
+	unsigned int pin;
+	u32 stat;
+
+	chained_irq_enter(chip, desc);
+
+	stat = gpio_readl(bank, CNF_REG);
+	pending = pic32_gpio_get_pending(gc, stat);
+
+	for_each_set_bit(pin, &pending, BITS_PER_LONG)
+		generic_handle_irq(irq_linear_revmap(gc->irqdomain, pin));
+
+	chained_irq_exit(chip, desc);
+}
+
+#define GPIO_BANK(_bank, _npins)					\
+	{								\
+		.gpio_chip = {						\
+			.label = "GPIO" #_bank,				\
+			.request = gpiochip_generic_request,		\
+			.free = gpiochip_generic_free,			\
+			.get_direction = pic32_gpio_get_direction,	\
+			.direction_input = pic32_gpio_direction_input,	\
+			.direction_output = pic32_gpio_direction_output, \
+			.get = pic32_gpio_get,				\
+			.set = pic32_gpio_set,				\
+			.ngpio = _npins,				\
+			.base = GPIO_BANK_START(_bank),			\
+			.owner = THIS_MODULE,				\
+			.can_sleep = 0,					\
+		},							\
+		.irq_chip = {						\
+			.name = "GPIO" #_bank,				\
+			.irq_startup = pic32_gpio_irq_startup,	\
+			.irq_ack = pic32_gpio_irq_ack,		\
+			.irq_mask = pic32_gpio_irq_mask,		\
+			.irq_unmask = pic32_gpio_irq_unmask,		\
+			.irq_set_type = pic32_gpio_irq_set_type,	\
+		},							\
+	}
+
+static struct pic32_gpio_bank pic32_gpio_banks[] = {
+	GPIO_BANK(0, PINS_PER_BANK),
+	GPIO_BANK(1, PINS_PER_BANK),
+	GPIO_BANK(2, PINS_PER_BANK),
+	GPIO_BANK(3, PINS_PER_BANK),
+	GPIO_BANK(4, PINS_PER_BANK),
+	GPIO_BANK(5, PINS_PER_BANK),
+	GPIO_BANK(6, PINS_PER_BANK),
+	GPIO_BANK(7, PINS_PER_BANK),
+	GPIO_BANK(8, PINS_PER_BANK),
+	GPIO_BANK(9, PINS_PER_BANK),
+};
+
+static int pic32_pinctrl_probe(struct platform_device *pdev)
+{
+	struct pic32_pinctrl *pctl;
+	struct resource *res;
+	int ret;
+
+	pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
+	if (!pctl)
+		return -ENOMEM;
+	pctl->dev = &pdev->dev;
+	dev_set_drvdata(&pdev->dev, pctl);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	pctl->reg_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(pctl->reg_base))
+		return PTR_ERR(pctl->reg_base);
+
+	pctl->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(pctl->clk)) {
+		ret = PTR_ERR(pctl->clk);
+		dev_err(&pdev->dev, "clk get failed\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(pctl->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "clk enable failed\n");
+		return ret;
+	}
+
+	pctl->pins = pic32_pins;
+	pctl->npins = ARRAY_SIZE(pic32_pins);
+	pctl->functions = pic32_functions;
+	pctl->nfunctions = ARRAY_SIZE(pic32_functions);
+	pctl->groups = pic32_groups;
+	pctl->ngroups = ARRAY_SIZE(pic32_groups);
+	pctl->gpio_banks = pic32_gpio_banks;
+	pctl->nbanks = ARRAY_SIZE(pic32_gpio_banks);
+
+	pic32_pinctrl_desc.pins = pctl->pins;
+	pic32_pinctrl_desc.npins = pctl->npins;
+	pic32_pinctrl_desc.custom_params = pic32_mpp_bindings;
+	pic32_pinctrl_desc.num_custom_params = ARRAY_SIZE(pic32_mpp_bindings);
+
+	pctl->pctldev = pinctrl_register(&pic32_pinctrl_desc, &pdev->dev, pctl);
+	if (IS_ERR(pctl->pctldev)) {
+		dev_err(&pdev->dev, "Failed to register pinctrl device\n");
+		return PTR_ERR(pctl->pctldev);
+	}
+
+	return 0;
+}
+
+static int pic32_gpio_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct pic32_gpio_bank *bank;
+	u32 id;
+	int irq, ret;
+	struct resource *res;
+
+	if (of_property_read_u32(np, "microchip,gpio-bank", &id)) {
+		dev_err(&pdev->dev, "microchip,gpio-bank property not found\n");
+		return -EINVAL;
+	}
+
+	if (id >= ARRAY_SIZE(pic32_gpio_banks)) {
+		dev_err(&pdev->dev, "invalid microchip,gpio-bank property\n");
+		return -EINVAL;
+	}
+
+	bank = &pic32_gpio_banks[id];
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	bank->reg_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(bank->reg_base))
+		return PTR_ERR(bank->reg_base);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "irq get failed\n");
+		return irq;
+	}
+
+	bank->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(bank->clk)) {
+		ret = PTR_ERR(bank->clk);
+		dev_err(&pdev->dev, "clk get failed\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(bank->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "clk enable failed\n");
+		return ret;
+	}
+
+	bank->gpio_chip.dev = &pdev->dev;
+	bank->gpio_chip.of_node = np;
+	ret = gpiochip_add(&bank->gpio_chip);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to add GPIO chip %u: %d\n",
+			id, ret);
+		return ret;
+	}
+
+	ret = gpiochip_irqchip_add(&bank->gpio_chip, &bank->irq_chip,
+				0, handle_level_irq, IRQ_TYPE_NONE);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to add IRQ chip %u: %d\n",
+			id, ret);
+		gpiochip_remove(&bank->gpio_chip);
+		return ret;
+	}
+
+	gpiochip_set_chained_irqchip(&bank->gpio_chip, &bank->irq_chip,
+				     irq, pic32_gpio_irq_handler);
+
+	return 0;
+}
+
+static const struct of_device_id pic32_pinctrl_of_match[] = {
+	{ .compatible = "microchip,pic32mzda-pinctrl", },
+	{ },
+};
+
+static struct platform_driver pic32_pinctrl_driver = {
+	.driver = {
+		.name = "pic32-pinctrl",
+		.of_match_table = pic32_pinctrl_of_match,
+		.suppress_bind_attrs = true,
+	},
+	.probe = pic32_pinctrl_probe,
+};
+
+static const struct of_device_id pic32_gpio_of_match[] = {
+	{ .compatible = "microchip,pic32mzda-gpio", },
+	{ },
+};
+
+static struct platform_driver pic32_gpio_driver = {
+	.driver = {
+		.name = "pic32-gpio",
+		.of_match_table = pic32_gpio_of_match,
+		.suppress_bind_attrs = true,
+	},
+	.probe = pic32_gpio_probe,
+};
+
+static int __init pic32_gpio_register(void)
+{
+	return platform_driver_register(&pic32_gpio_driver);
+}
+arch_initcall(pic32_gpio_register);
+
+static int __init pic32_pinctrl_register(void)
+{
+	return platform_driver_register(&pic32_pinctrl_driver);
+}
+arch_initcall(pic32_pinctrl_register);
diff --git a/drivers/pinctrl/pinctrl-pic32.h b/drivers/pinctrl/pinctrl-pic32.h
new file mode 100644
index 0000000..1282626
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-pic32.h
@@ -0,0 +1,141 @@
+/*
+ * PIC32 pinctrl driver
+ *
+ * Joshua Henderson, <joshua.henderson@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ * This program is free software; you can distribute it and/or modify it
+ * under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+#ifndef PINCTRL_PINCTRL_PIC32_H
+#define PINCTRL_PINCTRL_PIC32_H
+
+/* PORT Registers */
+#define ANSEL_REG	0x00
+#define TRIS_REG	0x10
+#define PORT_REG	0x20
+#define LAT_REG		0x30
+#define ODCU_REG	0x40
+#define CNPU_REG	0x50
+#define CNPD_REG	0x60
+#define CNCON_REG	0x70
+#define CNEN_REG	0x80
+#define CNSTAT_REG	0x90
+#define CNNE_REG	0xA0
+#define CNF_REG		0xB0
+
+/* Input PPS Registers */
+#define INT1R 0x04
+#define INT2R 0x08
+#define INT3R 0x0C
+#define INT4R 0x10
+#define T2CKR 0x18
+#define T3CKR 0x1C
+#define T4CKR 0x20
+#define T5CKR 0x24
+#define T6CKR 0x28
+#define T7CKR 0x2C
+#define T8CKR 0x30
+#define T9CKR 0x34
+#define IC1R 0x38
+#define IC2R 0x3C
+#define IC3R 0x40
+#define IC4R 0x44
+#define IC5R 0x48
+#define IC6R 0x4C
+#define IC7R 0x50
+#define IC8R 0x54
+#define IC9R 0x58
+#define OCFAR 0x60
+#define U1RXR 0x68
+#define U1CTSR 0x6C
+#define U2RXR 0x70
+#define U2CTSR 0x74
+#define U3RXR 0x78
+#define U3CTSR 0x7C
+#define U4RXR 0x80
+#define U4CTSR 0x84
+#define U5RXR 0x88
+#define U5CTSR 0x8C
+#define U6RXR 0x90
+#define U6CTSR 0x94
+#define SDI1R 0x9C
+#define SS1INR 0xA0
+#define SDI2R 0xA8
+#define SS2INR 0xAC
+#define SDI3R 0xB4
+#define SS3INR 0xB8
+#define SDI4R 0xC0
+#define SS4INR 0xC4
+#define SDI5R 0xCC
+#define SS5INR 0xD0
+#define SDI6R 0xD8
+#define SS6INR 0xDC
+#define C1RXR 0xE0
+#define C2RXR 0xE4
+#define REFCLKI1R 0xE8
+#define REFCLKI3R 0xF0
+#define REFCLKI4R 0xF4
+
+/* Output PPS Registers */
+#define RPA14R 0x138
+#define RPA15R 0x13C
+#define RPB0R 0x140
+#define RPB1R 0x144
+#define RPB2R 0x148
+#define RPB3R 0x14C
+#define RPB5R 0x154
+#define RPB6R 0x158
+#define RPB7R 0x15C
+#define RPB8R 0x160
+#define RPB9R 0x164
+#define RPB10R 0x168
+#define RPB14R 0x178
+#define RPB15R 0x17C
+#define RPC1R 0x184
+#define RPC2R 0x188
+#define RPC3R 0x18C
+#define RPC4R 0x190
+#define RPC13R 0x1B4
+#define RPC14R 0x1B8
+#define RPD0R 0x1C0
+#define RPD1R 0x1C4
+#define RPD2R 0x1C8
+#define RPD3R 0x1CC
+#define RPD4R 0x1D0
+#define RPD5R 0x1D4
+#define RPD6R 0x1D8
+#define RPD7R 0x1DC
+#define RPD9R 0x1E4
+#define RPD10R 0x1E8
+#define RPD11R 0x1EC
+#define RPD12R 0x1F0
+#define RPD14R 0x1F8
+#define RPD15R 0x1FC
+#define RPE3R 0x20C
+#define RPE5R 0x214
+#define RPE8R 0x220
+#define RPE9R 0x224
+#define RPF0R 0x240
+#define RPF1R 0x244
+#define RPF2R 0x248
+#define RPF3R 0x24C
+#define RPF4R 0x250
+#define RPF5R 0x254
+#define RPF8R 0x260
+#define RPF12R 0x270
+#define RPF13R 0x274
+#define RPG0R 0x280
+#define RPG1R 0x284
+#define RPG6R 0x298
+#define RPG7R 0x29C
+#define RPG8R 0x2A0
+#define RPG9R 0x2A4
+
+#endif  /* PINCTRL_PINCTRL_PIC32_H */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 09/14] DEVICETREE: Add bindings for PIC32 UART driver
  2015-12-14 22:42 [PATCH v2 00/14] Initial Microchip PIC32MZDA Support Joshua Henderson
                   ` (7 preceding siblings ...)
  2015-12-14 22:42 ` [PATCH v2 08/14] pinctrl: pinctrl-pic32: Add PIC32 pin control driver Joshua Henderson
@ 2015-12-14 22:42 ` Joshua Henderson
  2015-12-15 20:00   ` Rob Herring
  2015-12-14 22:42 ` [PATCH v2 10/14] serial: pic32_uart: Add " Joshua Henderson
                   ` (4 subsequent siblings)
  13 siblings, 1 reply; 33+ messages in thread
From: Joshua Henderson @ 2015-12-14 22:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, ralf, Andrei Pistirica, Joshua Henderson,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree

From: Andrei Pistirica <andrei.pistirica@microchip.com>

Document the devicetree bindings for the UART peripheral found on
Microchip PIC32 class devices.

Signed-off-by: Andrei Pistirica <andrei.pistirica@microchip.com>
Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
---
 .../bindings/serial/microchip,pic32-uart.txt       |   29 ++++++++++++++++++++
 1 file changed, 29 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/serial/microchip,pic32-uart.txt

diff --git a/Documentation/devicetree/bindings/serial/microchip,pic32-uart.txt b/Documentation/devicetree/bindings/serial/microchip,pic32-uart.txt
new file mode 100644
index 0000000..65b38bf6
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/microchip,pic32-uart.txt
@@ -0,0 +1,29 @@
+* Microchip Universal Asynchronous Receiver Transmitter (UART)
+
+Required properties:
+- compatible: Should be "microchip,pic32mzda-uart"
+- reg: Should contain registers location and length
+- interrupts: Should contain interrupt
+- clocks: Phandle to the clock.
+          See: Documentation/devicetree/bindings/clock/clock-bindings.txt
+- pinctrl-names: A pinctrl state names "default" must be defined.
+- pinctrl-0: Phandle referencing pin configuration of the UART peripheral.
+             See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
+
+Optional properties:
+- cts-gpios: CTS pin for UART
+
+Example:
+	uart1: serial@1f822000 {
+		compatible = "microchip,pic32mzda-uart";
+		reg = <0x1f822000 0x50>;
+		interrupts = <112 IRQ_TYPE_LEVEL_HIGH>,
+			<113 IRQ_TYPE_LEVEL_HIGH>,
+			<114 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&PBCLK2>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_uart1
+				&pinctrl_uart1_cts
+				&pinctrl_uart1_rts>;
+		cts-gpios = <&gpio1 15 0>;
+	};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 10/14] serial: pic32_uart: Add PIC32 UART driver
  2015-12-14 22:42 [PATCH v2 00/14] Initial Microchip PIC32MZDA Support Joshua Henderson
                   ` (8 preceding siblings ...)
  2015-12-14 22:42 ` [PATCH v2 09/14] DEVICETREE: Add bindings for PIC32 UART driver Joshua Henderson
@ 2015-12-14 22:42 ` Joshua Henderson
  2015-12-20 16:13   ` Andy Shevchenko
  2016-01-05 20:43   ` One Thousand Gnomes
  2015-12-14 22:42 ` [PATCH v2 11/14] DEVICETREE: Add bindings for PIC32 SDHCI host controller Joshua Henderson
                   ` (3 subsequent siblings)
  13 siblings, 2 replies; 33+ messages in thread
From: Joshua Henderson @ 2015-12-14 22:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, ralf, Andrei Pistirica, Joshua Henderson,
	Greg Kroah-Hartman, Jiri Slaby, linux-serial, linux-api

From: Andrei Pistirica <andrei.pistirica@microchip.com>

This adds UART and a serial console driver for Microchip PIC32 class
devices.

Signed-off-by: Andrei Pistirica <andrei.pistirica@microchip.com>
Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
---
 drivers/tty/serial/Kconfig       |   21 +
 drivers/tty/serial/Makefile      |    1 +
 drivers/tty/serial/pic32_uart.c  |  927 ++++++++++++++++++++++++++++++++++++++
 drivers/tty/serial/pic32_uart.h  |  198 ++++++++
 include/uapi/linux/serial_core.h |    3 +
 5 files changed, 1150 insertions(+)
 create mode 100644 drivers/tty/serial/pic32_uart.c
 create mode 100644 drivers/tty/serial/pic32_uart.h

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index f38beb2..8853b1e 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -901,6 +901,27 @@ config SERIAL_SGI_L1_CONSOLE
 		controller serial port as your console (you want this!),
 		say Y.  Otherwise, say N.
 
+config SERIAL_PIC32
+	tristate "Microchip PIC32 serial support"
+	depends on MACH_PIC32
+	select SERIAL_CORE
+	help
+	  If you have a PIC32, this driver supports the serial ports.
+
+	  Say Y or M to use PIC32 serial ports, otherwise say N. Note that
+	  to use a serial port as a console, this must be included in kernel and
+	  not as a module.
+
+config SERIAL_PIC32_CONSOLE
+	bool "PIC32 serial console support"
+	depends on SERIAL_PIC32
+	select SERIAL_CORE_CONSOLE
+	help
+	  If you have a PIC32, this driver supports the putting a console on one
+	  of the serial ports.
+
+	  Say Y to use the PIC32 console, otherwise say N.
+
 config SERIAL_MPC52xx
 	tristate "Freescale MPC52xx/MPC512x family PSC serial support"
 	depends on PPC_MPC52xx || PPC_MPC512x
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 5ab4111..bc5e354 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)	+= digicolor-usart.o
 obj-$(CONFIG_SERIAL_MEN_Z135)	+= men_z135_uart.o
 obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
 obj-$(CONFIG_SERIAL_STM32)	+= stm32-usart.o
+obj-$(CONFIG_SERIAL_PIC32)	+= pic32_uart.o
 
 # GPIOLIB helpers for modem control lines
 obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c
new file mode 100644
index 0000000..5c05c11
--- /dev/null
+++ b/drivers/tty/serial/pic32_uart.c
@@ -0,0 +1,927 @@
+/*
+ * PIC32 Integrated Serial Driver.
+ *
+ * Copyright (C) 2015 Microchip Technology, Inc.
+ *
+ * Authors:
+ *   Steve Scott <steve.scott@microchip.com>,
+ *   Sorin-Andrei Pistirica <andrei.pistirica@microchip.com>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/console.h>
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/sysrq.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <uapi/linux/serial_core.h>
+#include <linux/delay.h>
+
+#include "pic32_uart.h"
+
+/* UART name and device definitions */
+#define PIC32_DEV_NAME		"pic32-uart"
+#define PIC32_MAX_UARTS		6
+
+#define PIC32_SDEV_NAME		"ttyS"
+#define PIC32_SDEV_MAJOR	TTY_MAJOR
+#define PIC32_SDEV_MINOR	64
+
+/* pic32_sport pointer for console use */
+static struct pic32_sport *pic32_sports[PIC32_MAX_UARTS];
+
+static inline int pic32_enable_clock(struct pic32_sport *sport)
+{
+	sport->ref_clk++;
+
+	return clk_prepare_enable(sport->clk);
+}
+
+static inline void pic32_disable_clock(struct pic32_sport *sport)
+{
+	sport->ref_clk--;
+	clk_disable_unprepare(sport->clk);
+}
+
+/* serial core request to check if uart tx buffer is empty */
+static unsigned int pic32_uart_tx_empty(struct uart_port *port)
+{
+	struct pic32_sport *sport = to_pic32_sport(port);
+	u32 val = pic32_uart_read(sport, PIC32_UART_STA);
+
+	return (val & PIC32_UART_STA_TRMT) ? 1 : 0;
+}
+
+/* serial core request to set UART outputs */
+static void pic32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct pic32_sport *sport = to_pic32_sport(port);
+
+	/* set loopback mode */
+	if (mctrl & TIOCM_LOOP)
+		pic32_uart_rset(PIC32_UART_MODE_LPBK, sport, PIC32_UART_MODE);
+	else
+		pic32_uart_rclr(PIC32_UART_MODE_LPBK, sport, PIC32_UART_MODE);
+}
+
+/* get the state of CTS input pin for this port */
+static unsigned int get_cts_state(struct pic32_sport *sport)
+{
+	/* default state must be asserted */
+	int val = 1;
+
+	/* read and invert UxCTS */
+	if (gpio_is_valid(sport->cts_gpio))
+		val = !gpio_get_value(sport->cts_gpio);
+
+	return val;
+}
+
+/* serial core request to return the state of misc UART input pins */
+static unsigned int pic32_uart_get_mctrl(struct uart_port *port)
+{
+	struct pic32_sport *sport = to_pic32_sport(port);
+	unsigned int mctrl = 0;
+
+	if (!sport->hw_flow_ctrl) {
+		mctrl |= TIOCM_CTS;
+		goto ret;
+	}
+
+	if (get_cts_state(sport))
+		mctrl |= TIOCM_CTS;
+
+ret:
+	/* DSR and CD are not supported in PIC32, so return 1
+	 * RI is not supported in PIC32, so return 0
+	 */
+	mctrl |= TIOCM_CD;
+	mctrl |= TIOCM_DSR;
+
+	return mctrl;
+}
+
+/* stop tx and start tx are not called in pairs, therefore a flag indicates
+ * the status of irq to control the irq-depth.
+ */
+static inline void pic32_uart_irqtxen(struct pic32_sport *sport, u8 en)
+{
+	if (en && !tx_irq_enabled(sport)) {
+		enable_irq(sport->irq_tx);
+		tx_irq_enabled(sport) = 1;
+	} else if (!en && tx_irq_enabled(sport)) {
+		/* use disable_irq_nosync() and not disable_irq() to avoid self
+		 * imposed deadlock by not waiting for irq handler to end,
+		 * since this callback is called from interrupt context.
+		 */
+		disable_irq_nosync(sport->irq_tx);
+		tx_irq_enabled(sport) = 0;
+	}
+}
+
+/* serial core request to disable tx ASAP (used for flow control) */
+static void pic32_uart_stop_tx(struct uart_port *port)
+{
+	struct pic32_sport *sport = to_pic32_sport(port);
+
+	if (!(pic32_uart_read(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON))
+		return;
+
+	if (!(pic32_uart_read(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN))
+		return;
+
+	/* wait for tx empty */
+	while (!(pic32_uart_read(sport, PIC32_UART_STA) & PIC32_UART_STA_TRMT))
+		udelay(1);
+
+	pic32_uart_rclr(PIC32_UART_STA_UTXEN, sport, PIC32_UART_STA);
+	pic32_uart_irqtxen(sport, 0);
+}
+
+/* serial core request to (re)enable tx */
+static void pic32_uart_start_tx(struct uart_port *port)
+{
+	struct pic32_sport *sport = to_pic32_sport(port);
+
+	pic32_uart_irqtxen(sport, 1);
+	pic32_uart_rset(PIC32_UART_STA_UTXEN, sport, PIC32_UART_STA);
+}
+
+/* serial core request to stop rx, called before port shutdown */
+static void pic32_uart_stop_rx(struct uart_port *port)
+{
+	struct pic32_sport *sport = to_pic32_sport(port);
+
+	/* disable rx interrupts */
+	disable_irq(sport->irq_rx);
+
+	/* receiver Enable bit OFF */
+	pic32_uart_rclr(PIC32_UART_STA_URXEN, sport, PIC32_UART_STA);
+}
+
+/* serial core request to start/stop emitting break char */
+static void pic32_uart_break_ctl(struct uart_port *port, int ctl)
+{
+	struct pic32_sport *sport = to_pic32_sport(port);
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	if (ctl)
+		pic32_uart_rset(PIC32_UART_STA_UTXBRK, sport, PIC32_UART_STA);
+	else
+		pic32_uart_rclr(PIC32_UART_STA_UTXBRK, sport, PIC32_UART_STA);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/* get port type in string format */
+static const char *pic32_uart_type(struct uart_port *port)
+{
+	return (port->type == PORT_PIC32) ? PIC32_DEV_NAME : NULL;
+}
+
+/* read all chars in rx fifo and send them to core */
+static void pic32_uart_do_rx(struct uart_port *port)
+{
+	struct pic32_sport *sport = to_pic32_sport(port);
+	struct tty_port *tty;
+	unsigned int max_count;
+
+	/* limit number of char read in interrupt, should not be
+	 * higher than fifo size anyway since we're much faster than
+	 * serial port
+	 */
+	max_count = PIC32_UART_RX_FIFO_DEPTH;
+
+	spin_lock(&port->lock);
+
+	tty = &port->state->port;
+
+	do {
+		u32 sta_reg, c;
+		char flag;
+
+		/* get overrun/fifo empty information from status register */
+		sta_reg = pic32_uart_read(sport, PIC32_UART_STA);
+		if (unlikely(sta_reg & PIC32_UART_STA_OERR)) {
+
+			/* fifo reset is required to clear interrupt */
+			pic32_uart_rclr(PIC32_UART_STA_OERR, sport,
+							PIC32_UART_STA);
+
+			port->icount.overrun++;
+			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+		}
+
+		/* Can at least one more character can be read? */
+		if (!(sta_reg & PIC32_UART_STA_URXDA))
+			break;
+
+		/* read the character and increment the rx counter */
+		c = pic32_uart_read(sport, PIC32_UART_RX);
+
+		port->icount.rx++;
+		flag = TTY_NORMAL;
+		c &= 0xff;
+
+		if (unlikely((sta_reg & PIC32_UART_STA_PERR) ||
+			     (sta_reg & PIC32_UART_STA_FERR))) {
+
+			/* do stats first */
+			if (sta_reg & PIC32_UART_STA_PERR)
+				port->icount.parity++;
+			if (sta_reg & PIC32_UART_STA_FERR)
+				port->icount.frame++;
+
+			/* update flag wrt read_status_mask */
+			sta_reg &= port->read_status_mask;
+
+			if (sta_reg & PIC32_UART_STA_FERR)
+				flag = TTY_FRAME;
+			if (sta_reg & PIC32_UART_STA_PERR)
+				flag = TTY_PARITY;
+		}
+
+		if (uart_handle_sysrq_char(port, c))
+			continue;
+
+		if ((sta_reg & port->ignore_status_mask) == 0)
+			tty_insert_flip_char(tty, c, flag);
+
+	} while (--max_count);
+
+	spin_unlock(&port->lock);
+
+	tty_flip_buffer_push(tty);
+}
+
+/* fill tx fifo with chars to send, stop when fifo is about to be full
+ * or when all chars have been sent.
+ */
+static void pic32_uart_do_tx(struct uart_port *port)
+{
+	struct pic32_sport *sport = to_pic32_sport(port);
+	struct circ_buf *xmit = &port->state->xmit;
+	unsigned int max_count = PIC32_UART_TX_FIFO_DEPTH;
+
+	if (port->x_char) {
+		pic32_uart_write(port->x_char, sport, PIC32_UART_TX);
+		port->icount.tx++;
+		port->x_char = 0;
+		return;
+	}
+
+	if (uart_tx_stopped(port)) {
+		pic32_uart_stop_tx(port);
+		return;
+	}
+
+	if (uart_circ_empty(xmit))
+		goto txq_empty;
+
+	/* keep stuffing chars into uart tx buffer
+	 * 1) until uart fifo is full
+	 * or
+	 * 2) until the circ buffer is empty
+	 * (all chars have been sent)
+	 * or
+	 * 3) until the max count is reached
+	 * (prevents lingering here for too long in certain cases)
+	 */
+	while (!(PIC32_UART_STA_UTXBF &
+		pic32_uart_rval(sport, PIC32_UART_STA))) {
+		unsigned int c = xmit->buf[xmit->tail];
+
+		pic32_uart_write(c, sport, PIC32_UART_TX);
+
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+		--max_count;
+		if (uart_circ_empty(xmit))
+			break;
+		if (max_count == 0)
+			break;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		goto txq_empty;
+
+	return;
+
+txq_empty:
+	pic32_uart_irqtxen(sport, 0);
+}
+
+/* RX interrupt handler */
+static irqreturn_t pic32_uart_rx_interrupt(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+
+	pic32_uart_do_rx(port);
+
+	return IRQ_HANDLED;
+}
+
+/* TX interrupt handler */
+static irqreturn_t pic32_uart_tx_interrupt(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	pic32_uart_do_tx(port);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+/* FAULT interrupt handler */
+static irqreturn_t pic32_uart_fault_interrupt(int irq, void *dev_id)
+{
+	/* do nothing: pic32_uart_do_rx() handles faults. */
+	return IRQ_HANDLED;
+}
+
+/* enable rx & tx operation on uart */
+static void pic32_uart_en_and_unmask(struct uart_port *port)
+{
+	struct pic32_sport *sport = to_pic32_sport(port);
+
+	pic32_uart_rset(PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN,
+			sport, PIC32_UART_STA);
+	pic32_uart_rset(PIC32_UART_MODE_ON, sport, PIC32_UART_MODE);
+}
+
+/* disable rx & tx operation on uart */
+static void pic32_uart_dsbl_and_mask(struct uart_port *port)
+{
+	struct pic32_sport *sport = to_pic32_sport(port);
+
+	pic32_uart_rclr(PIC32_UART_MODE_ON, sport, PIC32_UART_MODE);
+	pic32_uart_rclr(PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN,
+			sport, PIC32_UART_STA);
+}
+
+/* serial core request to initialize uart and start rx operation */
+static int pic32_uart_startup(struct uart_port *port)
+{
+	struct pic32_sport *sport = to_pic32_sport(port);
+	u32 dflt_baud = ((port->uartclk / PIC32_UART_DFLT_BRATE) / 16) - 1;
+	unsigned long flags;
+	int ret = 0;
+
+	local_irq_save(flags);
+
+	ret = pic32_enable_clock(sport);
+	if (ret)
+		goto out_unlock;
+
+	/* clear status and mode registers */
+	pic32_uart_write(0, sport, PIC32_UART_MODE);
+	pic32_uart_write(0, sport, PIC32_UART_STA);
+
+	/* disable uart and mask all interrupts */
+	pic32_uart_dsbl_and_mask(port);
+
+	/* set default baud */
+	pic32_uart_write(dflt_baud, sport, PIC32_UART_BRG);
+
+	local_irq_restore(flags);
+
+	/* Each UART of a PIC32 has three interrupts therefore,
+	 * we setup driver to register the 3 irqs for the device.
+	 *
+	 * For each irq request_irq() is called with interrupt disabled.
+	 * And the irq is enabled as soon as we are ready to handle them.
+	 */
+	tx_irq_enabled(sport) = 0;
+
+	sport->irq_fault_name = kasprintf(GFP_KERNEL, "%s%d-fault",
+					  pic32_uart_type(port),
+					  sport->idx);
+	irq_set_status_flags(sport->irq_fault, IRQ_NOAUTOEN);
+	ret = request_irq(sport->irq_fault, pic32_uart_fault_interrupt,
+			  sport->irqflags_fault, sport->irq_fault_name, port);
+	if (ret) {
+		dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n",
+			__func__, sport->irq_fault, ret,
+			pic32_uart_type(port));
+		goto out_done;
+	}
+
+	sport->irq_rx_name = kasprintf(GFP_KERNEL, "%s%d-rx",
+				       pic32_uart_type(port),
+				       sport->idx);
+	irq_set_status_flags(sport->irq_rx, IRQ_NOAUTOEN);
+	ret = request_irq(sport->irq_rx, pic32_uart_rx_interrupt,
+			  sport->irqflags_rx, sport->irq_rx_name, port);
+	if (ret) {
+		dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n",
+			__func__, sport->irq_rx, ret,
+			pic32_uart_type(port));
+		goto out_done;
+	}
+
+	sport->irq_tx_name = kasprintf(GFP_KERNEL, "%s%d-tx",
+				       pic32_uart_type(port),
+				       sport->idx);
+	irq_set_status_flags(sport->irq_tx, IRQ_NOAUTOEN);
+	ret = request_irq(sport->irq_tx, pic32_uart_tx_interrupt,
+			  sport->irqflags_tx, sport->irq_tx_name, port);
+	if (ret) {
+		dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n",
+			__func__, sport->irq_tx, ret,
+			pic32_uart_type(port));
+		goto out_done;
+	}
+
+	local_irq_save(flags);
+
+	/* set rx interrupt on first receive */
+	pic32_uart_rclr(PIC32_UART_STA_URXISEL1 | PIC32_UART_STA_URXISEL0,
+							sport, PIC32_UART_STA);
+
+	/* set interrupt on empty */
+	pic32_uart_rclr(PIC32_UART_STA_UTXISEL1, sport, PIC32_UART_STA);
+
+	/* enable all interrupts and eanable uart */
+	pic32_uart_en_and_unmask(port);
+
+	enable_irq(sport->irq_fault);
+	enable_irq(sport->irq_rx);
+
+out_unlock:
+	local_irq_restore(flags);
+
+out_done:
+	return ret;
+}
+
+/* serial core request to flush & disable uart */
+static void pic32_uart_shutdown(struct uart_port *port)
+{
+	struct pic32_sport *sport = to_pic32_sport(port);
+	unsigned long flags;
+
+	/* disable uart */
+	spin_lock_irqsave(&port->lock, flags);
+	pic32_uart_dsbl_and_mask(port);
+	spin_unlock_irqrestore(&port->lock, flags);
+	pic32_disable_clock(sport);
+
+	/* free all 3 interrupts for this UART */
+	free_irq(sport->irq_fault, port);
+	free_irq(sport->irq_tx, port);
+	free_irq(sport->irq_rx, port);
+}
+
+/* serial core request to change current uart setting */
+static void pic32_uart_set_termios(struct uart_port *port,
+				   struct ktermios *new,
+				   struct ktermios *old)
+{
+	struct pic32_sport *sport = to_pic32_sport(port);
+	unsigned int baud;
+	unsigned int quot;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* disable uart and mask all interrupts while changing speed */
+	pic32_uart_dsbl_and_mask(port);
+
+	/* stop bit options */
+	if (new->c_cflag & CSTOPB)
+		pic32_uart_rset(PIC32_UART_MODE_STSEL, sport, PIC32_UART_MODE);
+	else
+		pic32_uart_rclr(PIC32_UART_MODE_STSEL, sport, PIC32_UART_MODE);
+
+	/* parity options */
+	if (new->c_cflag & PARENB) {
+		if (new->c_cflag & PARODD) {
+			pic32_uart_rset(PIC32_UART_MODE_PDSEL1, sport,
+					PIC32_UART_MODE);
+			pic32_uart_rclr(PIC32_UART_MODE_PDSEL0, sport,
+					PIC32_UART_MODE);
+		} else {
+			pic32_uart_rset(PIC32_UART_MODE_PDSEL0, sport,
+					PIC32_UART_MODE);
+			pic32_uart_rclr(PIC32_UART_MODE_PDSEL1, sport,
+					PIC32_UART_MODE);
+		}
+	} else {
+		pic32_uart_rclr(PIC32_UART_MODE_PDSEL1 | PIC32_UART_MODE_PDSEL0,
+				sport, PIC32_UART_MODE);
+	}
+	/* if hw flow ctrl, then the pins must be specified in device tree */
+	if ((new->c_cflag & CRTSCTS) && sport->hw_flow_ctrl) {
+		/* enable hardware flow control */
+		pic32_uart_rset(PIC32_UART_MODE_UEN1, sport, PIC32_UART_MODE);
+		pic32_uart_rclr(PIC32_UART_MODE_UEN0, sport, PIC32_UART_MODE);
+		pic32_uart_rclr(PIC32_UART_MODE_RTSMD, sport, PIC32_UART_MODE);
+	} else {
+		/* disable hardware flow control */
+		pic32_uart_rclr(PIC32_UART_MODE_UEN1, sport, PIC32_UART_MODE);
+		pic32_uart_rclr(PIC32_UART_MODE_UEN0, sport, PIC32_UART_MODE);
+		pic32_uart_rclr(PIC32_UART_MODE_RTSMD, sport, PIC32_UART_MODE);
+	}
+
+	/* update baud */
+	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
+	quot = uart_get_divisor(port, baud) - 1;
+	pic32_uart_write(quot, sport, PIC32_UART_BRG);
+	uart_update_timeout(port, new->c_cflag, baud);
+
+	/* enable uart */
+	pic32_uart_en_and_unmask(port);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/* serial core request to claim uart iomem */
+static int pic32_uart_request_port(struct uart_port *port)
+{
+	struct platform_device *pdev = to_platform_device(port->dev);
+	struct resource *res_mem;
+	unsigned int res_size;
+
+	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (unlikely(!res_mem))
+		return -EINVAL;
+	res_size = resource_size(res_mem);
+
+	if (!request_mem_region(port->mapbase, res_size, "pic32_uart_mem")) {
+		dev_err(port->dev, "Memory region busy\n");
+		return -EBUSY;
+	}
+
+	port->membase = devm_ioremap_nocache(port->dev,
+					     port->mapbase, res_size);
+	if (!port->membase) {
+		dev_err(port->dev, "Unable to map registers\n");
+		release_mem_region(port->mapbase, res_size);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/* serial core request to release uart iomem */
+static void pic32_uart_release_port(struct uart_port *port)
+{
+	struct platform_device *pdev = to_platform_device(port->dev);
+	struct resource *res_mem;
+	unsigned int res_size;
+
+	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (unlikely(!res_mem))
+		return;
+	res_size = resource_size(res_mem);
+
+	release_mem_region(port->mapbase, res_size);
+	devm_iounmap(port->dev, port->membase);
+}
+
+/* serial core request to do any port required auto-configuration */
+static void pic32_uart_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		if (pic32_uart_request_port(port))
+			return;
+		port->type = PORT_PIC32;
+	}
+}
+
+/* serial core request to check that port information in serinfo are suitable */
+static int pic32_uart_verify_port(struct uart_port *port,
+				  struct serial_struct *serinfo)
+{
+	if (port->type != PORT_PIC32)
+		return -EINVAL;
+	if (port->irq != serinfo->irq)
+		return -EINVAL;
+	if (port->iotype != serinfo->io_type)
+		return -EINVAL;
+	if (port->mapbase != (unsigned long)serinfo->iomem_base)
+		return -EINVAL;
+
+	return 0;
+}
+
+/* serial core callbacks */
+static const struct uart_ops pic32_uart_ops = {
+	.tx_empty	= pic32_uart_tx_empty,
+	.get_mctrl	= pic32_uart_get_mctrl,
+	.set_mctrl	= pic32_uart_set_mctrl,
+	.start_tx	= pic32_uart_start_tx,
+	.stop_tx	= pic32_uart_stop_tx,
+	.stop_rx	= pic32_uart_stop_rx,
+	.break_ctl	= pic32_uart_break_ctl,
+	.startup	= pic32_uart_startup,
+	.shutdown	= pic32_uart_shutdown,
+	.set_termios	= pic32_uart_set_termios,
+	.type		= pic32_uart_type,
+	.release_port	= pic32_uart_release_port,
+	.request_port	= pic32_uart_request_port,
+	.config_port	= pic32_uart_config_port,
+	.verify_port	= pic32_uart_verify_port,
+};
+
+#ifdef CONFIG_SERIAL_PIC32_CONSOLE
+/* output given char */
+static void pic32_console_putchar(struct uart_port *port, int ch)
+{
+	struct pic32_sport *sport = to_pic32_sport(port);
+
+	if (!(pic32_uart_read(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON))
+		return;
+
+	if (!(pic32_uart_read(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN))
+		return;
+
+	/* wait for tx empty */
+	while (!(pic32_uart_read(sport, PIC32_UART_STA) & PIC32_UART_STA_TRMT))
+		udelay(1);
+
+	pic32_uart_write(ch & 0xff, sport, PIC32_UART_TX);
+}
+
+/* console core request to output given string */
+static void pic32_console_write(struct console *co, const char *s,
+				unsigned int count)
+{
+	struct pic32_sport *sport = pic32_sports[co->index];
+	struct uart_port *port = pic32_get_port(sport);
+
+	/* call uart helper to deal with \r\n */
+	uart_console_write(port, s, count, pic32_console_putchar);
+}
+
+/* console core request to setup given console, find matching uart
+ * port and setup it.
+ */
+static int pic32_console_setup(struct console *co, char *options)
+{
+	struct pic32_sport *sport;
+	struct uart_port *port = NULL;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	int ret = 0;
+
+	if (unlikely(co->index < 0 || co->index >= PIC32_MAX_UARTS))
+		return -ENODEV;
+
+	sport = pic32_sports[co->index];
+	if (!sport)
+		return -ENODEV;
+	port = pic32_get_port(sport);
+
+	ret = pic32_enable_clock(sport);
+	if (ret)
+		return ret;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver pic32_uart_driver;
+static struct console pic32_console = {
+	.name		= PIC32_SDEV_NAME,
+	.write		= pic32_console_write,
+	.device		= uart_console_device,
+	.setup		= pic32_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &pic32_uart_driver,
+};
+#define PIC32_SCONSOLE (&pic32_console)
+
+static int __init pic32_console_init(void)
+{
+	register_console(&pic32_console);
+	return 0;
+}
+console_initcall(pic32_console_init);
+
+static inline bool is_pic32_console_port(struct uart_port *port)
+{
+	return (port->cons && port->cons->index == port->line);
+}
+
+/*
+ * Late console initialization.
+ */
+static int __init pic32_late_console_init(void)
+{
+	if (!(pic32_console.flags & CON_ENABLED))
+		register_console(&pic32_console);
+
+	return 0;
+}
+
+core_initcall(pic32_late_console_init);
+
+#else
+#define PIC32_SCONSOLE NULL
+#endif
+
+static struct uart_driver pic32_uart_driver = {
+	.owner			= THIS_MODULE,
+	.driver_name		= PIC32_DEV_NAME,
+	.dev_name		= PIC32_SDEV_NAME,
+	.major			= PIC32_SDEV_MAJOR,
+	.minor			= PIC32_SDEV_MINOR,
+	.nr			= PIC32_MAX_UARTS,
+	.cons			= PIC32_SCONSOLE,
+};
+
+static int pic32_uart_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct pic32_sport *sport;
+	int uart_idx = 0;
+	struct resource *res_mem;
+	struct uart_port *port;
+	int ret = 0;
+
+	uart_idx = of_alias_get_id(np, "serial");
+	if (uart_idx < 0 || uart_idx >= PIC32_MAX_UARTS)
+		return -EINVAL;
+
+	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res_mem)
+		return -EINVAL;
+
+	sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
+	if (!sport)
+		return -ENOMEM;
+
+	sport->idx		= uart_idx;
+	sport->irq_fault	= irq_of_parse_and_map(np, 0);
+	sport->irqflags_fault	= IRQF_NO_THREAD;
+	sport->irq_rx		= irq_of_parse_and_map(np, 1);
+	sport->irqflags_rx	= IRQF_NO_THREAD;
+	sport->irq_tx		= irq_of_parse_and_map(np, 2);
+	sport->irqflags_tx	= IRQF_NO_THREAD;
+	sport->clk		= devm_clk_get(&pdev->dev, NULL);
+	sport->cts_gpio		= -EINVAL;
+	sport->dev		= &pdev->dev;
+
+	ret = pic32_enable_clock(sport);
+	if (ret) {
+		dev_err(&pdev->dev, "clk enable ?\n");
+		goto err;
+	}
+
+	/* Hardware flow control: gpios
+	 * !Note: Basically, CTS is needed for reading the status.
+	 */
+	sport->hw_flow_ctrl = false;
+	sport->cts_gpio = of_get_named_gpio(np, "cts-gpios", 0);
+	if (gpio_is_valid(sport->cts_gpio)) {
+		sport->hw_flow_ctrl = true;
+
+		ret = devm_gpio_request(sport->dev,
+					sport->cts_gpio, "CTS");
+		if (ret) {
+			dev_err(&pdev->dev,
+				"error requesting CTS GPIO\n");
+			goto err_disable_clk;
+		}
+
+		ret = gpio_direction_input(sport->cts_gpio);
+		if (ret) {
+			dev_err(&pdev->dev, "error setting CTS GPIO\n");
+			goto err_disable_clk;
+		}
+	}
+
+	pic32_sports[uart_idx] = sport;
+	port = &sport->port;
+	memset(port, 0, sizeof(*port));
+	port->iotype	= UPIO_MEM;
+	port->mapbase	= res_mem->start;
+	port->ops	= &pic32_uart_ops;
+	port->flags	= UPF_BOOT_AUTOCONF;
+	port->dev	= &pdev->dev;
+	port->fifosize	= PIC32_UART_TX_FIFO_DEPTH;
+	port->uartclk	= clk_get_rate(sport->clk);
+	port->line	= uart_idx;
+
+	ret = uart_add_one_port(&pic32_uart_driver, port);
+	if (ret) {
+		port->membase = NULL;
+		dev_err(port->dev, "%s: uart add port error!\n", __func__);
+		goto err_disable_clk;
+	}
+
+#ifdef CONFIG_SERIAL_PIC32_CONSOLE
+	if (is_pic32_console_port(port) &&
+	    (pic32_console.flags & CON_ENABLED)) {
+		/* The peripheral clock has been enabled by console_setup,
+		 * so disable it till the port is used.
+		 */
+		pic32_disable_clock(sport);
+	}
+#endif
+
+	platform_set_drvdata(pdev, port);
+
+	dev_info(&pdev->dev, "%s: uart(%d) driver initialized.\n",
+		 __func__, uart_idx);
+	ret = 0;
+
+err_disable_clk:
+	/* disable clock till the port is used. */
+	pic32_disable_clock(sport);
+err:
+	/* automatic unroll of sport and gpios */
+	return ret;
+}
+
+static int pic32_uart_remove(struct platform_device *pdev)
+{
+	struct uart_port *port = platform_get_drvdata(pdev);
+	struct pic32_sport *sport = to_pic32_sport(port);
+
+	uart_remove_one_port(&pic32_uart_driver, port);
+	pic32_disable_clock(sport);
+	platform_set_drvdata(pdev, NULL);
+	pic32_sports[sport->idx] = NULL;
+
+	/* automatic unroll of sport and gpios */
+	return 0;
+}
+
+static const struct of_device_id pic32_serial_dt_ids[] = {
+	{ .compatible = "microchip,pic32mzda-uart" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pic32_serial_dt_ids);
+
+static struct platform_driver pic32_uart_platform_driver = {
+	.probe		= pic32_uart_probe,
+	.remove		= pic32_uart_remove,
+	.driver		= {
+		.name	= PIC32_DEV_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table	= of_match_ptr(pic32_serial_dt_ids),
+	},
+};
+
+static int __init pic32_uart_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&pic32_uart_driver);
+	if (ret) {
+		pr_err("failed to register %s:%d\n",
+		       pic32_uart_driver.driver_name, ret);
+		return ret;
+	}
+
+	ret = platform_driver_register(&pic32_uart_platform_driver);
+	if (ret) {
+		pr_err("fail to register pic32 uart\n");
+		uart_unregister_driver(&pic32_uart_driver);
+	}
+
+	return ret;
+}
+arch_initcall(pic32_uart_init);
+
+static void __exit pic32_uart_exit(void)
+{
+#ifdef CONFIG_SERIAL_PIC32_CONSOLE
+	unregister_console(&pic32_console);
+#endif
+	platform_driver_unregister(&pic32_uart_platform_driver);
+	uart_unregister_driver(&pic32_uart_driver);
+}
+module_exit(pic32_uart_exit);
+
+MODULE_AUTHOR("Steve Scott <steve.scott@microchip.com>");
+MODULE_DESCRIPTION("Microchip PIC32 integrated serial port driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/pic32_uart.h b/drivers/tty/serial/pic32_uart.h
new file mode 100644
index 0000000..b2f6960
--- /dev/null
+++ b/drivers/tty/serial/pic32_uart.h
@@ -0,0 +1,198 @@
+/*
+ * PIC32 Integrated Serial Driver.
+ *
+ * Copyright (C) 2015 Microchip Technology, Inc.
+ *
+ * Authors:
+ *   Sorin-Andrei Pistirica <andrei.pistirica@microchip.com>
+ *
+ * Licensed under GPLv2 or later.
+ */
+#ifndef __DT_PIC32_UART_H__
+#define __DT_PIC32_UART_H__
+
+#define PIC32_UART_DFLT_BRATE		(9600)
+#define PIC32_UART_TX_FIFO_DEPTH	(8)
+#define PIC32_UART_RX_FIFO_DEPTH	(8)
+
+struct pic32_console_opt {
+	int baud;
+	int parity;
+	int bits;
+	int flow;
+};
+/* struct pic32_sport - pic32 serial port descriptor
+ * @port: uart port descriptor
+ * @idx: port index
+ * @irq_fault: virtual fault interrupt number
+ * @irqflags_fault: flags related to fault irq
+ * @irq_fault_name: irq fault name
+ * @irq_rx: virtual rx interrupt number
+ * @irqflags_rx: flags related to rx irq
+ * @irq_rx_name: irq rx name
+ * @irq_tx: virtual tx interrupt number
+ * @irqflags_tx: : flags related to tx irq
+ * @irq_tx_name: irq tx name
+ * @cts_gpio: clear to send gpio
+ * @dev: device descriptor
+ **/
+struct pic32_sport {
+	struct uart_port port;
+	struct pic32_console_opt opt;
+	int idx;
+
+	int irq_fault;
+	int irqflags_fault;
+	const char *irq_fault_name;
+	int irq_rx;
+	int irqflags_rx;
+	const char *irq_rx_name;
+	int irq_tx;
+	int irqflags_tx;
+	const char *irq_tx_name;
+	u8 enable_tx_irq;
+
+	bool hw_flow_ctrl;
+	int cts_gpio;
+
+	int ref_clk;
+	struct clk *clk;
+
+	struct device *dev;
+};
+#define to_pic32_sport(c) container_of(c, struct pic32_sport, port)
+#define pic32_get_port(sport) (&sport->port)
+#define pic32_get_opt(sport) (&sport->opt)
+#define tx_irq_enabled(sport) (sport->enable_tx_irq)
+
+struct pic32_reg {
+	u32 val;
+	u32 clr;
+	u32 set;
+	u32 inv;
+} __packed;
+#define PIC32_REGS 4
+#define PIC32_REG_SIZE 4
+
+enum pic32_uart_regs {
+	PIC32_UART_UNKNOWN	= 0,
+	PIC32_UART_MODE		= 1,
+	PIC32_UART_STA		= 2,
+	PIC32_UART_TX		= 3,
+	PIC32_UART_RX		= 4,
+	PIC32_UART_BRG		= 5,
+
+	/* add above this line */
+	PIC32_UART_LAST
+};
+
+/* uart register offsets */
+static u32 pic32_uart_lookup_reg[PIC32_UART_LAST] = {
+	[PIC32_UART_MODE]	= 0 * PIC32_REGS * PIC32_REG_SIZE,
+	[PIC32_UART_STA]	= 1 * PIC32_REGS * PIC32_REG_SIZE,
+	[PIC32_UART_TX]		= 2 * PIC32_REGS * PIC32_REG_SIZE,
+	[PIC32_UART_RX]		= 3 * PIC32_REGS * PIC32_REG_SIZE,
+	[PIC32_UART_BRG]	= 4 * PIC32_REGS * PIC32_REG_SIZE,
+};
+
+static inline void __iomem *pic32_uart_get_reg(struct pic32_sport *sport,
+					       enum pic32_uart_regs reg)
+{
+	struct uart_port *port = pic32_get_port(sport);
+
+	return port->membase + pic32_uart_lookup_reg[reg];
+}
+
+static inline u32 pic32_uart_rval(struct pic32_sport *sport,
+				  enum pic32_uart_regs reg)
+{
+	void __iomem *addr = pic32_uart_get_reg(sport, reg);
+	struct pic32_reg __iomem *reg_addr = (struct pic32_reg __iomem *)addr;
+
+	return readl(&reg_addr->val);
+}
+
+static inline void pic32_uart_rset(u32 val,
+				   struct pic32_sport *sport,
+				   enum pic32_uart_regs reg)
+{
+	void __iomem *addr = pic32_uart_get_reg(sport, reg);
+	struct pic32_reg __iomem *reg_addr = (struct pic32_reg __iomem *)addr;
+
+	writel(val, &reg_addr->set);
+}
+
+static inline void pic32_uart_rclr(u32 val,
+				   struct pic32_sport *sport,
+				   enum pic32_uart_regs reg)
+{
+	void __iomem *addr = pic32_uart_get_reg(sport, reg);
+	struct pic32_reg __iomem *reg_addr = (struct pic32_reg __iomem *)addr;
+
+	writel(val, &reg_addr->clr);
+}
+
+static inline void pic32_uart_rinv(u32 val,
+				   struct pic32_sport *sport,
+				   enum pic32_uart_regs reg)
+{
+	void __iomem *addr = pic32_uart_get_reg(sport, reg);
+	struct pic32_reg __iomem *reg_addr = (struct pic32_reg __iomem *)addr;
+
+	writel(val, &reg_addr->inv);
+}
+
+static inline void pic32_uart_write(u32 val,
+				    struct pic32_sport *sport,
+				    enum pic32_uart_regs reg)
+{
+	void __iomem *addr = pic32_uart_get_reg(sport, reg);
+
+	writel(val, addr);
+}
+
+static inline u32 pic32_uart_read(struct pic32_sport *sport,
+				  enum pic32_uart_regs reg)
+{
+	void __iomem *addr = pic32_uart_get_reg(sport, reg);
+
+	return readl(addr);
+}
+
+/* pic32 uart mode register bits */
+#define PIC32_UART_MODE_ON        (1 << 15)
+#define PIC32_UART_MODE_FRZ       (1 << 14)
+#define PIC32_UART_MODE_SIDL      (1 << 13)
+#define PIC32_UART_MODE_IREN      (1 << 12)
+#define PIC32_UART_MODE_RTSMD     (1 << 11)
+#define PIC32_UART_MODE_RESV1     (1 << 10)
+#define PIC32_UART_MODE_UEN1      (1 << 9)
+#define PIC32_UART_MODE_UEN0      (1 << 8)
+#define PIC32_UART_MODE_WAKE      (1 << 7)
+#define PIC32_UART_MODE_LPBK      (1 << 6)
+#define PIC32_UART_MODE_ABAUD     (1 << 5)
+#define PIC32_UART_MODE_RXINV     (1 << 4)
+#define PIC32_UART_MODE_BRGH      (1 << 3)
+#define PIC32_UART_MODE_PDSEL1    (1 << 2)
+#define PIC32_UART_MODE_PDSEL0    (1 << 1)
+#define PIC32_UART_MODE_STSEL     (1 << 0)
+
+/* pic32 uart status register bits */
+#define PIC32_UART_STA_UTXISEL1   (1 << 15)
+#define PIC32_UART_STA_UTXISEL0   (1 << 14)
+#define PIC32_UART_STA_UTXINV     (1 << 13)
+#define PIC32_UART_STA_URXEN      (1 << 12)
+#define PIC32_UART_STA_UTXBRK     (1 << 11)
+#define PIC32_UART_STA_UTXEN      (1 << 10)
+#define PIC32_UART_STA_UTXBF      (1 << 9)
+#define PIC32_UART_STA_TRMT       (1 << 8)
+#define PIC32_UART_STA_URXISEL1   (1 << 7)
+#define PIC32_UART_STA_URXISEL0   (1 << 6)
+#define PIC32_UART_STA_ADDEN      (1 << 5)
+#define PIC32_UART_STA_RIDLE      (1 << 4)
+#define PIC32_UART_STA_PERR       (1 << 3)
+#define PIC32_UART_STA_FERR       (1 << 2)
+#define PIC32_UART_STA_OERR       (1 << 1)
+#define PIC32_UART_STA_URXDA      (1 << 0)
+
+#endif /* __DT_PIC32_UART_H__ */
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 93ba148..9df0a98 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -261,4 +261,7 @@
 /* STM32 USART */
 #define PORT_STM32	113
 
+/* Microchip PIC32 UART */
+#define PORT_PIC32	114
+
 #endif /* _UAPILINUX_SERIAL_CORE_H */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 11/14] DEVICETREE: Add bindings for PIC32 SDHCI host controller
  2015-12-14 22:42 [PATCH v2 00/14] Initial Microchip PIC32MZDA Support Joshua Henderson
                   ` (9 preceding siblings ...)
  2015-12-14 22:42 ` [PATCH v2 10/14] serial: pic32_uart: Add " Joshua Henderson
@ 2015-12-14 22:42 ` Joshua Henderson
  2015-12-15 20:00   ` Rob Herring
  2015-12-14 22:42 ` [PATCH v2 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver Joshua Henderson
                   ` (2 subsequent siblings)
  13 siblings, 1 reply; 33+ messages in thread
From: Joshua Henderson @ 2015-12-14 22:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, ralf, Andrei Pistirica, Joshua Henderson,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree

From: Andrei Pistirica <andrei.pistirica@microchip.com>

Document the devicetree bindings for the SDHCI peripheral found on
Microchip PIC32 class devices.

Signed-off-by: Andrei Pistirica <andrei.pistirica@microchip.com>
Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
---
 .../bindings/mmc/microchip,sdhci-pic32.txt         |   29 ++++++++++++++++++++
 1 file changed, 29 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/microchip,sdhci-pic32.txt

diff --git a/Documentation/devicetree/bindings/mmc/microchip,sdhci-pic32.txt b/Documentation/devicetree/bindings/mmc/microchip,sdhci-pic32.txt
new file mode 100644
index 0000000..71ad57e
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/microchip,sdhci-pic32.txt
@@ -0,0 +1,29 @@
+* Microchip PIC32 SDHCI Controller
+
+This file documents differences between the core properties in mmc.txt
+and the properties used by the sdhci-pic32 driver.
+
+Required properties:
+- compatible: Should be "microchip,pic32mzda-sdhci"
+- interrupts: Should contain interrupt
+- clock-names: Should be "base_clk", "sys_clk".
+               See: Documentation/devicetree/bindings/resource-names.txt
+- clocks: Phandle to the clock.
+          See: Documentation/devicetree/bindings/clock/clock-bindings.txt
+- pinctrl-names: A pinctrl state names "default" must be defined.
+- pinctrl-0: Phandle referencing pin configuration of the SDHCI controller.
+             See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
+
+Example:
+
+	sdhci@1f8ec000 {
+		compatible = "microchip,pic32mzda-sdhci";
+		reg = <0x1f8ec000 0x100>;
+		interrupts = <191 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&REFCLKO4>, <&PBCLK5>;
+		clock-names = "base_clk", "sys_clk";
+		bus-width = <4>;
+		cap-sd-highspeed;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_sdhc1>;
+	};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver
  2015-12-14 22:42 [PATCH v2 00/14] Initial Microchip PIC32MZDA Support Joshua Henderson
                   ` (10 preceding siblings ...)
  2015-12-14 22:42 ` [PATCH v2 11/14] DEVICETREE: Add bindings for PIC32 SDHCI host controller Joshua Henderson
@ 2015-12-14 22:42 ` Joshua Henderson
  2015-12-15  0:32   ` Andy Green
  2015-12-16 10:48   ` Ulf Hansson
  2015-12-14 22:42 ` [PATCH v2 13/14] MIPS: dts: Add initial DTS for the PIC32MZDA Starter Kit Joshua Henderson
  2015-12-14 22:42 ` [PATCH v2 14/14] MIPS: pic32mzda: Add initial PIC32MZDA Starter Kit defconfig Joshua Henderson
  13 siblings, 2 replies; 33+ messages in thread
From: Joshua Henderson @ 2015-12-14 22:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, ralf, Andrei Pistirica, Joshua Henderson,
	Ulf Hansson, Jean Delvare, Geert Uytterhoeven, Corneliu Doban,
	Haojian Zhuang, Luis de Bethencourt, Weijun Yang, Lokesh Vutla,
	Scott Branden, Vincent Yang, Chaotian Jing, ludovic.desroches,
	Shawn Lin, Stephen Boyd, yangbo lu, Kevin Hao, Ben Hutchings,
	Andy Green, linux-mmc

From: Andrei Pistirica <andrei.pistirica@microchip.com>

This driver supports the SDHCI host controller found on a PIC32.

Signed-off-by: Andrei Pistirica <andrei.pistirica@microchip.com>
Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
---
 drivers/mmc/host/Kconfig       |   11 ++
 drivers/mmc/host/Makefile      |    1 +
 drivers/mmc/host/sdhci-pic32.c |  291 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 303 insertions(+)
 create mode 100644 drivers/mmc/host/sdhci-pic32.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 1dee533..1a3a42b 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -785,3 +785,14 @@ config MMC_MTK
 	  If you have a machine with a integrated SD/MMC card reader, say Y or M here.
 	  This is needed if support for any SD/SDIO/MMC devices is required.
 	  If unsure, say N.
+
+config MMC_SDHCI_MICROCHIP_PIC32
+        tristate "Microchip PIC32MZDA SDHCI support"
+        depends on MMC_SDHCI && PIC32MZDA
+        help
+          This selects the Secure Digital Host Controller Interface (SDHCI)
+          for PIC32MZDA platform.
+
+          If you have a controller with this interface, say Y or M here.
+
+          If unsure, say N.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 3595f83..af918d2 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_MMC_SDHCI_BCM2835)		+= sdhci-bcm2835.o
 obj-$(CONFIG_MMC_SDHCI_IPROC)		+= sdhci-iproc.o
 obj-$(CONFIG_MMC_SDHCI_MSM)		+= sdhci-msm.o
 obj-$(CONFIG_MMC_SDHCI_ST)		+= sdhci-st.o
+obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32)	+= sdhci-pic32.o
 
 ifeq ($(CONFIG_CB710_DEBUG),y)
 	CFLAGS-cb710-mmc	+= -DDEBUG
diff --git a/drivers/mmc/host/sdhci-pic32.c b/drivers/mmc/host/sdhci-pic32.c
new file mode 100644
index 0000000..b7d7da2
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pic32.c
@@ -0,0 +1,291 @@
+/*
+ * Support of SDHCI platform devices for Microchip PIC32.
+ *
+ * Copyright (C) 2015 Microchip
+ * Andrei Pistirica, Paul Thacker
+ *
+ * Inspired by sdhci-pltfm.c
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/mmc/host.h>
+#include <linux/io.h>
+#include "sdhci.h"
+#include <linux/platform_data/sdhci-pic32.h>
+
+#define SDH_SHARED_BUS_CTRL		0x000000E0
+#define SDH_SHARED_BUS_NR_CLK_PINS_MASK	0x7
+#define SDH_SHARED_BUS_NR_IRQ_PINS_MASK	0x30
+#define SDH_SHARED_BUS_CLK_PINS		0x10
+#define SDH_SHARED_BUS_IRQ_PINS		0x14
+#define SDH_CAPS_SDH_SLOT_TYPE_MASK	0xC0000000
+#define SDH_SLOT_TYPE_REMOVABLE		0x0
+#define SDH_SLOT_TYPE_EMBEDDED		0x1
+#define SDH_SLOT_TYPE_SHARED_BUS	0x2
+#define SDHCI_CTRL_CDSSEL		0x80
+#define SDHCI_CTRL_CDTLVL		0x40
+
+#define ADMA_FIFO_RD_THSHLD	512
+#define ADMA_FIFO_WR_THSHLD	512
+
+#define DEV_NAME "pic32-sdhci"
+
+struct pic32_sdhci_pdata {
+	struct platform_device	*pdev;
+	struct clk *sys_clk;
+	struct clk *base_clk;
+};
+
+static unsigned int pic32_sdhci_get_max_clock(struct sdhci_host *host)
+{
+	struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
+
+	return clk_get_rate(sdhci_pdata->base_clk);
+}
+
+static void pic32_sdhci_set_bus_width(struct sdhci_host *host, int width)
+{
+	u8 ctrl;
+
+	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+	if (width == MMC_BUS_WIDTH_8) {
+		ctrl &= ~SDHCI_CTRL_4BITBUS;
+		if (host->version >= SDHCI_SPEC_300)
+			ctrl |= SDHCI_CTRL_8BITBUS;
+	} else {
+		if (host->version >= SDHCI_SPEC_300)
+			ctrl &= ~SDHCI_CTRL_8BITBUS;
+		if (width == MMC_BUS_WIDTH_4)
+			ctrl |= SDHCI_CTRL_4BITBUS;
+		else
+			ctrl &= ~SDHCI_CTRL_4BITBUS;
+	}
+	/*
+	 * SDHCI will not work if JTAG is not Connected.As a workaround fix,
+	 * set Card Detect Signal Selection bit in SDHCI Host Control
+	 * register and clear Card Detect Test Level bit in SDHCI Host
+	 * Control register.
+	 */
+	ctrl &= ~SDHCI_CTRL_CDTLVL;
+	ctrl |= SDHCI_CTRL_CDSSEL;
+	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+}
+
+static unsigned int pic32_sdhci_get_ro(struct sdhci_host *host)
+{
+	/*
+	 * The SDHCI_WRITE_PROTECT bit is unstable on current hardware so we
+	 * can't depend on its value in any way.
+	 */
+	return 0;
+}
+
+static const struct sdhci_ops pic32_sdhci_ops = {
+	.get_max_clock = pic32_sdhci_get_max_clock,
+	.set_clock = sdhci_set_clock,
+	.set_bus_width = pic32_sdhci_set_bus_width,
+	.reset = sdhci_reset,
+	.set_uhs_signaling = sdhci_set_uhs_signaling,
+	.get_ro = pic32_sdhci_get_ro,
+};
+
+static void pic32_sdhci_shared_bus(struct platform_device *pdev)
+{
+	struct sdhci_host *host = platform_get_drvdata(pdev);
+	u32 bus = readl(host->ioaddr + SDH_SHARED_BUS_CTRL);
+	u32 clk_pins = (bus & SDH_SHARED_BUS_NR_CLK_PINS_MASK) >> 0;
+	u32 irq_pins = (bus & SDH_SHARED_BUS_NR_IRQ_PINS_MASK) >> 4;
+
+	/* select first clock */
+	if (clk_pins & 0x1)
+		bus |= (0x1 << SDH_SHARED_BUS_CLK_PINS);
+
+	/* select first interrupt */
+	if (irq_pins & 0x1)
+		bus |= (0x1 << SDH_SHARED_BUS_IRQ_PINS);
+
+	writel(bus, host->ioaddr + SDH_SHARED_BUS_CTRL);
+}
+
+static int pic32_sdhci_probe_platform(struct platform_device *pdev,
+				      struct pic32_sdhci_pdata *pdata)
+{
+	int ret = 0;
+	u32 caps_slot_type;
+	struct sdhci_host *host = platform_get_drvdata(pdev);
+
+	/* Check card slot connected on shared bus. */
+	host->caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
+	caps_slot_type = (host->caps & SDH_CAPS_SDH_SLOT_TYPE_MASK) >> 30;
+	if (caps_slot_type == SDH_SLOT_TYPE_SHARED_BUS)
+		pic32_sdhci_shared_bus(pdev);
+
+	return ret;
+}
+
+static int pic32_sdhci_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct sdhci_host *host;
+	struct resource *iomem;
+	struct pic32_sdhci_pdata *sdhci_pdata;
+	struct pic32_sdhci_platform_data *plat_data;
+	unsigned int clk_rate = 0;
+	int ret;
+	struct pinctrl *pinctrl;
+
+	host = sdhci_alloc_host(dev, sizeof(*sdhci_pdata));
+	if (IS_ERR(host)) {
+		ret = PTR_ERR(host);
+		dev_err(&pdev->dev, "cannot allocate memory for sdhci\n");
+		goto err;
+	}
+
+	sdhci_pdata = sdhci_priv(host);
+	sdhci_pdata->pdev = pdev;
+	platform_set_drvdata(pdev, host);
+
+	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
+	if (IS_ERR(host->ioaddr)) {
+		ret = PTR_ERR(host->ioaddr);
+		dev_err(&pdev->dev, "unable to map iomem: %d\n", ret);
+		goto err_host;
+	}
+
+	plat_data = pdev->dev.platform_data;
+	if (plat_data && plat_data->setup_dma) {
+		ret = plat_data->setup_dma(ADMA_FIFO_RD_THSHLD,
+					   ADMA_FIFO_WR_THSHLD);
+		if (ret)
+			goto err_host;
+	}
+
+	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+	if (IS_ERR(pinctrl)) {
+		ret = PTR_ERR(pinctrl);
+		dev_warn(&pdev->dev, "No pinctrl provided %d\n", ret);
+		if (ret == -EPROBE_DEFER)
+			goto err_host;
+	}
+
+	host->ops = &pic32_sdhci_ops;
+	host->irq = platform_get_irq(pdev, 0);
+
+	sdhci_pdata->sys_clk = devm_clk_get(&pdev->dev, "sys_clk");
+	if (IS_ERR(sdhci_pdata->sys_clk)) {
+		ret = PTR_ERR(sdhci_pdata->sys_clk);
+		dev_err(&pdev->dev, "Error getting clock\n");
+		goto err_host;
+	}
+
+	/* Enable clock when available! */
+	ret = clk_prepare_enable(sdhci_pdata->sys_clk);
+	if (ret) {
+		dev_dbg(&pdev->dev, "Error enabling clock\n");
+		goto err_host;
+	}
+
+	/* SDH CLK enable */
+	sdhci_pdata->base_clk = devm_clk_get(&pdev->dev, "base_clk");
+	if (IS_ERR(sdhci_pdata->base_clk)) {
+		ret = PTR_ERR(sdhci_pdata->base_clk);
+		dev_err(&pdev->dev, "Error getting clock\n");
+		goto err_host;
+	}
+
+	/* Enable clock when available! */
+	ret = clk_prepare_enable(sdhci_pdata->base_clk);
+	if (ret) {
+		dev_dbg(&pdev->dev, "Error enabling clock\n");
+		goto err_host;
+	}
+
+	clk_rate = clk_get_rate(sdhci_pdata->base_clk);
+	dev_dbg(&pdev->dev, "base clock at: %u\n", clk_rate);
+	clk_rate = clk_get_rate(sdhci_pdata->sys_clk);
+	dev_dbg(&pdev->dev, "sys clock at: %u\n", clk_rate);
+
+	host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
+
+	host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
+
+	ret = mmc_of_parse(host->mmc);
+	if (ret)
+		goto err_host;
+
+	ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to probe platform!\n");
+		goto err_host;
+	}
+
+	ret = sdhci_add_host(host);
+	if (ret) {
+		dev_dbg(&pdev->dev, "error adding host\n");
+		goto err_host;
+	}
+
+	dev_info(&pdev->dev, "Successfully added sdhci host\n");
+	return 0;
+
+err_host:
+	sdhci_free_host(host);
+err:
+	dev_err(&pdev->dev, "pic32-sdhci probe failed: %d\n", ret);
+	return ret;
+}
+
+static int pic32_sdhci_remove(struct platform_device *pdev)
+{
+	struct sdhci_host *host = platform_get_drvdata(pdev);
+	struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
+	int dead = 0;
+	u32 scratch;
+
+	scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
+	if (scratch == (u32)-1)
+		dead = 1;
+
+	sdhci_remove_host(host, dead);
+	clk_disable_unprepare(sdhci_pdata->base_clk);
+	clk_disable_unprepare(sdhci_pdata->sys_clk);
+	sdhci_free_host(host);
+
+	return 0;
+}
+
+static const struct of_device_id pic32_sdhci_id_table[] = {
+	{ .compatible = "microchip,pic32mzda-sdhci" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, pic32_sdhci_id_table);
+
+static struct platform_driver pic32_sdhci_driver = {
+	.driver = {
+		.name	= DEV_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(pic32_sdhci_id_table),
+	},
+	.probe		= pic32_sdhci_probe,
+	.remove		= pic32_sdhci_remove,
+};
+
+module_platform_driver(pic32_sdhci_driver);
+
+MODULE_DESCRIPTION("Microchip PIC32 SDHCI driver");
+MODULE_AUTHOR("Pistirica Sorin Andrei & Sandeep Sheriker");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 13/14] MIPS: dts: Add initial DTS for the PIC32MZDA Starter Kit
  2015-12-14 22:42 [PATCH v2 00/14] Initial Microchip PIC32MZDA Support Joshua Henderson
                   ` (11 preceding siblings ...)
  2015-12-14 22:42 ` [PATCH v2 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver Joshua Henderson
@ 2015-12-14 22:42 ` Joshua Henderson
  2015-12-14 22:42 ` [PATCH v2 14/14] MIPS: pic32mzda: Add initial PIC32MZDA Starter Kit defconfig Joshua Henderson
  13 siblings, 0 replies; 33+ messages in thread
From: Joshua Henderson @ 2015-12-14 22:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, ralf, Joshua Henderson, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Andrew Bresticker,
	Paul Burton, devicetree

This adds basic DTS configuration for the PIC32MZDA chip and in turn the
PIC32MZDA Starter Kit.

Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
---
 arch/mips/boot/dts/Makefile                 |    1 +
 arch/mips/boot/dts/pic32/Makefile           |   12 ++
 arch/mips/boot/dts/pic32/pic32mzda-clk.dtsi |  235 +++++++++++++++++++++++
 arch/mips/boot/dts/pic32/pic32mzda.dtsi     |  275 +++++++++++++++++++++++++++
 arch/mips/boot/dts/pic32/pic32mzda_sk.dts   |  151 +++++++++++++++
 arch/mips/pic32/Kconfig                     |   16 ++
 6 files changed, 690 insertions(+)
 create mode 100644 arch/mips/boot/dts/pic32/Makefile
 create mode 100644 arch/mips/boot/dts/pic32/pic32mzda-clk.dtsi
 create mode 100644 arch/mips/boot/dts/pic32/pic32mzda.dtsi
 create mode 100644 arch/mips/boot/dts/pic32/pic32mzda_sk.dts

diff --git a/arch/mips/boot/dts/Makefile b/arch/mips/boot/dts/Makefile
index a0bf516..fc7a0a9 100644
--- a/arch/mips/boot/dts/Makefile
+++ b/arch/mips/boot/dts/Makefile
@@ -4,6 +4,7 @@ dts-dirs	+= ingenic
 dts-dirs	+= lantiq
 dts-dirs	+= mti
 dts-dirs	+= netlogic
+dts-dirs	+= pic32
 dts-dirs	+= qca
 dts-dirs	+= ralink
 dts-dirs	+= xilfpga
diff --git a/arch/mips/boot/dts/pic32/Makefile b/arch/mips/boot/dts/pic32/Makefile
new file mode 100644
index 0000000..7ac7905
--- /dev/null
+++ b/arch/mips/boot/dts/pic32/Makefile
@@ -0,0 +1,12 @@
+dtb-$(CONFIG_DTB_PIC32_MZDA_SK)		+= pic32mzda_sk.dtb
+
+dtb-$(CONFIG_DTB_PIC32_NONE)		+= \
+					pic32mzda_sk.dtb
+
+obj-y				+= $(patsubst %.dtb, %.dtb.o, $(dtb-y))
+
+# Force kbuild to make empty built-in.o if necessary
+obj-				+= dummy.o
+
+always				:= $(dtb-y)
+clean-files			:= *.dtb *.dtb.S
diff --git a/arch/mips/boot/dts/pic32/pic32mzda-clk.dtsi b/arch/mips/boot/dts/pic32/pic32mzda-clk.dtsi
new file mode 100644
index 0000000..9ab4df8
--- /dev/null
+++ b/arch/mips/boot/dts/pic32/pic32mzda-clk.dtsi
@@ -0,0 +1,235 @@
+/*
+ * Device Tree Source for PIC32MZDA clock data
+ *
+ * Purna Chandra Mandal <purna.mandal@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+/* all fixed rate clocks */
+
+/ {
+	POSC:posc_clk { /* On-chip primary oscillator */
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <24000000>;
+	};
+
+	FRC:frc_clk { /* internal FRC oscillator */
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <8000000>;
+	};
+
+	BFRC:bfrc_clk { /* internal backup FRC oscillator */
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <8000000>;
+	};
+
+	LPRC:lprc_clk { /* internal low-power FRC oscillator */
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <32000>;
+	};
+
+	/* UPLL provides UTMI clock to USBCORE */
+	UPLL:usb_phy_clk {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <24000000>;
+		clock-output-names = "usbphy_clk";
+	};
+
+	TxCKI:txcki_clk { /* external clock input on TxCLKI pin */
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <4000000>;
+		status = "disabled";
+	};
+
+	/* external clock input on REFCLKIx pin */
+	REFIx:refix_clk {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <24000000>;
+		status = "disabled";
+	};
+
+	/* PIC32 specific clks */
+	pic32_clktree {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0x1f801200 0x200>;
+		compatible = "microchip,pic32mzda-clk";
+		ranges = <0 0x1f801200 0x200>;
+
+		/* secondary oscillator; external input on SOSCI pin */
+		SOSC:sosc_clk {
+			#clock-cells = <0>;
+			compatible = "microchip,pic32mzda-sosc";
+			clock-frequency = <32768>;
+			reg = <0x000 0x10>,   /* enable reg */
+			      <0x1d0 0x10>; /* status reg */
+			microchip,bit-mask = <0x02>; /* enable mask */
+			microchip,status-bit-mask = <0x10>; /* status-mask*/
+		};
+
+		FRCDIV:frcdiv_clk {
+			#clock-cells = <0>;
+			compatible = "microchip,pic32mzda-frcdivclk";
+			clocks = <&FRC>;
+			clock-output-names = "frcdiv_clk";
+		};
+
+		/* System PLL clock */
+		SYSPLL:spll_clk {
+			#clock-cells = <0>;
+			compatible = "microchip,pic32mzda-syspll";
+			reg = <0x020 0x10>, /* SPLL register */
+			      <0x1d0 0x10>; /* CLKSTAT register */
+			clocks = <&POSC>, <&FRC>;
+			clock-output-names = "sys_pll";
+			microchip,status-bit-mask = <0x80>; /* SPLLRDY */
+		};
+
+		/* system clock; mux with postdiv & slew */
+		SYSCLK:sys_clk {
+			#clock-cells = <0>;
+			compatible = "microchip,pic32mzda-sysclk-v2";
+			reg = <0x1c0 0x04>; /* SLEWCON */
+			clocks = <&FRCDIV>, <&SYSPLL>, <&POSC>, <&SOSC>,
+				 <&LPRC>, <&FRCDIV>;
+			microchip,clock-indices = <0>, <1>, <2>, <4>, <5>, <7>;
+			clock-output-names = "sys_clk";
+		};
+
+		/* Peripheral bus1 clock */
+		PBCLK1:pb1_clk {
+			reg = <0x140 0x10>;
+			#clock-cells = <0>;
+			compatible = "microchip,pic32mzda-pbclk";
+			clocks = <&SYSCLK>;
+			clock-output-names = "pb1_clk";
+			/* used by system modules, not gateable */
+			microchip,ignore-unused;
+		};
+
+		/* Peripheral bus2 clock */
+		PBCLK2:pb2_clk {
+			reg = <0x150 0x10>;
+			#clock-cells = <0>;
+			compatible = "microchip,pic32mzda-pbclk";
+			clocks = <&SYSCLK>;
+			clock-output-names = "pb2_clk";
+			/* avoid gating even if unused */
+			microchip,ignore-unused;
+		};
+
+		/* Peripheral bus3 clock */
+		PBCLK3:pb3_clk {
+			reg = <0x160 0x10>;
+			#clock-cells = <0>;
+			compatible = "microchip,pic32mzda-pbclk";
+			clocks = <&SYSCLK>;
+			clock-output-names = "pb3_clk";
+		};
+
+		/* Peripheral bus4 clock(I/O ports, GPIO) */
+		PBCLK4:pb4_clk {
+			reg = <0x170 0x10>;
+			#clock-cells = <0>;
+			compatible = "microchip,pic32mzda-pbclk";
+			clocks = <&SYSCLK>;
+			clock-output-names = "pb4_clk";
+		};
+
+		/* Peripheral bus clock */
+		PBCLK5:pb5_clk {
+			reg = <0x180 0x10>;
+			#clock-cells = <0>;
+			compatible = "microchip,pic32mzda-pbclk";
+			clocks = <&SYSCLK>;
+			clock-output-names = "pb5_clk";
+		};
+
+		/* Peripheral Bus6 clock; */
+		PBCLK6:pb6_clk {
+			reg = <0x190 0x10>;
+			compatible = "microchip,pic32mzda-pbclk";
+			clocks = <&SYSCLK>;
+			#clock-cells = <0>;
+		};
+
+		/* Peripheral bus7 clock */
+		PBCLK7:pb7_clk {
+			reg = <0x1A0 0x10>;
+			#clock-cells = <0>;
+			compatible = "microchip,pic32mzda-pbclk";
+			/* CPU is driven by this clock; so named */
+			clock-output-names = "cpu_clk";
+			clocks = <&SYSCLK>;
+		};
+
+		/* Reference Oscillator clock for SPI/I2S */
+		REFCLKO1:refo1_clk {
+			reg = <0x080 0x20>;
+			#clock-cells = <0>;
+			compatible = "microchip,pic32mzda-refoclk";
+			clocks = <&SYSCLK>, <&PBCLK1>, <&POSC>, <&FRC>, <&LPRC>,
+				<&SOSC>, <&SYSPLL>, <&REFIx>, <&BFRC>;
+			microchip,clock-indices = <0>, <1>, <2>, <3>, <4>,
+						  <5>, <7>, <8>, <9>;
+			clock-output-names = "refo1_clk";
+		};
+
+		/* Reference Oscillator clock for SQI */
+		REFCLKO2:refo2_clk {
+			reg = <0x0A0 0x20>;
+			#clock-cells = <0>;
+			compatible = "microchip,pic32mzda-refoclk";
+			clocks = <&SYSCLK>, <&PBCLK1>, <&POSC>, <&FRC>, <&LPRC>,
+				<&SOSC>, <&SYSPLL>, <&REFIx>, <&BFRC>;
+			microchip,clock-indices = <0>, <1>, <2>, <3>, <4>,
+						  <5>, <7>, <8>, <9>;
+			clock-output-names = "refo2_clk";
+		};
+
+		/* Reference Oscillator clock, ADC */
+		REFCLKO3:refo3_clk {
+			reg = <0x0C0 0x20>;
+			compatible = "microchip,pic32mzda-refoclk";
+			clocks = <&SYSCLK>, <&PBCLK1>, <&POSC>, <&FRC>, <&LPRC>,
+				<&SOSC>, <&SYSPLL>, <&REFIx>, <&BFRC>;
+			microchip,clock-indices = <0>, <1>, <2>, <3>, <4>,
+						  <5>, <7>, <8>, <9>;
+			#clock-cells = <0>;
+			clock-output-names = "refo3_clk";
+		};
+
+		/* Reference Oscillator clock */
+		REFCLKO4:refo4_clk {
+			reg = <0x0E0 0x20>;
+			compatible = "microchip,pic32mzda-refoclk";
+			clocks = <&SYSCLK>, <&PBCLK1>, <&POSC>, <&FRC>, <&LPRC>,
+					<&SOSC>, <&SYSPLL>, <&REFIx>, <&BFRC>;
+			microchip,clock-indices = <0>, <1>, <2>, <3>, <4>,
+						  <5>, <7>, <8>, <9>;
+			#clock-cells = <0>;
+			clock-output-names = "refo4_clk";
+		};
+
+		/* Reference Oscillator clock, LCD */
+		REFCLKO5:refo5_clk {
+			reg = <0x100 0x20>;
+			compatible = "microchip,pic32mzda-refoclk";
+			clocks = <&SYSCLK>,<&PBCLK1>,<&POSC>,<&FRC>,<&LPRC>,
+				<&SOSC>,<&SYSPLL>,<&REFIx>,<&BFRC>;
+			microchip,clock-indices = <0>, <1>, <2>, <3>, <4>,
+						  <5>, <7>, <8>, <9>;
+			#clock-cells = <0>;
+			clock-output-names = "refo5_clk";
+		};
+	};
+};
diff --git a/arch/mips/boot/dts/pic32/pic32mzda.dtsi b/arch/mips/boot/dts/pic32/pic32mzda.dtsi
new file mode 100644
index 0000000..3eee106
--- /dev/null
+++ b/arch/mips/boot/dts/pic32/pic32mzda.dtsi
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#include "pic32mzda-clk.dtsi"
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	interrupt-parent = <&evic>;
+
+	aliases {
+		gpio0 = &gpio0;
+		gpio1 = &gpio1;
+		gpio2 = &gpio2;
+		gpio3 = &gpio3;
+		gpio4 = &gpio4;
+		gpio5 = &gpio5;
+		gpio6 = &gpio6;
+		gpio7 = &gpio7;
+		gpio8 = &gpio8;
+		gpio9 = &gpio9;
+		serial0 = &uart1;
+		serial1 = &uart2;
+		serial2 = &uart3;
+		serial3 = &uart4;
+		serial4 = &uart5;
+		serial5 = &uart6;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu@0 {
+			compatible = "mti,mips14KEc";
+			device_type = "cpu";
+		};
+	};
+
+	evic: interrupt-controller@1f810000 {
+		compatible = "microchip,pic32mzda-evic";
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		reg = <0x1f810000 0x1000>;
+	};
+
+	pic32_pinctrl: pinctrl@1f801400{
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "microchip,pic32mzda-pinctrl";
+		reg = <0x1f801400 0x400>;
+		clocks = <&PBCLK1>;
+	};
+
+	/* PORTA */
+	gpio0: gpio0@1f860000 {
+		compatible = "microchip,pic32mzda-gpio";
+		reg = <0x1f860000 0x100>;
+		interrupts = <118 IRQ_TYPE_LEVEL_HIGH>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		clocks = <&PBCLK4>;
+		microchip,gpio-bank = <0>;
+		gpio-ranges = <&pic32_pinctrl 0 0 16>;
+	};
+
+	/* PORTB */
+	gpio1: gpio1@1f860100 {
+		compatible = "microchip,pic32mzda-gpio";
+		reg = <0x1f860100 0x100>;
+		interrupts = <119 IRQ_TYPE_LEVEL_HIGH>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		clocks = <&PBCLK4>;
+		microchip,gpio-bank = <1>;
+		gpio-ranges = <&pic32_pinctrl 0 16 16>;
+	};
+
+	/* PORTC */
+	gpio2: gpio2@1f860200 {
+		compatible = "microchip,pic32mzda-gpio";
+		reg = <0x1f860200 0x100>;
+		interrupts = <120 IRQ_TYPE_LEVEL_HIGH>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		clocks = <&PBCLK4>;
+		microchip,gpio-bank = <2>;
+		gpio-ranges = <&pic32_pinctrl 0 32 16>;
+	};
+
+	/* PORTD */
+	gpio3: gpio3@1f860300 {
+		compatible = "microchip,pic32mzda-gpio";
+		reg = <0x1f860300 0x100>;
+		interrupts = <121 IRQ_TYPE_LEVEL_HIGH>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		clocks = <&PBCLK4>;
+		microchip,gpio-bank = <3>;
+		gpio-ranges = <&pic32_pinctrl 0 48 16>;
+	};
+
+	/* PORTE */
+	gpio4: gpio4@1f860400 {
+		compatible = "microchip,pic32mzda-gpio";
+		reg = <0x1f860400 0x100>;
+		interrupts = <122 IRQ_TYPE_LEVEL_HIGH>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		clocks = <&PBCLK4>;
+		microchip,gpio-bank = <4>;
+		gpio-ranges = <&pic32_pinctrl 0 64 16>;
+	};
+
+	/* PORTF */
+	gpio5: gpio5@1f860500 {
+		compatible = "microchip,pic32mzda-gpio";
+		reg = <0x1f860500 0x100>;
+		interrupts = <123 IRQ_TYPE_LEVEL_HIGH>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		clocks = <&PBCLK4>;
+		microchip,gpio-bank = <5>;
+		gpio-ranges = <&pic32_pinctrl 0 80 16>;
+	};
+
+	/* PORTG */
+	gpio6: gpio6@1f860600 {
+		compatible = "microchip,pic32mzda-gpio";
+		reg = <0x1f860600 0x100>;
+		interrupts = <124 IRQ_TYPE_LEVEL_HIGH>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		clocks = <&PBCLK4>;
+		microchip,gpio-bank = <6>;
+		gpio-ranges = <&pic32_pinctrl 0 96 16>;
+	};
+
+	/* PORTH */
+	gpio7: gpio7@1f860700 {
+		compatible = "microchip,pic32mzda-gpio";
+		reg = <0x1f860700 0x100>;
+		interrupts = <125 IRQ_TYPE_LEVEL_HIGH>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		clocks = <&PBCLK4>;
+		microchip,gpio-bank = <7>;
+		gpio-ranges = <&pic32_pinctrl 0 112 16>;
+	};
+
+	/* PORTI does not exist */
+
+	/* PORTJ */
+	gpio8: gpio8@1f860800 {
+		compatible = "microchip,pic32mzda-gpio";
+		reg = <0x1f860800 0x100>;
+		interrupts = <126 IRQ_TYPE_LEVEL_HIGH>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		clocks = <&PBCLK4>;
+		microchip,gpio-bank = <8>;
+		gpio-ranges = <&pic32_pinctrl 0 128 16>;
+	};
+
+	/* PORTK */
+	gpio9: gpio9@1f860900 {
+		compatible = "microchip,pic32mzda-gpio";
+		reg = <0x1f860900 0x100>;
+		interrupts = <127 IRQ_TYPE_LEVEL_HIGH>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		clocks = <&PBCLK4>;
+		microchip,gpio-bank = <9>;
+		gpio-ranges = <&pic32_pinctrl 0 144 16>;
+	};
+
+	sdhci: sdhci@1f8ec000 {
+		compatible = "microchip,pic32mzda-sdhci";
+		reg = <0x1f8ec000 0x100>;
+		interrupts = <191 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&REFCLKO4>, <&PBCLK5>;
+		clock-names = "base_clk", "sys_clk";
+		bus-width = <4>;
+		cap-sd-highspeed;
+		status = "disabled";
+	};
+
+	uart1: serial@1f822000 {
+		compatible = "microchip,pic32mzda-uart";
+		reg = <0x1f822000 0x50>;
+		interrupts = <112 IRQ_TYPE_LEVEL_HIGH>,
+			<113 IRQ_TYPE_LEVEL_HIGH>,
+			<114 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&PBCLK2>;
+		status = "disabled";
+	};
+
+	uart2: serial@1f822200 {
+		compatible = "microchip,pic32mzda-uart";
+		reg = <0x1f822200 0x50>;
+		interrupts = <145 IRQ_TYPE_LEVEL_HIGH>,
+			<146 IRQ_TYPE_LEVEL_HIGH>,
+			<147 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&PBCLK2>;
+		status = "disabled";
+	};
+
+	uart3: serial@1f822400 {
+		compatible = "microchip,pic32mzda-uart";
+		reg = <0x1f822400 0x50>;
+		interrupts = <157 IRQ_TYPE_LEVEL_HIGH>,
+			<158 IRQ_TYPE_LEVEL_HIGH>,
+			<159 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&PBCLK2>;
+		status = "disabled";
+	};
+
+	uart4: serial@1f822600 {
+		compatible = "microchip,pic32mzda-uart";
+		reg = <0x1f822600 0x50>;
+		interrupts = <170 IRQ_TYPE_LEVEL_HIGH>,
+			<171 IRQ_TYPE_LEVEL_HIGH>,
+			<172 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&PBCLK2>;
+		status = "disabled";
+	};
+
+	uart5: serial@1f822800 {
+		compatible = "microchip,pic32mzda-uart";
+		reg = <0x1f822800 0x50>;
+		interrupts = <179 IRQ_TYPE_LEVEL_HIGH>,
+			<180 IRQ_TYPE_LEVEL_HIGH>,
+			<181 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&PBCLK2>;
+		status = "disabled";
+	};
+
+	uart6: serial@1f822A00 {
+		compatible = "microchip,pic32mzda-uart";
+		reg = <0x1f822A00 0x50>;
+		interrupts = <188 IRQ_TYPE_LEVEL_HIGH>,
+			<189 IRQ_TYPE_LEVEL_HIGH>,
+			<190 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&PBCLK2>;
+		status = "disabled";
+	};
+};
diff --git a/arch/mips/boot/dts/pic32/pic32mzda_sk.dts b/arch/mips/boot/dts/pic32/pic32mzda_sk.dts
new file mode 100644
index 0000000..3fd2307
--- /dev/null
+++ b/arch/mips/boot/dts/pic32/pic32mzda_sk.dts
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#include "pic32mzda.dtsi"
+
+/ {
+	compatible = "microchip,pic32mzda-sk", "microchip,pic32mzda";
+	model = "Microchip PIC32MZDA Starter Kit";
+
+	memory {
+		device_type = "memory";
+		reg = <0x08000000 0x08000000>;
+	};
+
+	chosen {
+		bootargs = "earlyprintk=ttyS1,115200n8r console=ttyS1,115200n8";
+	};
+
+	leds0 {
+		compatible = "gpio-leds";
+		pinctrl-names = "default";
+		pinctrl-0 = <&user_leds_s0>;
+
+		led@1 {
+			label = "pic32mzda_sk:red:led1";
+			gpios = <&gpio7 0 GPIO_ACTIVE_HIGH>;
+			linux,default-trigger = "heartbeat";
+		};
+
+		led@2 {
+			label = "pic32mzda_sk:yellow:led2";
+			gpios = <&gpio7 1 GPIO_ACTIVE_HIGH>;
+			linux,default-trigger = "mmc0";
+		};
+
+		led@3 {
+			label = "pic32mzda_sk:green:led3";
+			gpios = <&gpio7 2 GPIO_ACTIVE_HIGH>;
+			default-state = "on";
+		};
+	};
+
+	keys0 {
+		compatible = "gpio-keys";
+		pinctrl-0 = <&user_buttons_s0>;
+		pinctrl-names = "default";
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		button@sw1 {
+			label = "ESC";
+			linux,code = <1>;
+			gpios = <&gpio1 12 0>;
+		};
+
+		button@sw2 {
+			label = "Home";
+			linux,code = <102>;
+			gpios = <&gpio1 13 0>;
+		};
+
+		button@sw3 {
+			label = "Menu";
+			linux,code = <139>;
+			gpios = <&gpio1 14 0>;
+		};
+	};
+};
+
+&uart2 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_uart2>;
+	status = "okay";
+};
+
+&uart4 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_uart4>;
+	status = "okay";
+};
+
+&sdhci {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_sdhc1>;
+	status = "okay";
+	assigned-clocks = <&REFCLKO2>,<&REFCLKO4>,<&REFCLKO5>;
+	assigned-clock-rates = <50000000>,<25000000>,<40000000>;
+};
+
+&pic32_pinctrl {
+
+	pinctrl_sdhc1: sdhc1_pins0 {
+		pins = "A6", "D4", "G13", "G12", "G14", "A7", "A0";
+		microchip,digital;
+	};
+
+	user_leds_s0: user_leds_s0 {
+		pins = "H0", "H1", "H2";
+		output-low;
+		microchip,digital;
+	};
+
+	user_buttons_s0: user_buttons_s0 {
+		pins = "B12", "B13", "B14";
+		microchip,digital;
+		input-enable;
+		bias-pull-up;
+	};
+
+	pinctrl_uart2: pinctrl_uart2 {
+		uart2-tx {
+			pins = "G9";
+			function = "U2TX";
+			microchip,digital;
+			output-low;
+		};
+		uart2-rx {
+			pins = "B0";
+			function = "U2RX";
+			microchip,digital;
+			input-enable;
+		};
+	};
+
+	pinctrl_uart4: uart4-0 {
+		uart4-tx {
+			pins = "C3";
+			function = "U4TX";
+			microchip,digital;
+			output-low;
+		};
+		uart4-rx {
+			pins = "E8";
+			function = "U4RX";
+			microchip,digital;
+			input-enable;
+		};
+	};
+};
diff --git a/arch/mips/pic32/Kconfig b/arch/mips/pic32/Kconfig
index 0161f09..b1bd7ba 100644
--- a/arch/mips/pic32/Kconfig
+++ b/arch/mips/pic32/Kconfig
@@ -31,4 +31,20 @@ config PIC32MZDA
 
 endchoice
 
+choice
+	prompt "Devicetree selection"
+	default DTB_PIC32_NONE
+	help
+	  Select the devicetree.
+
+config DTB_PIC32_NONE
+       bool "None"
+
+config DTB_PIC32_MZDA_SK
+       bool "PIC32MZDA Starter Kit"
+       depends on PIC32MZDA
+       select BUILTIN_DTB
+
+endchoice
+
 endif # MACH_PIC32
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 14/14] MIPS: pic32mzda: Add initial PIC32MZDA Starter Kit defconfig
  2015-12-14 22:42 [PATCH v2 00/14] Initial Microchip PIC32MZDA Support Joshua Henderson
                   ` (12 preceding siblings ...)
  2015-12-14 22:42 ` [PATCH v2 13/14] MIPS: dts: Add initial DTS for the PIC32MZDA Starter Kit Joshua Henderson
@ 2015-12-14 22:42 ` Joshua Henderson
  13 siblings, 0 replies; 33+ messages in thread
From: Joshua Henderson @ 2015-12-14 22:42 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-mips, ralf, Joshua Henderson

This adds an initial default config that enables all available PIC32
drivers and is enough for booting a PIC32MZDA Starter Kit.

Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
---
 arch/mips/configs/pic32mzda_defconfig |   88 +++++++++++++++++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 arch/mips/configs/pic32mzda_defconfig

diff --git a/arch/mips/configs/pic32mzda_defconfig b/arch/mips/configs/pic32mzda_defconfig
new file mode 100644
index 0000000..17014ca
--- /dev/null
+++ b/arch/mips/configs/pic32mzda_defconfig
@@ -0,0 +1,88 @@
+CONFIG_MACH_PIC32=y
+CONFIG_DTB_PIC32_MZDA_SK=y
+CONFIG_HZ_100=y
+CONFIG_PREEMPT_VOLUNTARY=y
+# CONFIG_SECCOMP is not set
+CONFIG_SYSVIPC=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_RELAY=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_SLAB=y
+CONFIG_JUMP_LABEL=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_BLK_DEV_BSGLIB=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_SGI_PARTITION=y
+CONFIG_BINFMT_MISC=m
+# CONFIG_SUSPEND is not set
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+# CONFIG_ALLOW_DEV_COREDUMP is not set
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_SCAN_ASYNC=y
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_INPUT_LEDS=m
+CONFIG_INPUT_POLLDEV=y
+CONFIG_INPUT_MOUSEDEV=m
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_EVBUG=m
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO=m
+CONFIG_KEYBOARD_GPIO_POLLED=m
+# CONFIG_MOUSE_PS2 is not set
+# CONFIG_SERIO is not set
+CONFIG_SERIAL_PIC32=y
+CONFIG_SERIAL_PIC32_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_RAW_DRIVER=m
+CONFIG_GPIO_SYSFS=y
+# CONFIG_HWMON is not set
+CONFIG_HIDRAW=y
+# CONFIG_USB_SUPPORT is not set
+CONFIG_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_MICROCHIP_PIC32=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=m
+CONFIG_LEDS_TRIGGER_ONESHOT=m
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_GPIO=m
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+# CONFIG_MIPS_PLATFORM_DEVICES is not set
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_AUTOFS4_FS=m
+CONFIG_FUSE_FS=m
+CONFIG_FSCACHE=m
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_SQUASHFS=m
+CONFIG_SQUASHFS_XATTR=y
+CONFIG_SQUASHFS_LZ4=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 01/14] DEVICETREE: Add bindings for PIC32 interrupt controller
  2015-12-14 22:42 ` [PATCH v2 01/14] DEVICETREE: Add bindings for PIC32 interrupt controller Joshua Henderson
@ 2015-12-14 23:34   ` Rob Herring
  0 siblings, 0 replies; 33+ messages in thread
From: Rob Herring @ 2015-12-14 23:34 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, linux-mips, ralf, Cristian Birsan, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, devicetree

On Mon, Dec 14, 2015 at 03:42:03PM -0700, Joshua Henderson wrote:
> From: Cristian Birsan <cristian.birsan@microchip.com>
> 
> Document the devicetree bindings for the interrupt controller on
> Microchip PIC32 class devices.
> 
> Signed-off-by: Cristian Birsan <cristian.birsan@microchip.com>
> Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
> Cc: Ralf Baechle <ralf@linux-mips.org>

Acked-by: Rob Herring <robh@kernel.org>


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver
  2015-12-14 22:42 ` [PATCH v2 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver Joshua Henderson
@ 2015-12-15  0:32   ` Andy Green
  2015-12-16 17:35     ` Paul.Thacker
  2015-12-16 10:48   ` Ulf Hansson
  1 sibling, 1 reply; 33+ messages in thread
From: Andy Green @ 2015-12-15  0:32 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, linux-mips, ralf, Andrei Pistirica, Ulf Hansson,
	Jean Delvare, Geert Uytterhoeven, Corneliu Doban, Haojian Zhuang,
	Luis de Bethencourt, Weijun Yang, Lokesh Vutla, Scott Branden,
	Vincent Yang, Chaotian Jing, ludovic.desroches, Shawn Lin,
	Stephen Boyd, yangbo lu, Kevin Hao, Ben Hutchings, linux-mmc

Hi... looks good, just some small general comments.

On 15 December 2015 at 06:42, Joshua Henderson
<joshua.henderson@microchip.com> wrote:
> From: Andrei Pistirica <andrei.pistirica@microchip.com>
>
> This driver supports the SDHCI host controller found on a PIC32.
>
> Signed-off-by: Andrei Pistirica <andrei.pistirica@microchip.com>
> Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
> Cc: Ralf Baechle <ralf@linux-mips.org>
> ---
>  drivers/mmc/host/Kconfig       |   11 ++
>  drivers/mmc/host/Makefile      |    1 +
>  drivers/mmc/host/sdhci-pic32.c |  291 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 303 insertions(+)
>  create mode 100644 drivers/mmc/host/sdhci-pic32.c
>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 1dee533..1a3a42b 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -785,3 +785,14 @@ config MMC_MTK
>           If you have a machine with a integrated SD/MMC card reader, say Y or M here.
>           This is needed if support for any SD/SDIO/MMC devices is required.
>           If unsure, say N.
> +
> +config MMC_SDHCI_MICROCHIP_PIC32
> +        tristate "Microchip PIC32MZDA SDHCI support"
> +        depends on MMC_SDHCI && PIC32MZDA
> +        help
> +          This selects the Secure Digital Host Controller Interface (SDHCI)
> +          for PIC32MZDA platform.
> +
> +          If you have a controller with this interface, say Y or M here.
> +
> +          If unsure, say N.
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index 3595f83..af918d2 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -75,6 +75,7 @@ obj-$(CONFIG_MMC_SDHCI_BCM2835)               += sdhci-bcm2835.o
>  obj-$(CONFIG_MMC_SDHCI_IPROC)          += sdhci-iproc.o
>  obj-$(CONFIG_MMC_SDHCI_MSM)            += sdhci-msm.o
>  obj-$(CONFIG_MMC_SDHCI_ST)             += sdhci-st.o
> +obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32)        += sdhci-pic32.o
>
>  ifeq ($(CONFIG_CB710_DEBUG),y)
>         CFLAGS-cb710-mmc        += -DDEBUG
> diff --git a/drivers/mmc/host/sdhci-pic32.c b/drivers/mmc/host/sdhci-pic32.c
> new file mode 100644
> index 0000000..b7d7da2
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-pic32.c
> @@ -0,0 +1,291 @@
> +/*
> + * Support of SDHCI platform devices for Microchip PIC32.
> + *
> + * Copyright (C) 2015 Microchip
> + * Andrei Pistirica, Paul Thacker
> + *
> + * Inspired by sdhci-pltfm.c
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/highmem.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm.h>
> +#include <linux/slab.h>
> +#include <linux/mmc/host.h>
> +#include <linux/io.h>
> +#include "sdhci.h"
> +#include <linux/platform_data/sdhci-pic32.h>
> +
> +#define SDH_SHARED_BUS_CTRL            0x000000E0
> +#define SDH_SHARED_BUS_NR_CLK_PINS_MASK        0x7
> +#define SDH_SHARED_BUS_NR_IRQ_PINS_MASK        0x30
> +#define SDH_SHARED_BUS_CLK_PINS                0x10
> +#define SDH_SHARED_BUS_IRQ_PINS                0x14
> +#define SDH_CAPS_SDH_SLOT_TYPE_MASK    0xC0000000
> +#define SDH_SLOT_TYPE_REMOVABLE                0x0
> +#define SDH_SLOT_TYPE_EMBEDDED         0x1
> +#define SDH_SLOT_TYPE_SHARED_BUS       0x2
> +#define SDHCI_CTRL_CDSSEL              0x80
> +#define SDHCI_CTRL_CDTLVL              0x40
> +
> +#define ADMA_FIFO_RD_THSHLD    512
> +#define ADMA_FIFO_WR_THSHLD    512
> +
> +#define DEV_NAME "pic32-sdhci"

Is there any point defining this when it only has one use in the driver?

> +struct pic32_sdhci_pdata {
> +       struct platform_device  *pdev;
> +       struct clk *sys_clk;
> +       struct clk *base_clk;
> +};
> +
> +static unsigned int pic32_sdhci_get_max_clock(struct sdhci_host *host)
> +{
> +       struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
> +
> +       return clk_get_rate(sdhci_pdata->base_clk);
> +}
> +
> +static void pic32_sdhci_set_bus_width(struct sdhci_host *host, int width)
> +{
> +       u8 ctrl;
> +
> +       ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
> +       if (width == MMC_BUS_WIDTH_8) {
> +               ctrl &= ~SDHCI_CTRL_4BITBUS;
> +               if (host->version >= SDHCI_SPEC_300)
> +                       ctrl |= SDHCI_CTRL_8BITBUS;
> +       } else {
> +               if (host->version >= SDHCI_SPEC_300)
> +                       ctrl &= ~SDHCI_CTRL_8BITBUS;
> +               if (width == MMC_BUS_WIDTH_4)
> +                       ctrl |= SDHCI_CTRL_4BITBUS;
> +               else
> +                       ctrl &= ~SDHCI_CTRL_4BITBUS;
> +       }
> +       /*
> +        * SDHCI will not work if JTAG is not Connected.As a workaround fix,
> +        * set Card Detect Signal Selection bit in SDHCI Host Control
> +        * register and clear Card Detect Test Level bit in SDHCI Host
> +        * Control register.
> +        */

Isn't this a clearer explanation, if I understood?

"Without setting CD select and test bits now, SDHCI only works with
JTAG connected."

> +       ctrl &= ~SDHCI_CTRL_CDTLVL;
> +       ctrl |= SDHCI_CTRL_CDSSEL;
> +       sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);

Also... is that a feature of the SDHCI IP or this particular chip's
implementation of it?  I guess if there is only one implementation
right now that has that restriction worry about making it selectable
later.  But if the implementation, it might make sense to also put the
affected implementation name in the comment to make it clear.

> +}
> +
> +static unsigned int pic32_sdhci_get_ro(struct sdhci_host *host)
> +{
> +       /*
> +        * The SDHCI_WRITE_PROTECT bit is unstable on current hardware so we
> +        * can't depend on its value in any way.
> +        */
> +       return 0;
> +}
> +
> +static const struct sdhci_ops pic32_sdhci_ops = {
> +       .get_max_clock = pic32_sdhci_get_max_clock,
> +       .set_clock = sdhci_set_clock,
> +       .set_bus_width = pic32_sdhci_set_bus_width,
> +       .reset = sdhci_reset,
> +       .set_uhs_signaling = sdhci_set_uhs_signaling,
> +       .get_ro = pic32_sdhci_get_ro,
> +};
> +
> +static void pic32_sdhci_shared_bus(struct platform_device *pdev)
> +{
> +       struct sdhci_host *host = platform_get_drvdata(pdev);
> +       u32 bus = readl(host->ioaddr + SDH_SHARED_BUS_CTRL);
> +       u32 clk_pins = (bus & SDH_SHARED_BUS_NR_CLK_PINS_MASK) >> 0;
> +       u32 irq_pins = (bus & SDH_SHARED_BUS_NR_IRQ_PINS_MASK) >> 4;
> +
> +       /* select first clock */
> +       if (clk_pins & 0x1)

BIT(0)?  Also a couple of lines down.

> +               bus |= (0x1 << SDH_SHARED_BUS_CLK_PINS);

I know it's popular but there is no meaning or use in "0x1" where you
could just say "1".

> +       /* select first interrupt */
> +       if (irq_pins & 0x1)
> +               bus |= (0x1 << SDH_SHARED_BUS_IRQ_PINS);

As above.

> +       writel(bus, host->ioaddr + SDH_SHARED_BUS_CTRL);
> +}
> +
> +static int pic32_sdhci_probe_platform(struct platform_device *pdev,
> +                                     struct pic32_sdhci_pdata *pdata)
> +{
> +       int ret = 0;
> +       u32 caps_slot_type;
> +       struct sdhci_host *host = platform_get_drvdata(pdev);
> +
> +       /* Check card slot connected on shared bus. */
> +       host->caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
> +       caps_slot_type = (host->caps & SDH_CAPS_SDH_SLOT_TYPE_MASK) >> 30;
> +       if (caps_slot_type == SDH_SLOT_TYPE_SHARED_BUS)
> +               pic32_sdhci_shared_bus(pdev);
> +
> +       return ret;
> +}
> +
> +static int pic32_sdhci_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct sdhci_host *host;
> +       struct resource *iomem;
> +       struct pic32_sdhci_pdata *sdhci_pdata;
> +       struct pic32_sdhci_platform_data *plat_data;
> +       unsigned int clk_rate = 0;
> +       int ret;
> +       struct pinctrl *pinctrl;

It's hardly critical but for extra gold star arranging local vars in
length order (longest first) is nice.

> +
> +       host = sdhci_alloc_host(dev, sizeof(*sdhci_pdata));
> +       if (IS_ERR(host)) {
> +               ret = PTR_ERR(host);
> +               dev_err(&pdev->dev, "cannot allocate memory for sdhci\n");
> +               goto err;
> +       }
> +
> +       sdhci_pdata = sdhci_priv(host);
> +       sdhci_pdata->pdev = pdev;
> +       platform_set_drvdata(pdev, host);
> +
> +       iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
> +       if (IS_ERR(host->ioaddr)) {
> +               ret = PTR_ERR(host->ioaddr);
> +               dev_err(&pdev->dev, "unable to map iomem: %d\n", ret);
> +               goto err_host;
> +       }
> +
> +       plat_data = pdev->dev.platform_data;
> +       if (plat_data && plat_data->setup_dma) {
> +               ret = plat_data->setup_dma(ADMA_FIFO_RD_THSHLD,
> +                                          ADMA_FIFO_WR_THSHLD);
> +               if (ret)
> +                       goto err_host;
> +       }
> +
> +       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
> +       if (IS_ERR(pinctrl)) {
> +               ret = PTR_ERR(pinctrl);
> +               dev_warn(&pdev->dev, "No pinctrl provided %d\n", ret);
> +               if (ret == -EPROBE_DEFER)
> +                       goto err_host;
> +       }
> +
> +       host->ops = &pic32_sdhci_ops;
> +       host->irq = platform_get_irq(pdev, 0);
> +
> +       sdhci_pdata->sys_clk = devm_clk_get(&pdev->dev, "sys_clk");
> +       if (IS_ERR(sdhci_pdata->sys_clk)) {
> +               ret = PTR_ERR(sdhci_pdata->sys_clk);
> +               dev_err(&pdev->dev, "Error getting clock\n");
> +               goto err_host;
> +       }
> +
> +       /* Enable clock when available! */
> +       ret = clk_prepare_enable(sdhci_pdata->sys_clk);
> +       if (ret) {
> +               dev_dbg(&pdev->dev, "Error enabling clock\n");

Shouldn't this be dev_err()?  You don't survive not having the clock
in the stanza above.  So if you have the clock, you would want to know
if it didn't deal with the clk_prepare_enable().

The comment 4 lines above is also wrong if so.

> +               goto err_host;
> +       }
> +
> +       /* SDH CLK enable */
> +       sdhci_pdata->base_clk = devm_clk_get(&pdev->dev, "base_clk");
> +       if (IS_ERR(sdhci_pdata->base_clk)) {
> +               ret = PTR_ERR(sdhci_pdata->base_clk);
> +               dev_err(&pdev->dev, "Error getting clock\n");
> +               goto err_host;
> +       }
> +
> +       /* Enable clock when available! */
> +       ret = clk_prepare_enable(sdhci_pdata->base_clk);

Again the comment seems wrong.

> +       if (ret) {
> +               dev_dbg(&pdev->dev, "Error enabling clock\n");

And if I understood it, dev_err() needed.

> +               goto err_host;
> +       }
> +
> +       clk_rate = clk_get_rate(sdhci_pdata->base_clk);
> +       dev_dbg(&pdev->dev, "base clock at: %u\n", clk_rate);
> +       clk_rate = clk_get_rate(sdhci_pdata->sys_clk);
> +       dev_dbg(&pdev->dev, "sys clock at: %u\n", clk_rate);
> +
> +       host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
> +

Probably can lose the blank line.

> +       host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
> +
> +       ret = mmc_of_parse(host->mmc);
> +       if (ret)
> +               goto err_host;
> +
> +       ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
> +       if (ret) {
> +               dev_err(&pdev->dev, "failed to probe platform!\n");
> +               goto err_host;
> +       }
> +
> +       ret = sdhci_add_host(host);
> +       if (ret) {
> +               dev_dbg(&pdev->dev, "error adding host\n");
> +               goto err_host;
> +       }
> +
> +       dev_info(&pdev->dev, "Successfully added sdhci host\n");
> +       return 0;
> +
> +err_host:
> +       sdhci_free_host(host);
> +err:
> +       dev_err(&pdev->dev, "pic32-sdhci probe failed: %d\n", ret);
> +       return ret;
> +}
> +
> +static int pic32_sdhci_remove(struct platform_device *pdev)
> +{
> +       struct sdhci_host *host = platform_get_drvdata(pdev);
> +       struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
> +       int dead = 0;
> +       u32 scratch;
> +
> +       scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
> +       if (scratch == (u32)-1)

Since it's not actually related to signed, (u32)~0 might be clearer.

> +               dead = 1;
> +
> +       sdhci_remove_host(host, dead);

You could get rid of "dead" and have

         sdhci_remove_host(host, scratch == (u32)~0);

> +       clk_disable_unprepare(sdhci_pdata->base_clk);
> +       clk_disable_unprepare(sdhci_pdata->sys_clk);
> +       sdhci_free_host(host);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id pic32_sdhci_id_table[] = {
> +       { .compatible = "microchip,pic32mzda-sdhci" },
> +       {}
> +};
> +MODULE_DEVICE_TABLE(of, pic32_sdhci_id_table);
> +
> +static struct platform_driver pic32_sdhci_driver = {
> +       .driver = {
> +               .name   = DEV_NAME,
> +               .owner  = THIS_MODULE,
> +               .of_match_table = of_match_ptr(pic32_sdhci_id_table),
> +       },
> +       .probe          = pic32_sdhci_probe,
> +       .remove         = pic32_sdhci_remove,
> +};
> +
> +module_platform_driver(pic32_sdhci_driver);
> +
> +MODULE_DESCRIPTION("Microchip PIC32 SDHCI driver");
> +MODULE_AUTHOR("Pistirica Sorin Andrei & Sandeep Sheriker");
> +MODULE_LICENSE("GPL v2");
> --
> 1.7.9.5
>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 02/14] irqchip: irq-pic32-evic: Add support for PIC32 interrupt controller
  2015-12-14 22:42 ` [PATCH v2 02/14] irqchip: irq-pic32-evic: Add support " Joshua Henderson
@ 2015-12-15 17:00   ` Marc Zyngier
  2016-01-05 17:50     ` Joshua Henderson
  0 siblings, 1 reply; 33+ messages in thread
From: Marc Zyngier @ 2015-12-15 17:00 UTC (permalink / raw)
  To: Joshua Henderson, linux-kernel
  Cc: linux-mips, ralf, Cristian Birsan, Thomas Gleixner, Jason Cooper

On 14/12/15 22:42, Joshua Henderson wrote:
> From: Cristian Birsan <cristian.birsan@microchip.com>
> 
> This adds support for the interrupt controller present on PIC32 class
> devices.
> 
> The following features are supported:
>  - DT properties for EVIC and for devices that use interrupt lines
>  - Persistent and non-persistent interrupt handling
>  - irqdomain support
> 
> Signed-off-by: Cristian Birsan <cristian.birsan@microchip.com>
> Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
> Cc: Ralf Baechle <ralf@linux-mips.org>
> ---
>  drivers/irqchip/Makefile           |    1 +
>  drivers/irqchip/irq-pic32-evic.c   |  321 ++++++++++++++++++++++++++++++++++++
>  include/linux/irqchip/pic32-evic.h |   19 +++
>  3 files changed, 341 insertions(+)
>  create mode 100644 drivers/irqchip/irq-pic32-evic.c
>  create mode 100644 include/linux/irqchip/pic32-evic.h
> 
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 177f78f..e3608fc 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -55,3 +55,4 @@ obj-$(CONFIG_RENESAS_H8S_INTC)		+= irq-renesas-h8s.o
>  obj-$(CONFIG_ARCH_SA1100)		+= irq-sa11x0.o
>  obj-$(CONFIG_INGENIC_IRQ)		+= irq-ingenic.o
>  obj-$(CONFIG_IMX_GPCV2)			+= irq-imx-gpcv2.o
> +obj-$(CONFIG_MACH_PIC32)		+= irq-pic32-evic.o
> diff --git a/drivers/irqchip/irq-pic32-evic.c b/drivers/irqchip/irq-pic32-evic.c
> new file mode 100644
> index 0000000..6a7747c
> --- /dev/null
> +++ b/drivers/irqchip/irq-pic32-evic.c
> @@ -0,0 +1,321 @@
> +/*
> + * Cristian Birsan <cristian.birsan@microchip.com>
> + * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + */
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/irqdomain.h>
> +#include <linux/of_address.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqchip/pic32-evic.h>
> +
> +#include <asm/irq.h>
> +#include <asm/traps.h>
> +
> +#define CORE_TIMER_INTERRUPT 0
> +#define EXTERNAL_INTERRUPT_0 3
> +#define EXTERNAL_INTERRUPT_1 8
> +#define EXTERNAL_INTERRUPT_2 13
> +#define EXTERNAL_INTERRUPT_3 18
> +#define EXTERNAL_INTERRUPT_4 23
> +
> +#define PRI_MASK	0x7	/* 3 bit priority mask */
> +#define SUBPRI_MASK	0x3	/* 2 bit subpriority mask */
> +#define INT_MASK	0x1F	/* 5 bit pri and subpri mask */
> +#define NR_EXT_IRQS	5	/* 5 external interrupts sources */
> +
> +#define PIC32_INT_PRI(pri, subpri)	\
> +	(((pri & PRI_MASK) << 2) | (subpri & SUBPRI_MASK))
> +#define DEFAULT_PIC32_INT_PRI PIC32_INT_PRI(2, 0)
> +
> +static struct irq_domain *evic_irq_domain;
> +static struct evic __iomem *evic_base;
> +
> +static unsigned int *evic_irq_prio;
> +
> +struct pic_reg {
> +	u32 val; /* value register*/
> +	u32 clr; /* clear register */
> +	u32 set; /* set register */
> +	u32 inv; /* inv register */
> +} __packed;
> +
> +struct evic {
> +	struct pic_reg intcon;
> +	struct pic_reg priss;
> +	struct pic_reg intstat;
> +	struct pic_reg iptmr;
> +	struct pic_reg ifs[6];
> +	u32 reserved1[8];
> +	struct pic_reg iec[6];
> +	u32 reserved2[8];
> +	struct pic_reg ipc[48];
> +	u32 reserved3[64];
> +	u32 off[191];
> +} __packed;
> +
> +static int get_ext_irq_index(irq_hw_number_t hw);
> +static void evic_set_ext_irq_polarity(int ext_irq, u32 type);
> +
> +#define BIT_REG_MASK(bit, reg, mask)		\
> +	do {					\
> +		reg = bit/32;			\
> +		mask = 1 << (bit % 32);		\
> +	} while (0)
> +
> +asmlinkage void __weak plat_irq_dispatch(void)
> +{
> +	unsigned int irq, hwirq;
> +	u32 reg, mask;
> +
> +	hwirq = readl(&evic_base->intstat.val) & 0xFF;
> +
> +	/* Check if the interrupt was really triggered by hardware*/
> +	BIT_REG_MASK(hwirq, reg, mask);
> +	if (likely(readl(&evic_base->ifs[reg].val) &
> +			readl(&evic_base->iec[reg].val) & mask)) {
> +		irq = irq_linear_revmap(evic_irq_domain, hwirq);
> +		do_IRQ(irq);
> +	} else
> +		spurious_interrupt();
> +}
> +
> +/* mask off an interrupt */
> +static inline void mask_pic32_irq(struct irq_data *irqd)
> +{
> +	u32 reg, mask;
> +	unsigned int hwirq = irqd_to_hwirq(irqd);
> +
> +	BIT_REG_MASK(hwirq, reg, mask);
> +	writel(mask, &evic_base->iec[reg].clr);
> +}
> +
> +/* unmask an interrupt */
> +static inline void unmask_pic32_irq(struct irq_data *irqd)
> +{
> +	u32 reg, mask;
> +	unsigned int hwirq = irqd_to_hwirq(irqd);
> +
> +	BIT_REG_MASK(hwirq, reg, mask);
> +	writel(mask, &evic_base->iec[reg].set);
> +}
> +
> +/* acknowledge an interrupt */
> +static void ack_pic32_irq(struct irq_data *irqd)
> +{
> +	u32 reg, mask;
> +	unsigned int hwirq = irqd_to_hwirq(irqd);
> +
> +	BIT_REG_MASK(hwirq, reg, mask);
> +	writel(mask, &evic_base->ifs[reg].clr);
> +}
> +
> +/* mask off and acknowledge an interrupt */
> +static inline void mask_ack_pic32_irq(struct irq_data *irqd)
> +{
> +	u32 reg, mask;
> +	unsigned int hwirq = irqd_to_hwirq(irqd);
> +
> +	BIT_REG_MASK(hwirq, reg, mask);
> +	writel(mask, &evic_base->iec[reg].clr);
> +	writel(mask, &evic_base->ifs[reg].clr);
> +}
> +
> +static int set_type_pic32_irq(struct irq_data *data, unsigned int flow_type)
> +{
> +	int index;
> +
> +	switch (flow_type) {
> +
> +	case IRQ_TYPE_EDGE_RISING:
> +	case IRQ_TYPE_EDGE_FALLING:
> +		irq_set_handler_locked(data, handle_edge_irq);
> +		break;
> +
> +	case IRQ_TYPE_LEVEL_HIGH:
> +	case IRQ_TYPE_LEVEL_LOW:
> +		irq_set_handler_locked(data, handle_fasteoi_irq);
> +		break;
> +
> +	default:
> +		pr_err("Invalid interrupt type !\n");
> +		return -EINVAL;
> +	}
> +
> +	/* set polarity for external interrupts only */
> +	index = get_ext_irq_index(data->hwirq);
> +	if (index >= 0)
> +		evic_set_ext_irq_polarity(index, flow_type);
> +
> +	return IRQ_SET_MASK_OK;
> +}
> +
> +static void pic32_bind_evic_interrupt(int irq, int set)
> +{
> +	writel(set, &evic_base->off[irq]);
> +}
> +
> +int pic32_get_c0_compare_int(void)
> +{
> +	int virq;
> +
> +	virq = irq_create_mapping(evic_irq_domain, CORE_TIMER_INTERRUPT);
> +	irq_set_irq_type(virq, IRQ_TYPE_EDGE_RISING);
> +	return virq;
> +}
> +
> +static struct irq_chip pic32_irq_chip = {
> +	.name = "PIC32-EVIC",
> +	.irq_ack = ack_pic32_irq,
> +	.irq_mask = mask_pic32_irq,
> +	.irq_mask_ack = mask_ack_pic32_irq,
> +	.irq_unmask = unmask_pic32_irq,
> +	.irq_eoi = ack_pic32_irq,
> +	.irq_set_type = set_type_pic32_irq,
> +	.irq_enable = unmask_pic32_irq,
> +	.irq_disable = mask_pic32_irq,

I'm not sure I see the point of having all these methods. First, there
is a lot of duplication: no need to provide enable/disable if all you
have is mask/unmask - the core code can deal with that.

Then, your EOI method is not really an EOI. It doesn't terminate the
handling, or at least that's not what the name suggest. If this is
really an EOI, then you should be able to simplify the whole thing on
only use the fasteoi handler, including for edge interrupts.

It would be good to have an insight on how this thing actually works
(I've tried to read the only documentation, but this is vague at best),
that would help picking the right design for your use case.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 07/14] DEVICETREE: Add bindings for PIC32 pin control and GPIO
  2015-12-14 22:42 ` [PATCH v2 07/14] DEVICETREE: Add bindings for PIC32 pin control and GPIO Joshua Henderson
@ 2015-12-15 19:58   ` Rob Herring
  0 siblings, 0 replies; 33+ messages in thread
From: Rob Herring @ 2015-12-15 19:58 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, linux-mips, ralf, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, devicetree

On Mon, Dec 14, 2015 at 03:42:09PM -0700, Joshua Henderson wrote:
> Document the devicetree bindings for PINCTRL and GPIO found on Microchip
> PIC32 class devices.
> 
> Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
> Cc: Ralf Baechle <ralf@linux-mips.org>

Acked-by: Rob Herring <robh@kernel.org>

> ---
>  .../bindings/gpio/microchip,pic32-gpio.txt         |   49 ++++++++++++++++
>  .../bindings/pinctrl/microchip,pic32-pinctrl.txt   |   60 ++++++++++++++++++++
>  2 files changed, 109 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt
>  create mode 100644 Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt
> 
> diff --git a/Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt b/Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt
> new file mode 100644
> index 0000000..ef37528
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt
> @@ -0,0 +1,49 @@
> +* Microchip PIC32 GPIO devices (PIO).
> +
> +Required properties:
> + - compatible: "microchip,pic32mzda-gpio"
> + - reg: Base address and length for the device.
> + - interrupts: The port interrupt shared by all pins.
> + - gpio-controller: Marks the port as GPIO controller.
> + - #gpio-cells: Two. The first cell is the pin number and
> +   the second cell is used to specify the gpio polarity as defined in
> +   defined in <dt-bindings/gpio/gpio.h>:
> +      0 = GPIO_ACTIVE_HIGH
> +      1 = GPIO_ACTIVE_LOW
> +      2 = GPIO_OPEN_DRAIN
> + - interrupt-controller: Marks the device node as an interrupt controller.
> + - #interrupt-cells: Two. The first cell is the GPIO number and second cell
> +   is used to specify the trigger type as defined in
> +   <dt-bindings/interrupt-controller/irq.h>:
> +      IRQ_TYPE_EDGE_RISING
> +      IRQ_TYPE_EDGE_FALLING
> +      IRQ_TYPE_EDGE_BOTH
> + - clocks: Clock specifier (see clock bindings for details).
> + - microchip,gpio-bank: Specifies which bank a controller owns.
> + - gpio-ranges: Interaction with the PINCTRL subsystem.
> +
> +Example:
> +
> +/* PORTA */
> +gpio0: gpio0@1f860000 {
> +	compatible = "microchip,pic32mzda-gpio";
> +	reg = <0x1f860000 0x100>;
> +	interrupts = <118 IRQ_TYPE_LEVEL_HIGH>;
> +	#gpio-cells = <2>;
> +	gpio-controller;
> +	interrupt-controller;
> +	#interrupt-cells = <2>;
> +	clocks = <&PBCLK4>;
> +	microchip,gpio-bank = <0>;
> +	gpio-ranges = <&pic32_pinctrl 0 0 16>;
> +};
> +
> +keys {
> +	...
> +
> +	button@sw1 {
> +		label = "ESC";
> +		linux,code = <1>;
> +		gpios = <&gpio0 12 0>;
> +	};
> +};
> diff --git a/Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt
> new file mode 100644
> index 0000000..4b5efa5
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt
> @@ -0,0 +1,60 @@
> +* Microchip PIC32 Pin Controller
> +
> +Please refer to pinctrl-bindings.txt, ../gpio/gpio.txt, and
> +../interrupt-controller/interrupts.txt for generic information regarding
> +pin controller, GPIO, and interrupt bindings.
> +
> +PIC32 'pin configuration node' is a node of a group of pins which can be
> +used for a specific device or function. This node represents configuraions of
> +pins, optional function, and optional mux related configuration.
> +
> +Required properties for pin controller node:
> + - compatible: "microchip,pic32mada-pinctrl"
> + - reg: Address range of the pinctrl registers.
> + - clocks: Clock specifier (see clock bindings for details)
> +
> +Required properties for pin configuration sub-nodes:
> + - pins: List of pins to which the configuration applies.
> +
> +Optional properties for pin configuration sub-nodes:
> +----------------------------------------------------
> + - function: Mux function for the specified pins.
> + - bias-pull-up: Enable weak pull-up.
> + - bias-pull-down: Enable weak pull-down.
> + - input-enable: Set the pin as an input.
> + - output-low: Set the pin as an output level low.
> + - output-high: Set the pin as an output level high.
> + - microchip,digital: Enable digital I/O.
> + - microchip,analog: Enable analog I/O.
> +
> +Example:
> +
> +pic32_pinctrl: pinctrl@1f801400{
> +	#address-cells = <1>;
> +	#size-cells = <1>;
> +	compatible = "microchip,pic32mzda-pinctrl";
> +	reg = <0x1f801400 0x400>;
> +	clocks = <&PBCLK1>;
> +
> +	pinctrl_uart2: pinctrl_uart2 {
> +		uart2-tx {
> +			pins = "G9";
> +			function = "U2TX";
> +			microchip,digital;
> +			output-low;
> +		};
> +		uart2-rx {
> +			pins = "B0";
> +			function = "U2RX";
> +			microchip,digital;
> +			input-enable;
> +		};
> +	};
> +};
> +
> +uart2: serial@1f822200 {
> +	compatible = "microchip,pic32mzda-uart";
> +	reg = <0x1f822200 0x50>;
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&pinctrl_uart2>;
> +};
> -- 
> 1.7.9.5
> 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 11/14] DEVICETREE: Add bindings for PIC32 SDHCI host controller
  2015-12-14 22:42 ` [PATCH v2 11/14] DEVICETREE: Add bindings for PIC32 SDHCI host controller Joshua Henderson
@ 2015-12-15 20:00   ` Rob Herring
  0 siblings, 0 replies; 33+ messages in thread
From: Rob Herring @ 2015-12-15 20:00 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, linux-mips, ralf, Andrei Pistirica, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, devicetree

On Mon, Dec 14, 2015 at 03:42:13PM -0700, Joshua Henderson wrote:
> From: Andrei Pistirica <andrei.pistirica@microchip.com>
> 
> Document the devicetree bindings for the SDHCI peripheral found on
> Microchip PIC32 class devices.
> 
> Signed-off-by: Andrei Pistirica <andrei.pistirica@microchip.com>
> Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
> Cc: Ralf Baechle <ralf@linux-mips.org>

Acked-by: Rob Herring <robh@kernel.org>

> ---
>  .../bindings/mmc/microchip,sdhci-pic32.txt         |   29 ++++++++++++++++++++
>  1 file changed, 29 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mmc/microchip,sdhci-pic32.txt
> 
> diff --git a/Documentation/devicetree/bindings/mmc/microchip,sdhci-pic32.txt b/Documentation/devicetree/bindings/mmc/microchip,sdhci-pic32.txt
> new file mode 100644
> index 0000000..71ad57e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mmc/microchip,sdhci-pic32.txt
> @@ -0,0 +1,29 @@
> +* Microchip PIC32 SDHCI Controller
> +
> +This file documents differences between the core properties in mmc.txt
> +and the properties used by the sdhci-pic32 driver.
> +
> +Required properties:
> +- compatible: Should be "microchip,pic32mzda-sdhci"
> +- interrupts: Should contain interrupt
> +- clock-names: Should be "base_clk", "sys_clk".
> +               See: Documentation/devicetree/bindings/resource-names.txt
> +- clocks: Phandle to the clock.
> +          See: Documentation/devicetree/bindings/clock/clock-bindings.txt
> +- pinctrl-names: A pinctrl state names "default" must be defined.
> +- pinctrl-0: Phandle referencing pin configuration of the SDHCI controller.
> +             See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
> +
> +Example:
> +
> +	sdhci@1f8ec000 {
> +		compatible = "microchip,pic32mzda-sdhci";
> +		reg = <0x1f8ec000 0x100>;
> +		interrupts = <191 IRQ_TYPE_LEVEL_HIGH>;
> +		clocks = <&REFCLKO4>, <&PBCLK5>;
> +		clock-names = "base_clk", "sys_clk";
> +		bus-width = <4>;
> +		cap-sd-highspeed;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&pinctrl_sdhc1>;
> +	};
> -- 
> 1.7.9.5
> 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 09/14] DEVICETREE: Add bindings for PIC32 UART driver
  2015-12-14 22:42 ` [PATCH v2 09/14] DEVICETREE: Add bindings for PIC32 UART driver Joshua Henderson
@ 2015-12-15 20:00   ` Rob Herring
  0 siblings, 0 replies; 33+ messages in thread
From: Rob Herring @ 2015-12-15 20:00 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, linux-mips, ralf, Andrei Pistirica, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, devicetree

On Mon, Dec 14, 2015 at 03:42:11PM -0700, Joshua Henderson wrote:
> From: Andrei Pistirica <andrei.pistirica@microchip.com>
> 
> Document the devicetree bindings for the UART peripheral found on
> Microchip PIC32 class devices.
> 
> Signed-off-by: Andrei Pistirica <andrei.pistirica@microchip.com>
> Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
> Cc: Ralf Baechle <ralf@linux-mips.org>

Acked-by: Rob Herring <robh@kernel.org>

> ---
>  .../bindings/serial/microchip,pic32-uart.txt       |   29 ++++++++++++++++++++
>  1 file changed, 29 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/serial/microchip,pic32-uart.txt
> 
> diff --git a/Documentation/devicetree/bindings/serial/microchip,pic32-uart.txt b/Documentation/devicetree/bindings/serial/microchip,pic32-uart.txt
> new file mode 100644
> index 0000000..65b38bf6
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/serial/microchip,pic32-uart.txt
> @@ -0,0 +1,29 @@
> +* Microchip Universal Asynchronous Receiver Transmitter (UART)
> +
> +Required properties:
> +- compatible: Should be "microchip,pic32mzda-uart"
> +- reg: Should contain registers location and length
> +- interrupts: Should contain interrupt
> +- clocks: Phandle to the clock.
> +          See: Documentation/devicetree/bindings/clock/clock-bindings.txt
> +- pinctrl-names: A pinctrl state names "default" must be defined.
> +- pinctrl-0: Phandle referencing pin configuration of the UART peripheral.
> +             See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
> +
> +Optional properties:
> +- cts-gpios: CTS pin for UART
> +
> +Example:
> +	uart1: serial@1f822000 {
> +		compatible = "microchip,pic32mzda-uart";
> +		reg = <0x1f822000 0x50>;
> +		interrupts = <112 IRQ_TYPE_LEVEL_HIGH>,
> +			<113 IRQ_TYPE_LEVEL_HIGH>,
> +			<114 IRQ_TYPE_LEVEL_HIGH>;
> +		clocks = <&PBCLK2>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&pinctrl_uart1
> +				&pinctrl_uart1_cts
> +				&pinctrl_uart1_rts>;
> +		cts-gpios = <&gpio1 15 0>;
> +	};
> -- 
> 1.7.9.5
> 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver
  2015-12-14 22:42 ` [PATCH v2 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver Joshua Henderson
  2015-12-15  0:32   ` Andy Green
@ 2015-12-16 10:48   ` Ulf Hansson
  2015-12-17 18:05     ` Paul.Thacker
  1 sibling, 1 reply; 33+ messages in thread
From: Ulf Hansson @ 2015-12-16 10:48 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, linux-mips, Ralf Baechle, Andrei Pistirica,
	Jean Delvare, Geert Uytterhoeven, Corneliu Doban, Haojian Zhuang,
	Luis de Bethencourt, Weijun Yang, Lokesh Vutla, Scott Branden,
	Vincent Yang, Chaotian Jing, ludovic.desroches, Shawn Lin,
	Stephen Boyd, yangbo lu, Kevin Hao, Ben Hutchings, Andy Green,
	linux-mmc

[...]

> +static int pic32_sdhci_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct sdhci_host *host;
> +       struct resource *iomem;
> +       struct pic32_sdhci_pdata *sdhci_pdata;
> +       struct pic32_sdhci_platform_data *plat_data;
> +       unsigned int clk_rate = 0;
> +       int ret;
> +       struct pinctrl *pinctrl;
> +
> +       host = sdhci_alloc_host(dev, sizeof(*sdhci_pdata));
> +       if (IS_ERR(host)) {
> +               ret = PTR_ERR(host);
> +               dev_err(&pdev->dev, "cannot allocate memory for sdhci\n");
> +               goto err;
> +       }
> +
> +       sdhci_pdata = sdhci_priv(host);
> +       sdhci_pdata->pdev = pdev;
> +       platform_set_drvdata(pdev, host);
> +
> +       iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
> +       if (IS_ERR(host->ioaddr)) {
> +               ret = PTR_ERR(host->ioaddr);
> +               dev_err(&pdev->dev, "unable to map iomem: %d\n", ret);
> +               goto err_host;
> +       }
> +
> +       plat_data = pdev->dev.platform_data;
> +       if (plat_data && plat_data->setup_dma) {
> +               ret = plat_data->setup_dma(ADMA_FIFO_RD_THSHLD,
> +                                          ADMA_FIFO_WR_THSHLD);
> +               if (ret)
> +                       goto err_host;
> +       }
> +
> +       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);

This isn't need as it's already handled by the PM core.

> +       if (IS_ERR(pinctrl)) {
> +               ret = PTR_ERR(pinctrl);
> +               dev_warn(&pdev->dev, "No pinctrl provided %d\n", ret);
> +               if (ret == -EPROBE_DEFER)
> +                       goto err_host;
> +       }
> +
> +       host->ops = &pic32_sdhci_ops;
> +       host->irq = platform_get_irq(pdev, 0);
> +
> +       sdhci_pdata->sys_clk = devm_clk_get(&pdev->dev, "sys_clk");
> +       if (IS_ERR(sdhci_pdata->sys_clk)) {
> +               ret = PTR_ERR(sdhci_pdata->sys_clk);
> +               dev_err(&pdev->dev, "Error getting clock\n");
> +               goto err_host;
> +       }
> +
> +       /* Enable clock when available! */
> +       ret = clk_prepare_enable(sdhci_pdata->sys_clk);
> +       if (ret) {
> +               dev_dbg(&pdev->dev, "Error enabling clock\n");
> +               goto err_host;
> +       }
> +
> +       /* SDH CLK enable */
> +       sdhci_pdata->base_clk = devm_clk_get(&pdev->dev, "base_clk");
> +       if (IS_ERR(sdhci_pdata->base_clk)) {
> +               ret = PTR_ERR(sdhci_pdata->base_clk);
> +               dev_err(&pdev->dev, "Error getting clock\n");
> +               goto err_host;
> +       }
> +
> +       /* Enable clock when available! */
> +       ret = clk_prepare_enable(sdhci_pdata->base_clk);
> +       if (ret) {
> +               dev_dbg(&pdev->dev, "Error enabling clock\n");
> +               goto err_host;
> +       }
> +
> +       clk_rate = clk_get_rate(sdhci_pdata->base_clk);
> +       dev_dbg(&pdev->dev, "base clock at: %u\n", clk_rate);
> +       clk_rate = clk_get_rate(sdhci_pdata->sys_clk);
> +       dev_dbg(&pdev->dev, "sys clock at: %u\n", clk_rate);

This looks like some leftover from a debugging task. Can you remove them?

> +
> +       host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
> +
> +       host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
> +
> +       ret = mmc_of_parse(host->mmc);
> +       if (ret)

>From this point, the error handling doesn't undo clk_prepare_enable().
Please add that.

> +               goto err_host;
> +
> +       ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
> +       if (ret) {
> +               dev_err(&pdev->dev, "failed to probe platform!\n");
> +               goto err_host;
> +       }
> +
> +       ret = sdhci_add_host(host);
> +       if (ret) {
> +               dev_dbg(&pdev->dev, "error adding host\n");
> +               goto err_host;
> +       }
> +
> +       dev_info(&pdev->dev, "Successfully added sdhci host\n");
> +       return 0;
> +
> +err_host:
> +       sdhci_free_host(host);
> +err:
> +       dev_err(&pdev->dev, "pic32-sdhci probe failed: %d\n", ret);
> +       return ret;

A general comment for the ->probe() and the below ->remove() callback,
is that you should probably be able to convert to use
sdhci_pltfm_init() and sdhci_pltfm_free() in favor of
sdhci_alloc_host() and sdhci_free_host().

I think that could simplify the code a bit.

> +}
> +
> +static int pic32_sdhci_remove(struct platform_device *pdev)
> +{
> +       struct sdhci_host *host = platform_get_drvdata(pdev);
> +       struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
> +       int dead = 0;
> +       u32 scratch;
> +
> +       scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
> +       if (scratch == (u32)-1)
> +               dead = 1;
> +
> +       sdhci_remove_host(host, dead);
> +       clk_disable_unprepare(sdhci_pdata->base_clk);
> +       clk_disable_unprepare(sdhci_pdata->sys_clk);
> +       sdhci_free_host(host);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id pic32_sdhci_id_table[] = {
> +       { .compatible = "microchip,pic32mzda-sdhci" },
> +       {}
> +};
> +MODULE_DEVICE_TABLE(of, pic32_sdhci_id_table);
> +
> +static struct platform_driver pic32_sdhci_driver = {
> +       .driver = {
> +               .name   = DEV_NAME,
> +               .owner  = THIS_MODULE,
> +               .of_match_table = of_match_ptr(pic32_sdhci_id_table),
> +       },
> +       .probe          = pic32_sdhci_probe,
> +       .remove         = pic32_sdhci_remove,
> +};
> +
> +module_platform_driver(pic32_sdhci_driver);
> +
> +MODULE_DESCRIPTION("Microchip PIC32 SDHCI driver");
> +MODULE_AUTHOR("Pistirica Sorin Andrei & Sandeep Sheriker");
> +MODULE_LICENSE("GPL v2");
> --
> 1.7.9.5
>

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 33+ messages in thread

* RE: [PATCH v2 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver
  2015-12-15  0:32   ` Andy Green
@ 2015-12-16 17:35     ` Paul.Thacker
  0 siblings, 0 replies; 33+ messages in thread
From: Paul.Thacker @ 2015-12-16 17:35 UTC (permalink / raw)
  To: andy.green, Joshua.Henderson
  Cc: linux-kernel, linux-mips, ralf, Andrei.Pistirica, ulf.hansson,
	jdelvare, geert, cdoban, haojian.zhuang, luisbg, Weijun.Yang,
	lokeshvutla, sbranden, vincent.yang.fujitsu, chaotian.jing,
	ludovic.desroches, shawn.lin, sboyd, yangbo.lu, haokexin, ben,
	linux-mmc

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 15024 bytes --]

On 12/14/2015 5:33 PM, Andy Green wrote:
> Hi... looks good, just some small general comments.
> 
> On 15 December 2015 at 06:42, Joshua Henderson
> <joshua.henderson@microchip.com> wrote:
> > From: Andrei Pistirica <andrei.pistirica@microchip.com>
> >
> > This driver supports the SDHCI host controller found on a PIC32.
> >
> > Signed-off-by: Andrei Pistirica <andrei.pistirica@microchip.com>
> > Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
> > Cc: Ralf Baechle <ralf@linux-mips.org>
> > ---
> >  drivers/mmc/host/Kconfig       |   11 ++
> >  drivers/mmc/host/Makefile      |    1 +
> >  drivers/mmc/host/sdhci-pic32.c |  291
> > ++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 303 insertions(+)
> >  create mode 100644 drivers/mmc/host/sdhci-pic32.c
> >
> > diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index
> > 1dee533..1a3a42b 100644
> > --- a/drivers/mmc/host/Kconfig
> > +++ b/drivers/mmc/host/Kconfig
> > @@ -785,3 +785,14 @@ config MMC_MTK
> >           If you have a machine with a integrated SD/MMC card reader, say Y or M
> here.
> >           This is needed if support for any SD/SDIO/MMC devices is required.
> >           If unsure, say N.
> > +
> > +config MMC_SDHCI_MICROCHIP_PIC32
> > +        tristate "Microchip PIC32MZDA SDHCI support"
> > +        depends on MMC_SDHCI && PIC32MZDA
> > +        help
> > +          This selects the Secure Digital Host Controller Interface (SDHCI)
> > +          for PIC32MZDA platform.
> > +
> > +          If you have a controller with this interface, say Y or M here.
> > +
> > +          If unsure, say N.
> > diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> > index 3595f83..af918d2 100644
> > --- a/drivers/mmc/host/Makefile
> > +++ b/drivers/mmc/host/Makefile
> > @@ -75,6 +75,7 @@ obj-$(CONFIG_MMC_SDHCI_BCM2835)               += sdhci-
> bcm2835.o
> >  obj-$(CONFIG_MMC_SDHCI_IPROC)          += sdhci-iproc.o
> >  obj-$(CONFIG_MMC_SDHCI_MSM)            += sdhci-msm.o
> >  obj-$(CONFIG_MMC_SDHCI_ST)             += sdhci-st.o
> > +obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32)        += sdhci-pic32.o
> >
> >  ifeq ($(CONFIG_CB710_DEBUG),y)
> >         CFLAGS-cb710-mmc        += -DDEBUG
> > diff --git a/drivers/mmc/host/sdhci-pic32.c
> > b/drivers/mmc/host/sdhci-pic32.c new file mode 100644 index
> > 0000000..b7d7da2
> > --- /dev/null
> > +++ b/drivers/mmc/host/sdhci-pic32.c
> > @@ -0,0 +1,291 @@
> > +/*
> > + * Support of SDHCI platform devices for Microchip PIC32.
> > + *
> > + * Copyright (C) 2015 Microchip
> > + * Andrei Pistirica, Paul Thacker
> > + *
> > + * Inspired by sdhci-pltfm.c
> > + *
> > + * This file is licensed under the terms of the GNU General Public
> > + * License version 2. This program is licensed "as is" without any
> > + * warranty of any kind, whether express or implied.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/highmem.h>
> > +#include <linux/module.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/irq.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm.h>
> > +#include <linux/slab.h>
> > +#include <linux/mmc/host.h>
> > +#include <linux/io.h>
> > +#include "sdhci.h"
> > +#include <linux/platform_data/sdhci-pic32.h>
> > +
> > +#define SDH_SHARED_BUS_CTRL            0x000000E0
> > +#define SDH_SHARED_BUS_NR_CLK_PINS_MASK        0x7
> > +#define SDH_SHARED_BUS_NR_IRQ_PINS_MASK        0x30
> > +#define SDH_SHARED_BUS_CLK_PINS                0x10
> > +#define SDH_SHARED_BUS_IRQ_PINS                0x14
> > +#define SDH_CAPS_SDH_SLOT_TYPE_MASK    0xC0000000
> > +#define SDH_SLOT_TYPE_REMOVABLE                0x0
> > +#define SDH_SLOT_TYPE_EMBEDDED         0x1
> > +#define SDH_SLOT_TYPE_SHARED_BUS       0x2
> > +#define SDHCI_CTRL_CDSSEL              0x80
> > +#define SDHCI_CTRL_CDTLVL              0x40
> > +
> > +#define ADMA_FIFO_RD_THSHLD    512
> > +#define ADMA_FIFO_WR_THSHLD    512
> > +
> > +#define DEV_NAME "pic32-sdhci"
> 
> Is there any point defining this when it only has one use in the driver?

Ack. Will remove.

> 
> > +struct pic32_sdhci_pdata {
> > +       struct platform_device  *pdev;
> > +       struct clk *sys_clk;
> > +       struct clk *base_clk;
> > +};
> > +
> > +static unsigned int pic32_sdhci_get_max_clock(struct sdhci_host
> > +*host) {
> > +       struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
> > +
> > +       return clk_get_rate(sdhci_pdata->base_clk);
> > +}
> > +
> > +static void pic32_sdhci_set_bus_width(struct sdhci_host *host, int
> > +width) {
> > +       u8 ctrl;
> > +
> > +       ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
> > +       if (width == MMC_BUS_WIDTH_8) {
> > +               ctrl &= ~SDHCI_CTRL_4BITBUS;
> > +               if (host->version >= SDHCI_SPEC_300)
> > +                       ctrl |= SDHCI_CTRL_8BITBUS;
> > +       } else {
> > +               if (host->version >= SDHCI_SPEC_300)
> > +                       ctrl &= ~SDHCI_CTRL_8BITBUS;
> > +               if (width == MMC_BUS_WIDTH_4)
> > +                       ctrl |= SDHCI_CTRL_4BITBUS;
> > +               else
> > +                       ctrl &= ~SDHCI_CTRL_4BITBUS;
> > +       }
> > +       /*
> > +        * SDHCI will not work if JTAG is not Connected.As a workaround fix,
> > +        * set Card Detect Signal Selection bit in SDHCI Host Control
> > +        * register and clear Card Detect Test Level bit in SDHCI Host
> > +        * Control register.
> > +        */
> 
> Isn't this a clearer explanation, if I understood?
> 
> "Without setting CD select and test bits now, SDHCI only works with JTAG
> connected."
> 
> > +       ctrl &= ~SDHCI_CTRL_CDTLVL;
> > +       ctrl |= SDHCI_CTRL_CDSSEL;
> > +       sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
> 
> Also... is that a feature of the SDHCI IP or this particular chip's implementation
> of it?  I guess if there is only one implementation right now that has that
> restriction worry about making it selectable later.  But if the implementation, it
> might make sense to also put the affected implementation name in the
> comment to make it clear.

This is a chip errata. Having JTAG connected masks the problem, but is otherwise irrelevant. Comment will be changed to: "CD select and test bits must be set for errata workaround."

> 
> > +}
> > +
> > +static unsigned int pic32_sdhci_get_ro(struct sdhci_host *host) {
> > +       /*
> > +        * The SDHCI_WRITE_PROTECT bit is unstable on current hardware so we
> > +        * can't depend on its value in any way.
> > +        */
> > +       return 0;
> > +}
> > +
> > +static const struct sdhci_ops pic32_sdhci_ops = {
> > +       .get_max_clock = pic32_sdhci_get_max_clock,
> > +       .set_clock = sdhci_set_clock,
> > +       .set_bus_width = pic32_sdhci_set_bus_width,
> > +       .reset = sdhci_reset,
> > +       .set_uhs_signaling = sdhci_set_uhs_signaling,
> > +       .get_ro = pic32_sdhci_get_ro,
> > +};
> > +
> > +static void pic32_sdhci_shared_bus(struct platform_device *pdev) {
> > +       struct sdhci_host *host = platform_get_drvdata(pdev);
> > +       u32 bus = readl(host->ioaddr + SDH_SHARED_BUS_CTRL);
> > +       u32 clk_pins = (bus & SDH_SHARED_BUS_NR_CLK_PINS_MASK) >> 0;
> > +       u32 irq_pins = (bus & SDH_SHARED_BUS_NR_IRQ_PINS_MASK) >> 4;
> > +
> > +       /* select first clock */
> > +       if (clk_pins & 0x1)
> 
> BIT(0)?  Also a couple of lines down.
> 
> > +               bus |= (0x1 << SDH_SHARED_BUS_CLK_PINS);
> 
> I know it's popular but there is no meaning or use in "0x1" where you could just
> say "1".
> 
> > +       /* select first interrupt */
> > +       if (irq_pins & 0x1)
> > +               bus |= (0x1 << SDH_SHARED_BUS_IRQ_PINS);
> 
> As above.

Ack. Will change.

> 
> > +       writel(bus, host->ioaddr + SDH_SHARED_BUS_CTRL); }
> > +
> > +static int pic32_sdhci_probe_platform(struct platform_device *pdev,
> > +                                     struct pic32_sdhci_pdata *pdata)
> > +{
> > +       int ret = 0;
> > +       u32 caps_slot_type;
> > +       struct sdhci_host *host = platform_get_drvdata(pdev);
> > +
> > +       /* Check card slot connected on shared bus. */
> > +       host->caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
> > +       caps_slot_type = (host->caps & SDH_CAPS_SDH_SLOT_TYPE_MASK) >>
> 30;
> > +       if (caps_slot_type == SDH_SLOT_TYPE_SHARED_BUS)
> > +               pic32_sdhci_shared_bus(pdev);
> > +
> > +       return ret;
> > +}
> > +
> > +static int pic32_sdhci_probe(struct platform_device *pdev) {
> > +       struct device *dev = &pdev->dev;
> > +       struct sdhci_host *host;
> > +       struct resource *iomem;
> > +       struct pic32_sdhci_pdata *sdhci_pdata;
> > +       struct pic32_sdhci_platform_data *plat_data;
> > +       unsigned int clk_rate = 0;
> > +       int ret;
> > +       struct pinctrl *pinctrl;
> 
> It's hardly critical but for extra gold star arranging local vars in length order
> (longest first) is nice.

Ack.

> 
> > +
> > +       host = sdhci_alloc_host(dev, sizeof(*sdhci_pdata));
> > +       if (IS_ERR(host)) {
> > +               ret = PTR_ERR(host);
> > +               dev_err(&pdev->dev, "cannot allocate memory for sdhci\n");
> > +               goto err;
> > +       }
> > +
> > +       sdhci_pdata = sdhci_priv(host);
> > +       sdhci_pdata->pdev = pdev;
> > +       platform_set_drvdata(pdev, host);
> > +
> > +       iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +       host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
> > +       if (IS_ERR(host->ioaddr)) {
> > +               ret = PTR_ERR(host->ioaddr);
> > +               dev_err(&pdev->dev, "unable to map iomem: %d\n", ret);
> > +               goto err_host;
> > +       }
> > +
> > +       plat_data = pdev->dev.platform_data;
> > +       if (plat_data && plat_data->setup_dma) {
> > +               ret = plat_data->setup_dma(ADMA_FIFO_RD_THSHLD,
> > +                                          ADMA_FIFO_WR_THSHLD);
> > +               if (ret)
> > +                       goto err_host;
> > +       }
> > +
> > +       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
> > +       if (IS_ERR(pinctrl)) {
> > +               ret = PTR_ERR(pinctrl);
> > +               dev_warn(&pdev->dev, "No pinctrl provided %d\n", ret);
> > +               if (ret == -EPROBE_DEFER)
> > +                       goto err_host;
> > +       }
> > +
> > +       host->ops = &pic32_sdhci_ops;
> > +       host->irq = platform_get_irq(pdev, 0);
> > +
> > +       sdhci_pdata->sys_clk = devm_clk_get(&pdev->dev, "sys_clk");
> > +       if (IS_ERR(sdhci_pdata->sys_clk)) {
> > +               ret = PTR_ERR(sdhci_pdata->sys_clk);
> > +               dev_err(&pdev->dev, "Error getting clock\n");
> > +               goto err_host;
> > +       }
> > +
> > +       /* Enable clock when available! */
> > +       ret = clk_prepare_enable(sdhci_pdata->sys_clk);
> > +       if (ret) {
> > +               dev_dbg(&pdev->dev, "Error enabling clock\n");
> 
> Shouldn't this be dev_err()?  You don't survive not having the clock in the stanza
> above.  So if you have the clock, you would want to know if it didn't deal with
> the clk_prepare_enable().
> 
> The comment 4 lines above is also wrong if so.

Ack. 

> 
> > +               goto err_host;
> > +       }
> > +
> > +       /* SDH CLK enable */
> > +       sdhci_pdata->base_clk = devm_clk_get(&pdev->dev, "base_clk");
> > +       if (IS_ERR(sdhci_pdata->base_clk)) {
> > +               ret = PTR_ERR(sdhci_pdata->base_clk);
> > +               dev_err(&pdev->dev, "Error getting clock\n");
> > +               goto err_host;
> > +       }
> > +
> > +       /* Enable clock when available! */
> > +       ret = clk_prepare_enable(sdhci_pdata->base_clk);
> 
> Again the comment seems wrong.

Ack.

> 
> > +       if (ret) {
> > +               dev_dbg(&pdev->dev, "Error enabling clock\n");
> 
> And if I understood it, dev_err() needed.

Ack.

> 
> > +               goto err_host;
> > +       }
> > +
> > +       clk_rate = clk_get_rate(sdhci_pdata->base_clk);
> > +       dev_dbg(&pdev->dev, "base clock at: %u\n", clk_rate);
> > +       clk_rate = clk_get_rate(sdhci_pdata->sys_clk);
> > +       dev_dbg(&pdev->dev, "sys clock at: %u\n", clk_rate);
> > +
> > +       host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
> > +
> 
> Probably can lose the blank line.

Ack.

> 
> > +       host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
> > +
> > +       ret = mmc_of_parse(host->mmc);
> > +       if (ret)
> > +               goto err_host;
> > +
> > +       ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
> > +       if (ret) {
> > +               dev_err(&pdev->dev, "failed to probe platform!\n");
> > +               goto err_host;
> > +       }
> > +
> > +       ret = sdhci_add_host(host);
> > +       if (ret) {
> > +               dev_dbg(&pdev->dev, "error adding host\n");
> > +               goto err_host;
> > +       }
> > +
> > +       dev_info(&pdev->dev, "Successfully added sdhci host\n");
> > +       return 0;
> > +
> > +err_host:
> > +       sdhci_free_host(host);
> > +err:
> > +       dev_err(&pdev->dev, "pic32-sdhci probe failed: %d\n", ret);
> > +       return ret;
> > +}
> > +
> > +static int pic32_sdhci_remove(struct platform_device *pdev) {
> > +       struct sdhci_host *host = platform_get_drvdata(pdev);
> > +       struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
> > +       int dead = 0;
> > +       u32 scratch;
> > +
> > +       scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
> > +       if (scratch == (u32)-1)
> 
> Since it's not actually related to signed, (u32)~0 might be clearer.

Ack.

> 
> > +               dead = 1;
> > +
> > +       sdhci_remove_host(host, dead);
> 
> You could get rid of "dead" and have
> 
>          sdhci_remove_host(host, scratch == (u32)~0);

Ack. 

> 
> > +       clk_disable_unprepare(sdhci_pdata->base_clk);
> > +       clk_disable_unprepare(sdhci_pdata->sys_clk);
> > +       sdhci_free_host(host);
> > +
> > +       return 0;
> > +}
> > +
> > +static const struct of_device_id pic32_sdhci_id_table[] = {
> > +       { .compatible = "microchip,pic32mzda-sdhci" },
> > +       {}
> > +};
> > +MODULE_DEVICE_TABLE(of, pic32_sdhci_id_table);
> > +
> > +static struct platform_driver pic32_sdhci_driver = {
> > +       .driver = {
> > +               .name   = DEV_NAME,
> > +               .owner  = THIS_MODULE,
> > +               .of_match_table = of_match_ptr(pic32_sdhci_id_table),
> > +       },
> > +       .probe          = pic32_sdhci_probe,
> > +       .remove         = pic32_sdhci_remove,
> > +};
> > +
> > +module_platform_driver(pic32_sdhci_driver);
> > +
> > +MODULE_DESCRIPTION("Microchip PIC32 SDHCI driver");
> > +MODULE_AUTHOR("Pistirica Sorin Andrei & Sandeep Sheriker");
> > +MODULE_LICENSE("GPL v2");
> > --
> > 1.7.9.5
> >

Thanks,
Paul
ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply	[flat|nested] 33+ messages in thread

* RE: [PATCH v2 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver
  2015-12-16 10:48   ` Ulf Hansson
@ 2015-12-17 18:05     ` Paul.Thacker
  0 siblings, 0 replies; 33+ messages in thread
From: Paul.Thacker @ 2015-12-17 18:05 UTC (permalink / raw)
  To: ulf.hansson, Joshua.Henderson
  Cc: linux-kernel, linux-mips, ralf, Andrei.Pistirica, jdelvare,
	geert, cdoban, haojian.zhuang, luisbg, Weijun.Yang, lokeshvutla,
	sbranden, vincent.yang.fujitsu, chaotian.jing, ludovic.desroches,
	shawn.lin, sboyd, yangbo.lu, haokexin, ben, andy.green,
	linux-mmc

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 6476 bytes --]

On 12/16/2015 3:48 AM, Ulf Hansson wrote:
> 
> [...]
> 
> > +static int pic32_sdhci_probe(struct platform_device *pdev) {
> > +       struct device *dev = &pdev->dev;
> > +       struct sdhci_host *host;
> > +       struct resource *iomem;
> > +       struct pic32_sdhci_pdata *sdhci_pdata;
> > +       struct pic32_sdhci_platform_data *plat_data;
> > +       unsigned int clk_rate = 0;
> > +       int ret;
> > +       struct pinctrl *pinctrl;
> > +
> > +       host = sdhci_alloc_host(dev, sizeof(*sdhci_pdata));
> > +       if (IS_ERR(host)) {
> > +               ret = PTR_ERR(host);
> > +               dev_err(&pdev->dev, "cannot allocate memory for sdhci\n");
> > +               goto err;
> > +       }
> > +
> > +       sdhci_pdata = sdhci_priv(host);
> > +       sdhci_pdata->pdev = pdev;
> > +       platform_set_drvdata(pdev, host);
> > +
> > +       iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +       host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
> > +       if (IS_ERR(host->ioaddr)) {
> > +               ret = PTR_ERR(host->ioaddr);
> > +               dev_err(&pdev->dev, "unable to map iomem: %d\n", ret);
> > +               goto err_host;
> > +       }
> > +
> > +       plat_data = pdev->dev.platform_data;
> > +       if (plat_data && plat_data->setup_dma) {
> > +               ret = plat_data->setup_dma(ADMA_FIFO_RD_THSHLD,
> > +                                          ADMA_FIFO_WR_THSHLD);
> > +               if (ret)
> > +                       goto err_host;
> > +       }
> > +
> > +       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
> 
> This isn't need as it's already handled by the PM core.

Ack. Will remove.

> 
> > +       if (IS_ERR(pinctrl)) {
> > +               ret = PTR_ERR(pinctrl);
> > +               dev_warn(&pdev->dev, "No pinctrl provided %d\n", ret);
> > +               if (ret == -EPROBE_DEFER)
> > +                       goto err_host;
> > +       }
> > +
> > +       host->ops = &pic32_sdhci_ops;
> > +       host->irq = platform_get_irq(pdev, 0);
> > +
> > +       sdhci_pdata->sys_clk = devm_clk_get(&pdev->dev, "sys_clk");
> > +       if (IS_ERR(sdhci_pdata->sys_clk)) {
> > +               ret = PTR_ERR(sdhci_pdata->sys_clk);
> > +               dev_err(&pdev->dev, "Error getting clock\n");
> > +               goto err_host;
> > +       }
> > +
> > +       /* Enable clock when available! */
> > +       ret = clk_prepare_enable(sdhci_pdata->sys_clk);
> > +       if (ret) {
> > +               dev_dbg(&pdev->dev, "Error enabling clock\n");
> > +               goto err_host;
> > +       }
> > +
> > +       /* SDH CLK enable */
> > +       sdhci_pdata->base_clk = devm_clk_get(&pdev->dev, "base_clk");
> > +       if (IS_ERR(sdhci_pdata->base_clk)) {
> > +               ret = PTR_ERR(sdhci_pdata->base_clk);
> > +               dev_err(&pdev->dev, "Error getting clock\n");
> > +               goto err_host;
> > +       }
> > +
> > +       /* Enable clock when available! */
> > +       ret = clk_prepare_enable(sdhci_pdata->base_clk);
> > +       if (ret) {
> > +               dev_dbg(&pdev->dev, "Error enabling clock\n");
> > +               goto err_host;
> > +       }
> > +
> > +       clk_rate = clk_get_rate(sdhci_pdata->base_clk);
> > +       dev_dbg(&pdev->dev, "base clock at: %u\n", clk_rate);
> > +       clk_rate = clk_get_rate(sdhci_pdata->sys_clk);
> > +       dev_dbg(&pdev->dev, "sys clock at: %u\n", clk_rate);
> 
> This looks like some leftover from a debugging task. Can you remove them?
> 

Yes, these are not needed and will be removed.

> > +
> > +       host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
> > +
> > +       host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
> > +
> > +       ret = mmc_of_parse(host->mmc);
> > +       if (ret)
> 
> From this point, the error handling doesn't undo clk_prepare_enable().
> Please add that.

Ack. Will do.

> 
> > +               goto err_host;
> > +
> > +       ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
> > +       if (ret) {
> > +               dev_err(&pdev->dev, "failed to probe platform!\n");
> > +               goto err_host;
> > +       }
> > +
> > +       ret = sdhci_add_host(host);
> > +       if (ret) {
> > +               dev_dbg(&pdev->dev, "error adding host\n");
> > +               goto err_host;
> > +       }
> > +
> > +       dev_info(&pdev->dev, "Successfully added sdhci host\n");
> > +       return 0;
> > +
> > +err_host:
> > +       sdhci_free_host(host);
> > +err:
> > +       dev_err(&pdev->dev, "pic32-sdhci probe failed: %d\n", ret);
> > +       return ret;
> 
> A general comment for the ->probe() and the below ->remove() callback, is that
> you should probably be able to convert to use
> sdhci_pltfm_init() and sdhci_pltfm_free() in favor of
> sdhci_alloc_host() and sdhci_free_host().
> 
> I think that could simplify the code a bit.

Ok. Will do.

> 
> > +}
> > +
> > +static int pic32_sdhci_remove(struct platform_device *pdev) {
> > +       struct sdhci_host *host = platform_get_drvdata(pdev);
> > +       struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
> > +       int dead = 0;
> > +       u32 scratch;
> > +
> > +       scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
> > +       if (scratch == (u32)-1)
> > +               dead = 1;
> > +
> > +       sdhci_remove_host(host, dead);
> > +       clk_disable_unprepare(sdhci_pdata->base_clk);
> > +       clk_disable_unprepare(sdhci_pdata->sys_clk);
> > +       sdhci_free_host(host);
> > +
> > +       return 0;
> > +}
> > +
> > +static const struct of_device_id pic32_sdhci_id_table[] = {
> > +       { .compatible = "microchip,pic32mzda-sdhci" },
> > +       {}
> > +};
> > +MODULE_DEVICE_TABLE(of, pic32_sdhci_id_table);
> > +
> > +static struct platform_driver pic32_sdhci_driver = {
> > +       .driver = {
> > +               .name   = DEV_NAME,
> > +               .owner  = THIS_MODULE,
> > +               .of_match_table = of_match_ptr(pic32_sdhci_id_table),
> > +       },
> > +       .probe          = pic32_sdhci_probe,
> > +       .remove         = pic32_sdhci_remove,
> > +};
> > +
> > +module_platform_driver(pic32_sdhci_driver);
> > +
> > +MODULE_DESCRIPTION("Microchip PIC32 SDHCI driver");
> > +MODULE_AUTHOR("Pistirica Sorin Andrei & Sandeep Sheriker");
> > +MODULE_LICENSE("GPL v2");
> > --
> > 1.7.9.5

Thanks,
Paul
ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 03/14] DEVICETREE: Add PIC32 clock binding documentation
  2015-12-14 22:42 ` [PATCH v2 03/14] DEVICETREE: Add PIC32 clock binding documentation Joshua Henderson
@ 2015-12-18 15:44   ` Rob Herring
  2015-12-19 11:48     ` Purna Chandra Mandal
  0 siblings, 1 reply; 33+ messages in thread
From: Rob Herring @ 2015-12-18 15:44 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, linux-mips, ralf, Purna Chandra Mandal, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, devicetree

On Mon, Dec 14, 2015 at 03:42:05PM -0700, Joshua Henderson wrote:
> From: Purna Chandra Mandal <purna.mandal@microchip.com>
> 
> Document the devicetree bindings for the clock driver found on Microchip
> PIC32 class devices.
> 
> Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com>
> Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
> Cc: Ralf Baechle <ralf@linux-mips.org>

A couple of nits on the example, otherwise:

Acked-by: Rob Herring <robh@kernel.org>

> ---
>  .../devicetree/bindings/clock/microchip,pic32.txt  |  256 ++++++++++++++++++++
>  1 file changed, 256 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/microchip,pic32.txt
> 
> diff --git a/Documentation/devicetree/bindings/clock/microchip,pic32.txt b/Documentation/devicetree/bindings/clock/microchip,pic32.txt
> new file mode 100644
> index 0000000..f50c653
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/microchip,pic32.txt
> @@ -0,0 +1,256 @@
> +Binding for a Clock hardware block found on
> +certain Microchip PIC32 MCU devices.
> +
> +Microchip SoC clocks-node consists of few oscillators, PLL, multiplexer
> +and few divider nodes.
> +
> +We will find only the base address of the clock tree, this base
> +address is common for some of the subnodes, not all. If no address is
> +specified for any of subnode base address of the clock tree will be
> +treated as its base. Each of subnodes follow the same common clock
> +binding with some additional optional properties.
> +
> +	clocks_node {
> +		reg = <>;
> +
> +		spll_node {
> +			...
> +		};
> +
> +		frcdiv_node {
> +			...
> +		};
> +
> +		sysclk_mux_node {
> +			...
> +		};
> +
> +		pbdiv_node {
> +			...
> +		};
> +
> +		refoclk_node {
> +			...
> +		};
> +		...
> +	};
> +
> +This binding uses the common clock binding[1].
> +
> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> +
> +Required properties:
> +- compatible : should be one of "microchip,pic32mzda-clk",
> +    "microchip,pic32mzda-sosc", "microchip,pic32mzda-frcdivclk",
> +    "microchip,pic32mzda-syspll", "microchip,pic32mzda-sysclk-v2",
> +    "microchip,pic32mzda-pbclk", "microchip,pic32mzda-refoclk".
> +- reg : A Base address and length of the register set.
> +- interrupts : source of interrupt.
> +
> +Optional properties (for subnodes):
> +- #clock-cells: From common clock binding, should be 0.
> +- microchip,clock-indices: in multiplexer node clock sources always aren't linear
> +    and contiguous. This property helps define clock-sources with respect to
> +    the mux clock node.
> +- microchip,ignore-unused : ignore gate request even if the gated clock is unused.
> +- microchip,status-bit-mask: bitmask for status check. This will be used to confirm
> +    particular operation by clock sub-node is completed. It is dependent sub-node.
> +- microchip,bit-mask: enable mask, similar to microchip,status-bit-mask.
> +- microchip,slew-step: enable frequency slewing(stepping) during rate change;
> +    applicable only to sys-clock subnode.
> +
> +Example:
> +
> +/* PIC32 specific clks */
> +pic32_clktree {
> +	#address-cells = <1>;
> +	#size-cells = <1>;
> +	reg = <0x1f801200 0x200>;
> +	compatible = "microchip,pic32mzda-clk";
> +	ranges = <0 0x1f801200 0x200>;
> +
> +	/* secondary oscillator; external input on SOSCI pin */
> +	SOSC:sosc_clk {

For the ones with reg property, do clock@0 instead of sosc_clk.

> +		#clock-cells = <0>;
> +		compatible = "microchip,pic32mzda-sosc";
> +		clock-frequency = <32768>;
> +		reg = <0x000 0x10>, /* enable reg */
> +		      <0x1d0 0x10>; /* status reg */
> +		microchip,bit-mask = <0x02>; /* enable mask */
> +		microchip,status-bit-mask = <0x10>; /* status-mask*/
> +	};
> +
> +	FRCDIV:frcdiv_clk {
> +		#clock-cells = <0>;
> +		compatible = "microchip,pic32mzda-frcdivclk";
> +		clocks = <&FRC>;
> +		clock-output-names = "frcdiv_clk";
> +	};
> +
> +	/* System PLL clock */
> +	SYSPLL:spll_clk {
> +		#clock-cells = <0>;
> +		compatible = "microchip,pic32mzda-syspll";
> +		reg = <0x020 0x10>, /* SPLL register */
> +		      <0x1d0 0x10>; /* CLKSTAT register */
> +		clocks = <&POSC>, <&FRC>;
> +		clock-output-names = "sys_pll";
> +		microchip,status-bit-mask = <0x80>; /* SPLLRDY */
> +	};
> +
> +	/* system clock; mux with postdiv & slew */
> +	SYSCLK:sys_clk {
> +		#clock-cells = <0>;
> +		compatible = "microchip,pic32mzda-sysclk-v2";
> +		reg = <0x1c0 0x04>; /* SLEWCON */
> +		clocks = <&FRCDIV>, <&SYSPLL>, <&POSC>, <&SOSC>,
> +				<&LPRC>, <&FRCDIV>;
> +		microchip,clock-indices = <0>, <1>, <2>, <4>, <5>, <7>;
> +		clock-output-names = "sys_clk";
> +	};
> +
> +	/* UPLL is integral part of USB PHY; UTMI clk for USBCORE */
> +	UPLL:usb_phy_clk {
> +		#clock-cells = <0>;
> +		compatible = "fixed-clocks";
> +		clock-frequency = <24000000>;
> +		clock-output-names = "usbphy_clk";
> +	};
> +
> +	/* Peripheral bus1 clock */
> +	PBCLK1:pb1_clk {
> +		reg = <0x140 0x10>;
> +		#clock-cells = <0>;
> +		compatible = "microchip,pic32mzda-pbclk";
> +		clocks = <&SYSCLK>;
> +		clock-output-names = "pb1_clk";
> +		/* used by system modules, not gateable */
> +		microchip,ignore-unused;
> +	};
> +
> +	/* Peripheral bus2 clock */
> +	PBCLK2:pb2_clk {
> +		reg = <0x150 0x10>;
> +		#clock-cells = <0>;
> +		compatible = "microchip,pic32mzda-pbclk";
> +		clocks = <&SYSCLK>;
> +		clock-output-names = "pb2_clk";
> +		/* avoid gating even if unused */
> +		microchip,ignore-unused;
> +	};
> +
> +	/* Peripheral bus3 clock */
> +	PBCLK3:pb3_clk {
> +		reg = <0x160 0x10>;
> +		#clock-cells = <0>;
> +		compatible = "microchip,pic32mzda-pbclk";
> +		clocks = <&SYSCLK>;
> +		clock-output-names = "pb3_clk";
> +	};
> +
> +	/* Peripheral bus4 clock(I/O ports, GPIO) */
> +	PBCLK4:pb4_clk {
> +		reg = <0x170 0x10>;
> +		#clock-cells = <0>;
> +		compatible = "microchip,pic32mzda-pbclk";
> +		clocks = <&SYSCLK>;
> +		clock-output-names = "pb4_clk";
> +	};
> +
> +	/* Peripheral bus clock */
> +	PBCLK5:pb5_clk {
> +		reg = <0x180 0x10>;
> +		#clock-cells = <0>;
> +		compatible = "microchip,pic32mzda-pbclk";
> +		clocks = <&SYSCLK>;
> +		clock-output-names = "pb5_clk";
> +	};
> +
> +	/* Peripheral Bus6 clock; */
> +	PBCLK6:pb6_clk {
> +		reg = <0x190 0x10>;
> +		compatible = "microchip,pic32mzda-pbclk";
> +		clocks = <&SYSCLK>;
> +		#clock-cells = <0>;
> +	};
> +
> +	/* Peripheral bus7 clock */
> +	PBCLK7:pb7_clk {
> +		reg = <0x1A0 0x10>;

lower case

> +		#clock-cells = <0>;
> +		compatible = "microchip,pic32mzda-pbclk";
> +		/* CPU is driven by this clock; so named */
> +		clock-output-names = "cpu_clk";
> +		clocks = <&SYSCLK>;
> +	};
> +
> +	/* Reference Oscillator clock for SPI/I2S */
> +	REFCLKO1:refo1_clk {
> +		reg = <0x080 0x20>;
> +		#clock-cells = <0>;
> +		compatible = "microchip,pic32mzda-refoclk";
> +		clocks = <&SYSCLK>, <&PBCLK1>, <&POSC>, <&FRC>, <&LPRC>,
> +			<&SOSC>, <&SYSPLL>, <&REFIx>, <&BFRC>;
> +		microchip,clock-indices = <0>, <1>, <2>, <3>, <4>, <5>,
> +						<7>, <8>, <9>;
> +		clock-output-names = "refo1_clk";
> +	};
> +
> +	/* Reference Oscillator clock for SQI */
> +	REFCLKO2:refo2_clk {
> +		reg = <0x0A0 0x20>;

lower case

> +		#clock-cells = <0>;
> +		compatible = "microchip,pic32mzda-refoclk";
> +		clocks = <&SYSCLK>, <&PBCLK1>, <&POSC>, <&FRC>, <&LPRC>,
> +			<&SOSC>, <&SYSPLL>, <&REFIx>, <&BFRC>;
> +		microchip,clock-indices = <0>, <1>, <2>, <3>, <4>, <5>,
> +						<7>, <8>, <9>;
> +		clock-output-names = "refo2_clk";
> +	};
> +
> +	/* Reference Oscillator clock, ADC */
> +	REFCLKO3:refo3_clk {
> +		reg = <0x0C0 0x20>;

lower case

> +		compatible = "microchip,pic32mzda-refoclk";
> +		clocks = <&SYSCLK>, <&PBCLK1>, <&POSC>, <&FRC>, <&LPRC>,
> +			<&SOSC>, <&SYSPLL>, <&REFIx>, <&BFRC>;
> +		microchip,clock-indices = <0>, <1>, <2>, <3>, <4>, <5>,
> +						<7>, <8>, <9>;
> +		#clock-cells = <0>;
> +		clock-output-names = "refo3_clk";
> +	};
> +
> +	/* Reference Oscillator clock */
> +	REFCLKO4:refo4_clk {
> +		reg = <0x0E0 0x20>;
> +		compatible = "microchip,pic32mzda-refoclk";
> +		clocks = <&SYSCLK>, <&PBCLK1>, <&POSC>, <&FRC>, <&LPRC>,
> +				<&SOSC>, <&SYSPLL>, <&REFIx>, <&BFRC>;
> +		microchip,clock-indices = <0>,<1>,<2>,<3>,<4>,<5>,<7>,
> +						<8>,<9>;
> +		#clock-cells = <0>;
> +		clock-output-names = "refo4_clk";
> +	};
> +
> +	/* Reference Oscillator clock, LCD */
> +	REFCLKO5:refo5_clk {
> +		reg = <0x100 0x20>;
> +		compatible = "microchip,pic32mzda-refoclk";
> +		clocks = <&SYSCLK>,<&PBCLK1>,<&POSC>,<&FRC>,<&LPRC>,
> +			<&SOSC>,<&SYSPLL>,<&REFIx>,<&BFRC>;
> +		microchip,clock-indices = <0>, <1>, <2>, <3>, <4>, <5>,
> +					<7>, <8>,<9>;
> +		#clock-cells = <0>;
> +		clock-output-names = "refo5_clk";
> +	};
> +};
> +
> +The clock consumer should specify the desired clock by having the clocks in its
> +"clock" phandle cell. For example for UART:
> +
> +uart2: serial@<> {
> +	compatible = "microchip,pic32mzda-uart";
> +	reg = <>;
> +	interrupts = <>;
> +	clocks = <&PBCLK2>;
> +}
> -- 
> 1.7.9.5
> 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 05/14] DEVICETREE: Add bindings for PIC32/MZDA platforms
  2015-12-14 22:42 ` [PATCH v2 05/14] DEVICETREE: Add bindings for PIC32/MZDA platforms Joshua Henderson
@ 2015-12-18 15:47   ` Rob Herring
  0 siblings, 0 replies; 33+ messages in thread
From: Rob Herring @ 2015-12-18 15:47 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, linux-mips, ralf, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, devicetree

On Mon, Dec 14, 2015 at 03:42:07PM -0700, Joshua Henderson wrote:

dt/bindings: ... is preferred for the subject.

> This adds support for the Microchip PIC32 platform along with the
> specific variant PIC32MZDA on a PIC32MZDA Starter Kit.
> 
> Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
> Cc: Ralf Baechle <ralf@linux-mips.org>

Acked-by: Rob Herring <robh@kernel.org>

> ---
>  .../bindings/mips/pic32/microchip,pic32mzda.txt    |   33 ++++++++++++++++++++
>  1 file changed, 33 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt
> 
> diff --git a/Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt b/Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt
> new file mode 100644
> index 0000000..bcf3e04
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt
> @@ -0,0 +1,33 @@
> +* Microchip PIC32MZDA Platforms
> +
> +PIC32MZDA Starter Kit
> +Required root node properties:
> +    - compatible = "microchip,pic32mzda-sk", "microchip,pic32mzda"
> +
> +CPU nodes:
> +----------
> +A "cpus" node is required.  Required properties:
> + - #address-cells: Must be 1.
> + - #size-cells: Must be 0.
> +A CPU sub-node is also required.  Required properties:
> + - device_type: Must be "cpu".
> + - compatible: Must be "mti,mips14KEc".
> +Example:
> +	cpus {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		cpu0: cpu@0 {
> +			device_type = "cpu";
> +			compatible = "mti,mips14KEc";
> +		};
> +	};
> +
> +Boot protocol
> +--------------
> +In accordance with the MIPS UHI specification[1], the bootloader must pass the
> +following arguments to the kernel:
> + - $a0: -2.
> + - $a1: KSEG0 address of the flattened device-tree blob.
> +
> +[1] http://prplfoundation.org/wiki/MIPS_documentation
> -- 
> 1.7.9.5
> 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 03/14] DEVICETREE: Add PIC32 clock binding documentation
  2015-12-18 15:44   ` Rob Herring
@ 2015-12-19 11:48     ` Purna Chandra Mandal
  0 siblings, 0 replies; 33+ messages in thread
From: Purna Chandra Mandal @ 2015-12-19 11:48 UTC (permalink / raw)
  To: Rob Herring
  Cc: Joshua Henderson, linux-kernel, linux-mips, ralf, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, devicetree

On 12/18/2015 09:14 PM, Rob Herring wrote:
> On Mon, Dec 14, 2015 at 03:42:05PM -0700, Joshua Henderson wrote:
>> From: Purna Chandra Mandal <purna.mandal@microchip.com>
>>
>> Document the devicetree bindings for the clock driver found on Microchip
>> PIC32 class devices.
>>
>> Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com>
>> Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
>> Cc: Ralf Baechle <ralf@linux-mips.org>
> A couple of nits on the example, otherwise:
>
> Acked-by: Rob Herring <robh@kernel.org>
>
>> ---
>>  .../devicetree/bindings/clock/microchip,pic32.txt  |  256 ++++++++++++++++++++
>>  1 file changed, 256 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/clock/microchip,pic32.txt
>>
>> diff --git a/Documentation/devicetree/bindings/clock/microchip,pic32.txt b/Documentation/devicetree/bindings/clock/microchip,pic32.txt
>> new file mode 100644
>> index 0000000..f50c653
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/clock/microchip,pic32.txt
>> @@ -0,0 +1,256 @@
>> +Binding for a Clock hardware block found on
>> +certain Microchip PIC32 MCU devices.
>> +
>> +Microchip SoC clocks-node consists of few oscillators, PLL, multiplexer
>> +and few divider nodes.
>> +
>> +We will find only the base address of the clock tree, this base
>> +address is common for some of the subnodes, not all. If no address is
>> +specified for any of subnode base address of the clock tree will be
>> +treated as its base. Each of subnodes follow the same common clock
>> +binding with some additional optional properties.
>> +
>> +	clocks_node {
>> +		reg = <>;
>> +
>> +		spll_node {
>> +			...
>> +		};
>> +
>> +		frcdiv_node {
>> +			...
>> +		};
>> +
>> +		sysclk_mux_node {
>> +			...
>> +		};
>> +
>> +		pbdiv_node {
>> +			...
>> +		};
>> +
>> +		refoclk_node {
>> +			...
>> +		};
>> +		...
>> +	};
>> +
>> +This binding uses the common clock binding[1].
>> +
>> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
>> +
>> +Required properties:
>> +- compatible : should be one of "microchip,pic32mzda-clk",
>> +    "microchip,pic32mzda-sosc", "microchip,pic32mzda-frcdivclk",
>> +    "microchip,pic32mzda-syspll", "microchip,pic32mzda-sysclk-v2",
>> +    "microchip,pic32mzda-pbclk", "microchip,pic32mzda-refoclk".
>> +- reg : A Base address and length of the register set.
>> +- interrupts : source of interrupt.
>> +
>> +Optional properties (for subnodes):
>> +- #clock-cells: From common clock binding, should be 0.
>> +- microchip,clock-indices: in multiplexer node clock sources always aren't linear
>> +    and contiguous. This property helps define clock-sources with respect to
>> +    the mux clock node.
>> +- microchip,ignore-unused : ignore gate request even if the gated clock is unused.
>> +- microchip,status-bit-mask: bitmask for status check. This will be used to confirm
>> +    particular operation by clock sub-node is completed. It is dependent sub-node.
>> +- microchip,bit-mask: enable mask, similar to microchip,status-bit-mask.
>> +- microchip,slew-step: enable frequency slewing(stepping) during rate change;
>> +    applicable only to sys-clock subnode.
>> +
>> +Example:
>> +
>> +/* PIC32 specific clks */
>> +pic32_clktree {
>> +	#address-cells = <1>;
>> +	#size-cells = <1>;
>> +	reg = <0x1f801200 0x200>;
>> +	compatible = "microchip,pic32mzda-clk";
>> +	ranges = <0 0x1f801200 0x200>;
>> +
>> +	/* secondary oscillator; external input on SOSCI pin */
>> +	SOSC:sosc_clk {
> For the ones with reg property, do clock@0 instead of sosc_clk.

ack. Will update.

>> +		#clock-cells = <0>;
>> +		compatible = "microchip,pic32mzda-sosc";
>> +		clock-frequency = <32768>;
>> +		reg = <0x000 0x10>, /* enable reg */
>> +		      <0x1d0 0x10>; /* status reg */
>> +		microchip,bit-mask = <0x02>; /* enable mask */
>> +		microchip,status-bit-mask = <0x10>; /* status-mask*/
>> +	};
>> +
>> +	FRCDIV:frcdiv_clk {
>> +		#clock-cells = <0>;
>> +		compatible = "microchip,pic32mzda-frcdivclk";
>> +		clocks = <&FRC>;
>> +		clock-output-names = "frcdiv_clk";
>> +	};
>> +
>> +	/* System PLL clock */
>> +	SYSPLL:spll_clk {
>> +		#clock-cells = <0>;
>> +		compatible = "microchip,pic32mzda-syspll";
>> +		reg = <0x020 0x10>, /* SPLL register */
>> +		      <0x1d0 0x10>; /* CLKSTAT register */
>> +		clocks = <&POSC>, <&FRC>;
>> +		clock-output-names = "sys_pll";
>> +		microchip,status-bit-mask = <0x80>; /* SPLLRDY */
>> +	};
>> +
>> +	/* system clock; mux with postdiv & slew */
>> +	SYSCLK:sys_clk {
>> +		#clock-cells = <0>;
>> +		compatible = "microchip,pic32mzda-sysclk-v2";
>> +		reg = <0x1c0 0x04>; /* SLEWCON */
>> +		clocks = <&FRCDIV>, <&SYSPLL>, <&POSC>, <&SOSC>,
>> +				<&LPRC>, <&FRCDIV>;
>> +		microchip,clock-indices = <0>, <1>, <2>, <4>, <5>, <7>;
>> +		clock-output-names = "sys_clk";
>> +	};
>> +
>> +	/* UPLL is integral part of USB PHY; UTMI clk for USBCORE */
>> +	UPLL:usb_phy_clk {
>> +		#clock-cells = <0>;
>> +		compatible = "fixed-clocks";
>> +		clock-frequency = <24000000>;
>> +		clock-output-names = "usbphy_clk";
>> +	};
>> +
>> +	/* Peripheral bus1 clock */
>> +	PBCLK1:pb1_clk {
>> +		reg = <0x140 0x10>;
>> +		#clock-cells = <0>;
>> +		compatible = "microchip,pic32mzda-pbclk";
>> +		clocks = <&SYSCLK>;
>> +		clock-output-names = "pb1_clk";
>> +		/* used by system modules, not gateable */
>> +		microchip,ignore-unused;
>> +	};
>> +
>> +	/* Peripheral bus2 clock */
>> +	PBCLK2:pb2_clk {
>> +		reg = <0x150 0x10>;
>> +		#clock-cells = <0>;
>> +		compatible = "microchip,pic32mzda-pbclk";
>> +		clocks = <&SYSCLK>;
>> +		clock-output-names = "pb2_clk";
>> +		/* avoid gating even if unused */
>> +		microchip,ignore-unused;
>> +	};
>> +
>> +	/* Peripheral bus3 clock */
>> +	PBCLK3:pb3_clk {
>> +		reg = <0x160 0x10>;
>> +		#clock-cells = <0>;
>> +		compatible = "microchip,pic32mzda-pbclk";
>> +		clocks = <&SYSCLK>;
>> +		clock-output-names = "pb3_clk";
>> +	};
>> +
>> +	/* Peripheral bus4 clock(I/O ports, GPIO) */
>> +	PBCLK4:pb4_clk {
>> +		reg = <0x170 0x10>;
>> +		#clock-cells = <0>;
>> +		compatible = "microchip,pic32mzda-pbclk";
>> +		clocks = <&SYSCLK>;
>> +		clock-output-names = "pb4_clk";
>> +	};
>> +
>> +	/* Peripheral bus clock */
>> +	PBCLK5:pb5_clk {
>> +		reg = <0x180 0x10>;
>> +		#clock-cells = <0>;
>> +		compatible = "microchip,pic32mzda-pbclk";
>> +		clocks = <&SYSCLK>;
>> +		clock-output-names = "pb5_clk";
>> +	};
>> +
>> +	/* Peripheral Bus6 clock; */
>> +	PBCLK6:pb6_clk {
>> +		reg = <0x190 0x10>;
>> +		compatible = "microchip,pic32mzda-pbclk";
>> +		clocks = <&SYSCLK>;
>> +		#clock-cells = <0>;
>> +	};
>> +
>> +	/* Peripheral bus7 clock */
>> +	PBCLK7:pb7_clk {
>> +		reg = <0x1A0 0x10>;
> lower case

ack.

>> +		#clock-cells = <0>;
>> +		compatible = "microchip,pic32mzda-pbclk";
>> +		/* CPU is driven by this clock; so named */
>> +		clock-output-names = "cpu_clk";
>> +		clocks = <&SYSCLK>;
>> +	};
>> +
>> +	/* Reference Oscillator clock for SPI/I2S */
>> +	REFCLKO1:refo1_clk {
>> +		reg = <0x080 0x20>;
>> +		#clock-cells = <0>;
>> +		compatible = "microchip,pic32mzda-refoclk";
>> +		clocks = <&SYSCLK>, <&PBCLK1>, <&POSC>, <&FRC>, <&LPRC>,
>> +			<&SOSC>, <&SYSPLL>, <&REFIx>, <&BFRC>;
>> +		microchip,clock-indices = <0>, <1>, <2>, <3>, <4>, <5>,
>> +						<7>, <8>, <9>;
>> +		clock-output-names = "refo1_clk";
>> +	};
>> +
>> +	/* Reference Oscillator clock for SQI */
>> +	REFCLKO2:refo2_clk {
>> +		reg = <0x0A0 0x20>;
> lower case

ack.

>> +		#clock-cells = <0>;
>> +		compatible = "microchip,pic32mzda-refoclk";
>> +		clocks = <&SYSCLK>, <&PBCLK1>, <&POSC>, <&FRC>, <&LPRC>,
>> +			<&SOSC>, <&SYSPLL>, <&REFIx>, <&BFRC>;
>> +		microchip,clock-indices = <0>, <1>, <2>, <3>, <4>, <5>,
>> +						<7>, <8>, <9>;
>> +		clock-output-names = "refo2_clk";
>> +	};
>> +
>> +	/* Reference Oscillator clock, ADC */
>> +	REFCLKO3:refo3_clk {
>> +		reg = <0x0C0 0x20>;
> lower case

ack.

>> +		compatible = "microchip,pic32mzda-refoclk";
>> +		clocks = <&SYSCLK>, <&PBCLK1>, <&POSC>, <&FRC>, <&LPRC>,
>> +			<&SOSC>, <&SYSPLL>, <&REFIx>, <&BFRC>;
>> +		microchip,clock-indices = <0>, <1>, <2>, <3>, <4>, <5>,
>> +						<7>, <8>, <9>;
>> +		#clock-cells = <0>;
>> +		clock-output-names = "refo3_clk";
>> +	};
>> +
>> +	/* Reference Oscillator clock */
>> +	REFCLKO4:refo4_clk {
>> +		reg = <0x0E0 0x20>;
>> +		compatible = "microchip,pic32mzda-refoclk";
>> +		clocks = <&SYSCLK>, <&PBCLK1>, <&POSC>, <&FRC>, <&LPRC>,
>> +				<&SOSC>, <&SYSPLL>, <&REFIx>, <&BFRC>;
>> +		microchip,clock-indices = <0>,<1>,<2>,<3>,<4>,<5>,<7>,
>> +						<8>,<9>;
>> +		#clock-cells = <0>;
>> +		clock-output-names = "refo4_clk";
>> +	};
>> +
>> +	/* Reference Oscillator clock, LCD */
>> +	REFCLKO5:refo5_clk {
>> +		reg = <0x100 0x20>;
>> +		compatible = "microchip,pic32mzda-refoclk";
>> +		clocks = <&SYSCLK>,<&PBCLK1>,<&POSC>,<&FRC>,<&LPRC>,
>> +			<&SOSC>,<&SYSPLL>,<&REFIx>,<&BFRC>;
>> +		microchip,clock-indices = <0>, <1>, <2>, <3>, <4>, <5>,
>> +					<7>, <8>,<9>;
>> +		#clock-cells = <0>;
>> +		clock-output-names = "refo5_clk";
>> +	};
>> +};
>> +
>> +The clock consumer should specify the desired clock by having the clocks in its
>> +"clock" phandle cell. For example for UART:
>> +
>> +uart2: serial@<> {
>> +	compatible = "microchip,pic32mzda-uart";
>> +	reg = <>;
>> +	interrupts = <>;
>> +	clocks = <&PBCLK2>;
>> +}
>> -- 
>> 1.7.9.5
>>


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 10/14] serial: pic32_uart: Add PIC32 UART driver
  2015-12-14 22:42 ` [PATCH v2 10/14] serial: pic32_uart: Add " Joshua Henderson
@ 2015-12-20 16:13   ` Andy Shevchenko
  2016-01-05 20:29     ` Paul.Thacker
  2016-01-05 20:43   ` One Thousand Gnomes
  1 sibling, 1 reply; 33+ messages in thread
From: Andy Shevchenko @ 2015-12-20 16:13 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, linux-mips, Ralf Baechle, Andrei Pistirica,
	Greg Kroah-Hartman, Jiri Slaby, linux-serial, linux-api

On Tue, Dec 15, 2015 at 12:42 AM, Joshua Henderson
<joshua.henderson@microchip.com> wrote:
> From: Andrei Pistirica <andrei.pistirica@microchip.com>
>
> This adds UART and a serial console driver for Microchip PIC32 class
> devices.
>
> Signed-off-by: Andrei Pistirica <andrei.pistirica@microchip.com>
> Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
> Cc: Ralf Baechle <ralf@linux-mips.org>
> ---
>  drivers/tty/serial/Kconfig       |   21 +
>  drivers/tty/serial/Makefile      |    1 +
>  drivers/tty/serial/pic32_uart.c  |  927 ++++++++++++++++++++++++++++++++++++++
>  drivers/tty/serial/pic32_uart.h  |  198 ++++++++
>  include/uapi/linux/serial_core.h |    3 +
>  5 files changed, 1150 insertions(+)
>  create mode 100644 drivers/tty/serial/pic32_uart.c
>  create mode 100644 drivers/tty/serial/pic32_uart.h
>
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index f38beb2..8853b1e 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -901,6 +901,27 @@ config SERIAL_SGI_L1_CONSOLE
>                 controller serial port as your console (you want this!),
>                 say Y.  Otherwise, say N.
>
> +config SERIAL_PIC32
> +       tristate "Microchip PIC32 serial support"
> +       depends on MACH_PIC32
> +       select SERIAL_CORE
> +       help
> +         If you have a PIC32, this driver supports the serial ports.
> +
> +         Say Y or M to use PIC32 serial ports, otherwise say N. Note that
> +         to use a serial port as a console, this must be included in kernel and
> +         not as a module.
> +
> +config SERIAL_PIC32_CONSOLE
> +       bool "PIC32 serial console support"
> +       depends on SERIAL_PIC32
> +       select SERIAL_CORE_CONSOLE
> +       help
> +         If you have a PIC32, this driver supports the putting a console on one
> +         of the serial ports.
> +
> +         Say Y to use the PIC32 console, otherwise say N.
> +
>  config SERIAL_MPC52xx
>         tristate "Freescale MPC52xx/MPC512x family PSC serial support"
>         depends on PPC_MPC52xx || PPC_MPC512x
> diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
> index 5ab4111..bc5e354 100644
> --- a/drivers/tty/serial/Makefile
> +++ b/drivers/tty/serial/Makefile
> @@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)       += digicolor-usart.o
>  obj-$(CONFIG_SERIAL_MEN_Z135)  += men_z135_uart.o
>  obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
>  obj-$(CONFIG_SERIAL_STM32)     += stm32-usart.o
> +obj-$(CONFIG_SERIAL_PIC32)     += pic32_uart.o
>
>  # GPIOLIB helpers for modem control lines
>  obj-$(CONFIG_SERIAL_MCTRL_GPIO)        += serial_mctrl_gpio.o
> diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c
> new file mode 100644
> index 0000000..5c05c11
> --- /dev/null
> +++ b/drivers/tty/serial/pic32_uart.c
> @@ -0,0 +1,927 @@
> +/*
> + * PIC32 Integrated Serial Driver.
> + *
> + * Copyright (C) 2015 Microchip Technology, Inc.
> + *
> + * Authors:
> + *   Steve Scott <steve.scott@microchip.com>,
> + *   Sorin-Andrei Pistirica <andrei.pistirica@microchip.com>
> + *
> + * Licensed under GPLv2 or later.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_gpio.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/console.h>
> +#include <linux/clk-provider.h>

Didn't notice clock provider here.

> +#include <linux/clk.h>
> +#include <linux/clkdev.h>
> +#include <linux/tty.h>
> +#include <linux/tty_flip.h>
> +#include <linux/sysrq.h>
> +#include <linux/serial.h>
> +#include <linux/serial_core.h>
> +#include <uapi/linux/serial_core.h>
> +#include <linux/delay.h>

Revisit this list and leave exactly what is used.

> +
> +#include "pic32_uart.h"
> +
> +/* UART name and device definitions */
> +#define PIC32_DEV_NAME         "pic32-uart"
> +#define PIC32_MAX_UARTS                6
> +
> +#define PIC32_SDEV_NAME                "ttyS"
> +#define PIC32_SDEV_MAJOR       TTY_MAJOR
> +#define PIC32_SDEV_MINOR       64
> +
> +/* pic32_sport pointer for console use */
> +static struct pic32_sport *pic32_sports[PIC32_MAX_UARTS];
> +
> +static inline int pic32_enable_clock(struct pic32_sport *sport)
> +{
> +       sport->ref_clk++;
> +

Useless empty line (do in one style).

> +       return clk_prepare_enable(sport->clk);
> +}
> +
> +static inline void pic32_disable_clock(struct pic32_sport *sport)
> +{
> +       sport->ref_clk--;
> +       clk_disable_unprepare(sport->clk);
> +}
> +
> +/* serial core request to check if uart tx buffer is empty */
> +static unsigned int pic32_uart_tx_empty(struct uart_port *port)
> +{
> +       struct pic32_sport *sport = to_pic32_sport(port);
> +       u32 val = pic32_uart_read(sport, PIC32_UART_STA);
> +
> +       return (val & PIC32_UART_STA_TRMT) ? 1 : 0;
> +}
> +
> +/* serial core request to set UART outputs */
> +static void pic32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
> +{
> +       struct pic32_sport *sport = to_pic32_sport(port);
> +
> +       /* set loopback mode */
> +       if (mctrl & TIOCM_LOOP)
> +               pic32_uart_rset(PIC32_UART_MODE_LPBK, sport, PIC32_UART_MODE);
> +       else
> +               pic32_uart_rclr(PIC32_UART_MODE_LPBK, sport, PIC32_UART_MODE);
> +}
> +
> +/* get the state of CTS input pin for this port */
> +static unsigned int get_cts_state(struct pic32_sport *sport)
> +{
> +       /* default state must be asserted */
> +       int val = 1;

Redundant.

> +
> +       /* read and invert UxCTS */
> +       if (gpio_is_valid(sport->cts_gpio))
> +               val = !gpio_get_value(sport->cts_gpio);

If (…)
 return …;

return 1;

> +
> +       return val;
> +}
> +
> +/* serial core request to return the state of misc UART input pins */
> +static unsigned int pic32_uart_get_mctrl(struct uart_port *port)
> +{
> +       struct pic32_sport *sport = to_pic32_sport(port);
> +       unsigned int mctrl = 0;
> +
> +       if (!sport->hw_flow_ctrl) {
> +               mctrl |= TIOCM_CTS;

> +               goto ret;
> +       }
> +
> +       if (get_cts_state(sport))

else if (get_cts_state(sport))

> +               mctrl |= TIOCM_CTS;
> +

> +ret:

And remove useless label, besides that fact that its name is awfully chosen.

> +       /* DSR and CD are not supported in PIC32, so return 1
> +        * RI is not supported in PIC32, so return 0
> +        */
> +       mctrl |= TIOCM_CD;
> +       mctrl |= TIOCM_DSR;
> +
> +       return mctrl;
> +}
> +
> +/* stop tx and start tx are not called in pairs, therefore a flag indicates
> + * the status of irq to control the irq-depth.
> + */
> +static inline void pic32_uart_irqtxen(struct pic32_sport *sport, u8 en)
> +{
> +       if (en && !tx_irq_enabled(sport)) {
> +               enable_irq(sport->irq_tx);
> +               tx_irq_enabled(sport) = 1;
> +       } else if (!en && tx_irq_enabled(sport)) {
> +               /* use disable_irq_nosync() and not disable_irq() to avoid self
> +                * imposed deadlock by not waiting for irq handler to end,
> +                * since this callback is called from interrupt context.
> +                */
> +               disable_irq_nosync(sport->irq_tx);
> +               tx_irq_enabled(sport) = 0;
> +       }
> +}
> +
> +/* serial core request to disable tx ASAP (used for flow control) */
> +static void pic32_uart_stop_tx(struct uart_port *port)
> +{
> +       struct pic32_sport *sport = to_pic32_sport(port);
> +
> +       if (!(pic32_uart_read(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON))
> +               return;
> +
> +       if (!(pic32_uart_read(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN))
> +               return;
> +
> +       /* wait for tx empty */
> +       while (!(pic32_uart_read(sport, PIC32_UART_STA) & PIC32_UART_STA_TRMT))
> +               udelay(1);
> +
> +       pic32_uart_rclr(PIC32_UART_STA_UTXEN, sport, PIC32_UART_STA);
> +       pic32_uart_irqtxen(sport, 0);
> +}
> +
> +/* serial core request to (re)enable tx */
> +static void pic32_uart_start_tx(struct uart_port *port)
> +{
> +       struct pic32_sport *sport = to_pic32_sport(port);
> +
> +       pic32_uart_irqtxen(sport, 1);
> +       pic32_uart_rset(PIC32_UART_STA_UTXEN, sport, PIC32_UART_STA);
> +}
> +
> +/* serial core request to stop rx, called before port shutdown */
> +static void pic32_uart_stop_rx(struct uart_port *port)
> +{
> +       struct pic32_sport *sport = to_pic32_sport(port);
> +
> +       /* disable rx interrupts */
> +       disable_irq(sport->irq_rx);
> +
> +       /* receiver Enable bit OFF */
> +       pic32_uart_rclr(PIC32_UART_STA_URXEN, sport, PIC32_UART_STA);
> +}
> +
> +/* serial core request to start/stop emitting break char */
> +static void pic32_uart_break_ctl(struct uart_port *port, int ctl)
> +{
> +       struct pic32_sport *sport = to_pic32_sport(port);
> +       unsigned long flags = 0;

Useless assignment.

> +
> +       spin_lock_irqsave(&port->lock, flags);
> +
> +       if (ctl)
> +               pic32_uart_rset(PIC32_UART_STA_UTXBRK, sport, PIC32_UART_STA);
> +       else
> +               pic32_uart_rclr(PIC32_UART_STA_UTXBRK, sport, PIC32_UART_STA);
> +
> +       spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
> +/* get port type in string format */
> +static const char *pic32_uart_type(struct uart_port *port)
> +{
> +       return (port->type == PORT_PIC32) ? PIC32_DEV_NAME : NULL;
> +}
> +
> +/* read all chars in rx fifo and send them to core */
> +static void pic32_uart_do_rx(struct uart_port *port)
> +{
> +       struct pic32_sport *sport = to_pic32_sport(port);
> +       struct tty_port *tty;
> +       unsigned int max_count;
> +
> +       /* limit number of char read in interrupt, should not be
> +        * higher than fifo size anyway since we're much faster than
> +        * serial port
> +        */
> +       max_count = PIC32_UART_RX_FIFO_DEPTH;
> +
> +       spin_lock(&port->lock);
> +
> +       tty = &port->state->port;
> +
> +       do {
> +               u32 sta_reg, c;
> +               char flag;
> +
> +               /* get overrun/fifo empty information from status register */
> +               sta_reg = pic32_uart_read(sport, PIC32_UART_STA);
> +               if (unlikely(sta_reg & PIC32_UART_STA_OERR)) {
> +
> +                       /* fifo reset is required to clear interrupt */
> +                       pic32_uart_rclr(PIC32_UART_STA_OERR, sport,
> +                                                       PIC32_UART_STA);
> +
> +                       port->icount.overrun++;
> +                       tty_insert_flip_char(tty, 0, TTY_OVERRUN);
> +               }
> +
> +               /* Can at least one more character can be read? */
> +               if (!(sta_reg & PIC32_UART_STA_URXDA))
> +                       break;
> +
> +               /* read the character and increment the rx counter */
> +               c = pic32_uart_read(sport, PIC32_UART_RX);
> +
> +               port->icount.rx++;
> +               flag = TTY_NORMAL;
> +               c &= 0xff;
> +
> +               if (unlikely((sta_reg & PIC32_UART_STA_PERR) ||
> +                            (sta_reg & PIC32_UART_STA_FERR))) {
> +
> +                       /* do stats first */
> +                       if (sta_reg & PIC32_UART_STA_PERR)
> +                               port->icount.parity++;
> +                       if (sta_reg & PIC32_UART_STA_FERR)
> +                               port->icount.frame++;
> +
> +                       /* update flag wrt read_status_mask */
> +                       sta_reg &= port->read_status_mask;
> +
> +                       if (sta_reg & PIC32_UART_STA_FERR)
> +                               flag = TTY_FRAME;
> +                       if (sta_reg & PIC32_UART_STA_PERR)
> +                               flag = TTY_PARITY;
> +               }
> +
> +               if (uart_handle_sysrq_char(port, c))
> +                       continue;
> +
> +               if ((sta_reg & port->ignore_status_mask) == 0)
> +                       tty_insert_flip_char(tty, c, flag);
> +
> +       } while (--max_count);
> +
> +       spin_unlock(&port->lock);
> +
> +       tty_flip_buffer_push(tty);
> +}
> +
> +/* fill tx fifo with chars to send, stop when fifo is about to be full
> + * or when all chars have been sent.
> + */
> +static void pic32_uart_do_tx(struct uart_port *port)
> +{
> +       struct pic32_sport *sport = to_pic32_sport(port);
> +       struct circ_buf *xmit = &port->state->xmit;
> +       unsigned int max_count = PIC32_UART_TX_FIFO_DEPTH;
> +
> +       if (port->x_char) {
> +               pic32_uart_write(port->x_char, sport, PIC32_UART_TX);
> +               port->icount.tx++;
> +               port->x_char = 0;
> +               return;
> +       }
> +
> +       if (uart_tx_stopped(port)) {
> +               pic32_uart_stop_tx(port);
> +               return;
> +       }
> +
> +       if (uart_circ_empty(xmit))
> +               goto txq_empty;
> +
> +       /* keep stuffing chars into uart tx buffer
> +        * 1) until uart fifo is full
> +        * or
> +        * 2) until the circ buffer is empty
> +        * (all chars have been sent)
> +        * or
> +        * 3) until the max count is reached
> +        * (prevents lingering here for too long in certain cases)
> +        */
> +       while (!(PIC32_UART_STA_UTXBF &
> +               pic32_uart_rval(sport, PIC32_UART_STA))) {
> +               unsigned int c = xmit->buf[xmit->tail];
> +
> +               pic32_uart_write(c, sport, PIC32_UART_TX);
> +
> +               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> +               port->icount.tx++;
> +               --max_count;

It's not used outside of while, so …

> +               if (uart_circ_empty(xmit))
> +                       break;
> +               if (max_count == 0)

if (--mac_count == 0)

> +                       break;

Or you can move the original 'if' condition inside 'while' one since
it's a last in the loop.

> +       }
> +
> +       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> +               uart_write_wakeup(port);
> +
> +       if (uart_circ_empty(xmit))
> +               goto txq_empty;
> +
> +       return;
> +
> +txq_empty:
> +       pic32_uart_irqtxen(sport, 0);
> +}
> +
> +/* RX interrupt handler */
> +static irqreturn_t pic32_uart_rx_interrupt(int irq, void *dev_id)
> +{
> +       struct uart_port *port = dev_id;
> +
> +       pic32_uart_do_rx(port);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +/* TX interrupt handler */
> +static irqreturn_t pic32_uart_tx_interrupt(int irq, void *dev_id)
> +{
> +       struct uart_port *port = dev_id;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&port->lock, flags);
> +       pic32_uart_do_tx(port);
> +       spin_unlock_irqrestore(&port->lock, flags);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +/* FAULT interrupt handler */
> +static irqreturn_t pic32_uart_fault_interrupt(int irq, void *dev_id)
> +{
> +       /* do nothing: pic32_uart_do_rx() handles faults. */
> +       return IRQ_HANDLED;
> +}
> +
> +/* enable rx & tx operation on uart */
> +static void pic32_uart_en_and_unmask(struct uart_port *port)
> +{
> +       struct pic32_sport *sport = to_pic32_sport(port);
> +
> +       pic32_uart_rset(PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN,
> +                       sport, PIC32_UART_STA);
> +       pic32_uart_rset(PIC32_UART_MODE_ON, sport, PIC32_UART_MODE);
> +}
> +
> +/* disable rx & tx operation on uart */
> +static void pic32_uart_dsbl_and_mask(struct uart_port *port)
> +{
> +       struct pic32_sport *sport = to_pic32_sport(port);
> +
> +       pic32_uart_rclr(PIC32_UART_MODE_ON, sport, PIC32_UART_MODE);
> +       pic32_uart_rclr(PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN,
> +                       sport, PIC32_UART_STA);
> +}
> +
> +/* serial core request to initialize uart and start rx operation */
> +static int pic32_uart_startup(struct uart_port *port)
> +{
> +       struct pic32_sport *sport = to_pic32_sport(port);
> +       u32 dflt_baud = ((port->uartclk / PIC32_UART_DFLT_BRATE) / 16) - 1;

Useless external parens.

> +       unsigned long flags;
> +       int ret = 0;

Useless assignment.

> +
> +       local_irq_save(flags);
> +
> +       ret = pic32_enable_clock(sport);
> +       if (ret)
> +               goto out_unlock;
> +
> +       /* clear status and mode registers */
> +       pic32_uart_write(0, sport, PIC32_UART_MODE);
> +       pic32_uart_write(0, sport, PIC32_UART_STA);
> +
> +       /* disable uart and mask all interrupts */
> +       pic32_uart_dsbl_and_mask(port);
> +
> +       /* set default baud */
> +       pic32_uart_write(dflt_baud, sport, PIC32_UART_BRG);
> +
> +       local_irq_restore(flags);
> +
> +       /* Each UART of a PIC32 has three interrupts therefore,
> +        * we setup driver to register the 3 irqs for the device.
> +        *
> +        * For each irq request_irq() is called with interrupt disabled.
> +        * And the irq is enabled as soon as we are ready to handle them.
> +        */
> +       tx_irq_enabled(sport) = 0;
> +
> +       sport->irq_fault_name = kasprintf(GFP_KERNEL, "%s%d-fault",
> +                                         pic32_uart_type(port),
> +                                         sport->idx);
> +       irq_set_status_flags(sport->irq_fault, IRQ_NOAUTOEN);
> +       ret = request_irq(sport->irq_fault, pic32_uart_fault_interrupt,
> +                         sport->irqflags_fault, sport->irq_fault_name, port);
> +       if (ret) {
> +               dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n",
> +                       __func__, sport->irq_fault, ret,
> +                       pic32_uart_type(port));
> +               goto out_done;
> +       }
> +
> +       sport->irq_rx_name = kasprintf(GFP_KERNEL, "%s%d-rx",
> +                                      pic32_uart_type(port),
> +                                      sport->idx);
> +       irq_set_status_flags(sport->irq_rx, IRQ_NOAUTOEN);
> +       ret = request_irq(sport->irq_rx, pic32_uart_rx_interrupt,
> +                         sport->irqflags_rx, sport->irq_rx_name, port);
> +       if (ret) {
> +               dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n",
> +                       __func__, sport->irq_rx, ret,
> +                       pic32_uart_type(port));
> +               goto out_done;
> +       }
> +
> +       sport->irq_tx_name = kasprintf(GFP_KERNEL, "%s%d-tx",
> +                                      pic32_uart_type(port),
> +                                      sport->idx);
> +       irq_set_status_flags(sport->irq_tx, IRQ_NOAUTOEN);
> +       ret = request_irq(sport->irq_tx, pic32_uart_tx_interrupt,
> +                         sport->irqflags_tx, sport->irq_tx_name, port);
> +       if (ret) {
> +               dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n",
> +                       __func__, sport->irq_tx, ret,
> +                       pic32_uart_type(port));
> +               goto out_done;
> +       }
> +
> +       local_irq_save(flags);
> +
> +       /* set rx interrupt on first receive */
> +       pic32_uart_rclr(PIC32_UART_STA_URXISEL1 | PIC32_UART_STA_URXISEL0,
> +                                                       sport, PIC32_UART_STA);
> +
> +       /* set interrupt on empty */
> +       pic32_uart_rclr(PIC32_UART_STA_UTXISEL1, sport, PIC32_UART_STA);
> +
> +       /* enable all interrupts and eanable uart */
> +       pic32_uart_en_and_unmask(port);
> +
> +       enable_irq(sport->irq_fault);
> +       enable_irq(sport->irq_rx);
> +
> +out_unlock:
> +       local_irq_restore(flags);
> +
> +out_done:
> +       return ret;
> +}
> +
> +/* serial core request to flush & disable uart */
> +static void pic32_uart_shutdown(struct uart_port *port)
> +{
> +       struct pic32_sport *sport = to_pic32_sport(port);
> +       unsigned long flags;
> +
> +       /* disable uart */
> +       spin_lock_irqsave(&port->lock, flags);
> +       pic32_uart_dsbl_and_mask(port);
> +       spin_unlock_irqrestore(&port->lock, flags);
> +       pic32_disable_clock(sport);
> +
> +       /* free all 3 interrupts for this UART */
> +       free_irq(sport->irq_fault, port);
> +       free_irq(sport->irq_tx, port);
> +       free_irq(sport->irq_rx, port);
> +}
> +
> +/* serial core request to change current uart setting */
> +static void pic32_uart_set_termios(struct uart_port *port,
> +                                  struct ktermios *new,
> +                                  struct ktermios *old)
> +{
> +       struct pic32_sport *sport = to_pic32_sport(port);
> +       unsigned int baud;
> +       unsigned int quot;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&port->lock, flags);
> +
> +       /* disable uart and mask all interrupts while changing speed */
> +       pic32_uart_dsbl_and_mask(port);
> +
> +       /* stop bit options */
> +       if (new->c_cflag & CSTOPB)
> +               pic32_uart_rset(PIC32_UART_MODE_STSEL, sport, PIC32_UART_MODE);
> +       else
> +               pic32_uart_rclr(PIC32_UART_MODE_STSEL, sport, PIC32_UART_MODE);
> +
> +       /* parity options */
> +       if (new->c_cflag & PARENB) {
> +               if (new->c_cflag & PARODD) {
> +                       pic32_uart_rset(PIC32_UART_MODE_PDSEL1, sport,
> +                                       PIC32_UART_MODE);
> +                       pic32_uart_rclr(PIC32_UART_MODE_PDSEL0, sport,
> +                                       PIC32_UART_MODE);
> +               } else {
> +                       pic32_uart_rset(PIC32_UART_MODE_PDSEL0, sport,
> +                                       PIC32_UART_MODE);
> +                       pic32_uart_rclr(PIC32_UART_MODE_PDSEL1, sport,
> +                                       PIC32_UART_MODE);
> +               }
> +       } else {
> +               pic32_uart_rclr(PIC32_UART_MODE_PDSEL1 | PIC32_UART_MODE_PDSEL0,
> +                               sport, PIC32_UART_MODE);
> +       }
> +       /* if hw flow ctrl, then the pins must be specified in device tree */
> +       if ((new->c_cflag & CRTSCTS) && sport->hw_flow_ctrl) {
> +               /* enable hardware flow control */
> +               pic32_uart_rset(PIC32_UART_MODE_UEN1, sport, PIC32_UART_MODE);
> +               pic32_uart_rclr(PIC32_UART_MODE_UEN0, sport, PIC32_UART_MODE);
> +               pic32_uart_rclr(PIC32_UART_MODE_RTSMD, sport, PIC32_UART_MODE);
> +       } else {
> +               /* disable hardware flow control */
> +               pic32_uart_rclr(PIC32_UART_MODE_UEN1, sport, PIC32_UART_MODE);
> +               pic32_uart_rclr(PIC32_UART_MODE_UEN0, sport, PIC32_UART_MODE);
> +               pic32_uart_rclr(PIC32_UART_MODE_RTSMD, sport, PIC32_UART_MODE);
> +       }
> +
> +       /* update baud */
> +       baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
> +       quot = uart_get_divisor(port, baud) - 1;
> +       pic32_uart_write(quot, sport, PIC32_UART_BRG);
> +       uart_update_timeout(port, new->c_cflag, baud);
> +
> +       /* enable uart */
> +       pic32_uart_en_and_unmask(port);
> +
> +       spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
> +/* serial core request to claim uart iomem */
> +static int pic32_uart_request_port(struct uart_port *port)
> +{
> +       struct platform_device *pdev = to_platform_device(port->dev);
> +       struct resource *res_mem;
> +       unsigned int res_size;
> +
> +       res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (unlikely(!res_mem))
> +               return -EINVAL;
> +       res_size = resource_size(res_mem);
> +
> +       if (!request_mem_region(port->mapbase, res_size, "pic32_uart_mem")) {
> +               dev_err(port->dev, "Memory region busy\n");

Looks a bit useless, return code will be converted to Resource is busy
in userspace.

> +               return -EBUSY;
> +       }
> +
> +       port->membase = devm_ioremap_nocache(port->dev,
> +                                            port->mapbase, res_size);

> +       if (!port->membase) {
> +               dev_err(port->dev, "Unable to map registers\n");
> +               release_mem_region(port->mapbase, res_size);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +/* serial core request to release uart iomem */
> +static void pic32_uart_release_port(struct uart_port *port)
> +{
> +       struct platform_device *pdev = to_platform_device(port->dev);
> +       struct resource *res_mem;
> +       unsigned int res_size;
> +
> +       res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (unlikely(!res_mem))
> +               return;
> +       res_size = resource_size(res_mem);
> +
> +       release_mem_region(port->mapbase, res_size);
> +       devm_iounmap(port->dev, port->membase);

And why do you need to do this explicitly?

> +}
> +
> +/* serial core request to do any port required auto-configuration */
> +static void pic32_uart_config_port(struct uart_port *port, int flags)
> +{
> +       if (flags & UART_CONFIG_TYPE) {
> +               if (pic32_uart_request_port(port))
> +                       return;
> +               port->type = PORT_PIC32;
> +       }
> +}
> +
> +/* serial core request to check that port information in serinfo are suitable */
> +static int pic32_uart_verify_port(struct uart_port *port,
> +                                 struct serial_struct *serinfo)
> +{
> +       if (port->type != PORT_PIC32)
> +               return -EINVAL;
> +       if (port->irq != serinfo->irq)
> +               return -EINVAL;
> +       if (port->iotype != serinfo->io_type)
> +               return -EINVAL;
> +       if (port->mapbase != (unsigned long)serinfo->iomem_base)
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +/* serial core callbacks */
> +static const struct uart_ops pic32_uart_ops = {
> +       .tx_empty       = pic32_uart_tx_empty,
> +       .get_mctrl      = pic32_uart_get_mctrl,
> +       .set_mctrl      = pic32_uart_set_mctrl,
> +       .start_tx       = pic32_uart_start_tx,
> +       .stop_tx        = pic32_uart_stop_tx,
> +       .stop_rx        = pic32_uart_stop_rx,
> +       .break_ctl      = pic32_uart_break_ctl,
> +       .startup        = pic32_uart_startup,
> +       .shutdown       = pic32_uart_shutdown,
> +       .set_termios    = pic32_uart_set_termios,
> +       .type           = pic32_uart_type,
> +       .release_port   = pic32_uart_release_port,
> +       .request_port   = pic32_uart_request_port,
> +       .config_port    = pic32_uart_config_port,
> +       .verify_port    = pic32_uart_verify_port,
> +};
> +
> +#ifdef CONFIG_SERIAL_PIC32_CONSOLE
> +/* output given char */
> +static void pic32_console_putchar(struct uart_port *port, int ch)
> +{
> +       struct pic32_sport *sport = to_pic32_sport(port);
> +
> +       if (!(pic32_uart_read(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON))
> +               return;
> +
> +       if (!(pic32_uart_read(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN))
> +               return;
> +
> +       /* wait for tx empty */
> +       while (!(pic32_uart_read(sport, PIC32_UART_STA) & PIC32_UART_STA_TRMT))
> +               udelay(1);
> +
> +       pic32_uart_write(ch & 0xff, sport, PIC32_UART_TX);
> +}
> +
> +/* console core request to output given string */
> +static void pic32_console_write(struct console *co, const char *s,
> +                               unsigned int count)
> +{
> +       struct pic32_sport *sport = pic32_sports[co->index];
> +       struct uart_port *port = pic32_get_port(sport);
> +
> +       /* call uart helper to deal with \r\n */
> +       uart_console_write(port, s, count, pic32_console_putchar);
> +}
> +
> +/* console core request to setup given console, find matching uart
> + * port and setup it.
> + */
> +static int pic32_console_setup(struct console *co, char *options)
> +{
> +       struct pic32_sport *sport;
> +       struct uart_port *port = NULL;
> +       int baud = 115200;
> +       int bits = 8;
> +       int parity = 'n';
> +       int flow = 'n';
> +       int ret = 0;
> +
> +       if (unlikely(co->index < 0 || co->index >= PIC32_MAX_UARTS))
> +               return -ENODEV;
> +
> +       sport = pic32_sports[co->index];
> +       if (!sport)
> +               return -ENODEV;
> +       port = pic32_get_port(sport);
> +
> +       ret = pic32_enable_clock(sport);
> +       if (ret)
> +               return ret;
> +
> +       if (options)
> +               uart_parse_options(options, &baud, &parity, &bits, &flow);
> +
> +       return uart_set_options(port, co, baud, parity, bits, flow);
> +}
> +
> +static struct uart_driver pic32_uart_driver;
> +static struct console pic32_console = {
> +       .name           = PIC32_SDEV_NAME,
> +       .write          = pic32_console_write,
> +       .device         = uart_console_device,
> +       .setup          = pic32_console_setup,
> +       .flags          = CON_PRINTBUFFER,
> +       .index          = -1,
> +       .data           = &pic32_uart_driver,
> +};
> +#define PIC32_SCONSOLE (&pic32_console)
> +
> +static int __init pic32_console_init(void)
> +{
> +       register_console(&pic32_console);
> +       return 0;
> +}
> +console_initcall(pic32_console_init);
> +
> +static inline bool is_pic32_console_port(struct uart_port *port)
> +{
> +       return (port->cons && port->cons->index == port->line);

Useless parens.

> +}
> +
> +/*
> + * Late console initialization.
> + */
> +static int __init pic32_late_console_init(void)
> +{
> +       if (!(pic32_console.flags & CON_ENABLED))
> +               register_console(&pic32_console);
> +
> +       return 0;
> +}
> +
> +core_initcall(pic32_late_console_init);
> +
> +#else
> +#define PIC32_SCONSOLE NULL
> +#endif
> +
> +static struct uart_driver pic32_uart_driver = {
> +       .owner                  = THIS_MODULE,
> +       .driver_name            = PIC32_DEV_NAME,
> +       .dev_name               = PIC32_SDEV_NAME,
> +       .major                  = PIC32_SDEV_MAJOR,
> +       .minor                  = PIC32_SDEV_MINOR,
> +       .nr                     = PIC32_MAX_UARTS,
> +       .cons                   = PIC32_SCONSOLE,
> +};
> +
> +static int pic32_uart_probe(struct platform_device *pdev)
> +{
> +       struct device_node *np = pdev->dev.of_node;
> +       struct pic32_sport *sport;
> +       int uart_idx = 0;
> +       struct resource *res_mem;
> +       struct uart_port *port;

> +       int ret = 0;

Unneeded assignment.

> +
> +       uart_idx = of_alias_get_id(np, "serial");
> +       if (uart_idx < 0 || uart_idx >= PIC32_MAX_UARTS)
> +               return -EINVAL;
> +
> +       res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (!res_mem)
> +               return -EINVAL;
> +
> +       sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
> +       if (!sport)
> +               return -ENOMEM;
> +
> +       sport->idx              = uart_idx;
> +       sport->irq_fault        = irq_of_parse_and_map(np, 0);
> +       sport->irqflags_fault   = IRQF_NO_THREAD;
> +       sport->irq_rx           = irq_of_parse_and_map(np, 1);
> +       sport->irqflags_rx      = IRQF_NO_THREAD;
> +       sport->irq_tx           = irq_of_parse_and_map(np, 2);
> +       sport->irqflags_tx      = IRQF_NO_THREAD;
> +       sport->clk              = devm_clk_get(&pdev->dev, NULL);
> +       sport->cts_gpio         = -EINVAL;
> +       sport->dev              = &pdev->dev;
> +
> +       ret = pic32_enable_clock(sport);
> +       if (ret) {
> +               dev_err(&pdev->dev, "clk enable ?\n");
> +               goto err;
> +       }
> +
> +       /* Hardware flow control: gpios
> +        * !Note: Basically, CTS is needed for reading the status.
> +        */
> +       sport->hw_flow_ctrl = false;
> +       sport->cts_gpio = of_get_named_gpio(np, "cts-gpios", 0);
> +       if (gpio_is_valid(sport->cts_gpio)) {
> +               sport->hw_flow_ctrl = true;
> +
> +               ret = devm_gpio_request(sport->dev,
> +                                       sport->cts_gpio, "CTS");
> +               if (ret) {
> +                       dev_err(&pdev->dev,
> +                               "error requesting CTS GPIO\n");
> +                       goto err_disable_clk;
> +               }
> +
> +               ret = gpio_direction_input(sport->cts_gpio);
> +               if (ret) {
> +                       dev_err(&pdev->dev, "error setting CTS GPIO\n");
> +                       goto err_disable_clk;
> +               }
> +       }
> +
> +       pic32_sports[uart_idx] = sport;
> +       port = &sport->port;
> +       memset(port, 0, sizeof(*port));
> +       port->iotype    = UPIO_MEM;
> +       port->mapbase   = res_mem->start;
> +       port->ops       = &pic32_uart_ops;
> +       port->flags     = UPF_BOOT_AUTOCONF;
> +       port->dev       = &pdev->dev;
> +       port->fifosize  = PIC32_UART_TX_FIFO_DEPTH;
> +       port->uartclk   = clk_get_rate(sport->clk);
> +       port->line      = uart_idx;
> +
> +       ret = uart_add_one_port(&pic32_uart_driver, port);
> +       if (ret) {
> +               port->membase = NULL;
> +               dev_err(port->dev, "%s: uart add port error!\n", __func__);
> +               goto err_disable_clk;
> +       }
> +
> +#ifdef CONFIG_SERIAL_PIC32_CONSOLE
> +       if (is_pic32_console_port(port) &&
> +           (pic32_console.flags & CON_ENABLED)) {
> +               /* The peripheral clock has been enabled by console_setup,
> +                * so disable it till the port is used.
> +                */
> +               pic32_disable_clock(sport);

(1)

> +       }
> +#endif
> +
> +       platform_set_drvdata(pdev, port);
> +
> +       dev_info(&pdev->dev, "%s: uart(%d) driver initialized.\n",
> +                __func__, uart_idx);
> +       ret = 0;
> +

(2)

> +err_disable_clk:
> +       /* disable clock till the port is used. */
> +       pic32_disable_clock(sport);

(3) double disable clock call?

> +err:
> +       /* automatic unroll of sport and gpios */
> +       return ret;
> +}
> +
> +static int pic32_uart_remove(struct platform_device *pdev)
> +{
> +       struct uart_port *port = platform_get_drvdata(pdev);
> +       struct pic32_sport *sport = to_pic32_sport(port);
> +
> +       uart_remove_one_port(&pic32_uart_driver, port);
> +       pic32_disable_clock(sport);
> +       platform_set_drvdata(pdev, NULL);
> +       pic32_sports[sport->idx] = NULL;
> +
> +       /* automatic unroll of sport and gpios */
> +       return 0;
> +}
> +
> +static const struct of_device_id pic32_serial_dt_ids[] = {
> +       { .compatible = "microchip,pic32mzda-uart" },
> +       { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, pic32_serial_dt_ids);
> +
> +static struct platform_driver pic32_uart_platform_driver = {
> +       .probe          = pic32_uart_probe,
> +       .remove         = pic32_uart_remove,
> +       .driver         = {
> +               .name   = PIC32_DEV_NAME,

> +               .owner  = THIS_MODULE,

Shouldn't core set this?

> +               .of_match_table = of_match_ptr(pic32_serial_dt_ids),
> +       },
> +};
> +
> +static int __init pic32_uart_init(void)
> +{
> +       int ret;
> +
> +       ret = uart_register_driver(&pic32_uart_driver);
> +       if (ret) {
> +               pr_err("failed to register %s:%d\n",
> +                      pic32_uart_driver.driver_name, ret);
> +               return ret;
> +       }
> +
> +       ret = platform_driver_register(&pic32_uart_platform_driver);
> +       if (ret) {
> +               pr_err("fail to register pic32 uart\n");
> +               uart_unregister_driver(&pic32_uart_driver);
> +       }
> +
> +       return ret;
> +}
> +arch_initcall(pic32_uart_init);
> +
> +static void __exit pic32_uart_exit(void)
> +{
> +#ifdef CONFIG_SERIAL_PIC32_CONSOLE
> +       unregister_console(&pic32_console);
> +#endif
> +       platform_driver_unregister(&pic32_uart_platform_driver);
> +       uart_unregister_driver(&pic32_uart_driver);
> +}
> +module_exit(pic32_uart_exit);
> +
> +MODULE_AUTHOR("Steve Scott <steve.scott@microchip.com>");
> +MODULE_DESCRIPTION("Microchip PIC32 integrated serial port driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/tty/serial/pic32_uart.h b/drivers/tty/serial/pic32_uart.h
> new file mode 100644
> index 0000000..b2f6960
> --- /dev/null
> +++ b/drivers/tty/serial/pic32_uart.h
> @@ -0,0 +1,198 @@
> +/*
> + * PIC32 Integrated Serial Driver.
> + *
> + * Copyright (C) 2015 Microchip Technology, Inc.
> + *
> + * Authors:
> + *   Sorin-Andrei Pistirica <andrei.pistirica@microchip.com>
> + *
> + * Licensed under GPLv2 or later.
> + */
> +#ifndef __DT_PIC32_UART_H__
> +#define __DT_PIC32_UART_H__
> +
> +#define PIC32_UART_DFLT_BRATE          (9600)
> +#define PIC32_UART_TX_FIFO_DEPTH       (8)
> +#define PIC32_UART_RX_FIFO_DEPTH       (8)
> +
> +struct pic32_console_opt {
> +       int baud;
> +       int parity;
> +       int bits;
> +       int flow;
> +};

+ empty line

> +/* struct pic32_sport - pic32 serial port descriptor

/**

> + * @port: uart port descriptor
> + * @idx: port index
> + * @irq_fault: virtual fault interrupt number
> + * @irqflags_fault: flags related to fault irq
> + * @irq_fault_name: irq fault name
> + * @irq_rx: virtual rx interrupt number
> + * @irqflags_rx: flags related to rx irq
> + * @irq_rx_name: irq rx name
> + * @irq_tx: virtual tx interrupt number
> + * @irqflags_tx: : flags related to tx irq
> + * @irq_tx_name: irq tx name
> + * @cts_gpio: clear to send gpio
> + * @dev: device descriptor
> + **/
> +struct pic32_sport {
> +       struct uart_port port;
> +       struct pic32_console_opt opt;
> +       int idx;
> +
> +       int irq_fault;
> +       int irqflags_fault;
> +       const char *irq_fault_name;
> +       int irq_rx;
> +       int irqflags_rx;
> +       const char *irq_rx_name;
> +       int irq_tx;
> +       int irqflags_tx;
> +       const char *irq_tx_name;
> +       u8 enable_tx_irq;
> +
> +       bool hw_flow_ctrl;
> +       int cts_gpio;
> +
> +       int ref_clk;
> +       struct clk *clk;
> +
> +       struct device *dev;
> +};
> +#define to_pic32_sport(c) container_of(c, struct pic32_sport, port)
> +#define pic32_get_port(sport) (&sport->port)
> +#define pic32_get_opt(sport) (&sport->opt)
> +#define tx_irq_enabled(sport) (sport->enable_tx_irq)
> +
> +struct pic32_reg {
> +       u32 val;
> +       u32 clr;
> +       u32 set;
> +       u32 inv;
> +} __packed;
> +#define PIC32_REGS 4
> +#define PIC32_REG_SIZE 4
> +
> +enum pic32_uart_regs {
> +       PIC32_UART_UNKNOWN      = 0,
> +       PIC32_UART_MODE         = 1,
> +       PIC32_UART_STA          = 2,
> +       PIC32_UART_TX           = 3,
> +       PIC32_UART_RX           = 4,
> +       PIC32_UART_BRG          = 5,
> +
> +       /* add above this line */
> +       PIC32_UART_LAST
> +};
> +
> +/* uart register offsets */
> +static u32 pic32_uart_lookup_reg[PIC32_UART_LAST] = {
> +       [PIC32_UART_MODE]       = 0 * PIC32_REGS * PIC32_REG_SIZE,
> +       [PIC32_UART_STA]        = 1 * PIC32_REGS * PIC32_REG_SIZE,
> +       [PIC32_UART_TX]         = 2 * PIC32_REGS * PIC32_REG_SIZE,
> +       [PIC32_UART_RX]         = 3 * PIC32_REGS * PIC32_REG_SIZE,
> +       [PIC32_UART_BRG]        = 4 * PIC32_REGS * PIC32_REG_SIZE,
> +};
> +
> +static inline void __iomem *pic32_uart_get_reg(struct pic32_sport *sport,
> +                                              enum pic32_uart_regs reg)
> +{
> +       struct uart_port *port = pic32_get_port(sport);
> +
> +       return port->membase + pic32_uart_lookup_reg[reg];
> +}
> +
> +static inline u32 pic32_uart_rval(struct pic32_sport *sport,
> +                                 enum pic32_uart_regs reg)
> +{
> +       void __iomem *addr = pic32_uart_get_reg(sport, reg);
> +       struct pic32_reg __iomem *reg_addr = (struct pic32_reg __iomem *)addr;
> +
> +       return readl(&reg_addr->val);
> +}
> +
> +static inline void pic32_uart_rset(u32 val,
> +                                  struct pic32_sport *sport,
> +                                  enum pic32_uart_regs reg)

Better to use more logical way for this, i.e.
(sport, regs, value)

Same for below.

> +{
> +       void __iomem *addr = pic32_uart_get_reg(sport, reg);
> +       struct pic32_reg __iomem *reg_addr = (struct pic32_reg __iomem *)addr;

struct pic32_reg __iomem *reg = pic32_uart_get_reg(sport, regs);
writel(value, &reg->set);

looks much better, does it?

> +
> +       writel(val, &reg_addr->set);
> +}
> +
> +static inline void pic32_uart_rclr(u32 val,
> +                                  struct pic32_sport *sport,
> +                                  enum pic32_uart_regs reg)
> +{
> +       void __iomem *addr = pic32_uart_get_reg(sport, reg);
> +       struct pic32_reg __iomem *reg_addr = (struct pic32_reg __iomem *)addr;
> +
> +       writel(val, &reg_addr->clr);
> +}
> +
> +static inline void pic32_uart_rinv(u32 val,
> +                                  struct pic32_sport *sport,
> +                                  enum pic32_uart_regs reg)
> +{
> +       void __iomem *addr = pic32_uart_get_reg(sport, reg);
> +       struct pic32_reg __iomem *reg_addr = (struct pic32_reg __iomem *)addr;
> +
> +       writel(val, &reg_addr->inv);
> +}
> +
> +static inline void pic32_uart_write(u32 val,
> +                                   struct pic32_sport *sport,
> +                                   enum pic32_uart_regs reg)
> +{
> +       void __iomem *addr = pic32_uart_get_reg(sport, reg);
> +
> +       writel(val, addr);
> +}
> +
> +static inline u32 pic32_uart_read(struct pic32_sport *sport,
> +                                 enum pic32_uart_regs reg)
> +{
> +       void __iomem *addr = pic32_uart_get_reg(sport, reg);
> +
> +       return readl(addr);
> +}
> +
> +/* pic32 uart mode register bits */
> +#define PIC32_UART_MODE_ON        (1 << 15)

BIT() ?

> +#define PIC32_UART_MODE_FRZ       (1 << 14)
> +#define PIC32_UART_MODE_SIDL      (1 << 13)
> +#define PIC32_UART_MODE_IREN      (1 << 12)
> +#define PIC32_UART_MODE_RTSMD     (1 << 11)
> +#define PIC32_UART_MODE_RESV1     (1 << 10)
> +#define PIC32_UART_MODE_UEN1      (1 << 9)
> +#define PIC32_UART_MODE_UEN0      (1 << 8)
> +#define PIC32_UART_MODE_WAKE      (1 << 7)
> +#define PIC32_UART_MODE_LPBK      (1 << 6)
> +#define PIC32_UART_MODE_ABAUD     (1 << 5)
> +#define PIC32_UART_MODE_RXINV     (1 << 4)
> +#define PIC32_UART_MODE_BRGH      (1 << 3)
> +#define PIC32_UART_MODE_PDSEL1    (1 << 2)
> +#define PIC32_UART_MODE_PDSEL0    (1 << 1)
> +#define PIC32_UART_MODE_STSEL     (1 << 0)
> +
> +/* pic32 uart status register bits */
> +#define PIC32_UART_STA_UTXISEL1   (1 << 15)
> +#define PIC32_UART_STA_UTXISEL0   (1 << 14)
> +#define PIC32_UART_STA_UTXINV     (1 << 13)
> +#define PIC32_UART_STA_URXEN      (1 << 12)
> +#define PIC32_UART_STA_UTXBRK     (1 << 11)
> +#define PIC32_UART_STA_UTXEN      (1 << 10)
> +#define PIC32_UART_STA_UTXBF      (1 << 9)
> +#define PIC32_UART_STA_TRMT       (1 << 8)
> +#define PIC32_UART_STA_URXISEL1   (1 << 7)
> +#define PIC32_UART_STA_URXISEL0   (1 << 6)
> +#define PIC32_UART_STA_ADDEN      (1 << 5)
> +#define PIC32_UART_STA_RIDLE      (1 << 4)
> +#define PIC32_UART_STA_PERR       (1 << 3)
> +#define PIC32_UART_STA_FERR       (1 << 2)
> +#define PIC32_UART_STA_OERR       (1 << 1)
> +#define PIC32_UART_STA_URXDA      (1 << 0)
> +
> +#endif /* __DT_PIC32_UART_H__ */
> diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
> index 93ba148..9df0a98 100644
> --- a/include/uapi/linux/serial_core.h
> +++ b/include/uapi/linux/serial_core.h
> @@ -261,4 +261,7 @@
>  /* STM32 USART */
>  #define PORT_STM32     113
>
> +/* Microchip PIC32 UART */
> +#define PORT_PIC32     114
> +
>  #endif /* _UAPILINUX_SERIAL_CORE_H */
> --
> 1.7.9.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/



-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 02/14] irqchip: irq-pic32-evic: Add support for PIC32 interrupt controller
  2015-12-15 17:00   ` Marc Zyngier
@ 2016-01-05 17:50     ` Joshua Henderson
  0 siblings, 0 replies; 33+ messages in thread
From: Joshua Henderson @ 2016-01-05 17:50 UTC (permalink / raw)
  To: Marc Zyngier, linux-kernel
  Cc: linux-mips, ralf, Cristian Birsan, Thomas Gleixner, Jason Cooper

Marc,

On 12/15/2015 10:00 AM, Marc Zyngier wrote:
> On 14/12/15 22:42, Joshua Henderson wrote:
>> From: Cristian Birsan <cristian.birsan@microchip.com>
>>
>> This adds support for the interrupt controller present on PIC32 class
>> devices.
>>
>> The following features are supported:
>>  - DT properties for EVIC and for devices that use interrupt lines
>>  - Persistent and non-persistent interrupt handling
>>  - irqdomain support
>>
>> Signed-off-by: Cristian Birsan <cristian.birsan@microchip.com>
>> Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
>> Cc: Ralf Baechle <ralf@linux-mips.org>
>> ---
>>  drivers/irqchip/Makefile           |    1 +
>>  drivers/irqchip/irq-pic32-evic.c   |  321 ++++++++++++++++++++++++++++++++++++
>>  include/linux/irqchip/pic32-evic.h |   19 +++
>>  3 files changed, 341 insertions(+)
>>  create mode 100644 drivers/irqchip/irq-pic32-evic.c
>>  create mode 100644 include/linux/irqchip/pic32-evic.h
>>
>> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
>> index 177f78f..e3608fc 100644
>> --- a/drivers/irqchip/Makefile
>> +++ b/drivers/irqchip/Makefile
>> @@ -55,3 +55,4 @@ obj-$(CONFIG_RENESAS_H8S_INTC)		+= irq-renesas-h8s.o
>>  obj-$(CONFIG_ARCH_SA1100)		+= irq-sa11x0.o
>>  obj-$(CONFIG_INGENIC_IRQ)		+= irq-ingenic.o
>>  obj-$(CONFIG_IMX_GPCV2)			+= irq-imx-gpcv2.o
>> +obj-$(CONFIG_MACH_PIC32)		+= irq-pic32-evic.o
>> diff --git a/drivers/irqchip/irq-pic32-evic.c b/drivers/irqchip/irq-pic32-evic.c
>> new file mode 100644
>> index 0000000..6a7747c
>> --- /dev/null
>> +++ b/drivers/irqchip/irq-pic32-evic.c

[...]

>> +
>> +static struct irq_chip pic32_irq_chip = {
>> +	.name = "PIC32-EVIC",
>> +	.irq_ack = ack_pic32_irq,
>> +	.irq_mask = mask_pic32_irq,
>> +	.irq_mask_ack = mask_ack_pic32_irq,
>> +	.irq_unmask = unmask_pic32_irq,
>> +	.irq_eoi = ack_pic32_irq,
>> +	.irq_set_type = set_type_pic32_irq,
>> +	.irq_enable = unmask_pic32_irq,
>> +	.irq_disable = mask_pic32_irq,
> 
> I'm not sure I see the point of having all these methods. First, there
> is a lot of duplication: no need to provide enable/disable if all you
> have is mask/unmask - the core code can deal with that.

This is to avoid the lazy disable approach in irq_disable(). The .irq_enable is there for symmetry.  .irq_mask_ack is also redundant excluding performance.  These can be removed if the preference is to end up with:

static struct irq_chip pic32_irq_chip = {
	.name = "PIC32-EVIC",
	.irq_ack = ack_pic32_irq,
	.irq_mask = mask_pic32_irq,
	.irq_unmask = unmask_pic32_irq,
	.irq_eoi = ack_pic32_irq,
	.irq_set_type = set_type_pic32_irq,
};

> 
> Then, your EOI method is not really an EOI. It doesn't terminate the
> handling, or at least that's not what the name suggest. If this is
> really an EOI, then you should be able to simplify the whole thing on
> only use the fasteoi handler, including for edge interrupts.

There are two types of hardware interrupts: persistent and non persistent. For the persistent ones we need to clear the condition that caused the interrupt before clearing the interrupt flag and this one is mapped to the fasteoi handler. For the non persistent ones we can clear the interrupt flag as soon as we enter the interrupt handler, but we need to re-enable the interrupt to avoid missing any event that occur during servicing the interrupt. This one is mapped to the edge handler. This is needed, for example, with the core timer interrupt.

>
> It would be good to have an insight on how this thing actually works
> (I've tried to read the only documentation, but this is vague at best),
> that would help picking the right design for your use case.
> 

We're in agreement here on the lack of documentation.  While this chip is not released to the public yet, we do have a generic document that outlines the interrupt controllers across the PIC32 family that will shed some light on how this thing operates:  http://ww1.microchip.com/downloads/en/DeviceDoc/60001108H.pdf

> Thanks,
> 
> 	M.
> 

Josh

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 10/14] serial: pic32_uart: Add PIC32 UART driver
  2015-12-20 16:13   ` Andy Shevchenko
@ 2016-01-05 20:29     ` Paul.Thacker
  0 siblings, 0 replies; 33+ messages in thread
From: Paul.Thacker @ 2016-01-05 20:29 UTC (permalink / raw)
  To: andy.shevchenko, Joshua.Henderson
  Cc: linux-kernel, linux-mips, ralf, Andrei.Pistirica, gregkh, jslaby,
	linux-serial, linux-api

On 12/20/2015 09:14 AM, Andy Shevchenko wrote:
> On Tue, Dec 15, 2015 at 12:42 AM, Joshua Henderson
> <joshua.henderson@microchip.com> wrote:
>> From: Andrei Pistirica <andrei.pistirica@microchip.com>
>>
>> This adds UART and a serial console driver for Microchip PIC32 class
>> devices.
>>
>> Signed-off-by: Andrei Pistirica <andrei.pistirica@microchip.com>
>> Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
>> Cc: Ralf Baechle <ralf@linux-mips.org>
>> ---
>>   drivers/tty/serial/Kconfig       |   21 +
>>   drivers/tty/serial/Makefile      |    1 +
>>   drivers/tty/serial/pic32_uart.c  |  927 ++++++++++++++++++++++++++++++++++++++
>>   drivers/tty/serial/pic32_uart.h  |  198 ++++++++
>>   include/uapi/linux/serial_core.h |    3 +
>>   5 files changed, 1150 insertions(+)
>>   create mode 100644 drivers/tty/serial/pic32_uart.c
>>   create mode 100644 drivers/tty/serial/pic32_uart.h
>>
>> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
>> index f38beb2..8853b1e 100644
>> --- a/drivers/tty/serial/Kconfig
>> +++ b/drivers/tty/serial/Kconfig
>> @@ -901,6 +901,27 @@ config SERIAL_SGI_L1_CONSOLE
>>                  controller serial port as your console (you want this!),
>>                  say Y.  Otherwise, say N.
>>
>> +config SERIAL_PIC32
>> +       tristate "Microchip PIC32 serial support"
>> +       depends on MACH_PIC32
>> +       select SERIAL_CORE
>> +       help
>> +         If you have a PIC32, this driver supports the serial ports.
>> +
>> +         Say Y or M to use PIC32 serial ports, otherwise say N. Note that
>> +         to use a serial port as a console, this must be included in kernel and
>> +         not as a module.
>> +
>> +config SERIAL_PIC32_CONSOLE
>> +       bool "PIC32 serial console support"
>> +       depends on SERIAL_PIC32
>> +       select SERIAL_CORE_CONSOLE
>> +       help
>> +         If you have a PIC32, this driver supports the putting a console on one
>> +         of the serial ports.
>> +
>> +         Say Y to use the PIC32 console, otherwise say N.
>> +
>>   config SERIAL_MPC52xx
>>          tristate "Freescale MPC52xx/MPC512x family PSC serial support"
>>          depends on PPC_MPC52xx || PPC_MPC512x
>> diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
>> index 5ab4111..bc5e354 100644
>> --- a/drivers/tty/serial/Makefile
>> +++ b/drivers/tty/serial/Makefile
>> @@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)       += digicolor-usart.o
>>   obj-$(CONFIG_SERIAL_MEN_Z135)  += men_z135_uart.o
>>   obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
>>   obj-$(CONFIG_SERIAL_STM32)     += stm32-usart.o
>> +obj-$(CONFIG_SERIAL_PIC32)     += pic32_uart.o
>>
>>   # GPIOLIB helpers for modem control lines
>>   obj-$(CONFIG_SERIAL_MCTRL_GPIO)        += serial_mctrl_gpio.o
>> diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c
>> new file mode 100644
>> index 0000000..5c05c11
>> --- /dev/null
>> +++ b/drivers/tty/serial/pic32_uart.c
>> @@ -0,0 +1,927 @@
>> +/*
>> + * PIC32 Integrated Serial Driver.
>> + *
>> + * Copyright (C) 2015 Microchip Technology, Inc.
>> + *
>> + * Authors:
>> + *   Steve Scott <steve.scott@microchip.com>,
>> + *   Sorin-Andrei Pistirica <andrei.pistirica@microchip.com>
>> + *
>> + * Licensed under GPLv2 or later.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/of_gpio.h>
>> +#include <linux/init.h>
>> +#include <linux/module.h>
>> +#include <linux/slab.h>
>> +#include <linux/console.h>
>> +#include <linux/clk-provider.h>
>
> Didn't notice clock provider here.

Not needed. Will be removed.

>
>> +#include <linux/clk.h>
>> +#include <linux/clkdev.h>
>> +#include <linux/tty.h>
>> +#include <linux/tty_flip.h>
>> +#include <linux/sysrq.h>
>> +#include <linux/serial.h>
>> +#include <linux/serial_core.h>
>> +#include <uapi/linux/serial_core.h>
>> +#include <linux/delay.h>
>
> Revisit this list and leave exactly what is used.

Done. Updated patch will remove linux/clkdev.h, linux/sysrq.h, 
linux/serial.h, uapi/linux/serial_core.h.

>
>> +
>> +#include "pic32_uart.h"
>> +
>> +/* UART name and device definitions */
>> +#define PIC32_DEV_NAME         "pic32-uart"
>> +#define PIC32_MAX_UARTS                6
>> +
>> +#define PIC32_SDEV_NAME                "ttyS"
>> +#define PIC32_SDEV_MAJOR       TTY_MAJOR
>> +#define PIC32_SDEV_MINOR       64
>> +
>> +/* pic32_sport pointer for console use */
>> +static struct pic32_sport *pic32_sports[PIC32_MAX_UARTS];
>> +
>> +static inline int pic32_enable_clock(struct pic32_sport *sport)
>> +{
>> +       sport->ref_clk++;
>> +
>
> Useless empty line (do in one style).

Ack.

>
>> +       return clk_prepare_enable(sport->clk);
>> +}
>> +
>> +static inline void pic32_disable_clock(struct pic32_sport *sport)
>> +{
>> +       sport->ref_clk--;
>> +       clk_disable_unprepare(sport->clk);
>> +}
>> +
>> +/* serial core request to check if uart tx buffer is empty */
>> +static unsigned int pic32_uart_tx_empty(struct uart_port *port)
>> +{
>> +       struct pic32_sport *sport = to_pic32_sport(port);
>> +       u32 val = pic32_uart_read(sport, PIC32_UART_STA);
>> +
>> +       return (val & PIC32_UART_STA_TRMT) ? 1 : 0;
>> +}
>> +
>> +/* serial core request to set UART outputs */
>> +static void pic32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
>> +{
>> +       struct pic32_sport *sport = to_pic32_sport(port);
>> +
>> +       /* set loopback mode */
>> +       if (mctrl & TIOCM_LOOP)
>> +               pic32_uart_rset(PIC32_UART_MODE_LPBK, sport, PIC32_UART_MODE);
>> +       else
>> +               pic32_uart_rclr(PIC32_UART_MODE_LPBK, sport, PIC32_UART_MODE);
>> +}
>> +
>> +/* get the state of CTS input pin for this port */
>> +static unsigned int get_cts_state(struct pic32_sport *sport)
>> +{
>> +       /* default state must be asserted */
>> +       int val = 1;
>
> Redundant.
>
>> +
>> +       /* read and invert UxCTS */
>> +       if (gpio_is_valid(sport->cts_gpio))
>> +               val = !gpio_get_value(sport->cts_gpio);
>
> If (…)
>   return …;
>
> return 1;

Ack.

>
>> +
>> +       return val;
>> +}
>> +
>> +/* serial core request to return the state of misc UART input pins */
>> +static unsigned int pic32_uart_get_mctrl(struct uart_port *port)
>> +{
>> +       struct pic32_sport *sport = to_pic32_sport(port);
>> +       unsigned int mctrl = 0;
>> +
>> +       if (!sport->hw_flow_ctrl) {
>> +               mctrl |= TIOCM_CTS;
>
>> +               goto ret;
>> +       }
>> +
>> +       if (get_cts_state(sport))
>
> else if (get_cts_state(sport))

Ack.

>
>> +               mctrl |= TIOCM_CTS;
>> +
>
>> +ret:
>
> And remove useless label, besides that fact that its name is awfully chosen.

Ack.

>
>> +       /* DSR and CD are not supported in PIC32, so return 1
>> +        * RI is not supported in PIC32, so return 0
>> +        */
>> +       mctrl |= TIOCM_CD;
>> +       mctrl |= TIOCM_DSR;
>> +
>> +       return mctrl;
>> +}
>> +
>> +/* stop tx and start tx are not called in pairs, therefore a flag indicates
>> + * the status of irq to control the irq-depth.
>> + */
>> +static inline void pic32_uart_irqtxen(struct pic32_sport *sport, u8 en)
>> +{
>> +       if (en && !tx_irq_enabled(sport)) {
>> +               enable_irq(sport->irq_tx);
>> +               tx_irq_enabled(sport) = 1;
>> +       } else if (!en && tx_irq_enabled(sport)) {
>> +               /* use disable_irq_nosync() and not disable_irq() to avoid self
>> +                * imposed deadlock by not waiting for irq handler to end,
>> +                * since this callback is called from interrupt context.
>> +                */
>> +               disable_irq_nosync(sport->irq_tx);
>> +               tx_irq_enabled(sport) = 0;
>> +       }
>> +}
>> +
>> +/* serial core request to disable tx ASAP (used for flow control) */
>> +static void pic32_uart_stop_tx(struct uart_port *port)
>> +{
>> +       struct pic32_sport *sport = to_pic32_sport(port);
>> +
>> +       if (!(pic32_uart_read(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON))
>> +               return;
>> +
>> +       if (!(pic32_uart_read(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN))
>> +               return;
>> +
>> +       /* wait for tx empty */
>> +       while (!(pic32_uart_read(sport, PIC32_UART_STA) & PIC32_UART_STA_TRMT))
>> +               udelay(1);
>> +
>> +       pic32_uart_rclr(PIC32_UART_STA_UTXEN, sport, PIC32_UART_STA);
>> +       pic32_uart_irqtxen(sport, 0);
>> +}
>> +
>> +/* serial core request to (re)enable tx */
>> +static void pic32_uart_start_tx(struct uart_port *port)
>> +{
>> +       struct pic32_sport *sport = to_pic32_sport(port);
>> +
>> +       pic32_uart_irqtxen(sport, 1);
>> +       pic32_uart_rset(PIC32_UART_STA_UTXEN, sport, PIC32_UART_STA);
>> +}
>> +
>> +/* serial core request to stop rx, called before port shutdown */
>> +static void pic32_uart_stop_rx(struct uart_port *port)
>> +{
>> +       struct pic32_sport *sport = to_pic32_sport(port);
>> +
>> +       /* disable rx interrupts */
>> +       disable_irq(sport->irq_rx);
>> +
>> +       /* receiver Enable bit OFF */
>> +       pic32_uart_rclr(PIC32_UART_STA_URXEN, sport, PIC32_UART_STA);
>> +}
>> +
>> +/* serial core request to start/stop emitting break char */
>> +static void pic32_uart_break_ctl(struct uart_port *port, int ctl)
>> +{
>> +       struct pic32_sport *sport = to_pic32_sport(port);
>> +       unsigned long flags = 0;
>
> Useless assignment.

Ack.

>
>> +
>> +       spin_lock_irqsave(&port->lock, flags);
>> +
>> +       if (ctl)
>> +               pic32_uart_rset(PIC32_UART_STA_UTXBRK, sport, PIC32_UART_STA);
>> +       else
>> +               pic32_uart_rclr(PIC32_UART_STA_UTXBRK, sport, PIC32_UART_STA);
>> +
>> +       spin_unlock_irqrestore(&port->lock, flags);
>> +}
>> +
>> +/* get port type in string format */
>> +static const char *pic32_uart_type(struct uart_port *port)
>> +{
>> +       return (port->type == PORT_PIC32) ? PIC32_DEV_NAME : NULL;
>> +}
>> +
>> +/* read all chars in rx fifo and send them to core */
>> +static void pic32_uart_do_rx(struct uart_port *port)
>> +{
>> +       struct pic32_sport *sport = to_pic32_sport(port);
>> +       struct tty_port *tty;
>> +       unsigned int max_count;
>> +
>> +       /* limit number of char read in interrupt, should not be
>> +        * higher than fifo size anyway since we're much faster than
>> +        * serial port
>> +        */
>> +       max_count = PIC32_UART_RX_FIFO_DEPTH;
>> +
>> +       spin_lock(&port->lock);
>> +
>> +       tty = &port->state->port;
>> +
>> +       do {
>> +               u32 sta_reg, c;
>> +               char flag;
>> +
>> +               /* get overrun/fifo empty information from status register */
>> +               sta_reg = pic32_uart_read(sport, PIC32_UART_STA);
>> +               if (unlikely(sta_reg & PIC32_UART_STA_OERR)) {
>> +
>> +                       /* fifo reset is required to clear interrupt */
>> +                       pic32_uart_rclr(PIC32_UART_STA_OERR, sport,
>> +                                                       PIC32_UART_STA);
>> +
>> +                       port->icount.overrun++;
>> +                       tty_insert_flip_char(tty, 0, TTY_OVERRUN);
>> +               }
>> +
>> +               /* Can at least one more character can be read? */
>> +               if (!(sta_reg & PIC32_UART_STA_URXDA))
>> +                       break;
>> +
>> +               /* read the character and increment the rx counter */
>> +               c = pic32_uart_read(sport, PIC32_UART_RX);
>> +
>> +               port->icount.rx++;
>> +               flag = TTY_NORMAL;
>> +               c &= 0xff;
>> +
>> +               if (unlikely((sta_reg & PIC32_UART_STA_PERR) ||
>> +                            (sta_reg & PIC32_UART_STA_FERR))) {
>> +
>> +                       /* do stats first */
>> +                       if (sta_reg & PIC32_UART_STA_PERR)
>> +                               port->icount.parity++;
>> +                       if (sta_reg & PIC32_UART_STA_FERR)
>> +                               port->icount.frame++;
>> +
>> +                       /* update flag wrt read_status_mask */
>> +                       sta_reg &= port->read_status_mask;
>> +
>> +                       if (sta_reg & PIC32_UART_STA_FERR)
>> +                               flag = TTY_FRAME;
>> +                       if (sta_reg & PIC32_UART_STA_PERR)
>> +                               flag = TTY_PARITY;
>> +               }
>> +
>> +               if (uart_handle_sysrq_char(port, c))
>> +                       continue;
>> +
>> +               if ((sta_reg & port->ignore_status_mask) == 0)
>> +                       tty_insert_flip_char(tty, c, flag);
>> +
>> +       } while (--max_count);
>> +
>> +       spin_unlock(&port->lock);
>> +
>> +       tty_flip_buffer_push(tty);
>> +}
>> +
>> +/* fill tx fifo with chars to send, stop when fifo is about to be full
>> + * or when all chars have been sent.
>> + */
>> +static void pic32_uart_do_tx(struct uart_port *port)
>> +{
>> +       struct pic32_sport *sport = to_pic32_sport(port);
>> +       struct circ_buf *xmit = &port->state->xmit;
>> +       unsigned int max_count = PIC32_UART_TX_FIFO_DEPTH;
>> +
>> +       if (port->x_char) {
>> +               pic32_uart_write(port->x_char, sport, PIC32_UART_TX);
>> +               port->icount.tx++;
>> +               port->x_char = 0;
>> +               return;
>> +       }
>> +
>> +       if (uart_tx_stopped(port)) {
>> +               pic32_uart_stop_tx(port);
>> +               return;
>> +       }
>> +
>> +       if (uart_circ_empty(xmit))
>> +               goto txq_empty;
>> +
>> +       /* keep stuffing chars into uart tx buffer
>> +        * 1) until uart fifo is full
>> +        * or
>> +        * 2) until the circ buffer is empty
>> +        * (all chars have been sent)
>> +        * or
>> +        * 3) until the max count is reached
>> +        * (prevents lingering here for too long in certain cases)
>> +        */
>> +       while (!(PIC32_UART_STA_UTXBF &
>> +               pic32_uart_rval(sport, PIC32_UART_STA))) {
>> +               unsigned int c = xmit->buf[xmit->tail];
>> +
>> +               pic32_uart_write(c, sport, PIC32_UART_TX);
>> +
>> +               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
>> +               port->icount.tx++;
>> +               --max_count;
>
> It's not used outside of while, so …
>
>> +               if (uart_circ_empty(xmit))
>> +                       break;
>> +               if (max_count == 0)
>
> if (--mac_count == 0)
>
>> +                       break;
>
> Or you can move the original 'if' condition inside 'while' one since
> it's a last in the loop.

Will change to if (--mac_count == 0).

>
>> +       }
>> +
>> +       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
>> +               uart_write_wakeup(port);
>> +
>> +       if (uart_circ_empty(xmit))
>> +               goto txq_empty;
>> +
>> +       return;
>> +
>> +txq_empty:
>> +       pic32_uart_irqtxen(sport, 0);
>> +}
>> +
>> +/* RX interrupt handler */
>> +static irqreturn_t pic32_uart_rx_interrupt(int irq, void *dev_id)
>> +{
>> +       struct uart_port *port = dev_id;
>> +
>> +       pic32_uart_do_rx(port);
>> +
>> +       return IRQ_HANDLED;
>> +}
>> +
>> +/* TX interrupt handler */
>> +static irqreturn_t pic32_uart_tx_interrupt(int irq, void *dev_id)
>> +{
>> +       struct uart_port *port = dev_id;
>> +       unsigned long flags;
>> +
>> +       spin_lock_irqsave(&port->lock, flags);
>> +       pic32_uart_do_tx(port);
>> +       spin_unlock_irqrestore(&port->lock, flags);
>> +
>> +       return IRQ_HANDLED;
>> +}
>> +
>> +/* FAULT interrupt handler */
>> +static irqreturn_t pic32_uart_fault_interrupt(int irq, void *dev_id)
>> +{
>> +       /* do nothing: pic32_uart_do_rx() handles faults. */
>> +       return IRQ_HANDLED;
>> +}
>> +
>> +/* enable rx & tx operation on uart */
>> +static void pic32_uart_en_and_unmask(struct uart_port *port)
>> +{
>> +       struct pic32_sport *sport = to_pic32_sport(port);
>> +
>> +       pic32_uart_rset(PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN,
>> +                       sport, PIC32_UART_STA);
>> +       pic32_uart_rset(PIC32_UART_MODE_ON, sport, PIC32_UART_MODE);
>> +}
>> +
>> +/* disable rx & tx operation on uart */
>> +static void pic32_uart_dsbl_and_mask(struct uart_port *port)
>> +{
>> +       struct pic32_sport *sport = to_pic32_sport(port);
>> +
>> +       pic32_uart_rclr(PIC32_UART_MODE_ON, sport, PIC32_UART_MODE);
>> +       pic32_uart_rclr(PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN,
>> +                       sport, PIC32_UART_STA);
>> +}
>> +
>> +/* serial core request to initialize uart and start rx operation */
>> +static int pic32_uart_startup(struct uart_port *port)
>> +{
>> +       struct pic32_sport *sport = to_pic32_sport(port);
>> +       u32 dflt_baud = ((port->uartclk / PIC32_UART_DFLT_BRATE) / 16) - 1;
>
> Useless external parens.

Ack.

>
>> +       unsigned long flags;
>> +       int ret = 0;
>
> Useless assignment.

Ack.

>
>> +
>> +       local_irq_save(flags);
>> +
>> +       ret = pic32_enable_clock(sport);
>> +       if (ret)
>> +               goto out_unlock;
>> +
>> +       /* clear status and mode registers */
>> +       pic32_uart_write(0, sport, PIC32_UART_MODE);
>> +       pic32_uart_write(0, sport, PIC32_UART_STA);
>> +
>> +       /* disable uart and mask all interrupts */
>> +       pic32_uart_dsbl_and_mask(port);
>> +
>> +       /* set default baud */
>> +       pic32_uart_write(dflt_baud, sport, PIC32_UART_BRG);
>> +
>> +       local_irq_restore(flags);
>> +
>> +       /* Each UART of a PIC32 has three interrupts therefore,
>> +        * we setup driver to register the 3 irqs for the device.
>> +        *
>> +        * For each irq request_irq() is called with interrupt disabled.
>> +        * And the irq is enabled as soon as we are ready to handle them.
>> +        */
>> +       tx_irq_enabled(sport) = 0;
>> +
>> +       sport->irq_fault_name = kasprintf(GFP_KERNEL, "%s%d-fault",
>> +                                         pic32_uart_type(port),
>> +                                         sport->idx);
>> +       irq_set_status_flags(sport->irq_fault, IRQ_NOAUTOEN);
>> +       ret = request_irq(sport->irq_fault, pic32_uart_fault_interrupt,
>> +                         sport->irqflags_fault, sport->irq_fault_name, port);
>> +       if (ret) {
>> +               dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n",
>> +                       __func__, sport->irq_fault, ret,
>> +                       pic32_uart_type(port));
>> +               goto out_done;
>> +       }
>> +
>> +       sport->irq_rx_name = kasprintf(GFP_KERNEL, "%s%d-rx",
>> +                                      pic32_uart_type(port),
>> +                                      sport->idx);
>> +       irq_set_status_flags(sport->irq_rx, IRQ_NOAUTOEN);
>> +       ret = request_irq(sport->irq_rx, pic32_uart_rx_interrupt,
>> +                         sport->irqflags_rx, sport->irq_rx_name, port);
>> +       if (ret) {
>> +               dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n",
>> +                       __func__, sport->irq_rx, ret,
>> +                       pic32_uart_type(port));
>> +               goto out_done;
>> +       }
>> +
>> +       sport->irq_tx_name = kasprintf(GFP_KERNEL, "%s%d-tx",
>> +                                      pic32_uart_type(port),
>> +                                      sport->idx);
>> +       irq_set_status_flags(sport->irq_tx, IRQ_NOAUTOEN);
>> +       ret = request_irq(sport->irq_tx, pic32_uart_tx_interrupt,
>> +                         sport->irqflags_tx, sport->irq_tx_name, port);
>> +       if (ret) {
>> +               dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n",
>> +                       __func__, sport->irq_tx, ret,
>> +                       pic32_uart_type(port));
>> +               goto out_done;
>> +       }
>> +
>> +       local_irq_save(flags);
>> +
>> +       /* set rx interrupt on first receive */
>> +       pic32_uart_rclr(PIC32_UART_STA_URXISEL1 | PIC32_UART_STA_URXISEL0,
>> +                                                       sport, PIC32_UART_STA);
>> +
>> +       /* set interrupt on empty */
>> +       pic32_uart_rclr(PIC32_UART_STA_UTXISEL1, sport, PIC32_UART_STA);
>> +
>> +       /* enable all interrupts and eanable uart */
>> +       pic32_uart_en_and_unmask(port);
>> +
>> +       enable_irq(sport->irq_fault);
>> +       enable_irq(sport->irq_rx);
>> +
>> +out_unlock:
>> +       local_irq_restore(flags);
>> +
>> +out_done:
>> +       return ret;
>> +}
>> +
>> +/* serial core request to flush & disable uart */
>> +static void pic32_uart_shutdown(struct uart_port *port)
>> +{
>> +       struct pic32_sport *sport = to_pic32_sport(port);
>> +       unsigned long flags;
>> +
>> +       /* disable uart */
>> +       spin_lock_irqsave(&port->lock, flags);
>> +       pic32_uart_dsbl_and_mask(port);
>> +       spin_unlock_irqrestore(&port->lock, flags);
>> +       pic32_disable_clock(sport);
>> +
>> +       /* free all 3 interrupts for this UART */
>> +       free_irq(sport->irq_fault, port);
>> +       free_irq(sport->irq_tx, port);
>> +       free_irq(sport->irq_rx, port);
>> +}
>> +
>> +/* serial core request to change current uart setting */
>> +static void pic32_uart_set_termios(struct uart_port *port,
>> +                                  struct ktermios *new,
>> +                                  struct ktermios *old)
>> +{
>> +       struct pic32_sport *sport = to_pic32_sport(port);
>> +       unsigned int baud;
>> +       unsigned int quot;
>> +       unsigned long flags;
>> +
>> +       spin_lock_irqsave(&port->lock, flags);
>> +
>> +       /* disable uart and mask all interrupts while changing speed */
>> +       pic32_uart_dsbl_and_mask(port);
>> +
>> +       /* stop bit options */
>> +       if (new->c_cflag & CSTOPB)
>> +               pic32_uart_rset(PIC32_UART_MODE_STSEL, sport, PIC32_UART_MODE);
>> +       else
>> +               pic32_uart_rclr(PIC32_UART_MODE_STSEL, sport, PIC32_UART_MODE);
>> +
>> +       /* parity options */
>> +       if (new->c_cflag & PARENB) {
>> +               if (new->c_cflag & PARODD) {
>> +                       pic32_uart_rset(PIC32_UART_MODE_PDSEL1, sport,
>> +                                       PIC32_UART_MODE);
>> +                       pic32_uart_rclr(PIC32_UART_MODE_PDSEL0, sport,
>> +                                       PIC32_UART_MODE);
>> +               } else {
>> +                       pic32_uart_rset(PIC32_UART_MODE_PDSEL0, sport,
>> +                                       PIC32_UART_MODE);
>> +                       pic32_uart_rclr(PIC32_UART_MODE_PDSEL1, sport,
>> +                                       PIC32_UART_MODE);
>> +               }
>> +       } else {
>> +               pic32_uart_rclr(PIC32_UART_MODE_PDSEL1 | PIC32_UART_MODE_PDSEL0,
>> +                               sport, PIC32_UART_MODE);
>> +       }
>> +       /* if hw flow ctrl, then the pins must be specified in device tree */
>> +       if ((new->c_cflag & CRTSCTS) && sport->hw_flow_ctrl) {
>> +               /* enable hardware flow control */
>> +               pic32_uart_rset(PIC32_UART_MODE_UEN1, sport, PIC32_UART_MODE);
>> +               pic32_uart_rclr(PIC32_UART_MODE_UEN0, sport, PIC32_UART_MODE);
>> +               pic32_uart_rclr(PIC32_UART_MODE_RTSMD, sport, PIC32_UART_MODE);
>> +       } else {
>> +               /* disable hardware flow control */
>> +               pic32_uart_rclr(PIC32_UART_MODE_UEN1, sport, PIC32_UART_MODE);
>> +               pic32_uart_rclr(PIC32_UART_MODE_UEN0, sport, PIC32_UART_MODE);
>> +               pic32_uart_rclr(PIC32_UART_MODE_RTSMD, sport, PIC32_UART_MODE);
>> +       }
>> +
>> +       /* update baud */
>> +       baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
>> +       quot = uart_get_divisor(port, baud) - 1;
>> +       pic32_uart_write(quot, sport, PIC32_UART_BRG);
>> +       uart_update_timeout(port, new->c_cflag, baud);
>> +
>> +       /* enable uart */
>> +       pic32_uart_en_and_unmask(port);
>> +
>> +       spin_unlock_irqrestore(&port->lock, flags);
>> +}
>> +
>> +/* serial core request to claim uart iomem */
>> +static int pic32_uart_request_port(struct uart_port *port)
>> +{
>> +       struct platform_device *pdev = to_platform_device(port->dev);
>> +       struct resource *res_mem;
>> +       unsigned int res_size;
>> +
>> +       res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       if (unlikely(!res_mem))
>> +               return -EINVAL;
>> +       res_size = resource_size(res_mem);
>> +
>> +       if (!request_mem_region(port->mapbase, res_size, "pic32_uart_mem")) {
>> +               dev_err(port->dev, "Memory region busy\n");
>
> Looks a bit useless, return code will be converted to Resource is busy
> in userspace.

Ok. Will remove.

>
>> +               return -EBUSY;
>> +       }
>> +
>> +       port->membase = devm_ioremap_nocache(port->dev,
>> +                                            port->mapbase, res_size);
>
>> +       if (!port->membase) {
>> +               dev_err(port->dev, "Unable to map registers\n");
>> +               release_mem_region(port->mapbase, res_size);
>> +               return -ENOMEM;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +/* serial core request to release uart iomem */
>> +static void pic32_uart_release_port(struct uart_port *port)
>> +{
>> +       struct platform_device *pdev = to_platform_device(port->dev);
>> +       struct resource *res_mem;
>> +       unsigned int res_size;
>> +
>> +       res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       if (unlikely(!res_mem))
>> +               return;
>> +       res_size = resource_size(res_mem);
>> +
>> +       release_mem_region(port->mapbase, res_size);
>> +       devm_iounmap(port->dev, port->membase);
>
> And why do you need to do this explicitly?

Don't. Will remove.

>
>> +}
>> +
>> +/* serial core request to do any port required auto-configuration */
>> +static void pic32_uart_config_port(struct uart_port *port, int flags)
>> +{
>> +       if (flags & UART_CONFIG_TYPE) {
>> +               if (pic32_uart_request_port(port))
>> +                       return;
>> +               port->type = PORT_PIC32;
>> +       }
>> +}
>> +
>> +/* serial core request to check that port information in serinfo are suitable */
>> +static int pic32_uart_verify_port(struct uart_port *port,
>> +                                 struct serial_struct *serinfo)
>> +{
>> +       if (port->type != PORT_PIC32)
>> +               return -EINVAL;
>> +       if (port->irq != serinfo->irq)
>> +               return -EINVAL;
>> +       if (port->iotype != serinfo->io_type)
>> +               return -EINVAL;
>> +       if (port->mapbase != (unsigned long)serinfo->iomem_base)
>> +               return -EINVAL;
>> +
>> +       return 0;
>> +}
>> +
>> +/* serial core callbacks */
>> +static const struct uart_ops pic32_uart_ops = {
>> +       .tx_empty       = pic32_uart_tx_empty,
>> +       .get_mctrl      = pic32_uart_get_mctrl,
>> +       .set_mctrl      = pic32_uart_set_mctrl,
>> +       .start_tx       = pic32_uart_start_tx,
>> +       .stop_tx        = pic32_uart_stop_tx,
>> +       .stop_rx        = pic32_uart_stop_rx,
>> +       .break_ctl      = pic32_uart_break_ctl,
>> +       .startup        = pic32_uart_startup,
>> +       .shutdown       = pic32_uart_shutdown,
>> +       .set_termios    = pic32_uart_set_termios,
>> +       .type           = pic32_uart_type,
>> +       .release_port   = pic32_uart_release_port,
>> +       .request_port   = pic32_uart_request_port,
>> +       .config_port    = pic32_uart_config_port,
>> +       .verify_port    = pic32_uart_verify_port,
>> +};
>> +
>> +#ifdef CONFIG_SERIAL_PIC32_CONSOLE
>> +/* output given char */
>> +static void pic32_console_putchar(struct uart_port *port, int ch)
>> +{
>> +       struct pic32_sport *sport = to_pic32_sport(port);
>> +
>> +       if (!(pic32_uart_read(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON))
>> +               return;
>> +
>> +       if (!(pic32_uart_read(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN))
>> +               return;
>> +
>> +       /* wait for tx empty */
>> +       while (!(pic32_uart_read(sport, PIC32_UART_STA) & PIC32_UART_STA_TRMT))
>> +               udelay(1);
>> +
>> +       pic32_uart_write(ch & 0xff, sport, PIC32_UART_TX);
>> +}
>> +
>> +/* console core request to output given string */
>> +static void pic32_console_write(struct console *co, const char *s,
>> +                               unsigned int count)
>> +{
>> +       struct pic32_sport *sport = pic32_sports[co->index];
>> +       struct uart_port *port = pic32_get_port(sport);
>> +
>> +       /* call uart helper to deal with \r\n */
>> +       uart_console_write(port, s, count, pic32_console_putchar);
>> +}
>> +
>> +/* console core request to setup given console, find matching uart
>> + * port and setup it.
>> + */
>> +static int pic32_console_setup(struct console *co, char *options)
>> +{
>> +       struct pic32_sport *sport;
>> +       struct uart_port *port = NULL;
>> +       int baud = 115200;
>> +       int bits = 8;
>> +       int parity = 'n';
>> +       int flow = 'n';
>> +       int ret = 0;
>> +
>> +       if (unlikely(co->index < 0 || co->index >= PIC32_MAX_UARTS))
>> +               return -ENODEV;
>> +
>> +       sport = pic32_sports[co->index];
>> +       if (!sport)
>> +               return -ENODEV;
>> +       port = pic32_get_port(sport);
>> +
>> +       ret = pic32_enable_clock(sport);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (options)
>> +               uart_parse_options(options, &baud, &parity, &bits, &flow);
>> +
>> +       return uart_set_options(port, co, baud, parity, bits, flow);
>> +}
>> +
>> +static struct uart_driver pic32_uart_driver;
>> +static struct console pic32_console = {
>> +       .name           = PIC32_SDEV_NAME,
>> +       .write          = pic32_console_write,
>> +       .device         = uart_console_device,
>> +       .setup          = pic32_console_setup,
>> +       .flags          = CON_PRINTBUFFER,
>> +       .index          = -1,
>> +       .data           = &pic32_uart_driver,
>> +};
>> +#define PIC32_SCONSOLE (&pic32_console)
>> +
>> +static int __init pic32_console_init(void)
>> +{
>> +       register_console(&pic32_console);
>> +       return 0;
>> +}
>> +console_initcall(pic32_console_init);
>> +
>> +static inline bool is_pic32_console_port(struct uart_port *port)
>> +{
>> +       return (port->cons && port->cons->index == port->line);
>
> Useless parens.

Ack.

>
>> +}
>> +
>> +/*
>> + * Late console initialization.
>> + */
>> +static int __init pic32_late_console_init(void)
>> +{
>> +       if (!(pic32_console.flags & CON_ENABLED))
>> +               register_console(&pic32_console);
>> +
>> +       return 0;
>> +}
>> +
>> +core_initcall(pic32_late_console_init);
>> +
>> +#else
>> +#define PIC32_SCONSOLE NULL
>> +#endif
>> +
>> +static struct uart_driver pic32_uart_driver = {
>> +       .owner                  = THIS_MODULE,
>> +       .driver_name            = PIC32_DEV_NAME,
>> +       .dev_name               = PIC32_SDEV_NAME,
>> +       .major                  = PIC32_SDEV_MAJOR,
>> +       .minor                  = PIC32_SDEV_MINOR,
>> +       .nr                     = PIC32_MAX_UARTS,
>> +       .cons                   = PIC32_SCONSOLE,
>> +};
>> +
>> +static int pic32_uart_probe(struct platform_device *pdev)
>> +{
>> +       struct device_node *np = pdev->dev.of_node;
>> +       struct pic32_sport *sport;
>> +       int uart_idx = 0;
>> +       struct resource *res_mem;
>> +       struct uart_port *port;
>
>> +       int ret = 0;
>
> Unneeded assignment.

Ack.

>
>> +
>> +       uart_idx = of_alias_get_id(np, "serial");
>> +       if (uart_idx < 0 || uart_idx >= PIC32_MAX_UARTS)
>> +               return -EINVAL;
>> +
>> +       res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       if (!res_mem)
>> +               return -EINVAL;
>> +
>> +       sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
>> +       if (!sport)
>> +               return -ENOMEM;
>> +
>> +       sport->idx              = uart_idx;
>> +       sport->irq_fault        = irq_of_parse_and_map(np, 0);
>> +       sport->irqflags_fault   = IRQF_NO_THREAD;
>> +       sport->irq_rx           = irq_of_parse_and_map(np, 1);
>> +       sport->irqflags_rx      = IRQF_NO_THREAD;
>> +       sport->irq_tx           = irq_of_parse_and_map(np, 2);
>> +       sport->irqflags_tx      = IRQF_NO_THREAD;
>> +       sport->clk              = devm_clk_get(&pdev->dev, NULL);
>> +       sport->cts_gpio         = -EINVAL;
>> +       sport->dev              = &pdev->dev;
>> +
>> +       ret = pic32_enable_clock(sport);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "clk enable ?\n");
>> +               goto err;
>> +       }
>> +
>> +       /* Hardware flow control: gpios
>> +        * !Note: Basically, CTS is needed for reading the status.
>> +        */
>> +       sport->hw_flow_ctrl = false;
>> +       sport->cts_gpio = of_get_named_gpio(np, "cts-gpios", 0);
>> +       if (gpio_is_valid(sport->cts_gpio)) {
>> +               sport->hw_flow_ctrl = true;
>> +
>> +               ret = devm_gpio_request(sport->dev,
>> +                                       sport->cts_gpio, "CTS");
>> +               if (ret) {
>> +                       dev_err(&pdev->dev,
>> +                               "error requesting CTS GPIO\n");
>> +                       goto err_disable_clk;
>> +               }
>> +
>> +               ret = gpio_direction_input(sport->cts_gpio);
>> +               if (ret) {
>> +                       dev_err(&pdev->dev, "error setting CTS GPIO\n");
>> +                       goto err_disable_clk;
>> +               }
>> +       }
>> +
>> +       pic32_sports[uart_idx] = sport;
>> +       port = &sport->port;
>> +       memset(port, 0, sizeof(*port));
>> +       port->iotype    = UPIO_MEM;
>> +       port->mapbase   = res_mem->start;
>> +       port->ops       = &pic32_uart_ops;
>> +       port->flags     = UPF_BOOT_AUTOCONF;
>> +       port->dev       = &pdev->dev;
>> +       port->fifosize  = PIC32_UART_TX_FIFO_DEPTH;
>> +       port->uartclk   = clk_get_rate(sport->clk);
>> +       port->line      = uart_idx;
>> +
>> +       ret = uart_add_one_port(&pic32_uart_driver, port);
>> +       if (ret) {
>> +               port->membase = NULL;
>> +               dev_err(port->dev, "%s: uart add port error!\n", __func__);
>> +               goto err_disable_clk;
>> +       }
>> +
>> +#ifdef CONFIG_SERIAL_PIC32_CONSOLE
>> +       if (is_pic32_console_port(port) &&
>> +           (pic32_console.flags & CON_ENABLED)) {
>> +               /* The peripheral clock has been enabled by console_setup,
>> +                * so disable it till the port is used.
>> +                */
>> +               pic32_disable_clock(sport);
>
> (1)
>
>> +       }
>> +#endif
>> +
>> +       platform_set_drvdata(pdev, port);
>> +
>> +       dev_info(&pdev->dev, "%s: uart(%d) driver initialized.\n",
>> +                __func__, uart_idx);
>> +       ret = 0;
>> +
>
> (2)
>
>> +err_disable_clk:
>> +       /* disable clock till the port is used. */
>> +       pic32_disable_clock(sport);
>
> (3) double disable clock call?

Good catch. This will be fixed in the next patch set.

>
>> +err:
>> +       /* automatic unroll of sport and gpios */
>> +       return ret;
>> +}
>> +
>> +static int pic32_uart_remove(struct platform_device *pdev)
>> +{
>> +       struct uart_port *port = platform_get_drvdata(pdev);
>> +       struct pic32_sport *sport = to_pic32_sport(port);
>> +
>> +       uart_remove_one_port(&pic32_uart_driver, port);
>> +       pic32_disable_clock(sport);
>> +       platform_set_drvdata(pdev, NULL);
>> +       pic32_sports[sport->idx] = NULL;
>> +
>> +       /* automatic unroll of sport and gpios */
>> +       return 0;
>> +}
>> +
>> +static const struct of_device_id pic32_serial_dt_ids[] = {
>> +       { .compatible = "microchip,pic32mzda-uart" },
>> +       { /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, pic32_serial_dt_ids);
>> +
>> +static struct platform_driver pic32_uart_platform_driver = {
>> +       .probe          = pic32_uart_probe,
>> +       .remove         = pic32_uart_remove,
>> +       .driver         = {
>> +               .name   = PIC32_DEV_NAME,
>
>> +               .owner  = THIS_MODULE,
>
> Shouldn't core set this?

Yes. Will remove.

>
>> +               .of_match_table = of_match_ptr(pic32_serial_dt_ids),
>> +       },
>> +};
>> +
>> +static int __init pic32_uart_init(void)
>> +{
>> +       int ret;
>> +
>> +       ret = uart_register_driver(&pic32_uart_driver);
>> +       if (ret) {
>> +               pr_err("failed to register %s:%d\n",
>> +                      pic32_uart_driver.driver_name, ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = platform_driver_register(&pic32_uart_platform_driver);
>> +       if (ret) {
>> +               pr_err("fail to register pic32 uart\n");
>> +               uart_unregister_driver(&pic32_uart_driver);
>> +       }
>> +
>> +       return ret;
>> +}
>> +arch_initcall(pic32_uart_init);
>> +
>> +static void __exit pic32_uart_exit(void)
>> +{
>> +#ifdef CONFIG_SERIAL_PIC32_CONSOLE
>> +       unregister_console(&pic32_console);
>> +#endif
>> +       platform_driver_unregister(&pic32_uart_platform_driver);
>> +       uart_unregister_driver(&pic32_uart_driver);
>> +}
>> +module_exit(pic32_uart_exit);
>> +
>> +MODULE_AUTHOR("Steve Scott <steve.scott@microchip.com>");
>> +MODULE_DESCRIPTION("Microchip PIC32 integrated serial port driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/tty/serial/pic32_uart.h b/drivers/tty/serial/pic32_uart.h
>> new file mode 100644
>> index 0000000..b2f6960
>> --- /dev/null
>> +++ b/drivers/tty/serial/pic32_uart.h
>> @@ -0,0 +1,198 @@
>> +/*
>> + * PIC32 Integrated Serial Driver.
>> + *
>> + * Copyright (C) 2015 Microchip Technology, Inc.
>> + *
>> + * Authors:
>> + *   Sorin-Andrei Pistirica <andrei.pistirica@microchip.com>
>> + *
>> + * Licensed under GPLv2 or later.
>> + */
>> +#ifndef __DT_PIC32_UART_H__
>> +#define __DT_PIC32_UART_H__
>> +
>> +#define PIC32_UART_DFLT_BRATE          (9600)
>> +#define PIC32_UART_TX_FIFO_DEPTH       (8)
>> +#define PIC32_UART_RX_FIFO_DEPTH       (8)
>> +
>> +struct pic32_console_opt {
>> +       int baud;
>> +       int parity;
>> +       int bits;
>> +       int flow;
>> +};
>
> + empty line

Ack.

>
>> +/* struct pic32_sport - pic32 serial port descriptor
>
> /**
>
>> + * @port: uart port descriptor
>> + * @idx: port index
>> + * @irq_fault: virtual fault interrupt number
>> + * @irqflags_fault: flags related to fault irq
>> + * @irq_fault_name: irq fault name
>> + * @irq_rx: virtual rx interrupt number
>> + * @irqflags_rx: flags related to rx irq
>> + * @irq_rx_name: irq rx name
>> + * @irq_tx: virtual tx interrupt number
>> + * @irqflags_tx: : flags related to tx irq
>> + * @irq_tx_name: irq tx name
>> + * @cts_gpio: clear to send gpio
>> + * @dev: device descriptor
>> + **/
>> +struct pic32_sport {
>> +       struct uart_port port;
>> +       struct pic32_console_opt opt;
>> +       int idx;
>> +
>> +       int irq_fault;
>> +       int irqflags_fault;
>> +       const char *irq_fault_name;
>> +       int irq_rx;
>> +       int irqflags_rx;
>> +       const char *irq_rx_name;
>> +       int irq_tx;
>> +       int irqflags_tx;
>> +       const char *irq_tx_name;
>> +       u8 enable_tx_irq;
>> +
>> +       bool hw_flow_ctrl;
>> +       int cts_gpio;
>> +
>> +       int ref_clk;
>> +       struct clk *clk;
>> +
>> +       struct device *dev;
>> +};
>> +#define to_pic32_sport(c) container_of(c, struct pic32_sport, port)
>> +#define pic32_get_port(sport) (&sport->port)
>> +#define pic32_get_opt(sport) (&sport->opt)
>> +#define tx_irq_enabled(sport) (sport->enable_tx_irq)
>> +
>> +struct pic32_reg {
>> +       u32 val;
>> +       u32 clr;
>> +       u32 set;
>> +       u32 inv;
>> +} __packed;
>> +#define PIC32_REGS 4
>> +#define PIC32_REG_SIZE 4
>> +
>> +enum pic32_uart_regs {
>> +       PIC32_UART_UNKNOWN      = 0,
>> +       PIC32_UART_MODE         = 1,
>> +       PIC32_UART_STA          = 2,
>> +       PIC32_UART_TX           = 3,
>> +       PIC32_UART_RX           = 4,
>> +       PIC32_UART_BRG          = 5,
>> +
>> +       /* add above this line */
>> +       PIC32_UART_LAST
>> +};
>> +
>> +/* uart register offsets */
>> +static u32 pic32_uart_lookup_reg[PIC32_UART_LAST] = {
>> +       [PIC32_UART_MODE]       = 0 * PIC32_REGS * PIC32_REG_SIZE,
>> +       [PIC32_UART_STA]        = 1 * PIC32_REGS * PIC32_REG_SIZE,
>> +       [PIC32_UART_TX]         = 2 * PIC32_REGS * PIC32_REG_SIZE,
>> +       [PIC32_UART_RX]         = 3 * PIC32_REGS * PIC32_REG_SIZE,
>> +       [PIC32_UART_BRG]        = 4 * PIC32_REGS * PIC32_REG_SIZE,
>> +};
>> +
>> +static inline void __iomem *pic32_uart_get_reg(struct pic32_sport *sport,
>> +                                              enum pic32_uart_regs reg)
>> +{
>> +       struct uart_port *port = pic32_get_port(sport);
>> +
>> +       return port->membase + pic32_uart_lookup_reg[reg];
>> +}
>> +
>> +static inline u32 pic32_uart_rval(struct pic32_sport *sport,
>> +                                 enum pic32_uart_regs reg)
>> +{
>> +       void __iomem *addr = pic32_uart_get_reg(sport, reg);
>> +       struct pic32_reg __iomem *reg_addr = (struct pic32_reg __iomem *)addr;
>> +
>> +       return readl(&reg_addr->val);
>> +}
>> +
>> +static inline void pic32_uart_rset(u32 val,
>> +                                  struct pic32_sport *sport,
>> +                                  enum pic32_uart_regs reg)
>
> Better to use more logical way for this, i.e.
> (sport, regs, value)
>
> Same for below.

The way the registers are accessed will be reworked and simplified for 
the next patch set. There will be a single read and single write function.

>
>> +{
>> +       void __iomem *addr = pic32_uart_get_reg(sport, reg);
>> +       struct pic32_reg __iomem *reg_addr = (struct pic32_reg __iomem *)addr;
>
> struct pic32_reg __iomem *reg = pic32_uart_get_reg(sport, regs);
> writel(value, &reg->set);
>
> looks much better, does it?
>
>> +
>> +       writel(val, &reg_addr->set);
>> +}
>> +
>> +static inline void pic32_uart_rclr(u32 val,
>> +                                  struct pic32_sport *sport,
>> +                                  enum pic32_uart_regs reg)
>> +{
>> +       void __iomem *addr = pic32_uart_get_reg(sport, reg);
>> +       struct pic32_reg __iomem *reg_addr = (struct pic32_reg __iomem *)addr;
>> +
>> +       writel(val, &reg_addr->clr);
>> +}
>> +
>> +static inline void pic32_uart_rinv(u32 val,
>> +                                  struct pic32_sport *sport,
>> +                                  enum pic32_uart_regs reg)
>> +{
>> +       void __iomem *addr = pic32_uart_get_reg(sport, reg);
>> +       struct pic32_reg __iomem *reg_addr = (struct pic32_reg __iomem *)addr;
>> +
>> +       writel(val, &reg_addr->inv);
>> +}
>> +
>> +static inline void pic32_uart_write(u32 val,
>> +                                   struct pic32_sport *sport,
>> +                                   enum pic32_uart_regs reg)
>> +{
>> +       void __iomem *addr = pic32_uart_get_reg(sport, reg);
>> +
>> +       writel(val, addr);
>> +}
>> +
>> +static inline u32 pic32_uart_read(struct pic32_sport *sport,
>> +                                 enum pic32_uart_regs reg)
>> +{
>> +       void __iomem *addr = pic32_uart_get_reg(sport, reg);
>> +
>> +       return readl(addr);
>> +}
>> +
>> +/* pic32 uart mode register bits */
>> +#define PIC32_UART_MODE_ON        (1 << 15)
>
> BIT() ?

Ack.

>
>> +#define PIC32_UART_MODE_FRZ       (1 << 14)
>> +#define PIC32_UART_MODE_SIDL      (1 << 13)
>> +#define PIC32_UART_MODE_IREN      (1 << 12)
>> +#define PIC32_UART_MODE_RTSMD     (1 << 11)
>> +#define PIC32_UART_MODE_RESV1     (1 << 10)
>> +#define PIC32_UART_MODE_UEN1      (1 << 9)
>> +#define PIC32_UART_MODE_UEN0      (1 << 8)
>> +#define PIC32_UART_MODE_WAKE      (1 << 7)
>> +#define PIC32_UART_MODE_LPBK      (1 << 6)
>> +#define PIC32_UART_MODE_ABAUD     (1 << 5)
>> +#define PIC32_UART_MODE_RXINV     (1 << 4)
>> +#define PIC32_UART_MODE_BRGH      (1 << 3)
>> +#define PIC32_UART_MODE_PDSEL1    (1 << 2)
>> +#define PIC32_UART_MODE_PDSEL0    (1 << 1)
>> +#define PIC32_UART_MODE_STSEL     (1 << 0)
>> +
>> +/* pic32 uart status register bits */
>> +#define PIC32_UART_STA_UTXISEL1   (1 << 15)
>> +#define PIC32_UART_STA_UTXISEL0   (1 << 14)
>> +#define PIC32_UART_STA_UTXINV     (1 << 13)
>> +#define PIC32_UART_STA_URXEN      (1 << 12)
>> +#define PIC32_UART_STA_UTXBRK     (1 << 11)
>> +#define PIC32_UART_STA_UTXEN      (1 << 10)
>> +#define PIC32_UART_STA_UTXBF      (1 << 9)
>> +#define PIC32_UART_STA_TRMT       (1 << 8)
>> +#define PIC32_UART_STA_URXISEL1   (1 << 7)
>> +#define PIC32_UART_STA_URXISEL0   (1 << 6)
>> +#define PIC32_UART_STA_ADDEN      (1 << 5)
>> +#define PIC32_UART_STA_RIDLE      (1 << 4)
>> +#define PIC32_UART_STA_PERR       (1 << 3)
>> +#define PIC32_UART_STA_FERR       (1 << 2)
>> +#define PIC32_UART_STA_OERR       (1 << 1)
>> +#define PIC32_UART_STA_URXDA      (1 << 0)
>> +
>> +#endif /* __DT_PIC32_UART_H__ */
>> diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
>> index 93ba148..9df0a98 100644
>> --- a/include/uapi/linux/serial_core.h
>> +++ b/include/uapi/linux/serial_core.h
>> @@ -261,4 +261,7 @@
>>   /* STM32 USART */
>>   #define PORT_STM32     113
>>
>> +/* Microchip PIC32 UART */
>> +#define PORT_PIC32     114
>> +
>>   #endif /* _UAPILINUX_SERIAL_CORE_H */
>> --
>> 1.7.9.5

Thanks,
Paul


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 10/14] serial: pic32_uart: Add PIC32 UART driver
  2015-12-14 22:42 ` [PATCH v2 10/14] serial: pic32_uart: Add " Joshua Henderson
  2015-12-20 16:13   ` Andy Shevchenko
@ 2016-01-05 20:43   ` One Thousand Gnomes
  2016-01-06 22:00     ` Paul.Thacker
  1 sibling, 1 reply; 33+ messages in thread
From: One Thousand Gnomes @ 2016-01-05 20:43 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, linux-mips, ralf, Andrei Pistirica,
	Greg Kroah-Hartman, Jiri Slaby, linux-serial, linux-api


> +#define PIC32_SDEV_NAME		"ttyS"
> +#define PIC32_SDEV_MAJOR	TTY_MAJOR
> +#define PIC32_SDEV_MINOR	64

No. Same goes for you as every one of the forty other people a year who
try and claim their console is ttyS. If it's not an 8250 it isn't.

ttyS is the 8250, use dynamic major and minor and a different name.


> +/* serial core request to change current uart setting */
> +static void pic32_uart_set_termios(struct uart_port *port,
> +				   struct ktermios *new,
> +				   struct ktermios *old)
> +{

You need to clear any termios features requested but not supported. In
your case that appears to be CMSPAR, as you don't seem to support
mark/space parity.

Similarly if you only support 8N1 or 7E1/7O1 you need to force the CSIZE
bits to match what you ended up setting the UART to do.

> +	/* update baud */
> +	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
> +	quot = uart_get_divisor(port, baud) - 1;
> +	pic32_uart_write(quot, sport, PIC32_UART_BRG);
> +	uart_update_timeout(port, new->c_cflag, baud);

See the 8250 driver for an example: you probably need to write back the
actual rate you got.

> +/* serial core request to release uart iomem */
> +static void pic32_uart_release_port(struct uart_port *port)
> +{
> +	struct platform_device *pdev = to_platform_device(port->dev);
> +	struct resource *res_mem;
> +	unsigned int res_size;

resource_size_t for resources. Or you could just avoid the pointless
variable in the first place 8)

Other oddments - things like kasprintf() returns should be checked


Alan

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 10/14] serial: pic32_uart: Add PIC32 UART driver
  2016-01-05 20:43   ` One Thousand Gnomes
@ 2016-01-06 22:00     ` Paul.Thacker
  2016-01-06 22:32       ` One Thousand Gnomes
  0 siblings, 1 reply; 33+ messages in thread
From: Paul.Thacker @ 2016-01-06 22:00 UTC (permalink / raw)
  To: gnomes, Joshua.Henderson
  Cc: linux-kernel, linux-mips, ralf, Andrei.Pistirica, gregkh, jslaby,
	linux-serial, linux-api

On 01/05/2016 03:50 PM, One Thousand Gnomes wrote:
>
>> +#define PIC32_SDEV_NAME		"ttyS"
>> +#define PIC32_SDEV_MAJOR	TTY_MAJOR
>> +#define PIC32_SDEV_MINOR	64
>
> No. Same goes for you as every one of the forty other people a year who
> try and claim their console is ttyS. If it's not an 8250 it isn't.
>
> ttyS is the 8250, use dynamic major and minor and a different name.

Ok. Is there a naming convention documented anywhere? How about ttyPIC?

>
>
>> +/* serial core request to change current uart setting */
>> +static void pic32_uart_set_termios(struct uart_port *port,
>> +				   struct ktermios *new,
>> +				   struct ktermios *old)
>> +{
>
> You need to clear any termios features requested but not supported. In
> your case that appears to be CMSPAR, as you don't seem to support
> mark/space parity.

Ack.

>
> Similarly if you only support 8N1 or 7E1/7O1 you need to force the CSIZE
> bits to match what you ended up setting the UART to do.

Ack.

>
>> +	/* update baud */
>> +	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
>> +	quot = uart_get_divisor(port, baud) - 1;
>> +	pic32_uart_write(quot, sport, PIC32_UART_BRG);
>> +	uart_update_timeout(port, new->c_cflag, baud);
>
> See the 8250 driver for an example: you probably need to write back the
> actual rate you got.

Ack.

>
>> +/* serial core request to release uart iomem */
>> +static void pic32_uart_release_port(struct uart_port *port)
>> +{
>> +	struct platform_device *pdev = to_platform_device(port->dev);
>> +	struct resource *res_mem;
>> +	unsigned int res_size;
>
> resource_size_t for resources. Or you could just avoid the pointless
> variable in the first place 8)

Pointless variable removed.

>
> Other oddments - things like kasprintf() returns should be checked

Ack.

>
>
> Alan

Thanks,
Paul


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 10/14] serial: pic32_uart: Add PIC32 UART driver
  2016-01-06 22:00     ` Paul.Thacker
@ 2016-01-06 22:32       ` One Thousand Gnomes
  0 siblings, 0 replies; 33+ messages in thread
From: One Thousand Gnomes @ 2016-01-06 22:32 UTC (permalink / raw)
  To: Paul.Thacker
  Cc: Joshua.Henderson, linux-kernel, linux-mips, ralf,
	Andrei.Pistirica, gregkh, jslaby, linux-serial, linux-api

On Wed, 6 Jan 2016 22:00:43 +0000
<Paul.Thacker@microchip.com> wrote:

> On 01/05/2016 03:50 PM, One Thousand Gnomes wrote:
> >
> >> +#define PIC32_SDEV_NAME		"ttyS"
> >> +#define PIC32_SDEV_MAJOR	TTY_MAJOR
> >> +#define PIC32_SDEV_MINOR	64
> >
> > No. Same goes for you as every one of the forty other people a year who
> > try and claim their console is ttyS. If it's not an 8250 it isn't.
> >
> > ttyS is the 8250, use dynamic major and minor and a different name.
> 
> Ok. Is there a naming convention documented anywhere? How about ttyPIC?

We used to document it but the document was always stale. ttyPIC sounds
fine providing nobody else is using it (and I don't think they are but
grep is your friend). We enforce the rule because in the early days lots
of people re-used ttyS for their chip. Then their chip grew an external
bus or turned into a SoC and a 16x50 got added and it all broke.

ttyPIC ought to be fine because even if you get new PIC devices with a
different uart you aren't likely to have both of the PIC cores on the
same device.

Alan


^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2016-01-06 22:33 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-12-14 22:42 [PATCH v2 00/14] Initial Microchip PIC32MZDA Support Joshua Henderson
2015-12-14 22:42 ` [PATCH v2 01/14] DEVICETREE: Add bindings for PIC32 interrupt controller Joshua Henderson
2015-12-14 23:34   ` Rob Herring
2015-12-14 22:42 ` [PATCH v2 02/14] irqchip: irq-pic32-evic: Add support " Joshua Henderson
2015-12-15 17:00   ` Marc Zyngier
2016-01-05 17:50     ` Joshua Henderson
2015-12-14 22:42 ` [PATCH v2 03/14] DEVICETREE: Add PIC32 clock binding documentation Joshua Henderson
2015-12-18 15:44   ` Rob Herring
2015-12-19 11:48     ` Purna Chandra Mandal
2015-12-14 22:42 ` [PATCH v2 04/14] clk: clk-pic32: Add PIC32 clock driver Joshua Henderson
2015-12-14 22:42 ` [PATCH v2 05/14] DEVICETREE: Add bindings for PIC32/MZDA platforms Joshua Henderson
2015-12-18 15:47   ` Rob Herring
2015-12-14 22:42 ` [PATCH v2 06/14] MIPS: Add support for PIC32MZDA platform Joshua Henderson
2015-12-14 22:42 ` [PATCH v2 07/14] DEVICETREE: Add bindings for PIC32 pin control and GPIO Joshua Henderson
2015-12-15 19:58   ` Rob Herring
2015-12-14 22:42 ` [PATCH v2 08/14] pinctrl: pinctrl-pic32: Add PIC32 pin control driver Joshua Henderson
2015-12-14 22:42 ` [PATCH v2 09/14] DEVICETREE: Add bindings for PIC32 UART driver Joshua Henderson
2015-12-15 20:00   ` Rob Herring
2015-12-14 22:42 ` [PATCH v2 10/14] serial: pic32_uart: Add " Joshua Henderson
2015-12-20 16:13   ` Andy Shevchenko
2016-01-05 20:29     ` Paul.Thacker
2016-01-05 20:43   ` One Thousand Gnomes
2016-01-06 22:00     ` Paul.Thacker
2016-01-06 22:32       ` One Thousand Gnomes
2015-12-14 22:42 ` [PATCH v2 11/14] DEVICETREE: Add bindings for PIC32 SDHCI host controller Joshua Henderson
2015-12-15 20:00   ` Rob Herring
2015-12-14 22:42 ` [PATCH v2 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver Joshua Henderson
2015-12-15  0:32   ` Andy Green
2015-12-16 17:35     ` Paul.Thacker
2015-12-16 10:48   ` Ulf Hansson
2015-12-17 18:05     ` Paul.Thacker
2015-12-14 22:42 ` [PATCH v2 13/14] MIPS: dts: Add initial DTS for the PIC32MZDA Starter Kit Joshua Henderson
2015-12-14 22:42 ` [PATCH v2 14/14] MIPS: pic32mzda: Add initial PIC32MZDA Starter Kit defconfig Joshua Henderson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).