All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 00/14] Initial Microchip PIC32MZDA Support
@ 2016-01-14  1:15 ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, ralf, Joshua Henderson, Andrei Pistirica,
	Andrew Bresticker, Andy Green, Ben Hutchings, Chaotian Jing,
	Corneliu Doban, 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

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/gpio 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-v5

Changes since v4 (https://lkml.org/lkml/2016/1/8/964
	          https://lkml.org/lkml/2016/1/8/965):

	+ Add soc node for core timer interrupt to DTS
        + Add external IRQ property to DTS
	+ Clean up irq alloc on failure
        + Fix rework regression with handling failure in probe
	+ Select config PIC32_EVIC for PIC32MZDA
        + Implement get_c0_compare_int() in platform using DTS
	+ Rearchitect (rewrite) to use generic chip
        + Be consistent with naming of functions, use pic32_ prefix
        + Move get_c0_compare_int() to platform where it belongs
        + Drop obsolete header
        + Add comments about the handler flow of the different interrupt types
        + Prevent external interrupts from being requested as level flow type
        + Simplify/optimize register access
        + Configure external interrupts from DT
        + Simplify plat_irq_dispatch() implementation
        + Change irq chip names to evic-*
        + Add EVIC_PIC32 config option and have platform select it
        + Support to configure core timer interrupt from DT
        + Use proper code comment formatting
	+ Add new microchip,external-interrupts property
        + Provide a better description of some of the features
        + Clean up formatting

Changes since v3 (https://lkml.org/lkml/2016/1/7/760):

	+ Remove broken URL and use full manual name for boot protocol
	+ Formatting and comment location
	+ Move functions to remove need for forward declaration

Changes since v2 (https://lkml.org/lkml/2015/12/14/818):

	+ Prefer dt/bindings: prefix for subject
	+ Remove redundant irq_chip functions in interrupt driver
	+ Use 'sdhci_pltfm_*' instead of 'sdhci_*_host' and other cleanup
	+ Use dynamic major/minor and ttyPIC* instead of ttyS*
	+ Follow device-tree node naming convention for clocks
	+ Remove pinctrl pins that are not port pins
	+ Force lowercase in PIC32 clock binding documentation
	+ Replace __clk_debug with pr_debug
	+ Add of_clk_parent_fill usage in PIC32 clock driver
	+ UART: Remove unused header files
	+ UART: Refactor register read/write functions
	+ UART: Reorder arguments to readl/writel functions
	+ UART: Add missing initializations to termios
	+ UART: Fix clk enable/disable mismatch

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 referring to SD host
	  controller
	+ Remove unnecessary PIC32 sdhci_ops min clock function.
	+ Make platform PIC32[_CLR|_SET|_INV] register macros safer.


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

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

Joshua Henderson (6):
  dt/bindings: Add bindings for PIC32/MZDA platforms
  MIPS: Add support for PIC32MZDA platform
  dt/bindings: 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):
  dt/bindings: Add PIC32 clock binding documentation
  clk: clk-pic32: Add PIC32 clock driver

 .../devicetree/bindings/clock/microchip,pic32.txt  |  257 +++
 .../bindings/gpio/microchip,pic32-gpio.txt         |   49 +
 .../interrupt-controller/microchip,pic32-evic.txt  |   67 +
 .../bindings/mips/pic32/microchip,pic32mzda.txt    |   31 +
 .../bindings/mmc/microchip,sdhci-pic32.txt         |   29 +
 .../bindings/pinctrl/microchip,pic32-pinctrl.txt   |   60 +
 .../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        |  236 ++
 arch/mips/boot/dts/pic32/pic32mzda.dtsi            |  281 +++
 arch/mips/boot/dts/pic32/pic32mzda_sk.dts          |  151 ++
 arch/mips/configs/pic32mzda_defconfig              |   89 +
 .../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                            |   51 +
 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                   |   73 +
 drivers/clk/Kconfig                                |    3 +
 drivers/clk/Makefile                               |    1 +
 drivers/clk/clk-pic32.c                            | 1801 +++++++++++++++
 drivers/irqchip/Kconfig                            |    5 +
 drivers/irqchip/Makefile                           |    1 +
 drivers/irqchip/irq-pic32-evic.c                   |  324 +++
 drivers/mmc/host/Kconfig                           |   11 +
 drivers/mmc/host/Makefile                          |    1 +
 drivers/mmc/host/sdhci-pic32.c                     |  257 +++
 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                    |  960 ++++++++
 drivers/tty/serial/pic32_uart.h                    |  126 ++
 include/linux/platform_data/sdhci-pic32.h          |   22 +
 include/uapi/linux/serial_core.h                   |    3 +
 53 files changed, 8797 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/platform_data/sdhci-pic32.h

--
1.7.9.5


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

* [PATCH v5 00/14] Initial Microchip PIC32MZDA Support
@ 2016-01-14  1:15 ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, ralf, Joshua Henderson, Andrei Pistirica,
	Andrew Bresticker, Andy Green, Ben Hutchings, Chaotian Jing,
	Corneliu Doban, 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,
	Rob Herring, 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/gpio 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-v5

Changes since v4 (https://lkml.org/lkml/2016/1/8/964
	          https://lkml.org/lkml/2016/1/8/965):

	+ Add soc node for core timer interrupt to DTS
        + Add external IRQ property to DTS
	+ Clean up irq alloc on failure
        + Fix rework regression with handling failure in probe
	+ Select config PIC32_EVIC for PIC32MZDA
        + Implement get_c0_compare_int() in platform using DTS
	+ Rearchitect (rewrite) to use generic chip
        + Be consistent with naming of functions, use pic32_ prefix
        + Move get_c0_compare_int() to platform where it belongs
        + Drop obsolete header
        + Add comments about the handler flow of the different interrupt types
        + Prevent external interrupts from being requested as level flow type
        + Simplify/optimize register access
        + Configure external interrupts from DT
        + Simplify plat_irq_dispatch() implementation
        + Change irq chip names to evic-*
        + Add EVIC_PIC32 config option and have platform select it
        + Support to configure core timer interrupt from DT
        + Use proper code comment formatting
	+ Add new microchip,external-interrupts property
        + Provide a better description of some of the features
        + Clean up formatting

Changes since v3 (https://lkml.org/lkml/2016/1/7/760):

	+ Remove broken URL and use full manual name for boot protocol
	+ Formatting and comment location
	+ Move functions to remove need for forward declaration

Changes since v2 (https://lkml.org/lkml/2015/12/14/818):

	+ Prefer dt/bindings: prefix for subject
	+ Remove redundant irq_chip functions in interrupt driver
	+ Use 'sdhci_pltfm_*' instead of 'sdhci_*_host' and other cleanup
	+ Use dynamic major/minor and ttyPIC* instead of ttyS*
	+ Follow device-tree node naming convention for clocks
	+ Remove pinctrl pins that are not port pins
	+ Force lowercase in PIC32 clock binding documentation
	+ Replace __clk_debug with pr_debug
	+ Add of_clk_parent_fill usage in PIC32 clock driver
	+ UART: Remove unused header files
	+ UART: Refactor register read/write functions
	+ UART: Reorder arguments to readl/writel functions
	+ UART: Add missing initializations to termios
	+ UART: Fix clk enable/disable mismatch

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 referring to SD host
	  controller
	+ Remove unnecessary PIC32 sdhci_ops min clock function.
	+ Make platform PIC32[_CLR|_SET|_INV] register macros safer.


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

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

Joshua Henderson (6):
  dt/bindings: Add bindings for PIC32/MZDA platforms
  MIPS: Add support for PIC32MZDA platform
  dt/bindings: 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):
  dt/bindings: Add PIC32 clock binding documentation
  clk: clk-pic32: Add PIC32 clock driver

 .../devicetree/bindings/clock/microchip,pic32.txt  |  257 +++
 .../bindings/gpio/microchip,pic32-gpio.txt         |   49 +
 .../interrupt-controller/microchip,pic32-evic.txt  |   67 +
 .../bindings/mips/pic32/microchip,pic32mzda.txt    |   31 +
 .../bindings/mmc/microchip,sdhci-pic32.txt         |   29 +
 .../bindings/pinctrl/microchip,pic32-pinctrl.txt   |   60 +
 .../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        |  236 ++
 arch/mips/boot/dts/pic32/pic32mzda.dtsi            |  281 +++
 arch/mips/boot/dts/pic32/pic32mzda_sk.dts          |  151 ++
 arch/mips/configs/pic32mzda_defconfig              |   89 +
 .../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                            |   51 +
 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                   |   73 +
 drivers/clk/Kconfig                                |    3 +
 drivers/clk/Makefile                               |    1 +
 drivers/clk/clk-pic32.c                            | 1801 +++++++++++++++
 drivers/irqchip/Kconfig                            |    5 +
 drivers/irqchip/Makefile                           |    1 +
 drivers/irqchip/irq-pic32-evic.c                   |  324 +++
 drivers/mmc/host/Kconfig                           |   11 +
 drivers/mmc/host/Makefile                          |    1 +
 drivers/mmc/host/sdhci-pic32.c                     |  257 +++
 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                    |  960 ++++++++
 drivers/tty/serial/pic32_uart.h                    |  126 ++
 include/linux/platform_data/sdhci-pic32.h          |   22 +
 include/uapi/linux/serial_core.h                   |    3 +
 53 files changed, 8797 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/platform_data/sdhci-pic32.h

--
1.7.9.5

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

* [PATCH v5 00/14] Initial Microchip PIC32MZDA Support
@ 2016-01-14  1:15 ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, ralf, Joshua Henderson, Andrei Pistirica,
	Andrew Bresticker, Andy Green, Ben Hutchings, Chaotian Jing,
	Corneliu Doban, 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,
	Rob Herring, 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/gpio 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-v5

Changes since v4 (https://lkml.org/lkml/2016/1/8/964
	          https://lkml.org/lkml/2016/1/8/965):

	+ Add soc node for core timer interrupt to DTS
        + Add external IRQ property to DTS
	+ Clean up irq alloc on failure
        + Fix rework regression with handling failure in probe
	+ Select config PIC32_EVIC for PIC32MZDA
        + Implement get_c0_compare_int() in platform using DTS
	+ Rearchitect (rewrite) to use generic chip
        + Be consistent with naming of functions, use pic32_ prefix
        + Move get_c0_compare_int() to platform where it belongs
        + Drop obsolete header
        + Add comments about the handler flow of the different interrupt types
        + Prevent external interrupts from being requested as level flow type
        + Simplify/optimize register access
        + Configure external interrupts from DT
        + Simplify plat_irq_dispatch() implementation
        + Change irq chip names to evic-*
        + Add EVIC_PIC32 config option and have platform select it
        + Support to configure core timer interrupt from DT
        + Use proper code comment formatting
	+ Add new microchip,external-interrupts property
        + Provide a better description of some of the features
        + Clean up formatting

Changes since v3 (https://lkml.org/lkml/2016/1/7/760):

	+ Remove broken URL and use full manual name for boot protocol
	+ Formatting and comment location
	+ Move functions to remove need for forward declaration

Changes since v2 (https://lkml.org/lkml/2015/12/14/818):

	+ Prefer dt/bindings: prefix for subject
	+ Remove redundant irq_chip functions in interrupt driver
	+ Use 'sdhci_pltfm_*' instead of 'sdhci_*_host' and other cleanup
	+ Use dynamic major/minor and ttyPIC* instead of ttyS*
	+ Follow device-tree node naming convention for clocks
	+ Remove pinctrl pins that are not port pins
	+ Force lowercase in PIC32 clock binding documentation
	+ Replace __clk_debug with pr_debug
	+ Add of_clk_parent_fill usage in PIC32 clock driver
	+ UART: Remove unused header files
	+ UART: Refactor register read/write functions
	+ UART: Reorder arguments to readl/writel functions
	+ UART: Add missing initializations to termios
	+ UART: Fix clk enable/disable mismatch

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 referring to SD host
	  controller
	+ Remove unnecessary PIC32 sdhci_ops min clock function.
	+ Make platform PIC32[_CLR|_SET|_INV] register macros safer.


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

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

Joshua Henderson (6):
  dt/bindings: Add bindings for PIC32/MZDA platforms
  MIPS: Add support for PIC32MZDA platform
  dt/bindings: 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):
  dt/bindings: Add PIC32 clock binding documentation
  clk: clk-pic32: Add PIC32 clock driver

 .../devicetree/bindings/clock/microchip,pic32.txt  |  257 +++
 .../bindings/gpio/microchip,pic32-gpio.txt         |   49 +
 .../interrupt-controller/microchip,pic32-evic.txt  |   67 +
 .../bindings/mips/pic32/microchip,pic32mzda.txt    |   31 +
 .../bindings/mmc/microchip,sdhci-pic32.txt         |   29 +
 .../bindings/pinctrl/microchip,pic32-pinctrl.txt   |   60 +
 .../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        |  236 ++
 arch/mips/boot/dts/pic32/pic32mzda.dtsi            |  281 +++
 arch/mips/boot/dts/pic32/pic32mzda_sk.dts          |  151 ++
 arch/mips/configs/pic32mzda_defconfig              |   89 +
 .../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                            |   51 +
 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                   |   73 +
 drivers/clk/Kconfig                                |    3 +
 drivers/clk/Makefile                               |    1 +
 drivers/clk/clk-pic32.c                            | 1801 +++++++++++++++
 drivers/irqchip/Kconfig                            |    5 +
 drivers/irqchip/Makefile                           |    1 +
 drivers/irqchip/irq-pic32-evic.c                   |  324 +++
 drivers/mmc/host/Kconfig                           |   11 +
 drivers/mmc/host/Makefile                          |    1 +
 drivers/mmc/host/sdhci-pic32.c                     |  257 +++
 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                    |  960 ++++++++
 drivers/tty/serial/pic32_uart.h                    |  126 ++
 include/linux/platform_data/sdhci-pic32.h          |   22 +
 include/uapi/linux/serial_core.h                   |    3 +
 53 files changed, 8797 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/platform_data/sdhci-pic32.h

--
1.7.9.5

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

* [PATCH v5 01/14] dt/bindings: Add bindings for PIC32 interrupt controller
  2016-01-14  1:15 ` Joshua Henderson
@ 2016-01-14  1:15   ` Joshua Henderson
  -1 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
---
Changes since v4:
	- Add new microchip,external-interrupts property
	- Provide a better description of some of the features
	- Clean up formatting
Changes since v3: None
Changes since v2: None
Changes since v1:
	- Remove hardware interrupt priorities from interrupt controller DT
	  bindings.
---
 .../interrupt-controller/microchip,pic32-evic.txt  |   67 ++++++++++++++++++++
 1 file changed, 67 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..c3a1b37
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/microchip,pic32-evic.txt
@@ -0,0 +1,67 @@
+Microchip PIC32 Interrupt Controller
+====================================
+
+The Microchip PIC32 contains an Enhanced Vectored Interrupt Controller (EVIC).
+It handles all internal and external interrupts. This controller exists outside
+of the CPU and is the arbitrator of all interrupts (including interrupts from
+the CPU itself) before they are presented to the CPU.
+
+External interrupts have a software configurable edge polarity. Non external
+interrupts have a type and polarity that is determined by the source of the
+interrupt.
+
+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.
+
+Optional properties
+-------------------
+- microchip,external-irqs: u32 array of external interrupts with software
+  polarity configuration. This array corresponds to the bits in the INTCON
+  SFR.
+
+Example
+-------
+
+evic: interrupt-controller@1f810000 {
+	compatible = "microchip,pic32mzda-evic";
+	interrupt-controller;
+	#interrupt-cells = <2>;
+	reg = <0x1f810000 0x1000>;
+	microchip,external-irqs = <3 8 13 18 23>;
+};
+
+Each device/peripheral must request its interrupt line with the associated type
+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] 66+ messages in thread

* [PATCH v5 01/14] dt/bindings: Add bindings for PIC32 interrupt controller
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
---
Changes since v4:
	- Add new microchip,external-interrupts property
	- Provide a better description of some of the features
	- Clean up formatting
Changes since v3: None
Changes since v2: None
Changes since v1:
	- Remove hardware interrupt priorities from interrupt controller DT
	  bindings.
---
 .../interrupt-controller/microchip,pic32-evic.txt  |   67 ++++++++++++++++++++
 1 file changed, 67 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..c3a1b37
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/microchip,pic32-evic.txt
@@ -0,0 +1,67 @@
+Microchip PIC32 Interrupt Controller
+====================================
+
+The Microchip PIC32 contains an Enhanced Vectored Interrupt Controller (EVIC).
+It handles all internal and external interrupts. This controller exists outside
+of the CPU and is the arbitrator of all interrupts (including interrupts from
+the CPU itself) before they are presented to the CPU.
+
+External interrupts have a software configurable edge polarity. Non external
+interrupts have a type and polarity that is determined by the source of the
+interrupt.
+
+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.
+
+Optional properties
+-------------------
+- microchip,external-irqs: u32 array of external interrupts with software
+  polarity configuration. This array corresponds to the bits in the INTCON
+  SFR.
+
+Example
+-------
+
+evic: interrupt-controller@1f810000 {
+	compatible = "microchip,pic32mzda-evic";
+	interrupt-controller;
+	#interrupt-cells = <2>;
+	reg = <0x1f810000 0x1000>;
+	microchip,external-irqs = <3 8 13 18 23>;
+};
+
+Each device/peripheral must request its interrupt line with the associated type
+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] 66+ messages in thread

* [PATCH v5 02/14] irqchip: irq-pic32-evic: Add support for PIC32 interrupt controller
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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. It handles all internal and external interrupts. This controller
exists outside of the CPU core and is the arbitrator of all interrupts
(including interrupts from the CPU itself) before they are presented to
the CPU.

The following features are supported:
 - DT properties for EVIC and for devices/peripherals that use interrupt lines
 - Persistent and non-persistent interrupt handling
 - irqdomain and generic chip support
 - Configuration of external interrupt edge polarity

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>
---
Changes since v4:
	- Rearchitect (rewrite) to use generic chip
	- Be consistent with naming of functions, use pic32_ prefix
	- Move get_c0_compare_int() to platform where it belongs
	- Drop obsolete header
	- Add comments about the handler flow of the different interrupt types
	- Prevent external interrupts from being requested as level flow type
	- Simplify/optimize register access
	- Configure external interrupts from DT
	- Simplify plat_irq_dispatch() implementation
	- Change irq chip names to evic-*
	- Add EVIC_PIC32 config option and have platform select it
	- Support to configure core timer interrupt from DT
	- Use proper code comment formatting
Changes since v3:
	- Formatting and comment location
	- Move functions to remove need for forward declaration
Changes since v2:
	- Remove redundant irq_chip functions in interrupt driver
Changes since v1:
	- Remove configuring hardware priorities and just set a default
---
 drivers/irqchip/Kconfig          |    5 +
 drivers/irqchip/Makefile         |    1 +
 drivers/irqchip/irq-pic32-evic.c |  324 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 330 insertions(+)
 create mode 100644 drivers/irqchip/irq-pic32-evic.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 4d7294e..d5bafdd 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -117,6 +117,11 @@ config ORION_IRQCHIP
 	select IRQ_DOMAIN
 	select MULTI_IRQ_HANDLER
 
+config PIC32_EVIC
+	bool
+	select GENERIC_IRQ_CHIP
+	select IRQ_DOMAIN
+
 config RENESAS_INTC_IRQPIN
 	bool
 	select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 177f78f..5278893 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_PIC32_EVIC)		+= 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..e7155db
--- /dev/null
+++ b/drivers/irqchip/irq-pic32-evic.c
@@ -0,0 +1,324 @@
+/*
+ * Cristian Birsan <cristian.birsan@microchip.com>
+ * Joshua Henderson <joshua.henderson@microchip.com>
+ * Copyright (C) 2016 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/irq.h>
+
+#include <asm/irq.h>
+#include <asm/traps.h>
+#include <asm/mach-pic32/pic32.h>
+
+#define REG_INTCON	0x0000
+#define REG_INTSTAT	0x0020
+#define REG_IFS_OFFSET	0x0040
+#define REG_IEC_OFFSET	0x00C0
+#define REG_IPC_OFFSET	0x0140
+#define REG_OFF_OFFSET	0x0540
+
+#define MAJPRI_MASK	0x07
+#define SUBPRI_MASK	0x03
+#define PRIORITY_MASK	0x1F
+
+#define PIC32_INT_PRI(pri, subpri)				\
+	((((pri) & MAJPRI_MASK) << 2) | ((subpri) & SUBPRI_MASK))
+
+struct evic_chip_data {
+	u32 irq_types[NR_IRQS];
+	u32 ext_irqs[8];
+};
+
+static struct irq_domain *evic_irq_domain;
+static void __iomem *evic_base;
+
+asmlinkage void __weak plat_irq_dispatch(void)
+{
+	unsigned int irq, hwirq;
+
+	hwirq = readl(evic_base + REG_INTSTAT) & 0xFF;
+	irq = irq_linear_revmap(evic_irq_domain, hwirq);
+	do_IRQ(irq);
+}
+
+static struct evic_chip_data *irqd_to_priv(struct irq_data *data)
+{
+	return (struct evic_chip_data *)data->domain->host_data;
+}
+
+static int pic32_set_ext_polarity(int bit, u32 type)
+{
+	/*
+	 * External interrupts can be either edge rising or edge falling,
+	 * but not both.
+	 */
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+		writel(BIT(bit), evic_base + PIC32_SET(REG_INTCON));
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		writel(BIT(bit), evic_base + PIC32_CLR(REG_INTCON));
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int pic32_set_type_edge(struct irq_data *data,
+			       unsigned int flow_type)
+{
+	struct evic_chip_data *priv = irqd_to_priv(data);
+	int ret;
+	int i;
+
+	if (!(flow_type & IRQ_TYPE_EDGE_BOTH))
+		return -EBADR;
+
+	/* set polarity for external interrupts only */
+	for (i = 0; i < ARRAY_SIZE(priv->ext_irqs); i++) {
+		if (priv->ext_irqs[i] == data->hwirq) {
+			ret = pic32_set_ext_polarity(i + 1, flow_type);
+			if (ret)
+				return ret;
+		}
+	}
+
+	irqd_set_trigger_type(data, flow_type);
+
+	return IRQ_SET_MASK_OK;
+}
+
+static void pic32_bind_evic_interrupt(int irq, int set)
+{
+	writel(set, evic_base + REG_OFF_OFFSET + irq * 4);
+}
+
+static void pic32_set_irq_priority(int irq, int priority)
+{
+	u32 reg, shift;
+
+	reg = irq / 4;
+	shift = (irq % 4) * 8;
+
+	writel(PRIORITY_MASK << shift,
+		evic_base + PIC32_CLR(REG_IPC_OFFSET + reg * 0x10));
+	writel(priority << shift,
+		evic_base + PIC32_SET(REG_IPC_OFFSET + reg * 0x10));
+}
+
+#define IRQ_REG_MASK(_hwirq, _reg, _mask)		       \
+	do {						       \
+		_reg = _hwirq / 32;			       \
+		_mask = 1 << (_hwirq % 32);		       \
+	} while (0)
+
+static int pic32_irq_domain_map(struct irq_domain *d, unsigned int virq,
+				irq_hw_number_t hw)
+{
+	struct evic_chip_data *priv = d->host_data;
+	struct irq_data *data;
+	int ret;
+	u32 iecclr, ifsclr;
+	u32 reg, mask;
+
+	ret = irq_map_generic_chip(d, virq, hw);
+	if (ret)
+		return ret;
+
+	/*
+	 * Piggyback on xlate function to move to an alternate chip as necessary
+	 * at time of mapping instead of allowing the flow handler/chip to be
+	 * changed later. This requires all interrupts to be configured through
+	 * DT.
+	 */
+	if (priv->irq_types[hw] & IRQ_TYPE_SENSE_MASK) {
+		data = irq_domain_get_irq_data(d, virq);
+		irqd_set_trigger_type(data, priv->irq_types[hw]);
+		irq_setup_alt_chip(data, priv->irq_types[hw]);
+	}
+
+	IRQ_REG_MASK(hw, reg, mask);
+
+	iecclr = PIC32_CLR(REG_IEC_OFFSET + reg * 0x10);
+	ifsclr = PIC32_CLR(REG_IFS_OFFSET + reg * 0x10);
+
+	/* mask and clear flag */
+	writel(mask, evic_base + iecclr);
+	writel(mask, evic_base + ifsclr);
+
+	/* default priority is required */
+	pic32_set_irq_priority(hw, PIC32_INT_PRI(2, 0));
+
+	return ret;
+}
+
+int pic32_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)
+{
+	struct evic_chip_data *priv = d->host_data;
+
+	if (WARN_ON(intsize < 2))
+		return -EINVAL;
+
+	if (WARN_ON(intspec[0] >= NR_IRQS))
+		return -EINVAL;
+
+	*out_hwirq = intspec[0];
+	*out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
+
+	priv->irq_types[intspec[0]] = intspec[1] & IRQ_TYPE_SENSE_MASK;
+
+	return 0;
+}
+
+static const struct irq_domain_ops pic32_irq_domain_ops = {
+	.map	= pic32_irq_domain_map,
+	.xlate	= pic32_irq_domain_xlate,
+};
+
+static void __init pic32_ext_irq_of_init(struct irq_domain *domain)
+{
+	struct device_node *node = irq_domain_get_of_node(domain);
+	struct evic_chip_data *priv = domain->host_data;
+	struct property *prop;
+	const __le32 *p;
+	u32 hwirq;
+	int i = 0;
+	const char *pname = "microchip,external-irqs";
+
+	of_property_for_each_u32(node, pname, prop, p, hwirq) {
+		if (i >= ARRAY_SIZE(priv->ext_irqs)) {
+			pr_warn("More than %d external irq, skip rest\n",
+				ARRAY_SIZE(priv->ext_irqs));
+			break;
+		}
+
+		priv->ext_irqs[i] = hwirq;
+		i++;
+	}
+}
+
+static int __init pic32_of_init(struct device_node *node,
+				struct device_node *parent)
+{
+	struct irq_chip_generic *gc;
+	struct evic_chip_data *priv;
+	unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+	int nchips, ret;
+	int i;
+
+	nchips = DIV_ROUND_UP(NR_IRQS, 32);
+
+	evic_base = of_iomap(node, 0);
+	if (!evic_base)
+		return -ENOMEM;
+
+	priv = kcalloc(nchips, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		ret = -ENOMEM;
+		goto err_iounmap;
+	}
+
+	evic_irq_domain = irq_domain_add_linear(node, nchips * 32,
+						&pic32_irq_domain_ops,
+						priv);
+	if (!evic_irq_domain) {
+		ret = -ENOMEM;
+		goto err_free_priv;
+	}
+
+	/*
+	 * The PIC32 EVIC has a linear list of irqs and the type of each
+	 * irq is determined by the hardware peripheral the EVIC is arbitrating.
+	 * These irq types are defined in the datasheet as "persistent" and
+	 * "non-persistent" which are mapped here to level and edge
+	 * respectively. To manage the different flow handler requirements of
+	 * each irq type, different chip_types are used.
+	 */
+	ret = irq_alloc_domain_generic_chips(evic_irq_domain, 32, 2,
+					     "evic-level", handle_level_irq,
+					     clr, 0, 0);
+	if (ret)
+		goto err_domain_remove;
+
+	board_bind_eic_interrupt = &pic32_bind_evic_interrupt;
+
+	for (i = 0; i < nchips; i++) {
+		u32 ifsclr = PIC32_CLR(REG_IFS_OFFSET + (i * 0x10));
+		u32 iec = REG_IEC_OFFSET + (i * 0x10);
+
+		gc = irq_get_domain_generic_chip(evic_irq_domain, i * 32);
+
+		gc->reg_base = evic_base;
+		gc->unused = 0;
+
+		/*
+		 * Level/persistent interrupts have a special requirement that
+		 * the condition generating the interrupt be cleared before the
+		 * interrupt flag (ifs) can be cleared. chip.irq_eoi is used to
+		 * complete the interrupt with an ack.
+		 */
+		gc->chip_types[0].type			= IRQ_TYPE_LEVEL_MASK;
+		gc->chip_types[0].handler		= handle_fasteoi_irq;
+		gc->chip_types[0].regs.ack		= ifsclr;
+		gc->chip_types[0].regs.mask		= iec;
+		gc->chip_types[0].chip.name		= "evic-level";
+		gc->chip_types[0].chip.irq_eoi		= irq_gc_ack_set_bit;
+		gc->chip_types[0].chip.irq_mask		= irq_gc_mask_clr_bit;
+		gc->chip_types[0].chip.irq_unmask	= irq_gc_mask_set_bit;
+		gc->chip_types[0].chip.flags		= IRQCHIP_SKIP_SET_WAKE;
+
+		/* Edge interrupts */
+		gc->chip_types[1].type			= IRQ_TYPE_EDGE_BOTH;
+		gc->chip_types[1].handler		= handle_edge_irq;
+		gc->chip_types[1].regs.ack		= ifsclr;
+		gc->chip_types[1].regs.mask		= iec;
+		gc->chip_types[1].chip.name		= "evic-edge";
+		gc->chip_types[1].chip.irq_ack		= irq_gc_ack_set_bit;
+		gc->chip_types[1].chip.irq_mask		= irq_gc_mask_clr_bit;
+		gc->chip_types[1].chip.irq_unmask	= irq_gc_mask_set_bit;
+		gc->chip_types[1].chip.irq_set_type	= pic32_set_type_edge;
+		gc->chip_types[1].chip.flags		= IRQCHIP_SKIP_SET_WAKE;
+
+		gc->private = &priv[i];
+	}
+
+	irq_set_default_host(evic_irq_domain);
+
+	/*
+	 * External interrupts have software configurable edge polarity. These
+	 * interrupts are defined in DT allowing polarity to be configured only
+	 * for these interrupts when requested.
+	 */
+	pic32_ext_irq_of_init(evic_irq_domain);
+
+	return 0;
+
+err_domain_remove:
+	irq_domain_remove(evic_irq_domain);
+
+err_free_priv:
+	kfree(priv);
+
+err_iounmap:
+	iounmap(evic_base);
+
+	return ret;
+}
+
+IRQCHIP_DECLARE(pic32_evic, "microchip,pic32mzda-evic", pic32_of_init);
-- 
1.7.9.5

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

* [PATCH v5 02/14] irqchip: irq-pic32-evic: Add support for PIC32 interrupt controller
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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. It handles all internal and external interrupts. This controller
exists outside of the CPU core and is the arbitrator of all interrupts
(including interrupts from the CPU itself) before they are presented to
the CPU.

The following features are supported:
 - DT properties for EVIC and for devices/peripherals that use interrupt lines
 - Persistent and non-persistent interrupt handling
 - irqdomain and generic chip support
 - Configuration of external interrupt edge polarity

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>
---
Changes since v4:
	- Rearchitect (rewrite) to use generic chip
	- Be consistent with naming of functions, use pic32_ prefix
	- Move get_c0_compare_int() to platform where it belongs
	- Drop obsolete header
	- Add comments about the handler flow of the different interrupt types
	- Prevent external interrupts from being requested as level flow type
	- Simplify/optimize register access
	- Configure external interrupts from DT
	- Simplify plat_irq_dispatch() implementation
	- Change irq chip names to evic-*
	- Add EVIC_PIC32 config option and have platform select it
	- Support to configure core timer interrupt from DT
	- Use proper code comment formatting
Changes since v3:
	- Formatting and comment location
	- Move functions to remove need for forward declaration
Changes since v2:
	- Remove redundant irq_chip functions in interrupt driver
Changes since v1:
	- Remove configuring hardware priorities and just set a default
---
 drivers/irqchip/Kconfig          |    5 +
 drivers/irqchip/Makefile         |    1 +
 drivers/irqchip/irq-pic32-evic.c |  324 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 330 insertions(+)
 create mode 100644 drivers/irqchip/irq-pic32-evic.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 4d7294e..d5bafdd 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -117,6 +117,11 @@ config ORION_IRQCHIP
 	select IRQ_DOMAIN
 	select MULTI_IRQ_HANDLER
 
+config PIC32_EVIC
+	bool
+	select GENERIC_IRQ_CHIP
+	select IRQ_DOMAIN
+
 config RENESAS_INTC_IRQPIN
 	bool
 	select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 177f78f..5278893 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_PIC32_EVIC)		+= 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..e7155db
--- /dev/null
+++ b/drivers/irqchip/irq-pic32-evic.c
@@ -0,0 +1,324 @@
+/*
+ * Cristian Birsan <cristian.birsan@microchip.com>
+ * Joshua Henderson <joshua.henderson@microchip.com>
+ * Copyright (C) 2016 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/irq.h>
+
+#include <asm/irq.h>
+#include <asm/traps.h>
+#include <asm/mach-pic32/pic32.h>
+
+#define REG_INTCON	0x0000
+#define REG_INTSTAT	0x0020
+#define REG_IFS_OFFSET	0x0040
+#define REG_IEC_OFFSET	0x00C0
+#define REG_IPC_OFFSET	0x0140
+#define REG_OFF_OFFSET	0x0540
+
+#define MAJPRI_MASK	0x07
+#define SUBPRI_MASK	0x03
+#define PRIORITY_MASK	0x1F
+
+#define PIC32_INT_PRI(pri, subpri)				\
+	((((pri) & MAJPRI_MASK) << 2) | ((subpri) & SUBPRI_MASK))
+
+struct evic_chip_data {
+	u32 irq_types[NR_IRQS];
+	u32 ext_irqs[8];
+};
+
+static struct irq_domain *evic_irq_domain;
+static void __iomem *evic_base;
+
+asmlinkage void __weak plat_irq_dispatch(void)
+{
+	unsigned int irq, hwirq;
+
+	hwirq = readl(evic_base + REG_INTSTAT) & 0xFF;
+	irq = irq_linear_revmap(evic_irq_domain, hwirq);
+	do_IRQ(irq);
+}
+
+static struct evic_chip_data *irqd_to_priv(struct irq_data *data)
+{
+	return (struct evic_chip_data *)data->domain->host_data;
+}
+
+static int pic32_set_ext_polarity(int bit, u32 type)
+{
+	/*
+	 * External interrupts can be either edge rising or edge falling,
+	 * but not both.
+	 */
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+		writel(BIT(bit), evic_base + PIC32_SET(REG_INTCON));
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		writel(BIT(bit), evic_base + PIC32_CLR(REG_INTCON));
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int pic32_set_type_edge(struct irq_data *data,
+			       unsigned int flow_type)
+{
+	struct evic_chip_data *priv = irqd_to_priv(data);
+	int ret;
+	int i;
+
+	if (!(flow_type & IRQ_TYPE_EDGE_BOTH))
+		return -EBADR;
+
+	/* set polarity for external interrupts only */
+	for (i = 0; i < ARRAY_SIZE(priv->ext_irqs); i++) {
+		if (priv->ext_irqs[i] == data->hwirq) {
+			ret = pic32_set_ext_polarity(i + 1, flow_type);
+			if (ret)
+				return ret;
+		}
+	}
+
+	irqd_set_trigger_type(data, flow_type);
+
+	return IRQ_SET_MASK_OK;
+}
+
+static void pic32_bind_evic_interrupt(int irq, int set)
+{
+	writel(set, evic_base + REG_OFF_OFFSET + irq * 4);
+}
+
+static void pic32_set_irq_priority(int irq, int priority)
+{
+	u32 reg, shift;
+
+	reg = irq / 4;
+	shift = (irq % 4) * 8;
+
+	writel(PRIORITY_MASK << shift,
+		evic_base + PIC32_CLR(REG_IPC_OFFSET + reg * 0x10));
+	writel(priority << shift,
+		evic_base + PIC32_SET(REG_IPC_OFFSET + reg * 0x10));
+}
+
+#define IRQ_REG_MASK(_hwirq, _reg, _mask)		       \
+	do {						       \
+		_reg = _hwirq / 32;			       \
+		_mask = 1 << (_hwirq % 32);		       \
+	} while (0)
+
+static int pic32_irq_domain_map(struct irq_domain *d, unsigned int virq,
+				irq_hw_number_t hw)
+{
+	struct evic_chip_data *priv = d->host_data;
+	struct irq_data *data;
+	int ret;
+	u32 iecclr, ifsclr;
+	u32 reg, mask;
+
+	ret = irq_map_generic_chip(d, virq, hw);
+	if (ret)
+		return ret;
+
+	/*
+	 * Piggyback on xlate function to move to an alternate chip as necessary
+	 * at time of mapping instead of allowing the flow handler/chip to be
+	 * changed later. This requires all interrupts to be configured through
+	 * DT.
+	 */
+	if (priv->irq_types[hw] & IRQ_TYPE_SENSE_MASK) {
+		data = irq_domain_get_irq_data(d, virq);
+		irqd_set_trigger_type(data, priv->irq_types[hw]);
+		irq_setup_alt_chip(data, priv->irq_types[hw]);
+	}
+
+	IRQ_REG_MASK(hw, reg, mask);
+
+	iecclr = PIC32_CLR(REG_IEC_OFFSET + reg * 0x10);
+	ifsclr = PIC32_CLR(REG_IFS_OFFSET + reg * 0x10);
+
+	/* mask and clear flag */
+	writel(mask, evic_base + iecclr);
+	writel(mask, evic_base + ifsclr);
+
+	/* default priority is required */
+	pic32_set_irq_priority(hw, PIC32_INT_PRI(2, 0));
+
+	return ret;
+}
+
+int pic32_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)
+{
+	struct evic_chip_data *priv = d->host_data;
+
+	if (WARN_ON(intsize < 2))
+		return -EINVAL;
+
+	if (WARN_ON(intspec[0] >= NR_IRQS))
+		return -EINVAL;
+
+	*out_hwirq = intspec[0];
+	*out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
+
+	priv->irq_types[intspec[0]] = intspec[1] & IRQ_TYPE_SENSE_MASK;
+
+	return 0;
+}
+
+static const struct irq_domain_ops pic32_irq_domain_ops = {
+	.map	= pic32_irq_domain_map,
+	.xlate	= pic32_irq_domain_xlate,
+};
+
+static void __init pic32_ext_irq_of_init(struct irq_domain *domain)
+{
+	struct device_node *node = irq_domain_get_of_node(domain);
+	struct evic_chip_data *priv = domain->host_data;
+	struct property *prop;
+	const __le32 *p;
+	u32 hwirq;
+	int i = 0;
+	const char *pname = "microchip,external-irqs";
+
+	of_property_for_each_u32(node, pname, prop, p, hwirq) {
+		if (i >= ARRAY_SIZE(priv->ext_irqs)) {
+			pr_warn("More than %d external irq, skip rest\n",
+				ARRAY_SIZE(priv->ext_irqs));
+			break;
+		}
+
+		priv->ext_irqs[i] = hwirq;
+		i++;
+	}
+}
+
+static int __init pic32_of_init(struct device_node *node,
+				struct device_node *parent)
+{
+	struct irq_chip_generic *gc;
+	struct evic_chip_data *priv;
+	unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+	int nchips, ret;
+	int i;
+
+	nchips = DIV_ROUND_UP(NR_IRQS, 32);
+
+	evic_base = of_iomap(node, 0);
+	if (!evic_base)
+		return -ENOMEM;
+
+	priv = kcalloc(nchips, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		ret = -ENOMEM;
+		goto err_iounmap;
+	}
+
+	evic_irq_domain = irq_domain_add_linear(node, nchips * 32,
+						&pic32_irq_domain_ops,
+						priv);
+	if (!evic_irq_domain) {
+		ret = -ENOMEM;
+		goto err_free_priv;
+	}
+
+	/*
+	 * The PIC32 EVIC has a linear list of irqs and the type of each
+	 * irq is determined by the hardware peripheral the EVIC is arbitrating.
+	 * These irq types are defined in the datasheet as "persistent" and
+	 * "non-persistent" which are mapped here to level and edge
+	 * respectively. To manage the different flow handler requirements of
+	 * each irq type, different chip_types are used.
+	 */
+	ret = irq_alloc_domain_generic_chips(evic_irq_domain, 32, 2,
+					     "evic-level", handle_level_irq,
+					     clr, 0, 0);
+	if (ret)
+		goto err_domain_remove;
+
+	board_bind_eic_interrupt = &pic32_bind_evic_interrupt;
+
+	for (i = 0; i < nchips; i++) {
+		u32 ifsclr = PIC32_CLR(REG_IFS_OFFSET + (i * 0x10));
+		u32 iec = REG_IEC_OFFSET + (i * 0x10);
+
+		gc = irq_get_domain_generic_chip(evic_irq_domain, i * 32);
+
+		gc->reg_base = evic_base;
+		gc->unused = 0;
+
+		/*
+		 * Level/persistent interrupts have a special requirement that
+		 * the condition generating the interrupt be cleared before the
+		 * interrupt flag (ifs) can be cleared. chip.irq_eoi is used to
+		 * complete the interrupt with an ack.
+		 */
+		gc->chip_types[0].type			= IRQ_TYPE_LEVEL_MASK;
+		gc->chip_types[0].handler		= handle_fasteoi_irq;
+		gc->chip_types[0].regs.ack		= ifsclr;
+		gc->chip_types[0].regs.mask		= iec;
+		gc->chip_types[0].chip.name		= "evic-level";
+		gc->chip_types[0].chip.irq_eoi		= irq_gc_ack_set_bit;
+		gc->chip_types[0].chip.irq_mask		= irq_gc_mask_clr_bit;
+		gc->chip_types[0].chip.irq_unmask	= irq_gc_mask_set_bit;
+		gc->chip_types[0].chip.flags		= IRQCHIP_SKIP_SET_WAKE;
+
+		/* Edge interrupts */
+		gc->chip_types[1].type			= IRQ_TYPE_EDGE_BOTH;
+		gc->chip_types[1].handler		= handle_edge_irq;
+		gc->chip_types[1].regs.ack		= ifsclr;
+		gc->chip_types[1].regs.mask		= iec;
+		gc->chip_types[1].chip.name		= "evic-edge";
+		gc->chip_types[1].chip.irq_ack		= irq_gc_ack_set_bit;
+		gc->chip_types[1].chip.irq_mask		= irq_gc_mask_clr_bit;
+		gc->chip_types[1].chip.irq_unmask	= irq_gc_mask_set_bit;
+		gc->chip_types[1].chip.irq_set_type	= pic32_set_type_edge;
+		gc->chip_types[1].chip.flags		= IRQCHIP_SKIP_SET_WAKE;
+
+		gc->private = &priv[i];
+	}
+
+	irq_set_default_host(evic_irq_domain);
+
+	/*
+	 * External interrupts have software configurable edge polarity. These
+	 * interrupts are defined in DT allowing polarity to be configured only
+	 * for these interrupts when requested.
+	 */
+	pic32_ext_irq_of_init(evic_irq_domain);
+
+	return 0;
+
+err_domain_remove:
+	irq_domain_remove(evic_irq_domain);
+
+err_free_priv:
+	kfree(priv);
+
+err_iounmap:
+	iounmap(evic_base);
+
+	return ret;
+}
+
+IRQCHIP_DECLARE(pic32_evic, "microchip,pic32mzda-evic", pic32_of_init);
-- 
1.7.9.5

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

* [PATCH v5 03/14] dt/bindings: Add PIC32 clock binding documentation
  2016-01-14  1:15 ` Joshua Henderson
@ 2016-01-14  1:15   ` Joshua Henderson
  -1 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes since v4: None
Changes since v3: None
Changes since v2:
	- Force lowercase in PIC32 clock binding documentation
Changes since v1: None
---
 .../devicetree/bindings/clock/microchip,pic32.txt  |  257 ++++++++++++++++++++
 1 file changed, 257 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..06540e4
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/microchip,pic32.txt
@@ -0,0 +1,257 @@
+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@0 {
+		#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@20 {
+		#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@1c0 {
+		#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@140 {
+		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@150 {
+		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@160 {
+		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@170 {
+		reg = <0x170 0x10>;
+		#clock-cells = <0>;
+		compatible = "microchip,pic32mzda-pbclk";
+		clocks = <&SYSCLK>;
+		clock-output-names = "pb4_clk";
+	};
+
+	/* Peripheral bus clock */
+	PBCLK5:pb5_clk@180 {
+		reg = <0x180 0x10>;
+		#clock-cells = <0>;
+		compatible = "microchip,pic32mzda-pbclk";
+		clocks = <&SYSCLK>;
+		clock-output-names = "pb5_clk";
+	};
+
+	/* Peripheral Bus6 clock; */
+	PBCLK6:pb6_clk@190 {
+		reg = <0x190 0x10>;
+		compatible = "microchip,pic32mzda-pbclk";
+		clocks = <&SYSCLK>;
+		#clock-cells = <0>;
+	};
+
+	/* Peripheral bus7 clock */
+	PBCLK7:pb7_clk@1a0 {
+		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@80 {
+		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@a0 {
+		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@c0 {
+		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@e0 {
+		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@100 {
+		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] 66+ messages in thread

* [PATCH v5 03/14] dt/bindings: Add PIC32 clock binding documentation
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes since v4: None
Changes since v3: None
Changes since v2:
	- Force lowercase in PIC32 clock binding documentation
Changes since v1: None
---
 .../devicetree/bindings/clock/microchip,pic32.txt  |  257 ++++++++++++++++++++
 1 file changed, 257 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..06540e4
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/microchip,pic32.txt
@@ -0,0 +1,257 @@
+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@0 {
+		#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@20 {
+		#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@1c0 {
+		#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@140 {
+		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@150 {
+		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@160 {
+		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@170 {
+		reg = <0x170 0x10>;
+		#clock-cells = <0>;
+		compatible = "microchip,pic32mzda-pbclk";
+		clocks = <&SYSCLK>;
+		clock-output-names = "pb4_clk";
+	};
+
+	/* Peripheral bus clock */
+	PBCLK5:pb5_clk@180 {
+		reg = <0x180 0x10>;
+		#clock-cells = <0>;
+		compatible = "microchip,pic32mzda-pbclk";
+		clocks = <&SYSCLK>;
+		clock-output-names = "pb5_clk";
+	};
+
+	/* Peripheral Bus6 clock; */
+	PBCLK6:pb6_clk@190 {
+		reg = <0x190 0x10>;
+		compatible = "microchip,pic32mzda-pbclk";
+		clocks = <&SYSCLK>;
+		#clock-cells = <0>;
+	};
+
+	/* Peripheral bus7 clock */
+	PBCLK7:pb7_clk@1a0 {
+		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@80 {
+		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@a0 {
+		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@c0 {
+		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@e0 {
+		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@100 {
+		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] 66+ messages in thread

* [PATCH v5 04/14] clk: clk-pic32: Add PIC32 clock driver
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
Cc: Michael Turquette <mturquette@baylibre.com>
Cc: Stephen Boyd <sboyd@codeaurora.org>
---
Changes from v4: None
Changes from v3: None
Changes from v2:
	- Replace __clk_debug with pr_debug
	- Add of_clk_parent_fill usage in PIC32 clock driver
Changes from v1:
	- Remove unused PIC32 MPLL support.
	- Remove support for initializing default parent/rate for REFOSC
	  clocks.
---
 drivers/clk/Kconfig     |    3 +
 drivers/clk/Makefile    |    1 +
 drivers/clk/clk-pic32.c | 1801 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1805 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..9dc5f78
--- /dev/null
+++ b/drivers/clk/clk-pic32.c
@@ -0,0 +1,1801 @@
+/*
+ * 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, 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;
+
+	of_clk_parent_fill(np, parents, count);
+
+	/* 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, 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;
+
+	of_clk_parent_fill(np, parents, count);
+
+	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 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;
+
+	of_clk_parent_fill(np, parent_names, count);
+
+	/* 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] 66+ messages in thread

* [PATCH v5 04/14] clk: clk-pic32: Add PIC32 clock driver
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
Cc: Michael Turquette <mturquette@baylibre.com>
Cc: Stephen Boyd <sboyd@codeaurora.org>
---
Changes from v4: None
Changes from v3: None
Changes from v2:
	- Replace __clk_debug with pr_debug
	- Add of_clk_parent_fill usage in PIC32 clock driver
Changes from v1:
	- Remove unused PIC32 MPLL support.
	- Remove support for initializing default parent/rate for REFOSC
	  clocks.
---
 drivers/clk/Kconfig     |    3 +
 drivers/clk/Makefile    |    1 +
 drivers/clk/clk-pic32.c | 1801 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1805 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..9dc5f78
--- /dev/null
+++ b/drivers/clk/clk-pic32.c
@@ -0,0 +1,1801 @@
+/*
+ * 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, 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;
+
+	of_clk_parent_fill(np, parents, count);
+
+	/* 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, 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;
+
+	of_clk_parent_fill(np, parents, count);
+
+	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 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;
+
+	of_clk_parent_fill(np, parent_names, count);
+
+	/* 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] 66+ messages in thread

* [PATCH v5 05/14] dt/bindings: Add bindings for PIC32/MZDA platforms
  2016-01-14  1:15 ` Joshua Henderson
@ 2016-01-14  1:15   ` Joshua Henderson
  -1 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes since v4: None
Changes since v3:
	- Remove broken URL and use full manual name for boot protocol
Changes since v2: None
Changes since v1: None
---
 .../bindings/mips/pic32/microchip,pic32mzda.txt    |   31 ++++++++++++++++++++
 1 file changed, 31 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..1c8dbc4
--- /dev/null
+++ b/Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt
@@ -0,0 +1,31 @@
+* 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 Unified Hosting Interface Reference Manual (MD01069), the
+bootloader must pass the following arguments to the kernel:
+ - $a0: -2.
+ - $a1: KSEG0 address of the flattened device-tree blob.
-- 
1.7.9.5

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

* [PATCH v5 05/14] dt/bindings: Add bindings for PIC32/MZDA platforms
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes since v4: None
Changes since v3:
	- Remove broken URL and use full manual name for boot protocol
Changes since v2: None
Changes since v1: None
---
 .../bindings/mips/pic32/microchip,pic32mzda.txt    |   31 ++++++++++++++++++++
 1 file changed, 31 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..1c8dbc4
--- /dev/null
+++ b/Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt
@@ -0,0 +1,31 @@
+* 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 Unified Hosting Interface Reference Manual (MD01069), the
+bootloader must pass the following arguments to the kernel:
+ - $a0: -2.
+ - $a1: KSEG0 address of the flattened device-tree blob.
-- 
1.7.9.5

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

* [PATCH v5 06/14] MIPS: Add support for PIC32MZDA platform
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-mips, ralf, Joshua Henderson

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>
---
Changes since v4:
	- Select config PIC32_EVIC for PIC32MZDA
	- Implement get_c0_compare_int() in platform using DTS
Changes since v3: None
Changes since v2: None
Changes since v1:
	- Remove redundant probing 'pb7_clk' to find CPU clock.
	- Make platform PIC32[_CLR|_SET|_INV] register macros safer
	- Remove unused functions
	- Rename sdhc to sdhci
	- Address missing static on local functions and other sparse
	  warnings in several drivers.
---
 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                            |   35 +++
 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                   |   73 ++++++
 include/linux/platform_data/sdhci-pic32.h          |   22 ++
 22 files changed, 1476 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..9be43c1
--- /dev/null
+++ b/arch/mips/pic32/Kconfig
@@ -0,0 +1,35 @@
+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
+	select PIC32_EVIC
+	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..ca6a62b
--- /dev/null
+++ b/arch/mips/pic32/pic32mzda/time.c
@@ -0,0 +1,73 @@
+/*
+ * 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/of.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+
+#include <asm/time.h>
+
+#include "pic32mzda.h"
+
+static const struct of_device_id pic32_infra_match[] = {
+	{ .compatible = "microchip,pic32mzda-infra", },
+	{ },
+};
+
+#define DEFAULT_CORE_TIMER_INTERRUPT 0
+
+static unsigned int pic32_xlate_core_timer_irq(void)
+{
+	static struct device_node *node;
+	unsigned int irq;
+
+	node = of_find_matching_node(NULL, pic32_infra_match);
+
+	if (WARN_ON(!node))
+		goto default_map;
+
+	irq = irq_of_parse_and_map(node, 0);
+	if (!irq)
+		goto default_map;
+
+	return irq;
+
+default_map:
+
+	return irq_create_mapping(NULL, DEFAULT_CORE_TIMER_INTERRUPT);
+}
+
+unsigned int get_c0_compare_int(void)
+{
+	return pic32_xlate_core_timer_irq();
+}
+
+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] 66+ messages in thread

* [PATCH v5 06/14] MIPS: Add support for PIC32MZDA platform
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-mips, ralf, Joshua Henderson

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>
---
Changes since v4:
	- Select config PIC32_EVIC for PIC32MZDA
	- Implement get_c0_compare_int() in platform using DTS
Changes since v3: None
Changes since v2: None
Changes since v1:
	- Remove redundant probing 'pb7_clk' to find CPU clock.
	- Make platform PIC32[_CLR|_SET|_INV] register macros safer
	- Remove unused functions
	- Rename sdhc to sdhci
	- Address missing static on local functions and other sparse
	  warnings in several drivers.
---
 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                            |   35 +++
 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                   |   73 ++++++
 include/linux/platform_data/sdhci-pic32.h          |   22 ++
 22 files changed, 1476 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..9be43c1
--- /dev/null
+++ b/arch/mips/pic32/Kconfig
@@ -0,0 +1,35 @@
+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
+	select PIC32_EVIC
+	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..ca6a62b
--- /dev/null
+++ b/arch/mips/pic32/pic32mzda/time.c
@@ -0,0 +1,73 @@
+/*
+ * 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/of.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+
+#include <asm/time.h>
+
+#include "pic32mzda.h"
+
+static const struct of_device_id pic32_infra_match[] = {
+	{ .compatible = "microchip,pic32mzda-infra", },
+	{ },
+};
+
+#define DEFAULT_CORE_TIMER_INTERRUPT 0
+
+static unsigned int pic32_xlate_core_timer_irq(void)
+{
+	static struct device_node *node;
+	unsigned int irq;
+
+	node = of_find_matching_node(NULL, pic32_infra_match);
+
+	if (WARN_ON(!node))
+		goto default_map;
+
+	irq = irq_of_parse_and_map(node, 0);
+	if (!irq)
+		goto default_map;
+
+	return irq;
+
+default_map:
+
+	return irq_create_mapping(NULL, DEFAULT_CORE_TIMER_INTERRUPT);
+}
+
+unsigned int get_c0_compare_int(void)
+{
+	return pic32_xlate_core_timer_irq();
+}
+
+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] 66+ messages in thread

* [PATCH v5 07/14] dt/bindings: Add bindings for PIC32 pin control and GPIO
  2016-01-14  1:15 ` Joshua Henderson
@ 2016-01-14  1:15   ` Joshua Henderson
  -1 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes since v4: None
Changes since v3: None
Changes since v2: None
Changes since v1:
	- Complete bindings API change to use standard bindings
---
 .../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] 66+ messages in thread

* [PATCH v5 07/14] dt/bindings: Add bindings for PIC32 pin control and GPIO
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes since v4: None
Changes since v3: None
Changes since v2: None
Changes since v1:
	- Complete bindings API change to use standard bindings
---
 .../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] 66+ messages in thread

* [PATCH v5 08/14] pinctrl: pinctrl-pic32: Add PIC32 pin control driver
  2016-01-14  1:15 ` Joshua Henderson
@ 2016-01-14  1:15   ` Joshua Henderson
  -1 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
---
Changes since v4: None
Changes since v3: None
Changes since v2:
	- Remove pinctrl pins that are not port pins
Changes since v1:
	- Complete rewrite to simplify and use standard bindings.
---
 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..e814ae7 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -238,6 +238,23 @@ config PINCTRL_PALMAS
 	  open drain configuration for the Palmas series devices like
 	  TPS65913, TPS80036 etc.
 
+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
+
 config PINCTRL_ZYNQ
 	bool "Pinctrl driver for Xilinx Zynq"
 	depends on ARCH_ZYNQ
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] 66+ messages in thread

* [PATCH v5 08/14] pinctrl: pinctrl-pic32: Add PIC32 pin control driver
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
---
Changes since v4: None
Changes since v3: None
Changes since v2:
	- Remove pinctrl pins that are not port pins
Changes since v1:
	- Complete rewrite to simplify and use standard bindings.
---
 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..e814ae7 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -238,6 +238,23 @@ config PINCTRL_PALMAS
 	  open drain configuration for the Palmas series devices like
 	  TPS65913, TPS80036 etc.
 
+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
+
 config PINCTRL_ZYNQ
 	bool "Pinctrl driver for Xilinx Zynq"
 	depends on ARCH_ZYNQ
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] 66+ messages in thread

* [PATCH v5 09/14] dt/bindings: Add bindings for PIC32 UART driver
  2016-01-14  1:15 ` Joshua Henderson
@ 2016-01-14  1:15   ` Joshua Henderson
  -1 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes since v4: None
Changes since v3: None
Changes since v2: None
Changes since v1:
	- Fix example
	- Use UART instead of incorrect USART
	- Document otherwise unundocumented properties
---
 .../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] 66+ messages in thread

* [PATCH v5 09/14] dt/bindings: Add bindings for PIC32 UART driver
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes since v4: None
Changes since v3: None
Changes since v2: None
Changes since v1:
	- Fix example
	- Use UART instead of incorrect USART
	- Document otherwise unundocumented properties
---
 .../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] 66+ messages in thread

* [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
  2016-01-14  1:15 ` Joshua Henderson
@ 2016-01-14  1:15   ` Joshua Henderson
  -1 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
---
Changes since v4:
	- Clean up irq alloc on failure
	- Fix rework regression with handling failure in probe
Changes since v3: None
Changes since v2:
	- Use dynamic major/minor and ttyPIC* instead of ttyS*
	- Remove unused header files
	- Refactor register read/write functions
	- Reorder arguments to readl/writel functions
	- Add missing initializations to termios
	- Fix clk enable/disable mismatch
Changes since v1:
	- Rename all instances of "USART" to "UART"
	- Remove unecessary uart-has-rtscts support
---
 drivers/tty/serial/Kconfig       |   21 +
 drivers/tty/serial/Makefile      |    1 +
 drivers/tty/serial/pic32_uart.c  |  960 ++++++++++++++++++++++++++++++++++++++
 drivers/tty/serial/pic32_uart.h  |  126 +++++
 include/uapi/linux/serial_core.h |    3 +
 5 files changed, 1111 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..62a43bf
--- /dev/null
+++ b/drivers/tty/serial/pic32_uart.c
@@ -0,0 +1,960 @@
+/*
+ * PIC32 Integrated Serial Driver.
+ *
+ * Copyright (C) 2015 Microchip Technology, Inc.
+ *
+ * Authors:
+ *   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.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/delay.h>
+
+#include <asm/mach-pic32/pic32.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		"ttyPIC"
+
+/* pic32_sport pointer for console use */
+static struct pic32_sport *pic32_sports[PIC32_MAX_UARTS];
+
+static inline void pic32_wait_deplete_txbuf(struct pic32_sport *sport)
+{
+	/* wait for tx empty, otherwise chars will be lost or corrupted */
+	while (!(pic32_uart_readl(sport, PIC32_UART_STA) & PIC32_UART_STA_TRMT))
+		udelay(1);
+}
+
+static inline int pic32_enable_clock(struct pic32_sport *sport)
+{
+	int ret = clk_prepare_enable(sport->clk);
+
+	if (ret)
+		return ret;
+
+	sport->ref_clk++;
+	return 0;
+}
+
+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_readl(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_writel(sport, PIC32_SET(PIC32_UART_MODE),
+					PIC32_UART_MODE_LPBK);
+	else
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_LPBK);
+}
+
+/* get the state of CTS input pin for this port */
+static unsigned int get_cts_state(struct pic32_sport *sport)
+{
+	/* read and invert UxCTS */
+	if (gpio_is_valid(sport->cts_gpio))
+		return !gpio_get_value(sport->cts_gpio);
+
+	return 1;
+}
+
+/* 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;
+	else if (get_cts_state(sport))
+		mctrl |= TIOCM_CTS;
+
+	/* 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_readl(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON))
+		return;
+
+	if (!(pic32_uart_readl(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN))
+		return;
+
+	/* wait for tx empty */
+	pic32_wait_deplete_txbuf(sport);
+
+	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
+				PIC32_UART_STA_UTXEN);
+	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_writel(sport, PIC32_SET(PIC32_UART_STA),
+				PIC32_UART_STA_UTXEN);
+}
+
+/* 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_writel(sport, PIC32_CLR(PIC32_UART_STA),
+				PIC32_UART_STA_URXEN);
+}
+
+/* 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;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	if (ctl)
+		pic32_uart_writel(sport, PIC32_SET(PIC32_UART_STA),
+					PIC32_UART_STA_UTXBRK);
+	else
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
+					PIC32_UART_STA_UTXBRK);
+
+	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_readl(sport, PIC32_UART_STA);
+		if (unlikely(sta_reg & PIC32_UART_STA_OERR)) {
+
+			/* fifo reset is required to clear interrupt */
+			pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
+						PIC32_UART_STA_OERR);
+
+			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_readl(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_writel(sport, PIC32_UART_TX, port->x_char);
+		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_readl(sport, PIC32_UART_STA))) {
+		unsigned int c = xmit->buf[xmit->tail];
+
+		pic32_uart_writel(sport, PIC32_UART_TX, c);
+
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+		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_writel(sport, PIC32_SET(PIC32_UART_STA),
+				PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN);
+	pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE),
+				PIC32_UART_MODE_ON);
+}
+
+/* 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);
+
+	/* wait for tx empty, otherwise chars will be lost or corrupted */
+	pic32_wait_deplete_txbuf(sport);
+
+	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
+				PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN);
+	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+				PIC32_UART_MODE_ON);
+}
+
+/* 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;
+
+	local_irq_save(flags);
+
+	ret = pic32_enable_clock(sport);
+	if (ret) {
+		local_irq_restore(flags);
+		goto out_done;
+	}
+
+	/* clear status and mode registers */
+	pic32_uart_writel(sport, PIC32_UART_MODE, 0);
+	pic32_uart_writel(sport, PIC32_UART_STA, 0);
+
+	/* disable uart and mask all interrupts */
+	pic32_uart_dsbl_and_mask(port);
+
+	/* set default baud */
+	pic32_uart_writel(sport, PIC32_UART_BRG, dflt_baud);
+
+	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);
+	if (!sport->irq_fault_name) {
+		dev_err(port->dev, "%s: kasprintf err!", __func__);
+		ret = -ENOMEM;
+		goto out_done;
+	}
+	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_f;
+	}
+
+	sport->irq_rx_name = kasprintf(GFP_KERNEL, "%s%d-rx",
+				       pic32_uart_type(port),
+				       sport->idx);
+	if (!sport->irq_rx_name) {
+		dev_err(port->dev, "%s: kasprintf err!", __func__);
+		kfree(sport->irq_fault_name);
+		ret = -ENOMEM;
+		goto out_f;
+	}
+	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_r;
+	}
+
+	sport->irq_tx_name = kasprintf(GFP_KERNEL, "%s%d-tx",
+				       pic32_uart_type(port),
+				       sport->idx);
+	if (!sport->irq_tx_name) {
+		dev_err(port->dev, "%s: kasprintf err!", __func__);
+		ret = -ENOMEM;
+		goto out_r;
+	}
+	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_t;
+	}
+
+	local_irq_save(flags);
+
+	/* set rx interrupt on first receive */
+	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
+			PIC32_UART_STA_URXISEL1 | PIC32_UART_STA_URXISEL0);
+
+	/* set interrupt on empty */
+	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
+			PIC32_UART_STA_UTXISEL1);
+
+	/* enable all interrupts and eanable uart */
+	pic32_uart_en_and_unmask(port);
+
+	enable_irq(sport->irq_rx);
+
+	return 0;
+
+out_t:
+	kfree(sport->irq_tx_name);
+	free_irq(sport->irq_tx, sport);
+out_r:
+	kfree(sport->irq_rx_name);
+	free_irq(sport->irq_rx, sport);
+out_f:
+	kfree(sport->irq_fault_name);
+	free_irq(sport->irq_fault, sport);
+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_writel(sport, PIC32_SET(PIC32_UART_MODE),
+					PIC32_UART_MODE_STSEL);
+	else
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_STSEL);
+
+	/* parity options */
+	if (new->c_cflag & PARENB) {
+		if (new->c_cflag & PARODD) {
+			pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE),
+					PIC32_UART_MODE_PDSEL1);
+			pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_PDSEL0);
+		} else {
+			pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE),
+					PIC32_UART_MODE_PDSEL0);
+			pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_PDSEL1);
+		}
+	} else {
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_PDSEL1 |
+					PIC32_UART_MODE_PDSEL0);
+	}
+	/* 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_writel(sport, PIC32_SET(PIC32_UART_MODE),
+					PIC32_UART_MODE_UEN1);
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_UEN0);
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_RTSMD);
+	} else {
+		/* disable hardware flow control */
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_UEN1);
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_UEN0);
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_RTSMD);
+	}
+
+	/* Always 8-bit */
+	new->c_cflag |= CS8;
+
+	/* Mark/Space parity is not supported */
+	new->c_cflag &= ~CMSPAR;
+
+	/* update baud */
+	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
+	quot = uart_get_divisor(port, baud) - 1;
+	pic32_uart_writel(sport, PIC32_UART_BRG, quot);
+	uart_update_timeout(port, new->c_cflag, baud);
+
+	if (tty_termios_baud_rate(new))
+		tty_termios_encode_baud_rate(new, baud, 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;
+
+	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (unlikely(!res_mem))
+		return -EINVAL;
+
+	if (!request_mem_region(port->mapbase, resource_size(res_mem),
+				"pic32_uart_mem"))
+		return -EBUSY;
+
+	port->membase = devm_ioremap_nocache(port->dev, port->mapbase,
+						resource_size(res_mem));
+	if (!port->membase) {
+		dev_err(port->dev, "Unable to map registers\n");
+		release_mem_region(port->mapbase, resource_size(res_mem));
+		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);
+}
+
+/* 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_readl(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON))
+		return;
+
+	if (!(pic32_uart_readl(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN))
+		return;
+
+	/* wait for tx empty */
+	pic32_wait_deplete_txbuf(sport);
+
+	pic32_uart_writel(sport, PIC32_UART_TX, ch & 0xff);
+}
+
+/* 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,
+	.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;
+
+	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;
+
+	/* 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;
+		}
+
+		ret = gpio_direction_input(sport->cts_gpio);
+		if (ret) {
+			dev_err(&pdev->dev, "error setting CTS GPIO\n");
+			goto err;
+		}
+	}
+
+	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;
+	}
+
+#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);
+
+	return 0;
+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,
+		.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("Sorin-Andrei Pistirica <andrei.pistirica@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..ec379da
--- /dev/null
+++ b/drivers/tty/serial/pic32_uart.h
@@ -0,0 +1,126 @@
+/*
+ * 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)
+
+#define PIC32_UART_MODE		0x00
+#define PIC32_UART_STA		0x10
+#define PIC32_UART_TX		0x20
+#define PIC32_UART_RX		0x30
+#define PIC32_UART_BRG		0x40
+
+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)
+
+static inline void pic32_uart_writel(struct pic32_sport *sport,
+					u32 reg, u32 val)
+{
+	struct uart_port *port = pic32_get_port(sport);
+
+	__raw_writel(val, port->membase + reg);
+}
+
+static inline u32 pic32_uart_readl(struct pic32_sport *sport, u32 reg)
+{
+	struct uart_port *port = pic32_get_port(sport);
+
+	return	__raw_readl(port->membase + reg);
+}
+
+/* pic32 uart mode register bits */
+#define PIC32_UART_MODE_ON        BIT(15)
+#define PIC32_UART_MODE_FRZ       BIT(14)
+#define PIC32_UART_MODE_SIDL      BIT(13)
+#define PIC32_UART_MODE_IREN      BIT(12)
+#define PIC32_UART_MODE_RTSMD     BIT(11)
+#define PIC32_UART_MODE_RESV1     BIT(10)
+#define PIC32_UART_MODE_UEN1      BIT(9)
+#define PIC32_UART_MODE_UEN0      BIT(8)
+#define PIC32_UART_MODE_WAKE      BIT(7)
+#define PIC32_UART_MODE_LPBK      BIT(6)
+#define PIC32_UART_MODE_ABAUD     BIT(5)
+#define PIC32_UART_MODE_RXINV     BIT(4)
+#define PIC32_UART_MODE_BRGH      BIT(3)
+#define PIC32_UART_MODE_PDSEL1    BIT(2)
+#define PIC32_UART_MODE_PDSEL0    BIT(1)
+#define PIC32_UART_MODE_STSEL     BIT(0)
+
+/* pic32 uart status register bits */
+#define PIC32_UART_STA_UTXISEL1   BIT(15)
+#define PIC32_UART_STA_UTXISEL0   BIT(14)
+#define PIC32_UART_STA_UTXINV     BIT(13)
+#define PIC32_UART_STA_URXEN      BIT(12)
+#define PIC32_UART_STA_UTXBRK     BIT(11)
+#define PIC32_UART_STA_UTXEN      BIT(10)
+#define PIC32_UART_STA_UTXBF      BIT(9)
+#define PIC32_UART_STA_TRMT       BIT(8)
+#define PIC32_UART_STA_URXISEL1   BIT(7)
+#define PIC32_UART_STA_URXISEL0   BIT(6)
+#define PIC32_UART_STA_ADDEN      BIT(5)
+#define PIC32_UART_STA_RIDLE      BIT(4)
+#define PIC32_UART_STA_PERR       BIT(3)
+#define PIC32_UART_STA_FERR       BIT(2)
+#define PIC32_UART_STA_OERR       BIT(1)
+#define PIC32_UART_STA_URXDA      BIT(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] 66+ messages in thread

* [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
---
Changes since v4:
	- Clean up irq alloc on failure
	- Fix rework regression with handling failure in probe
Changes since v3: None
Changes since v2:
	- Use dynamic major/minor and ttyPIC* instead of ttyS*
	- Remove unused header files
	- Refactor register read/write functions
	- Reorder arguments to readl/writel functions
	- Add missing initializations to termios
	- Fix clk enable/disable mismatch
Changes since v1:
	- Rename all instances of "USART" to "UART"
	- Remove unecessary uart-has-rtscts support
---
 drivers/tty/serial/Kconfig       |   21 +
 drivers/tty/serial/Makefile      |    1 +
 drivers/tty/serial/pic32_uart.c  |  960 ++++++++++++++++++++++++++++++++++++++
 drivers/tty/serial/pic32_uart.h  |  126 +++++
 include/uapi/linux/serial_core.h |    3 +
 5 files changed, 1111 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..62a43bf
--- /dev/null
+++ b/drivers/tty/serial/pic32_uart.c
@@ -0,0 +1,960 @@
+/*
+ * PIC32 Integrated Serial Driver.
+ *
+ * Copyright (C) 2015 Microchip Technology, Inc.
+ *
+ * Authors:
+ *   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.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/delay.h>
+
+#include <asm/mach-pic32/pic32.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		"ttyPIC"
+
+/* pic32_sport pointer for console use */
+static struct pic32_sport *pic32_sports[PIC32_MAX_UARTS];
+
+static inline void pic32_wait_deplete_txbuf(struct pic32_sport *sport)
+{
+	/* wait for tx empty, otherwise chars will be lost or corrupted */
+	while (!(pic32_uart_readl(sport, PIC32_UART_STA) & PIC32_UART_STA_TRMT))
+		udelay(1);
+}
+
+static inline int pic32_enable_clock(struct pic32_sport *sport)
+{
+	int ret = clk_prepare_enable(sport->clk);
+
+	if (ret)
+		return ret;
+
+	sport->ref_clk++;
+	return 0;
+}
+
+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_readl(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_writel(sport, PIC32_SET(PIC32_UART_MODE),
+					PIC32_UART_MODE_LPBK);
+	else
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_LPBK);
+}
+
+/* get the state of CTS input pin for this port */
+static unsigned int get_cts_state(struct pic32_sport *sport)
+{
+	/* read and invert UxCTS */
+	if (gpio_is_valid(sport->cts_gpio))
+		return !gpio_get_value(sport->cts_gpio);
+
+	return 1;
+}
+
+/* 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;
+	else if (get_cts_state(sport))
+		mctrl |= TIOCM_CTS;
+
+	/* 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_readl(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON))
+		return;
+
+	if (!(pic32_uart_readl(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN))
+		return;
+
+	/* wait for tx empty */
+	pic32_wait_deplete_txbuf(sport);
+
+	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
+				PIC32_UART_STA_UTXEN);
+	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_writel(sport, PIC32_SET(PIC32_UART_STA),
+				PIC32_UART_STA_UTXEN);
+}
+
+/* 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_writel(sport, PIC32_CLR(PIC32_UART_STA),
+				PIC32_UART_STA_URXEN);
+}
+
+/* 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;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	if (ctl)
+		pic32_uart_writel(sport, PIC32_SET(PIC32_UART_STA),
+					PIC32_UART_STA_UTXBRK);
+	else
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
+					PIC32_UART_STA_UTXBRK);
+
+	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_readl(sport, PIC32_UART_STA);
+		if (unlikely(sta_reg & PIC32_UART_STA_OERR)) {
+
+			/* fifo reset is required to clear interrupt */
+			pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
+						PIC32_UART_STA_OERR);
+
+			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_readl(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_writel(sport, PIC32_UART_TX, port->x_char);
+		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_readl(sport, PIC32_UART_STA))) {
+		unsigned int c = xmit->buf[xmit->tail];
+
+		pic32_uart_writel(sport, PIC32_UART_TX, c);
+
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+		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_writel(sport, PIC32_SET(PIC32_UART_STA),
+				PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN);
+	pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE),
+				PIC32_UART_MODE_ON);
+}
+
+/* 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);
+
+	/* wait for tx empty, otherwise chars will be lost or corrupted */
+	pic32_wait_deplete_txbuf(sport);
+
+	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
+				PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN);
+	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+				PIC32_UART_MODE_ON);
+}
+
+/* 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;
+
+	local_irq_save(flags);
+
+	ret = pic32_enable_clock(sport);
+	if (ret) {
+		local_irq_restore(flags);
+		goto out_done;
+	}
+
+	/* clear status and mode registers */
+	pic32_uart_writel(sport, PIC32_UART_MODE, 0);
+	pic32_uart_writel(sport, PIC32_UART_STA, 0);
+
+	/* disable uart and mask all interrupts */
+	pic32_uart_dsbl_and_mask(port);
+
+	/* set default baud */
+	pic32_uart_writel(sport, PIC32_UART_BRG, dflt_baud);
+
+	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);
+	if (!sport->irq_fault_name) {
+		dev_err(port->dev, "%s: kasprintf err!", __func__);
+		ret = -ENOMEM;
+		goto out_done;
+	}
+	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_f;
+	}
+
+	sport->irq_rx_name = kasprintf(GFP_KERNEL, "%s%d-rx",
+				       pic32_uart_type(port),
+				       sport->idx);
+	if (!sport->irq_rx_name) {
+		dev_err(port->dev, "%s: kasprintf err!", __func__);
+		kfree(sport->irq_fault_name);
+		ret = -ENOMEM;
+		goto out_f;
+	}
+	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_r;
+	}
+
+	sport->irq_tx_name = kasprintf(GFP_KERNEL, "%s%d-tx",
+				       pic32_uart_type(port),
+				       sport->idx);
+	if (!sport->irq_tx_name) {
+		dev_err(port->dev, "%s: kasprintf err!", __func__);
+		ret = -ENOMEM;
+		goto out_r;
+	}
+	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_t;
+	}
+
+	local_irq_save(flags);
+
+	/* set rx interrupt on first receive */
+	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
+			PIC32_UART_STA_URXISEL1 | PIC32_UART_STA_URXISEL0);
+
+	/* set interrupt on empty */
+	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
+			PIC32_UART_STA_UTXISEL1);
+
+	/* enable all interrupts and eanable uart */
+	pic32_uart_en_and_unmask(port);
+
+	enable_irq(sport->irq_rx);
+
+	return 0;
+
+out_t:
+	kfree(sport->irq_tx_name);
+	free_irq(sport->irq_tx, sport);
+out_r:
+	kfree(sport->irq_rx_name);
+	free_irq(sport->irq_rx, sport);
+out_f:
+	kfree(sport->irq_fault_name);
+	free_irq(sport->irq_fault, sport);
+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_writel(sport, PIC32_SET(PIC32_UART_MODE),
+					PIC32_UART_MODE_STSEL);
+	else
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_STSEL);
+
+	/* parity options */
+	if (new->c_cflag & PARENB) {
+		if (new->c_cflag & PARODD) {
+			pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE),
+					PIC32_UART_MODE_PDSEL1);
+			pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_PDSEL0);
+		} else {
+			pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE),
+					PIC32_UART_MODE_PDSEL0);
+			pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_PDSEL1);
+		}
+	} else {
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_PDSEL1 |
+					PIC32_UART_MODE_PDSEL0);
+	}
+	/* 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_writel(sport, PIC32_SET(PIC32_UART_MODE),
+					PIC32_UART_MODE_UEN1);
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_UEN0);
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_RTSMD);
+	} else {
+		/* disable hardware flow control */
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_UEN1);
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_UEN0);
+		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
+					PIC32_UART_MODE_RTSMD);
+	}
+
+	/* Always 8-bit */
+	new->c_cflag |= CS8;
+
+	/* Mark/Space parity is not supported */
+	new->c_cflag &= ~CMSPAR;
+
+	/* update baud */
+	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
+	quot = uart_get_divisor(port, baud) - 1;
+	pic32_uart_writel(sport, PIC32_UART_BRG, quot);
+	uart_update_timeout(port, new->c_cflag, baud);
+
+	if (tty_termios_baud_rate(new))
+		tty_termios_encode_baud_rate(new, baud, 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;
+
+	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (unlikely(!res_mem))
+		return -EINVAL;
+
+	if (!request_mem_region(port->mapbase, resource_size(res_mem),
+				"pic32_uart_mem"))
+		return -EBUSY;
+
+	port->membase = devm_ioremap_nocache(port->dev, port->mapbase,
+						resource_size(res_mem));
+	if (!port->membase) {
+		dev_err(port->dev, "Unable to map registers\n");
+		release_mem_region(port->mapbase, resource_size(res_mem));
+		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);
+}
+
+/* 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_readl(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON))
+		return;
+
+	if (!(pic32_uart_readl(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN))
+		return;
+
+	/* wait for tx empty */
+	pic32_wait_deplete_txbuf(sport);
+
+	pic32_uart_writel(sport, PIC32_UART_TX, ch & 0xff);
+}
+
+/* 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,
+	.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;
+
+	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;
+
+	/* 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;
+		}
+
+		ret = gpio_direction_input(sport->cts_gpio);
+		if (ret) {
+			dev_err(&pdev->dev, "error setting CTS GPIO\n");
+			goto err;
+		}
+	}
+
+	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;
+	}
+
+#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);
+
+	return 0;
+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,
+		.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("Sorin-Andrei Pistirica <andrei.pistirica@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..ec379da
--- /dev/null
+++ b/drivers/tty/serial/pic32_uart.h
@@ -0,0 +1,126 @@
+/*
+ * 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)
+
+#define PIC32_UART_MODE		0x00
+#define PIC32_UART_STA		0x10
+#define PIC32_UART_TX		0x20
+#define PIC32_UART_RX		0x30
+#define PIC32_UART_BRG		0x40
+
+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)
+
+static inline void pic32_uart_writel(struct pic32_sport *sport,
+					u32 reg, u32 val)
+{
+	struct uart_port *port = pic32_get_port(sport);
+
+	__raw_writel(val, port->membase + reg);
+}
+
+static inline u32 pic32_uart_readl(struct pic32_sport *sport, u32 reg)
+{
+	struct uart_port *port = pic32_get_port(sport);
+
+	return	__raw_readl(port->membase + reg);
+}
+
+/* pic32 uart mode register bits */
+#define PIC32_UART_MODE_ON        BIT(15)
+#define PIC32_UART_MODE_FRZ       BIT(14)
+#define PIC32_UART_MODE_SIDL      BIT(13)
+#define PIC32_UART_MODE_IREN      BIT(12)
+#define PIC32_UART_MODE_RTSMD     BIT(11)
+#define PIC32_UART_MODE_RESV1     BIT(10)
+#define PIC32_UART_MODE_UEN1      BIT(9)
+#define PIC32_UART_MODE_UEN0      BIT(8)
+#define PIC32_UART_MODE_WAKE      BIT(7)
+#define PIC32_UART_MODE_LPBK      BIT(6)
+#define PIC32_UART_MODE_ABAUD     BIT(5)
+#define PIC32_UART_MODE_RXINV     BIT(4)
+#define PIC32_UART_MODE_BRGH      BIT(3)
+#define PIC32_UART_MODE_PDSEL1    BIT(2)
+#define PIC32_UART_MODE_PDSEL0    BIT(1)
+#define PIC32_UART_MODE_STSEL     BIT(0)
+
+/* pic32 uart status register bits */
+#define PIC32_UART_STA_UTXISEL1   BIT(15)
+#define PIC32_UART_STA_UTXISEL0   BIT(14)
+#define PIC32_UART_STA_UTXINV     BIT(13)
+#define PIC32_UART_STA_URXEN      BIT(12)
+#define PIC32_UART_STA_UTXBRK     BIT(11)
+#define PIC32_UART_STA_UTXEN      BIT(10)
+#define PIC32_UART_STA_UTXBF      BIT(9)
+#define PIC32_UART_STA_TRMT       BIT(8)
+#define PIC32_UART_STA_URXISEL1   BIT(7)
+#define PIC32_UART_STA_URXISEL0   BIT(6)
+#define PIC32_UART_STA_ADDEN      BIT(5)
+#define PIC32_UART_STA_RIDLE      BIT(4)
+#define PIC32_UART_STA_PERR       BIT(3)
+#define PIC32_UART_STA_FERR       BIT(2)
+#define PIC32_UART_STA_OERR       BIT(1)
+#define PIC32_UART_STA_URXDA      BIT(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] 66+ messages in thread

* [PATCH v5 11/14] dt/bindings: Add bindings for PIC32 SDHCI host controller
  2016-01-14  1:15 ` Joshua Henderson
@ 2016-01-14  1:15   ` Joshua Henderson
  -1 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes since v4: None
Changes since v3: None
Changes since v2: None
Changes since v1:
	- Drop usage of piomode and no-1-8-v DT properties
---
 .../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] 66+ messages in thread

* [PATCH v5 11/14] dt/bindings: Add bindings for PIC32 SDHCI host controller
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes since v4: None
Changes since v3: None
Changes since v2: None
Changes since v1:
	- Drop usage of piomode and no-1-8-v DT properties
---
 .../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] 66+ messages in thread

* [PATCH v5 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
---
Changes since v4: None
Changes since v3: None
Changes since v2:
	- Use 'sdhci_pltfm_*' instead of 'sdhci_*_host' and other cleanup
Changes since v1:
	- Be consistent and use only "SDHCI" when referring to SD host
	  controller
	- Remove unnecessary PIC32 sdhci_ops min clock function.
	- Drop usage of piomode and no-1-8-v DT properties
        - Formatting
        - Fix use of devm_iounmap
        - Address code comment
---
 drivers/mmc/host/Kconfig       |   11 ++
 drivers/mmc/host/Makefile      |    1 +
 drivers/mmc/host/sdhci-pic32.c |  257 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 269 insertions(+)
 create mode 100644 drivers/mmc/host/sdhci-pic32.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 1dee533..c6a8916 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 && MMC_SDHCI_PLTFM
+        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..059df70
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pic32.c
@@ -0,0 +1,257 @@
+/*
+ * 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 "sdhci-pltfm.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
+
+struct pic32_sdhci_priv {
+	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_priv *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;
+	}
+
+	/* CD select and test bits must be set for errata workaround. */
+	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 struct sdhci_pltfm_data sdhci_pic32_pdata = {
+	.ops = &pic32_sdhci_ops,
+	.quirks = SDHCI_QUIRK_NO_HISPD_BIT,
+	.quirks2 = SDHCI_QUIRK2_NO_1_8_V,
+};
+
+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 & 1)
+		bus |= (1 << SDH_SHARED_BUS_CLK_PINS);
+
+	/* select first interrupt */
+	if (irq_pins & 1)
+		bus |= (1 << 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_priv *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 sdhci_host *host;
+	struct sdhci_pltfm_host *pltfm_host;
+	struct pic32_sdhci_priv *sdhci_pdata;
+	struct pic32_sdhci_platform_data *plat_data;
+	int ret;
+
+	host = sdhci_pltfm_init(pdev, &sdhci_pic32_pdata,
+				sizeof(struct pic32_sdhci_priv));
+	if (IS_ERR(host)) {
+		ret = PTR_ERR(host);
+		goto err;
+	}
+
+	pltfm_host = sdhci_priv(host);
+	sdhci_pdata = sdhci_pltfm_priv(pltfm_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;
+	}
+
+	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;
+	}
+
+	ret = clk_prepare_enable(sdhci_pdata->sys_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Error enabling clock\n");
+		goto err_host;
+	}
+
+	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_sys_clk;
+	}
+
+	ret = clk_prepare_enable(sdhci_pdata->base_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Error enabling clock\n");
+		goto err_base_clk;
+	}
+
+	ret = mmc_of_parse(host->mmc);
+	if (ret)
+		goto err_base_clk;
+
+	ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to probe platform!\n");
+		goto err_base_clk;
+	}
+
+	ret = sdhci_add_host(host);
+	if (ret) {
+		dev_err(&pdev->dev, "error adding host\n");
+		goto err_base_clk;
+	}
+
+	dev_info(&pdev->dev, "Successfully added sdhci host\n");
+	return 0;
+
+err_base_clk:
+	clk_disable_unprepare(sdhci_pdata->base_clk);
+err_sys_clk:
+	clk_disable_unprepare(sdhci_pdata->sys_clk);
+err_host:
+	sdhci_pltfm_free(pdev);
+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_priv *sdhci_pdata = sdhci_priv(host);
+	u32 scratch;
+
+	scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
+	sdhci_remove_host(host, scratch == (u32)~0);
+	clk_disable_unprepare(sdhci_pdata->base_clk);
+	clk_disable_unprepare(sdhci_pdata->sys_clk);
+	sdhci_pltfm_free(pdev);
+
+	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	= "pic32-sdhci",
+		.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] 66+ messages in thread

* [PATCH v5 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
---
Changes since v4: None
Changes since v3: None
Changes since v2:
	- Use 'sdhci_pltfm_*' instead of 'sdhci_*_host' and other cleanup
Changes since v1:
	- Be consistent and use only "SDHCI" when referring to SD host
	  controller
	- Remove unnecessary PIC32 sdhci_ops min clock function.
	- Drop usage of piomode and no-1-8-v DT properties
        - Formatting
        - Fix use of devm_iounmap
        - Address code comment
---
 drivers/mmc/host/Kconfig       |   11 ++
 drivers/mmc/host/Makefile      |    1 +
 drivers/mmc/host/sdhci-pic32.c |  257 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 269 insertions(+)
 create mode 100644 drivers/mmc/host/sdhci-pic32.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 1dee533..c6a8916 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 && MMC_SDHCI_PLTFM
+        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..059df70
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pic32.c
@@ -0,0 +1,257 @@
+/*
+ * 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 "sdhci-pltfm.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
+
+struct pic32_sdhci_priv {
+	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_priv *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;
+	}
+
+	/* CD select and test bits must be set for errata workaround. */
+	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 struct sdhci_pltfm_data sdhci_pic32_pdata = {
+	.ops = &pic32_sdhci_ops,
+	.quirks = SDHCI_QUIRK_NO_HISPD_BIT,
+	.quirks2 = SDHCI_QUIRK2_NO_1_8_V,
+};
+
+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 & 1)
+		bus |= (1 << SDH_SHARED_BUS_CLK_PINS);
+
+	/* select first interrupt */
+	if (irq_pins & 1)
+		bus |= (1 << 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_priv *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 sdhci_host *host;
+	struct sdhci_pltfm_host *pltfm_host;
+	struct pic32_sdhci_priv *sdhci_pdata;
+	struct pic32_sdhci_platform_data *plat_data;
+	int ret;
+
+	host = sdhci_pltfm_init(pdev, &sdhci_pic32_pdata,
+				sizeof(struct pic32_sdhci_priv));
+	if (IS_ERR(host)) {
+		ret = PTR_ERR(host);
+		goto err;
+	}
+
+	pltfm_host = sdhci_priv(host);
+	sdhci_pdata = sdhci_pltfm_priv(pltfm_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;
+	}
+
+	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;
+	}
+
+	ret = clk_prepare_enable(sdhci_pdata->sys_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Error enabling clock\n");
+		goto err_host;
+	}
+
+	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_sys_clk;
+	}
+
+	ret = clk_prepare_enable(sdhci_pdata->base_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Error enabling clock\n");
+		goto err_base_clk;
+	}
+
+	ret = mmc_of_parse(host->mmc);
+	if (ret)
+		goto err_base_clk;
+
+	ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to probe platform!\n");
+		goto err_base_clk;
+	}
+
+	ret = sdhci_add_host(host);
+	if (ret) {
+		dev_err(&pdev->dev, "error adding host\n");
+		goto err_base_clk;
+	}
+
+	dev_info(&pdev->dev, "Successfully added sdhci host\n");
+	return 0;
+
+err_base_clk:
+	clk_disable_unprepare(sdhci_pdata->base_clk);
+err_sys_clk:
+	clk_disable_unprepare(sdhci_pdata->sys_clk);
+err_host:
+	sdhci_pltfm_free(pdev);
+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_priv *sdhci_pdata = sdhci_priv(host);
+	u32 scratch;
+
+	scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
+	sdhci_remove_host(host, scratch == (u32)~0);
+	clk_disable_unprepare(sdhci_pdata->base_clk);
+	clk_disable_unprepare(sdhci_pdata->sys_clk);
+	sdhci_pltfm_free(pdev);
+
+	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	= "pic32-sdhci",
+		.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] 66+ messages in thread

* [PATCH v5 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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

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>
---
Changes since v4: None
Changes since v3: None
Changes since v2:
	- Use 'sdhci_pltfm_*' instead of 'sdhci_*_host' and other cleanup
Changes since v1:
	- Be consistent and use only "SDHCI" when referring to SD host
	  controller
	- Remove unnecessary PIC32 sdhci_ops min clock function.
	- Drop usage of piomode and no-1-8-v DT properties
        - Formatting
        - Fix use of devm_iounmap
        - Address code comment
---
 drivers/mmc/host/Kconfig       |   11 ++
 drivers/mmc/host/Makefile      |    1 +
 drivers/mmc/host/sdhci-pic32.c |  257 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 269 insertions(+)
 create mode 100644 drivers/mmc/host/sdhci-pic32.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 1dee533..c6a8916 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 && MMC_SDHCI_PLTFM
+        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..059df70
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pic32.c
@@ -0,0 +1,257 @@
+/*
+ * 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 "sdhci-pltfm.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
+
+struct pic32_sdhci_priv {
+	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_priv *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;
+	}
+
+	/* CD select and test bits must be set for errata workaround. */
+	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 struct sdhci_pltfm_data sdhci_pic32_pdata = {
+	.ops = &pic32_sdhci_ops,
+	.quirks = SDHCI_QUIRK_NO_HISPD_BIT,
+	.quirks2 = SDHCI_QUIRK2_NO_1_8_V,
+};
+
+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 & 1)
+		bus |= (1 << SDH_SHARED_BUS_CLK_PINS);
+
+	/* select first interrupt */
+	if (irq_pins & 1)
+		bus |= (1 << 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_priv *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 sdhci_host *host;
+	struct sdhci_pltfm_host *pltfm_host;
+	struct pic32_sdhci_priv *sdhci_pdata;
+	struct pic32_sdhci_platform_data *plat_data;
+	int ret;
+
+	host = sdhci_pltfm_init(pdev, &sdhci_pic32_pdata,
+				sizeof(struct pic32_sdhci_priv));
+	if (IS_ERR(host)) {
+		ret = PTR_ERR(host);
+		goto err;
+	}
+
+	pltfm_host = sdhci_priv(host);
+	sdhci_pdata = sdhci_pltfm_priv(pltfm_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;
+	}
+
+	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;
+	}
+
+	ret = clk_prepare_enable(sdhci_pdata->sys_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Error enabling clock\n");
+		goto err_host;
+	}
+
+	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_sys_clk;
+	}
+
+	ret = clk_prepare_enable(sdhci_pdata->base_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Error enabling clock\n");
+		goto err_base_clk;
+	}
+
+	ret = mmc_of_parse(host->mmc);
+	if (ret)
+		goto err_base_clk;
+
+	ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to probe platform!\n");
+		goto err_base_clk;
+	}
+
+	ret = sdhci_add_host(host);
+	if (ret) {
+		dev_err(&pdev->dev, "error adding host\n");
+		goto err_base_clk;
+	}
+
+	dev_info(&pdev->dev, "Successfully added sdhci host\n");
+	return 0;
+
+err_base_clk:
+	clk_disable_unprepare(sdhci_pdata->base_clk);
+err_sys_clk:
+	clk_disable_unprepare(sdhci_pdata->sys_clk);
+err_host:
+	sdhci_pltfm_free(pdev);
+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_priv *sdhci_pdata = sdhci_priv(host);
+	u32 scratch;
+
+	scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
+	sdhci_remove_host(host, scratch == (u32)~0);
+	clk_disable_unprepare(sdhci_pdata->base_clk);
+	clk_disable_unprepare(sdhci_pdata->sys_clk);
+	sdhci_pltfm_free(pdev);
+
+	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	= "pic32-sdhci",
+		.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] 66+ messages in thread

* [PATCH v5 13/14] MIPS: dts: Add initial DTS for the PIC32MZDA Starter Kit
  2016-01-14  1:15 ` Joshua Henderson
@ 2016-01-14  1:15   ` Joshua Henderson
  -1 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
---
Changes since v4:
	- Add soc node for core timer interrupt to DTS
	- Add external IRQ property to DTS
Changes since v3: None
Changes since v2:
	- Follow device-tree node naming convention for clocks
Changes since v1:
	- Drop usage of the following, mostly non-standard, properties in
	  DT bindings:
	    device_type
	    piomode
	    no-1-8-v
	    clock-frequency => assigned-clock-rate
	- Rename all DT compatible properties to be chip specific
	- Remove PIC32 memory PLL support from DT
	- Replace empty 'ranges' with populated one for clock tree node
	- Remove 'interrupts' property from FSCM of PIC32 clock tree node
	- Add default REFCLK rate initialization required for SDHCI
	- Remove default frequency setup for REFOSC clocks in -clk
	- Remove all dependencies on include headers used by PIC32
	  DT files
---
 arch/mips/boot/dts/Makefile                 |    1 +
 arch/mips/boot/dts/pic32/Makefile           |   12 ++
 arch/mips/boot/dts/pic32/pic32mzda-clk.dtsi |  236 ++++++++++++++++++++++
 arch/mips/boot/dts/pic32/pic32mzda.dtsi     |  281 +++++++++++++++++++++++++++
 arch/mips/boot/dts/pic32/pic32mzda_sk.dts   |  151 ++++++++++++++
 arch/mips/pic32/Kconfig                     |   16 ++
 6 files changed, 697 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..ef13350
--- /dev/null
+++ b/arch/mips/boot/dts/pic32/pic32mzda-clk.dtsi
@@ -0,0 +1,236 @@
+/*
+ * 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 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@0 {
+			#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@020 {
+			#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@1c0 {
+			#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@140 {
+			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@150 {
+			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@160 {
+			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@170 {
+			reg = <0x170 0x10>;
+			#clock-cells = <0>;
+			compatible = "microchip,pic32mzda-pbclk";
+			clocks = <&SYSCLK>;
+			clock-output-names = "pb4_clk";
+		};
+
+		/* Peripheral bus clock */
+		PBCLK5:pb5_clk@180 {
+			reg = <0x180 0x10>;
+			#clock-cells = <0>;
+			compatible = "microchip,pic32mzda-pbclk";
+			clocks = <&SYSCLK>;
+			clock-output-names = "pb5_clk";
+		};
+
+		/* Peripheral Bus6 clock; */
+		PBCLK6:pb6_clk@190 {
+			reg = <0x190 0x10>;
+			compatible = "microchip,pic32mzda-pbclk";
+			clocks = <&SYSCLK>;
+			#clock-cells = <0>;
+		};
+
+		/* Peripheral bus7 clock */
+		PBCLK7:pb7_clk@1a0 {
+			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@80 {
+			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@a0 {
+			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@c0 {
+			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@e0 {
+			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@100 {
+			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..ad9e3318
--- /dev/null
+++ b/arch/mips/boot/dts/pic32/pic32mzda.dtsi
@@ -0,0 +1,281 @@
+/*
+ * 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";
+		};
+	};
+
+	soc {
+		compatible = "microchip,pic32mzda-infra";
+		interrupts = <0 IRQ_TYPE_EDGE_RISING>;
+	};
+
+	evic: interrupt-controller@1f810000 {
+		compatible = "microchip,pic32mzda-evic";
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		reg = <0x1f810000 0x1000>;
+		microchip,external-irqs = <3 8 13 18 23>;
+	};
+
+	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..5d434a5
--- /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=ttyPIC1,115200n8r console=ttyPIC1,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-high;
+		};
+		uart2-rx {
+			pins = "B0";
+			function = "U2RX";
+			microchip,digital;
+			input-enable;
+		};
+	};
+
+	pinctrl_uart4: uart4-0 {
+		uart4-tx {
+			pins = "C3";
+			function = "U4TX";
+			microchip,digital;
+			output-high;
+		};
+		uart4-rx {
+			pins = "E8";
+			function = "U4RX";
+			microchip,digital;
+			input-enable;
+		};
+	};
+};
diff --git a/arch/mips/pic32/Kconfig b/arch/mips/pic32/Kconfig
index 9be43c1..fde56a8 100644
--- a/arch/mips/pic32/Kconfig
+++ b/arch/mips/pic32/Kconfig
@@ -32,4 +32,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] 66+ messages in thread

* [PATCH v5 13/14] MIPS: dts: Add initial DTS for the PIC32MZDA Starter Kit
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
---
Changes since v4:
	- Add soc node for core timer interrupt to DTS
	- Add external IRQ property to DTS
Changes since v3: None
Changes since v2:
	- Follow device-tree node naming convention for clocks
Changes since v1:
	- Drop usage of the following, mostly non-standard, properties in
	  DT bindings:
	    device_type
	    piomode
	    no-1-8-v
	    clock-frequency => assigned-clock-rate
	- Rename all DT compatible properties to be chip specific
	- Remove PIC32 memory PLL support from DT
	- Replace empty 'ranges' with populated one for clock tree node
	- Remove 'interrupts' property from FSCM of PIC32 clock tree node
	- Add default REFCLK rate initialization required for SDHCI
	- Remove default frequency setup for REFOSC clocks in -clk
	- Remove all dependencies on include headers used by PIC32
	  DT files
---
 arch/mips/boot/dts/Makefile                 |    1 +
 arch/mips/boot/dts/pic32/Makefile           |   12 ++
 arch/mips/boot/dts/pic32/pic32mzda-clk.dtsi |  236 ++++++++++++++++++++++
 arch/mips/boot/dts/pic32/pic32mzda.dtsi     |  281 +++++++++++++++++++++++++++
 arch/mips/boot/dts/pic32/pic32mzda_sk.dts   |  151 ++++++++++++++
 arch/mips/pic32/Kconfig                     |   16 ++
 6 files changed, 697 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..ef13350
--- /dev/null
+++ b/arch/mips/boot/dts/pic32/pic32mzda-clk.dtsi
@@ -0,0 +1,236 @@
+/*
+ * 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 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@0 {
+			#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@020 {
+			#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@1c0 {
+			#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@140 {
+			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@150 {
+			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@160 {
+			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@170 {
+			reg = <0x170 0x10>;
+			#clock-cells = <0>;
+			compatible = "microchip,pic32mzda-pbclk";
+			clocks = <&SYSCLK>;
+			clock-output-names = "pb4_clk";
+		};
+
+		/* Peripheral bus clock */
+		PBCLK5:pb5_clk@180 {
+			reg = <0x180 0x10>;
+			#clock-cells = <0>;
+			compatible = "microchip,pic32mzda-pbclk";
+			clocks = <&SYSCLK>;
+			clock-output-names = "pb5_clk";
+		};
+
+		/* Peripheral Bus6 clock; */
+		PBCLK6:pb6_clk@190 {
+			reg = <0x190 0x10>;
+			compatible = "microchip,pic32mzda-pbclk";
+			clocks = <&SYSCLK>;
+			#clock-cells = <0>;
+		};
+
+		/* Peripheral bus7 clock */
+		PBCLK7:pb7_clk@1a0 {
+			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@80 {
+			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@a0 {
+			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@c0 {
+			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@e0 {
+			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@100 {
+			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..ad9e3318
--- /dev/null
+++ b/arch/mips/boot/dts/pic32/pic32mzda.dtsi
@@ -0,0 +1,281 @@
+/*
+ * 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";
+		};
+	};
+
+	soc {
+		compatible = "microchip,pic32mzda-infra";
+		interrupts = <0 IRQ_TYPE_EDGE_RISING>;
+	};
+
+	evic: interrupt-controller@1f810000 {
+		compatible = "microchip,pic32mzda-evic";
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		reg = <0x1f810000 0x1000>;
+		microchip,external-irqs = <3 8 13 18 23>;
+	};
+
+	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..5d434a5
--- /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=ttyPIC1,115200n8r console=ttyPIC1,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-high;
+		};
+		uart2-rx {
+			pins = "B0";
+			function = "U2RX";
+			microchip,digital;
+			input-enable;
+		};
+	};
+
+	pinctrl_uart4: uart4-0 {
+		uart4-tx {
+			pins = "C3";
+			function = "U4TX";
+			microchip,digital;
+			output-high;
+		};
+		uart4-rx {
+			pins = "E8";
+			function = "U4RX";
+			microchip,digital;
+			input-enable;
+		};
+	};
+};
diff --git a/arch/mips/pic32/Kconfig b/arch/mips/pic32/Kconfig
index 9be43c1..fde56a8 100644
--- a/arch/mips/pic32/Kconfig
+++ b/arch/mips/pic32/Kconfig
@@ -32,4 +32,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] 66+ messages in thread

* [PATCH v5 14/14] MIPS: pic32mzda: Add initial PIC32MZDA Starter Kit defconfig
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
---
Changes since v4: None
Changes since v3: None
Changes since v2:
	- Add CONFIG_MMC_SDHCI_PLTFM=y
Changes since v1: None
---
 arch/mips/configs/pic32mzda_defconfig |   89 +++++++++++++++++++++++++++++++++
 1 file changed, 89 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..52192c6
--- /dev/null
+++ b/arch/mips/configs/pic32mzda_defconfig
@@ -0,0 +1,89 @@
+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_PLTFM=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] 66+ messages in thread

* [PATCH v5 14/14] MIPS: pic32mzda: Add initial PIC32MZDA Starter Kit defconfig
@ 2016-01-14  1:15   ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-14  1:15 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>
---
Changes since v4: None
Changes since v3: None
Changes since v2:
	- Add CONFIG_MMC_SDHCI_PLTFM=y
Changes since v1: None
---
 arch/mips/configs/pic32mzda_defconfig |   89 +++++++++++++++++++++++++++++++++
 1 file changed, 89 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..52192c6
--- /dev/null
+++ b/arch/mips/configs/pic32mzda_defconfig
@@ -0,0 +1,89 @@
+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_PLTFM=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] 66+ messages in thread

* Re: [PATCH v5 02/14] irqchip: irq-pic32-evic: Add support for PIC32 interrupt controller
  2016-01-14  1:15   ` Joshua Henderson
  (?)
@ 2016-01-14  8:10   ` Thomas Gleixner
  2016-01-14 11:12       ` Ralf Baechle
  2016-01-14 11:12     ` Ralf Baechle
  -1 siblings, 2 replies; 66+ messages in thread
From: Thomas Gleixner @ 2016-01-14  8:10 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, linux-mips, ralf, Cristian Birsan, Jason Cooper,
	Marc Zyngier

On Wed, 13 Jan 2016, Joshua Henderson wrote:
> +static int pic32_set_type_edge(struct irq_data *data,
> +			       unsigned int flow_type)
> +{
> +	struct evic_chip_data *priv = irqd_to_priv(data);
> +	int ret;
> +	int i;
> +
> +	if (!(flow_type & IRQ_TYPE_EDGE_BOTH))
> +		return -EBADR;
> +
> +	/* set polarity for external interrupts only */
> +	for (i = 0; i < ARRAY_SIZE(priv->ext_irqs); i++) {
> +		if (priv->ext_irqs[i] == data->hwirq) {
> +			ret = pic32_set_ext_polarity(i + 1, flow_type);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	irqd_set_trigger_type(data, flow_type);

The core code handles that already.

> +static int pic32_irq_domain_map(struct irq_domain *d, unsigned int virq,
> +				irq_hw_number_t hw)
> +{
> +	struct evic_chip_data *priv = d->host_data;
> +	struct irq_data *data;
> +	int ret;
> +	u32 iecclr, ifsclr;
> +	u32 reg, mask;
> +
> +	ret = irq_map_generic_chip(d, virq, hw);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Piggyback on xlate function to move to an alternate chip as necessary
> +	 * at time of mapping instead of allowing the flow handler/chip to be
> +	 * changed later. This requires all interrupts to be configured through
> +	 * DT.
> +	 */
> +	if (priv->irq_types[hw] & IRQ_TYPE_SENSE_MASK) {
> +		data = irq_domain_get_irq_data(d, virq);
> +		irqd_set_trigger_type(data, priv->irq_types[hw]);
> +		irq_setup_alt_chip(data, priv->irq_types[hw]);
> +	}

I like that approach.

So except for the nit in pic32_set_type_edge() this looks good. It's pretty
clear now what the code does and how the hardware works.

Thanks for following up!

       tglx

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

* Re: [PATCH v5 02/14] irqchip: irq-pic32-evic: Add support for PIC32 interrupt controller
  2016-01-14  8:10   ` Thomas Gleixner
@ 2016-01-14 11:12       ` Ralf Baechle
  2016-01-14 11:12     ` Ralf Baechle
  1 sibling, 0 replies; 66+ messages in thread
From: Ralf Baechle @ 2016-01-14 11:12 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Joshua Henderson, linux-kernel, linux-mips, Cristian Birsan,
	Jason Cooper, Marc Zyngier, Purna Chandra Mandal,
	Michael Turquette, Stephen Boyd, linux-clk, Linus Walleij,
	linux-gpio, Andrei Pistirica, Greg Kroah-Hartman, Jiri Slaby,
	linux-serial, linux-api, Ulf Hansson, Jean Delvare,
	Geert Uytterhoeven, Cornel

On Thu, Jan 14, 2016 at 09:10:09AM +0100, Thomas Gleixner wrote:

> I like that approach.
> 
> So except for the nit in pic32_set_type_edge() this looks good. It's pretty
> clear now what the code does and how the hardware works.
> 
> Thanks for following up!

So I take that for an ack.  I still haven't seen any acks or comments for
the patches:

     4/14      drivers/clk
     8/14      drivers/pinctrl
    10/14      drivers/serial
    12/14      drivers/mmc

Of this series.  Ping?

  Ralf

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

* Re: [PATCH v5 02/14] irqchip: irq-pic32-evic: Add support for PIC32 interrupt controller
@ 2016-01-14 11:12       ` Ralf Baechle
  0 siblings, 0 replies; 66+ messages in thread
From: Ralf Baechle @ 2016-01-14 11:12 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Joshua Henderson, linux-kernel, linux-mips, Cristian Birsan,
	Jason Cooper, Marc Zyngier, Purna Chandra Mandal,
	Michael Turquette, Stephen Boyd, linux-clk, Linus Walleij,
	linux-gpio, Andrei Pistirica, Greg Kroah-Hartman, Jiri Slaby,
	linux-serial, linux-api, 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,
	yangbo lu, Kevin Hao, Ben Hutchings

On Thu, Jan 14, 2016 at 09:10:09AM +0100, Thomas Gleixner wrote:

> I like that approach.
> 
> So except for the nit in pic32_set_type_edge() this looks good. It's pretty
> clear now what the code does and how the hardware works.
> 
> Thanks for following up!

So I take that for an ack.  I still haven't seen any acks or comments for
the patches:

     4/14      drivers/clk
     8/14      drivers/pinctrl
    10/14      drivers/serial
    12/14      drivers/mmc

Of this series.  Ping?

  Ralf

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

* Re: [PATCH v5 02/14] irqchip: irq-pic32-evic: Add support for PIC32 interrupt controller
  2016-01-14  8:10   ` Thomas Gleixner
  2016-01-14 11:12       ` Ralf Baechle
@ 2016-01-14 11:12     ` Ralf Baechle
  1 sibling, 0 replies; 66+ messages in thread
From: Ralf Baechle @ 2016-01-14 11:12 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Joshua Henderson, linux-kernel, linux-mips, Cristian Birsan,
	Jason Cooper, Marc Zyngier, Purna Chandra Mandal,
	Michael Turquette, Stephen Boyd, linux-clk, Linus Walleij,
	linux-gpio, Andrei Pistirica, Greg Kroah-Hartman, Jiri Slaby,
	linux-serial, linux-api, Ulf Hansson, Jean Delvare,
	Geert Uytterhoeven, Corneliu Doban, Haojian Zhuang,
	Luis de Bethencourt

On Thu, Jan 14, 2016 at 09:10:09AM +0100, Thomas Gleixner wrote:

> I like that approach.
> 
> So except for the nit in pic32_set_type_edge() this looks good. It's pretty
> clear now what the code does and how the hardware works.
> 
> Thanks for following up!

So I take that for an ack.  I still haven't seen any acks or comments for
the patches:

     4/14      drivers/clk
     8/14      drivers/pinctrl
    10/14      drivers/serial
    12/14      drivers/mmc

Of this series.  Ping?

  Ralf

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

* Re: [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
@ 2016-01-14 13:55     ` One Thousand Gnomes
  0 siblings, 0 replies; 66+ messages in thread
From: One Thousand Gnomes @ 2016-01-14 13:55 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, linux-mips, ralf, Andrei Pistirica,
	Greg Kroah-Hartman, Jiri Slaby, linux-serial, linux-api

On Wed, 13 Jan 2016 18:15:43 -0700
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>

Reviewed-by: Alan Cox <alan@linux.intel.com>

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

* Re: [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
@ 2016-01-14 13:55     ` One Thousand Gnomes
  0 siblings, 0 replies; 66+ messages in thread
From: One Thousand Gnomes @ 2016-01-14 13:55 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA, ralf-6z/3iImG2C8G8FEW9MqTrA,
	Andrei Pistirica, Greg Kroah-Hartman, Jiri Slaby,
	linux-serial-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA

On Wed, 13 Jan 2016 18:15:43 -0700
Joshua Henderson <joshua.henderson-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org> wrote:

> From: Andrei Pistirica <andrei.pistirica-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
> 
> This adds UART and a serial console driver for Microchip PIC32 class
> devices.
> 
> Signed-off-by: Andrei Pistirica <andrei.pistirica-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
> Signed-off-by: Joshua Henderson <joshua.henderson-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
> Cc: Ralf Baechle <ralf-6z/3iImG2C8G8FEW9MqTrA@public.gmane.org>

Reviewed-by: Alan Cox <alan-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>

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

* Re: [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
@ 2016-01-14 13:55     ` One Thousand Gnomes
  0 siblings, 0 replies; 66+ messages in thread
From: One Thousand Gnomes @ 2016-01-14 13:55 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, linux-mips, ralf, Andrei Pistirica,
	Greg Kroah-Hartman, Jiri Slaby, linux-serial, linux-api

On Wed, 13 Jan 2016 18:15:43 -0700
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>

Reviewed-by: Alan Cox <alan@linux.intel.com>

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

* Re: [PATCH v5 01/14] dt/bindings: Add bindings for PIC32 interrupt controller
  2016-01-14  1:15   ` Joshua Henderson
  (?)
@ 2016-01-17  3:57   ` Rob Herring
  -1 siblings, 0 replies; 66+ messages in thread
From: Rob Herring @ 2016-01-17  3:57 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 Wed, Jan 13, 2016 at 06:15:34PM -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>
> ---
> Changes since v4:
> 	- Add new microchip,external-interrupts property
> 	- Provide a better description of some of the features
> 	- Clean up formatting
> Changes since v3: None
> Changes since v2: None
> Changes since v1:
> 	- Remove hardware interrupt priorities from interrupt controller DT
> 	  bindings.
> ---
>  .../interrupt-controller/microchip,pic32-evic.txt  |   67 ++++++++++++++++++++
>  1 file changed, 67 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/microchip,pic32-evic.txt

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

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

* Re: [PATCH v5 04/14] clk: clk-pic32: Add PIC32 clock driver
@ 2016-01-26 17:04     ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-26 17:04 UTC (permalink / raw)
  To: linux-kernel, Michael Turquette, Stephen Boyd
  Cc: linux-mips, ralf, Purna Chandra Mandal, linux-clk

Hi Michael and Stephen,

Ping! Need an ack for this or pull it upstream.  DT bindings have long been acked.

On 01/13/2016 06:15 PM, Joshua Henderson wrote:
> 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>
> Cc: Michael Turquette <mturquette@baylibre.com>
> Cc: Stephen Boyd <sboyd@codeaurora.org>
> ---
> Changes from v4: None
> Changes from v3: None
> Changes from v2:
> 	- Replace __clk_debug with pr_debug
> 	- Add of_clk_parent_fill usage in PIC32 clock driver
> Changes from v1:
> 	- Remove unused PIC32 MPLL support.
> 	- Remove support for initializing default parent/rate for REFOSC
> 	  clocks.
> ---
>  drivers/clk/Kconfig     |    3 +
>  drivers/clk/Makefile    |    1 +
>  drivers/clk/clk-pic32.c | 1801 +++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1805 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..9dc5f78
> --- /dev/null
> +++ b/drivers/clk/clk-pic32.c
> @@ -0,0 +1,1801 @@
> +/*
> + * 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, 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;
> +
> +	of_clk_parent_fill(np, parents, count);
> +
> +	/* 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, 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;
> +
> +	of_clk_parent_fill(np, parents, count);
> +
> +	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 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;
> +
> +	of_clk_parent_fill(np, parent_names, count);
> +
> +	/* 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);
> 

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

* Re: [PATCH v5 04/14] clk: clk-pic32: Add PIC32 clock driver
@ 2016-01-26 17:04     ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-26 17:04 UTC (permalink / raw)
  To: linux-kernel, Michael Turquette, Stephen Boyd
  Cc: linux-mips, ralf, Purna Chandra Mandal, linux-clk

Hi Michael and Stephen,

Ping! Need an ack for this or pull it upstream.  DT bindings have long been acked.

On 01/13/2016 06:15 PM, Joshua Henderson wrote:
> 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>
> Cc: Michael Turquette <mturquette@baylibre.com>
> Cc: Stephen Boyd <sboyd@codeaurora.org>
> ---
> Changes from v4: None
> Changes from v3: None
> Changes from v2:
> 	- Replace __clk_debug with pr_debug
> 	- Add of_clk_parent_fill usage in PIC32 clock driver
> Changes from v1:
> 	- Remove unused PIC32 MPLL support.
> 	- Remove support for initializing default parent/rate for REFOSC
> 	  clocks.
> ---
>  drivers/clk/Kconfig     |    3 +
>  drivers/clk/Makefile    |    1 +
>  drivers/clk/clk-pic32.c | 1801 +++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1805 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..9dc5f78
> --- /dev/null
> +++ b/drivers/clk/clk-pic32.c
> @@ -0,0 +1,1801 @@
> +/*
> + * 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, 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;
> +
> +	of_clk_parent_fill(np, parents, count);
> +
> +	/* 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, 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;
> +
> +	of_clk_parent_fill(np, parents, count);
> +
> +	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 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;
> +
> +	of_clk_parent_fill(np, parent_names, count);
> +
> +	/* 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);
> 

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

* Re: [PATCH v5 08/14] pinctrl: pinctrl-pic32: Add PIC32 pin control driver
  2016-01-14  1:15   ` Joshua Henderson
@ 2016-01-26 17:04     ` Joshua Henderson
  -1 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-26 17:04 UTC (permalink / raw)
  To: linux-kernel, Linus Walleij; +Cc: linux-mips, ralf, linux-gpio

Hi Linus,

Ping! Need an ack for this or pull it upstream.

On 01/13/2016 06:15 PM, Joshua Henderson wrote:
> 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>
> ---
> Changes since v4: None
> Changes since v3: None
> Changes since v2:
> 	- Remove pinctrl pins that are not port pins
> Changes since v1:
> 	- Complete rewrite to simplify and use standard bindings.
> ---
>  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..e814ae7 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -238,6 +238,23 @@ config PINCTRL_PALMAS
>  	  open drain configuration for the Palmas series devices like
>  	  TPS65913, TPS80036 etc.
>  
> +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
> +
>  config PINCTRL_ZYNQ
>  	bool "Pinctrl driver for Xilinx Zynq"
>  	depends on ARCH_ZYNQ
> 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 */
> 


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

* Re: [PATCH v5 08/14] pinctrl: pinctrl-pic32: Add PIC32 pin control driver
@ 2016-01-26 17:04     ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-26 17:04 UTC (permalink / raw)
  To: linux-kernel, Linus Walleij; +Cc: linux-mips, ralf, linux-gpio

Hi Linus,

Ping! Need an ack for this or pull it upstream.

On 01/13/2016 06:15 PM, Joshua Henderson wrote:
> 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>
> ---
> Changes since v4: None
> Changes since v3: None
> Changes since v2:
> 	- Remove pinctrl pins that are not port pins
> Changes since v1:
> 	- Complete rewrite to simplify and use standard bindings.
> ---
>  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..e814ae7 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -238,6 +238,23 @@ config PINCTRL_PALMAS
>  	  open drain configuration for the Palmas series devices like
>  	  TPS65913, TPS80036 etc.
>  
> +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
> +
>  config PINCTRL_ZYNQ
>  	bool "Pinctrl driver for Xilinx Zynq"
>  	depends on ARCH_ZYNQ
> 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 */
> 

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

* Re: [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
  2016-01-14  1:15   ` Joshua Henderson
@ 2016-01-26 17:04     ` Joshua Henderson
  -1 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-26 17:04 UTC (permalink / raw)
  To: linux-kernel, Greg Kroah-Hartman, Jiri Slaby
  Cc: linux-mips, ralf, Andrei Pistirica, linux-serial, linux-api

Hi Greg and Jiri,

Ping!  Need an ack for this or pull it upstream.

On 01/13/2016 06:15 PM, Joshua Henderson 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>
> ---
> Changes since v4:
> 	- Clean up irq alloc on failure
> 	- Fix rework regression with handling failure in probe
> Changes since v3: None
> Changes since v2:
> 	- Use dynamic major/minor and ttyPIC* instead of ttyS*
> 	- Remove unused header files
> 	- Refactor register read/write functions
> 	- Reorder arguments to readl/writel functions
> 	- Add missing initializations to termios
> 	- Fix clk enable/disable mismatch
> Changes since v1:
> 	- Rename all instances of "USART" to "UART"
> 	- Remove unecessary uart-has-rtscts support
> ---
>  drivers/tty/serial/Kconfig       |   21 +
>  drivers/tty/serial/Makefile      |    1 +
>  drivers/tty/serial/pic32_uart.c  |  960 ++++++++++++++++++++++++++++++++++++++
>  drivers/tty/serial/pic32_uart.h  |  126 +++++
>  include/uapi/linux/serial_core.h |    3 +
>  5 files changed, 1111 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..62a43bf
> --- /dev/null
> +++ b/drivers/tty/serial/pic32_uart.c
> @@ -0,0 +1,960 @@
> +/*
> + * PIC32 Integrated Serial Driver.
> + *
> + * Copyright (C) 2015 Microchip Technology, Inc.
> + *
> + * Authors:
> + *   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.h>
> +#include <linux/tty.h>
> +#include <linux/tty_flip.h>
> +#include <linux/serial_core.h>
> +#include <linux/delay.h>
> +
> +#include <asm/mach-pic32/pic32.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		"ttyPIC"
> +
> +/* pic32_sport pointer for console use */
> +static struct pic32_sport *pic32_sports[PIC32_MAX_UARTS];
> +
> +static inline void pic32_wait_deplete_txbuf(struct pic32_sport *sport)
> +{
> +	/* wait for tx empty, otherwise chars will be lost or corrupted */
> +	while (!(pic32_uart_readl(sport, PIC32_UART_STA) & PIC32_UART_STA_TRMT))
> +		udelay(1);
> +}
> +
> +static inline int pic32_enable_clock(struct pic32_sport *sport)
> +{
> +	int ret = clk_prepare_enable(sport->clk);
> +
> +	if (ret)
> +		return ret;
> +
> +	sport->ref_clk++;
> +	return 0;
> +}
> +
> +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_readl(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_writel(sport, PIC32_SET(PIC32_UART_MODE),
> +					PIC32_UART_MODE_LPBK);
> +	else
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_LPBK);
> +}
> +
> +/* get the state of CTS input pin for this port */
> +static unsigned int get_cts_state(struct pic32_sport *sport)
> +{
> +	/* read and invert UxCTS */
> +	if (gpio_is_valid(sport->cts_gpio))
> +		return !gpio_get_value(sport->cts_gpio);
> +
> +	return 1;
> +}
> +
> +/* 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;
> +	else if (get_cts_state(sport))
> +		mctrl |= TIOCM_CTS;
> +
> +	/* 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_readl(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON))
> +		return;
> +
> +	if (!(pic32_uart_readl(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN))
> +		return;
> +
> +	/* wait for tx empty */
> +	pic32_wait_deplete_txbuf(sport);
> +
> +	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
> +				PIC32_UART_STA_UTXEN);
> +	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_writel(sport, PIC32_SET(PIC32_UART_STA),
> +				PIC32_UART_STA_UTXEN);
> +}
> +
> +/* 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_writel(sport, PIC32_CLR(PIC32_UART_STA),
> +				PIC32_UART_STA_URXEN);
> +}
> +
> +/* 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;
> +
> +	spin_lock_irqsave(&port->lock, flags);
> +
> +	if (ctl)
> +		pic32_uart_writel(sport, PIC32_SET(PIC32_UART_STA),
> +					PIC32_UART_STA_UTXBRK);
> +	else
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
> +					PIC32_UART_STA_UTXBRK);
> +
> +	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_readl(sport, PIC32_UART_STA);
> +		if (unlikely(sta_reg & PIC32_UART_STA_OERR)) {
> +
> +			/* fifo reset is required to clear interrupt */
> +			pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
> +						PIC32_UART_STA_OERR);
> +
> +			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_readl(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_writel(sport, PIC32_UART_TX, port->x_char);
> +		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_readl(sport, PIC32_UART_STA))) {
> +		unsigned int c = xmit->buf[xmit->tail];
> +
> +		pic32_uart_writel(sport, PIC32_UART_TX, c);
> +
> +		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> +		port->icount.tx++;
> +		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_writel(sport, PIC32_SET(PIC32_UART_STA),
> +				PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN);
> +	pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE),
> +				PIC32_UART_MODE_ON);
> +}
> +
> +/* 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);
> +
> +	/* wait for tx empty, otherwise chars will be lost or corrupted */
> +	pic32_wait_deplete_txbuf(sport);
> +
> +	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
> +				PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN);
> +	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +				PIC32_UART_MODE_ON);
> +}
> +
> +/* 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;
> +
> +	local_irq_save(flags);
> +
> +	ret = pic32_enable_clock(sport);
> +	if (ret) {
> +		local_irq_restore(flags);
> +		goto out_done;
> +	}
> +
> +	/* clear status and mode registers */
> +	pic32_uart_writel(sport, PIC32_UART_MODE, 0);
> +	pic32_uart_writel(sport, PIC32_UART_STA, 0);
> +
> +	/* disable uart and mask all interrupts */
> +	pic32_uart_dsbl_and_mask(port);
> +
> +	/* set default baud */
> +	pic32_uart_writel(sport, PIC32_UART_BRG, dflt_baud);
> +
> +	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);
> +	if (!sport->irq_fault_name) {
> +		dev_err(port->dev, "%s: kasprintf err!", __func__);
> +		ret = -ENOMEM;
> +		goto out_done;
> +	}
> +	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_f;
> +	}
> +
> +	sport->irq_rx_name = kasprintf(GFP_KERNEL, "%s%d-rx",
> +				       pic32_uart_type(port),
> +				       sport->idx);
> +	if (!sport->irq_rx_name) {
> +		dev_err(port->dev, "%s: kasprintf err!", __func__);
> +		kfree(sport->irq_fault_name);
> +		ret = -ENOMEM;
> +		goto out_f;
> +	}
> +	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_r;
> +	}
> +
> +	sport->irq_tx_name = kasprintf(GFP_KERNEL, "%s%d-tx",
> +				       pic32_uart_type(port),
> +				       sport->idx);
> +	if (!sport->irq_tx_name) {
> +		dev_err(port->dev, "%s: kasprintf err!", __func__);
> +		ret = -ENOMEM;
> +		goto out_r;
> +	}
> +	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_t;
> +	}
> +
> +	local_irq_save(flags);
> +
> +	/* set rx interrupt on first receive */
> +	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
> +			PIC32_UART_STA_URXISEL1 | PIC32_UART_STA_URXISEL0);
> +
> +	/* set interrupt on empty */
> +	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
> +			PIC32_UART_STA_UTXISEL1);
> +
> +	/* enable all interrupts and eanable uart */
> +	pic32_uart_en_and_unmask(port);
> +
> +	enable_irq(sport->irq_rx);
> +
> +	return 0;
> +
> +out_t:
> +	kfree(sport->irq_tx_name);
> +	free_irq(sport->irq_tx, sport);
> +out_r:
> +	kfree(sport->irq_rx_name);
> +	free_irq(sport->irq_rx, sport);
> +out_f:
> +	kfree(sport->irq_fault_name);
> +	free_irq(sport->irq_fault, sport);
> +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_writel(sport, PIC32_SET(PIC32_UART_MODE),
> +					PIC32_UART_MODE_STSEL);
> +	else
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_STSEL);
> +
> +	/* parity options */
> +	if (new->c_cflag & PARENB) {
> +		if (new->c_cflag & PARODD) {
> +			pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE),
> +					PIC32_UART_MODE_PDSEL1);
> +			pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_PDSEL0);
> +		} else {
> +			pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE),
> +					PIC32_UART_MODE_PDSEL0);
> +			pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_PDSEL1);
> +		}
> +	} else {
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_PDSEL1 |
> +					PIC32_UART_MODE_PDSEL0);
> +	}
> +	/* 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_writel(sport, PIC32_SET(PIC32_UART_MODE),
> +					PIC32_UART_MODE_UEN1);
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_UEN0);
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_RTSMD);
> +	} else {
> +		/* disable hardware flow control */
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_UEN1);
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_UEN0);
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_RTSMD);
> +	}
> +
> +	/* Always 8-bit */
> +	new->c_cflag |= CS8;
> +
> +	/* Mark/Space parity is not supported */
> +	new->c_cflag &= ~CMSPAR;
> +
> +	/* update baud */
> +	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
> +	quot = uart_get_divisor(port, baud) - 1;
> +	pic32_uart_writel(sport, PIC32_UART_BRG, quot);
> +	uart_update_timeout(port, new->c_cflag, baud);
> +
> +	if (tty_termios_baud_rate(new))
> +		tty_termios_encode_baud_rate(new, baud, 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;
> +
> +	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (unlikely(!res_mem))
> +		return -EINVAL;
> +
> +	if (!request_mem_region(port->mapbase, resource_size(res_mem),
> +				"pic32_uart_mem"))
> +		return -EBUSY;
> +
> +	port->membase = devm_ioremap_nocache(port->dev, port->mapbase,
> +						resource_size(res_mem));
> +	if (!port->membase) {
> +		dev_err(port->dev, "Unable to map registers\n");
> +		release_mem_region(port->mapbase, resource_size(res_mem));
> +		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);
> +}
> +
> +/* 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_readl(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON))
> +		return;
> +
> +	if (!(pic32_uart_readl(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN))
> +		return;
> +
> +	/* wait for tx empty */
> +	pic32_wait_deplete_txbuf(sport);
> +
> +	pic32_uart_writel(sport, PIC32_UART_TX, ch & 0xff);
> +}
> +
> +/* 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,
> +	.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;
> +
> +	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;
> +
> +	/* 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;
> +		}
> +
> +		ret = gpio_direction_input(sport->cts_gpio);
> +		if (ret) {
> +			dev_err(&pdev->dev, "error setting CTS GPIO\n");
> +			goto err;
> +		}
> +	}
> +
> +	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;
> +	}
> +
> +#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);
> +
> +	return 0;
> +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,
> +		.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("Sorin-Andrei Pistirica <andrei.pistirica@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..ec379da
> --- /dev/null
> +++ b/drivers/tty/serial/pic32_uart.h
> @@ -0,0 +1,126 @@
> +/*
> + * 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)
> +
> +#define PIC32_UART_MODE		0x00
> +#define PIC32_UART_STA		0x10
> +#define PIC32_UART_TX		0x20
> +#define PIC32_UART_RX		0x30
> +#define PIC32_UART_BRG		0x40
> +
> +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)
> +
> +static inline void pic32_uart_writel(struct pic32_sport *sport,
> +					u32 reg, u32 val)
> +{
> +	struct uart_port *port = pic32_get_port(sport);
> +
> +	__raw_writel(val, port->membase + reg);
> +}
> +
> +static inline u32 pic32_uart_readl(struct pic32_sport *sport, u32 reg)
> +{
> +	struct uart_port *port = pic32_get_port(sport);
> +
> +	return	__raw_readl(port->membase + reg);
> +}
> +
> +/* pic32 uart mode register bits */
> +#define PIC32_UART_MODE_ON        BIT(15)
> +#define PIC32_UART_MODE_FRZ       BIT(14)
> +#define PIC32_UART_MODE_SIDL      BIT(13)
> +#define PIC32_UART_MODE_IREN      BIT(12)
> +#define PIC32_UART_MODE_RTSMD     BIT(11)
> +#define PIC32_UART_MODE_RESV1     BIT(10)
> +#define PIC32_UART_MODE_UEN1      BIT(9)
> +#define PIC32_UART_MODE_UEN0      BIT(8)
> +#define PIC32_UART_MODE_WAKE      BIT(7)
> +#define PIC32_UART_MODE_LPBK      BIT(6)
> +#define PIC32_UART_MODE_ABAUD     BIT(5)
> +#define PIC32_UART_MODE_RXINV     BIT(4)
> +#define PIC32_UART_MODE_BRGH      BIT(3)
> +#define PIC32_UART_MODE_PDSEL1    BIT(2)
> +#define PIC32_UART_MODE_PDSEL0    BIT(1)
> +#define PIC32_UART_MODE_STSEL     BIT(0)
> +
> +/* pic32 uart status register bits */
> +#define PIC32_UART_STA_UTXISEL1   BIT(15)
> +#define PIC32_UART_STA_UTXISEL0   BIT(14)
> +#define PIC32_UART_STA_UTXINV     BIT(13)
> +#define PIC32_UART_STA_URXEN      BIT(12)
> +#define PIC32_UART_STA_UTXBRK     BIT(11)
> +#define PIC32_UART_STA_UTXEN      BIT(10)
> +#define PIC32_UART_STA_UTXBF      BIT(9)
> +#define PIC32_UART_STA_TRMT       BIT(8)
> +#define PIC32_UART_STA_URXISEL1   BIT(7)
> +#define PIC32_UART_STA_URXISEL0   BIT(6)
> +#define PIC32_UART_STA_ADDEN      BIT(5)
> +#define PIC32_UART_STA_RIDLE      BIT(4)
> +#define PIC32_UART_STA_PERR       BIT(3)
> +#define PIC32_UART_STA_FERR       BIT(2)
> +#define PIC32_UART_STA_OERR       BIT(1)
> +#define PIC32_UART_STA_URXDA      BIT(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 */
> 

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

* Re: [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
@ 2016-01-26 17:04     ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-26 17:04 UTC (permalink / raw)
  To: linux-kernel, Greg Kroah-Hartman, Jiri Slaby
  Cc: linux-mips, ralf, Andrei Pistirica, linux-serial, linux-api

Hi Greg and Jiri,

Ping!  Need an ack for this or pull it upstream.

On 01/13/2016 06:15 PM, Joshua Henderson 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>
> ---
> Changes since v4:
> 	- Clean up irq alloc on failure
> 	- Fix rework regression with handling failure in probe
> Changes since v3: None
> Changes since v2:
> 	- Use dynamic major/minor and ttyPIC* instead of ttyS*
> 	- Remove unused header files
> 	- Refactor register read/write functions
> 	- Reorder arguments to readl/writel functions
> 	- Add missing initializations to termios
> 	- Fix clk enable/disable mismatch
> Changes since v1:
> 	- Rename all instances of "USART" to "UART"
> 	- Remove unecessary uart-has-rtscts support
> ---
>  drivers/tty/serial/Kconfig       |   21 +
>  drivers/tty/serial/Makefile      |    1 +
>  drivers/tty/serial/pic32_uart.c  |  960 ++++++++++++++++++++++++++++++++++++++
>  drivers/tty/serial/pic32_uart.h  |  126 +++++
>  include/uapi/linux/serial_core.h |    3 +
>  5 files changed, 1111 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..62a43bf
> --- /dev/null
> +++ b/drivers/tty/serial/pic32_uart.c
> @@ -0,0 +1,960 @@
> +/*
> + * PIC32 Integrated Serial Driver.
> + *
> + * Copyright (C) 2015 Microchip Technology, Inc.
> + *
> + * Authors:
> + *   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.h>
> +#include <linux/tty.h>
> +#include <linux/tty_flip.h>
> +#include <linux/serial_core.h>
> +#include <linux/delay.h>
> +
> +#include <asm/mach-pic32/pic32.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		"ttyPIC"
> +
> +/* pic32_sport pointer for console use */
> +static struct pic32_sport *pic32_sports[PIC32_MAX_UARTS];
> +
> +static inline void pic32_wait_deplete_txbuf(struct pic32_sport *sport)
> +{
> +	/* wait for tx empty, otherwise chars will be lost or corrupted */
> +	while (!(pic32_uart_readl(sport, PIC32_UART_STA) & PIC32_UART_STA_TRMT))
> +		udelay(1);
> +}
> +
> +static inline int pic32_enable_clock(struct pic32_sport *sport)
> +{
> +	int ret = clk_prepare_enable(sport->clk);
> +
> +	if (ret)
> +		return ret;
> +
> +	sport->ref_clk++;
> +	return 0;
> +}
> +
> +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_readl(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_writel(sport, PIC32_SET(PIC32_UART_MODE),
> +					PIC32_UART_MODE_LPBK);
> +	else
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_LPBK);
> +}
> +
> +/* get the state of CTS input pin for this port */
> +static unsigned int get_cts_state(struct pic32_sport *sport)
> +{
> +	/* read and invert UxCTS */
> +	if (gpio_is_valid(sport->cts_gpio))
> +		return !gpio_get_value(sport->cts_gpio);
> +
> +	return 1;
> +}
> +
> +/* 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;
> +	else if (get_cts_state(sport))
> +		mctrl |= TIOCM_CTS;
> +
> +	/* 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_readl(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON))
> +		return;
> +
> +	if (!(pic32_uart_readl(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN))
> +		return;
> +
> +	/* wait for tx empty */
> +	pic32_wait_deplete_txbuf(sport);
> +
> +	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
> +				PIC32_UART_STA_UTXEN);
> +	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_writel(sport, PIC32_SET(PIC32_UART_STA),
> +				PIC32_UART_STA_UTXEN);
> +}
> +
> +/* 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_writel(sport, PIC32_CLR(PIC32_UART_STA),
> +				PIC32_UART_STA_URXEN);
> +}
> +
> +/* 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;
> +
> +	spin_lock_irqsave(&port->lock, flags);
> +
> +	if (ctl)
> +		pic32_uart_writel(sport, PIC32_SET(PIC32_UART_STA),
> +					PIC32_UART_STA_UTXBRK);
> +	else
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
> +					PIC32_UART_STA_UTXBRK);
> +
> +	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_readl(sport, PIC32_UART_STA);
> +		if (unlikely(sta_reg & PIC32_UART_STA_OERR)) {
> +
> +			/* fifo reset is required to clear interrupt */
> +			pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
> +						PIC32_UART_STA_OERR);
> +
> +			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_readl(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_writel(sport, PIC32_UART_TX, port->x_char);
> +		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_readl(sport, PIC32_UART_STA))) {
> +		unsigned int c = xmit->buf[xmit->tail];
> +
> +		pic32_uart_writel(sport, PIC32_UART_TX, c);
> +
> +		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> +		port->icount.tx++;
> +		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_writel(sport, PIC32_SET(PIC32_UART_STA),
> +				PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN);
> +	pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE),
> +				PIC32_UART_MODE_ON);
> +}
> +
> +/* 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);
> +
> +	/* wait for tx empty, otherwise chars will be lost or corrupted */
> +	pic32_wait_deplete_txbuf(sport);
> +
> +	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
> +				PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN);
> +	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +				PIC32_UART_MODE_ON);
> +}
> +
> +/* 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;
> +
> +	local_irq_save(flags);
> +
> +	ret = pic32_enable_clock(sport);
> +	if (ret) {
> +		local_irq_restore(flags);
> +		goto out_done;
> +	}
> +
> +	/* clear status and mode registers */
> +	pic32_uart_writel(sport, PIC32_UART_MODE, 0);
> +	pic32_uart_writel(sport, PIC32_UART_STA, 0);
> +
> +	/* disable uart and mask all interrupts */
> +	pic32_uart_dsbl_and_mask(port);
> +
> +	/* set default baud */
> +	pic32_uart_writel(sport, PIC32_UART_BRG, dflt_baud);
> +
> +	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);
> +	if (!sport->irq_fault_name) {
> +		dev_err(port->dev, "%s: kasprintf err!", __func__);
> +		ret = -ENOMEM;
> +		goto out_done;
> +	}
> +	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_f;
> +	}
> +
> +	sport->irq_rx_name = kasprintf(GFP_KERNEL, "%s%d-rx",
> +				       pic32_uart_type(port),
> +				       sport->idx);
> +	if (!sport->irq_rx_name) {
> +		dev_err(port->dev, "%s: kasprintf err!", __func__);
> +		kfree(sport->irq_fault_name);
> +		ret = -ENOMEM;
> +		goto out_f;
> +	}
> +	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_r;
> +	}
> +
> +	sport->irq_tx_name = kasprintf(GFP_KERNEL, "%s%d-tx",
> +				       pic32_uart_type(port),
> +				       sport->idx);
> +	if (!sport->irq_tx_name) {
> +		dev_err(port->dev, "%s: kasprintf err!", __func__);
> +		ret = -ENOMEM;
> +		goto out_r;
> +	}
> +	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_t;
> +	}
> +
> +	local_irq_save(flags);
> +
> +	/* set rx interrupt on first receive */
> +	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
> +			PIC32_UART_STA_URXISEL1 | PIC32_UART_STA_URXISEL0);
> +
> +	/* set interrupt on empty */
> +	pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA),
> +			PIC32_UART_STA_UTXISEL1);
> +
> +	/* enable all interrupts and eanable uart */
> +	pic32_uart_en_and_unmask(port);
> +
> +	enable_irq(sport->irq_rx);
> +
> +	return 0;
> +
> +out_t:
> +	kfree(sport->irq_tx_name);
> +	free_irq(sport->irq_tx, sport);
> +out_r:
> +	kfree(sport->irq_rx_name);
> +	free_irq(sport->irq_rx, sport);
> +out_f:
> +	kfree(sport->irq_fault_name);
> +	free_irq(sport->irq_fault, sport);
> +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_writel(sport, PIC32_SET(PIC32_UART_MODE),
> +					PIC32_UART_MODE_STSEL);
> +	else
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_STSEL);
> +
> +	/* parity options */
> +	if (new->c_cflag & PARENB) {
> +		if (new->c_cflag & PARODD) {
> +			pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE),
> +					PIC32_UART_MODE_PDSEL1);
> +			pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_PDSEL0);
> +		} else {
> +			pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE),
> +					PIC32_UART_MODE_PDSEL0);
> +			pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_PDSEL1);
> +		}
> +	} else {
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_PDSEL1 |
> +					PIC32_UART_MODE_PDSEL0);
> +	}
> +	/* 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_writel(sport, PIC32_SET(PIC32_UART_MODE),
> +					PIC32_UART_MODE_UEN1);
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_UEN0);
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_RTSMD);
> +	} else {
> +		/* disable hardware flow control */
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_UEN1);
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_UEN0);
> +		pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE),
> +					PIC32_UART_MODE_RTSMD);
> +	}
> +
> +	/* Always 8-bit */
> +	new->c_cflag |= CS8;
> +
> +	/* Mark/Space parity is not supported */
> +	new->c_cflag &= ~CMSPAR;
> +
> +	/* update baud */
> +	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
> +	quot = uart_get_divisor(port, baud) - 1;
> +	pic32_uart_writel(sport, PIC32_UART_BRG, quot);
> +	uart_update_timeout(port, new->c_cflag, baud);
> +
> +	if (tty_termios_baud_rate(new))
> +		tty_termios_encode_baud_rate(new, baud, 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;
> +
> +	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (unlikely(!res_mem))
> +		return -EINVAL;
> +
> +	if (!request_mem_region(port->mapbase, resource_size(res_mem),
> +				"pic32_uart_mem"))
> +		return -EBUSY;
> +
> +	port->membase = devm_ioremap_nocache(port->dev, port->mapbase,
> +						resource_size(res_mem));
> +	if (!port->membase) {
> +		dev_err(port->dev, "Unable to map registers\n");
> +		release_mem_region(port->mapbase, resource_size(res_mem));
> +		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);
> +}
> +
> +/* 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_readl(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON))
> +		return;
> +
> +	if (!(pic32_uart_readl(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN))
> +		return;
> +
> +	/* wait for tx empty */
> +	pic32_wait_deplete_txbuf(sport);
> +
> +	pic32_uart_writel(sport, PIC32_UART_TX, ch & 0xff);
> +}
> +
> +/* 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,
> +	.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;
> +
> +	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;
> +
> +	/* 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;
> +		}
> +
> +		ret = gpio_direction_input(sport->cts_gpio);
> +		if (ret) {
> +			dev_err(&pdev->dev, "error setting CTS GPIO\n");
> +			goto err;
> +		}
> +	}
> +
> +	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;
> +	}
> +
> +#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);
> +
> +	return 0;
> +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,
> +		.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("Sorin-Andrei Pistirica <andrei.pistirica@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..ec379da
> --- /dev/null
> +++ b/drivers/tty/serial/pic32_uart.h
> @@ -0,0 +1,126 @@
> +/*
> + * 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)
> +
> +#define PIC32_UART_MODE		0x00
> +#define PIC32_UART_STA		0x10
> +#define PIC32_UART_TX		0x20
> +#define PIC32_UART_RX		0x30
> +#define PIC32_UART_BRG		0x40
> +
> +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)
> +
> +static inline void pic32_uart_writel(struct pic32_sport *sport,
> +					u32 reg, u32 val)
> +{
> +	struct uart_port *port = pic32_get_port(sport);
> +
> +	__raw_writel(val, port->membase + reg);
> +}
> +
> +static inline u32 pic32_uart_readl(struct pic32_sport *sport, u32 reg)
> +{
> +	struct uart_port *port = pic32_get_port(sport);
> +
> +	return	__raw_readl(port->membase + reg);
> +}
> +
> +/* pic32 uart mode register bits */
> +#define PIC32_UART_MODE_ON        BIT(15)
> +#define PIC32_UART_MODE_FRZ       BIT(14)
> +#define PIC32_UART_MODE_SIDL      BIT(13)
> +#define PIC32_UART_MODE_IREN      BIT(12)
> +#define PIC32_UART_MODE_RTSMD     BIT(11)
> +#define PIC32_UART_MODE_RESV1     BIT(10)
> +#define PIC32_UART_MODE_UEN1      BIT(9)
> +#define PIC32_UART_MODE_UEN0      BIT(8)
> +#define PIC32_UART_MODE_WAKE      BIT(7)
> +#define PIC32_UART_MODE_LPBK      BIT(6)
> +#define PIC32_UART_MODE_ABAUD     BIT(5)
> +#define PIC32_UART_MODE_RXINV     BIT(4)
> +#define PIC32_UART_MODE_BRGH      BIT(3)
> +#define PIC32_UART_MODE_PDSEL1    BIT(2)
> +#define PIC32_UART_MODE_PDSEL0    BIT(1)
> +#define PIC32_UART_MODE_STSEL     BIT(0)
> +
> +/* pic32 uart status register bits */
> +#define PIC32_UART_STA_UTXISEL1   BIT(15)
> +#define PIC32_UART_STA_UTXISEL0   BIT(14)
> +#define PIC32_UART_STA_UTXINV     BIT(13)
> +#define PIC32_UART_STA_URXEN      BIT(12)
> +#define PIC32_UART_STA_UTXBRK     BIT(11)
> +#define PIC32_UART_STA_UTXEN      BIT(10)
> +#define PIC32_UART_STA_UTXBF      BIT(9)
> +#define PIC32_UART_STA_TRMT       BIT(8)
> +#define PIC32_UART_STA_URXISEL1   BIT(7)
> +#define PIC32_UART_STA_URXISEL0   BIT(6)
> +#define PIC32_UART_STA_ADDEN      BIT(5)
> +#define PIC32_UART_STA_RIDLE      BIT(4)
> +#define PIC32_UART_STA_PERR       BIT(3)
> +#define PIC32_UART_STA_FERR       BIT(2)
> +#define PIC32_UART_STA_OERR       BIT(1)
> +#define PIC32_UART_STA_URXDA      BIT(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 */
> 

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

* Re: [PATCH v5 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver
@ 2016-01-26 17:04     ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-26 17:04 UTC (permalink / raw)
  To: linux-kernel, Ulf Hansson
  Cc: linux-mips, ralf, 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

Hi Ulf,

Ping! Need an ack for this or pull it upstream.

On 01/13/2016 06:15 PM, Joshua Henderson 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>
> ---
> Changes since v4: None
> Changes since v3: None
> Changes since v2:
> 	- Use 'sdhci_pltfm_*' instead of 'sdhci_*_host' and other cleanup
> Changes since v1:
> 	- Be consistent and use only "SDHCI" when referring to SD host
> 	  controller
> 	- Remove unnecessary PIC32 sdhci_ops min clock function.
> 	- Drop usage of piomode and no-1-8-v DT properties
>         - Formatting
>         - Fix use of devm_iounmap
>         - Address code comment
> ---
>  drivers/mmc/host/Kconfig       |   11 ++
>  drivers/mmc/host/Makefile      |    1 +
>  drivers/mmc/host/sdhci-pic32.c |  257 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 269 insertions(+)
>  create mode 100644 drivers/mmc/host/sdhci-pic32.c
> 
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 1dee533..c6a8916 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 && MMC_SDHCI_PLTFM
> +        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..059df70
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-pic32.c
> @@ -0,0 +1,257 @@
> +/*
> + * 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 "sdhci-pltfm.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
> +
> +struct pic32_sdhci_priv {
> +	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_priv *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;
> +	}
> +
> +	/* CD select and test bits must be set for errata workaround. */
> +	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 struct sdhci_pltfm_data sdhci_pic32_pdata = {
> +	.ops = &pic32_sdhci_ops,
> +	.quirks = SDHCI_QUIRK_NO_HISPD_BIT,
> +	.quirks2 = SDHCI_QUIRK2_NO_1_8_V,
> +};
> +
> +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 & 1)
> +		bus |= (1 << SDH_SHARED_BUS_CLK_PINS);
> +
> +	/* select first interrupt */
> +	if (irq_pins & 1)
> +		bus |= (1 << 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_priv *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 sdhci_host *host;
> +	struct sdhci_pltfm_host *pltfm_host;
> +	struct pic32_sdhci_priv *sdhci_pdata;
> +	struct pic32_sdhci_platform_data *plat_data;
> +	int ret;
> +
> +	host = sdhci_pltfm_init(pdev, &sdhci_pic32_pdata,
> +				sizeof(struct pic32_sdhci_priv));
> +	if (IS_ERR(host)) {
> +		ret = PTR_ERR(host);
> +		goto err;
> +	}
> +
> +	pltfm_host = sdhci_priv(host);
> +	sdhci_pdata = sdhci_pltfm_priv(pltfm_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;
> +	}
> +
> +	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;
> +	}
> +
> +	ret = clk_prepare_enable(sdhci_pdata->sys_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Error enabling clock\n");
> +		goto err_host;
> +	}
> +
> +	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_sys_clk;
> +	}
> +
> +	ret = clk_prepare_enable(sdhci_pdata->base_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Error enabling clock\n");
> +		goto err_base_clk;
> +	}
> +
> +	ret = mmc_of_parse(host->mmc);
> +	if (ret)
> +		goto err_base_clk;
> +
> +	ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to probe platform!\n");
> +		goto err_base_clk;
> +	}
> +
> +	ret = sdhci_add_host(host);
> +	if (ret) {
> +		dev_err(&pdev->dev, "error adding host\n");
> +		goto err_base_clk;
> +	}
> +
> +	dev_info(&pdev->dev, "Successfully added sdhci host\n");
> +	return 0;
> +
> +err_base_clk:
> +	clk_disable_unprepare(sdhci_pdata->base_clk);
> +err_sys_clk:
> +	clk_disable_unprepare(sdhci_pdata->sys_clk);
> +err_host:
> +	sdhci_pltfm_free(pdev);
> +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_priv *sdhci_pdata = sdhci_priv(host);
> +	u32 scratch;
> +
> +	scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
> +	sdhci_remove_host(host, scratch == (u32)~0);
> +	clk_disable_unprepare(sdhci_pdata->base_clk);
> +	clk_disable_unprepare(sdhci_pdata->sys_clk);
> +	sdhci_pltfm_free(pdev);
> +
> +	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	= "pic32-sdhci",
> +		.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");
> 

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

* Re: [PATCH v5 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver
@ 2016-01-26 17:04     ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-26 17:04 UTC (permalink / raw)
  To: linux-kernel, Ulf Hansson
  Cc: linux-mips, ralf, 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

Hi Ulf,

Ping! Need an ack for this or pull it upstream.

On 01/13/2016 06:15 PM, Joshua Henderson 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>
> ---
> Changes since v4: None
> Changes since v3: None
> Changes since v2:
> 	- Use 'sdhci_pltfm_*' instead of 'sdhci_*_host' and other cleanup
> Changes since v1:
> 	- Be consistent and use only "SDHCI" when referring to SD host
> 	  controller
> 	- Remove unnecessary PIC32 sdhci_ops min clock function.
> 	- Drop usage of piomode and no-1-8-v DT properties
>         - Formatting
>         - Fix use of devm_iounmap
>         - Address code comment
> ---
>  drivers/mmc/host/Kconfig       |   11 ++
>  drivers/mmc/host/Makefile      |    1 +
>  drivers/mmc/host/sdhci-pic32.c |  257 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 269 insertions(+)
>  create mode 100644 drivers/mmc/host/sdhci-pic32.c
> 
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 1dee533..c6a8916 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 && MMC_SDHCI_PLTFM
> +        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..059df70
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-pic32.c
> @@ -0,0 +1,257 @@
> +/*
> + * 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 "sdhci-pltfm.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
> +
> +struct pic32_sdhci_priv {
> +	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_priv *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;
> +	}
> +
> +	/* CD select and test bits must be set for errata workaround. */
> +	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 struct sdhci_pltfm_data sdhci_pic32_pdata = {
> +	.ops = &pic32_sdhci_ops,
> +	.quirks = SDHCI_QUIRK_NO_HISPD_BIT,
> +	.quirks2 = SDHCI_QUIRK2_NO_1_8_V,
> +};
> +
> +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 & 1)
> +		bus |= (1 << SDH_SHARED_BUS_CLK_PINS);
> +
> +	/* select first interrupt */
> +	if (irq_pins & 1)
> +		bus |= (1 << 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_priv *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 sdhci_host *host;
> +	struct sdhci_pltfm_host *pltfm_host;
> +	struct pic32_sdhci_priv *sdhci_pdata;
> +	struct pic32_sdhci_platform_data *plat_data;
> +	int ret;
> +
> +	host = sdhci_pltfm_init(pdev, &sdhci_pic32_pdata,
> +				sizeof(struct pic32_sdhci_priv));
> +	if (IS_ERR(host)) {
> +		ret = PTR_ERR(host);
> +		goto err;
> +	}
> +
> +	pltfm_host = sdhci_priv(host);
> +	sdhci_pdata = sdhci_pltfm_priv(pltfm_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;
> +	}
> +
> +	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;
> +	}
> +
> +	ret = clk_prepare_enable(sdhci_pdata->sys_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Error enabling clock\n");
> +		goto err_host;
> +	}
> +
> +	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_sys_clk;
> +	}
> +
> +	ret = clk_prepare_enable(sdhci_pdata->base_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Error enabling clock\n");
> +		goto err_base_clk;
> +	}
> +
> +	ret = mmc_of_parse(host->mmc);
> +	if (ret)
> +		goto err_base_clk;
> +
> +	ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to probe platform!\n");
> +		goto err_base_clk;
> +	}
> +
> +	ret = sdhci_add_host(host);
> +	if (ret) {
> +		dev_err(&pdev->dev, "error adding host\n");
> +		goto err_base_clk;
> +	}
> +
> +	dev_info(&pdev->dev, "Successfully added sdhci host\n");
> +	return 0;
> +
> +err_base_clk:
> +	clk_disable_unprepare(sdhci_pdata->base_clk);
> +err_sys_clk:
> +	clk_disable_unprepare(sdhci_pdata->sys_clk);
> +err_host:
> +	sdhci_pltfm_free(pdev);
> +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_priv *sdhci_pdata = sdhci_priv(host);
> +	u32 scratch;
> +
> +	scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
> +	sdhci_remove_host(host, scratch == (u32)~0);
> +	clk_disable_unprepare(sdhci_pdata->base_clk);
> +	clk_disable_unprepare(sdhci_pdata->sys_clk);
> +	sdhci_pltfm_free(pdev);
> +
> +	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	= "pic32-sdhci",
> +		.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");
> 

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

* Re: [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
@ 2016-01-26 17:33       ` Greg Kroah-Hartman
  0 siblings, 0 replies; 66+ messages in thread
From: Greg Kroah-Hartman @ 2016-01-26 17:33 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, Jiri Slaby, linux-mips, ralf, Andrei Pistirica,
	linux-serial, linux-api

On Tue, Jan 26, 2016 at 10:04:41AM -0700, Joshua Henderson wrote:
> Hi Greg and Jiri,
> 
> Ping!  Need an ack for this or pull it upstream.

The merge window _just_ ended, please give us a chance to catch up on
patches to be reviewed.  There's no reason you need a response for this
right away, it can't be merged until 4.6-rc1, right?

thanks,

greg k-h

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

* Re: [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
@ 2016-01-26 17:33       ` Greg Kroah-Hartman
  0 siblings, 0 replies; 66+ messages in thread
From: Greg Kroah-Hartman @ 2016-01-26 17:33 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Jiri Slaby,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA, ralf-6z/3iImG2C8G8FEW9MqTrA,
	Andrei Pistirica, linux-serial-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA

On Tue, Jan 26, 2016 at 10:04:41AM -0700, Joshua Henderson wrote:
> Hi Greg and Jiri,
> 
> Ping!  Need an ack for this or pull it upstream.

The merge window _just_ ended, please give us a chance to catch up on
patches to be reviewed.  There's no reason you need a response for this
right away, it can't be merged until 4.6-rc1, right?

thanks,

greg k-h

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

* Re: [PATCH v5 08/14] pinctrl: pinctrl-pic32: Add PIC32 pin control driver
  2016-01-14  1:15   ` Joshua Henderson
  (?)
  (?)
@ 2016-01-27 13:49   ` Linus Walleij
  2016-01-28  0:33     ` Joshua Henderson
  -1 siblings, 1 reply; 66+ messages in thread
From: Linus Walleij @ 2016-01-27 13:49 UTC (permalink / raw)
  To: Joshua Henderson; +Cc: linux-kernel, Linux MIPS, Ralf Baechle, linux-gpio

On Thu, Jan 14, 2016 at 2:15 AM, Joshua Henderson
<joshua.henderson@microchip.com> wrote:

> 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>
> ---
> Changes since v4: None

Sorry for the delay. Fell between the cracks.

Overall this looks nice. Small things that need to be fixed below.

Is it possible to merge this patch separately into the pinctrl tree?

I was hoping there are no compile-time dependencies to the
MIPS tree so I can just merge this. Else I can provide some
kind of immutable branch to the MIPS maintainers.
(Having dangling symbols in Kconfig is OK with me if I know
they will come in from some other tree.)

> +#include <linux/clk.h>
> +#include <linux/gpio.h>

This include should not be needed, just the one below.

> +#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>

Oh this is how you do things still? Is this necessary? :/
Then I guess it has to be applied to the MIPS tree.

> +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);
> +}

This is a bit excess abstraction, I would just readl() and writel()
directly in the code, adding the offset. Butr it's you pick.

> +static inline struct pic32_gpio_bank *gc_to_bank(struct gpio_chip *gc)
> +{
> +       return container_of(gc, struct pic32_gpio_bank, gpio_chip);
> +}

Don't do this anymore.

Se all the "use gpiochip data pointer" patches I merged last
merge window.

Replace

= gc_to_bank(gc)
with
= gpiochip_get_data(gc);

everywhere and use

gpiochip_add_data(&gc, bank)

to make sure that gpipchip_get_data() returns what you want.

> +static inline struct pic32_gpio_bank *irqd_to_bank(struct irq_data *d)
> +{
> +       return gc_to_bank(irq_data_get_irq_chip_data(d));
> +}

For example this becomes:
return gpiochip_get_data(irq_data_get_irq_chip_data(d));

> +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);
> +}

Again excessive abstraction IMO.

> +#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),
> +};

Ewwwww.... I guess it's OK though. I would have made it with
some dynamic allocation and a for() loop in code.

> +       bank->gpio_chip.dev = &pdev->dev;

This is named .parent in the upstream kernel so you need to change it.

> +       bank->gpio_chip.of_node = np;
> +       ret = gpiochip_add(&bank->gpio_chip);

So use gpiochip_add_data()

Apart from this it looks pretty OK.

Yours,
Linus Walleij

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

* Re: [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
@ 2016-01-27 15:55         ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-27 15:55 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Jiri Slaby, linux-mips, ralf, Andrei Pistirica,
	linux-serial, linux-api

On 01/26/2016 10:33 AM, Greg Kroah-Hartman wrote:
> On Tue, Jan 26, 2016 at 10:04:41AM -0700, Joshua Henderson wrote:
>> Hi Greg and Jiri,
>>
>> Ping!  Need an ack for this or pull it upstream.
> 
> The merge window _just_ ended, please give us a chance to catch up on
> patches to be reviewed.  There's no reason you need a response for this
> right away, it can't be merged until 4.6-rc1, right?
> 

No problem.  There are a few dangling drivers in this series to complete a bootable platform, including this one, that have been stale for awhile now.  I just want to make sure they are ready.

Thanks,
Josh

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

* Re: [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
@ 2016-01-27 15:55         ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-27 15:55 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Jiri Slaby,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA, ralf-6z/3iImG2C8G8FEW9MqTrA,
	Andrei Pistirica, linux-serial-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA

On 01/26/2016 10:33 AM, Greg Kroah-Hartman wrote:
> On Tue, Jan 26, 2016 at 10:04:41AM -0700, Joshua Henderson wrote:
>> Hi Greg and Jiri,
>>
>> Ping!  Need an ack for this or pull it upstream.
> 
> The merge window _just_ ended, please give us a chance to catch up on
> patches to be reviewed.  There's no reason you need a response for this
> right away, it can't be merged until 4.6-rc1, right?
> 

No problem.  There are a few dangling drivers in this series to complete a bootable platform, including this one, that have been stale for awhile now.  I just want to make sure they are ready.

Thanks,
Josh

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

* Re: [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
@ 2016-01-27 15:55         ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-27 15:55 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Jiri Slaby, linux-mips, ralf, Andrei Pistirica,
	linux-serial, linux-api

On 01/26/2016 10:33 AM, Greg Kroah-Hartman wrote:
> On Tue, Jan 26, 2016 at 10:04:41AM -0700, Joshua Henderson wrote:
>> Hi Greg and Jiri,
>>
>> Ping!  Need an ack for this or pull it upstream.
> 
> The merge window _just_ ended, please give us a chance to catch up on
> patches to be reviewed.  There's no reason you need a response for this
> right away, it can't be merged until 4.6-rc1, right?
> 

No problem.  There are a few dangling drivers in this series to complete a bootable platform, including this one, that have been stale for awhile now.  I just want to make sure they are ready.

Thanks,
Josh

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

* Re: [PATCH v5 08/14] pinctrl: pinctrl-pic32: Add PIC32 pin control driver
  2016-01-27 13:49   ` Linus Walleij
@ 2016-01-28  0:33     ` Joshua Henderson
  0 siblings, 0 replies; 66+ messages in thread
From: Joshua Henderson @ 2016-01-28  0:33 UTC (permalink / raw)
  To: Linus Walleij; +Cc: linux-kernel, Linux MIPS, Ralf Baechle, linux-gpio

Linus,

On 01/27/2016 06:49 AM, Linus Walleij wrote:
> On Thu, Jan 14, 2016 at 2:15 AM, Joshua Henderson
> <joshua.henderson@microchip.com> wrote:
> 
>> 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>
>> ---
>> Changes since v4: None
> 
> Sorry for the delay. Fell between the cracks.
>
> Overall this looks nice. Small things that need to be fixed below.
> 
> Is it possible to merge this patch separately into the pinctrl tree?

Yes. That was not the original goal as it is part of a larger patch set for dependency reasons, but a couple in the series got left behind, including this one.

> 
> I was hoping there are no compile-time dependencies to the
> MIPS tree so I can just merge this. Else I can provide some
> kind of immutable branch to the MIPS maintainers.
> (Having dangling symbols in Kconfig is OK with me if I know
> they will come in from some other tree.)
> 

You have what you need in your pinctrl tree.  I see 4.5-rc1 pulled with MIPS PIC32 platform support.

>> +#include <linux/clk.h>
>> +#include <linux/gpio.h>
> 
> This include should not be needed, just the one below.
> 

Ack.

>> +#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>
> 
> Oh this is how you do things still? Is this necessary? :/

This is needed only for the PIC32_CLR()/PIC32_SET()/PIC32_INV() macros that are just providing register offsets. These macros are used by many (all?) of the PIC32 drivers and platform.  Explicitly labeling these offsets where used is valuable because it changes the meaning of "value" to writel().  If you can suggest a better place to put these I am all for it.

> Then I guess it has to be applied to the MIPS tree.
> 

Doesn't have to now.

>> +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);
>> +}
> 
> This is a bit excess abstraction, I would just readl() and writel()
> directly in the code, adding the offset. Butr it's you pick.
> 

I'm in agreement.  It was nice for initial debug, but we are past that.  I'll yank the wrapper *_writel/*_readl functions.

>> +static inline struct pic32_gpio_bank *gc_to_bank(struct gpio_chip *gc)
>> +{
>> +       return container_of(gc, struct pic32_gpio_bank, gpio_chip);
>> +}
> 
> Don't do this anymore.
> 
> Se all the "use gpiochip data pointer" patches I merged last
> merge window.
> 

Those made it in with this patch outstanding.  I will double check against your pinctrl tree.

> Replace
> 
> = gc_to_bank(gc)
> with
> = gpiochip_get_data(gc);
> 
> everywhere and use
> 
> gpiochip_add_data(&gc, bank)
> 
> to make sure that gpipchip_get_data() returns what you want.
> 
>> +static inline struct pic32_gpio_bank *irqd_to_bank(struct irq_data *d)
>> +{
>> +       return gc_to_bank(irq_data_get_irq_chip_data(d));
>> +}
> 
> For example this becomes:
> return gpiochip_get_data(irq_data_get_irq_chip_data(d));
> 

Thanks for the explanation.  Consider it done.

>> +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);
>> +}
> 
> Again excessive abstraction IMO.
> 

Ack.

>> +#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),
>> +};
> 
> Ewwwww.... I guess it's OK though. I would have made it with
> some dynamic allocation and a for() loop in code.
> 

Let me look into this more and see if I can clean it up.

>> +       bank->gpio_chip.dev = &pdev->dev;
> 
> This is named .parent in the upstream kernel so you need to change it.

Ack.

> 
>> +       bank->gpio_chip.of_node = np;
>> +       ret = gpiochip_add(&bank->gpio_chip);
> 
> So use gpiochip_add_data()
> 
> Apart from this it looks pretty OK.
> 

I'll take what I can get.  Let me submit a patch in response to this feedback and you can take it at will.

Josh

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

* Re: [PATCH v5 04/14] clk: clk-pic32: Add PIC32 clock driver
  2016-01-14  1:15   ` Joshua Henderson
  (?)
  (?)
@ 2016-01-29 23:58   ` Stephen Boyd
  2016-02-03  5:36       ` Purna Chandra Mandal
  -1 siblings, 1 reply; 66+ messages in thread
From: Stephen Boyd @ 2016-01-29 23:58 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, linux-mips, ralf, Purna Chandra Mandal,
	Michael Turquette, linux-clk

On 01/13, Joshua Henderson wrote:
> diff --git a/drivers/clk/clk-pic32.c b/drivers/clk/clk-pic32.c
> new file mode 100644
> index 0000000..9dc5f78
> --- /dev/null
> +++ b/drivers/clk/clk-pic32.c
> @@ -0,0 +1,1801 @@
> +/*
> + * 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>

Is this used?

> +#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>

Please move asm includes after all linux includes.

Include <linux/kernel.h> for abs usage?

> +
> +#include <asm/mach-pic32/pic32.h>

Is this header required? I'd like to be able to build this file
without using a MIPS compiler. For example, if we could move that
pic32_syskey_unlock() prototype somewhere else besides
asm/mach-pic32 then this should compile fine on non-MIPS kernels?

> +
> +/* 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;

Maybe this could be called lock_mask?

> +	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;

#ifdef CONFIG_DEBUGFS?

> +	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;

This should probably be unsigned long.

> +	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()

Will this be defined to something one day?

> +
> +/* 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.
> + */

This is not kernel doc notation.

> +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);

Who frees this? kcalloc too.

> +	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);

And who uses this API?

> +
> +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));

Please don't use clk_writel. Just use readl/writel directly.

> +	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;
> +		}
> +	}

We have iopoll for this stuff too.

> +
> +	/* 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[] = {

Can this be const? Same goes for all these debugfs_reg32 arrays.

> +	{ .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();

What is this for? Usually cpu_relax() only goes in loops. If it's
still needed, we need a comment above it.

> +}
> +
> +static void roclk_init(struct clk_hw *hw)
> +{
> +	roclk_disable(hw);
> +}
> +
> +static u8 roclk_get_parent(struct clk_hw *hw)
> +{
> +	u8 i = 0;

Use int for iterator types. u8 is for when you really care about
the size.

> +	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 (;;) {

Can we put some timeout on this? I'd rather not have the hardware
flip a bit minutes, hours, days later and we're sitting here
waiting.

> +		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;

Scared of overflow. Why aren't these just int or long?

> +	u64 frac;
> +
> +	/* Find integer approximation of floating-point arithmatic.

s/arithmatic/arithmetic/ ?

> +	 *      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;

Useless cast.

> +	}
> +
> +	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;

Just return roclk_calc_rate(...)

> +}
> +
> +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;
> +		}
> +	}

Why can't we use the generic mux determine rate code here?

> +
> +	/* 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__);

We have a flag CLK_SET_RATE_GATE for this.

> +		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);

If we spelled out spin_lock_irqsave() here instead we wouldn't
need the comment. Please do that and get rid of any spinlock
comments.

> +	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 */

Useless comment.

> +	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;

Wrong check for error.

> +}
> +
> +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;

clamp(odiv, PLL_ODIV_MIN, 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;

mult can't overflow here?

> +	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))

Drop useless parenthesis.

> +		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.

a,b,c,d is usually used for a choice. Please use 1,2,3,4 instead.

> +	 */
> +
> +	/* 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)))

Does this ever occur? We control when pic32_sys_clk is assigned
so it seems like all we could have is broken code.

> +		return -EPERM;
> +
> +	/* get sysclk parent */
> +	sclk_parent = clk_get_parent(pic32_sys_clk);

Ah here's the clk API usage. Maybe we need some clk_hw API that
tells us if a clk_hw is a child of this clk_hw? Something like:

	bool clk_hw_is_child(struct clk_hw *hw, struct clk_hw *child)

This also highlights a problem we have with CLK_SET_RATE_GATE,
where that flag isn't considered when the clock is changing rate
due to something upstream. We may want to add some new flag, or
just change the behavior so that CLK_SET_RATE_GATE is always
respected.

> +
> +	/* does sys_clk using spll_clk as parent ? */

Is sys_clk using spll_clk as its 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");

This will look like "clk.First reparent". Is that intentional?
And is the user looking at the kernel log even going to know what
that means?

> +		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))

Debugfs APIs check for NULL or non-NULL to indicate error or
success.

> +		return PTR_ERR(file);
> +
> +	return 0;

Can be simplified to return PTR_ERR_OR_ZERO().

Also, have you considered using regmap-mmio?

> +}
> +
> +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;

Just return idx then.

> +
> +	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);

Drop useless parenthesis.

> +	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);

This is already called with irqs saved and disabled, so what's
the point of doing that again here?

> +
> +	/* 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);

Same story.

> +
> +	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 = {

Can this be const?

> +	.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,
> +};

Same const question for all these ops.

> +
> +#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);

Don't use CLK_IS_BASIC.

> +
> +	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.

Please do multi-line comments with a /* on its own line.

> +	 */
> +	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 */

Uhh yes.

> +	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)) {

The bindings have really been acked? I think we've been actively
prevented from putting linux concepts into DT, and ignore-unused
is one of those.

> +		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 */

This comment is worthless.

> +	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, 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);

kcalloc.

> +	if (!parents)
> +		return;
> +
> +	ret = pic32_of_clk_get_parent_indices(np, &parent_idx, count);
> +	if (ret)
> +		goto err_parent;
> +
> +	of_clk_parent_fill(np, parents, count);
> +
> +	/* 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, 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);

kcalloc

> +	if (!parents)
> +		return;
> +
> +	ret = pic32_of_clk_get_parent_indices(np, &parent_idx, count);
> +	if (ret)
> +		goto err_name;
> +
> +	of_clk_parent_fill(np, parents, count);
> +
> +	ret = of_property_read_string_index(np, "clock-output-names",
> +					    0, &clk_name);
> +	if (ret)
> +		clk_name = np->name;

If at all possible, don't use clock-output-names to set the name
of your clock. We're trying to figure out how to get rid of clk
names being used by the clk framework to describe the hierarchy.

> +
> +	/* get slew base */
> +	slew_reg = of_iomap(np, 0);
> +	if (!slew_reg) {
> +		pr_warn("%s: no slew register ?\n", clk_name);

More like, couldn't map slew register, or just silence.

> +		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);

Useless parenthesis.

> +		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 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);

kcalloc. I see a pattern.

> +	if (!parent_names)
> +		return;
> +
> +	of_clk_parent_fill(np, parent_names, count);
> +
> +	/* 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);

Yuck. Bit masks in DT? This was approved?

> +	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)) {

Use IS_ERR(), not IS_ERR_OR_NULL(). NULL is a valid clock
pointer.

> +		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");

Why isn't this pr_err()?

> +
> +	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));

Can we use ioremap() instead? If so, we could use
of_io_request_and_map() then.


> +	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;
> +	}

Why can't we parse and map the irq down there at the if (nmi)?
Lose the local variable.

> +
> +	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);
> +	}

Finally, can this be a platform driver instead of using
OF_CLK_DECLARE()?

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

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

* Re: [PATCH v5 04/14] clk: clk-pic32: Add PIC32 clock driver
@ 2016-02-03  5:36       ` Purna Chandra Mandal
  0 siblings, 0 replies; 66+ messages in thread
From: Purna Chandra Mandal @ 2016-02-03  5:36 UTC (permalink / raw)
  To: Stephen Boyd, Joshua Henderson
  Cc: linux-kernel, linux-mips, ralf, Michael Turquette, linux-clk

On 01/30/2016 05:28 AM, Stephen Boyd wrote:

> On 01/13, Joshua Henderson wrote:
>> diff --git a/drivers/clk/clk-pic32.c b/drivers/clk/clk-pic32.c
>> new file mode 100644
>> index 0000000..9dc5f78
>> --- /dev/null
>> +++ b/drivers/clk/clk-pic32.c
>> @@ -0,0 +1,1801 @@
>> +/*
>> + * 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>
> Is this used?

Yes. used below. Will remove as discussed below.

>> +#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>
> Please move asm includes after all linux includes.
>
> Include <linux/kernel.h> for abs usage?
>
ack.

>> +
>> +#include <asm/mach-pic32/pic32.h>
> Is this header required? I'd like to be able to build this file
> without using a MIPS compiler. For example, if we could move that
> pic32_syskey_unlock() prototype somewhere else besides
> asm/mach-pic32 then this should compile fine on non-MIPS kernels?

Yes it is good to remove the dependency for compilation purpose. But this
driver uses few more functionality/macros (like PIC32_CLR, PIC32_SET)
defined in pic32.h which are used by this driver to select
atomic-register-offset.

>> +
>> +/* 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;
> Maybe this could be called lock_mask?

ack.

>> +	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;
> #ifdef CONFIG_DEBUGFS?

ack. Will remove it and corresponding clk operation callback 'debug_init()' as well.

>> +	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;
> This should probably be unsigned long.

ack.

>> +	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()
> Will this be defined to something one day?

Yes, in future it might be used in production software.
Will remove it now.

>> +
>> +/* 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.
>> + */
> This is not kernel doc notation.

ack. Will update as per kernel doc notation.

>> +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);
> Who frees this? kcalloc too.

Good catch. Caller will have to free the memory as currently being done. Will add comment accordingly.

>> +	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);
> And who uses this API?

Some platform drivers (e.g. peripheral timer) uses this to select input clock
source(s). It might better be a platform API, but added here clock driver as it
is related to clock functionality and this driver uses it.

>> +
>> +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));
> Please don't use clk_writel. Just use readl/writel directly.

ack.

>> +	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;
>> +		}
>> +	}
> We have iopoll for this stuff too.

ack. Will replace with iopoll API.

>> +
>> +	/* 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[] = {
> Can this be const? Same goes for all these debugfs_reg32 arrays.

ack. Will update.

>> +	{ .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();
> What is this for? Usually cpu_relax() only goes in loops. If it's
> still needed, we need a comment above it.

Good catch. This is added to insert a nop cycle. As per datasheet CPU should not access
the clock register at the immediate next cycle of the clock-gating instruction.
Will remove it, as we are not really accessing the clock register just after clock gate.

>> +}
>> +
>> +static void roclk_init(struct clk_hw *hw)
>> +{
>> +	roclk_disable(hw);
>> +}
>> +
>> +static u8 roclk_get_parent(struct clk_hw *hw)
>> +{
>> +	u8 i = 0;
> Use int for iterator types. u8 is for when you really care about
> the size.

ack.

>> +	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 (;;) {
> Can we put some timeout on this? I'd rather not have the hardware
> flip a bit minutes, hours, days later and we're sitting here
> waiting.

ack. Replace with iopoll timeout variant.

>> +		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;
> Scared of overflow. Why aren't these just int or long?

ack. Will update.

>> +	u64 frac;
>> +
>> +	/* Find integer approximation of floating-point arithmatic.
> s/arithmatic/arithmetic/ ?

ack. :)

>> +	 *      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;
> Useless cast.

ack. Will drop.

>> +	}
>> +
>> +	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;
> Just return roclk_calc_rate(...)

ack.

>> +}
>> +
>> +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;
>> +		}
>> +	}
> Why can't we use the generic mux determine rate code here?

Generic __clk_mux_determine_rate() and sister works with clocks which has
only muxing property and no divider, no multiplier. It selects best parents
which can generate frequency nearest to target rate.
But in PIC32 refo clock has (integer & floating-point) divider and input
selector which are capable of generating wide range of output frequencies
from the same parent clock. This custom .determine_rate() callback searches all
the parents to generate best output clock by applying different dividers.

>> +
>> +	/* 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__);
> We have a flag CLK_SET_RATE_GATE for this.

Ack. Will use CLK_SET_RATE_GATE, CLK_SET_PARENT_GATE accordingly.

>> +		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);
> If we spelled out spin_lock_irqsave() here instead we wouldn't
> need the comment. Please do that and get rid of any spinlock
> comments.

Ack, Will remove the wrapper.

>> +	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 */
> Useless comment.

ack.

>> +	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;
> Wrong check for error.

ack.

>> +}
>> +
>> +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;
> clamp(odiv, PLL_ODIV_MIN, PLL_ODIV_MAX)

ack.

>> +
>> +	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;
> mult can't overflow here?

ack, As per datasheet 'mult' is 7-bit wide. So will not overflow. Anyway will promote it to u32.

>> +	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))
> Drop useless parenthesis.

ack.

>> +		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.
> a,b,c,d is usually used for a choice. Please use 1,2,3,4 instead.

ack.

>> +	 */
>> +
>> +	/* 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)))
> Does this ever occur? We control when pic32_sys_clk is assigned
> so it seems like all we could have is broken code.

This is not required at all. Will remove.

>> +		return -EPERM;
>> +
>> +	/* get sysclk parent */
>> +	sclk_parent = clk_get_parent(pic32_sys_clk);
> Ah here's the clk API usage. Maybe we need some clk_hw API that
> tells us if a clk_hw is a child of this clk_hw? Something like:
>
> 	bool clk_hw_is_child(struct clk_hw *hw, struct clk_hw *child)
>
> This also highlights a problem we have with CLK_SET_RATE_GATE,
> where that flag isn't considered when the clock is changing rate
> due to something upstream. We may want to add some new flag, or
> just change the behavior so that CLK_SET_RATE_GATE is always
> respected.

In PIC32 one of the possible "immediate" parent of SCLK is SPLL, so I hope comparing
clk_hw_get_parent(sclk_hw) with spll_hw is sufficient to find parent-child relation.

In this function 'spll_clk_set_rate()' we want to ensure correct pre-condition for
SPLL rate change is followed, in-case of condition-check failure we will return with error,
else will apply the new rate.
Note we will not force set_parent() or re-parent from this function because of internal
complexity in handling flow.
So clock user can follow this sequence to perform SPLL rate change:
	clk_set_parent(sclk, frcdiv_clk);
        clk_set_rate(spll, new-rate);
	clk_set_parent(sclk, spll);

Will update comment accordingly.

>> +
>> +	/* does sys_clk using spll_clk as parent ? */
> Is sys_clk using spll_clk as its 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");
> This will look like "clk.First reparent". Is that intentional?
> And is the user looking at the kernel log even going to know what
> that means?

Will remove.

>> +		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))
> Debugfs APIs check for NULL or non-NULL to indicate error or
> success.

ack. Will drop as mentioned above.

>> +		return PTR_ERR(file);
>> +
>> +	return 0;
> Can be simplified to return PTR_ERR_OR_ZERO().
>
> Also, have you considered using regmap-mmio?
>
>> +}
>> +
>> +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;
> Just return idx then.

ack.

>> +
>> +	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);
> Drop useless parenthesis.

ack.

>> +	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);
> This is already called with irqs saved and disabled, so what's
> the point of doing that again here?

ack. Will remove.

>> +
>> +	/* 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);
> Same story.

Will remove.

>> +
>> +	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 = {
> Can this be const?

ack.

>> +	.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,
>> +};
> Same const question for all these ops.

ack.

>> +
>> +#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);
> Don't use CLK_IS_BASIC.

ack. Will drop here and in other places too.

>> +
>> +	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.
> Please do multi-line comments with a /* on its own line.

ack.

>> +	 */
>> +	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 */
> Uhh yes.
>
>> +	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)) {
> The bindings have really been acked? I think we've been actively
> prevented from putting linux concepts into DT, and ignore-unused
> is one of those.

Property 'microchip,ignore-unused' is to identify critical clocks (like CPU clock)
in the system which should never be gated for whatsoever be the reason. I'll drop
this now and use 'critical-clock' whenever available in upstream.

>> +		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 */
> This comment is worthless.

ack.

>> +	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, 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);
> kcalloc.

ack.

>> +	if (!parents)
>> +		return;
>> +
>> +	ret = pic32_of_clk_get_parent_indices(np, &parent_idx, count);
>> +	if (ret)
>> +		goto err_parent;
>> +
>> +	of_clk_parent_fill(np, parents, count);
>> +
>> +	/* 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, 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);
> kcalloc

ack.

>> +	if (!parents)
>> +		return;
>> +
>> +	ret = pic32_of_clk_get_parent_indices(np, &parent_idx, count);
>> +	if (ret)
>> +		goto err_name;
>> +
>> +	of_clk_parent_fill(np, parents, count);
>> +
>> +	ret = of_property_read_string_index(np, "clock-output-names",
>> +					    0, &clk_name);
>> +	if (ret)
>> +		clk_name = np->name;
> If at all possible, don't use clock-output-names to set the name
> of your clock. We're trying to figure out how to get rid of clk
> names being used by the clk framework to describe the hierarchy.

ack, will drop.

>> +
>> +	/* get slew base */
>> +	slew_reg = of_iomap(np, 0);
>> +	if (!slew_reg) {
>> +		pr_warn("%s: no slew register ?\n", clk_name);
> More like, couldn't map slew register, or just silence.

ack.

>> +		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);
> Useless parenthesis.

ack. Will remove others as well.

>> +		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 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);
> kcalloc. I see a pattern.

ack.

>> +	if (!parent_names)
>> +		return;
>> +
>> +	of_clk_parent_fill(np, parent_names, count);
>> +
>> +	/* 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);
> Yuck. Bit masks in DT? This was approved?

Yes, this bitmask is from datasheet and accepted as border-line case.

>> +	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)) {
> Use IS_ERR(), not IS_ERR_OR_NULL(). NULL is a valid clock
> pointer.

ack.

>> +		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");
> Why isn't this pr_err()?

ack

>> +
>> +	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));
> Can we use ioremap() instead? If so, we could use
> of_io_request_and_map() then.

ack. Will replace with of_io_request_and_map().

>> +	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;
>> +	}
> Why can't we parse and map the irq down there at the if (nmi)?
> Lose the local variable.

ack.

>> +
>> +	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);
>> +	}
> Finally, can this be a platform driver instead of using
> OF_CLK_DECLARE()?
>
I'll prefer to use OF_CLK_DECLARE() way. please provide some reference otherwise.

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

* Re: [PATCH v5 04/14] clk: clk-pic32: Add PIC32 clock driver
@ 2016-02-03  5:36       ` Purna Chandra Mandal
  0 siblings, 0 replies; 66+ messages in thread
From: Purna Chandra Mandal @ 2016-02-03  5:36 UTC (permalink / raw)
  To: Stephen Boyd, Joshua Henderson
  Cc: linux-kernel, linux-mips, ralf, Michael Turquette, linux-clk

On 01/30/2016 05:28 AM, Stephen Boyd wrote:

> On 01/13, Joshua Henderson wrote:
>> diff --git a/drivers/clk/clk-pic32.c b/drivers/clk/clk-pic32.c
>> new file mode 100644
>> index 0000000..9dc5f78
>> --- /dev/null
>> +++ b/drivers/clk/clk-pic32.c
>> @@ -0,0 +1,1801 @@
>> +/*
>> + * 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>
> Is this used?

Yes. used below. Will remove as discussed below.

>> +#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>
> Please move asm includes after all linux includes.
>
> Include <linux/kernel.h> for abs usage?
>
ack.

>> +
>> +#include <asm/mach-pic32/pic32.h>
> Is this header required? I'd like to be able to build this file
> without using a MIPS compiler. For example, if we could move that
> pic32_syskey_unlock() prototype somewhere else besides
> asm/mach-pic32 then this should compile fine on non-MIPS kernels?

Yes it is good to remove the dependency for compilation purpose. But this
driver uses few more functionality/macros (like PIC32_CLR, PIC32_SET)
defined in pic32.h which are used by this driver to select
atomic-register-offset.

>> +
>> +/* 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;
> Maybe this could be called lock_mask?

ack.

>> +	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;
> #ifdef CONFIG_DEBUGFS?

ack. Will remove it and corresponding clk operation callback 'debug_init()' as well.

>> +	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;
> This should probably be unsigned long.

ack.

>> +	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()
> Will this be defined to something one day?

Yes, in future it might be used in production software.
Will remove it now.

>> +
>> +/* 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.
>> + */
> This is not kernel doc notation.

ack. Will update as per kernel doc notation.

>> +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);
> Who frees this? kcalloc too.

Good catch. Caller will have to free the memory as currently being done. Will add comment accordingly.

>> +	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);
> And who uses this API?

Some platform drivers (e.g. peripheral timer) uses this to select input clock
source(s). It might better be a platform API, but added here clock driver as it
is related to clock functionality and this driver uses it.

>> +
>> +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));
> Please don't use clk_writel. Just use readl/writel directly.

ack.

>> +	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;
>> +		}
>> +	}
> We have iopoll for this stuff too.

ack. Will replace with iopoll API.

>> +
>> +	/* 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[] = {
> Can this be const? Same goes for all these debugfs_reg32 arrays.

ack. Will update.

>> +	{ .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();
> What is this for? Usually cpu_relax() only goes in loops. If it's
> still needed, we need a comment above it.

Good catch. This is added to insert a nop cycle. As per datasheet CPU should not access
the clock register at the immediate next cycle of the clock-gating instruction.
Will remove it, as we are not really accessing the clock register just after clock gate.

>> +}
>> +
>> +static void roclk_init(struct clk_hw *hw)
>> +{
>> +	roclk_disable(hw);
>> +}
>> +
>> +static u8 roclk_get_parent(struct clk_hw *hw)
>> +{
>> +	u8 i = 0;
> Use int for iterator types. u8 is for when you really care about
> the size.

ack.

>> +	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 (;;) {
> Can we put some timeout on this? I'd rather not have the hardware
> flip a bit minutes, hours, days later and we're sitting here
> waiting.

ack. Replace with iopoll timeout variant.

>> +		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;
> Scared of overflow. Why aren't these just int or long?

ack. Will update.

>> +	u64 frac;
>> +
>> +	/* Find integer approximation of floating-point arithmatic.
> s/arithmatic/arithmetic/ ?

ack. :)

>> +	 *      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;
> Useless cast.

ack. Will drop.

>> +	}
>> +
>> +	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;
> Just return roclk_calc_rate(...)

ack.

>> +}
>> +
>> +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;
>> +		}
>> +	}
> Why can't we use the generic mux determine rate code here?

Generic __clk_mux_determine_rate() and sister works with clocks which has
only muxing property and no divider, no multiplier. It selects best parents
which can generate frequency nearest to target rate.
But in PIC32 refo clock has (integer & floating-point) divider and input
selector which are capable of generating wide range of output frequencies
from the same parent clock. This custom .determine_rate() callback searches all
the parents to generate best output clock by applying different dividers.

>> +
>> +	/* 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__);
> We have a flag CLK_SET_RATE_GATE for this.

Ack. Will use CLK_SET_RATE_GATE, CLK_SET_PARENT_GATE accordingly.

>> +		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);
> If we spelled out spin_lock_irqsave() here instead we wouldn't
> need the comment. Please do that and get rid of any spinlock
> comments.

Ack, Will remove the wrapper.

>> +	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 */
> Useless comment.

ack.

>> +	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;
> Wrong check for error.

ack.

>> +}
>> +
>> +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;
> clamp(odiv, PLL_ODIV_MIN, PLL_ODIV_MAX)

ack.

>> +
>> +	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;
> mult can't overflow here?

ack, As per datasheet 'mult' is 7-bit wide. So will not overflow. Anyway will promote it to u32.

>> +	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))
> Drop useless parenthesis.

ack.

>> +		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.
> a,b,c,d is usually used for a choice. Please use 1,2,3,4 instead.

ack.

>> +	 */
>> +
>> +	/* 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)))
> Does this ever occur? We control when pic32_sys_clk is assigned
> so it seems like all we could have is broken code.

This is not required at all. Will remove.

>> +		return -EPERM;
>> +
>> +	/* get sysclk parent */
>> +	sclk_parent = clk_get_parent(pic32_sys_clk);
> Ah here's the clk API usage. Maybe we need some clk_hw API that
> tells us if a clk_hw is a child of this clk_hw? Something like:
>
> 	bool clk_hw_is_child(struct clk_hw *hw, struct clk_hw *child)
>
> This also highlights a problem we have with CLK_SET_RATE_GATE,
> where that flag isn't considered when the clock is changing rate
> due to something upstream. We may want to add some new flag, or
> just change the behavior so that CLK_SET_RATE_GATE is always
> respected.

In PIC32 one of the possible "immediate" parent of SCLK is SPLL, so I hope comparing
clk_hw_get_parent(sclk_hw) with spll_hw is sufficient to find parent-child relation.

In this function 'spll_clk_set_rate()' we want to ensure correct pre-condition for
SPLL rate change is followed, in-case of condition-check failure we will return with error,
else will apply the new rate.
Note we will not force set_parent() or re-parent from this function because of internal
complexity in handling flow.
So clock user can follow this sequence to perform SPLL rate change:
	clk_set_parent(sclk, frcdiv_clk);
        clk_set_rate(spll, new-rate);
	clk_set_parent(sclk, spll);

Will update comment accordingly.

>> +
>> +	/* does sys_clk using spll_clk as parent ? */
> Is sys_clk using spll_clk as its 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");
> This will look like "clk.First reparent". Is that intentional?
> And is the user looking at the kernel log even going to know what
> that means?

Will remove.

>> +		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))
> Debugfs APIs check for NULL or non-NULL to indicate error or
> success.

ack. Will drop as mentioned above.

>> +		return PTR_ERR(file);
>> +
>> +	return 0;
> Can be simplified to return PTR_ERR_OR_ZERO().
>
> Also, have you considered using regmap-mmio?
>
>> +}
>> +
>> +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;
> Just return idx then.

ack.

>> +
>> +	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);
> Drop useless parenthesis.

ack.

>> +	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);
> This is already called with irqs saved and disabled, so what's
> the point of doing that again here?

ack. Will remove.

>> +
>> +	/* 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);
> Same story.

Will remove.

>> +
>> +	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 = {
> Can this be const?

ack.

>> +	.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,
>> +};
> Same const question for all these ops.

ack.

>> +
>> +#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);
> Don't use CLK_IS_BASIC.

ack. Will drop here and in other places too.

>> +
>> +	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.
> Please do multi-line comments with a /* on its own line.

ack.

>> +	 */
>> +	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 */
> Uhh yes.
>
>> +	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)) {
> The bindings have really been acked? I think we've been actively
> prevented from putting linux concepts into DT, and ignore-unused
> is one of those.

Property 'microchip,ignore-unused' is to identify critical clocks (like CPU clock)
in the system which should never be gated for whatsoever be the reason. I'll drop
this now and use 'critical-clock' whenever available in upstream.

>> +		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 */
> This comment is worthless.

ack.

>> +	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, 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);
> kcalloc.

ack.

>> +	if (!parents)
>> +		return;
>> +
>> +	ret = pic32_of_clk_get_parent_indices(np, &parent_idx, count);
>> +	if (ret)
>> +		goto err_parent;
>> +
>> +	of_clk_parent_fill(np, parents, count);
>> +
>> +	/* 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, 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);
> kcalloc

ack.

>> +	if (!parents)
>> +		return;
>> +
>> +	ret = pic32_of_clk_get_parent_indices(np, &parent_idx, count);
>> +	if (ret)
>> +		goto err_name;
>> +
>> +	of_clk_parent_fill(np, parents, count);
>> +
>> +	ret = of_property_read_string_index(np, "clock-output-names",
>> +					    0, &clk_name);
>> +	if (ret)
>> +		clk_name = np->name;
> If at all possible, don't use clock-output-names to set the name
> of your clock. We're trying to figure out how to get rid of clk
> names being used by the clk framework to describe the hierarchy.

ack, will drop.

>> +
>> +	/* get slew base */
>> +	slew_reg = of_iomap(np, 0);
>> +	if (!slew_reg) {
>> +		pr_warn("%s: no slew register ?\n", clk_name);
> More like, couldn't map slew register, or just silence.

ack.

>> +		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);
> Useless parenthesis.

ack. Will remove others as well.

>> +		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 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);
> kcalloc. I see a pattern.

ack.

>> +	if (!parent_names)
>> +		return;
>> +
>> +	of_clk_parent_fill(np, parent_names, count);
>> +
>> +	/* 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);
> Yuck. Bit masks in DT? This was approved?

Yes, this bitmask is from datasheet and accepted as border-line case.

>> +	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)) {
> Use IS_ERR(), not IS_ERR_OR_NULL(). NULL is a valid clock
> pointer.

ack.

>> +		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");
> Why isn't this pr_err()?

ack

>> +
>> +	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));
> Can we use ioremap() instead? If so, we could use
> of_io_request_and_map() then.

ack. Will replace with of_io_request_and_map().

>> +	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;
>> +	}
> Why can't we parse and map the irq down there at the if (nmi)?
> Lose the local variable.

ack.

>> +
>> +	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);
>> +	}
> Finally, can this be a platform driver instead of using
> OF_CLK_DECLARE()?
>
I'll prefer to use OF_CLK_DECLARE() way. please provide some reference otherwise.

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

* Re: [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
@ 2016-02-07  7:04     ` Greg Kroah-Hartman
  0 siblings, 0 replies; 66+ messages in thread
From: Greg Kroah-Hartman @ 2016-02-07  7:04 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, linux-mips, ralf, Andrei Pistirica, Jiri Slaby,
	linux-serial, linux-api

On Wed, Jan 13, 2016 at 06:15:43PM -0700, Joshua Henderson 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>
> ---

Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

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

* Re: [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
@ 2016-02-07  7:04     ` Greg Kroah-Hartman
  0 siblings, 0 replies; 66+ messages in thread
From: Greg Kroah-Hartman @ 2016-02-07  7:04 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA, ralf-6z/3iImG2C8G8FEW9MqTrA,
	Andrei Pistirica, Jiri Slaby,
	linux-serial-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA

On Wed, Jan 13, 2016 at 06:15:43PM -0700, Joshua Henderson wrote:
> From: Andrei Pistirica <andrei.pistirica-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
> 
> This adds UART and a serial console driver for Microchip PIC32 class
> devices.
> 
> Signed-off-by: Andrei Pistirica <andrei.pistirica-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
> Signed-off-by: Joshua Henderson <joshua.henderson-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
> Cc: Ralf Baechle <ralf-6z/3iImG2C8G8FEW9MqTrA@public.gmane.org>
> ---

Acked-by: Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>

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

* Re: [PATCH v5 11/14] dt/bindings: Add bindings for PIC32 SDHCI host controller
  2016-01-14  1:15   ` Joshua Henderson
  (?)
@ 2016-02-08  9:58   ` Ulf Hansson
  -1 siblings, 0 replies; 66+ messages in thread
From: Ulf Hansson @ 2016-02-08  9:58 UTC (permalink / raw)
  To: Joshua Henderson
  Cc: linux-kernel, linux-mips, Ralf Baechle, Andrei Pistirica,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree

On 14 January 2016 at 02:15, Joshua Henderson
<joshua.henderson@microchip.com> 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>

I have picked this up and applied it to my next branch in my mmc tree.

Thanks and kind regards!
Uffe

> ---
> Changes since v4: None
> Changes since v3: None
> Changes since v2: None
> Changes since v1:
>         - Drop usage of piomode and no-1-8-v DT properties
> ---
>  .../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] 66+ messages in thread

* Re: [PATCH v5 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver
  2016-01-14  1:15   ` Joshua Henderson
@ 2016-02-08  9:59     ` Ulf Hansson
  -1 siblings, 0 replies; 66+ messages in thread
From: Ulf Hansson @ 2016-02-08  9:59 UTC (permalink / raw)
  To: Joshua Henderson, Adrian Hunter
  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

+ Adrian

On 14 January 2016 at 02:15, 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>

I have queued this up for next via my mmc tree.

Adrian, please tell me if you have any objections.

Thanks and kind regards!
Uffe

> ---
> Changes since v4: None
> Changes since v3: None
> Changes since v2:
>         - Use 'sdhci_pltfm_*' instead of 'sdhci_*_host' and other cleanup
> Changes since v1:
>         - Be consistent and use only "SDHCI" when referring to SD host
>           controller
>         - Remove unnecessary PIC32 sdhci_ops min clock function.
>         - Drop usage of piomode and no-1-8-v DT properties
>         - Formatting
>         - Fix use of devm_iounmap
>         - Address code comment
> ---
>  drivers/mmc/host/Kconfig       |   11 ++
>  drivers/mmc/host/Makefile      |    1 +
>  drivers/mmc/host/sdhci-pic32.c |  257 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 269 insertions(+)
>  create mode 100644 drivers/mmc/host/sdhci-pic32.c
>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 1dee533..c6a8916 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 && MMC_SDHCI_PLTFM
> +        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..059df70
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-pic32.c
> @@ -0,0 +1,257 @@
> +/*
> + * 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 "sdhci-pltfm.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
> +
> +struct pic32_sdhci_priv {
> +       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_priv *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;
> +       }
> +
> +       /* CD select and test bits must be set for errata workaround. */
> +       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 struct sdhci_pltfm_data sdhci_pic32_pdata = {
> +       .ops = &pic32_sdhci_ops,
> +       .quirks = SDHCI_QUIRK_NO_HISPD_BIT,
> +       .quirks2 = SDHCI_QUIRK2_NO_1_8_V,
> +};
> +
> +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 & 1)
> +               bus |= (1 << SDH_SHARED_BUS_CLK_PINS);
> +
> +       /* select first interrupt */
> +       if (irq_pins & 1)
> +               bus |= (1 << 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_priv *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 sdhci_host *host;
> +       struct sdhci_pltfm_host *pltfm_host;
> +       struct pic32_sdhci_priv *sdhci_pdata;
> +       struct pic32_sdhci_platform_data *plat_data;
> +       int ret;
> +
> +       host = sdhci_pltfm_init(pdev, &sdhci_pic32_pdata,
> +                               sizeof(struct pic32_sdhci_priv));
> +       if (IS_ERR(host)) {
> +               ret = PTR_ERR(host);
> +               goto err;
> +       }
> +
> +       pltfm_host = sdhci_priv(host);
> +       sdhci_pdata = sdhci_pltfm_priv(pltfm_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;
> +       }
> +
> +       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;
> +       }
> +
> +       ret = clk_prepare_enable(sdhci_pdata->sys_clk);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Error enabling clock\n");
> +               goto err_host;
> +       }
> +
> +       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_sys_clk;
> +       }
> +
> +       ret = clk_prepare_enable(sdhci_pdata->base_clk);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Error enabling clock\n");
> +               goto err_base_clk;
> +       }
> +
> +       ret = mmc_of_parse(host->mmc);
> +       if (ret)
> +               goto err_base_clk;
> +
> +       ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
> +       if (ret) {
> +               dev_err(&pdev->dev, "failed to probe platform!\n");
> +               goto err_base_clk;
> +       }
> +
> +       ret = sdhci_add_host(host);
> +       if (ret) {
> +               dev_err(&pdev->dev, "error adding host\n");
> +               goto err_base_clk;
> +       }
> +
> +       dev_info(&pdev->dev, "Successfully added sdhci host\n");
> +       return 0;
> +
> +err_base_clk:
> +       clk_disable_unprepare(sdhci_pdata->base_clk);
> +err_sys_clk:
> +       clk_disable_unprepare(sdhci_pdata->sys_clk);
> +err_host:
> +       sdhci_pltfm_free(pdev);
> +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_priv *sdhci_pdata = sdhci_priv(host);
> +       u32 scratch;
> +
> +       scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
> +       sdhci_remove_host(host, scratch == (u32)~0);
> +       clk_disable_unprepare(sdhci_pdata->base_clk);
> +       clk_disable_unprepare(sdhci_pdata->sys_clk);
> +       sdhci_pltfm_free(pdev);
> +
> +       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   = "pic32-sdhci",
> +               .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] 66+ messages in thread

* Re: [PATCH v5 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver
@ 2016-02-08  9:59     ` Ulf Hansson
  0 siblings, 0 replies; 66+ messages in thread
From: Ulf Hansson @ 2016-02-08  9:59 UTC (permalink / raw)
  To: Joshua Henderson, Adrian Hunter
  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

+ Adrian

On 14 January 2016 at 02:15, 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>

I have queued this up for next via my mmc tree.

Adrian, please tell me if you have any objections.

Thanks and kind regards!
Uffe

> ---
> Changes since v4: None
> Changes since v3: None
> Changes since v2:
>         - Use 'sdhci_pltfm_*' instead of 'sdhci_*_host' and other cleanup
> Changes since v1:
>         - Be consistent and use only "SDHCI" when referring to SD host
>           controller
>         - Remove unnecessary PIC32 sdhci_ops min clock function.
>         - Drop usage of piomode and no-1-8-v DT properties
>         - Formatting
>         - Fix use of devm_iounmap
>         - Address code comment
> ---
>  drivers/mmc/host/Kconfig       |   11 ++
>  drivers/mmc/host/Makefile      |    1 +
>  drivers/mmc/host/sdhci-pic32.c |  257 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 269 insertions(+)
>  create mode 100644 drivers/mmc/host/sdhci-pic32.c
>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 1dee533..c6a8916 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 && MMC_SDHCI_PLTFM
> +        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..059df70
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-pic32.c
> @@ -0,0 +1,257 @@
> +/*
> + * 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 "sdhci-pltfm.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
> +
> +struct pic32_sdhci_priv {
> +       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_priv *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;
> +       }
> +
> +       /* CD select and test bits must be set for errata workaround. */
> +       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 struct sdhci_pltfm_data sdhci_pic32_pdata = {
> +       .ops = &pic32_sdhci_ops,
> +       .quirks = SDHCI_QUIRK_NO_HISPD_BIT,
> +       .quirks2 = SDHCI_QUIRK2_NO_1_8_V,
> +};
> +
> +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 & 1)
> +               bus |= (1 << SDH_SHARED_BUS_CLK_PINS);
> +
> +       /* select first interrupt */
> +       if (irq_pins & 1)
> +               bus |= (1 << 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_priv *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 sdhci_host *host;
> +       struct sdhci_pltfm_host *pltfm_host;
> +       struct pic32_sdhci_priv *sdhci_pdata;
> +       struct pic32_sdhci_platform_data *plat_data;
> +       int ret;
> +
> +       host = sdhci_pltfm_init(pdev, &sdhci_pic32_pdata,
> +                               sizeof(struct pic32_sdhci_priv));
> +       if (IS_ERR(host)) {
> +               ret = PTR_ERR(host);
> +               goto err;
> +       }
> +
> +       pltfm_host = sdhci_priv(host);
> +       sdhci_pdata = sdhci_pltfm_priv(pltfm_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;
> +       }
> +
> +       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;
> +       }
> +
> +       ret = clk_prepare_enable(sdhci_pdata->sys_clk);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Error enabling clock\n");
> +               goto err_host;
> +       }
> +
> +       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_sys_clk;
> +       }
> +
> +       ret = clk_prepare_enable(sdhci_pdata->base_clk);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Error enabling clock\n");
> +               goto err_base_clk;
> +       }
> +
> +       ret = mmc_of_parse(host->mmc);
> +       if (ret)
> +               goto err_base_clk;
> +
> +       ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
> +       if (ret) {
> +               dev_err(&pdev->dev, "failed to probe platform!\n");
> +               goto err_base_clk;
> +       }
> +
> +       ret = sdhci_add_host(host);
> +       if (ret) {
> +               dev_err(&pdev->dev, "error adding host\n");
> +               goto err_base_clk;
> +       }
> +
> +       dev_info(&pdev->dev, "Successfully added sdhci host\n");
> +       return 0;
> +
> +err_base_clk:
> +       clk_disable_unprepare(sdhci_pdata->base_clk);
> +err_sys_clk:
> +       clk_disable_unprepare(sdhci_pdata->sys_clk);
> +err_host:
> +       sdhci_pltfm_free(pdev);
> +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_priv *sdhci_pdata = sdhci_priv(host);
> +       u32 scratch;
> +
> +       scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
> +       sdhci_remove_host(host, scratch == (u32)~0);
> +       clk_disable_unprepare(sdhci_pdata->base_clk);
> +       clk_disable_unprepare(sdhci_pdata->sys_clk);
> +       sdhci_pltfm_free(pdev);
> +
> +       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   = "pic32-sdhci",
> +               .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] 66+ messages in thread

* Re: [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
@ 2016-04-15 17:28     ` Sudeep Holla
  0 siblings, 0 replies; 66+ messages in thread
From: Sudeep Holla @ 2016-04-15 17:28 UTC (permalink / raw)
  To: Joshua Henderson, Greg Kroah-Hartman
  Cc: open list, linux-mips, ralf, Andrei Pistirica, Jiri Slaby,
	linux-serial, linux-api, Sudeep Holla

Hi Greg,

I just noticed this now. I am having similar issue with MPS2 UART driver
posted @[1], hence I am asking here to get some clarification myself.
Sorry for replying on very old thread.

On Thu, Jan 14, 2016 at 1:15 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>

[...]

> 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

This was posted before v4.6-rc1 similar to MPS2 UART and has taken
port# 114 for it. However MVEBU UART obtained 114 with v4.6-rc1
And MPS2 UART was assigned 115 when it got revised/reposted.

I also see this patch in linux-next with 114 itself as its port number.
So the allocation of port number needs to be resolved before it gets
merged or it's OK to wait for v4.7-rc1 ?

If it's former, can PORT_PIC32 take 116 as the latest post of MPS2 assigned
it 115 and I have pulled the same to take it via arm-soc.

I am fine with any solution, just want to be notified if I need to
take any action.

Regards,
Sudeep

[1] http://www.spinics.net/lists/devicetree/msg120727.html

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

* Re: [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
@ 2016-04-15 17:28     ` Sudeep Holla
  0 siblings, 0 replies; 66+ messages in thread
From: Sudeep Holla @ 2016-04-15 17:28 UTC (permalink / raw)
  To: Joshua Henderson, Greg Kroah-Hartman
  Cc: open list, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	ralf-6z/3iImG2C8G8FEW9MqTrA, Andrei Pistirica, Jiri Slaby,
	linux-serial-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA, Sudeep Holla

Hi Greg,

I just noticed this now. I am having similar issue with MPS2 UART driver
posted @[1], hence I am asking here to get some clarification myself.
Sorry for replying on very old thread.

On Thu, Jan 14, 2016 at 1:15 AM, Joshua Henderson
<joshua.henderson-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org> wrote:
> From: Andrei Pistirica <andrei.pistirica-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
>
> This adds UART and a serial console driver for Microchip PIC32 class
> devices.
>
> Signed-off-by: Andrei Pistirica <andrei.pistirica-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
> Signed-off-by: Joshua Henderson <joshua.henderson-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
> Cc: Ralf Baechle <ralf-6z/3iImG2C8G8FEW9MqTrA@public.gmane.org>

[...]

> 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

This was posted before v4.6-rc1 similar to MPS2 UART and has taken
port# 114 for it. However MVEBU UART obtained 114 with v4.6-rc1
And MPS2 UART was assigned 115 when it got revised/reposted.

I also see this patch in linux-next with 114 itself as its port number.
So the allocation of port number needs to be resolved before it gets
merged or it's OK to wait for v4.7-rc1 ?

If it's former, can PORT_PIC32 take 116 as the latest post of MPS2 assigned
it 115 and I have pulled the same to take it via arm-soc.

I am fine with any solution, just want to be notified if I need to
take any action.

Regards,
Sudeep

[1] http://www.spinics.net/lists/devicetree/msg120727.html

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

* Re: [PATCH v5 10/14] serial: pic32_uart: Add PIC32 UART driver
  2016-04-15 17:28     ` Sudeep Holla
  (?)
@ 2016-04-16 16:09     ` Greg Kroah-Hartman
  -1 siblings, 0 replies; 66+ messages in thread
From: Greg Kroah-Hartman @ 2016-04-16 16:09 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Joshua Henderson, open list, linux-mips, ralf, Andrei Pistirica,
	Jiri Slaby, linux-serial, linux-api

On Fri, Apr 15, 2016 at 06:28:32PM +0100, Sudeep Holla wrote:
> Hi Greg,
> 
> I just noticed this now. I am having similar issue with MPS2 UART driver
> posted @[1], hence I am asking here to get some clarification myself.
> Sorry for replying on very old thread.
> 
> On Thu, Jan 14, 2016 at 1:15 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>
> 
> [...]
> 
> > 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
> 
> This was posted before v4.6-rc1 similar to MPS2 UART and has taken
> port# 114 for it. However MVEBU UART obtained 114 with v4.6-rc1
> And MPS2 UART was assigned 115 when it got revised/reposted.
> 
> I also see this patch in linux-next with 114 itself as its port number.
> So the allocation of port number needs to be resolved before it gets
> merged or it's OK to wait for v4.7-rc1 ?
> 
> If it's former, can PORT_PIC32 take 116 as the latest post of MPS2 assigned
> it 115 and I have pulled the same to take it via arm-soc.
> 
> I am fine with any solution, just want to be notified if I need to
> take any action.

Just do the merge so the numbers are allocated in sequence.

We really need to fix this up one of these days, there's no real need
for these numbers, and it's a pain in merging, as you have found out...

thanks,

greg k-h

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

end of thread, other threads:[~2016-04-16 16:09 UTC | newest]

Thread overview: 66+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-14  1:15 [PATCH v5 00/14] Initial Microchip PIC32MZDA Support Joshua Henderson
2016-01-14  1:15 ` Joshua Henderson
2016-01-14  1:15 ` Joshua Henderson
2016-01-14  1:15 ` [PATCH v5 01/14] dt/bindings: Add bindings for PIC32 interrupt controller Joshua Henderson
2016-01-14  1:15   ` Joshua Henderson
2016-01-17  3:57   ` Rob Herring
2016-01-14  1:15 ` [PATCH v5 02/14] irqchip: irq-pic32-evic: Add support " Joshua Henderson
2016-01-14  1:15   ` Joshua Henderson
2016-01-14  8:10   ` Thomas Gleixner
2016-01-14 11:12     ` Ralf Baechle
2016-01-14 11:12       ` Ralf Baechle
2016-01-14 11:12     ` Ralf Baechle
2016-01-14  1:15 ` [PATCH v5 03/14] dt/bindings: Add PIC32 clock binding documentation Joshua Henderson
2016-01-14  1:15   ` Joshua Henderson
2016-01-14  1:15 ` [PATCH v5 04/14] clk: clk-pic32: Add PIC32 clock driver Joshua Henderson
2016-01-14  1:15   ` Joshua Henderson
2016-01-26 17:04   ` Joshua Henderson
2016-01-26 17:04     ` Joshua Henderson
2016-01-29 23:58   ` Stephen Boyd
2016-02-03  5:36     ` Purna Chandra Mandal
2016-02-03  5:36       ` Purna Chandra Mandal
2016-01-14  1:15 ` [PATCH v5 05/14] dt/bindings: Add bindings for PIC32/MZDA platforms Joshua Henderson
2016-01-14  1:15   ` Joshua Henderson
2016-01-14  1:15 ` [PATCH v5 06/14] MIPS: Add support for PIC32MZDA platform Joshua Henderson
2016-01-14  1:15   ` Joshua Henderson
2016-01-14  1:15 ` [PATCH v5 07/14] dt/bindings: Add bindings for PIC32 pin control and GPIO Joshua Henderson
2016-01-14  1:15   ` Joshua Henderson
2016-01-14  1:15 ` [PATCH v5 08/14] pinctrl: pinctrl-pic32: Add PIC32 pin control driver Joshua Henderson
2016-01-14  1:15   ` Joshua Henderson
2016-01-26 17:04   ` Joshua Henderson
2016-01-26 17:04     ` Joshua Henderson
2016-01-27 13:49   ` Linus Walleij
2016-01-28  0:33     ` Joshua Henderson
2016-01-14  1:15 ` [PATCH v5 09/14] dt/bindings: Add bindings for PIC32 UART driver Joshua Henderson
2016-01-14  1:15   ` Joshua Henderson
2016-01-14  1:15 ` [PATCH v5 10/14] serial: pic32_uart: Add " Joshua Henderson
2016-01-14  1:15   ` Joshua Henderson
2016-01-14 13:55   ` One Thousand Gnomes
2016-01-14 13:55     ` One Thousand Gnomes
2016-01-14 13:55     ` One Thousand Gnomes
2016-01-26 17:04   ` Joshua Henderson
2016-01-26 17:04     ` Joshua Henderson
2016-01-26 17:33     ` Greg Kroah-Hartman
2016-01-26 17:33       ` Greg Kroah-Hartman
2016-01-27 15:55       ` Joshua Henderson
2016-01-27 15:55         ` Joshua Henderson
2016-01-27 15:55         ` Joshua Henderson
2016-02-07  7:04   ` Greg Kroah-Hartman
2016-02-07  7:04     ` Greg Kroah-Hartman
2016-04-15 17:28   ` Sudeep Holla
2016-04-15 17:28     ` Sudeep Holla
2016-04-16 16:09     ` Greg Kroah-Hartman
2016-01-14  1:15 ` [PATCH v5 11/14] dt/bindings: Add bindings for PIC32 SDHCI host controller Joshua Henderson
2016-01-14  1:15   ` Joshua Henderson
2016-02-08  9:58   ` Ulf Hansson
2016-01-14  1:15 ` [PATCH v5 12/14] mmc: sdhci-pic32: Add PIC32 SDHCI host controller driver Joshua Henderson
2016-01-14  1:15   ` Joshua Henderson
2016-01-14  1:15   ` Joshua Henderson
2016-01-26 17:04   ` Joshua Henderson
2016-01-26 17:04     ` Joshua Henderson
2016-02-08  9:59   ` Ulf Hansson
2016-02-08  9:59     ` Ulf Hansson
2016-01-14  1:15 ` [PATCH v5 13/14] MIPS: dts: Add initial DTS for the PIC32MZDA Starter Kit Joshua Henderson
2016-01-14  1:15   ` Joshua Henderson
2016-01-14  1:15 ` [PATCH v5 14/14] MIPS: pic32mzda: Add initial PIC32MZDA Starter Kit defconfig Joshua Henderson
2016-01-14  1:15   ` Joshua Henderson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.