This patch series adds support for the Microchip PIC32MZDA MIPS
platform. All drivers required to boot from MMC uSD card are
included. Clock and external interrupt controller drivers are
included. USART, console, and SDHCI peripheral drivers along with
the dependent pinctrl driver are included. This has been tested on
an applicable PIC32MZDA Starter Kit. A tree with these changes is
available at [0].
[0] https://github.com/joshua-henderson/linux/tree/pic32-upstream-v1
Andrei Pistirica (6):
DEVICETREE: Add bindings for PIC32 pin control and GPIO
pinctrl: Add PIC32 pin control driver
DEVICETREE: Add bindings for PIC32 usart driver
serial: pic32_uart: Add PIC32 uart driver
mmc: sdhci-pic32: Add PIC32 SDHC host controller driver
DEVICETREE: Add bindings for PIC32 SDHC host controller
Cristian Birsan (2):
DEVICETREE: Add bindings for PIC32 interrupt controller
irqchip: irq-pic32-evic: Add support for PIC32 interrupt controller
Joshua Henderson (4):
DEVICETREE: Add bindings for PIC32/MZDA platforms
MIPS: Add support for PIC32MZDA platform
MIPS: dts: Add initial DTS for the PIC32MZDA Starter Kit
MIPS: pic32mzda: Add initial PIC32MZDA Starter Kit defconfig
Purna Chandra Mandal (2):
DEVICETREE: Add PIC32 clock binding documentation
clk: clk-pic32: Add PIC32 clock driver
.../devicetree/bindings/clock/microchip,pic32.txt | 263 +++
.../bindings/gpio/microchip,pic32-gpio.txt | 33 +
.../microchip,pic32mz-evic.txt | 65 +
.../bindings/mips/pic32/microchip,pic32mzda.txt | 33 +
.../devicetree/bindings/mmc/sdhci-pic32.txt | 24 +
.../bindings/pinctrl/microchip,pic32-pinctrl.txt | 100 +
.../bindings/serial/microchip,pic32-usart.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 | 251 +++
arch/mips/boot/dts/pic32/pic32mzda.dtsi | 280 +++
arch/mips/boot/dts/pic32/pic32mzda_sk.dts | 150 ++
arch/mips/configs/pic32mzda_defconfig | 88 +
.../include/asm/mach-pic32/cpu-feature-overrides.h | 32 +
arch/mips/include/asm/mach-pic32/gpio.h | 26 +
arch/mips/include/asm/mach-pic32/irq.h | 22 +
arch/mips/include/asm/mach-pic32/pic32.h | 44 +
arch/mips/include/asm/mach-pic32/spaces.h | 24 +
arch/mips/pic32/Kconfig | 50 +
arch/mips/pic32/Makefile | 6 +
arch/mips/pic32/Platform | 7 +
arch/mips/pic32/common/Makefile | 5 +
arch/mips/pic32/common/irq.c | 20 +
arch/mips/pic32/common/reset.c | 62 +
arch/mips/pic32/pic32mzda/Makefile | 9 +
arch/mips/pic32/pic32mzda/config.c | 148 ++
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 | 30 +
arch/mips/pic32/pic32mzda/time.c | 47 +
drivers/clk/Kconfig | 3 +
drivers/clk/Makefile | 1 +
drivers/clk/clk-pic32.c | 1947 ++++++++++++++++++
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-pic32-evic.c | 309 +++
drivers/mmc/host/Kconfig | 11 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/sdhci-pic32.c | 354 ++++
drivers/pinctrl/Kconfig | 17 +
drivers/pinctrl/Makefile | 2 +
drivers/pinctrl/pinctrl-pic32.c | 2127 ++++++++++++++++++++
drivers/pinctrl/pinctrl-pic32.h | 158 ++
drivers/pinctrl/pinctrl-pic32mzda.c | 294 +++
drivers/pinctrl/pinctrl-pic32mzda.h | 40 +
drivers/tty/serial/Kconfig | 21 +
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/pic32_uart.c | 930 +++++++++
drivers/tty/serial/pic32_uart.h | 199 ++
.../interrupt-controller/microchip,pic32mz-evic.h | 238 +++
include/dt-bindings/pinctrl/pic32mzda.h | 404 ++++
include/linux/irqchip/pic32-evic.h | 19 +
include/linux/platform_data/sdhci-pic32.h | 22 +
include/uapi/linux/serial_core.h | 3 +
58 files changed, 9922 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,pic32mz-evic.txt
create mode 100644 Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt
create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-pic32.txt
create mode 100644 Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt
create mode 100644 Documentation/devicetree/bindings/serial/microchip,pic32-usart.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/gpio.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/pinctrl/pinctrl-pic32mzda.c
create mode 100644 drivers/pinctrl/pinctrl-pic32mzda.h
create mode 100644 drivers/tty/serial/pic32_uart.c
create mode 100644 drivers/tty/serial/pic32_uart.h
create mode 100644 include/dt-bindings/interrupt-controller/microchip,pic32mz-evic.h
create mode 100644 include/dt-bindings/pinctrl/pic32mzda.h
create mode 100644 include/linux/irqchip/pic32-evic.h
create mode 100644 include/linux/platform_data/sdhci-pic32.h
--
1.7.9.5
From: Cristian Birsan <[email protected]>
Document the devicetree bindings for the interrupt controller on Microchip
PIC32 class devices. This also adds a header defining associated interrupts
and related settings.
Signed-off-by: Cristian Birsan <[email protected]>
Signed-off-by: Joshua Henderson <[email protected]>
---
.../microchip,pic32mz-evic.txt | 65 ++++++
.../interrupt-controller/microchip,pic32mz-evic.h | 238 ++++++++++++++++++++
2 files changed, 303 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/microchip,pic32mz-evic.txt
create mode 100644 include/dt-bindings/interrupt-controller/microchip,pic32mz-evic.h
diff --git a/Documentation/devicetree/bindings/interrupt-controller/microchip,pic32mz-evic.txt b/Documentation/devicetree/bindings/interrupt-controller/microchip,pic32mz-evic.txt
new file mode 100644
index 0000000..12fb91f
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/microchip,pic32mz-evic.txt
@@ -0,0 +1,65 @@
+Microchip PIC32MZ Interrupt Controller
+======================================
+
+The Microchip PIC32MZ SOC contains an Enhanced Vectored Interrupt Controller
+(EVIC) version 2. It handles internal and external interrupts and provides
+support for priority, sub-priority, irq type and polarity.
+
+Required properties
+-------------------
+
+- compatible: Should be "microchip,evic-v2"
+
+- 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 3 and interrupt
+descriptor shall have the following format:
+ <hw_irq irq_priority_and_subpriority irq_type>
+
+hw_irq - represents the hardware interrupt number as in the data sheet.
+
+irq_priority_and_subpriority - sets the priority and sub-priority for the
+interrupt line. The INT_PRI(pri, subpri) macro can be used to set desired
+values or the DEFAULT_INT_PRI can be used for the default value.
+
+irq_type - is used to describe the type and polarity of an interrupt. For
+internal interrupts use IRQ_TYPE_EDGE_RISING for non persistent interrupts and
+IRQ_TYPE_LEVEL_HIGH for persistent interrupts. For external interrupts use
+IRQ_TYPE_EDGE_RISING or IRQ_TYPE_EDGE_FALLING to select the desired polarity.
+
+Example
+-------
+
+evic: interrupt-controller@1f810000 {
+ compatible = "microchip,evic-v2";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = <0x1f810000 0x1000>;
+ device_type="evic-v2";
+};
+
+Each device must request his interrupt line with the associated priority and
+polarity
+
+Internal interrupt DTS snippet
+------------------------------
+
+device@1f800000 {
+ ...
+ interrupt-parent = <&evic>;
+ interrupts = <UART1_RECEIVE_DONE DEFAULT_INT_PRI IRQ_TYPE_NONE>;
+ ...
+};
+
+External interrupt DTS snippet
+------------------------------
+
+device@1f800000 {
+ ...
+ interrupt-parent = <&evic>;
+ interrupts = <EXTERNAL_INTERRUPT_0 DEFAULT_INT_PRI IRQ_TYPE_EDGE_RISING>;
+ ...
+};
diff --git a/include/dt-bindings/interrupt-controller/microchip,pic32mz-evic.h b/include/dt-bindings/interrupt-controller/microchip,pic32mz-evic.h
new file mode 100644
index 0000000..2c466b8
--- /dev/null
+++ b/include/dt-bindings/interrupt-controller/microchip,pic32mz-evic.h
@@ -0,0 +1,238 @@
+/*
+ * This header provides constants for the MICROCHIP PIC32 EVIC.
+ */
+
+#ifndef _DT_BINDINGS_INTERRUPT_CONTROLLER_MICROCHIP_EVIC_H
+#define _DT_BINDINGS_INTERRUPT_CONTROLLER_MICROCHIP_EVIC_H
+
+#include <dt-bindings/interrupt-controller/irq.h>
+
+/* Hardware interrupt number */
+#define CORE_TIMER_INTERRUPT 0
+#define CORE_SOFTWARE_INTERRUPT_0 1
+#define CORE_SOFTWARE_INTERRUPT_1 2
+#define EXTERNAL_INTERRUPT_0 3
+#define TIMER1 4
+#define INPUT_CAPTURE_1_ERROR 5
+#define INPUT_CAPTURE_1 6
+#define OUTPUT_COMPARE_1 7
+#define EXTERNAL_INTERRUPT_1 8
+#define TIMER2 9
+#define INPUT_CAPTURE_2_ERROR 10
+#define INPUT_CAPTURE_2 11
+#define OUTPUT_COMPARE_2 12
+#define EXTERNAL_INTERRUPT_2 13
+#define TIMER3 14
+#define INPUT_CAPTURE_3_ERROR 15
+#define INPUT_CAPTURE_3 16
+#define OUTPUT_COMPARE_3 17
+#define EXTERNAL_INTERRUPT_3 18
+#define TIMER4 19
+#define INPUT_CAPTURE_4_ERROR 20
+#define INPUT_CAPTURE_4 21
+#define OUTPUT_COMPARE_4 22
+#define EXTERNAL_INTERRUPT_4 23
+#define TIMER5 24
+#define INPUT_CAPTURE_5_ERROR 25
+#define INPUT_CAPTURE_5 26
+#define OUTPUT_COMPARE_5 27
+#define TIMER6 28
+#define INPUT_CAPTURE_6_ERROR 29
+#define INPUT_CAPTURE_6 30
+#define OUTPUT_COMPARE_6 31
+#define TIMER7 32
+#define INPUT_CAPTURE_7_ERROR 33
+#define INPUT_CAPTURE_7 34
+#define OUTPUT_COMPARE_7 35
+#define TIMER8 36
+#define INPUT_CAPTURE_8_ERROR 37
+#define INPUT_CAPTURE_8 38
+#define OUTPUT_COMPARE_8 39
+#define TIMER9 40
+#define INPUT_CAPTURE_9_ERROR 41
+#define INPUT_CAPTURE_9 42
+#define OUTPUT_COMPARE_9 43
+/* ADC */
+#define ADC1_GLOBAL 44
+/* Reserved */
+#define ADC1_DIGITAL_COMPARATOR_1 46
+#define ADC1_DIGITAL_COMPARATOR_2 47
+#define ADC1_DIGITAL_COMPARATOR_3 48
+#define ADC1_DIGITAL_COMPARATOR_4 49
+#define ADC1_DIGITAL_COMPARATOR_5 50
+#define ADC1_DIGITAL_COMPARATOR_6 51
+#define ADC1_DIGITAL_FILTER_1 52
+#define ADC1_DIGITAL_FILTER_2 53
+#define ADC1_DIGITAL_FILTER_3 54
+#define ADC1_DIGITAL_FILTER_4 55
+#define ADC1_DIGITAL_FILTER_5 56
+#define ADC1_DIGITAL_FILTER_6 57
+/* Reserved */
+#define ADC1_DATA_0 59
+#define ADC1_DATA_1 60
+#define ADC1_DATA_2 61
+#define ADC1_DATA_3 62
+#define ADC1_DATA_4 63
+#define ADC1_DATA_5 64
+#define ADC1_DATA_6 65
+#define ADC1_DATA_7 66
+#define ADC1_DATA_8 67
+#define ADC1_DATA_9 68
+#define ADC1_DATA_10 69
+#define ADC1_DATA_11 70
+#define ADC1_DATA_12 71
+#define ADC1_DATA_13 72
+#define ADC1_DATA_14 73
+#define ADC1_DATA_15 74
+#define ADC1_DATA_16 75
+#define ADC1_DATA_17 76
+#define ADC1_DATA_18 77
+#define ADC1_DATA_19 78
+#define ADC1_DATA_20 79
+#define ADC1_DATA_21 80
+#define ADC1_DATA_22 81
+#define ADC1_DATA_23 82
+#define ADC1_DATA_24 83
+#define ADC1_DATA_25 84
+#define ADC1_DATA_26 85
+#define ADC1_DATA_27 86
+#define ADC1_DATA_28 87
+#define ADC1_DATA_29 88
+#define ADC1_DATA_30 89
+#define ADC1_DATA_31 90
+#define ADC1_DATA_32 91
+#define ADC1_DATA_33 92
+#define ADC1_DATA_34 93
+#define ADC1_DATA_35 94
+#define ADC1_DATA_36 95
+#define ADC1_DATA_37 96
+#define ADC1_DATA_38 97
+#define ADC1_DATA_39 98
+#define ADC1_DATA_40 99
+#define ADC1_DATA_41 100
+#define ADC1_DATA_42 101
+#define ADC1_DATA_43 102
+#define ADC1_DATA_44 103
+#define CORE_PERFORMANCE_COUNTER_INTERRUPT 104
+#define CORE_FAST_DEBUG_CHANNEL_INTERRUPT 105
+#define SYSTEM_BUS_PROTECTION_VIOLATION 106
+#define CRYPTO_ENGINE_EVENT 107
+/* Reserved */
+#define SPI1_FAULT 109
+#define SPI1_RECEIVE_DONE 110
+#define SPI1_TRANSFER_DONE 111
+#define UART1_FAULT 112
+#define UART1_RECEIVE_DONE 113
+#define UART1_TRANSFER_DONE 114
+#define I2C1_BUS_COLLISION_EVENT 115
+#define I2C1_SLAVE_EVENT 116
+#define I2C1_MASTER_EVENT 117
+#define PORTA_INPUT_CHANGE_INTERRUPT 118
+#define PORTB_INPUT_CHANGE_INTERRUPT 119
+#define PORTC_INPUT_CHANGE_INTERRUPT 120
+#define PORTD_INPUT_CHANGE_INTERRUPT 121
+#define PORTE_INPUT_CHANGE_INTERRUPT 122
+#define PORTF_INPUT_CHANGE_INTERRUPT 123
+#define PORTG_INPUT_CHANGE_INTERRUPT 124
+#define PORTH_INPUT_CHANGE_INTERRUPT 125
+#define PORTJ_INPUT_CHANGE_INTERRUPT 126
+#define PORTK_INPUT_CHANGE_INTERRUPT 127
+#define PARALLEL_MASTER_PORT 128
+#define PARALLEL_MASTER_PORT_ERROR 129
+#define COMPARATOR_1_INTERRUPT 130
+#define COMPARATOR_2_INTERRUPT 131
+#define USB_GENERAL_EVENT 132
+#define USB_DMA_EVENT 133
+#define DMA_CHANNEL_0 134
+#define DMA_CHANNEL_1 135
+#define DMA_CHANNEL_2 136
+#define DMA_CHANNEL_3 137
+#define DMA_CHANNEL_4 138
+#define DMA_CHANNEL_5 139
+#define DMA_CHANNEL_6 140
+#define DMA_CHANNEL_7 141
+#define SPI2_FAULT 142
+#define SPI2_RECEIVE_DONE 143
+#define SPI2_TRANSFER_DONE 144
+#define UART2_FAULT 145
+#define UART2_RECEIVE_DONE 146
+#define UART2_TRANSFER_DONE 147
+#define I2C2_BUS_COLLISION_EVENT 148
+#define I2C2_SLAVE_EVENT 149
+#define I2C2_MASTER_EVENT 150
+#define CONTROL_AREA_NETWORK_1 151
+#define CONTROL_AREA_NETWORK_2 152
+#define ETHERNET_INTERRUPT 153
+#define SPI3_FAULT 154
+#define SPI3_RECEIVE_DONE 155
+#define SPI3_TRANSFER_DONE 156
+#define UART3_FAULT 157
+#define UART3_RECEIVE_DONE 158
+#define UART3_TRANSFER_DONE 159
+#define I2C3_BUS_COLLISION_EVENT 160
+#define I2C3_SLAVE_EVENT 161
+#define I2C3_MASTER_EVENT 162
+#define SPI4_FAULT 163
+#define SPI4_RECEIVE_DONE 164
+#define SPI4_TRANSFER_DONE 165
+#define REAL_TIME_CLOCK 166
+#define FLASH_CONTROL_EVENT 167
+#define PREFETCH_MODULE_SEC_EVENT 168
+#define SQI1_EVENT 169
+#define UART4_FAULT 170
+#define UART4_RECEIVE_DONE 171
+#define UART4_TRANSFER_DONE 172
+#define I2C4_BUS_COLLISION_EVENT 173
+#define I2C4_SLAVE_EVENT 174
+#define I2C4_MASTER_EVENT 175
+#define SPI5_FAULT 176
+#define SPI5_RECEIVE_DONE 177
+#define SPI5_TRANSFER_DONE 178
+#define UART5_FAULT 179
+#define UART5_RECEIVE_DONE 180
+#define UART5_TRANSFER_DONE 181
+#define I2C5_BUS_COLLISION_EVENT 182
+#define I2C5_SLAVE_EVENT 183
+#define I2C5_MASTER_EVENT 184
+#define SPI6_FAULT 185
+#define SPI6_RECEIVE_DONE 186
+#define SPI6_TRANSFER_DONE 187
+#define UART6_FAULT 188
+#define UART6_RECEIVE_DONE 189
+#define UART6_TRANSFER_DONE 190
+#define SDHC_EVENT 191
+#define GLCD_INTERRUPT 192
+#define GPU_INTERRUPT 193
+
+/* Interrupt priority bits */
+#define PRI_0 0 /* Note:This priority disables the interrupt! */
+#define PRI_1 1
+#define PRI_2 2
+#define PRI_3 3
+#define PRI_4 4
+#define PRI_5 5
+#define PRI_6 6
+#define PRI_7 7
+
+/* Interrupt subpriority bits */
+#define SUB_PRI_0 0
+#define SUB_PRI_1 1
+#define SUB_PRI_2 2
+#define SUB_PRI_3 3
+
+#define PRI_MASK 0x7 /* 3 bit priority mask */
+#define SUBPRI_MASK 0x3 /* 2 bit subpriority mask */
+#define INT_MASK 0x1F /* 5 bit pri and subpri mask */
+#define NR_EXT_IRQS 5 /* 5 external interrupts sources */
+
+#define MICROCHIP_EVIC_MIN_PRIORITY 0
+#define MICROCHIP_EVIC_MAX_PRIORITY INT_MASK
+
+#define INT_PRI(pri, subpri) \
+ (((pri & PRI_MASK) << 2) | (subpri & SUBPRI_MASK))
+
+#define DEFINE_INT(irq, pri) { irq, pri }
+
+#define DEFAULT_INT_PRI INT_PRI(2, 0)
+
+#endif /*_DT_BINDINGS_INTERRUPT_CONTROLLER_MICROCHIP_EVIC_H*/
--
1.7.9.5
From: Cristian Birsan <[email protected]>
This adds support for the EVIC present on a PIC32MZDA.
The following features are supported:
- DT properties for EVIC and for devices that use interrupt lines
- persistent and non-persistent interrupt handling
- Priority, sub-priority and polariy settings for each interrupt line
- irqdomain support
Signed-off-by: Cristian Birsan <[email protected]>
Signed-off-by: Joshua Henderson <[email protected]>
---
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-pic32-evic.c | 309 ++++++++++++++++++++++++++++++++++++
include/linux/irqchip/pic32-evic.h | 19 +++
3 files changed, 329 insertions(+)
create mode 100644 drivers/irqchip/irq-pic32-evic.c
create mode 100644 include/linux/irqchip/pic32-evic.h
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 177f78f..e3608fc 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -55,3 +55,4 @@ obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o
obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o
obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o
obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o
+obj-$(CONFIG_MACH_PIC32) += irq-pic32-evic.o
diff --git a/drivers/irqchip/irq-pic32-evic.c b/drivers/irqchip/irq-pic32-evic.c
new file mode 100644
index 0000000..7b87b43
--- /dev/null
+++ b/drivers/irqchip/irq-pic32-evic.c
@@ -0,0 +1,309 @@
+/*
+ * Cristian Birsan <[email protected]>
+ * Copyright (C) 2015 Microchip Technology Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/irqchip.h>
+
+#include <asm/irq.h>
+#include <asm/traps.h>
+#include <dt-bindings/interrupt-controller/microchip,pic32mz-evic.h>
+
+struct irq_domain *evic_irq_domain;
+static struct evic __iomem *evic_base;
+
+static unsigned int *evic_irq_prio;
+
+struct pic_reg {
+ u32 val; /* value register*/
+ u32 clr; /* clear register */
+ u32 set; /* set register */
+ u32 inv; /* inv register */
+} __packed;
+
+struct evic {
+ struct pic_reg intcon;
+ struct pic_reg priss;
+ struct pic_reg intstat;
+ struct pic_reg iptmr;
+ struct pic_reg ifs[6];
+ u32 reserved1[8];
+ struct pic_reg iec[6];
+ u32 reserved2[8];
+ struct pic_reg ipc[48];
+ u32 reserved3[64];
+ u32 off[191];
+} __packed;
+
+static int get_ext_irq_index(irq_hw_number_t hw);
+static void evic_set_ext_irq_polarity(int ext_irq, u32 type);
+
+#define BIT_REG_MASK(bit, reg, mask) \
+ do { \
+ reg = bit/32; \
+ mask = 1 << (bit % 32); \
+ } while (0)
+
+asmlinkage void __weak plat_irq_dispatch(void)
+{
+ unsigned int irq, hwirq;
+ u32 reg, mask;
+
+ hwirq = readl(&evic_base->intstat.val) & 0xFF;
+
+ /* Check if the interrupt was really triggered by hardware*/
+ BIT_REG_MASK(hwirq, reg, mask);
+ if (likely(readl(&evic_base->ifs[reg].val) &
+ readl(&evic_base->iec[reg].val) & mask)) {
+ irq = irq_linear_revmap(evic_irq_domain, hwirq);
+ do_IRQ(irq);
+ } else
+ spurious_interrupt();
+}
+
+/* mask off an interrupt */
+static inline void mask_pic32_irq(struct irq_data *irqd)
+{
+ u32 reg, mask;
+ unsigned int hwirq = irqd_to_hwirq(irqd);
+
+ BIT_REG_MASK(hwirq, reg, mask);
+ writel(mask, &evic_base->iec[reg].clr);
+}
+
+/* unmask an interrupt */
+static inline void unmask_pic32_irq(struct irq_data *irqd)
+{
+ u32 reg, mask;
+ unsigned int hwirq = irqd_to_hwirq(irqd);
+
+ BIT_REG_MASK(hwirq, reg, mask);
+ writel(mask, &evic_base->iec[reg].set);
+}
+
+/* acknowledge an interrupt */
+static void ack_pic32_irq(struct irq_data *irqd)
+{
+ u32 reg, mask;
+ unsigned int hwirq = irqd_to_hwirq(irqd);
+
+ BIT_REG_MASK(hwirq, reg, mask);
+ writel(mask, &evic_base->ifs[reg].clr);
+}
+
+/* mask off and acknowledge an interrupt */
+static inline void mask_ack_pic32_irq(struct irq_data *irqd)
+{
+ u32 reg, mask;
+ unsigned int hwirq = irqd_to_hwirq(irqd);
+
+ BIT_REG_MASK(hwirq, reg, mask);
+ writel(mask, &evic_base->iec[reg].clr);
+ writel(mask, &evic_base->ifs[reg].clr);
+}
+
+static int set_type_pic32_irq(struct irq_data *data, unsigned int flow_type)
+{
+ int index;
+
+ switch (flow_type) {
+
+ case IRQ_TYPE_EDGE_RISING:
+ case IRQ_TYPE_EDGE_FALLING:
+ irq_set_handler_locked(data, handle_edge_irq);
+ break;
+
+ case IRQ_TYPE_LEVEL_HIGH:
+ case IRQ_TYPE_LEVEL_LOW:
+ irq_set_handler_locked(data, handle_fasteoi_irq);
+ break;
+
+ default:
+ pr_err("Invalid interrupt type !\n");
+ return -EINVAL;
+ }
+
+ /* set polarity for external interrupts only */
+ index = get_ext_irq_index(data->hwirq);
+ if (index >= 0)
+ evic_set_ext_irq_polarity(index, flow_type);
+
+ return IRQ_SET_MASK_OK;
+}
+
+static void pic32_bind_evic_interrupt(int irq, int set)
+{
+ writel(set, &evic_base->off[irq]);
+}
+
+int pic32_get_c0_compare_int(void)
+{
+ int virq;
+
+ virq = irq_create_mapping(evic_irq_domain, CORE_TIMER_INTERRUPT);
+ irq_set_irq_type(virq, IRQ_TYPE_EDGE_RISING);
+ return virq;
+}
+
+static struct irq_chip pic32_irq_chip = {
+ .name = "PIC32-EVIC",
+ .irq_ack = ack_pic32_irq,
+ .irq_mask = mask_pic32_irq,
+ .irq_mask_ack = mask_ack_pic32_irq,
+ .irq_unmask = unmask_pic32_irq,
+ .irq_eoi = ack_pic32_irq,
+ .irq_set_type = set_type_pic32_irq,
+ .irq_enable = unmask_pic32_irq,
+ .irq_disable = mask_pic32_irq,
+};
+
+static void evic_set_irq_priority(int irq, int priority)
+{
+ u32 reg, shift;
+
+ reg = irq / 4;
+ shift = (irq % 4) * 8;
+
+ /* set priority */
+ writel(INT_MASK << shift, &evic_base->ipc[reg].clr);
+ writel(priority << shift, &evic_base->ipc[reg].set);
+}
+
+static void evic_set_ext_irq_polarity(int ext_irq, u32 type)
+{
+ if (WARN_ON(ext_irq >= NR_EXT_IRQS))
+ return;
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ writel(1 << ext_irq, &evic_base->intcon.set);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ writel(1 << ext_irq, &evic_base->intcon.clr);
+ break;
+ default:
+ pr_err("Invalid external interrupt polarity !\n");
+ }
+}
+
+static int get_ext_irq_index(irq_hw_number_t hw)
+{
+ switch (hw) {
+ case EXTERNAL_INTERRUPT_0:
+ return 0;
+ case EXTERNAL_INTERRUPT_1:
+ return 1;
+ case EXTERNAL_INTERRUPT_2:
+ return 2;
+ case EXTERNAL_INTERRUPT_3:
+ return 3;
+ case EXTERNAL_INTERRUPT_4:
+ return 4;
+ default:
+ return -1;
+ }
+}
+
+static int evic_intc_map(struct irq_domain *irqd, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ u32 reg, mask;
+
+ irq_set_chip(virq, &pic32_irq_chip);
+
+ BIT_REG_MASK(hw, reg, mask);
+
+ /* disable */
+ writel(mask, &evic_base->iec[reg].clr);
+
+ /* clear flag */
+ writel(mask, &evic_base->ifs[reg].clr);
+
+ evic_set_irq_priority(hw, evic_irq_prio[hw]);
+
+ return 0;
+}
+
+static int evic_irq_domain_xlate(struct irq_domain *d,
+ struct device_node *ctrlr,
+ const u32 *intspec,
+ unsigned int intsize,
+ irq_hw_number_t *out_hwirq,
+ unsigned int *out_type)
+{
+ /* Check for number of params */
+ if (WARN_ON(intsize < 3))
+ return -EINVAL;
+ if (WARN_ON(intspec[0] >= NR_IRQS))
+ return -EINVAL;
+ /* Check for correct priority settings */
+ if (WARN_ON((intspec[1] < MICROCHIP_EVIC_MIN_PRIORITY)
+ || (intspec[1] > MICROCHIP_EVIC_MAX_PRIORITY)))
+ return -EINVAL;
+
+ *out_hwirq = intspec[0];
+
+ evic_irq_prio[*out_hwirq] = intspec[1];
+
+ *out_type = intspec[2];
+
+ return 0;
+}
+
+static const struct irq_domain_ops evic_intc_irq_domain_ops = {
+ .map = evic_intc_map,
+ .xlate = evic_irq_domain_xlate,
+};
+
+#ifdef CONFIG_OF
+
+static int __init
+microchip_evic_of_init(struct device_node *node, struct device_node *parent)
+{
+ struct resource res;
+
+ if (WARN_ON(!node))
+ return -ENODEV;
+
+ evic_irq_prio = kcalloc(NR_IRQS, sizeof(*evic_irq_prio),
+ GFP_KERNEL);
+ if (!evic_irq_prio)
+ return -ENOMEM;
+
+ evic_irq_prio[CORE_TIMER_INTERRUPT] = DEFAULT_INT_PRI; /* Default IRQ*/
+
+ if (of_address_to_resource(node, 0, &res))
+ panic("Failed to get evic memory range");
+
+ if (request_mem_region(res.start, resource_size(&res),
+ res.name) == NULL)
+ panic("Failed to request evic memory");
+
+ evic_base = ioremap_nocache(res.start, resource_size(&res));
+ if (!evic_base)
+ panic("Failed to remap evic memory");
+
+ board_bind_eic_interrupt = &pic32_bind_evic_interrupt;
+
+ evic_irq_domain = irq_domain_add_linear(node, NR_IRQS,
+ &evic_intc_irq_domain_ops, NULL);
+ if (!evic_irq_domain)
+ panic("Failed to add linear irqdomain for EVIC");
+
+ irq_set_default_host(evic_irq_domain);
+
+ return 0;
+}
+
+IRQCHIP_DECLARE(microchip_evic, "microchip,evic-v2", microchip_evic_of_init);
+#endif
diff --git a/include/linux/irqchip/pic32-evic.h b/include/linux/irqchip/pic32-evic.h
new file mode 100644
index 0000000..c514bae
--- /dev/null
+++ b/include/linux/irqchip/pic32-evic.h
@@ -0,0 +1,19 @@
+/*
+ * Joshua Henderson, <[email protected]>
+ * Copyright (C) 2015 Microchip Technology Inc. All rights reserved.
+ *
+ * This program is free software; you can distribute it and/or modify it
+ * under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+#ifndef __LINUX_IRQCHIP_PIC32_EVIC_H
+#define __LINUX_IRQCHIP_PIC32_EVIC_H
+
+extern int pic32_get_c0_compare_int(void);
+
+#endif /* __LINUX_IRQCHIP_PIC32_EVIC_H */
--
1.7.9.5
From: Purna Chandra Mandal <[email protected]>
Document the devicetree bindings for the clock driver found on Microchip
PIC32 class devices.
Signed-off-by: Purna Chandra Mandal <[email protected]>
Signed-off-by: Joshua Henderson <[email protected]>
---
.../devicetree/bindings/clock/microchip,pic32.txt | 263 ++++++++++++++++++++
1 file changed, 263 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..4cef72d
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/microchip,pic32.txt
@@ -0,0 +1,263 @@
+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 have "microchip,pic32-clk".
+- 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,pic32-clk";
+ interrupts = <12>;
+ ranges;
+
+ /* secondary oscillator; external input on SOSCI pin */
+ SOSC:sosc_clk {
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-sosc";
+ clock-frequency = <32768>;
+ reg = <0x1f801200 0x10 /* enable reg */
+ 0x1f801390 0x10>; /* status reg */
+ microchip,bit-mask = <0x02>; /* enable mask */
+ microchip,status-bit-mask = <0x10>; /* status-mask*/
+ };
+
+ FRCDIV:frcdiv_clk {
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-frcdivclk";
+ clocks = <&FRC>;
+ clock-output-names = "frcdiv_clk";
+ };
+
+ /* System PLL clock */
+ SYSPLL:spll_clk {
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-syspll";
+ reg = <0x1f801220 0x10 /* SPLL register */
+ 0x1f801390 0x10>; /* CLKSTAT register */
+ clocks = <&POSC>, <&FRC>;
+ clock-output-names = "sys_pll";
+ microchip,status-bit-mask = <0x80>; /* SPLLRDY */
+ };
+
+ /* system clock; mux with postdiv & slew */
+ SYSCLK:sys_clk {
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-sysclk-v2";
+ reg = <0x1f8013c0 0x04>; /* SLEWCON */
+ clocks = <&FRCDIV>, <&SYSPLL>, <&POSC>, <&SOSC>,
+ <&LPRC>, <&FRCDIV>;
+ microchip,clock-indices = <0>, <1>, <2>, <4>, <5>, <7>;
+ clock-output-names = "sys_clk";
+ };
+
+ /* DDR Ctrl & DDR PHY PLL */
+ MPLL: CLK_MPLL {
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-mpll";
+ reg = <0x1f800100 0x04>; /* CFGMPLL */
+ clocks = <&POSC>;
+ clock-output-names = "pic32-mpll";
+ status = "disabled";
+ };
+
+ /* Peripheral bus1 clock */
+ PBCLK1:pb1_clk {
+ reg = <0x1f801340 0x10>;
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-pbclk";
+ clocks = <&SYSCLK>;
+ clock-output-names = "pb1_clk";
+ /* used by system modules, not gateable */
+ microchip,ignore-unused;
+ };
+
+ /* Peripheral bus2 clock */
+ PBCLK2:pb2_clk {
+ reg = <0x1f801350 0x10>;
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-pbclk";
+ clocks = <&SYSCLK>;
+ clock-output-names = "pb2_clk";
+ /* avoid gating even if unused */
+ microchip,ignore-unused;
+ };
+
+ /* Peripheral bus3 clock */
+ PBCLK3:pb3_clk {
+ reg = <0x1f801360 0x10>;
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-pbclk";
+ clocks = <&SYSCLK>;
+ clock-output-names = "pb3_clk";
+ };
+
+ /* Peripheral bus4 clock(I/O ports, GPIO) */
+ PBCLK4:pb4_clk {
+ reg = <0x1f801370 0x10>;
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-pbclk";
+ clocks = <&SYSCLK>;
+ clock-output-names = "pb4_clk";
+ };
+
+ /* Peripheral bus clock */
+ PBCLK5:pb5_clk {
+ reg = <0x1f801380 0x10>;
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-pbclk";
+ clocks = <&SYSCLK>;
+ clock-output-names = "pb5_clk";
+ };
+
+ /* Peripheral Bus6 clock; */
+ PBCLK6:pb6_clk {
+ reg = <0x1f801390 0x10>;
+ compatible = "microchip,pic32-pbclk";
+ clocks = <&SYSCLK>;
+ #clock-cells = <0>;
+ };
+
+ /* Peripheral bus7 clock */
+ PBCLK7:pb7_clk {
+ reg = <0x1f8013A0 0x10>;
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-pbclk";
+ /* CPU is driven by this clock; so named */
+ clock-output-names = "cpu_clk";
+ clocks = <&SYSCLK>;
+ };
+
+ /* Reference Oscillator clock for SPI/I2S */
+ REFCLKO1:refo1_clk {
+ reg = <0x1f801280 0x20>;
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-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";
+ clock-frequency = <10000000>; /* 10Mhz for I2S MCLK */
+ };
+
+ /* Reference Oscillator clock for SQI */
+ REFCLKO2:refo2_clk {
+ reg = <0x1f8012A0 0x20>;
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-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";
+ clock-frequency = <50000000>; /* 50MHz for SQI */
+ };
+
+ /* Reference Oscillator clock, ADC */
+ REFCLKO3:refo3_clk {
+ reg = <0x1f8012C0 0x20>;
+ compatible = "microchip,pic32-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";
+ clock-frequency = <10000000>; /* 1Mhz */
+ };
+
+ /* Reference Oscillator clock */
+ REFCLKO4:refo4_clk {
+ reg = <0x1f8012E0 0x20>;
+ compatible = "microchip,pic32-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";
+ clock-frequency = <25000000>; /* 25Mhz */
+ };
+
+ /* Reference Oscillator clock, LCD */
+ REFCLKO5:refo5_clk {
+ reg = <0x1f801300 0x20>;
+ compatible = "microchip,pic32-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";
+ clock-frequency = <40000000>; /* 40Mhz */
+ };
+};
+
+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,pic32-uart";
+ reg = <>;
+ interrupts = <>;
+ clocks = <&PBCLK2>;
+}
--
1.7.9.5
From: Purna Chandra Mandal <[email protected]>
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 <[email protected]>
Signed-off-by: Joshua Henderson <[email protected]>
---
drivers/clk/Kconfig | 3 +
drivers/clk/Makefile | 1 +
drivers/clk/clk-pic32.c | 1947 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1951 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..bc00f5e
--- /dev/null
+++ b/drivers/clk/clk-pic32.c
@@ -0,0 +1,1947 @@
+/*
+ * Purna Chandra Mandal,<[email protected]>
+ * 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_SOSCEN_MASK 0x02
+#define OSC_CLK_FAILED 0x04
+#define OSC_SPLL_LOCKED 0x20 /* SPLL is locked by SCLK MUX */
+#define OSC_SYS_LOCK 0x80
+#define OSC_SOSC_READY BIT(22)
+
+/* 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
+
+/* CFGMPLL Register fields */
+#define MPLL_IDIV 0x3f /* Input divider */
+#define MPLL_MULT 0xff /* Feedback multiplier */
+#define MPLL_MULT_SHIFT 8
+#define MPLL_VREG_DISABLE BIT(22) /* Voltage Regulator Disable */
+#define MPLL_VREG_READY BIT(23) /* Voltage Regulator Ready */
+#define MPLL_ODIV1 0x7 /* Output divider */
+#define MPLL_ODIV1_SHIFT 24
+#define MPLL_ODIV2 0x7 /* Output Divider (second stage) */
+#define MPLL_ODIV2_SHIFT 27
+#define MPLL_DISABLE BIT(30) /* PLL disable */
+#define MPLL_READY BIT(31) /* PLL Ready */
+
+/* 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;
+};
+
+/* DDR Phy & DDR clk */
+struct pic32_mpll {
+ struct clk_hw hw;
+ void __iomem *regs;
+};
+
+/* 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)
+
+#ifdef __CLK_DEBUG
+#define __clk_debug(fmt, args...) \
+ pr_info("%s:%d::" fmt, __func__, __LINE__, ##args)
+#else
+#define __clk_debug(fmt, args...)
+#endif
+
+#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)
+#define clkhw_to_mpll(_hw) container_of(_hw, struct pic32_mpll, 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);
+ }
+
+ __clk_debug("%s,rate %lu /best_parent(%s, %lu) /best %lu /delta %d\n",
+ __clk_get_name(clk), rate, __clk_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);
+
+ __clk_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;
+ }
+
+ __clk_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 int mpll_clk_is_enable(struct clk_hw *hw)
+{
+ struct pic32_mpll *mpll = clkhw_to_mpll(hw);
+ u32 v = clk_readl(mpll->regs);
+
+ return (!(v & MPLL_DISABLE)) && (v & MPLL_READY);
+}
+
+static int mpll_clk_enable(struct clk_hw *hw)
+{
+ return 0;
+}
+
+static void mpll_clk_disable(struct clk_hw *hw)
+{
+}
+
+static unsigned long mpll_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ u64 rate;
+ u32 v, idiv, odiv1, odiv2, mul;
+ struct pic32_mpll *mpll = clkhw_to_mpll(hw);
+
+ v = clk_readl(mpll->regs);
+ idiv = v & MPLL_IDIV;
+ mul = (v >> MPLL_MULT_SHIFT) & MPLL_MULT;
+ odiv1 = (v >> MPLL_ODIV1_SHIFT) & MPLL_ODIV1;
+ odiv2 = (v >> MPLL_ODIV2_SHIFT) & MPLL_ODIV2;
+
+ rate = (parent_rate / idiv) * mul;
+ do_div(rate, odiv1);
+ do_div(rate, odiv2);
+
+ return (unsigned long)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 mpll_clk_ops = {
+ .is_enabled = mpll_clk_is_enable,
+ .enable = mpll_clk_enable,
+ .disable = mpll_clk_disable,
+ .recalc_rate = mpll_clk_recalc_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 *mpll_clk_register(const char *name,
+ const char *parent, void __iomem *regs)
+{
+ struct clk_init_data init;
+ struct clk *clk;
+ struct pic32_mpll *mpll;
+
+ init_clk_data(init, name, &parent, 1,
+ CLK_IGNORE_UNUSED | CLK_IS_BASIC, &mpll_clk_ops);
+
+ mpll = kzalloc(sizeof(*mpll), GFP_KERNEL);
+ if (!mpll)
+ return ERR_PTR(-ENOMEM);
+
+ /* init mpll */
+ mpll->hw.init = &init;
+ mpll->regs = regs;
+
+ clk = clk_register(NULL, &mpll->hw);
+ if (IS_ERR(clk)) {
+ pr_err("mem_pll: failed to register clk\n");
+ kfree(mpll);
+ }
+
+ 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;
+
+ if (of_property_read_u32(np, "microchip,bit-mask", &bitmask))
+ bitmask = OSC_SOSCEN_MASK;
+
+ if (of_property_read_u32(np, "microchip,status-bit-mask", &stsmask))
+ stsmask = OSC_SOSC_READY;
+
+ /* allocate fixed-rate clock */
+ sosc = kzalloc(sizeof(*sosc), GFP_KERNEL);
+ if (!sosc)
+ return;
+
+ init_clk_data(init, name, NULL, 0,
+ CLK_IS_BASIC | CLK_IS_ROOT, &sosc_ops);
+
+ /* struct clk assignments */
+ sosc->fixed_rate = rate;
+ sosc->hw.init = &init;
+ sosc->regs = regs;
+ sosc->status_reg = status_reg;
+ sosc->bitmask = bitmask;
+ sosc->status_bitmask = stsmask;
+
+ /* register the clock */
+ clk = clk_register(NULL, &sosc->hw);
+ if (IS_ERR(clk))
+ kfree(sosc);
+ else
+ pic32_of_clk_register_clkdev(np, clk);
+}
+
+static void __init of_periph_clk_setup(struct device_node *np)
+{
+ const char *parent_name;
+ const char *name = np->name;
+ struct clk *clk;
+ u32 flags = 0;
+ void __iomem *regs;
+
+ regs = of_iomap(np, 0);
+ if (!regs) {
+ pr_err("%s: could not get reg property\n", name);
+ return;
+ }
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name) {
+ pr_err("pbclk: %s must have a parent\n", name);
+ goto err_map;
+ }
+
+ if (of_find_property(np, "microchip,ignore-unused", NULL)) {
+ flags |= CLK_IGNORE_UNUSED;
+ pr_info("%s: ignore gating even if unused\n", name);
+ }
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ /* register peripheral clock */
+ clk = periph_clk_register(name, &parent_name, regs, flags);
+ if (IS_ERR(clk)) {
+ pr_err("%s: could not register clock\n", name);
+ goto err_map;
+ }
+
+ pic32_of_clk_register_clkdev(np, clk);
+
+ return;
+
+err_map:
+ iounmap(regs);
+}
+
+static void __init of_refo_clk_setup(struct device_node *np)
+{
+ struct clk *clk;
+ int ret, i, count;
+ const char **parents;
+ const char *clk_name = np->name;
+ void __iomem *regs;
+ u32 *parent_idx, rate;
+
+ /* get the input clock source count */
+ count = of_clk_get_parent_count(np);
+ if (count < 0) {
+ pr_err("%s: get clock count error\n", np->name);
+ return;
+ }
+
+ parents = kzalloc((sizeof(char *) * count), GFP_KERNEL);
+ if (!parents)
+ return;
+
+ ret = pic32_of_clk_get_parent_indices(np, &parent_idx, count);
+ if (ret)
+ goto err_parent;
+
+ for (i = 0; i < count; i++)
+ parents[i] = of_clk_get_parent_name(np, i);
+
+ /* get iobase */
+ regs = of_iomap(np, 0);
+ if (!regs) {
+ pr_err("%s: could not get reg property\n", np->name);
+ goto err_parent_idx;
+ }
+
+ of_property_read_string(np, "clock-output-names", &clk_name);
+
+ clk = refo_clk_register(clk_name, parents, count, regs, parent_idx);
+ if (IS_ERR(clk)) {
+ pr_err("%s: could not register clock\n", clk_name);
+ goto err_map;
+ }
+
+ /* by default refclk(s) are disabled and unconfigured until
+ * requested by client module(s) & programmaed to enable & set rate.
+ * Instead, if provided by OF, driver can program default clock rate.
+ */
+ ret = of_property_read_u32(np, "clock-frequency", (u32 *)&rate);
+ if (!ret) {
+ /* clk get & prepare */
+ __clk_get(clk);
+ clk_prepare(clk);
+
+ /* set initial rate */
+ clk_set_rate(clk, rate);
+
+ /* keep it enabled */
+ clk_enable(clk);
+ }
+
+ pic32_of_clk_register_clkdev(np, clk);
+
+ goto err_parent;
+
+err_map:
+ iounmap(regs);
+err_parent_idx:
+ kfree(parent_idx);
+err_parent:
+ kfree(parents);
+}
+
+static void __init of_sys_mux_slew_setup(struct device_node *np)
+{
+ struct clk *clk;
+ int ret, i, count;
+ const char *clk_name;
+ const char **parents;
+ u32 *parent_idx, slew, v;
+ unsigned long flags;
+ void __iomem *slew_reg;
+
+ /* get the input clock source count */
+ count = of_clk_get_parent_count(np);
+ if (count < 0) {
+ pr_err("%s: get clock count error\n", np->name);
+ return;
+ }
+
+ parents = kzalloc((sizeof(char *) * count), GFP_KERNEL);
+ if (!parents)
+ return;
+
+ ret = pic32_of_clk_get_parent_indices(np, &parent_idx, count);
+ if (ret)
+ goto err_name;
+
+ for (i = 0; i < count; i++)
+ parents[i] = of_clk_get_parent_name(np, i);
+
+ ret = of_property_read_string_index(np, "clock-output-names",
+ 0, &clk_name);
+ if (ret)
+ clk_name = np->name;
+
+ /* get slew base */
+ slew_reg = of_iomap(np, 0);
+ if (!slew_reg) {
+ pr_warn("%s: no slew register ?\n", clk_name);
+ goto err_name;
+ }
+
+ /* register mux clk */
+ clk = sys_mux_clk_register(clk_name, parents, count, pic32_clk_regbase,
+ slew_reg, parent_idx, &sclk_postdiv_ops);
+ if (IS_ERR(clk)) {
+ pr_err("%s: could not register clock\n", clk_name);
+ goto err_parent_idx;
+ }
+
+ /* enable slew, if asked */
+ if (!of_property_read_u32(np, "microchip,slew-step", &slew)) {
+ __clk_lock(flags);
+
+ v = clk_readl(slew_reg);
+ /* Apply new slew-div and enable up/down slewing */
+ v &= ~(SLEW_DIV << SLEW_DIV_SHIFT);
+ v |= (slew << SLEW_DIV_SHIFT);
+ v |= SLEW_DOWNEN | SLEW_UPEN;
+ clk_writel(v, slew_reg);
+
+ __clk_unlock(flags);
+ }
+
+ /* register clkdev */
+ pic32_of_clk_register_clkdev(np, clk);
+
+ goto err_name;
+
+err_parent_idx:
+ iounmap(slew_reg);
+ kfree(parent_idx);
+err_name:
+ kfree(parents);
+}
+
+static void __init of_sys_pll_setup(struct device_node *np)
+{
+ int i, count;
+ const char *clk_name = np->name;
+ const char **parent_names;
+ const char *plliclk_name = "spll_mux_clk";
+ void __iomem *regs, *stat_reg;
+ struct clk *clk, *mux_clk;
+ u32 bitmask;
+
+ /* get the input clock source count */
+ count = of_clk_get_parent_count(np);
+ if (count < 0) {
+ pr_err("%s: get clock count error, %d\n", np->name, count);
+ return;
+ }
+
+ parent_names = kzalloc((sizeof(char *) * count), GFP_KERNEL);
+ if (!parent_names)
+ return;
+
+ for (i = 0; i < count; i++)
+ parent_names[i] = of_clk_get_parent_name(np, i);
+
+ /* get output name */
+ of_property_read_string(np, "clock-output-names", &clk_name);
+
+ /* get iobase */
+ regs = of_iomap(np, 0);
+ if (!regs) {
+ pr_err("%s: of_iomap failed\n", np->name);
+ goto err_name;
+ }
+
+ /* get status reg & status bitmask */
+ stat_reg = of_iomap(np, 1);
+
+ if (of_property_read_u32(np, "microchip,status-bit-mask", &bitmask)) {
+ /* OSC_SPLL_LOCKED (bit5 of reg OSCCON) does not behave as
+ * documented. Bit is asserted if SYSCLK uses SPLL as clksrc and
+ * vice-versa. So use of this bit to detect success or failure
+ * might not be reliable.
+ */
+ bitmask = OSC_SPLL_LOCKED;
+ stat_reg = regs - 0x20; /* OSCCON */
+ }
+
+ /* 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_mem_pll_setup(struct device_node *np)
+{
+ void __iomem *regs;
+ struct clk *clk;
+ const char *parent_name;
+ const char *clk_name = np->name;
+
+ /* get parent name */
+ parent_name = of_clk_get_parent_name(np, 0);
+
+ /* 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);
+ return;
+ }
+
+ /* register memory-pll clock */
+ clk = mpll_clk_register(clk_name, parent_name, regs);
+ if (IS_ERR(clk)) {
+ pr_err("spll_clk: not registered\n");
+ goto err_unmap;
+ }
+
+ pic32_of_clk_register_clkdev(np, clk);
+ return;
+
+err_unmap:
+ iounmap(regs);
+}
+
+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,pic32-refoclk", .data = of_refo_clk_setup, },
+ { .compatible = "microchip,pic32-pbclk", .data = of_periph_clk_setup, },
+ { .compatible = "microchip,pic32-syspll", .data = of_sys_pll_setup, },
+ { .compatible = "microchip,pic32-sosc", .data = of_sosc_clk_setup, },
+ { .compatible = "microchip,pic32-frcdivclk", .data = of_frcdiv_setup, },
+ { .compatible = "microchip,pic32-mpll", .data = of_mem_pll_setup, },
+ { .compatible = "microchip,pic32-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; uses 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,pic32-clk", of_pic32_soc_clock_init);
--
1.7.9.5
This adds support for the Microchip PIC32 platform along with the
specific variant PIC32MZDA on a PIC32MZDA Starter Kit.
Signed-off-by: Joshua Henderson <[email protected]>
---
.../bindings/mips/pic32/microchip,pic32mzda.txt | 33 ++++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt
diff --git a/Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt b/Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt
new file mode 100644
index 0000000..bcf3e04
--- /dev/null
+++ b/Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt
@@ -0,0 +1,33 @@
+* Microchip PIC32MZDA Platforms
+
+PIC32MZDA Starter Kit
+Required root node properties:
+ - compatible = "microchip,pic32mzda-sk", "microchip,pic32mzda"
+
+CPU nodes:
+----------
+A "cpus" node is required. Required properties:
+ - #address-cells: Must be 1.
+ - #size-cells: Must be 0.
+A CPU sub-node is also required. Required properties:
+ - device_type: Must be "cpu".
+ - compatible: Must be "mti,mips14KEc".
+Example:
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "mti,mips14KEc";
+ };
+ };
+
+Boot protocol
+--------------
+In accordance with the MIPS UHI specification[1], the bootloader must pass the
+following arguments to the kernel:
+ - $a0: -2.
+ - $a1: KSEG0 address of the flattened device-tree blob.
+
+[1] http://prplfoundation.org/wiki/MIPS_documentation
--
1.7.9.5
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 <[email protected]>
---
arch/mips/Kbuild.platforms | 1 +
arch/mips/Kconfig | 9 +
.../include/asm/mach-pic32/cpu-feature-overrides.h | 32 +++
arch/mips/include/asm/mach-pic32/gpio.h | 26 ++
arch/mips/include/asm/mach-pic32/irq.h | 22 ++
arch/mips/include/asm/mach-pic32/pic32.h | 44 ++++
arch/mips/include/asm/mach-pic32/spaces.h | 24 ++
arch/mips/pic32/Kconfig | 34 +++
arch/mips/pic32/Makefile | 6 +
arch/mips/pic32/Platform | 7 +
arch/mips/pic32/common/Makefile | 5 +
arch/mips/pic32/common/irq.c | 20 ++
arch/mips/pic32/common/reset.c | 62 +++++
arch/mips/pic32/pic32mzda/Makefile | 9 +
arch/mips/pic32/pic32mzda/config.c | 148 +++++++++++
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 | 30 +++
arch/mips/pic32/pic32mzda/time.c | 47 ++++
include/linux/platform_data/sdhci-pic32.h | 22 ++
23 files changed, 1497 insertions(+)
create mode 100644 arch/mips/include/asm/mach-pic32/cpu-feature-overrides.h
create mode 100644 arch/mips/include/asm/mach-pic32/gpio.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 <[email protected]>
+ * 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/gpio.h b/arch/mips/include/asm/mach-pic32/gpio.h
new file mode 100644
index 0000000..92d8b58
--- /dev/null
+++ b/arch/mips/include/asm/mach-pic32/gpio.h
@@ -0,0 +1,26 @@
+/*
+ * Joshua Henderson, <[email protected]>
+ * Copyright (C) 2014 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_GPIO_H
+#define __ASM_MACH_PIC32_GPIO_H
+
+#define ARCH_NR_GPIOS 352
+
+#define gpio_to_irq(gpio) __gpio_to_irq(gpio)
+#define gpio_get_value __gpio_get_value
+#define gpio_set_value __gpio_set_value
+#define gpio_cansleep __gpio_cansleep
+
+#include <asm-generic/gpio.h>
+
+#endif /* __ASM_MACH_PIC32_GPIO_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 <[email protected]>
+ * 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..a3cdec0
--- /dev/null
+++ b/arch/mips/include/asm/mach-pic32/pic32.h
@@ -0,0 +1,44 @@
+/*
+ * Joshua Henderson <[email protected]>
+ * 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 <[email protected]>
+ * Copyright (C) 2015 Microchip Technology Inc. All rights reserved.
+ *
+ * This program is free software; you can distribute it and/or modify it
+ * under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+#ifndef _ASM_MACH_PIC32_SPACES_H
+#define _ASM_MACH_PIC32_SPACES_H
+
+#ifdef CONFIG_PIC32MZDA
+#define PHYS_OFFSET _AC(0x08000000, UL)
+#define UNCAC_BASE _AC(0xa8000000, UL)
+#endif
+
+#include <asm/mach-generic/spaces.h>
+
+#endif /* __ASM_MACH_PIC32_SPACES_H */
diff --git a/arch/mips/pic32/Kconfig b/arch/mips/pic32/Kconfig
new file mode 100644
index 0000000..0161f09
--- /dev/null
+++ b/arch/mips/pic32/Kconfig
@@ -0,0 +1,34 @@
+if MACH_PIC32
+
+choice
+ prompt "Machine Type"
+
+config PIC32MZDA
+ bool "Microchip PIC32MZDA Platform"
+ select BOOT_ELF32
+ select BOOT_RAW
+ select CEVT_R4K
+ select CSRC_R4K
+ select DMA_NONCOHERENT
+ select SYS_HAS_CPU_MIPS32_R2
+ select SYS_HAS_EARLY_PRINTK
+ select SYS_SUPPORTS_32BIT_KERNEL
+ select SYS_SUPPORTS_LITTLE_ENDIAN
+ select ARCH_REQUIRE_GPIOLIB
+ select HAVE_MACH_CLKDEV
+ select COMMON_CLK
+ select CLKDEV_LOOKUP
+ select LIBFDT
+ select USE_OF
+ select PINCTRL
+ help
+ Support for the Microchip PIC32MZDA microcontroller.
+
+ This is a 32-bit microcontroller with support for external or
+ internally packaged DDR2 memory up to 128MB.
+
+ For more information, see <http://www.microchip.com/>.
+
+endchoice
+
+endif # MACH_PIC32
diff --git a/arch/mips/pic32/Makefile b/arch/mips/pic32/Makefile
new file mode 100644
index 0000000..fd357f4
--- /dev/null
+++ b/arch/mips/pic32/Makefile
@@ -0,0 +1,6 @@
+#
+# Joshua Henderson, <[email protected]>
+# 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, <[email protected]>
+# 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..01d4596
--- /dev/null
+++ b/arch/mips/pic32/common/irq.c
@@ -0,0 +1,20 @@
+/*
+ * Joshua Henderson <[email protected]>
+ * 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>
+
+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 <[email protected]>
+ * 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, <[email protected]>
+# 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..c6b02a6
--- /dev/null
+++ b/arch/mips/pic32/pic32mzda/config.c
@@ -0,0 +1,148 @@
+/*
+ * Purna Chandra Mandal, [email protected]
+ * 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);
+}
+
+void pic32_enable_ebi_addr_pins(int mask)
+{
+ pic32_conf_modify_atomic(PIC32_CFGEBIA, ~0, mask);
+}
+
+void pic32_enable_ebi_ctrl_pins(int mask)
+{
+ pic32_conf_modify_atomic(PIC32_CFGEBIC, ~0, mask);
+}
+
+int pic32_set_sdhc_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);
+}
+
+u32 pic32_get_device_id(void)
+{
+ return pic32_conf_get_reg_field(PIC32_DEVID, 0, 0x0fffffff);
+}
+
+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 pic32_set_flash_ecc_mode(int mode)
+{
+ pic32_conf_modify_atomic(PIC32_CFGCON, 0x3 << 4, mode << 4);
+}
+EXPORT_SYMBOL(pic32_set_flash_ecc_mode);
+
+u32 pic32_get_flash_ecc_mode(void)
+{
+ return pic32_conf_get_reg_field(PIC32_CFGCON, 4, 0x3);
+}
+EXPORT_SYMBOL(pic32_get_flash_ecc_mode);
+
+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 <[email protected]>
+ * 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 <[email protected]>
+ * 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 <[email protected]>
+ * 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 <[email protected]>
+ * 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..1b9bef1
--- /dev/null
+++ b/arch/mips/pic32/pic32mzda/init.c
@@ -0,0 +1,156 @@
+/*
+ * Joshua Henderson, [email protected]
+ * 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_sdhc_adma_fifo_threshold,
+};
+
+static struct of_dev_auxdata pic32_auxdata_lookup[] __initdata = {
+ OF_DEV_AUXDATA("microchip,pic32-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..0c2872f
--- /dev/null
+++ b/arch/mips/pic32/pic32mzda/pic32mzda.h
@@ -0,0 +1,30 @@
+/*
+ * Joshua Henderson <[email protected]>
+ * 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_sdhc_adma_fifo_threshold(u32 rthrs, u32 wthrs);
+u32 pic32_get_boot_status(void);
+u32 pic32_get_usb_pll_mode(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..b04e3e6
--- /dev/null
+++ b/arch/mips/pic32/pic32mzda/time.c
@@ -0,0 +1,47 @@
+/*
+ * Joshua Henderson <[email protected]>
+ * Copyright (C) 2015 Microchip Technology Inc. All rights reserved.
+ *
+ * This program is free software; you can distribute it and/or modify it
+ * under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clocksource.h>
+#include <linux/init.h>
+#include <linux/irqchip/pic32-evic.h>
+#include <linux/of.h>
+
+#include <asm/time.h>
+
+#include "pic32mzda.h"
+
+unsigned int get_c0_compare_int(void)
+{
+ return pic32_get_c0_compare_int();
+}
+
+void __init plat_time_init(void)
+{
+ struct clk *clk;
+
+ of_clk_init(NULL);
+ clk = clk_get_sys("cpu_clk", NULL);
+ if (IS_ERR(clk)) {
+ clk = clk_get_sys("pb7_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, [email protected]
+ * 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
From: Andrei Pistirica <[email protected]>
Document the devicetree bindings for PINCTRL and GPIO found on Microchip
PIC32 class devices. This also adds a header defining related port and
peripheral pin select functionality.
Signed-off-by: Andrei Pistirica <[email protected]>
Signed-off-by: Joshua Henderson <[email protected]>
---
.../bindings/gpio/microchip,pic32-gpio.txt | 33 ++
.../bindings/pinctrl/microchip,pic32-pinctrl.txt | 100 +++++
include/dt-bindings/pinctrl/pic32mzda.h | 404 ++++++++++++++++++++
3 files changed, 537 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt
create mode 100644 Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt
create mode 100644 include/dt-bindings/pinctrl/pic32mzda.h
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..f6eeb2f
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt
@@ -0,0 +1,33 @@
+* Microchip PIC32 GPIO devices (PIO).
+
+Required properties:
+ - compatible: "microchip,pic32-gpio"
+ - reg: Base address and length for the device.
+ - interrupts: The port interrupt shared be 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 unused.
+ - 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:
+ PIC32_PIN_CN_RISING : low-to-high edge triggered.
+ PIC32_PIN_CN_FALLING : high-to-low edge triggered.
+ PIC32_PIN_CN_BOTH : low-to-high and high-to-low edges triggered.
+
+Note:
+ - If gpio-ranges is missing, then all the pins (32) related to the gpio bank
+ are enabled.
+
+Example:
+ pioA: gpio@1f860000 {
+ compatible = "microchip,pic32-gpio";
+ reg = <0x1f860000 0x24>;
+ interrupts = <PORTA_INPUT_CHANGE_INTERRUPT
+ DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-ranges = <&pic32_pinctrl 0 0 32>;
+ clocks = <&PBCLK4>;
+ };
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..7cf4167
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt
@@ -0,0 +1,100 @@
+* Microchip PIC32 Pinmux device.
+
+Please refer to pinctrl-bindings.txt for details of the pinctrl properties and
+common 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
+single pins or a pairs of mux and related configuration.
+
+Required properties for pic32 device:
+ - compatible: "microchip,pic32-pinctrl", "microchip,pic32mz-pinctrl"
+ - reg: Base address and length for pps:in and pps:out registers.
+
+Properties for 'pin configuration node':
+ - pic32,pins: each entry consists of 3 intergers and represents the mux and
+ config settings for one pin. The first integer represent the remappable pin,
+ the second represent the peripheral pin and the last the configuration.
+ The format is pic32,pins = <PIC32_RP_'dir'_'pin'
+ PIC32_PP_'dir'_'peripherl-pin' PIC32_PIN_CONF_'config'>. The configurations
+ are divided in 2 classes: IN and OUT and each in 4 buckets. Each entry must
+ contains items from the same class and bucket, otherwise the driver will
+ notify an error and the initialization will fail.
+ - pic32,single-pins: each entry consists of 3 intergers and represents a pin
+ (that is not remappable) and related configuraion. The format is
+ pic32,single-pins = <PORT_'x' 'pin' PIC32_PIN_CONF_'config'>. Each port has
+ 32 pins and please refer to chip documentation for details of remappable
+ pins.
+
+Available pin configurations (refer to dt-bindings/pinctrl/pic32.h):
+ PIC32_PIN_CONF_NONE : no configuration (default).
+ PIC32_PIN_CONF_OD : indicate this pin need a open-drain (no direction).
+ PIC32_PIN_CONF_OD_OUT : indicate this pin need a open-drain out.
+ PIC32_PIN_CONF_PU : indicate this pin need a pull up (no direction).
+ PIC32_PIN_CONF_PU_IN : indicate this pin need a pull up in.
+ PIC32_PIN_CONF_PD : indicate this pin need a pull down (no direction).
+ PIC32_PIN_CONF_PD_IN : indicate this pin need a pull down input.
+ PIC32_PIN_CONF_AN : indicate this pin as analogic (no direction).
+ PIC32_PIN_CONF_AN_IN : indicate this pin as analogic input.
+ PIC32_PIN_CONF_DG : indicate this pin as digital (no direction).
+ PIC32_PIN_CONF_DG_IN : indicate this pin as digital input.
+ PIC32_PIN_CONF_DG_OUT : indicate this pin as digital output.
+
+NOTEs:
+1. The pins functions nods are defined under pic32 pinctrl node. The function's
+ pin groups are defined under functions node.
+2. Each pin group can have both pic32,pins and pic32,single-pins properties to
+ specify re-mappable or non-remappable pins with related mux and configs or
+ at least one.
+3. Each pin configuration node can have a phandle and devices can set pins
+ configurations by referring to the phandle of that pin configuration node.
+4. The pinctrl bindings are listed in dt-bindings/pinctrl/pic32.h.
+5. The gpio controller must be described in the pinctrl simple-bus.
+
+Example:
+pinctrl@1f800000{
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "microchip,pic32-pinctrl", "simple-bus";
+ ranges;
+ reg = <0x1f801404 0x3c>, /* in */
+ <0x1f801538 0x57>; /* out */
+
+ pioA: gpio@1f860000 {
+ compatible = "microchip,pic32-gpio";
+ reg = <0x1f860000 0x24>;
+ gpio-controller;
+ };
+
+ /* functions */
+ sw1 {
+ pinctrl_sw1: sw1-0 {
+ pic32,single-pins = <PORT_B 12 PIC32_PIN_CONF_PULLUP>;
+ };
+ };
+
+ uart1 {
+ pinctrl_uart1: uart1-0 {
+ pic32,pins =
+ <PIC32_RP_OUT_RPG7 PIC32_PP_OUT_U1TX PIC32_PIN_CONF_NONE
+ PIC32_RP_IN_RPG8 PIC32_PP_IN_U1RX PIC32_PIN_CONF_NONE>;
+ };
+ };
+};
+
+uart1: serial@1f822000 {
+ compatible = "microchip,pic32-uart";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+};
+
+sw@1 {
+ compatible = "microchip,pic32-switch";
+
+ interrupt-parent = <&pioB>;
+ interrupts = <12 PIC32_CN_RISING>;/* GPIO_B_12 */
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sw1>;
+};
diff --git a/include/dt-bindings/pinctrl/pic32mzda.h b/include/dt-bindings/pinctrl/pic32mzda.h
new file mode 100644
index 0000000..2fb86e8
--- /dev/null
+++ b/include/dt-bindings/pinctrl/pic32mzda.h
@@ -0,0 +1,404 @@
+/*
+ * This header provides constants for PIC32MZDA pinctrl bindings.
+ *
+ * Copyright (C) 2015 Sorin-Andrei Pistirica <[email protected]>
+ *
+ * GPLv2 or later.
+*/
+
+#ifndef __DT_BINDINGS_PIC32MZDA_PINCTRL_H__
+#define __DT_BINDINGS_PIC32MZDA_PINCTRL_H__
+
+/* cods offsets */
+#define BANK_OFF 0
+#define RP_OFF 5
+#define PP_OFF 0
+#define BUCKET_OFF 10
+#define DIR_OFF 15
+#define COD_OFF 16
+
+#define BANK(x) ((x) << (BANK_OFF))
+#define RPIN(x) ((x) << (RP_OFF))
+#define PPIN(x) ((x) << (PP_OFF))
+#define BUCKET(x) ((x) << (BUCKET_OFF))
+#define PINDIR(x) ((x) << (DIR_OFF))
+#define COD(x) ((x) << (COD_OFF))
+
+#define PP(x_pin, x_bucket, x_dir) \
+ ((PPIN(x_pin)) | (BUCKET(x_bucket)) | (PINDIR(x_dir)))
+
+#define RP(x_bank, x_pin, x_bucket, x_dir) \
+ ((BANK(x_bank)) | (RPIN(x_pin)) | \
+ (BUCKET(x_bucket)) | (PINDIR(x_dir)))
+
+/* pin direction
+ * Note: for PPS direction has 1 bit: IN or OUT and
+ * for CONF direction has 2 bit: IN, OUT or NONE
+ */
+#ifdef DIR_IN
+#undef DIR_IN
+#endif
+#ifdef DIR_OUT
+#undef DIR_OUT
+#endif
+#define DIR_IN 0 /* 00 */
+#define DIR_OUT 1 /* 01 */
+#define DIR_NONE 2 /* 10 */
+
+/* buckets */
+#define BUCKET_A (0x1) /* 00001 */
+#define BUCKET_B (0x2) /* 00010 */
+#define BUCKET_C (0x4) /* 00100 */
+#define BUCKET_D (0x8) /* 01000 */
+#define BUCKET_AB (BUCKET_A | BUCKET_B) /* 00011 */
+#define BUCKET_BD (BUCKET_B | BUCKET_D) /* 01010 */
+#define BUCKET_CD (BUCKET_C | BUCKET_D) /* 01100 */
+
+/* port, bank */
+#define PORT_A 0
+#define PORT_B 1
+#define PORT_C 2
+#define PORT_D 3
+#define PORT_E 4
+#define PORT_F 5
+#define PORT_G 6
+#define PORT_H 7
+#define PORT_J 8
+#define PORT_K 9
+
+/* peripheral pins */
+#define PP_INT0 0
+#define PP_INT1 1
+#define PP_INT2 2
+#define PP_INT3 3
+#define PP_INT4 4
+#define PP_T2CK 5
+#define PP_T3CK 6
+#define PP_T4CK 7
+#define PP_T5CK 8
+#define PP_T6CK 9
+#define PP_T7CK 10
+#define PP_T8CK 11
+#define PP_T9CK 12
+#define PP_IC1 13
+#define PP_IC2 14
+#define PP_IC3 15
+#define PP_IC4 16
+#define PP_IC5 17
+#define PP_IC6 18
+#define PP_IC7 19
+#define PP_IC8 20
+#define PP_IC9 21
+#define PP_OCFA 22
+#define PP_OCFB 23
+#define PP_U1RX 24
+#define PP_U1CTS 25
+#define PP_U2RX 26
+#define PP_U2CTS 27
+#define PP_U3RX 28
+#define PP_U3CTS 29
+#define PP_U4RX 30
+#define PP_U4CTS 31
+#define PP_U5RX 32
+#define PP_U5CTS 33
+#define PP_U6RX 34
+#define PP_U6CTS 35
+#define PP_SDI1 36
+#define PP_SS1 37
+#define PP_SDI2 38
+#define PP_SS2 39
+#define PP_SDI3 40
+#define PP_SS3 41
+#define PP_SDI4 42
+#define PP_SS4 43
+#define PP_SDI5 44
+#define PP_SS5 45
+#define PP_SDI6 46
+#define PP_SS6 47
+#define PP_C1RX 48
+#define PP_C2RX 49
+#define PP_REFCLKI1 50
+#define PP_REFCLKI3 51
+#define PP_REFCLKI4 52
+#define PP_U1RTS 53
+#define PP_U2RTS 54
+#define PP_U3RTS 55
+#define PP_U4RTS 56
+#define PP_U5RTS 57
+#define PP_U6RTS 58
+#define PP_U1TX 59
+#define PP_U2TX 60
+#define PP_U3TX 61
+#define PP_U4TX 62
+#define PP_U5TX 63
+#define PP_U6TX 64
+#define PP_REFCLKO1 65
+#define PP_REFCLKO3 66
+#define PP_REFCLKO4 67
+#define PP_SDO1 68
+#define PP_SDO2 69
+#define PP_SDO3 70
+#define PP_SDO4 71
+#define PP_SDO5 72
+#define PP_SDO6 73
+#define PP_OC1 74
+#define PP_OC2 75
+#define PP_OC3 76
+#define PP_OC4 77
+#define PP_OC5 78
+#define PP_OC6 79
+#define PP_OC7 80
+#define PP_OC8 81
+#define PP_OC9 82
+#define PP_C1OUT 83
+#define PP_C2OUT 84
+#define PP_C1TX 85
+#define PP_C2TX 86
+#define PP_SENTINEL 87
+/* add above this line and update the PP_MAX accordingly */
+#define PP_MAX 88
+
+/* MUX INPUT ----------------------------------------------------------------*/
+/* Peripheral pins: BUCKET A */
+#define PIC32_PP_IN_INT3 (PP(PP_INT3, BUCKET_A, DIR_IN))
+#define PIC32_PP_IN_T2CK (PP(PP_T2CK, BUCKET_A, DIR_IN))
+#define PIC32_PP_IN_T6CK (PP(PP_T6CK, BUCKET_A, DIR_IN))
+#define PIC32_PP_IN_IC3 (PP(PP_IC3, BUCKET_A, DIR_IN))
+#define PIC32_PP_IN_IC7 (PP(PP_IC7, BUCKET_A, DIR_IN))
+#define PIC32_PP_IN_U1RX (PP(PP_U1RX, BUCKET_A, DIR_IN))
+#define PIC32_PP_IN_U2CTS (PP(PP_U2CTS, BUCKET_A, DIR_IN))
+#define PIC32_PP_IN_U5RX (PP(PP_U5RX, BUCKET_A, DIR_IN))
+#define PIC32_PP_IN_U6CTS (PP(PP_U6CTS, BUCKET_A, DIR_IN))
+#define PIC32_PP_IN_SDI1 (PP(PP_SDI1, BUCKET_A, DIR_IN))
+#define PIC32_PP_IN_SDI3 (PP(PP_SDI3, BUCKET_A, DIR_IN))
+#define PIC32_PP_IN_SDI5 (PP(PP_SDI5, BUCKET_A, DIR_IN))
+#define PIC32_PP_IN_SS6 (PP(PP_SS6, BUCKET_A, DIR_IN))
+#define PIC32_PP_IN_REFCLKI1 (PP(PP_REFCLKI1, BUCKET_A, DIR_IN))
+
+/* Peripheral pins: BUCKET B */
+#define PIC32_PP_IN_INT4 (PP(PP_INT4, BUCKET_B, DIR_IN))
+#define PIC32_PP_IN_T5CK (PP(PP_T5CK, BUCKET_B, DIR_IN))
+#define PIC32_PP_IN_T7CK (PP(PP_T7CK, BUCKET_B, DIR_IN))
+#define PIC32_PP_IN_IC4 (PP(PP_IC4, BUCKET_B, DIR_IN))
+#define PIC32_PP_IN_IC8 (PP(PP_IC8, BUCKET_B, DIR_IN))
+#define PIC32_PP_IN_U3RX (PP(PP_U3RX, BUCKET_B, DIR_IN))
+#define PIC32_PP_IN_U4CTS (PP(PP_U4CTS, BUCKET_B, DIR_IN))
+#define PIC32_PP_IN_SDI2 (PP(PP_SDI2, BUCKET_B, DIR_IN))
+#define PIC32_PP_IN_SDI4 (PP(PP_SDI4, BUCKET_B, DIR_IN))
+#define PIC32_PP_IN_C1RX (PP(PP_C1RX, BUCKET_B, DIR_IN))
+#define PIC32_PP_IN_REFCLKI4 (PP(PP_REFCLKI4, BUCKET_A, DIR_IN))
+
+/* Peripheral pins: BUCKET C */
+#define PIC32_PP_IN_INT2 (PP(PP_INT2, BUCKET_C, DIR_IN))
+#define PIC32_PP_IN_T3CK (PP(PP_T3CK, BUCKET_C, DIR_IN))
+#define PIC32_PP_IN_T8CK (PP(PP_T8CK, BUCKET_C, DIR_IN))
+#define PIC32_PP_IN_IC2 (PP(PP_IC2, BUCKET_C, DIR_IN))
+#define PIC32_PP_IN_IC5 (PP(PP_IC5, BUCKET_C, DIR_IN))
+#define PIC32_PP_IN_IC9 (PP(PP_IC9, BUCKET_C, DIR_IN))
+#define PIC32_PP_IN_U1CTS (PP(PP_U1CTS, BUCKET_C, DIR_IN))
+#define PIC32_PP_IN_U2RX (PP(PP_U2RX, BUCKET_C, DIR_IN))
+#define PIC32_PP_IN_U5CTS (PP(PP_U5CTS, BUCKET_C, DIR_IN))
+#define PIC32_PP_IN_SS1 (PP(PP_SS1, BUCKET_C, DIR_IN))
+#define PIC32_PP_IN_SS3 (PP(PP_SS3, BUCKET_C, DIR_IN))
+#define PIC32_PP_IN_SS4 (PP(PP_SS4, BUCKET_C, DIR_IN))
+#define PIC32_PP_IN_SS5 (PP(PP_SS5, BUCKET_C, DIR_IN))
+#define PIC32_PP_IN_C2RX (PP(PP_C2RX, BUCKET_C, DIR_IN))
+
+/* Peripheral pins: BUCKET D */
+#define PIC32_PP_IN_INT1 (PP(PP_INT1, BUCKET_D, DIR_IN))
+#define PIC32_PP_IN_T4CK (PP(PP_T4CK, BUCKET_D, DIR_IN))
+#define PIC32_PP_IN_T9CK (PP(PP_T9CK, BUCKET_D, DIR_IN))
+#define PIC32_PP_IN_IC1 (PP(PP_IC1, BUCKET_D, DIR_IN))
+#define PIC32_PP_IN_IC6 (PP(PP_IC6, BUCKET_D, DIR_IN))
+#define PIC32_PP_IN_U3CTS (PP(PP_U3CTS, BUCKET_D, DIR_IN))
+#define PIC32_PP_IN_U4RX (PP(PP_U4RX, BUCKET_D, DIR_IN))
+#define PIC32_PP_IN_U6RX (PP(PP_U6RX, BUCKET_D, DIR_IN))
+#define PIC32_PP_IN_SS2 (PP(PP_SS2, BUCKET_D, DIR_IN))
+#define PIC32_PP_IN_SDI6 (PP(PP_SDI6, BUCKET_D, DIR_IN))
+#define PIC32_PP_IN_OCFA (PP(PP_OCFA, BUCKET_D, DIR_IN))
+#define PIC32_PP_IN_REFCLKI3 (PP(PP_REFCLKI3, BUCKET_D, DIR_IN))
+
+/* Remappable pins: BUCKET A */
+#define PIC32_RP_IN_RPD2 (RP(PORT_D, 2, BUCKET_A, DIR_IN) | COD(0x0))
+#define PIC32_RP_IN_RPG8 (RP(PORT_G, 8, BUCKET_A, DIR_IN) | COD(0x1))
+#define PIC32_RP_IN_RPF4 (RP(PORT_F, 4, BUCKET_A, DIR_IN) | COD(0x2))
+#define PIC32_RP_IN_RPF1 (RP(PORT_F, 1, BUCKET_A, DIR_IN) | COD(0x4))
+#define PIC32_RP_IN_RPB9 (RP(PORT_B, 9, BUCKET_A, DIR_IN) | COD(0x5))
+#define PIC32_RP_IN_RPB10 (RP(PORT_B, 10, BUCKET_A, DIR_IN) | COD(0x6))
+#define PIC32_RP_IN_RPC14 (RP(PORT_C, 14, BUCKET_A, DIR_IN) | COD(0x7))
+#define PIC32_RP_IN_RPB5 (RP(PORT_B, 5, BUCKET_A, DIR_IN) | COD(0x8))
+#define PIC32_RP_IN_RPC1 (RP(PORT_C, 1, BUCKET_A, DIR_IN) | COD(0xA))
+#define PIC32_RP_IN_RPD14 (RP(PORT_D, 14, BUCKET_A, DIR_IN) | COD(0xB))
+#define PIC32_RP_IN_RPG1 (RP(PORT_G, 1, BUCKET_A, DIR_IN) | COD(0xC))
+#define PIC32_RP_IN_RPA14 (RP(PORT_A, 14, BUCKET_A, DIR_IN) | COD(0xD))
+#define PIC32_RP_IN_RPD6 (RP(PORT_D, 6, BUCKET_A, DIR_IN) | COD(0xE))
+
+/* Remappable pins: BUCKET B */
+#define PIC32_RP_IN_RPD3 (RP(PORT_D, 3, BUCKET_B, DIR_IN) | COD(0x0))
+#define PIC32_RP_IN_RPG7 (RP(PORT_G, 7, BUCKET_B, DIR_IN) | COD(0x1))
+#define PIC32_RP_IN_RPF5 (RP(PORT_F, 5, BUCKET_B, DIR_IN) | COD(0x2))
+#define PIC32_RP_IN_RPD11 (RP(PORT_D, 11, BUCKET_B, DIR_IN) | COD(0x3))
+#define PIC32_RP_IN_RPF0 (RP(PORT_F, 0, BUCKET_B, DIR_IN) | COD(0x4))
+#define PIC32_RP_IN_RPB1 (RP(PORT_B, 1, BUCKET_B, DIR_IN) | COD(0x5))
+#define PIC32_RP_IN_RPE5 (RP(PORT_E, 5, BUCKET_B, DIR_IN) | COD(0x6))
+#define PIC32_RP_IN_RPC13 (RP(PORT_C, 13, BUCKET_B, DIR_IN) | COD(0x7))
+#define PIC32_RP_IN_RPB3 (RP(PORT_B, 3, BUCKET_B, DIR_IN) | COD(0x8))
+#define PIC32_RP_IN_RPC4 (RP(PORT_C, 4, BUCKET_B, DIR_IN) | COD(0xA))
+#define PIC32_RP_IN_RPG0 (RP(PORT_G, 0, BUCKET_B, DIR_IN) | COD(0xC))
+#define PIC32_RP_IN_RPA15 (RP(PORT_A, 15, BUCKET_B, DIR_IN) | COD(0xD))
+#define PIC32_RP_IN_RPD7 (RP(PORT_D, 7, BUCKET_B, DIR_IN) | COD(0xE))
+
+/* Remappable pins: BUCKET C */
+#define PIC32_RP_IN_RPD9 (RP(PORT_D, 9, BUCKET_C, DIR_IN) | COD(0x0))
+#define PIC32_RP_IN_RPB8 (RP(PORT_B, 8, BUCKET_C, DIR_IN) | COD(0x2))
+#define PIC32_RP_IN_RPB15 (RP(PORT_B, 15, BUCKET_C, DIR_IN) | COD(0x3))
+#define PIC32_RP_IN_RPD4 (RP(PORT_D, 4, BUCKET_C, DIR_IN) | COD(0x4))
+#define PIC32_RP_IN_RPB0 (RP(PORT_B, 0, BUCKET_C, DIR_IN) | COD(0x5))
+#define PIC32_RP_IN_RPE3 (RP(PORT_E, 3, BUCKET_C, DIR_IN) | COD(0x6))
+#define PIC32_RP_IN_RPB7 (RP(PORT_B, 7, BUCKET_C, DIR_IN) | COD(0x7))
+#define PIC32_RP_IN_RPF12 (RP(PORT_F, 12, BUCKET_C, DIR_IN) | COD(0x9))
+#define PIC32_RP_IN_RPD12 (RP(PORT_D, 12, BUCKET_C, DIR_IN) | COD(0xA))
+#define PIC32_RP_IN_RPF8 (RP(PORT_F, 8, BUCKET_C, DIR_IN) | COD(0xB))
+#define PIC32_RP_IN_RPC3 (RP(PORT_C, 3, BUCKET_C, DIR_IN) | COD(0xC))
+#define PIC32_RP_IN_RPE9 (RP(PORT_E, 9, BUCKET_C, DIR_IN) | COD(0xD))
+
+/* Remappable pins: BUCKET D */
+#define PIC32_RP_IN_RPG9 (RP(PORT_G, 9, BUCKET_D, DIR_IN) | COD(0x1))
+#define PIC32_RP_IN_RPD0 (RP(PORT_D, 0, BUCKET_D, DIR_IN) | COD(0x3))
+#define PIC32_RP_IN_RPB6 (RP(PORT_B, 6, BUCKET_D, DIR_IN) | COD(0x5))
+#define PIC32_RP_IN_RPD5 (RP(PORT_D, 5, BUCKET_D, DIR_IN) | COD(0x6))
+#define PIC32_RP_IN_RPB2 (RP(PORT_B, 2, BUCKET_D, DIR_IN) | COD(0x7))
+#define PIC32_RP_IN_RPF3 (RP(PORT_F, 3, BUCKET_D, DIR_IN) | COD(0x8))
+#define PIC32_RP_IN_RPF2 (RP(PORT_F, 2, BUCKET_D, DIR_IN) | COD(0xB))
+#define PIC32_RP_IN_RPC2 (RP(PORT_C, 2, BUCKET_D, DIR_IN) | COD(0xC))
+#define PIC32_RP_IN_RPE8 (RP(PORT_E, 8, BUCKET_D, DIR_IN) | COD(0xD))
+
+/* MUX OUTPUT ---------------------------------------------------------------*/
+/* Remappable pins: BUCKET A */
+#define PIC32_RP_OUT_RPD2 (RP(PORT_D, 2, BUCKET_A, DIR_OUT))
+#define PIC32_RP_OUT_RPG8 (RP(PORT_G, 8, BUCKET_A, DIR_OUT))
+#define PIC32_RP_OUT_RPF4 (RP(PORT_F, 4, BUCKET_A, DIR_OUT))
+#define PIC32_RP_OUT_RPF1 (RP(PORT_F, 1, BUCKET_A, DIR_OUT))
+#define PIC32_RP_OUT_RPB9 (RP(PORT_B, 9, BUCKET_A, DIR_OUT))
+#define PIC32_RP_OUT_RPB10 (RP(PORT_B, 10, BUCKET_A, DIR_OUT))
+#define PIC32_RP_OUT_RPB5 (RP(PORT_B, 5, BUCKET_A, DIR_OUT))
+#define PIC32_RP_OUT_RPC1 (RP(PORT_C, 1, BUCKET_A, DIR_OUT))
+#define PIC32_RP_OUT_RPD14 (RP(PORT_D, 14, BUCKET_A, DIR_OUT))
+#define PIC32_RP_OUT_RPG1 (RP(PORT_G, 1, BUCKET_A, DIR_OUT))
+#define PIC32_RP_OUT_RPA14 (RP(PORT_A, 14, BUCKET_A, DIR_OUT))
+#define PIC32_RP_OUT_RPD6 (RP(PORT_D, 6, BUCKET_A, DIR_OUT))
+
+/* Remappable pins: BUCKET B */
+#define PIC32_RP_OUT_RPD3 (RP(PORT_D, 3, BUCKET_B, DIR_OUT))
+#define PIC32_RP_OUT_RPG7 (RP(PORT_G, 7, BUCKET_B, DIR_OUT))
+#define PIC32_RP_OUT_RPF5 (RP(PORT_F, 5, BUCKET_B, DIR_OUT))
+#define PIC32_RP_OUT_RPD11 (RP(PORT_D, 11, BUCKET_B, DIR_OUT))
+#define PIC32_RP_OUT_RPF0 (RP(PORT_F, 0, BUCKET_B, DIR_OUT))
+#define PIC32_RP_OUT_RPB1 (RP(PORT_B, 1, BUCKET_B, DIR_OUT))
+#define PIC32_RP_OUT_RPE5 (RP(PORT_E, 5, BUCKET_B, DIR_OUT))
+#define PIC32_RP_OUT_RPB3 (RP(PORT_B, 3, BUCKET_B, DIR_OUT))
+#define PIC32_RP_OUT_RPC4 (RP(PORT_C, 4, BUCKET_B, DIR_OUT))
+#define PIC32_RP_OUT_RPG0 (RP(PORT_G, 0, BUCKET_B, DIR_OUT))
+#define PIC32_RP_OUT_RPA15 (RP(PORT_A, 15, BUCKET_B, DIR_OUT))
+#define PIC32_RP_OUT_RPD7 (RP(PORT_D, 7, BUCKET_B, DIR_OUT))
+
+/* Remappable pins: BUCKET C */
+#define PIC32_RP_OUT_RPD9 (RP(PORT_D, 9, BUCKET_C, DIR_OUT))
+#define PIC32_RP_OUT_RPB8 (RP(PORT_B, 8, BUCKET_C, DIR_OUT))
+#define PIC32_RP_OUT_RPB15 (RP(PORT_B, 15, BUCKET_C, DIR_OUT))
+#define PIC32_RP_OUT_RPD4 (RP(PORT_D, 4, BUCKET_C, DIR_OUT))
+#define PIC32_RP_OUT_RPB0 (RP(PORT_B, 0, BUCKET_C, DIR_OUT))
+#define PIC32_RP_OUT_RPE3 (RP(PORT_E, 3, BUCKET_C, DIR_OUT))
+#define PIC32_RP_OUT_RPB7 (RP(PORT_B, 7, BUCKET_C, DIR_OUT))
+#define PIC32_RP_OUT_RPF12 (RP(PORT_F, 12, BUCKET_C, DIR_OUT))
+#define PIC32_RP_OUT_RPD12 (RP(PORT_D, 12, BUCKET_C, DIR_OUT))
+#define PIC32_RP_OUT_RPF8 (RP(PORT_F, 8, BUCKET_C, DIR_OUT))
+#define PIC32_RP_OUT_RPC3 (RP(PORT_C, 3, BUCKET_C, DIR_OUT))
+#define PIC32_RP_OUT_RPE9 (RP(PORT_E, 9, BUCKET_C, DIR_OUT))
+
+/* Remappable pins: BUCKET D */
+#define PIC32_RP_OUT_RPG9 (RP(PORT_G, 9, BUCKET_D, DIR_OUT))
+#define PIC32_RP_OUT_RPD0 (RP(PORT_D, 0, BUCKET_D, DIR_OUT))
+#define PIC32_RP_OUT_RPB6 (RP(PORT_B, 6, BUCKET_D, DIR_OUT))
+#define PIC32_RP_OUT_RPD5 (RP(PORT_D, 5, BUCKET_D, DIR_OUT))
+#define PIC32_RP_OUT_RPB2 (RP(PORT_B, 2, BUCKET_D, DIR_OUT))
+#define PIC32_RP_OUT_RPF3 (RP(PORT_F, 3, BUCKET_D, DIR_OUT))
+#define PIC32_RP_OUT_RPC2 (RP(PORT_C, 2, BUCKET_D, DIR_OUT))
+#define PIC32_RP_OUT_RPE8 (RP(PORT_E, 8, BUCKET_D, DIR_OUT))
+#define PIC32_RP_OUT_RPF2 (RP(PORT_F, 2, BUCKET_D, DIR_OUT))
+
+/* Peripheral pins: all buckets */
+#define PIC32_PP_OUT_U3TX (PP(PP_U3TX, BUCKET_A, DIR_OUT) | COD(0x1))
+#define PIC32_PP_OUT_U4RTS (PP(PP_U4RTS, BUCKET_A, DIR_OUT) | COD(0x2))
+#define PIC32_PP_OUT_SDO1 (PP(PP_SDO1, BUCKET_AB, DIR_OUT) | COD(0x5))
+#define PIC32_PP_OUT_SDO2 (PP(PP_SDO2, BUCKET_AB, DIR_OUT) | COD(0x6))
+#define PIC32_PP_OUT_SDO3 (PP(PP_SDO3, BUCKET_AB, DIR_OUT) | COD(0x7))
+#define PIC32_PP_OUT_SDO5 (PP(PP_SDO5, BUCKET_AB, DIR_OUT) | COD(0x9))
+#define PIC32_PP_OUT_SS6 (PP(PP_SS6, BUCKET_A, DIR_OUT) | COD(0xA))
+#define PIC32_PP_OUT_OC3 (PP(PP_OC3, BUCKET_A, DIR_OUT) | COD(0xB))
+#define PIC32_PP_OUT_OC6 (PP(PP_OC6, BUCKET_A, DIR_OUT) | COD(0xC))
+#define PIC32_PP_OUT_REFCLKO4 (PP(PP_REFCLKO4, BUCKET_A, DIR_OUT) | COD(0xD))
+#define PIC32_PP_OUT_C2OUT (PP(PP_C2OUT, BUCKET_A, DIR_OUT) | COD(0xE))
+#define PIC32_PP_OUT_C1TX (PP(PP_C1TX, BUCKET_A, DIR_OUT) | COD(0xF))
+
+#define PIC32_PP_OUT_U1TX (PP(PP_U1TX, BUCKET_B, DIR_OUT) | COD(0x1))
+#define PIC32_PP_OUT_U2RTS (PP(PP_U2RTS, BUCKET_B, DIR_OUT) | COD(0x2))
+#define PIC32_PP_OUT_U5TX (PP(PP_U5TX, BUCKET_B, DIR_OUT) | COD(0x3))
+#define PIC32_PP_OUT_U6RTS (PP(PP_U6RTS, BUCKET_B, DIR_OUT) | COD(0x4))
+#define PIC32_PP_OUT_SDO4 (PP(PP_SDO4, BUCKET_BD, DIR_OUT) | COD(0x8))
+#define PIC32_PP_OUT_OC4 (PP(PP_OC4, BUCKET_B, DIR_OUT) | COD(0xB))
+#define PIC32_PP_OUT_OC7 (PP(PP_OC7, BUCKET_B, DIR_OUT) | COD(0xC))
+#define PIC32_PP_OUT_REFCLKO1 (PP(PP_REFCLKO1, BUCKET_B, DIR_OUT) | COD(0xF))
+
+#define PIC32_PP_OUT_U3RTS (PP(PP_U3RTS, BUCKET_C, DIR_OUT) | COD(0x1))
+#define PIC32_PP_OUT_U4TX (PP(PP_U4TX, BUCKET_C, DIR_OUT) | COD(0x2))
+#define PIC32_PP_OUT_U6TX (PP(PP_U6TX, BUCKET_CD, DIR_OUT) | COD(0x4))
+#define PIC32_PP_OUT_SS1 (PP(PP_SS1, BUCKET_C, DIR_OUT) | COD(0x5))
+#define PIC32_PP_OUT_SS3 (PP(PP_SS3, BUCKET_C, DIR_OUT) | COD(0x7))
+#define PIC32_PP_OUT_SS4 (PP(PP_SS4, BUCKET_C, DIR_OUT) | COD(0x8))
+#define PIC32_PP_OUT_SS5 (PP(PP_SS5, BUCKET_C, DIR_OUT) | COD(0x9))
+#define PIC32_PP_OUT_SDO6 (PP(PP_SDO6, BUCKET_CD, DIR_OUT) | COD(0xA))
+#define PIC32_PP_OUT_OC5 (PP(PP_OC5, BUCKET_C, DIR_OUT) | COD(0xB))
+#define PIC32_PP_OUT_OC8 (PP(PP_OC8, BUCKET_C, DIR_OUT) | COD(0xC))
+#define PIC32_PP_OUT_C1OUT (PP(PP_C1OUT, BUCKET_C, DIR_OUT) | COD(0xE))
+#define PIC32_PP_OUT_REFCLKO3 (PP(PP_REFCLKO3, BUCKET_C, DIR_OUT) | COD(0xF))
+
+#define PIC32_PP_OUT_U1RTS (PP(PP_U1RTS, BUCKET_D, DIR_OUT) | COD(0x1))
+#define PIC32_PP_OUT_U2TX (PP(PP_U2TX, BUCKET_D, DIR_OUT) | COD(0x2))
+#define PIC32_PP_OUT_U5RTS (PP(PP_U5RTS, BUCKET_D, DIR_OUT) | COD(0x3))
+#define PIC32_PP_OUT_SS2 (PP(PP_SS2, BUCKET_D, DIR_OUT) | COD(0x6))
+#define PIC32_PP_OUT_OC2 (PP(PP_OC2, BUCKET_D, DIR_OUT) | COD(0xB))
+#define PIC32_PP_OUT_OC1 (PP(PP_OC1, BUCKET_D, DIR_OUT) | COD(0xC))
+#define PIC32_PP_OUT_OC9 (PP(PP_OC9, BUCKET_D, DIR_OUT) | COD(0xD))
+#define PIC32_PP_OUT_C2TX (PP(PP_C2TX, BUCKET_D, DIR_OUT) | COD(0xF))
+
+/* pin configurations flags */
+#define CONF_DIR_OFF 0
+#define CONF_COD_OFF 2
+#define CONF_DIR(x) ((x) << (CONF_DIR_OFF))
+#define CONF_COD(x) ((x) << (CONF_COD_OFF))
+
+#define PIC32_PIN_CONF_NONE (CONF_DIR(DIR_NONE) | CONF_COD(0x0))
+
+#define PIC32_PIN_CONF_OD (CONF_DIR(DIR_NONE) | CONF_COD(0x1))
+#define PIC32_PIN_CONF_OD_OUT (CONF_DIR(DIR_OUT) | CONF_COD(0x2))
+
+#define PIC32_PIN_CONF_PU (CONF_DIR(DIR_NONE) | CONF_COD(0x3))
+#define PIC32_PIN_CONF_PU_IN (CONF_DIR(DIR_IN) | CONF_COD(0x4))
+
+#define PIC32_PIN_CONF_PD (CONF_DIR(DIR_NONE) | CONF_COD(0x5))
+#define PIC32_PIN_CONF_PD_IN (CONF_DIR(DIR_IN) | CONF_COD(0x6))
+
+#define PIC32_PIN_CONF_AN (CONF_DIR(DIR_NONE) | CONF_COD(0x7))
+#define PIC32_PIN_CONF_AN_IN (CONF_DIR(DIR_IN) | CONF_COD(0x8))
+
+#define PIC32_PIN_CONF_DG (CONF_DIR(DIR_NONE) | CONF_COD(0x9))
+#define PIC32_PIN_CONF_DG_IN (CONF_DIR(DIR_IN) | CONF_COD(0xA))
+#define PIC32_PIN_CONF_DG_OUT (CONF_DIR(DIR_OUT) | CONF_COD(0xB))
+
+/* change notification trigger type */
+#define PIC32_CN_RISING 1
+#define PIC32_CN_FALLING 2
+#define PIC32_CN_BOTH 3
+
+#endif /* __DT_BINDINGS_PIC32MZDA_PINCTRL_H__ */
--
1.7.9.5
From: Andrei Pistirica <[email protected]>
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: Andrei Pistirica <[email protected]>
Signed-off-by: Joshua Henderson <[email protected]>
---
drivers/pinctrl/Kconfig | 17 +
drivers/pinctrl/Makefile | 2 +
drivers/pinctrl/pinctrl-pic32.c | 2127 +++++++++++++++++++++++++++++++++++
drivers/pinctrl/pinctrl-pic32.h | 158 +++
drivers/pinctrl/pinctrl-pic32mzda.c | 294 +++++
drivers/pinctrl/pinctrl-pic32mzda.h | 40 +
6 files changed, 2638 insertions(+)
create mode 100644 drivers/pinctrl/pinctrl-pic32.c
create mode 100644 drivers/pinctrl/pinctrl-pic32.h
create mode 100644 drivers/pinctrl/pinctrl-pic32mzda.c
create mode 100644 drivers/pinctrl/pinctrl-pic32mzda.h
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index b422e4e..27feef0 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -248,6 +248,23 @@ config PINCTRL_ZYNQ
help
This selectes the pinctrl driver for Xilinx Zynq.
+config PINCTRL_PIC32
+ bool "Microchip PIC32 pin controller driver"
+ depends on OF
+ depends on MACH_PIC32
+ select PINMUX
+ select GENERIC_PINCONF
+ select GPIOLIB_IRQCHIP
+ select GENERIC_IRQ_CHIP
+ help
+ This is the pin controller and gpio driver for Microchip PIC32
+ microcontrollers. This option is selected automatically when specific
+ machine and arch are selected to build.
+
+config PINCTRL_PIC32MZDA
+ def_bool y if PIC32MZDA
+ select PINCTRL_PIC32
+
source "drivers/pinctrl/bcm/Kconfig"
source "drivers/pinctrl/berlin/Kconfig"
source "drivers/pinctrl/freescale/Kconfig"
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 738cb49..149e723 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -54,3 +54,5 @@ obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/
obj-$(CONFIG_ARCH_VT8500) += vt8500/
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
+obj-$(CONFIG_PINCTRL_PIC32) += pinctrl-pic32.o
+obj-$(CONFIG_PINCTRL_PIC32MZDA) += pinctrl-pic32mzda.o
diff --git a/drivers/pinctrl/pinctrl-pic32.c b/drivers/pinctrl/pinctrl-pic32.c
new file mode 100644
index 0000000..ae2ab21
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-pic32.c
@@ -0,0 +1,2127 @@
+/*
+ * pic32 pinctrl core driver.
+ *
+ * Copyright (C) 2015 Microchip Technology, Inc.
+ * Author: Sorin-Andrei Pistirica <[email protected]>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+/* Since we request GPIOs from ourself */
+#include <linux/pinctrl/consumer.h>
+
+#include "core.h"
+#include "pinctrl-pic32.h"
+
+/* struct pic32_gpio_irq - pic32 gpio irq descriptor
+ * @gpio_irqchip: irq chip descriptor related to pin bank
+ * @domain: associated irq domain
+ * @pio_irq: PIO bank hardware interrupt
+ * @type: type of interrupt per pin: RISE, FALL or BOTH.
+ **/
+struct pic32_gpio_irq {
+ struct irq_chip gpio_irqchip;
+ struct irq_domain *domain;
+ int pio_irq;
+
+ u32 type[PINS_PER_BANK];
+};
+
+/* struct pic32_gpio_chip - pic32 gpio chip descriptor
+ * @chip: gpio chip descriptor
+ * @pio_base: port's registers base address
+ * @reg_lookup_off: registers offsets (map may vary within pic32 family chips)
+ * @pio_idx: port index (0-to-10 alias A-to-J)
+ * @clk: associated clock
+ * @gpio_irq: pic32 gpio irq descriptor
+ * @range: pin ranges as gpio in pinctrl device
+ **/
+struct pic32_gpio_chip {
+ struct gpio_chip chip;
+ void __iomem *pio_base;
+ unsigned *reg_lookup_off;
+ unsigned lookup_size;
+ int pio_idx;
+ int gpio_base;
+ int ngpio;
+ struct clk *clk;
+
+ struct pic32_gpio_irq gpio_irq;
+ struct pinctrl_gpio_range range;
+};
+#define to_pic32_gpio_chip(c) container_of(c, struct pic32_gpio_chip, chip)
+
+static struct pic32_gpio_chip *gpio_chips[MAX_PIO_BANKS];
+
+/* get a specific pic32:pio register */
+static void __iomem *pic32_pio_get_reg(struct pic32_gpio_chip *pic32_chip,
+ enum pic32_pio_regs pic32_reg)
+{
+ void __iomem *base = pic32_chip->pio_base;
+ unsigned *lookup = pic32_chip->reg_lookup_off;
+ unsigned lookup_size = pic32_chip->lookup_size;
+
+ if (WARN_ON(pic32_reg > lookup_size ||
+ lookup[pic32_reg] == PIC32_OFF_UNSPEC)) {
+ dev_err(pic32_chip->chip.dev,
+ "BUG: Wrong register offset: %u!\n",
+ pic32_reg);
+
+ /* check against NULL before use the register !*/
+ return NULL;
+ }
+
+ return base + lookup[pic32_reg];
+}
+
+/* set pin to open-drain */
+static int pic32_pinconf_open_drain(struct pic32_gpio_chip *pic32_chip,
+ unsigned pin, int value)
+{
+ struct pic32_reg __iomem *odc_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_ODC);
+ u32 mask = BIT(pin);
+
+ if (WARN_ON(odc_reg == NULL || pin >= pic32_chip->chip.ngpio))
+ return -EINVAL;
+
+ if (value)
+ /* NOTE: I/O direction will be changed to out */
+ writel(mask, &odc_reg->set);
+ else
+ writel(mask, &odc_reg->clr);
+
+ dev_dbg(pic32_chip->chip.dev, "%s: OPEN-DRAIN pin (%u:%u)\n", __func__,
+ pic32_chip->pio_idx, pin);
+
+ return 0;
+}
+
+/* set pin to pull-up */
+static int pic32_pinconf_pullup(struct pic32_gpio_chip *pic32_chip,
+ unsigned pin, int value)
+{
+ struct pic32_reg __iomem *cnpu_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_CNPU);
+ u32 mask = BIT(pin);
+
+ if (WARN_ON(cnpu_reg == NULL || pin >= pic32_chip->chip.ngpio))
+ return -EINVAL;
+
+ if (value)
+ writel(mask, &cnpu_reg->set);
+ else
+ writel(mask, &cnpu_reg->clr);
+
+ dev_dbg(pic32_chip->chip.dev, "%s: PULL-UP pin (%u:%u)\n", __func__,
+ pic32_chip->pio_idx, pin);
+
+ return 0;
+}
+
+/* set pin to pull-down */
+static int pic32_pinconf_pulldown(struct pic32_gpio_chip *pic32_chip,
+ unsigned pin, int value)
+{
+ struct pic32_reg __iomem *cnpd_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_CNPD);
+ u32 mask = BIT(pin);
+
+ if (WARN_ON(cnpd_reg == NULL || pin >= pic32_chip->chip.ngpio))
+ return -EINVAL;
+
+ if (value)
+ writel(mask, &cnpd_reg->set);
+ else
+ writel(mask, &cnpd_reg->clr);
+
+ dev_dbg(pic32_chip->chip.dev, "%s: PULL-DOWN pin (%u:%u)\n", __func__,
+ pic32_chip->pio_idx, pin);
+
+ return 0;
+}
+
+/* set pin to analog */
+static int pic32_pinconf_analog(struct pic32_gpio_chip *pic32_chip,
+ unsigned pin)
+{
+ struct pic32_reg __iomem *ansel_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_ANSEL);
+ u32 mask = BIT(pin);
+
+ if (WARN_ON(ansel_reg == NULL || pin >= pic32_chip->chip.ngpio))
+ return -EINVAL;
+
+ writel(mask, &ansel_reg->set);
+
+ dev_dbg(pic32_chip->chip.dev, "%s: ANALOG pin (%u:%u)\n", __func__,
+ pic32_chip->pio_idx, pin);
+
+ return 0;
+}
+
+/* set pin as digital */
+static int pic32_pinconf_dg(struct pic32_gpio_chip *pic32_chip, unsigned pin)
+{
+ struct pic32_reg __iomem *ansel_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_ANSEL);
+ u32 mask = BIT(pin);
+
+ if (WARN_ON(ansel_reg == NULL || pin >= pic32_chip->chip.ngpio))
+ return -EINVAL;
+
+ writel(mask, &ansel_reg->clr);
+
+ dev_dbg(pic32_chip->chip.dev, "%s: DIGITAL pin (%u:%u)\n", __func__,
+ pic32_chip->pio_idx, pin);
+
+ return 0;
+}
+
+/* set pin dir according to configuration */
+static int pic32_pinconf_set_dir(struct pic32_gpio_chip *pic32_chip,
+ unsigned pin, unsigned long conf)
+{
+ struct pin_conf *pinconf = (struct pin_conf *)&conf;
+ struct pic32_reg __iomem *tris_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_TRIS);
+ u32 mask = BIT(pin);
+
+ if (WARN_ON(tris_reg == NULL || pin >= pic32_chip->chip.ngpio))
+ goto err;
+
+ switch (pinconf->dir) {
+ case DIR_NONE:
+ goto out;
+ break;
+ case DIR_IN:
+ writel(mask, &tris_reg->set);
+ break;
+ case DIR_OUT:
+ writel(mask, &tris_reg->clr);
+ break;
+ default:
+ dev_err(pic32_chip->chip.dev,
+ "BUG: Wrong pin direction (%u)!\n",
+ pinconf->dir);
+ goto err;
+ break;
+ }
+
+out:
+ dev_dbg(pic32_chip->chip.dev, "%s: pin (%u:%u), DIR:%s\n", __func__,
+ pic32_chip->pio_idx, pin,
+ pinconf->dir == DIR_IN ? "IN" : "OUT");
+ return 0;
+err:
+ return -EINVAL;
+}
+
+/* exported wrappers - runtime manipulation */
+static struct pic32_gpio_chip *gpio_to_pic32_gpio_chip(unsigned pin_id)
+{
+ unsigned bank = pin_id / PINS_PER_BANK;
+ unsigned pin = pin_id % PINS_PER_BANK;
+
+ if (WARN_ON(pin > PINS_PER_BANK || bank >= MAX_PIO_BANKS))
+ return ERR_PTR(-EINVAL);
+
+ return gpio_chips[bank];
+}
+
+int pic32_pinconf_open_drain_runtime(unsigned pin_id, int value)
+{
+ struct pic32_gpio_chip *pic32_chip = gpio_to_pic32_gpio_chip(pin_id);
+ unsigned pin = pin_id % PINS_PER_BANK;
+
+ if (IS_ERR_OR_NULL(pic32_chip))
+ return -ENODEV;
+
+ return pic32_pinconf_open_drain(pic32_chip, pin, value);
+}
+EXPORT_SYMBOL(pic32_pinconf_open_drain_runtime);
+
+int pic32_pinconf_pullup_runtime(unsigned pin_id, int value)
+{
+ struct pic32_gpio_chip *pic32_chip = gpio_to_pic32_gpio_chip(pin_id);
+ unsigned pin = pin_id % PINS_PER_BANK;
+
+ if (IS_ERR_OR_NULL(pic32_chip))
+ return -ENODEV;
+
+ return pic32_pinconf_pullup(pic32_chip, pin, value);
+}
+EXPORT_SYMBOL(pic32_pinconf_pullup_runtime);
+
+int pic32_pinconf_pulldown_runtime(unsigned pin_id, int value)
+{
+ struct pic32_gpio_chip *pic32_chip = gpio_to_pic32_gpio_chip(pin_id);
+ unsigned pin = pin_id % PINS_PER_BANK;
+
+ if (IS_ERR_OR_NULL(pic32_chip))
+ return -ENODEV;
+
+ return pic32_pinconf_pulldown(pic32_chip, pin, value);
+}
+EXPORT_SYMBOL(pic32_pinconf_pulldown_runtime);
+
+int pic32_pinconf_analog_runtime(unsigned pin_id)
+{
+ struct pic32_gpio_chip *pic32_chip = gpio_to_pic32_gpio_chip(pin_id);
+ unsigned pin = pin_id % PINS_PER_BANK;
+
+ if (IS_ERR_OR_NULL(pic32_chip))
+ return -ENODEV;
+
+ return pic32_pinconf_analog(pic32_chip, pin);
+}
+EXPORT_SYMBOL(pic32_pinconf_analog_runtime);
+
+int pic32_pinconf_dg_runtime(unsigned pin_id)
+{
+ struct pic32_gpio_chip *pic32_chip = gpio_to_pic32_gpio_chip(pin_id);
+ unsigned pin = pin_id % PINS_PER_BANK;
+
+ if (IS_ERR_OR_NULL(pic32_chip))
+ return -ENODEV;
+
+ return pic32_pinconf_dg(pic32_chip, pin);
+}
+EXPORT_SYMBOL(pic32_pinconf_dg_runtime);
+
+static int pic32_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ int gpio = chip->base + offset;
+ int bank = chip->base / chip->ngpio;
+
+ dev_dbg(chip->dev, "%s: request GPIO-%c:%d(%d)\n", __func__,
+ 'A' + bank, offset, gpio);
+
+ return pinctrl_request_gpio(gpio);
+}
+
+static void pic32_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ int gpio = chip->base + offset;
+ int bank = chip->base / chip->ngpio;
+
+ dev_dbg(chip->dev, "%s: free GPIO-%c:%d(%d)\n", __func__,
+ 'A' + bank, offset, gpio);
+
+ pinctrl_free_gpio(gpio);
+}
+
+static void pic32_gpio_set(struct gpio_chip *chip, unsigned gpio, int val)
+{
+ struct pic32_gpio_chip *pic32_chip = to_pic32_gpio_chip(chip);
+ struct pic32_reg __iomem *port_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_PORT);
+ u32 mask = BIT(gpio);
+
+ if (WARN_ON(port_reg == NULL || gpio >= chip->ngpio)) {
+ dev_err(pic32_chip->chip.dev, "gpio setting error!\n");
+ return;
+ }
+
+ if (val)
+ writel(mask, &port_reg->set);
+ else
+ writel(mask, &port_reg->clr);
+}
+
+static int pic32_gpio_get(struct gpio_chip *chip, unsigned gpio)
+{
+ struct pic32_gpio_chip *pic32_chip = to_pic32_gpio_chip(chip);
+ struct pic32_reg __iomem *port_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_PORT);
+ u32 mask = BIT(gpio);
+
+ if (WARN_ON(port_reg == NULL || gpio >= chip->ngpio))
+ return -EINVAL;
+
+ return readl(&port_reg->val) & mask;
+}
+
+static int pic32_gpio_get_dir(struct gpio_chip *chip, unsigned offset)
+{
+ struct pic32_gpio_chip *pic32_chip = to_pic32_gpio_chip(chip);
+ struct pic32_reg __iomem *tris_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_TRIS);
+ u32 mask = BIT(offset);
+
+ if (WARN_ON(tris_reg == NULL))
+ return -EINVAL;
+
+ return readl(&tris_reg->val) & mask;
+}
+
+static int pic32_gpio_set_dir(struct gpio_chip *chip, unsigned gpio, int dir)
+{
+ struct pic32_gpio_chip *pic32_chip = to_pic32_gpio_chip(chip);
+ struct pic32_reg __iomem *tris_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_TRIS);
+ u32 mask = BIT(gpio);
+
+ if (WARN_ON(tris_reg == NULL || gpio >= chip->ngpio))
+ return -EINVAL;
+
+ /* clear analog selection when digital input is required! */
+ if (WARN_ON(pic32_pinconf_dg(pic32_chip, gpio) < 0))
+ return -EINVAL;
+
+ if (dir == DIR_IN)
+ writel(mask, &tris_reg->set);
+ else if (dir == DIR_OUT)
+ writel(mask, &tris_reg->clr);
+
+ return 0;
+}
+
+static int pic32_gpio_dir_in(struct gpio_chip *chip, unsigned gpio)
+{
+ return pic32_gpio_set_dir(chip, gpio, DIR_IN);
+}
+
+static int pic32_gpio_dir_out(struct gpio_chip *chip,
+ unsigned gpio, int value)
+{
+ struct pic32_gpio_chip *pic32_chip = to_pic32_gpio_chip(chip);
+ struct pic32_reg __iomem *cnpu_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_CNPU);
+ struct pic32_reg __iomem *cnpd_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_CNPD);
+ u32 mask = BIT(gpio);
+ int ret = 0;
+
+ if (WARN_ON(cnpu_reg == NULL || cnpd_reg == NULL ||
+ gpio >= chip->ngpio)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* clear open-drain for value of 1 */
+ if (value) {
+ ret = pic32_pinconf_open_drain(pic32_chip, gpio, 0);
+ if (WARN_ON(ret < 0))
+ goto out;
+ }
+
+ /* set required value */
+ pic32_gpio_set(chip, gpio, value);
+
+ /* clear when digital output is required! */
+ writel(mask, &cnpu_reg->clr); /* pull-up */
+ writel(mask, &cnpd_reg->clr); /* pull-down */
+
+ return pic32_gpio_set_dir(chip, gpio, DIR_OUT);
+out:
+ return ret;
+}
+
+static int pic32_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct pic32_gpio_chip *pic32_chip = to_pic32_gpio_chip(chip);
+ struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
+ int virq;
+
+ if (offset < chip->ngpio)
+ virq = irq_create_mapping(gpio_irq->domain, offset);
+ else
+ virq = -ENXIO;
+
+ dev_dbg(chip->dev, "%s: request IRQ for GPIO:%d, return:%d\n",
+ __func__, offset + chip->base, virq);
+
+ return virq;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void pic32_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ int i;
+
+ for (i = 0; i < chip->ngpio; i++) {
+ const char *gpio_label;
+
+ gpio_label = gpiochip_is_requested(chip, i);
+ if (!gpio_label)
+ continue;
+
+ seq_printf(s, "%s: GPIO-%s:%d\n", gpio_label, chip->label, i);
+ }
+}
+#else
+#define pic32_gpio_dbg_show NULL
+#endif
+
+static void pic32_gpio_ranges_setup(struct platform_device *pdev,
+ struct pic32_gpio_chip *pic32_chip)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct pinctrl_gpio_range *range;
+ struct of_phandle_args args;
+ int ret;
+
+ ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args);
+ pic32_chip->gpio_base = (ret == 0) ? args.args[1] + args.args[0] :
+ pic32_chip->pio_idx * PINS_PER_BANK;
+ pic32_chip->ngpio = (ret == 0) ? args.args[2] - args.args[0] :
+ PINS_PER_BANK;
+
+ range = &pic32_chip->range;
+ range->name = dev_name(&pdev->dev);
+ range->id = pic32_chip->pio_idx;
+ range->pin_base = range->base = pic32_chip->gpio_base;
+
+ range->npins = pic32_chip->ngpio;
+ range->gc = &pic32_chip->chip;
+
+ dev_dbg(&pdev->dev, "%s: GPIO-%c ranges: (%d,%d)\n", __func__,
+ 'A' + range->id,
+ pic32_chip->gpio_base, pic32_chip->ngpio);
+}
+
+
+static inline int
+pic32_gpio_irq_rise_dset(struct pic32_gpio_chip *pic32_chip, unsigned pin)
+{
+ struct pic32_reg __iomem *cncon_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_CNCON);
+ struct pic32_reg __iomem *cnen_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_CNEN);
+ u32 cn_en = BIT(PIC32_CNCON_ON);
+ u32 cn_edge = BIT(PIC32_CNCON_EDGE);
+ u32 pin_mask = BIT(pin);
+
+ if (WARN_ON(cncon_reg == NULL || cnen_reg == NULL ||
+ pin >= pic32_chip->chip.ngpio))
+ return -EINVAL;
+
+ /* enable RISE detection */
+ writel(pin_mask, &cnen_reg->set);
+
+ /* enable CN-EDGE detection */
+ writel(cn_edge, &cncon_reg->set);
+
+ /* enable CN module */
+ writel(cn_en, &cncon_reg->set);
+
+ dev_dbg(pic32_chip->chip.dev, "%s: CN rise edge set for pin:%u\n",
+ __func__, pin);
+
+ return 0;
+}
+
+static inline int
+pic32_gpio_irq_fall_dset(struct pic32_gpio_chip *pic32_chip, unsigned pin)
+{
+ struct pic32_reg __iomem *cncon_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_CNCON);
+ struct pic32_reg __iomem *cnne_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_CNNE);
+ u32 cn_en = BIT(PIC32_CNCON_ON);
+ u32 cn_edge = BIT(PIC32_CNCON_EDGE);
+ u32 pin_mask = BIT(pin);
+
+ if (WARN_ON(cncon_reg == NULL || cnne_reg == NULL ||
+ pin >= pic32_chip->chip.ngpio))
+ return -EINVAL;
+
+ /* enable FALL detection */
+ writel(pin_mask, &cnne_reg->set);
+
+ /* enable CN-EDGE detection */
+ writel(cn_edge, &cncon_reg->set);
+
+ /* enable CN module */
+ writel(cn_en, &cncon_reg->set);
+
+ dev_dbg(pic32_chip->chip.dev, "%s: CN fall edge set for pin:%u\n",
+ __func__, pin);
+
+ return 0;
+}
+
+static unsigned int gpio_irq_startup(struct irq_data *d)
+{
+ struct pic32_gpio_chip *pic32_chip = irq_data_get_irq_chip_data(d);
+ struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
+ unsigned pin = d->hwirq;
+ int ret;
+
+ ret = gpiochip_lock_as_irq(&pic32_chip->chip, pin);
+ if (ret) {
+ dev_err(pic32_chip->chip.dev, "unable to lock pind %lu IRQ\n",
+ d->hwirq);
+ return ret;
+ }
+
+ /* start CN */
+ switch (gpio_irq->type[pin]) {
+ case IRQ_TYPE_EDGE_RISING:
+ pic32_gpio_irq_rise_dset(pic32_chip, pin);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ pic32_gpio_irq_fall_dset(pic32_chip, pin);
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ pic32_gpio_irq_rise_dset(pic32_chip, pin);
+ pic32_gpio_irq_fall_dset(pic32_chip, pin);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(pic32_chip->chip.dev, "%s: irq lock pin:%u\n", __func__, pin);
+
+ return 0;
+}
+
+static void gpio_irq_shutdown(struct irq_data *d)
+{
+ struct pic32_gpio_chip *pic32_chip = irq_data_get_irq_chip_data(d);
+ unsigned pin = d->hwirq;
+
+ gpiochip_unlock_as_irq(&pic32_chip->chip, pin);
+
+ dev_dbg(pic32_chip->chip.dev, "%s: irq unlock:%u\n", __func__, pin);
+}
+
+static int gpio_irq_type(struct irq_data *d, unsigned type)
+{
+ struct pic32_gpio_chip *pic32_chip = irq_data_get_irq_chip_data(d);
+ struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
+ unsigned pin = d->hwirq;
+
+ dev_dbg(pic32_chip->chip.dev, "%s: irq type:%u\n", __func__, type);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ case IRQ_TYPE_EDGE_FALLING:
+ case IRQ_TYPE_EDGE_BOTH:
+ gpio_irq->type[pin] = type;
+ return IRQ_SET_MASK_OK;
+ default:
+ gpio_irq->type[pin] = IRQ_TYPE_NONE;
+ return -EINVAL;
+ }
+}
+
+/* map virtual irq on hw irq: domain translation */
+static int pic32_gpio_irq_map(struct irq_domain *d,
+ unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct pic32_gpio_chip *pic32_chip = d->host_data;
+ struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
+ struct irq_chip *irqchip = &gpio_irq->gpio_irqchip;
+
+ dev_dbg(pic32_chip->chip.dev, "%s: GPIO-%c:%d map virq:%u\n", __func__,
+ 'A' + pic32_chip->pio_idx, virq, virq);
+
+ /* set the gpioX chip */
+ irq_set_chip(virq, irqchip);
+ irq_set_chip_data(virq, pic32_chip);
+ irq_set_handler(virq, handle_simple_irq);
+
+ return 0;
+}
+
+/* decode irq number: base + pin */
+static int pic32_gpio_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 pic32_gpio_chip *pic32_chip = d->host_data;
+ int pin = pic32_chip->chip.base + intspec[0];
+ int ret;
+
+ if (WARN_ON(intsize < 2))
+ return -EINVAL;
+
+ ret = gpio_request(pin, ctrlr->full_name);
+ if (ret)
+ return ret;
+
+ *out_hwirq = intspec[0];
+ *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
+
+ /* pic32_gpio_dir_in */
+ ret = gpio_direction_input(pin);
+ if (ret)
+ return ret;
+
+ dev_dbg(pic32_chip->chip.dev, "%s: xlate pin:%d\n", __func__, pin);
+ return 0;
+}
+
+static struct irq_domain_ops pic32_gpio_irqd_ops = {
+ .map = pic32_gpio_irq_map,
+ .xlate = pic32_gpio_irq_domain_xlate,
+};
+
+static unsigned long pic32_gpio_to_isr(struct pic32_gpio_chip *pic32_chip,
+ int status)
+{
+ struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
+ struct pic32_reg __iomem *cnne_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_CNNE);
+ struct pic32_reg __iomem *cnen_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_CNEN);
+ unsigned long isr = (unsigned long)status;
+ unsigned long visr = 0;
+ int cnen_rise, cnne_fall;
+ int pin;
+
+ if (WARN_ON(cnne_reg == NULL || cnen_reg == NULL)) {
+ dev_err(pic32_chip->chip.dev,
+ "BUG: wrong register offsets!\n");
+ return 0;
+ }
+
+ cnen_rise = readl(cnen_reg);
+ cnne_fall = readl(cnne_reg);
+
+ /* for each change that occurred, match with irq type and
+ * set it accordingly.
+ */
+ visr = 0;
+ for_each_set_bit(pin, &isr, BITS_PER_BYTE * sizeof(u32)) {
+ u32 mask = BIT(pin);
+ bool type_rise = (gpio_irq->type[pin] == IRQ_TYPE_EDGE_RISING);
+ bool type_fall = (gpio_irq->type[pin] == IRQ_TYPE_EDGE_FALLING);
+ bool type_both = gpio_irq->type[pin] == IRQ_TYPE_EDGE_BOTH;
+ bool rise = cnen_rise & mask;
+ bool fall = cnne_fall & mask;
+
+ if ((type_rise && rise) || (type_fall && fall) || type_both)
+ visr |= mask;
+ }
+
+ return visr;
+}
+
+static void gpio_irq_handler(struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct irq_data *idata = irq_desc_get_irq_data(desc);
+ struct pic32_gpio_chip *pic32_chip = irq_data_get_irq_chip_data(idata);
+ struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
+ struct pic32_reg __iomem *cnf_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_CNF);
+ u32 cnfstat = 0;
+ unsigned long isr;
+ int n;
+
+ if (WARN_ON(cnf_reg == NULL)) {
+ dev_err(pic32_chip->chip.dev,
+ "BUG: wrong register offsets!\n");
+ return;
+ }
+
+ /* read CN status */
+ cnfstat = readl(cnf_reg);
+
+ /* set a logical isr based on CN status
+ * Note: in case of isr error, the isr will be 0.
+ */
+ isr = pic32_gpio_to_isr(pic32_chip, cnfstat);
+ dev_dbg(pic32_chip->chip.dev, "%s: isr:0x%lx\n", __func__, isr);
+
+ /* clear CN source */
+ writel(cnfstat, &cnf_reg->clr);
+
+ chained_irq_enter(chip, desc);
+
+ /* for each interrupt, call handle */
+ for_each_set_bit(n, &isr, BITS_PER_LONG) {
+ unsigned int irq = irq_find_mapping(gpio_irq->domain, n);
+
+ generic_handle_irq(irq);
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static int pic32_gpio_of_irq_setup(struct platform_device *pdev,
+ struct pic32_gpio_chip *pic32_chip)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
+ struct irq_chip *irqchip = &gpio_irq->gpio_irqchip;
+ int base_irq;
+
+ /* set irqchip */
+ irqchip->name = kasprintf(GFP_KERNEL, "GPIO-%c",
+ pic32_chip->pio_idx + 'A');
+ irqchip->irq_startup = gpio_irq_startup;
+ irqchip->irq_shutdown = gpio_irq_shutdown;
+ irqchip->irq_set_type = gpio_irq_type;
+
+ base_irq = platform_get_irq(pdev, 0);
+ if (base_irq < 0)
+ return base_irq;
+
+ gpio_irq->pio_irq = base_irq;
+
+ /* Setup irq domain of ngpio lines */
+ gpio_irq->domain = irq_domain_add_linear(
+ node,
+ pic32_chip->chip.ngpio,
+ &pic32_gpio_irqd_ops, pic32_chip);
+ if (!gpio_irq->domain) {
+ dev_err(pic32_chip->chip.dev, "Couldn't allocate IRQ domain\n");
+ return -ENXIO;
+ }
+
+ dev_dbg(&pdev->dev, "%s: irq GPIO-%c, base_irq:%d, domain:%d\n",
+ __func__, pic32_chip->pio_idx + 'A',
+ base_irq, pic32_chip->chip.ngpio);
+
+ /* setup chained handler */
+ irq_set_chip_data(gpio_irq->pio_irq, pic32_chip);
+ irq_set_chained_handler(gpio_irq->pio_irq, gpio_irq_handler);
+
+ return 0;
+}
+
+static struct gpio_chip gpio_template = {
+ .request = pic32_gpio_request,
+ .free = pic32_gpio_free,
+ .get_direction = pic32_gpio_get_dir,
+ .direction_input = pic32_gpio_dir_in,
+ .direction_output = pic32_gpio_dir_out,
+ .get = pic32_gpio_get,
+ .set = pic32_gpio_set,
+ .to_irq = pic32_gpio_to_irq,
+ .dbg_show = pic32_gpio_dbg_show,
+ .ngpio = PINS_PER_BANK,
+ .can_sleep = false,
+};
+
+int pic32_gpio_probe(struct platform_device *pdev,
+ unsigned (*reg_lookup_off)[],
+ unsigned lookup_size)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int alias_idx = of_alias_get_id(np, "gpio");
+ struct pic32_gpio_chip *pic32_chip = NULL;
+ struct gpio_chip *chip;
+ struct resource *r;
+ int ret = 0;
+
+ dev_dbg(&pdev->dev, "%s: probing...\n", __func__);
+
+ if (!np)
+ return -ENODEV;
+
+ if (WARN_ON(alias_idx >= ARRAY_SIZE(gpio_chips)))
+ return -EINVAL;
+
+ if (gpio_chips[alias_idx]) {
+ dev_err(&pdev->dev, "Failure %i for GPIO %i\n", ret, alias_idx);
+ return -EBUSY;
+ }
+
+ /* pic32 gpio chip - private data */
+ pic32_chip = devm_kzalloc(&pdev->dev, sizeof(*pic32_chip),
+ GFP_KERNEL);
+ if (!pic32_chip)
+ return -ENOMEM;
+
+ /* base address of pio(alias_idx) registers */
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ ret = -EINVAL;
+ goto probe_err;
+ }
+ pic32_chip->pio_base = devm_ioremap_nocache(&pdev->dev, r->start,
+ resource_size(r));
+ if (IS_ERR(pic32_chip->pio_base)) {
+ ret = PTR_ERR(pic32_chip->pio_base);
+ goto probe_err;
+ }
+
+ /* clocks */
+ pic32_chip->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pic32_chip->clk)) {
+ ret = PTR_ERR(pic32_chip->clk);
+ dev_err(&pdev->dev, "clk get failed\n");
+ goto probe_err;
+ }
+
+ ret = clk_prepare_enable(pic32_chip->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "clk enable failed\n");
+ goto probe_err;
+ }
+
+ pic32_chip->reg_lookup_off = *reg_lookup_off;
+ pic32_chip->lookup_size = lookup_size;
+ pic32_chip->pio_idx = alias_idx;
+ pic32_chip->chip = gpio_template;
+ pic32_gpio_ranges_setup(pdev, pic32_chip);/* pin_ranges: unsupported */
+ pic32_gpio_of_irq_setup(pdev, pic32_chip);/* irq domain */
+
+ /* gpio chip */
+ chip = &pic32_chip->chip;
+ chip->of_node = np;
+ chip->label = dev_name(&pdev->dev);
+ chip->dev = &pdev->dev;
+ chip->owner = THIS_MODULE;
+ chip->base = pic32_chip->gpio_base;
+ chip->ngpio = pic32_chip->ngpio;
+
+ ret = gpiochip_add(chip);
+ if (ret)
+ goto probe_err;
+ gpio_chips[alias_idx] = pic32_chip;
+
+ dev_info(&pdev->dev, "(%d) driver initialized.\n", alias_idx);
+ return 0;
+
+probe_err:
+ if (pic32_chip->pio_base)
+ devm_iounmap(&pdev->dev, pic32_chip->pio_base);
+ if (pic32_chip) {
+ devm_kfree(&pdev->dev, pic32_chip);
+ gpio_chips[alias_idx] = NULL;
+ }
+ return ret;
+}
+
+static struct pic32_pinctrl_prop pinctrl_props[PIC32_PINCTRL_PROP_LAST] = {
+ [PIC32_PINCTRL_PROP_SINGLE_PINS] =
+ PIC32_PINCTRL_DT_PROP("pic32,single-pins", 3),
+ [PIC32_PINCTRL_PROP_PINS] =
+ PIC32_PINCTRL_DT_PROP("pic32,pins", 3),
+};
+
+/* struct pin_function - pic32 pin function descriptor
+ * @name: the name of the pinmux function
+ * @ngroups: number of groups related to this function
+ * @groups: groups related to this function
+ **/
+struct pin_function {
+ const char *name;
+ unsigned ngroups;
+ const char **groups;
+};
+
+/* struct gspin - pic32 single-pins descriptor
+ * @bank: the pin's bank(PORT)
+ * @pin: the pin's id within bank(PORT)
+ * @conf: the configuration of the pin: PULL_UP, MULTIDRIVE etc...
+ **/
+struct gspin {
+ unsigned bank;
+ unsigned pin;
+ unsigned long conf;
+};
+
+/* struct gpins - pic32 pinmux descriptor
+ * @dir: pin direction (in or out)
+ * @bucket: remappable pin bucket (A(0):D(3))
+ * @bank: remappable pin's bank (A(0):K(9))
+ * @pin: the remappable pin in the @bank
+ * @ppin: peripheral pin mapped
+ * @cod: pinmux code
+ * @conf: the configuration of the pin: PULL_UP, MULTIDRIVE etc...
+ **/
+struct gpins {
+ u8 dir;
+ u8 bucket;
+ u8 bank;
+ u8 pin;
+ u8 ppin;
+ u16 cod;
+ unsigned long conf;
+};
+
+/* struct pin_group - pic32 pin group descriptor
+ * @name: the name of the pin group
+ * @npins: number of pins in this group (no. single-pins + no. pinmux)
+ * @pins: array of pin ids (pinctrl forces to maintain such an array)
+ * @ngspins: number of single pins within group
+ * @gspins: descriptor of single pins within group
+ * @ngpins: number of pinmux pins within group
+ * @gpins: descriptor of pinmux pins within group
+ **/
+struct pin_group {
+ const char *name;
+
+ unsigned npins;
+ unsigned int *pins;
+
+ unsigned ngspins;
+ struct gspin *gspins;
+ unsigned ngpins;
+ struct gpins *gpins;
+};
+
+/* struct pic32_pinctrl_data - pic32 pinctrl descriptor
+ * @dev: related device
+ * @pctl: related pinctrl device
+ * @ppsin_base: peripheral pin select input base address
+ * @ppsout_base: peripheral pin select output base address
+ * @pps_off: pps registers mapping
+ * @clk: associated clock
+ * @nbanks: number of banks (ports)
+ * @npins: number pf pins
+ * @ngroups: number of groups
+ * @groups: pinctrl's groups (related to all functions)
+ * @nfunctions: number of functions
+ * @functions: pinctrl's functions
+ **/
+struct pic32_pinctrl_data {
+ struct device *dev;
+ struct pinctrl_dev *pctl;
+
+ void __iomem *ppsin_base;
+ void __iomem *ppsout_base;
+ struct pic32_pps_off *pps_off;
+ struct clk *clk;
+
+ struct pic32_caps caps;
+
+ unsigned nbanks;
+ unsigned npins;
+
+ unsigned ngroups;
+ struct pin_group *groups;
+
+ unsigned nfunctions;
+ struct pin_function *functions;
+};
+
+/* check pinconf capabilities and set the configuration */
+static int pic32_pinconf_set(struct pic32_pinctrl_data *data,
+ struct pic32_gpio_chip *pic32_chip,
+ unsigned long conf, unsigned pin)
+{
+ struct pic32_caps *caps = &data->caps;
+ int ret = 0;
+
+ /* check if pin configuration is supported */
+ if (!(conf & caps->pinconf_caps) &&
+ !(conf & caps->pinconf_outcaps) &&
+ !(conf & caps->pinconf_incaps)) {
+
+ dev_err(data->dev, "pin configuration not supported %lu\n",
+ conf);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* set direction */
+ if (WARN_ON(pic32_pinconf_set_dir(pic32_chip, pin, conf) < 0)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* set configuration */
+ switch (conf) {
+ case PIC32_PIN_CONF_OD:
+ case PIC32_PIN_CONF_OD_OUT:
+ return pic32_pinconf_open_drain(pic32_chip, pin, 1);
+ case PIC32_PIN_CONF_PU:
+ case PIC32_PIN_CONF_PU_IN:
+ return pic32_pinconf_pullup(pic32_chip, pin, 1);
+ case PIC32_PIN_CONF_PD:
+ case PIC32_PIN_CONF_PD_IN:
+ return pic32_pinconf_pulldown(pic32_chip, pin, 1);
+ case PIC32_PIN_CONF_AN:
+ case PIC32_PIN_CONF_AN_IN:
+ return pic32_pinconf_analog(pic32_chip, pin);
+ case PIC32_PIN_CONF_DG:
+ case PIC32_PIN_CONF_DG_IN:
+ case PIC32_PIN_CONF_DG_OUT:
+ return pic32_pinconf_dg(pic32_chip, pin);
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+/* is pin open-drain? */
+static u32 pic32_pinconf_is_open_drain(struct pic32_gpio_chip *pic32_chip,
+ unsigned pin)
+{
+ struct pic32_reg __iomem *odc_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_ODC);
+ u32 mask = BIT(pin);
+
+ if (WARN_ON(odc_reg == NULL || pin >= pic32_chip->chip.ngpio))
+ return -EINVAL;
+
+ return readl(&odc_reg->val) & mask;
+}
+
+/* is pin pull-up? */
+static u32 pic32_pinconf_is_pullup(struct pic32_gpio_chip *pic32_chip,
+ unsigned pin)
+{
+ struct pic32_reg __iomem *cnpu_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_CNPU);
+ u32 mask = BIT(pin);
+
+ if (WARN_ON(cnpu_reg == NULL || pin >= pic32_chip->chip.ngpio))
+ return -EINVAL;
+
+ return readl(&cnpu_reg->val) & mask;
+}
+
+/* is pin pull-down? */
+static u32 pic32_pinconf_is_pulldown(struct pic32_gpio_chip *pic32_chip,
+ unsigned pin)
+{
+ struct pic32_reg __iomem *cnpd_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_CNPD);
+ u32 mask = BIT(pin);
+
+ if (WARN_ON(cnpd_reg == NULL || pin >= pic32_chip->chip.ngpio))
+ return -EINVAL;
+
+ return readl(&cnpd_reg->set) & mask;
+}
+
+/* is pin analog? */
+static u32 pic32_pinconf_is_analog(struct pic32_gpio_chip *pic32_chip,
+ unsigned pin)
+{
+ struct pic32_reg __iomem *ansel_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_ANSEL);
+ u32 mask = BIT(pin);
+
+ if (WARN_ON(ansel_reg == NULL || pin >= pic32_chip->chip.ngpio))
+ return -EINVAL;
+
+ return readl(&ansel_reg->set) & mask;
+}
+
+/* get pin's configuration */
+static unsigned long pic32_pinconf_get(struct pic32_gpio_chip *pic32_chip,
+ unsigned pin)
+{
+ u32 pinconf = 0;
+
+ pinconf |= pic32_pinconf_is_open_drain(pic32_chip, pin);
+ pinconf |= pic32_pinconf_is_pullup(pic32_chip, pin);
+ pinconf |= pic32_pinconf_is_pulldown(pic32_chip, pin);
+ pinconf |= pic32_pinconf_is_analog(pic32_chip, pin);
+
+ return (unsigned long)pinconf;
+}
+
+/* get the config of a certain pin */
+static int pic32_pin_config_get(struct pinctrl_dev *pctldev,
+ unsigned pin_id, unsigned long *config)
+{
+ struct pic32_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev);
+ unsigned bank = pin_id / PINS_PER_BANK;
+ unsigned pin = pin_id % PINS_PER_BANK;
+ struct pic32_gpio_chip *pic32_chip = gpio_chips[bank];
+
+ if (WARN_ON(pin_id > PINS_PER_BANK * MAX_PIO_BANKS))
+ return -EINVAL;
+
+ *config = pic32_pinconf_get(pic32_chip, pin);
+
+ dev_dbg(data->dev, "%s: get config:0x%lx of pin (%u,%u)\n", __func__,
+ *config, bank, pin);
+
+ return 0;
+}
+
+/* configure an individual pin */
+static int pic32_pin_config_set(struct pinctrl_dev *pctldev,
+ unsigned pin_id, unsigned long *configs,
+ unsigned num_configs)
+{
+ struct pic32_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev);
+ struct pic32_gpio_chip *pic32_chip;
+ unsigned bank = pin_id / PINS_PER_BANK;
+ unsigned pin = pin_id % PINS_PER_BANK;
+ int i;
+
+ if (WARN_ON(pin > PINS_PER_BANK || bank >= MAX_PIO_BANKS))
+ return -EINVAL;
+
+ pic32_chip = gpio_chips[bank];
+
+ for (i = 0; i < num_configs; i++) {
+ unsigned long config = configs[i];
+ int err;
+
+ if (config == PIC32_PIN_CONF_NONE)
+ continue;
+
+ dev_dbg(data->dev, "%s: set config:0x%lx to pin (%u,%u)\n",
+ __func__, config, bank, pin);
+
+ /* check caps and set a particular mux function */
+ err = pic32_pinconf_set(data, pic32_chip, config, pin);
+ if (WARN_ON(err < 0)) {
+ dev_err(data->dev, "pin(%u) settings failed.\n", pin);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+/* debugfs: info for a certain pin */
+static void pic32_pinconf_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned pin)
+{
+ struct pic32_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev);
+
+ dev_dbg(data->dev, "%s: NULL\n", __func__);
+}
+
+/* debugfs: info for a certain pin group */
+static void pic32_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned group)
+{
+ struct pic32_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev);
+
+ dev_dbg(data->dev, "%s: NULL\n", __func__);
+}
+#else
+#define pic32_pinconf_dbg_show NULL
+#define pic32_pinconf_group_dbg_show NULL
+#endif
+
+/* pin config operations */
+static const struct pinconf_ops pic32_pinconf_ops = {
+ .pin_config_get = pic32_pin_config_get,
+ .pin_config_set = pic32_pin_config_set,
+ .pin_config_dbg_show = pic32_pinconf_dbg_show,
+ .pin_config_group_dbg_show = pic32_pinconf_group_dbg_show,
+};
+
+/* returns number of selectable functions */
+static int pic32_pinmux_get_funcs_count(struct pinctrl_dev *pctldev)
+{
+ struct pic32_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev);
+
+ dev_dbg(data->dev, "%s: nfunction=%u\n", __func__, data->nfunctions);
+
+ return data->nfunctions;
+}
+
+/* return the function name */
+static const char *pic32_pinmux_get_func_name(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ struct pic32_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev);
+
+ dev_dbg(data->dev, "%s: get:%s\n", __func__,
+ data->functions[selector].name);
+
+ return data->functions[selector].name;
+}
+
+/* return an array of groups names */
+static int pic32_pinmux_get_func_groups(struct pinctrl_dev *pctldev,
+ unsigned selector,
+ const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct pic32_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev);
+
+ *groups = data->functions[selector].groups;
+ *num_groups = data->functions[selector].ngroups;
+
+ dev_dbg(data->dev, "%s: get groups(%u) of function=%u\n", __func__,
+ *num_groups, selector);
+
+ return 0;
+}
+
+/* get offset within PPS-OUT register for a particular re-mappable pin */
+static unsigned pic32_get_ppsout_offset(struct pic32_pinctrl_data *data,
+ unsigned bank, unsigned pin)
+{
+ struct pic32_pps_off *pps_off = data->pps_off;
+ unsigned (*lookup)[MAX_PIO_BANKS][PINS_PER_BANK];
+
+ lookup = pps_off->ppsout_lookup_off;
+ if (bank >= MAX_PIO_BANKS || pin > PINS_PER_BANK ||
+ (*lookup)[bank][pin] == PIC32_OFF_UNSPEC)
+ return -EINVAL;
+
+ return (*lookup)[bank][pin];
+}
+
+/* set a particular OUT mux function for a particular
+ * re-mappable pin (bank,pin)
+ */
+static void pic32_ppsout_set(struct pic32_pinctrl_data *data,
+ struct gpins *gpins,
+ bool en)
+{
+ void __iomem *base = data->ppsout_base;
+ unsigned bank = gpins->bank;
+ unsigned pin = gpins->pin;
+ unsigned off = pic32_get_ppsout_offset(data, bank, pin);
+
+ if (en)
+ writel(gpins->cod, base + off);
+ else
+ writel(0x0, base + off);
+
+ dev_dbg(data->dev, "%s: en=%d bank=%u pin=%u off=%u cod=0x%x\n",
+ __func__, en, bank, pin, off, gpins->cod);
+}
+
+/* get offset within PPS-IN register for a particular peripheral pin */
+static unsigned pic32_get_ppsin_offset(struct pic32_pinctrl_data *data,
+ unsigned ppin)
+{
+ struct pic32_pps_off *pps_off = data->pps_off;
+ unsigned (*lookup)[PP_MAX] = pps_off->ppsin_lookup_off;
+
+ if ((*lookup)[ppin] == PIC32_OFF_UNSPEC)
+ return -EINVAL;
+
+ return (*lookup)[ppin];
+}
+
+/* set a particular IN mux function for a particular
+ * peripheral pin (bank,pin)
+ */
+static void pic32_ppsin_set(struct pic32_pinctrl_data *data,
+ struct gpins *gpins, bool en)
+{
+ unsigned ppin = gpins->ppin;
+ unsigned off = pic32_get_ppsin_offset(data, ppin);
+ void __iomem *base = data->ppsin_base;
+
+ if (en)
+ writel(gpins->cod, base + off);
+ else
+ writel(0x0, base + off);
+
+ dev_dbg(data->dev, "%s: en=%d ppin=%u off=%u cod=0x%x\n",
+ __func__, en, ppin, off, gpins->cod);
+}
+
+/* set a particular pinmux function */
+static int pic32_pinmux_muxen(struct pic32_pinctrl_data *data,
+ struct gpins *gpins,
+ bool en)
+{
+ if (gpins->dir == DIR_IN)
+ pic32_ppsin_set(data, gpins, en);
+ else if (gpins->dir == DIR_OUT)
+ pic32_ppsout_set(data, gpins, en);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+/* enable a certain pinmux function within a pin group and
+ * within a muxing function
+ */
+static int pic32_pinmux_set_mux(struct pinctrl_dev *pctldev,
+ unsigned selector, unsigned group)
+{
+ struct pic32_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev);
+ struct pin_group *grp = &data->groups[group];
+ unsigned ngpins = grp->ngpins;
+ struct gpins *gpins = data->groups[group].gpins;
+ int i;
+
+ dev_dbg(data->dev, "enable function %s for group %s\n",
+ data->functions[selector].name, grp->name);
+
+ for (i = 0; i < ngpins; i++) {
+ struct gpins *pins = &gpins[i];
+
+ pic32_pinmux_muxen(data, pins, true);
+ }
+
+ return 0;
+}
+
+/* enable a pin to work in GPIO mode */
+static int pic32_gpio_request_enable(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned offset)
+{
+ struct pic32_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev);
+ struct gpio_chip *chip = range->gc;
+ struct pic32_gpio_chip *pic32_chip = to_pic32_gpio_chip(chip);
+ struct pic32_reg __iomem *ansel_reg = (struct pic32_reg __iomem *)
+ pic32_pio_get_reg(pic32_chip, PIC32_ANSEL);
+ int pin = offset - chip->base;
+ u32 mask = BIT(pin);
+
+ if (WARN_ON(ansel_reg == NULL || pin >= pic32_chip->chip.ngpio))
+ return -EINVAL;
+
+ if (unlikely(!range)) {
+ dev_err(data->dev, "invalid range\n");
+ return -EINVAL;
+ }
+ if (unlikely(!range->gc)) {
+ dev_err(data->dev, "missing GPIO chip in range\n");
+ return -EINVAL;
+ }
+
+ /* clear analog selection when digital input is required! */
+ writel(mask, &ansel_reg->clr);
+
+ dev_dbg(data->dev, "%s: enable pin %u as GPIO-%c:%d\n", __func__,
+ offset, 'A' + range->id, pin);
+
+ return 0;
+}
+
+/* disable a pin from GPIO mode */
+static void pic32_gpio_disable_free(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned offset)
+{
+ struct pic32_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev);
+ struct gpio_chip *chip = range->gc;
+
+ if (unlikely(!range)) {
+ dev_err(data->dev, "invalid range\n");
+ return;
+ }
+ if (unlikely(!range->gc)) {
+ dev_err(data->dev, "missing GPIO chip in range\n");
+ return;
+ }
+
+ dev_dbg(data->dev, "%s: free pin %u as GPIO-%c:%d\n", __func__,
+ offset, 'A' + range->id, offset - chip->base);
+}
+
+/* pinmux operations */
+static const struct pinmux_ops pic32_pinmux_ops = {
+ .get_functions_count = pic32_pinmux_get_funcs_count,
+ .get_function_name = pic32_pinmux_get_func_name,
+ .get_function_groups = pic32_pinmux_get_func_groups,
+ .set_mux = pic32_pinmux_set_mux,
+ .gpio_request_enable = pic32_gpio_request_enable,
+ .gpio_disable_free = pic32_gpio_disable_free,
+};
+
+static const struct pin_group *pic32_pinctrl_find_group_by_name(
+ const struct pic32_pinctrl_data *data,
+ const char *name)
+{
+ const struct pin_group *grp = NULL;
+ int i;
+
+ for (i = 0; i < data->ngroups; i++) {
+ if (strcmp(data->groups[i].name, name))
+ continue;
+
+ grp = &data->groups[i];
+ dev_dbg(data->dev, "%s: found group:%s\n", __func__, name);
+ break;
+ }
+
+ return grp;
+}
+
+static int pic32_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ struct pic32_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev);
+
+ dev_dbg(data->dev, "%s: ngroups:%u\n", __func__, data->ngroups);
+
+ return data->ngroups;
+}
+
+static const char *pic32_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ struct pic32_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev);
+
+ dev_dbg(data->dev, "%s: selector=%u group=%s\n", __func__,
+ selector, data->groups[selector].name);
+
+ return data->groups[selector].name;
+}
+
+static int pic32_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
+ const unsigned **pins, unsigned *npins)
+{
+ struct pic32_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev);
+
+ *pins = data->groups[selector].pins;
+ *npins = data->groups[selector].npins;
+
+ dev_dbg(data->dev, "%s: get pins(%u) for group=%u\n", __func__,
+ *npins, selector);
+
+ return 0;
+}
+
+static void pic32_pin_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned offset)
+{
+ struct pic32_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev);
+
+ seq_printf(s, "%s", dev_name(data->dev));
+}
+
+/* construct a config map for pins(single and pinmux) within a group */
+static int pic32_dt_node_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np,
+ struct pinctrl_map **map,
+ unsigned *num_maps)
+{
+ struct pic32_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev);
+ const struct pin_group *grp;
+ struct pinctrl_map *new_map;
+ struct device_node *parent;
+ int map_num = 1;
+ int i;
+
+ /* first find the group of this node and check if we need create
+ * config maps for pins
+ */
+ grp = pic32_pinctrl_find_group_by_name(data, np->name);
+ if (!grp) {
+ dev_err(data->dev, "unable to find group for node %s\n",
+ np->name);
+ return -EINVAL;
+ }
+
+ map_num += (grp->ngspins + grp->ngpins);
+ new_map = devm_kzalloc(pctldev->dev,
+ sizeof(*new_map) * map_num, GFP_KERNEL);
+ if (!new_map)
+ return -ENOMEM;
+
+ *map = new_map;
+ *num_maps = map_num;
+
+ /* create mux map */
+ parent = of_get_parent(np);
+ if (!parent) {
+ devm_kfree(pctldev->dev, new_map);
+ return -EINVAL;
+ }
+ new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;
+ new_map[0].data.mux.function = parent->name;
+ new_map[0].data.mux.group = np->name;
+ of_node_put(parent);
+
+ /* single-pins */
+ new_map++;
+ for (i = 0; i < grp->ngspins; i++) {
+ unsigned pin = grp->gspins[i].bank * PINS_PER_BANK +
+ grp->gspins[i].pin;
+
+ new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN;
+ new_map[i].data.configs.group_or_pin =
+ pin_get_name(pctldev, pin);
+ new_map[i].data.configs.configs = &grp->gspins[i].conf;
+ new_map[i].data.configs.num_configs = 1;
+
+ }
+
+ /* pin-mux */
+ for (i = 0; i < grp->ngpins; i++) {
+ unsigned pin = grp->gpins[i].bank * PINS_PER_BANK +
+ grp->gpins[i].pin;
+
+ new_map[grp->ngspins + i].type = PIN_MAP_TYPE_CONFIGS_PIN;
+ new_map[grp->ngspins + i].data.configs.group_or_pin =
+ pin_get_name(pctldev, pin);
+ new_map[grp->ngspins + i].data.configs.configs =
+ &grp->gpins[i].conf;
+ new_map[grp->ngspins + i].data.configs.num_configs = 1;
+
+ }
+
+ dev_dbg(pctldev->dev, "%s: maps: function:%s group:%s num:%d\n",
+ __func__, (*map)->data.mux.function, (*map)->data.mux.group, map_num);
+
+ return 0;
+}
+
+static void pic32_dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+ struct pic32_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev);
+
+ dev_dbg(data->dev, "%s: free map=%p\n", __func__, map);
+
+ devm_kfree(pctldev->dev, map);
+}
+
+static const struct pinctrl_ops pic32_pinctrl_ops = {
+ .get_groups_count = pic32_get_groups_count,
+ .get_group_name = pic32_get_group_name,
+ .get_group_pins = pic32_get_group_pins,
+ .pin_dbg_show = pic32_pin_dbg_show,
+ .dt_node_to_map = pic32_dt_node_to_map,
+ .dt_free_map = pic32_dt_free_map,
+};
+
+static struct pinctrl_desc pic32_pinctrl_desc = {
+ .pctlops = &pic32_pinctrl_ops,
+ .pmxops = &pic32_pinmux_ops,
+ .confops = &pic32_pinconf_ops,
+ .owner = THIS_MODULE,
+};
+
+/* pinctrl mechanism force to keep a pin array, therfore the pin array will
+ * contain single-pinss and pinmux as well.
+ */
+static int pic32_pinctrl_cout_pins(struct platform_device *pdev,
+ struct pin_group *grp)
+{
+ int i;
+
+ grp->npins = grp->ngspins + grp->ngpins;
+ grp->pins = devm_kzalloc(&pdev->dev, grp->npins * sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!grp->pins)
+ return -ENOMEM;
+
+ /* the single-pinss */
+ for (i = 0; i < grp->ngspins; i++) {
+ grp->pins[i] =
+ grp->gspins[i].bank * PINS_PER_BANK +
+ grp->gspins[i].pin;
+ }
+
+ /* the pinmux */
+ for (i = 0; i < grp->ngpins; i++) {
+ grp->pins[grp->ngspins + i] =
+ grp->gpins[i].bank * PINS_PER_BANK +
+ grp->gpins[i].pin;
+ }
+
+ return 0;
+}
+
+/* return the idx of a property within the array of properties */
+static int get_pic32_pinctrl_propidx(struct property *prop)
+{
+ int i;
+
+ for (i = 0; i < PIC32_PINCTRL_PROP_LAST; i++) {
+ if (!strcmp(pinctrl_props[i].name, prop->name))
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+/* parse single-pins entry within a group */
+static int pic32_pinctrl_parse_single_pins(struct platform_device *pdev,
+ struct device_node *np,
+ struct pin_group *grp,
+ struct pic32_pinctrl_data *data,
+ int idx)
+{
+ struct property *prop;
+ int length;
+ int u32array_size;
+ u32 *u32array;
+ int i;
+ int ret = 0;
+
+ /* sanity check */
+ prop = of_find_property(np, pinctrl_props[idx].name, &length);
+ if (!prop) {
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+ /* get single-pins entries */
+ u32array_size = length/sizeof(u32);
+ if (u32array_size % pinctrl_props[idx].narg) {
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+ u32array = kcalloc(u32array_size, sizeof(u32), GFP_KERNEL);
+ if (!u32array) {
+ ret = -ENOMEM;
+ goto out_err;
+ }
+
+ ret = of_property_read_u32_array(np, pinctrl_props[idx].name,
+ u32array, u32array_size);
+ if (ret)
+ goto clean_out_err;
+
+ grp->ngspins = u32array_size/pinctrl_props[idx].narg;
+ grp->gspins = devm_kzalloc(&pdev->dev,
+ grp->ngspins * sizeof(struct gspin),
+ GFP_KERNEL);
+
+ dev_dbg(data->dev, "%s: parse %u pins\n", __func__,
+ grp->ngspins);
+ /* single-pins configuration entry:
+ * <pin-bank> <pin-idx> <pin-conf>
+ */
+ for (i = 0; i < grp->ngspins; i++) {
+ u32 *bank = (i == 0 ? u32array : (++u32array));
+ u32 *pin = (++u32array);
+ struct pin_conf *pinconf = (struct pin_conf *)(++u32array);
+
+ grp->gspins[i].bank = *bank;
+ grp->gspins[i].pin = *pin;
+
+ /* we need the direction for differentiate between
+ * PPS-in and PPS-out
+ */
+ grp->gspins[i].conf = (CONF_DIR(pinconf->dir) |
+ CONF_COD(pinconf->conf));
+ }
+
+ return 0;
+clean_out_err:
+ kfree(u32array);
+out_err:
+ dev_err(data->dev, "wrong single-pins entry\n");
+ return ret;
+}
+
+/* parse pinmux entry within a group */
+static int pic32_pinctrl_parse_pins(struct platform_device *pdev,
+ struct device_node *np,
+ struct pin_group *grp,
+ struct pic32_pinctrl_data *data,
+ int idx)
+{
+ struct property *prop;
+ int length;
+ int u32array_size;
+ u32 *u32array;
+ int i;
+ int ret = 0;
+
+ /* sanity check */
+ prop = of_find_property(np, pinctrl_props[idx].name, &length);
+ if (!prop) {
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+ u32array_size = length/sizeof(u32);
+ if (u32array_size % pinctrl_props[idx].narg) {
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+
+ /* get pinmux entries */
+ u32array = kcalloc(u32array_size, sizeof(u32), GFP_KERNEL);
+ if (!u32array) {
+ ret = -ENOMEM;
+ goto out_err;
+ }
+
+ ret = of_property_read_u32_array(np, pinctrl_props[idx].name,
+ u32array, u32array_size);
+ if (ret)
+ goto out_clean_err;
+
+ grp->ngpins = u32array_size/pinctrl_props[idx].narg;
+ grp->gpins = devm_kzalloc(&pdev->dev,
+ grp->ngpins * sizeof(struct gpins),
+ GFP_KERNEL);
+
+ dev_dbg(data->dev, "%s: parse %u pins\n", __func__,
+ grp->ngpins);
+ /* pinmux configuration entry:
+ * <pin-code> <peripheral-pin-code> <pin-conf>
+ */
+ for (i = 0; i < grp->ngpins; i++) {
+ struct rpin_cod *pincod = (struct rpin_cod *)
+ (i == 0 ? u32array : (++u32array));
+ struct ppin_cod *ppincod = (struct ppin_cod *)(++u32array);
+ struct pin_conf *pinconf = (struct pin_conf *)(++u32array);
+
+ dev_dbg(data->dev, "rpin(%x:%x:%x:%x:%x)\n",
+ pincod->bank, pincod->pin, pincod->bucket,
+ pincod->dir, pincod->cod);
+ dev_dbg(data->dev, "ppin(%x:%x:%x:%x)\n",
+ ppincod->pin, ppincod->bucket,
+ ppincod->dir, ppincod->cod);
+ dev_dbg(data->dev, "rpin-conf(%x:%x)\n",
+ pinconf->dir, pinconf->conf);
+
+ /* syntax check:
+ * 1) if the function is not available for bucket or
+ * 2) if the function is not availbale for direction
+ */
+ if (((pincod->bucket & ppincod->bucket) == 0) ||
+ (pincod->dir != ppincod->dir)) {
+ ret = -EINVAL;
+ goto out_clean_err;
+ }
+
+ grp->gpins[i].dir = pincod->dir;
+ grp->gpins[i].bucket = pincod->bucket;
+ grp->gpins[i].bank = pincod->bank;
+ grp->gpins[i].pin = pincod->pin;
+ grp->gpins[i].ppin = ppincod->pin;
+ if (grp->gpins[i].dir == DIR_OUT)
+ grp->gpins[i].cod = ppincod->cod;
+ else
+ grp->gpins[i].cod = pincod->cod;
+
+ grp->gpins[i].conf = (CONF_DIR(pinconf->dir) |
+ CONF_COD(pinconf->conf));
+ }
+
+ return 0;
+out_clean_err:
+ kfree(u32array);
+out_err:
+ dev_err(data->dev, "wrong pins entry\n");
+ return ret;
+
+}
+
+/* parse group's properties */
+static int pic32_pinctrl_parse_group_prop(struct platform_device *pdev,
+ struct device_node *np,
+ struct pin_group *grp,
+ struct pic32_pinctrl_data *data,
+ int idx)
+{
+ switch (idx) {
+ case PIC32_PINCTRL_PROP_SINGLE_PINS:
+ return pic32_pinctrl_parse_single_pins(pdev, np, grp,
+ data, idx);
+ break;
+ case PIC32_PINCTRL_PROP_PINS:
+ return pic32_pinctrl_parse_pins(pdev, np, grp,
+ data, idx);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+/* parse groups within a function */
+static int pic32_pinctrl_parse_groups(struct platform_device *pdev,
+ struct device_node *np,
+ struct pin_group *grp,
+ struct pic32_pinctrl_data *data,
+ u32 gidx)
+{
+ struct property *prop;
+ int ret = 0;
+
+ dev_dbg(data->dev, "%s: group(%d): %s\n", __func__, gidx, np->name);
+
+ grp->name = np->name;
+
+ /* parse group's properties */
+ for_each_property_of_node(np, prop) {
+ int prop_idx;
+
+ if (!strcmp(prop->name, "name") ||
+ !strcmp(prop->name, "phandle") ||
+ !strcmp(prop->name, "linux,phandle"))
+ continue;
+
+ prop_idx = get_pic32_pinctrl_propidx(prop);
+ if (prop_idx == -EINVAL) {
+ dev_err(data->dev, "property (%s) not found!\n",
+ prop->name);
+ return -EINVAL;
+ }
+ dev_dbg(data->dev, "property found: %s\n",
+ pinctrl_props[prop_idx].name);
+
+ ret = pic32_pinctrl_parse_group_prop(pdev, np, grp,
+ data, prop_idx);
+ if (ret) {
+ dev_err(data->dev, "wrong property (%s)!\n",
+ prop->name);
+ return ret;
+ }
+ }
+
+ return pic32_pinctrl_cout_pins(pdev, grp);
+}
+
+/* parse pinctrl's functions */
+static int pic32_pinctrl_parse_functions(struct platform_device *pdev,
+ struct device_node *np,
+ struct pic32_pinctrl_data *data,
+ u32 fidx)
+{
+ struct device_node *child;
+ struct pin_function *func;
+ struct pin_group *grp;
+ static unsigned gidx; /* group index index related to pinctrl */
+ unsigned f_gidx = 0; /* group index related to function */
+ int ret;
+
+ dev_dbg(data->dev, "%s: parse function(%d): %s\n", __func__,
+ fidx, np->name);
+
+ func = &data->functions[fidx];
+
+ /* initialise function */
+ func->name = np->name;
+ func->ngroups = of_get_child_count(np);
+ if (func->ngroups <= 0) {
+ dev_err(data->dev, "no groups defined\n");
+ return -EINVAL;
+ }
+ func->groups = devm_kzalloc(data->dev,
+ func->ngroups * sizeof(char *), GFP_KERNEL);
+ if (!func->groups)
+ return -ENOMEM;
+
+ /* parse groups within this function */
+ f_gidx = 0;
+ for_each_child_of_node(np, child) {
+ func->groups[f_gidx] = child->name;
+ grp = &data->groups[gidx++];
+ ret = pic32_pinctrl_parse_groups(pdev, child,
+ grp, data, f_gidx++);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* probe(parse) pinctrl's device tree node */
+static int pic32_pinctrl_probe_dt(struct platform_device *pdev,
+ struct pic32_pinctrl_data *data)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *child;
+ const char *pio_compat = "microchip,pic32-gpio";
+ struct pinctrl_pin_desc *pdesc;
+ int f_idx = 0;
+ int i, kbank, kpio;
+ int ret = 0;
+
+ child = of_get_next_child(np, NULL);
+ if (!child) {
+ dev_err(&pdev->dev, "no group is defined\n");
+ return -ENOENT;
+ }
+
+ /* count total functions and groups */
+ for_each_child_of_node(np, child) {
+ if (of_device_is_compatible(child, pio_compat)) {
+ data->nbanks++;
+ } else {
+ data->nfunctions++;
+ data->ngroups += of_get_child_count(child);
+ }
+ }
+
+ if (data->nbanks < 1 || data->nbanks > MAX_PIO_BANKS) {
+ dev_err(&pdev->dev,
+ "gpio-controllers between 1 to %u.\n", MAX_PIO_BANKS);
+ return -EINVAL;
+ }
+ data->npins = data->nbanks * PINS_PER_BANK;
+
+ dev_dbg(&pdev->dev, "%s:(nbanks:%u, nfunctions:%u, ngroups:%u)\n",
+ __func__, data->nbanks, data->nfunctions, data->ngroups);
+
+ data->functions = devm_kzalloc(&pdev->dev, data->nfunctions *
+ sizeof(*data->functions), GFP_KERNEL);
+ if (!data->functions)
+ return -ENOMEM;
+
+ data->groups = devm_kzalloc(&pdev->dev, data->ngroups *
+ sizeof(*data->groups), GFP_KERNEL);
+ if (!data->groups)
+ return -ENOMEM;
+
+ /* parse all functions */
+ f_idx = 0;
+ for_each_child_of_node(np, child) {
+ if (of_device_is_compatible(child, pio_compat))
+ continue;
+
+ ret = pic32_pinctrl_parse_functions(pdev, child,
+ data, f_idx++);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to parse function\n");
+ return ret;
+ }
+ }
+
+ pic32_pinctrl_desc.name = dev_name(&pdev->dev);
+ pic32_pinctrl_desc.npins = data->npins;
+ pic32_pinctrl_desc.pins = pdesc = devm_kzalloc(&pdev->dev,
+ sizeof(*pdesc) * data->npins,
+ GFP_KERNEL);
+ if (!pic32_pinctrl_desc.pins)
+ return -ENOMEM;
+
+ /* populate pinctrl pin descriptor */
+ kbank = 0;
+ for_each_child_of_node(np, child) {
+ if (of_device_is_compatible(child, pio_compat)) {
+ kpio = 0;
+ for (i = 0; i < PINS_PER_BANK; i++) {
+ pdesc->number = (kbank*PINS_PER_BANK) + kpio;
+ pdesc->name = kasprintf(GFP_KERNEL, "%s%c%d",
+ child->name,
+ kbank + 'A',
+ kpio);
+ pdesc->drv_data = NULL; /* unused */
+
+ pdesc++;
+ kpio++;
+ }
+ kbank++;
+ }
+ }
+
+ return 0;
+}
+
+/* probe pinctrl device */
+int pic32_pinctrl_probe(struct platform_device *pdev,
+ struct pic32_pps_off *pps_off,
+ struct pic32_caps *caps)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct pic32_pinctrl_data *data = NULL;
+ struct resource *r;
+ int i;
+ int ret = 0;
+
+ dev_dbg(&pdev->dev, "%s: probing...\n", __func__);
+
+ if (!np)
+ return -ENODEV;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ data->dev = &pdev->dev;
+ data->caps.pinconf_incaps = caps->pinconf_incaps;
+ data->caps.pinconf_outcaps = caps->pinconf_outcaps;
+
+ /* base address of pps(in, out) registers */
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ ret = -EINVAL;
+ goto probe_err;
+ }
+ data->ppsin_base = devm_ioremap_nocache(&pdev->dev, r->start,
+ resource_size(r));
+ if (IS_ERR(data->ppsin_base)) {
+ ret = PTR_ERR(data->ppsin_base);
+ goto probe_err;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!r) {
+ ret = -EINVAL;
+ goto probe_err;
+ }
+ data->ppsout_base = devm_ioremap_nocache(&pdev->dev, r->start,
+ resource_size(r));
+ if (IS_ERR(data->ppsout_base)) {
+ ret = PTR_ERR(data->ppsout_base);
+ goto probe_err;
+ }
+ data->pps_off = pps_off;
+
+ /* clocks */
+ data->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(data->clk)) {
+ ret = PTR_ERR(data->clk);
+ dev_err(&pdev->dev, "clk get failed\n");
+ goto probe_err;
+ }
+
+ ret = clk_prepare_enable(data->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "clk enable failed\n");
+ goto probe_err;
+ }
+
+ /* probe(parse) pinctrl device tree node */
+ ret = pic32_pinctrl_probe_dt(pdev, data);
+ if (ret) {
+ dev_err(&pdev->dev, "dt probe failed: %d\n", ret);
+ goto probe_err;
+ }
+
+ /* PIO driver must be probed first */
+ for (i = 0; i < data->nbanks; i++) {
+ if (!gpio_chips[i]) {
+ dev_warn(&pdev->dev,
+ "GPIO chip %d not registered\n", i);
+ ret = -EPROBE_DEFER;
+ goto probe_defer;
+ }
+ }
+
+ platform_set_drvdata(pdev, data);
+ data->pctl = pinctrl_register(&pic32_pinctrl_desc, &pdev->dev, data);
+ if (!data->pctl) {
+ dev_err(&pdev->dev, "Couldn't register pic32 pinctrl driver\n");
+ ret = -EINVAL;
+ goto probe_err;
+ }
+
+ /* Add gpio pin ranges - defined by GPIO nodes */
+ for (i = 0; i < data->nbanks; i++)
+ pinctrl_add_gpio_range(data->pctl, &gpio_chips[i]->range);
+
+ dev_info(&pdev->dev, "pic32 pinctrl driver initialized.\n");
+ return 0;
+
+probe_defer:
+probe_err:
+ if (data->ppsin_base)
+ devm_iounmap(&pdev->dev, data->ppsin_base);
+ if (data->ppsout_base)
+ devm_iounmap(&pdev->dev, data->ppsout_base);
+ if (data)
+ devm_kfree(&pdev->dev, data);
+ return ret;
+}
diff --git a/drivers/pinctrl/pinctrl-pic32.h b/drivers/pinctrl/pinctrl-pic32.h
new file mode 100644
index 0000000..4bc94f3
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-pic32.h
@@ -0,0 +1,158 @@
+/*
+ * pic32 pinctrl core definitions.
+ *
+ * Copyright (C) 2015 Microchip Technology, Inc.
+ * Author: Sorin-Andrei Pistirica <[email protected]>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __DT_PIC32_PINCTRL_H__
+#define __DT_PIC32_PINCTRL_H__
+
+#include <linux/of_device.h>
+
+#ifdef CONFIG_PINCTRL_PIC32MZ
+#include "dt-bindings/pinctrl/pic32.h"
+#elif defined(CONFIG_PINCTRL_PIC32MZDA)
+#include "dt-bindings/pinctrl/pic32mzda.h"
+#endif
+
+#define MAX_PIO_BANKS 10
+#define PINS_PER_BANK 32
+
+/* device tree bindings */
+enum pic32_pinctrl_props {
+ PIC32_PINCTRL_PROP_SINGLE_PINS = 0,
+ PIC32_PINCTRL_PROP_PINS,
+
+ /* add above this line */
+ PIC32_PINCTRL_PROP_LAST
+};
+
+struct pic32_pinctrl_prop {
+ const char *name;
+ const unsigned int narg;
+};
+#define PIC32_PINCTRL_DT_PROP(_name, _narg) { .name = _name, .narg = _narg }
+
+#define PINMUX_MASK(off, size) (((1 << (size)) - 1) << (off))
+#define PINMUX_FIELD(entry, off, size) \
+ (((PINMUX_MASK(off, size)) & (entry)) >> (off))
+
+#define PIC32_OFF_UNSPEC (~0UL)
+#define PIC32_REG_SIZE 4
+
+/* struct rpin_cod - remapable pin code
+ * @bank: the pin's bank (PORT_A-to-PORT_J)
+ * @pin: the pin (0-to-31)
+ * @bucket: the bucket (BUCKET_A-to-BUCKET_D)
+ * @dir: the pin direction (DIR_IN or DIR_OUT)
+ * @cod: the code for input pinmux (COD(0x0)-to-COD(0xF))
+ **/
+struct rpin_cod {
+ u32 bank:5,
+ pin:5,
+ bucket:5,
+ dir:1,
+ cod:16;
+};
+
+/* struct ppin_cod - peripheral pin code
+ * @pin: peripherl pin code (e.g. PP_INT1, PP_T2CK...)
+ * @bucket: the bucket (BUCKET_A-to-BUCKET_D)
+ * @dir: the pin direction (DIR_IN or DIR_OUT)
+ * @cod: the code for output pinmux (COD(0x0)-to-COD(0xF))
+ **/
+struct ppin_cod {
+ u32 pin:10,
+ bucket:5,
+ dir:1,
+ cod:16;
+};
+
+/* struct pin_conf - pin configuration code
+ * @dir: the pin direction (DIR_IN, DIR_OUT or DIR_NONE)
+ * @mode: the pinmux mode (PIC32_PIN_MODE_NONE, PIC32_PIN_MODE_SOURCING...)
+ **/
+struct pin_conf {
+ u32 dir:2,
+ conf:30;
+};
+
+/* struct pic32_reg - pic register
+ * @val: register value
+ * @clr: clear register value
+ * @set: set register value
+ * @inv: invert bits of register value
+ **/
+struct pic32_reg {
+ u32 val;
+ u32 clr;
+ u32 set;
+ u32 inv;
+} __packed;
+#define PIC32_PIO_REGS 4
+
+/* enum pic32_pio_regs - pic32 regs for manipulate pin configuration; the
+ * mapping may vary between pic32 flavors (e.g. MZ).
+ **/
+enum pic32_pio_regs {
+ PIC32_UNKNOWN = 0,
+
+ PIC32_ANSEL = 1,
+ PIC32_TRIS = 2,
+ PIC32_PORT = 3,
+ PIC32_LAT = 4,
+ PIC32_ODC = 5,
+ PIC32_CNPU = 6,
+ PIC32_CNPD = 7,
+ PIC32_CNCON = 8,
+ PIC32_CNEN = 9,
+ PIC32_CNSTAT = 10,
+
+ PIC32_CNNE = 11,
+ PIC32_CNF = 12,
+ PIC32_SRCON1 = 13,
+ PIC32_SRCON0 = 14,
+
+ /* add above this line */
+ PIC32_LAST
+};
+#define PIC32_CNCON_EDGE 11
+#define PIC32_CNCON_ON 15
+
+
+/* struct pic32_pps_off - pps registers mapping
+ * @ppsout_lookup_off: pps:out lookup registers offsets
+ * @ppsout_bank_start: pps:out mapping bank start
+ * @ppsout_bank_end: pps:out mapping bank end
+ * @ppsout_pin_start: pps:out mapping pin start
+ * @ppsout_pin_end: pps:out mapping pin end
+ * @ppsout_map_pins: pps:out mapping pins per bank
+ * @ppsin_lookup_off: pps:in lookup registers offsets
+ * @ppsin_lookup_size: pps:in lookup map size
+ **/
+struct pic32_pps_off {
+ unsigned (*ppsout_lookup_off)[MAX_PIO_BANKS][PINS_PER_BANK];
+ unsigned (*ppsin_lookup_off)[PP_MAX];
+};
+
+/* struct pic32_caps - pin configuration capabilities
+ * @pinconf_incaps: pin configuration input capabilities
+ * @pinconf_outcaps: pin configuration output capabilities
+ **/
+struct pic32_caps {
+ u32 pinconf_caps;
+ u32 pinconf_incaps;
+ u32 pinconf_outcaps;
+};
+
+int pic32_gpio_probe(struct platform_device *pdev,
+ unsigned (*reg_lookup_off)[], unsigned lookup_size);
+
+int pic32_pinctrl_probe(struct platform_device *pdev,
+ struct pic32_pps_off *pps_off,
+ struct pic32_caps *caps);
+
+#endif /*__DT_PIC32_PINCTRL_H__*/
diff --git a/drivers/pinctrl/pinctrl-pic32mzda.c b/drivers/pinctrl/pinctrl-pic32mzda.c
new file mode 100644
index 0000000..137dce1
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-pic32mzda.c
@@ -0,0 +1,294 @@
+/*
+ * pic32mzda pinctrl driver.
+ *
+ * Copyright (C) 2015 Microchip Technology, Inc.
+ * Author: Sorin-Andrei Pistirica <[email protected]>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include "pinctrl-pic32.h"
+
+/* PIC32MZDA PORT I/O: register offsets */
+static unsigned pic32mzda_pio_lookup_off[PIC32_LAST] = {
+ [PIC32_ANSEL] = 0 * PIC32_PIO_REGS * PIC32_REG_SIZE,
+ [PIC32_TRIS] = 1 * PIC32_PIO_REGS * PIC32_REG_SIZE,
+ [PIC32_PORT] = 2 * PIC32_PIO_REGS * PIC32_REG_SIZE,
+ [PIC32_LAT] = 3 * PIC32_PIO_REGS * PIC32_REG_SIZE,
+ [PIC32_ODC] = 4 * PIC32_PIO_REGS * PIC32_REG_SIZE,
+ [PIC32_CNPU] = 5 * PIC32_PIO_REGS * PIC32_REG_SIZE,
+ [PIC32_CNPD] = 6 * PIC32_PIO_REGS * PIC32_REG_SIZE,
+ [PIC32_CNCON] = 7 * PIC32_PIO_REGS * PIC32_REG_SIZE,
+ [PIC32_CNEN] = 8 * PIC32_PIO_REGS * PIC32_REG_SIZE,
+ [PIC32_CNSTAT] = 9 * PIC32_PIO_REGS * PIC32_REG_SIZE,
+ [PIC32_CNNE] = 10 * PIC32_PIO_REGS * PIC32_REG_SIZE,
+ [PIC32_CNF] = 11 * PIC32_PIO_REGS * PIC32_REG_SIZE,
+ [PIC32_SRCON1] = 12 * PIC32_PIO_REGS * PIC32_REG_SIZE,
+ [PIC32_SRCON0] = 13 * PIC32_PIO_REGS * PIC32_REG_SIZE,
+};
+
+static void pic32mzda_build_pio_lookup_off(
+ unsigned (*pio_lookup_off)[PIC32_LAST])
+{
+ int i;
+
+ /* Guard unsupported registers by flag PIC32_OFF_UNSPEC. */
+ for (i = 0; i < PIC32_LAST; i++) {
+ if ((*pio_lookup_off)[i] == 0 && i != PIC32_ANSEL)
+ (*pio_lookup_off)[i] = PIC32_OFF_UNSPEC;
+ }
+}
+
+static int pic32mzda_gpio_probe(struct platform_device *pdev)
+{
+ pic32mzda_build_pio_lookup_off(&pic32mzda_pio_lookup_off);
+
+ return pic32_gpio_probe(pdev,
+ &pic32mzda_pio_lookup_off,
+ PIC32_LAST);
+}
+
+static const struct of_device_id pic32mzda_gpio_of_match[] = {
+ { .compatible = "microchip,pic32-gpio" },
+ { /* sentinel */ }
+};
+
+static struct platform_driver pic32mzda_gpio_driver = {
+ .driver = {
+ .name = "gpio-pic32mzda",
+ .owner = THIS_MODULE,
+ .of_match_table = pic32mzda_gpio_of_match,
+ },
+ .probe = pic32mzda_gpio_probe,
+};
+
+/* Peripheral Pin Select (input): register offsets */
+static unsigned pic32mzda_ppsin_lookup_off[PP_MAX] = {
+ /*[PP_INT0] = 0 * PIC32_REG_SIZE, Not remappable */
+ [PP_INT1] = 1 * PIC32_REG_SIZE,
+ [PP_INT2] = 2 * PIC32_REG_SIZE,
+ [PP_INT3] = 3 * PIC32_REG_SIZE,
+ [PP_INT4] = 4 * PIC32_REG_SIZE,
+ /*[PP_T1CK] = 5 * PIC32_REG_SIZE, Not remappable */
+ [PP_T2CK] = 6 * PIC32_REG_SIZE,
+ [PP_T3CK] = 7 * PIC32_REG_SIZE,
+ [PP_T4CK] = 8 * PIC32_REG_SIZE,
+ [PP_T5CK] = 9 * PIC32_REG_SIZE,
+ [PP_T6CK] = 10 * PIC32_REG_SIZE,
+ [PP_T7CK] = 11 * PIC32_REG_SIZE,
+ [PP_T8CK] = 12 * PIC32_REG_SIZE,
+ [PP_T9CK] = 13 * PIC32_REG_SIZE,
+ [PP_IC1] = 14 * PIC32_REG_SIZE,
+ [PP_IC2] = 15 * PIC32_REG_SIZE,
+ [PP_IC3] = 16 * PIC32_REG_SIZE,
+ [PP_IC4] = 17 * PIC32_REG_SIZE,
+ [PP_IC5] = 18 * PIC32_REG_SIZE,
+ [PP_IC6] = 19 * PIC32_REG_SIZE,
+ [PP_IC7] = 20 * PIC32_REG_SIZE,
+ [PP_IC8] = 21 * PIC32_REG_SIZE,
+ [PP_IC9] = 22 * PIC32_REG_SIZE,
+ /* rsv = 23 */
+ [PP_OCFA] = 24 * PIC32_REG_SIZE,
+ /*[PP_OCFB] = 25 * PIC32_REG_SIZE, Not remappable */
+ [PP_U1RX] = 26 * PIC32_REG_SIZE,
+ [PP_U1CTS] = 27 * PIC32_REG_SIZE,
+ [PP_U2RX] = 28 * PIC32_REG_SIZE,
+ [PP_U2CTS] = 29 * PIC32_REG_SIZE,
+ [PP_U3RX] = 30 * PIC32_REG_SIZE,
+ [PP_U3CTS] = 31 * PIC32_REG_SIZE,
+ [PP_U4RX] = 32 * PIC32_REG_SIZE,
+ [PP_U4CTS] = 33 * PIC32_REG_SIZE,
+ [PP_U5RX] = 34 * PIC32_REG_SIZE,
+ [PP_U5CTS] = 35 * PIC32_REG_SIZE,
+ [PP_U6RX] = 36 * PIC32_REG_SIZE,
+ [PP_U6CTS] = 37 * PIC32_REG_SIZE,
+ /*[PP_SCK1IN] = 38 * PIC32_REG_SIZE, Not remappable */
+ [PP_SDI1] = 39 * PIC32_REG_SIZE,
+ [PP_SS1] = 40 * PIC32_REG_SIZE,
+ /*[PP_SCK2IN] = 41 * PIC32_REG_SIZE, Not remappable */
+ [PP_SDI2] = 42 * PIC32_REG_SIZE,
+ [PP_SS2] = 43 * PIC32_REG_SIZE,
+ /*[PP_SCK3IN] = 44 * PIC32_REG_SIZE, Not remappable */
+ [PP_SDI3] = 45 * PIC32_REG_SIZE,
+ [PP_SS3] = 46 * PIC32_REG_SIZE,
+ /*[PP_SCK4IN] = 47 * PIC32_REG_SIZE, Not remappable */
+ [PP_SDI4] = 48 * PIC32_REG_SIZE,
+ [PP_SS4] = 49 * PIC32_REG_SIZE,
+ /*[PP_SCK5IN] = 50 * PIC32_REG_SIZE, Not remappable */
+ [PP_SDI5] = 51 * PIC32_REG_SIZE,
+ [PP_SS5] = 52 * PIC32_REG_SIZE,
+ /*[PP_SCK6IN] = 53 * PIC32_REG_SIZE, Not remappable */
+ [PP_SDI6] = 54 * PIC32_REG_SIZE,
+ [PP_SS6] = 55 * PIC32_REG_SIZE,
+ [PP_C1RX] = 56 * PIC32_REG_SIZE,
+ [PP_C2RX] = 57 * PIC32_REG_SIZE,
+ [PP_REFCLKI1] = 58 * PIC32_REG_SIZE,
+ /*[PP_REFCLKI2] = 59 * PIC32_REG_SIZE, Not remappable */
+ [PP_REFCLKI3] = 60 * PIC32_REG_SIZE,
+ [PP_REFCLKI4] = 61 * PIC32_REG_SIZE
+};
+
+static void pic32mzda_build_ppsin_lookup_off(
+ unsigned (*ppsin_lookup_off)[PP_MAX])
+{
+ int i;
+
+ /* Guard unsupported configurations by flag PIC32_OFF_UNSPEC. */
+ for (i = 0; i < PP_MAX; i++) {
+ if ((*ppsin_lookup_off)[i] == 0)
+ (*ppsin_lookup_off)[i] = PIC32_OFF_UNSPEC;
+ }
+}
+
+/* Peripheral Pin Select (output): register offsets */
+static unsigned pic32mzda_ppsout_lookup_off[MAX_PIO_BANKS][PINS_PER_BANK] = {
+ [PORT_A][14] = (PORT_A * 16 + 14) * PIC32_REG_SIZE,
+ [PORT_A][15] = (PORT_A * 16 + 15) * PIC32_REG_SIZE,
+
+ [PORT_B][0] = (PORT_B * 16 + 0) * PIC32_REG_SIZE,
+ [PORT_B][1] = (PORT_B * 16 + 1) * PIC32_REG_SIZE,
+ [PORT_B][2] = (PORT_B * 16 + 2) * PIC32_REG_SIZE,
+ [PORT_B][3] = (PORT_B * 16 + 3) * PIC32_REG_SIZE,
+ [PORT_B][5] = (PORT_B * 16 + 5) * PIC32_REG_SIZE,
+ [PORT_B][6] = (PORT_B * 16 + 6) * PIC32_REG_SIZE,
+ [PORT_B][7] = (PORT_B * 16 + 7) * PIC32_REG_SIZE,
+ [PORT_B][8] = (PORT_B * 16 + 8) * PIC32_REG_SIZE,
+ [PORT_B][9] = (PORT_B * 16 + 9) * PIC32_REG_SIZE,
+ [PORT_B][10] = (PORT_B * 16 + 10) * PIC32_REG_SIZE,
+ [PORT_B][15] = (PORT_B * 16 + 15) * PIC32_REG_SIZE,
+
+ [PORT_C][1] = (PORT_C * 16 + 1) * PIC32_REG_SIZE,
+ [PORT_C][2] = (PORT_C * 16 + 2) * PIC32_REG_SIZE,
+ [PORT_C][3] = (PORT_C * 16 + 3) * PIC32_REG_SIZE,
+ [PORT_C][13] = (PORT_C * 16 + 13) * PIC32_REG_SIZE,
+ [PORT_C][14] = (PORT_C * 16 + 14) * PIC32_REG_SIZE,
+
+ [PORT_D][0] = (PORT_D * 16 + 0) * PIC32_REG_SIZE,
+ [PORT_D][2] = (PORT_D * 16 + 2) * PIC32_REG_SIZE,
+ [PORT_D][3] = (PORT_D * 16 + 3) * PIC32_REG_SIZE,
+ [PORT_D][4] = (PORT_D * 16 + 4) * PIC32_REG_SIZE,
+ [PORT_D][5] = (PORT_D * 16 + 5) * PIC32_REG_SIZE,
+ [PORT_D][6] = (PORT_D * 16 + 6) * PIC32_REG_SIZE,
+ [PORT_D][7] = (PORT_D * 16 + 7) * PIC32_REG_SIZE,
+ [PORT_D][9] = (PORT_D * 16 + 9) * PIC32_REG_SIZE,
+ [PORT_D][10] = (PORT_D * 16 + 10) * PIC32_REG_SIZE,
+ [PORT_D][11] = (PORT_D * 16 + 11) * PIC32_REG_SIZE,
+ [PORT_D][12] = (PORT_D * 16 + 12) * PIC32_REG_SIZE,
+ [PORT_D][14] = (PORT_D * 16 + 14) * PIC32_REG_SIZE,
+ [PORT_D][15] = (PORT_D * 16 + 15) * PIC32_REG_SIZE,
+
+ [PORT_E][3] = (PORT_E * 16 + 3) * PIC32_REG_SIZE,
+ [PORT_E][5] = (PORT_E * 16 + 5) * PIC32_REG_SIZE,
+ [PORT_E][8] = (PORT_E * 16 + 8) * PIC32_REG_SIZE,
+ [PORT_E][9] = (PORT_E * 16 + 9) * PIC32_REG_SIZE,
+
+ [PORT_F][0] = (PORT_F * 16 + 0) * PIC32_REG_SIZE,
+ [PORT_F][1] = (PORT_F * 16 + 1) * PIC32_REG_SIZE,
+ [PORT_F][2] = (PORT_F * 16 + 2) * PIC32_REG_SIZE,
+ [PORT_F][3] = (PORT_F * 16 + 3) * PIC32_REG_SIZE,
+ [PORT_F][4] = (PORT_F * 16 + 4) * PIC32_REG_SIZE,
+ [PORT_F][5] = (PORT_F * 16 + 5) * PIC32_REG_SIZE,
+ [PORT_F][8] = (PORT_F * 16 + 8) * PIC32_REG_SIZE,
+ [PORT_F][12] = (PORT_F * 16 + 12) * PIC32_REG_SIZE,
+
+ [PORT_G][0] = (PORT_G * 16 + 0) * PIC32_REG_SIZE,
+ [PORT_G][1] = (PORT_G * 16 + 1) * PIC32_REG_SIZE,
+ [PORT_G][7] = (PORT_G * 16 + 7) * PIC32_REG_SIZE,
+ [PORT_G][8] = (PORT_G * 16 + 8) * PIC32_REG_SIZE,
+ [PORT_G][9] = (PORT_G * 16 + 9) * PIC32_REG_SIZE,
+};
+
+static void pic32mzda_build_ppsout_lookup_off(
+ unsigned (*ppsout_lookup_off)[MAX_PIO_BANKS][PINS_PER_BANK])
+{
+ unsigned bank, pin;
+
+ /* Guard unsupported configurations by flag PIC32_OFF_UNSPEC. */
+ for (bank = 0; bank < MAX_PIO_BANKS; bank++) {
+ for (pin = 0; pin < PINS_PER_BANK; pin++)
+ if ((*ppsout_lookup_off)[bank][pin] == 0)
+ (*ppsout_lookup_off)[bank][pin] =
+ PIC32_OFF_UNSPEC;
+ }
+}
+
+static struct pic32_pps_off pic32mzda_pps_off = {
+ .ppsout_lookup_off = &pic32mzda_ppsout_lookup_off,
+ .ppsin_lookup_off = &pic32mzda_ppsin_lookup_off,
+};
+
+static struct pic32_caps pic32mzda_caps = {
+ .pinconf_caps = 0,
+ .pinconf_incaps = 0,
+ .pinconf_outcaps = 0
+};
+
+static void pic32mzda_build_caps(struct pic32_caps *caps)
+{
+ (caps->pinconf_caps) = PIC32_PIN_CONF_NONE |
+ PIC32_PIN_CONF_OD |
+ PIC32_PIN_CONF_PU |
+ PIC32_PIN_CONF_PD |
+ PIC32_PIN_CONF_AN |
+ PIC32_PIN_CONF_DG;
+ (caps->pinconf_outcaps) = PIC32_PIN_CONF_OD_OUT |
+ PIC32_PIN_CONF_DG_OUT;
+ (caps->pinconf_incaps) = PIC32_PIN_CONF_PU_IN |
+ PIC32_PIN_CONF_PD_IN |
+ PIC32_PIN_CONF_AN_IN |
+ PIC32_PIN_CONF_DG_IN;
+}
+
+static int pic32mzda_pinctrl_probe(struct platform_device *pdev)
+{
+ pic32mzda_build_caps(&pic32mzda_caps);
+
+ return pic32_pinctrl_probe(pdev,
+ &pic32mzda_pps_off,
+ &pic32mzda_caps);
+}
+
+static const struct of_device_id pic32mzda_pinctrl_of_match[] = {
+ { .compatible = "microchip,pic32-pinctrl"},
+ { /* sentinel */ }
+};
+
+static struct platform_driver pic32mzda_pinctrl_driver = {
+ .driver = {
+ .name = "pinctrl-pic32mzda",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pic32mzda_pinctrl_of_match),
+ },
+ .probe = pic32mzda_pinctrl_probe,
+};
+
+static int __init pic32mzda_pinctrl_init(void)
+{
+ int ret = 0;
+
+ pic32mzda_build_ppsin_lookup_off(&pic32mzda_ppsin_lookup_off);
+ pic32mzda_build_ppsout_lookup_off(&pic32mzda_ppsout_lookup_off);
+
+ ret = platform_driver_register(&pic32mzda_gpio_driver);
+ if (ret)
+ return ret;
+
+ return platform_driver_register(&pic32mzda_pinctrl_driver);
+}
+arch_initcall(pic32mzda_pinctrl_init);
+
+static void __exit pic32mzda_pinctrl_exit(void)
+{
+ platform_driver_unregister(&pic32mzda_gpio_driver);
+ platform_driver_unregister(&pic32mzda_pinctrl_driver);
+}
+module_exit(pic32mzda_pinctrl_exit);
+
+MODULE_AUTHOR("Sorin-Andrei Pistirica <[email protected]>");
+MODULE_DESCRIPTION("Microchop pic32mzda pinctrl driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/pinctrl-pic32mzda.h b/drivers/pinctrl/pinctrl-pic32mzda.h
new file mode 100644
index 0000000..1da0bc2
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-pic32mzda.h
@@ -0,0 +1,40 @@
+/*
+ * pic32mz pinctrl definitions.
+ *
+ * Copyright (C) 2015 Microchip Technology, Inc.
+ * Author: Sorin-Andrei Pistirica <[email protected]>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __DT_PIC32MZ_PINCTRL_H__
+#define __DT_PIC32MZ_PINCTRL_H__
+
+#include "pinctrl-pic32.h"
+
+/* struct pic32mz_pio - portX config register map
+ * @ansel: operations of analog port pins
+ * @tris: data direction register
+ * @port: read from port pins
+ * @lat: write to the port pins
+ * @odc: digital or open-drain register
+ * @cnpu: weak pull-up register
+ * @cnpd: weak pull-down register
+ * @cncon: change notification (CN) control register
+ * @cnen: CN interrupt enable control bits
+ * @cnstat: change occurred on corresponding pins
+ **/
+struct pic32mz_pio {
+ struct pic32_reg ansel;
+ struct pic32_reg tris;
+ struct pic32_reg port;
+ struct pic32_reg lat;
+ struct pic32_reg odc;
+ struct pic32_reg cnpu;
+ struct pic32_reg cnpd;
+ struct pic32_reg cncon;
+ struct pic32_reg cnen;
+ struct pic32_reg cnstat;
+} __packed;
+
+#endif /*__DT_PIC32MZ_PINCTRL_H__*/
--
1.7.9.5
From: Andrei Pistirica <[email protected]>
Document the devicetree bindings for the USART peripheral found on
Microchip PIC32 class devices.
Signed-off-by: Andrei Pistirica <[email protected]>
Signed-off-by: Joshua Henderson <[email protected]>
---
.../bindings/serial/microchip,pic32-usart.txt | 29 ++++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 Documentation/devicetree/bindings/serial/microchip,pic32-usart.txt
diff --git a/Documentation/devicetree/bindings/serial/microchip,pic32-usart.txt b/Documentation/devicetree/bindings/serial/microchip,pic32-usart.txt
new file mode 100644
index 0000000..c87321c
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/microchip,pic32-usart.txt
@@ -0,0 +1,29 @@
+* Microchip Universal Synchronous Asynchronous Receiver/Transmitter (USART)
+
+Required properties:
+- compatible: Should be "microchip,pic32-usart"
+- reg: Should contain registers location and length
+- interrupts: Should contain interrupt
+- pinctrl: Should contain pinctrl for TX/RX/RTS/CTS
+
+Optional properties:
+- microchip,uart-has-rtscts : Indicate the uart has hardware flow control
+- rts-gpios: RTS pin for USP-based UART if microchip,uart-has-rtscts
+- cts-gpios: CTS pin for USP-based UART if microchip,uart-has-rtscts
+
+Example:
+ usart0: serial@1f822000 {
+ compatible = "microchip,pic32-usart";
+ reg = <0x1f822000 0x50>;
+ interrupts = <UART1_FAULT DEFAULT_INT_PRI IRQ_TYPE_NONE>,
+ <UART1_RECEIVE_DONE DEFAULT_INT_PRI IRQ_TYPE_NONE>,
+ <UART1_TRANSFER_DONE DEFAULT_INT_PRI IRQ_TYPE_NONE>;
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ &pinctrl_uart1
+ &pinctrl_uart1_cts
+ &pinctrl_uart1_rts>;
+ microchip,uart-has-rtscts;
+ cts-gpios = <&pioB 15 0>;
+ rts-gpios = <&pioD 1 0>;
+ };
--
1.7.9.5
From: Andrei Pistirica <[email protected]>
This adds UART and a serial console driver for Microchip PIC32 class
devices.
Signed-off-by: Andrei Pistirica <[email protected]>
Signed-off-by: Joshua Henderson <[email protected]>
---
drivers/tty/serial/Kconfig | 21 +
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/pic32_uart.c | 930 ++++++++++++++++++++++++++++++++++++++
drivers/tty/serial/pic32_uart.h | 199 ++++++++
include/uapi/linux/serial_core.h | 3 +
5 files changed, 1154 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 1aec440..c844c32 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..5b5ec94
--- /dev/null
+++ b/drivers/tty/serial/pic32_uart.c
@@ -0,0 +1,930 @@
+/*
+ * PIC32 Integrated Serial Driver.
+ *
+ * Copyright (C) 2015 Microchip Technology, Inc.
+ *
+ * Authors:
+ * Steve Scott <[email protected]>,
+ * Sorin-Andrei Pistirica <[email protected]>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/console.h>
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/sysrq.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <uapi/linux/serial_core.h>
+#include <linux/delay.h>
+
+#include "pic32_uart.h"
+
+/* UART name and device definitions */
+#define PIC32_DEV_NAME "pic32-usart"
+#define PIC32_MAX_UARTS 6
+
+#define PIC32_SDEV_NAME "ttyS"
+#define PIC32_SDEV_MAJOR TTY_MAJOR
+#define PIC32_SDEV_MINOR 64
+
+/* pic32_sport pointer for console use */
+static struct pic32_sport *pic32_sports[PIC32_MAX_UARTS];
+
+static inline int pic32_enable_clock(struct pic32_sport *sport)
+{
+ sport->ref_clk++;
+
+ return clk_prepare_enable(sport->clk);
+}
+
+static inline void pic32_disable_clock(struct pic32_sport *sport)
+{
+ sport->ref_clk--;
+ clk_disable_unprepare(sport->clk);
+}
+
+/* serial core request to check if uart tx buffer is empty */
+static unsigned int pic32_uart_tx_empty(struct uart_port *port)
+{
+ struct pic32_sport *sport = to_pic32_sport(port);
+ u32 val = pic32_uart_read(sport, PIC32_UART_STA);
+
+ return (val & PIC32_UART_STA_TRMT) ? 1 : 0;
+}
+
+/* serial core request to set UART outputs */
+static void pic32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ struct pic32_sport *sport = to_pic32_sport(port);
+
+ /* set loopback mode */
+ if (mctrl & TIOCM_LOOP)
+ pic32_uart_rset(PIC32_UART_MODE_LPBK, sport, PIC32_UART_MODE);
+ else
+ pic32_uart_rclr(PIC32_UART_MODE_LPBK, sport, PIC32_UART_MODE);
+}
+
+/* get the state of CTS input pin for this port */
+static unsigned int get_cts_state(struct pic32_sport *sport)
+{
+ /* default state must be asserted */
+ int val = 1;
+
+ /* read and invert UxCTS */
+ if (gpio_is_valid(sport->cts_gpio))
+ val = !gpio_get_value(sport->cts_gpio);
+
+ return val;
+}
+
+/* serial core request to return the state of misc UART input pins */
+static unsigned int pic32_uart_get_mctrl(struct uart_port *port)
+{
+ struct pic32_sport *sport = to_pic32_sport(port);
+ unsigned int mctrl = 0;
+
+ if (!sport->hw_flow_ctrl) {
+ mctrl |= TIOCM_CTS;
+ goto ret;
+ }
+
+ if (get_cts_state(sport))
+ mctrl |= TIOCM_CTS;
+
+ret:
+ /* DSR and CD are not supported in PIC32, so return 1
+ * RI is not supported in PIC32, so return 0
+ */
+ mctrl |= TIOCM_CD;
+ mctrl |= TIOCM_DSR;
+
+ return mctrl;
+}
+
+/* stop tx and start tx are not called in pairs, therefore a flag indicates
+ * the status of irq to control the irq-depth.
+ */
+static inline void pic32_uart_irqtxen(struct pic32_sport *sport, u8 en)
+{
+ if (en && !tx_irq_enabled(sport)) {
+ enable_irq(sport->irq_tx);
+ tx_irq_enabled(sport) = 1;
+ } else if (!en && tx_irq_enabled(sport)) {
+ /* use disable_irq_nosync() and not disable_irq() to avoid self
+ * imposed deadlock by not waiting for irq handler to end,
+ * since this callback is called from interrupt context.
+ */
+ disable_irq_nosync(sport->irq_tx);
+ tx_irq_enabled(sport) = 0;
+ }
+}
+
+/* serial core request to disable tx ASAP (used for flow control) */
+static void pic32_uart_stop_tx(struct uart_port *port)
+{
+ struct pic32_sport *sport = to_pic32_sport(port);
+
+ if (!(pic32_uart_read(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON))
+ return;
+
+ if (!(pic32_uart_read(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN))
+ return;
+
+ /* wait for tx empty */
+ while (!(pic32_uart_read(sport, PIC32_UART_STA) & PIC32_UART_STA_TRMT))
+ udelay(1);
+
+ pic32_uart_rclr(PIC32_UART_STA_UTXEN, sport, PIC32_UART_STA);
+ pic32_uart_irqtxen(sport, 0);
+}
+
+/* serial core request to (re)enable tx */
+static void pic32_uart_start_tx(struct uart_port *port)
+{
+ struct pic32_sport *sport = to_pic32_sport(port);
+
+ pic32_uart_irqtxen(sport, 1);
+ pic32_uart_rset(PIC32_UART_STA_UTXEN, sport, PIC32_UART_STA);
+}
+
+/* serial core request to stop rx, called before port shutdown */
+static void pic32_uart_stop_rx(struct uart_port *port)
+{
+ struct pic32_sport *sport = to_pic32_sport(port);
+
+ /* disable rx interrupts */
+ disable_irq(sport->irq_rx);
+
+ /* receiver Enable bit OFF */
+ pic32_uart_rclr(PIC32_UART_STA_URXEN, sport, PIC32_UART_STA);
+}
+
+/* serial core request to start/stop emitting break char */
+static void pic32_uart_break_ctl(struct uart_port *port, int ctl)
+{
+ struct pic32_sport *sport = to_pic32_sport(port);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ if (ctl)
+ pic32_uart_rset(PIC32_UART_STA_UTXBRK, sport, PIC32_UART_STA);
+ else
+ pic32_uart_rclr(PIC32_UART_STA_UTXBRK, sport, PIC32_UART_STA);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/* get port type in string format */
+static const char *pic32_uart_type(struct uart_port *port)
+{
+ return (port->type == PORT_PIC32) ? PIC32_DEV_NAME : NULL;
+}
+
+/* read all chars in rx fifo and send them to core */
+static void pic32_uart_do_rx(struct uart_port *port)
+{
+ struct pic32_sport *sport = to_pic32_sport(port);
+ struct tty_port *tty;
+ unsigned int max_count;
+
+ /* limit number of char read in interrupt, should not be
+ * higher than fifo size anyway since we're much faster than
+ * serial port
+ */
+ max_count = PIC32_UART_RX_FIFO_DEPTH;
+
+ spin_lock(&port->lock);
+
+ tty = &port->state->port;
+
+ do {
+ u32 sta_reg, c;
+ char flag;
+
+ /* get overrun/fifo empty information from status register */
+ sta_reg = pic32_uart_read(sport, PIC32_UART_STA);
+ if (unlikely(sta_reg & PIC32_UART_STA_OERR)) {
+
+ /* fifo reset is required to clear interrupt */
+ pic32_uart_rclr(PIC32_UART_STA_OERR, sport,
+ PIC32_UART_STA);
+
+ port->icount.overrun++;
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ }
+
+ /* Can at least one more character can be read? */
+ if (!(sta_reg & PIC32_UART_STA_URXDA))
+ break;
+
+ /* read the character and increment the rx counter */
+ c = pic32_uart_read(sport, PIC32_UART_RX);
+
+ port->icount.rx++;
+ flag = TTY_NORMAL;
+ c &= 0xff;
+
+ if (unlikely((sta_reg & PIC32_UART_STA_PERR) ||
+ (sta_reg & PIC32_UART_STA_FERR))) {
+
+ /* do stats first */
+ if (sta_reg & PIC32_UART_STA_PERR)
+ port->icount.parity++;
+ if (sta_reg & PIC32_UART_STA_FERR)
+ port->icount.frame++;
+
+ /* update flag wrt read_status_mask */
+ sta_reg &= port->read_status_mask;
+
+ if (sta_reg & PIC32_UART_STA_FERR)
+ flag = TTY_FRAME;
+ if (sta_reg & PIC32_UART_STA_PERR)
+ flag = TTY_PARITY;
+ }
+
+ if (uart_handle_sysrq_char(port, c))
+ continue;
+
+ if ((sta_reg & port->ignore_status_mask) == 0)
+ tty_insert_flip_char(tty, c, flag);
+
+ } while (--max_count);
+
+ spin_unlock(&port->lock);
+
+ tty_flip_buffer_push(tty);
+}
+
+/* fill tx fifo with chars to send, stop when fifo is about to be full
+ * or when all chars have been sent.
+ */
+static void pic32_uart_do_tx(struct uart_port *port)
+{
+ struct pic32_sport *sport = to_pic32_sport(port);
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned int max_count = PIC32_UART_TX_FIFO_DEPTH;
+
+ if (port->x_char) {
+ pic32_uart_write(port->x_char, sport, PIC32_UART_TX);
+ port->icount.tx++;
+ port->x_char = 0;
+ return;
+ }
+
+ if (uart_tx_stopped(port)) {
+ pic32_uart_stop_tx(port);
+ return;
+ }
+
+ if (uart_circ_empty(xmit))
+ goto txq_empty;
+
+ /* keep stuffing chars into uart tx buffer
+ * 1) until uart fifo is full
+ * or
+ * 2) until the circ buffer is empty
+ * (all chars have been sent)
+ * or
+ * 3) until the max count is reached
+ * (prevents lingering here for too long in certain cases)
+ */
+ while (!(PIC32_UART_STA_UTXBF &
+ pic32_uart_rval(sport, PIC32_UART_STA))) {
+ unsigned int c = xmit->buf[xmit->tail];
+
+ pic32_uart_write(c, sport, PIC32_UART_TX);
+
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ --max_count;
+ if (uart_circ_empty(xmit))
+ break;
+ if (max_count == 0)
+ break;
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ if (uart_circ_empty(xmit))
+ goto txq_empty;
+
+ return;
+
+txq_empty:
+ pic32_uart_irqtxen(sport, 0);
+}
+
+/* RX interrupt handler */
+static irqreturn_t pic32_uart_rx_interrupt(int irq, void *dev_id)
+{
+ struct uart_port *port = dev_id;
+
+ pic32_uart_do_rx(port);
+
+ return IRQ_HANDLED;
+}
+
+/* TX interrupt handler */
+static irqreturn_t pic32_uart_tx_interrupt(int irq, void *dev_id)
+{
+ struct uart_port *port = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ pic32_uart_do_tx(port);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+/* FAULT interrupt handler */
+static irqreturn_t pic32_uart_fault_interrupt(int irq, void *dev_id)
+{
+ /* do nothing: pic32_uart_do_rx() handles faults. */
+ return IRQ_HANDLED;
+}
+
+/* enable rx & tx operation on uart */
+static void pic32_uart_en_and_unmask(struct uart_port *port)
+{
+ struct pic32_sport *sport = to_pic32_sport(port);
+
+ pic32_uart_rset(PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN,
+ sport, PIC32_UART_STA);
+ pic32_uart_rset(PIC32_UART_MODE_ON, sport, PIC32_UART_MODE);
+}
+
+/* disable rx & tx operation on uart */
+static void pic32_uart_dsbl_and_mask(struct uart_port *port)
+{
+ struct pic32_sport *sport = to_pic32_sport(port);
+
+ pic32_uart_rclr(PIC32_UART_MODE_ON, sport, PIC32_UART_MODE);
+ pic32_uart_rclr(PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN,
+ sport, PIC32_UART_STA);
+}
+
+/* serial core request to initialize uart and start rx operation */
+static int pic32_uart_startup(struct uart_port *port)
+{
+ struct pic32_sport *sport = to_pic32_sport(port);
+ u32 dflt_baud = ((port->uartclk / PIC32_UART_DFLT_BRATE) / 16) - 1;
+ unsigned long flags;
+ int ret = 0;
+
+ local_irq_save(flags);
+
+ ret = pic32_enable_clock(sport);
+ if (ret)
+ goto out_unlock;
+
+ /* clear status and mode registers */
+ pic32_uart_write(0, sport, PIC32_UART_MODE);
+ pic32_uart_write(0, sport, PIC32_UART_STA);
+
+ /* disable uart and mask all interrupts */
+ pic32_uart_dsbl_and_mask(port);
+
+ /* set default baud */
+ pic32_uart_write(dflt_baud, sport, PIC32_UART_BRG);
+
+ local_irq_restore(flags);
+
+ /* Each UART of a PIC32 has three interrupts therefore,
+ * we setup driver to register the 3 irqs for the device.
+ *
+ * For each irq request_irq() is called with interrupt disabled.
+ * And the irq is enabled as soon as we are ready to handle them.
+ */
+ tx_irq_enabled(sport) = 0;
+
+ sport->irq_fault_name = kasprintf(GFP_KERNEL, "%s%d-fault",
+ pic32_uart_type(port),
+ sport->idx);
+ irq_set_status_flags(sport->irq_fault, IRQ_NOAUTOEN);
+ ret = request_irq(sport->irq_fault, pic32_uart_fault_interrupt,
+ sport->irqflags_fault, sport->irq_fault_name, port);
+ if (ret) {
+ dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n",
+ __func__, sport->irq_fault, ret,
+ pic32_uart_type(port));
+ goto out_done;
+ }
+
+ sport->irq_rx_name = kasprintf(GFP_KERNEL, "%s%d-rx",
+ pic32_uart_type(port),
+ sport->idx);
+ irq_set_status_flags(sport->irq_rx, IRQ_NOAUTOEN);
+ ret = request_irq(sport->irq_rx, pic32_uart_rx_interrupt,
+ sport->irqflags_rx, sport->irq_rx_name, port);
+ if (ret) {
+ dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n",
+ __func__, sport->irq_rx, ret,
+ pic32_uart_type(port));
+ goto out_done;
+ }
+
+ sport->irq_tx_name = kasprintf(GFP_KERNEL, "%s%d-tx",
+ pic32_uart_type(port),
+ sport->idx);
+ irq_set_status_flags(sport->irq_tx, IRQ_NOAUTOEN);
+ ret = request_irq(sport->irq_tx, pic32_uart_tx_interrupt,
+ sport->irqflags_tx, sport->irq_tx_name, port);
+ if (ret) {
+ dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n",
+ __func__, sport->irq_tx, ret,
+ pic32_uart_type(port));
+ goto out_done;
+ }
+
+ local_irq_save(flags);
+
+ /* set rx interrupt on first receive */
+ pic32_uart_rclr(PIC32_UART_STA_URXISEL1 | PIC32_UART_STA_URXISEL0,
+ sport, PIC32_UART_STA);
+
+ /* set interrupt on empty */
+ pic32_uart_rclr(PIC32_UART_STA_UTXISEL1, sport, PIC32_UART_STA);
+
+ /* enable all interrupts and eanable uart */
+ pic32_uart_en_and_unmask(port);
+
+ enable_irq(sport->irq_fault);
+ enable_irq(sport->irq_rx);
+
+out_unlock:
+ local_irq_restore(flags);
+
+out_done:
+ return ret;
+}
+
+/* serial core request to flush & disable uart */
+static void pic32_uart_shutdown(struct uart_port *port)
+{
+ struct pic32_sport *sport = to_pic32_sport(port);
+ unsigned long flags;
+
+ /* disable uart */
+ spin_lock_irqsave(&port->lock, flags);
+ pic32_uart_dsbl_and_mask(port);
+ spin_unlock_irqrestore(&port->lock, flags);
+ pic32_disable_clock(sport);
+
+ /* free all 3 interrupts for this UART */
+ free_irq(sport->irq_fault, port);
+ free_irq(sport->irq_tx, port);
+ free_irq(sport->irq_rx, port);
+}
+
+/* serial core request to change current uart setting */
+static void pic32_uart_set_termios(struct uart_port *port,
+ struct ktermios *new,
+ struct ktermios *old)
+{
+ struct pic32_sport *sport = to_pic32_sport(port);
+ unsigned int baud;
+ unsigned int quot;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* disable uart and mask all interrupts while changing speed */
+ pic32_uart_dsbl_and_mask(port);
+
+ /* stop bit options */
+ if (new->c_cflag & CSTOPB)
+ pic32_uart_rset(PIC32_UART_MODE_STSEL, sport, PIC32_UART_MODE);
+ else
+ pic32_uart_rclr(PIC32_UART_MODE_STSEL, sport, PIC32_UART_MODE);
+
+ /* parity options */
+ if (new->c_cflag & PARENB) {
+ if (new->c_cflag & PARODD) {
+ pic32_uart_rset(PIC32_UART_MODE_PDSEL1, sport,
+ PIC32_UART_MODE);
+ pic32_uart_rclr(PIC32_UART_MODE_PDSEL0, sport,
+ PIC32_UART_MODE);
+ } else {
+ pic32_uart_rset(PIC32_UART_MODE_PDSEL0, sport,
+ PIC32_UART_MODE);
+ pic32_uart_rclr(PIC32_UART_MODE_PDSEL1, sport,
+ PIC32_UART_MODE);
+ }
+ } else {
+ pic32_uart_rclr(PIC32_UART_MODE_PDSEL1 | PIC32_UART_MODE_PDSEL0,
+ sport, PIC32_UART_MODE);
+ }
+ /* if hw flow ctrl, then the pins must be specified in device tree */
+ if ((new->c_cflag & CRTSCTS) && sport->hw_flow_ctrl) {
+ /* enable hardware flow control */
+ pic32_uart_rset(PIC32_UART_MODE_UEN1, sport, PIC32_UART_MODE);
+ pic32_uart_rclr(PIC32_UART_MODE_UEN0, sport, PIC32_UART_MODE);
+ pic32_uart_rclr(PIC32_UART_MODE_RTSMD, sport, PIC32_UART_MODE);
+ } else {
+ /* disable hardware flow control */
+ pic32_uart_rclr(PIC32_UART_MODE_UEN1, sport, PIC32_UART_MODE);
+ pic32_uart_rclr(PIC32_UART_MODE_UEN0, sport, PIC32_UART_MODE);
+ pic32_uart_rclr(PIC32_UART_MODE_RTSMD, sport, PIC32_UART_MODE);
+ }
+
+ /* update baud */
+ baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
+ quot = uart_get_divisor(port, baud) - 1;
+ pic32_uart_write(quot, sport, PIC32_UART_BRG);
+ uart_update_timeout(port, new->c_cflag, baud);
+
+ /* enable uart */
+ pic32_uart_en_and_unmask(port);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/* serial core request to claim uart iomem */
+static int pic32_uart_request_port(struct uart_port *port)
+{
+ struct platform_device *pdev = to_platform_device(port->dev);
+ struct resource *res_mem;
+ unsigned int res_size;
+
+ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(!res_mem))
+ return -EINVAL;
+ res_size = resource_size(res_mem);
+
+ if (!request_mem_region(port->mapbase, res_size, "pic32_uart_mem")) {
+ dev_err(port->dev, "Memory region busy\n");
+ return -EBUSY;
+ }
+
+ port->membase = devm_ioremap_nocache(port->dev,
+ port->mapbase, res_size);
+ if (!port->membase) {
+ dev_err(port->dev, "Unable to map registers\n");
+ release_mem_region(port->mapbase, res_size);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/* serial core request to release uart iomem */
+static void pic32_uart_release_port(struct uart_port *port)
+{
+ struct platform_device *pdev = to_platform_device(port->dev);
+ struct resource *res_mem;
+ unsigned int res_size;
+
+ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(!res_mem))
+ return;
+ res_size = resource_size(res_mem);
+
+ release_mem_region(port->mapbase, res_size);
+ devm_iounmap(port->dev, port->membase);
+}
+
+/* serial core request to do any port required auto-configuration */
+static void pic32_uart_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE) {
+ if (pic32_uart_request_port(port))
+ return;
+ port->type = PORT_PIC32;
+ }
+}
+
+/* serial core request to check that port information in serinfo are suitable */
+static int pic32_uart_verify_port(struct uart_port *port,
+ struct serial_struct *serinfo)
+{
+ if (port->type != PORT_PIC32)
+ return -EINVAL;
+ if (port->irq != serinfo->irq)
+ return -EINVAL;
+ if (port->iotype != serinfo->io_type)
+ return -EINVAL;
+ if (port->mapbase != (unsigned long)serinfo->iomem_base)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* serial core callbacks */
+static const struct uart_ops pic32_uart_ops = {
+ .tx_empty = pic32_uart_tx_empty,
+ .get_mctrl = pic32_uart_get_mctrl,
+ .set_mctrl = pic32_uart_set_mctrl,
+ .start_tx = pic32_uart_start_tx,
+ .stop_tx = pic32_uart_stop_tx,
+ .stop_rx = pic32_uart_stop_rx,
+ .break_ctl = pic32_uart_break_ctl,
+ .startup = pic32_uart_startup,
+ .shutdown = pic32_uart_shutdown,
+ .set_termios = pic32_uart_set_termios,
+ .type = pic32_uart_type,
+ .release_port = pic32_uart_release_port,
+ .request_port = pic32_uart_request_port,
+ .config_port = pic32_uart_config_port,
+ .verify_port = pic32_uart_verify_port,
+};
+
+#ifdef CONFIG_SERIAL_PIC32_CONSOLE
+/* output given char */
+static void pic32_console_putchar(struct uart_port *port, int ch)
+{
+ struct pic32_sport *sport = to_pic32_sport(port);
+
+ if (!(pic32_uart_read(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON))
+ return;
+
+ if (!(pic32_uart_read(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN))
+ return;
+
+ /* wait for tx empty */
+ while (!(pic32_uart_read(sport, PIC32_UART_STA) & PIC32_UART_STA_TRMT))
+ udelay(1);
+
+ pic32_uart_write(ch & 0xff, sport, PIC32_UART_TX);
+}
+
+/* console core request to output given string */
+static void pic32_console_write(struct console *co, const char *s,
+ unsigned int count)
+{
+ struct pic32_sport *sport = pic32_sports[co->index];
+ struct uart_port *port = pic32_get_port(sport);
+
+ /* call uart helper to deal with \r\n */
+ uart_console_write(port, s, count, pic32_console_putchar);
+}
+
+/* console core request to setup given console, find matching uart
+ * port and setup it.
+ */
+static int pic32_console_setup(struct console *co, char *options)
+{
+ struct pic32_sport *sport;
+ struct uart_port *port = NULL;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+ int ret = 0;
+
+ if (unlikely(co->index < 0 || co->index >= PIC32_MAX_UARTS))
+ return -ENODEV;
+
+ sport = pic32_sports[co->index];
+ if (!sport)
+ return -ENODEV;
+ port = pic32_get_port(sport);
+
+ ret = pic32_enable_clock(sport);
+ if (ret)
+ return ret;
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver pic32_uart_driver;
+static struct console pic32_console = {
+ .name = PIC32_SDEV_NAME,
+ .write = pic32_console_write,
+ .device = uart_console_device,
+ .setup = pic32_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &pic32_uart_driver,
+};
+#define PIC32_SCONSOLE (&pic32_console)
+
+static int __init pic32_console_init(void)
+{
+ register_console(&pic32_console);
+ return 0;
+}
+console_initcall(pic32_console_init);
+
+static inline bool is_pic32_console_port(struct uart_port *port)
+{
+ return (port->cons && port->cons->index == port->line);
+}
+
+/*
+ * Late console initialization.
+ */
+static int __init pic32_late_console_init(void)
+{
+ if (!(pic32_console.flags & CON_ENABLED))
+ register_console(&pic32_console);
+
+ return 0;
+}
+
+core_initcall(pic32_late_console_init);
+
+#else
+#define PIC32_SCONSOLE NULL
+#endif
+
+static struct uart_driver pic32_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = PIC32_DEV_NAME,
+ .dev_name = PIC32_SDEV_NAME,
+ .major = PIC32_SDEV_MAJOR,
+ .minor = PIC32_SDEV_MINOR,
+ .nr = PIC32_MAX_UARTS,
+ .cons = PIC32_SCONSOLE,
+};
+
+static int pic32_uart_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct pic32_sport *sport;
+ int uart_idx = 0;
+ struct resource *res_mem;
+ struct uart_port *port;
+ int ret = 0;
+
+ uart_idx = of_alias_get_id(np, "serial");
+ if (uart_idx < 0 || uart_idx >= PIC32_MAX_UARTS)
+ return -EINVAL;
+
+ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res_mem)
+ return -EINVAL;
+
+ sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
+ if (!sport)
+ return -ENOMEM;
+
+ sport->idx = uart_idx;
+ sport->irq_fault = irq_of_parse_and_map(np, 0);
+ sport->irqflags_fault = IRQF_NO_THREAD;
+ sport->irq_rx = irq_of_parse_and_map(np, 1);
+ sport->irqflags_rx = IRQF_NO_THREAD;
+ sport->irq_tx = irq_of_parse_and_map(np, 2);
+ sport->irqflags_tx = IRQF_NO_THREAD;
+ sport->clk = devm_clk_get(&pdev->dev, NULL);
+ sport->cts_gpio = -EINVAL;
+ sport->dev = &pdev->dev;
+
+ ret = pic32_enable_clock(sport);
+ if (ret) {
+ dev_err(&pdev->dev, "clk enable ?\n");
+ goto err;
+ }
+
+ sport->hw_flow_ctrl = of_property_read_bool(np,
+ "microchip,uart-has-rtscts");
+ if (!sport->hw_flow_ctrl)
+ goto uart_no_flow_ctrl;
+
+ /* Hardware flow control: gpios
+ * !Note: Basically, CTS is needed for reading the status.
+ */
+ sport->cts_gpio = of_get_named_gpio(np, "cts-gpios", 0);
+ if (gpio_is_valid(sport->cts_gpio)) {
+ ret = devm_gpio_request(sport->dev,
+ sport->cts_gpio, "CTS");
+ if (ret) {
+ dev_err(&pdev->dev,
+ "error requesting CTS GPIO\n");
+ goto err_disable_clk;
+ }
+
+ ret = gpio_direction_input(sport->cts_gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "error setting CTS GPIO\n");
+ goto err_disable_clk;
+ }
+ }
+
+uart_no_flow_ctrl:
+ 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 = 0;
+ dev_err(port->dev, "%s: uart add port error!\n", __func__);
+ goto err_disable_clk;
+ }
+
+#ifdef CONFIG_SERIAL_PIC32_CONSOLE
+ if (is_pic32_console_port(port) &&
+ (pic32_console.flags & CON_ENABLED)) {
+ /* The peripheral clock has been enabled by console_setup,
+ * so disable it till the port is used.
+ */
+ pic32_disable_clock(sport);
+ }
+#endif
+
+ platform_set_drvdata(pdev, port);
+
+ dev_info(&pdev->dev, "%s: uart(%d) driver initialized.\n",
+ __func__, uart_idx);
+ ret = 0;
+
+err_disable_clk:
+ /* disable clock till the port is used. */
+ pic32_disable_clock(sport);
+err:
+ /* automatic unroll of sport and gpios */
+ return ret;
+}
+
+static int pic32_uart_remove(struct platform_device *pdev)
+{
+ struct uart_port *port = platform_get_drvdata(pdev);
+ struct pic32_sport *sport = to_pic32_sport(port);
+
+ uart_remove_one_port(&pic32_uart_driver, port);
+ pic32_disable_clock(sport);
+ platform_set_drvdata(pdev, NULL);
+ pic32_sports[sport->idx] = NULL;
+
+ /* automatic unroll of sport and gpios */
+ return 0;
+}
+
+static const struct of_device_id pic32_serial_dt_ids[] = {
+ { .compatible = "microchip,pic32-usart" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pic32_serial_dt_ids);
+
+static struct platform_driver pic32_uart_platform_driver = {
+ .probe = pic32_uart_probe,
+ .remove = pic32_uart_remove,
+ .driver = {
+ .name = PIC32_DEV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pic32_serial_dt_ids),
+ },
+};
+
+static int __init pic32_uart_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&pic32_uart_driver);
+ if (ret) {
+ pr_err("failed to register %s:%d\n",
+ pic32_uart_driver.driver_name, ret);
+ return ret;
+ }
+
+ ret = platform_driver_register(&pic32_uart_platform_driver);
+ if (ret) {
+ pr_err("fail to register pic32 uart\n");
+ uart_unregister_driver(&pic32_uart_driver);
+ }
+
+ return ret;
+}
+arch_initcall(pic32_uart_init);
+
+static void __exit pic32_uart_exit(void)
+{
+#ifdef CONFIG_SERIAL_PIC32_CONSOLE
+ unregister_console(&pic32_console);
+#endif
+ platform_driver_unregister(&pic32_uart_platform_driver);
+ uart_unregister_driver(&pic32_uart_driver);
+}
+module_exit(pic32_uart_exit);
+
+MODULE_AUTHOR("Steve Scott <[email protected]>");
+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..157ae7a
--- /dev/null
+++ b/drivers/tty/serial/pic32_uart.h
@@ -0,0 +1,199 @@
+/*
+ * PIC32 Integrated Serial Driver.
+ *
+ * Copyright (C) 2015 Microchip Technology, Inc.
+ *
+ * Authors:
+ * Sorin-Andrei Pistirica <[email protected]>
+ *
+ * Licensed under GPLv2 or later.
+ */
+#ifndef __DT_PIC32_UART_H__
+#define __DT_PIC32_UART_H__
+
+#define PIC32_UART_DFLT_BRATE (9600)
+#define PIC32_UART_TX_FIFO_DEPTH (8)
+#define PIC32_UART_RX_FIFO_DEPTH (8)
+
+struct pic32_console_opt {
+ int baud;
+ int parity;
+ int bits;
+ int flow;
+};
+/* struct pic32_sport - pic32 serial port descriptor
+ * @port: uart port descriptor
+ * @idx: port index
+ * @irq_fault: virtual fault interrupt number
+ * @irqflags_fault: flags related to fault irq
+ * @irq_fault_name: irq fault name
+ * @irq_rx: virtual rx interrupt number
+ * @irqflags_rx: flags related to rx irq
+ * @irq_rx_name: irq rx name
+ * @irq_tx: virtual tx interrupt number
+ * @irqflags_tx: : flags related to tx irq
+ * @irq_tx_name: irq tx name
+ * @cts_gpio: clear to send gpio
+ * @rts_gpio: ready to sent gpio
+ * @dev: device descriptor
+ **/
+struct pic32_sport {
+ struct uart_port port;
+ struct pic32_console_opt opt;
+ int idx;
+
+ int irq_fault;
+ int irqflags_fault;
+ const char *irq_fault_name;
+ int irq_rx;
+ int irqflags_rx;
+ const char *irq_rx_name;
+ int irq_tx;
+ int irqflags_tx;
+ const char *irq_tx_name;
+ u8 enable_tx_irq;
+
+ bool hw_flow_ctrl;
+ int cts_gpio;
+
+ int ref_clk;
+ struct clk *clk;
+
+ struct device *dev;
+};
+#define to_pic32_sport(c) container_of(c, struct pic32_sport, port)
+#define pic32_get_port(sport) (&sport->port)
+#define pic32_get_opt(sport) (&sport->opt)
+#define tx_irq_enabled(sport) (sport->enable_tx_irq)
+
+struct pic32_reg {
+ u32 val;
+ u32 clr;
+ u32 set;
+ u32 inv;
+} __packed;
+#define PIC32_REGS 4
+#define PIC32_REG_SIZE 4
+
+enum pic32_uart_regs {
+ PIC32_UART_UNKNOWN = 0,
+ PIC32_UART_MODE = 1,
+ PIC32_UART_STA = 2,
+ PIC32_UART_TX = 3,
+ PIC32_UART_RX = 4,
+ PIC32_UART_BRG = 5,
+
+ /* add above this line */
+ PIC32_UART_LAST
+};
+
+/* uart register offsets */
+static u32 pic32_uart_lookup_reg[PIC32_UART_LAST] = {
+ [PIC32_UART_MODE] = 0 * PIC32_REGS * PIC32_REG_SIZE,
+ [PIC32_UART_STA] = 1 * PIC32_REGS * PIC32_REG_SIZE,
+ [PIC32_UART_TX] = 2 * PIC32_REGS * PIC32_REG_SIZE,
+ [PIC32_UART_RX] = 3 * PIC32_REGS * PIC32_REG_SIZE,
+ [PIC32_UART_BRG] = 4 * PIC32_REGS * PIC32_REG_SIZE,
+};
+
+static inline void __iomem *pic32_uart_get_reg(struct pic32_sport *sport,
+ enum pic32_uart_regs reg)
+{
+ struct uart_port *port = pic32_get_port(sport);
+
+ return port->membase + pic32_uart_lookup_reg[reg];
+}
+
+static inline u32 pic32_uart_rval(struct pic32_sport *sport,
+ enum pic32_uart_regs reg)
+{
+ void __iomem *addr = pic32_uart_get_reg(sport, reg);
+ struct pic32_reg __iomem *reg_addr = (struct pic32_reg __iomem *)addr;
+
+ return readl(®_addr->val);
+}
+
+static inline void pic32_uart_rset(u32 val,
+ struct pic32_sport *sport,
+ enum pic32_uart_regs reg)
+{
+ void __iomem *addr = pic32_uart_get_reg(sport, reg);
+ struct pic32_reg __iomem *reg_addr = (struct pic32_reg __iomem *)addr;
+
+ writel(val, ®_addr->set);
+}
+
+static inline void pic32_uart_rclr(u32 val,
+ struct pic32_sport *sport,
+ enum pic32_uart_regs reg)
+{
+ void __iomem *addr = pic32_uart_get_reg(sport, reg);
+ struct pic32_reg __iomem *reg_addr = (struct pic32_reg __iomem *)addr;
+
+ writel(val, ®_addr->clr);
+}
+
+static inline void pic32_uart_rinv(u32 val,
+ struct pic32_sport *sport,
+ enum pic32_uart_regs reg)
+{
+ void __iomem *addr = pic32_uart_get_reg(sport, reg);
+ struct pic32_reg __iomem *reg_addr = (struct pic32_reg __iomem *)addr;
+
+ writel(val, ®_addr->inv);
+}
+
+static inline void pic32_uart_write(u32 val,
+ struct pic32_sport *sport,
+ enum pic32_uart_regs reg)
+{
+ void __iomem *addr = pic32_uart_get_reg(sport, reg);
+
+ writel(val, addr);
+}
+
+static inline u32 pic32_uart_read(struct pic32_sport *sport,
+ enum pic32_uart_regs reg)
+{
+ void __iomem *addr = pic32_uart_get_reg(sport, reg);
+
+ return readl(addr);
+}
+
+/* pic32 uart mode register bits */
+#define PIC32_UART_MODE_ON (1 << 15)
+#define PIC32_UART_MODE_FRZ (1 << 14)
+#define PIC32_UART_MODE_SIDL (1 << 13)
+#define PIC32_UART_MODE_IREN (1 << 12)
+#define PIC32_UART_MODE_RTSMD (1 << 11)
+#define PIC32_UART_MODE_RESV1 (1 << 10)
+#define PIC32_UART_MODE_UEN1 (1 << 9)
+#define PIC32_UART_MODE_UEN0 (1 << 8)
+#define PIC32_UART_MODE_WAKE (1 << 7)
+#define PIC32_UART_MODE_LPBK (1 << 6)
+#define PIC32_UART_MODE_ABAUD (1 << 5)
+#define PIC32_UART_MODE_RXINV (1 << 4)
+#define PIC32_UART_MODE_BRGH (1 << 3)
+#define PIC32_UART_MODE_PDSEL1 (1 << 2)
+#define PIC32_UART_MODE_PDSEL0 (1 << 1)
+#define PIC32_UART_MODE_STSEL (1 << 0)
+
+/* pic32 uart status register bits */
+#define PIC32_UART_STA_UTXISEL1 (1 << 15)
+#define PIC32_UART_STA_UTXISEL0 (1 << 14)
+#define PIC32_UART_STA_UTXINV (1 << 13)
+#define PIC32_UART_STA_URXEN (1 << 12)
+#define PIC32_UART_STA_UTXBRK (1 << 11)
+#define PIC32_UART_STA_UTXEN (1 << 10)
+#define PIC32_UART_STA_UTXBF (1 << 9)
+#define PIC32_UART_STA_TRMT (1 << 8)
+#define PIC32_UART_STA_URXISEL1 (1 << 7)
+#define PIC32_UART_STA_URXISEL0 (1 << 6)
+#define PIC32_UART_STA_ADDEN (1 << 5)
+#define PIC32_UART_STA_RIDLE (1 << 4)
+#define PIC32_UART_STA_PERR (1 << 3)
+#define PIC32_UART_STA_FERR (1 << 2)
+#define PIC32_UART_STA_OERR (1 << 1)
+#define PIC32_UART_STA_URXDA (1 << 0)
+
+#endif /* __DT_PIC32_UART_H__ */
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 93ba148..9df0a98 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -261,4 +261,7 @@
/* STM32 USART */
#define PORT_STM32 113
+/* Microchip PIC32 UART */
+#define PORT_PIC32 114
+
#endif /* _UAPILINUX_SERIAL_CORE_H */
--
1.7.9.5
From: Andrei Pistirica <[email protected]>
This driver supports the SDHCI host controller found on the PIC32 in DMA
or PIO mode.
Signed-off-by: Andrei Pistirica <[email protected]>
Signed-off-by: Joshua Henderson <[email protected]>
---
drivers/mmc/host/Kconfig | 11 ++
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/sdhci-pic32.c | 354 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 366 insertions(+)
create mode 100644 drivers/mmc/host/sdhci-pic32.c
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index af71de5..75aaed1 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -784,3 +784,14 @@ config MMC_MTK
If you have a machine with a integrated SD/MMC card reader, say Y or M here.
This is needed if support for any SD/SDIO/MMC devices is required.
If unsure, say N.
+
+config MMC_SDHCI_MICROCHIP_PIC32
+ tristate "Microchip PIC32MZDA SDHCI support"
+ depends on MMC_SDHCI && PIC32MZDA
+ help
+ This selects the Secure Digital Host Controller Interface (SDHCI)
+ for PIC32MZDA platform.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 3595f83..af918d2 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o
+obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o
ifeq ($(CONFIG_CB710_DEBUG),y)
CFLAGS-cb710-mmc += -DDEBUG
diff --git a/drivers/mmc/host/sdhci-pic32.c b/drivers/mmc/host/sdhci-pic32.c
new file mode 100644
index 0000000..7ff23a9
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pic32.c
@@ -0,0 +1,354 @@
+/*
+ * Support of SDHCI platform devices for Microchip PIC32.
+ *
+ * Copyright (C) 2015 Microchip
+ * Andrei Pistirica, Paul Thacker
+ *
+ * Inspired by sdhci-pltfm.c
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/mmc/host.h>
+#include <linux/io.h>
+#include "sdhci.h"
+#include <linux/platform_data/sdhci-pic32.h>
+
+#define PIC32_MMC_OCR (MMC_VDD_32_33 | MMC_VDD_33_34)
+
+#define SDH_SHARED_BUS_CTRL 0x000000E0
+#define SDH_SHARED_BUS_NR_CLK_PINS_MASK 0x7
+#define SDH_SHARED_BUS_NR_IRQ_PINS_MASK 0x30
+#define SDH_SHARED_BUS_CLK_PINS 0x10
+#define SDH_SHARED_BUS_IRQ_PINS 0x14
+#define SDH_CAPS_SDH_SLOT_TYPE_MASK 0xC0000000
+#define SDH_SLOT_TYPE_REMOVABLE 0x0
+#define SDH_SLOT_TYPE_EMBEDDED 0x1
+#define SDH_SLOT_TYPE_SHARED_BUS 0x2
+#define SDHCI_CTRL_CDSSEL 0x80
+#define SDHCI_CTRL_CDTLVL 0x40
+
+#define ADMA_FIFO_RD_THSHLD 512
+#define ADMA_FIFO_WR_THSHLD 512
+
+#define DEV_NAME "pic32-sdhci"
+
+struct pic32_sdhci_pdata {
+ struct platform_device *pdev;
+ struct clk *sys_clk;
+ struct clk *base_clk;
+ bool support_vsel;
+ bool piomode;
+};
+
+unsigned int pic32_sdhci_get_max_clock(struct sdhci_host *host)
+{
+ struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
+ unsigned int clk_rate = clk_get_rate(sdhci_pdata->base_clk);
+ struct platform_device *pdev = sdhci_pdata->pdev;
+
+ dev_dbg(&pdev->dev, "Sdhc max clock rate: %u\n", clk_rate);
+ return clk_rate;
+}
+
+unsigned int pic32_sdhci_get_min_clock(struct sdhci_host *host)
+{
+ struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
+ unsigned int clk_rate = clk_get_rate(sdhci_pdata->base_clk);
+ struct platform_device *pdev = sdhci_pdata->pdev;
+
+ dev_dbg(&pdev->dev, "Sdhc min clock rate: %u\n", clk_rate);
+ return clk_rate;
+}
+
+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;
+ }
+ /*
+ * SDHC will not work if JTAG is not Connected.As a workaround fix,
+ * set Card Detect Signal Selection bit in SDHC Host Control
+ * register and clear Card Detect Test Level bit in SDHC Host
+ * Control register.
+ */
+ ctrl &= ~SDHCI_CTRL_CDTLVL;
+ ctrl |= SDHCI_CTRL_CDSSEL;
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+}
+
+static unsigned int pic32_sdhci_get_ro(struct sdhci_host *host)
+{
+ /*
+ * The SDHCI_WRITE_PROTECT bit is unstable on current hardware so we
+ * can't depend on its value in any way.
+ */
+ return 0;
+}
+
+static const struct sdhci_ops pic32_sdhci_ops = {
+ .get_max_clock = pic32_sdhci_get_max_clock,
+ .get_min_clock = pic32_sdhci_get_min_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,
+};
+
+void pic32_sdhci_shared_bus(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ u32 bus = readl(host->ioaddr + SDH_SHARED_BUS_CTRL);
+ u32 clk_pins = (bus & SDH_SHARED_BUS_NR_CLK_PINS_MASK) >> 0;
+ u32 irq_pins = (bus & SDH_SHARED_BUS_NR_IRQ_PINS_MASK) >> 4;
+
+ /* select first clock */
+ if (clk_pins & 0x1)
+ bus |= (0x1 << SDH_SHARED_BUS_CLK_PINS);
+
+ /* select first interrupt */
+ if (irq_pins & 0x1)
+ bus |= (0x1 << SDH_SHARED_BUS_IRQ_PINS);
+
+ writel(bus, host->ioaddr + SDH_SHARED_BUS_CTRL);
+}
+
+static int pic32_sdhci_probe_platform(struct platform_device *pdev,
+ struct pic32_sdhci_pdata *pdata)
+{
+ int ret = 0;
+ u32 caps_slot_type;
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+
+ /* Check card slot connected on shared bus. */
+ host->caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
+ caps_slot_type = (host->caps & SDH_CAPS_SDH_SLOT_TYPE_MASK) >> 30;
+ if (caps_slot_type == SDH_SLOT_TYPE_SHARED_BUS)
+ pic32_sdhci_shared_bus(pdev);
+
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static inline int
+sdhci_pic32_probe_dts(struct platform_device *pdev,
+ struct pic32_sdhci_pdata *boarddata)
+{
+ struct device_node *np = pdev->dev.of_node;
+
+ if (!np)
+ return -ENODEV;
+
+ if (of_find_property(np, "no-1-8-v", NULL))
+ boarddata->support_vsel = true;
+ else
+ boarddata->support_vsel = false;
+
+ if (of_find_property(np, "piomode", NULL))
+ boarddata->piomode = true;
+ else
+ boarddata->piomode = false;
+
+ return 0;
+}
+#else
+static inline int
+sdhci_pic32_probe_dts(struct platform_device *pdev,
+ struct pic32_sdhci_pdata *boarddata)
+{
+ return -ENODEV;
+}
+#endif
+
+int pic32_sdhci_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sdhci_host *host;
+ struct resource *iomem;
+ struct pic32_sdhci_pdata *sdhci_pdata;
+ struct pic32_sdhci_platform_data *plat_data;
+ unsigned int clk_rate = 0;
+ int ret;
+ struct pinctrl *pinctrl;
+
+ host = sdhci_alloc_host(dev, sizeof(*sdhci_pdata));
+ if (IS_ERR(host)) {
+ ret = PTR_ERR(host);
+ dev_err(&pdev->dev, "cannot allocate memory for sdhci\n");
+ goto err;
+ }
+
+ sdhci_pdata = sdhci_priv(host);
+ sdhci_pdata->pdev = pdev;
+ platform_set_drvdata(pdev, host);
+
+ if (sdhci_pic32_probe_dts(pdev, sdhci_pdata) < 0) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "no device tree information %d\n", ret);
+ goto err_host1;
+ }
+
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
+ if (IS_ERR(host->ioaddr)) {
+ ret = PTR_ERR(host->ioaddr);
+ dev_err(&pdev->dev, "unable to map iomem: %d\n", ret);
+ goto err_host;
+ }
+
+ if (!sdhci_pdata->piomode) {
+ plat_data = pdev->dev.platform_data;
+ if (plat_data && plat_data->setup_dma) {
+ ret = plat_data->setup_dma(ADMA_FIFO_RD_THSHLD,
+ ADMA_FIFO_WR_THSHLD);
+ if (ret)
+ goto err_host;
+ }
+ }
+
+ pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ if (IS_ERR(pinctrl)) {
+ ret = PTR_ERR(pinctrl);
+ dev_warn(&pdev->dev, "No pinctrl provided %d\n", ret);
+ if (ret == -EPROBE_DEFER)
+ goto err_host;
+ }
+
+ host->ops = &pic32_sdhci_ops;
+ host->irq = platform_get_irq(pdev, 0);
+
+ sdhci_pdata->sys_clk = devm_clk_get(&pdev->dev, "sys_clk");
+ if (IS_ERR(sdhci_pdata->sys_clk)) {
+ ret = PTR_ERR(sdhci_pdata->sys_clk);
+ dev_err(&pdev->dev, "Error getting clock\n");
+ goto err_host;
+ }
+
+ /* Enable clock when available! */
+ ret = clk_prepare_enable(sdhci_pdata->sys_clk);
+ if (ret) {
+ dev_dbg(&pdev->dev, "Error enabling clock\n");
+ goto err_host;
+ }
+
+ /* SDH CLK enable */
+ sdhci_pdata->base_clk = devm_clk_get(&pdev->dev, "base_clk");
+ if (IS_ERR(sdhci_pdata->base_clk)) {
+ ret = PTR_ERR(sdhci_pdata->base_clk);
+ dev_err(&pdev->dev, "Error getting clock\n");
+ goto err_host;
+ }
+
+ /* Enable clock when available! */
+ ret = clk_prepare_enable(sdhci_pdata->base_clk);
+ if (ret) {
+ dev_dbg(&pdev->dev, "Error enabling clock\n");
+ goto err_host;
+ }
+
+ clk_rate = clk_get_rate(sdhci_pdata->base_clk);
+ dev_dbg(&pdev->dev, "base clock at: %u\n", clk_rate);
+ clk_rate = clk_get_rate(sdhci_pdata->sys_clk);
+ dev_dbg(&pdev->dev, "sys clock at: %u\n", clk_rate);
+
+ if (sdhci_pdata->support_vsel)
+ host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
+
+ if (sdhci_pdata->piomode)
+ host->quirks |= SDHCI_QUIRK_BROKEN_ADMA |
+ SDHCI_QUIRK_BROKEN_DMA;
+
+ host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
+
+ host->mmc->ocr_avail = PIC32_MMC_OCR;
+
+ ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to probe platform!\n");
+ goto err_host;
+ }
+
+ ret = sdhci_add_host(host);
+ if (ret) {
+ dev_dbg(&pdev->dev, "error adding host\n");
+ goto err_host;
+ }
+
+ dev_info(&pdev->dev, "Successfully added sdhci host\n");
+ return 0;
+
+err_host:
+ devm_iounmap(&pdev->dev, host->ioaddr);
+err_host1:
+ sdhci_free_host(host);
+err:
+ dev_err(&pdev->dev, "pic32-sdhci probe failed: %d\n", ret);
+ return ret;
+}
+
+static int pic32_sdhci_remove(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
+ int dead = 0;
+ u32 scratch;
+
+ scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
+ if (scratch == (u32)-1)
+ dead = 1;
+
+ sdhci_remove_host(host, dead);
+ clk_disable_unprepare(sdhci_pdata->base_clk);
+ clk_disable_unprepare(sdhci_pdata->sys_clk);
+ devm_iounmap(&pdev->dev, host->ioaddr);
+ sdhci_free_host(host);
+
+ return 0;
+}
+
+static const struct of_device_id pic32_sdhci_id_table[] = {
+ { .compatible = "microchip,pic32-sdhci" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, pic32_sdhci_id_table);
+
+static struct platform_driver pic32_sdhci_driver = {
+ .driver = {
+ .name = DEV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pic32_sdhci_id_table),
+ },
+ .probe = pic32_sdhci_probe,
+ .remove = pic32_sdhci_remove,
+};
+
+module_platform_driver(pic32_sdhci_driver);
+
+MODULE_DESCRIPTION("Microchip PIC32 SDHCI driver");
+MODULE_AUTHOR("Pistirica Sorin Andrei & Sandeep Sheriker");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
From: Andrei Pistirica <[email protected]>
Document the devicetree bindings for the SDHC peripheral found on
Microchip PIC32 class devices.
Signed-off-by: Andrei Pistirica <[email protected]>
Signed-off-by: Joshua Henderson <[email protected]>
---
.../devicetree/bindings/mmc/sdhci-pic32.txt | 24 ++++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-pic32.txt
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-pic32.txt b/Documentation/devicetree/bindings/mmc/sdhci-pic32.txt
new file mode 100644
index 0000000..f16388c
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/sdhci-pic32.txt
@@ -0,0 +1,24 @@
+* 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,pic32-sdhci"
+- reg: Should contain registers location and length
+- interrupts: Should contain interrupt
+- pinctrl: Should contain pinctrl for data and command lines
+
+Optional properties:
+- no-1-8-v: 1.8V voltage selection not supported
+- piomode: disable DMA support
+
+Example:
+
+ sdhci@1f8ec000 {
+ compatible = "microchip,pic32-sdhci";
+ reg = <0x1f8ec000 0x100>;
+ interrupts = <SDHC_EVENT DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&REFCLKO4>, <&PBCLK5>;
+ clock-names = "base_clk", "sys_clk";
+ };
--
1.7.9.5
This adds basic DTS configuration for the PIC32MZDA and in turn the
PIC32MZDA Starter Kit.
Signed-off-by: Joshua Henderson <[email protected]>
---
arch/mips/boot/dts/Makefile | 1 +
arch/mips/boot/dts/pic32/Makefile | 12 ++
arch/mips/boot/dts/pic32/pic32mzda-clk.dtsi | 251 ++++++++++++++++++++++++
arch/mips/boot/dts/pic32/pic32mzda.dtsi | 280 +++++++++++++++++++++++++++
arch/mips/boot/dts/pic32/pic32mzda_sk.dts | 150 ++++++++++++++
arch/mips/pic32/Kconfig | 16 ++
6 files changed, 710 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..fa1d2bb
--- /dev/null
+++ b/arch/mips/boot/dts/pic32/pic32mzda-clk.dtsi
@@ -0,0 +1,251 @@
+/*
+ * Device Tree Source for PIC32MZDA clock data
+ *
+ * Purna Chandra Mandal <[email protected]>
+ * Copyright (C) 2015 Microchip Technology Inc. All rights reserved.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+/* all fixed rate clocks */
+
+/ {
+ POSC:posc_clk { /* On-chip primary oscillator */
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <24000000>;
+ };
+
+ FRC:frc_clk { /* internal FRC oscillator */
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <8000000>;
+ };
+
+ BFRC:bfrc_clk { /* internal backup FRC oscillator */
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <8000000>;
+ };
+
+ LPRC:lprc_clk { /* internal low-power FRC oscillator */
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <32000>;
+ };
+
+ /* UPLL provides UTMI clock to USBCORE */
+ UPLL:usb_phy_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <24000000>;
+ clock-output-names = "usbphy_clk";
+ };
+
+ TxCKI:txcki_clk { /* external clock input on TxCLKI pin */
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <4000000>;
+ status = "disabled";
+ };
+
+ /* external clock input on REFCLKIx pin */
+ REFIx:refix_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <24000000>;
+ status = "disabled";
+ };
+
+ /* PIC32 specific clks */
+ pic32_clktree {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x1f801200 0x200>;
+ compatible = "microchip,pic32-clk";
+ interrupts = <12>;
+ ranges;
+
+ /* secondary oscillator; external input on SOSCI pin */
+ SOSC:sosc_clk {
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-sosc";
+ clock-frequency = <32768>;
+ reg = <0x1f801200 0x10 /* enable reg */
+ 0x1f8013d0 0x10>; /* status reg */
+ microchip,bit-mask = <0x02>; /* enable mask */
+ microchip,status-bit-mask = <0x10>; /* status-mask*/
+ };
+
+ FRCDIV:frcdiv_clk {
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-frcdivclk";
+ clocks = <&FRC>;
+ clock-output-names = "frcdiv_clk";
+ };
+
+ /* System PLL clock */
+ SYSPLL:spll_clk {
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-syspll";
+ reg = <0x1f801220 0x10 /* SPLL register */
+ 0x1f8013d0 0x10>; /* CLKSTAT register */
+ clocks = <&POSC>, <&FRC>;
+ clock-output-names = "sys_pll";
+ microchip,status-bit-mask = <0x80>; /* SPLLRDY */
+ };
+
+ /* system clock; mux with postdiv & slew */
+ SYSCLK:sys_clk {
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-sysclk-v2";
+ reg = <0x1f8013c0 0x04>; /* SLEWCON */
+ clocks = <&FRCDIV>, <&SYSPLL>, <&POSC>, <&SOSC>,
+ <&LPRC>, <&FRCDIV>;
+ microchip,clock-indices = <0>, <1>, <2>, <4>, <5>, <7>;
+ clock-output-names = "sys_clk";
+ };
+
+ /* DDR Ctrl & DDR PHY PLL */
+ MPLL: CLK_MPLL {
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-mpll";
+ reg = <0x1f800100 0x04>; /* CFGMPLL */
+ clocks = <&POSC>;
+ clock-output-names = "pic32-mpll";
+ status = "disabled";
+ };
+
+ /* Peripheral bus1 clock */
+ PBCLK1:pb1_clk {
+ reg = <0x1f801340 0x10>;
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-pbclk";
+ clocks = <&SYSCLK>;
+ clock-output-names = "pb1_clk";
+ /* used by system modules, not gateable */
+ microchip,ignore-unused;
+ };
+
+ /* Peripheral bus2 clock */
+ PBCLK2:pb2_clk {
+ reg = <0x1f801350 0x10>;
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-pbclk";
+ clocks = <&SYSCLK>;
+ clock-output-names = "pb2_clk";
+ /* avoid gating even if unused */
+ microchip,ignore-unused;
+ };
+
+ /* Peripheral bus3 clock */
+ PBCLK3:pb3_clk {
+ reg = <0x1f801360 0x10>;
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-pbclk";
+ clocks = <&SYSCLK>;
+ clock-output-names = "pb3_clk";
+ };
+
+ /* Peripheral bus4 clock(I/O ports, GPIO) */
+ PBCLK4:pb4_clk {
+ reg = <0x1f801370 0x10>;
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-pbclk";
+ clocks = <&SYSCLK>;
+ clock-output-names = "pb4_clk";
+ };
+
+ /* Peripheral bus clock */
+ PBCLK5:pb5_clk {
+ reg = <0x1f801380 0x10>;
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-pbclk";
+ clocks = <&SYSCLK>;
+ clock-output-names = "pb5_clk";
+ };
+
+ /* Peripheral Bus6 clock; */
+ PBCLK6:pb6_clk {
+ reg = <0x1f801390 0x10>;
+ compatible = "microchip,pic32-pbclk";
+ clocks = <&SYSCLK>;
+ #clock-cells = <0>;
+ };
+
+ /* Peripheral bus7 clock */
+ PBCLK7:pb7_clk {
+ reg = <0x1f8013A0 0x10>;
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-pbclk";
+ /* CPU is driven by this clock; so named */
+ clock-output-names = "cpu_clk";
+ clocks = <&SYSCLK>;
+ };
+
+ /* Reference Oscillator clock for SPI/I2S */
+ REFCLKO1:refo1_clk {
+ reg = <0x1f801280 0x20>;
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-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";
+ clock-frequency = <10000000>; /* 10Mhz for I2S MCLK */
+ };
+
+ /* Reference Oscillator clock for SQI */
+ REFCLKO2:refo2_clk {
+ reg = <0x1f8012A0 0x20>;
+ #clock-cells = <0>;
+ compatible = "microchip,pic32-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";
+ clock-frequency = <50000000>; /* 50MHz for SQI */
+ };
+
+ /* Reference Oscillator clock, ADC */
+ REFCLKO3:refo3_clk {
+ reg = <0x1f8012C0 0x20>;
+ compatible = "microchip,pic32-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";
+ clock-frequency = <10000000>; /* 1Mhz */
+ };
+
+ /* Reference Oscillator clock */
+ REFCLKO4:refo4_clk {
+ reg = <0x1f8012E0 0x20>;
+ compatible = "microchip,pic32-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";
+ clock-frequency = <25000000>; /* 25Mhz */
+ };
+
+ /* Reference Oscillator clock, LCD */
+ REFCLKO5:refo5_clk {
+ reg = <0x1f801300 0x20>;
+ compatible = "microchip,pic32-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";
+ clock-frequency = <40000000>; /* 40Mhz */
+ };
+ };
+};
diff --git a/arch/mips/boot/dts/pic32/pic32mzda.dtsi b/arch/mips/boot/dts/pic32/pic32mzda.dtsi
new file mode 100644
index 0000000..08d3156
--- /dev/null
+++ b/arch/mips/boot/dts/pic32/pic32mzda.dtsi
@@ -0,0 +1,280 @@
+/*
+ * 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/microchip,pic32mz-evic.h>
+
+#include "pic32mzda-clk.dtsi"
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ interrupt-parent = <&evic>;
+
+ aliases {
+ gpio0 = &pioA;
+ gpio1 = &pioB;
+ gpio2 = &pioC;
+ gpio3 = &pioD;
+ gpio4 = &pioE;
+ gpio5 = &pioF;
+ gpio6 = &pioG;
+ gpio7 = &pioH;
+ gpio8 = &pioJ;
+ gpio9 = &pioK;
+ serial1 = &usart2;
+ serial3 = &usart4;
+ serial5 = &usart6;
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ compatible = "mti,mips14KEc";
+ device_type = "cpu";
+ };
+ };
+
+ evic: interrupt-controller@1f810000 {
+ compatible = "microchip,evic-v2";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = <0x1f810000 0x1000>;
+ device_type="evic-v2";
+ };
+
+ pic32_pinctrl: pinctrl@1f800000{
+ #address-cells = <1>;
+ #size-cells = <1>;
+ #gpio-range-cells = <3>;
+ compatible = "microchip,pic32-pinctrl", "simple-bus";
+ ranges;
+ reg = <0x1f801400 0x100>, /* in */
+ <0x1f801500 0x200>; /* out */
+ clocks = <&PBCLK1>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-ranges = <&pic32_pinctrl 0 0 32
+ &pic32_pinctrl 0 32 32
+ &pic32_pinctrl 0 64 32
+ &pic32_pinctrl 0 96 32
+ &pic32_pinctrl 0 128 32
+ &pic32_pinctrl 0 160 32
+ &pic32_pinctrl 0 192 32
+ &pic32_pinctrl 0 224 32
+ &pic32_pinctrl 0 256 32
+ &pic32_pinctrl 0 288 32>;
+
+ /* GPIO banks */
+ pioA: gpio@1f860000 {
+ compatible = "microchip,pic32-gpio";
+ reg = <0x1f860000 0x24>;
+ interrupts = <PORTA_INPUT_CHANGE_INTERRUPT
+ DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&PBCLK4>;
+ };
+
+ pioB: gpio@1f860100 {
+ compatible = "microchip,pic32-gpio";
+ reg = <0x1f860100 0x24>;
+ interrupts = <PORTB_INPUT_CHANGE_INTERRUPT
+ DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&PBCLK4>;
+ };
+
+ pioC: gpio@1f860200 {
+ compatible = "microchip,pic32-gpio";
+ reg = <0x1f860200 0x24>;
+ interrupts = <PORTC_INPUT_CHANGE_INTERRUPT
+ DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&PBCLK4>;
+ };
+
+ pioD: gpio@1f860300 {
+ compatible = "microchip,pic32-gpio";
+ reg = <0x1f860300 0x24>;
+ interrupts = <PORTD_INPUT_CHANGE_INTERRUPT
+ DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&PBCLK4>;
+ };
+
+ pioE: gpio@1f860400 {
+ compatible = "microchip,pic32-gpio";
+ reg = <0x1f860400 0x24>;
+ interrupts = <PORTE_INPUT_CHANGE_INTERRUPT
+ DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&PBCLK4>;
+ };
+
+ pioF: gpio@1f860500 {
+ compatible = "microchip,pic32-gpio";
+ reg = <0x1f860500 0x24>;
+ interrupts = <PORTF_INPUT_CHANGE_INTERRUPT
+ DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&PBCLK4>;
+ };
+
+ pioG: gpio@1f860600 {
+ compatible = "microchip,pic32-gpio";
+ reg = <0x1f860600 0x24>;
+ interrupts = <PORTG_INPUT_CHANGE_INTERRUPT
+ DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&PBCLK4>;
+ };
+
+ pioH: gpio@1f860700 {
+ compatible = "microchip,pic32-gpio";
+ reg = <0x1f860700 0x24>;
+ interrupts = <PORTH_INPUT_CHANGE_INTERRUPT
+ DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&PBCLK4>;
+ };
+
+ /* There is no pioI */
+
+ pioJ: gpio@1f860800 {
+ compatible = "microchip,pic32-gpio";
+ reg = <0x1f860800 0x24>;
+ interrupts = <PORTJ_INPUT_CHANGE_INTERRUPT
+ DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&PBCLK4>;
+ };
+
+ pioK: gpio@1f860900 {
+ compatible = "microchip,pic32-gpio";
+ reg = <0x1f860900 0x24>;
+ interrupts = <PORTK_INPUT_CHANGE_INTERRUPT
+ DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&PBCLK4>;
+ };
+ };
+
+ sdhci: sdhci@1f8ec000 {
+ compatible = "microchip,pic32-sdhci";
+ reg = <0x1f8ec000 0x100>;
+ interrupts = <SDHC_EVENT DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&REFCLKO4>, <&PBCLK5>;
+ clock-names = "base_clk", "sys_clk";
+ no-1-8-v;
+ status = "disabled";
+ };
+
+ usart1: serial@1f822000 {
+ compatible = "microchip,pic32-usart";
+ reg = <0x1f822000 0x50>;
+ interrupts = <UART1_FAULT DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>,
+ <UART1_RECEIVE_DONE DEFAULT_INT_PRI
+ IRQ_TYPE_LEVEL_HIGH>,
+ <UART1_TRANSFER_DONE DEFAULT_INT_PRI
+ IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&PBCLK2>;
+ status = "disabled";
+ };
+
+ usart2: serial@1f822200 {
+ compatible = "microchip,pic32-usart";
+ reg = <0x1f822200 0x50>;
+ interrupts = <UART2_FAULT DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>,
+ <UART2_RECEIVE_DONE DEFAULT_INT_PRI
+ IRQ_TYPE_LEVEL_HIGH>,
+ <UART2_TRANSFER_DONE DEFAULT_INT_PRI
+ IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&PBCLK2>;
+ status = "disabled";
+ };
+
+ usart3: serial@1f822400 {
+ compatible = "microchip,pic32-usart";
+ reg = <0x1f822400 0x50>;
+ interrupts = <UART3_FAULT DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>,
+ <UART3_RECEIVE_DONE DEFAULT_INT_PRI
+ IRQ_TYPE_LEVEL_HIGH>,
+ <UART3_TRANSFER_DONE DEFAULT_INT_PRI
+ IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&PBCLK2>;
+ status = "disabled";
+ };
+
+ usart4: serial@1f822600 {
+ compatible = "microchip,pic32-usart";
+ reg = <0x1f822600 0x50>;
+ interrupts = <UART4_FAULT DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>,
+ <UART4_RECEIVE_DONE DEFAULT_INT_PRI
+ IRQ_TYPE_LEVEL_HIGH>,
+ <UART4_TRANSFER_DONE DEFAULT_INT_PRI
+ IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&PBCLK2>;
+ status = "disabled";
+ };
+
+ usart5: serial@1f822800 {
+ compatible = "microchip,pic32-usart";
+ reg = <0x1f822800 0x50>;
+ interrupts = <UART5_FAULT DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>,
+ <UART5_RECEIVE_DONE DEFAULT_INT_PRI
+ IRQ_TYPE_LEVEL_HIGH>,
+ <UART5_TRANSFER_DONE DEFAULT_INT_PRI
+ IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&PBCLK2>;
+ status = "disabled";
+ };
+
+ usart6: serial@1f822A00 {
+ compatible = "microchip,pic32-usart";
+ reg = <0x1f822A00 0x50>;
+ interrupts = <UART6_FAULT DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>,
+ <UART6_RECEIVE_DONE DEFAULT_INT_PRI
+ IRQ_TYPE_LEVEL_HIGH>,
+ <UART6_TRANSFER_DONE DEFAULT_INT_PRI
+ 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..5c62228
--- /dev/null
+++ b/arch/mips/boot/dts/pic32/pic32mzda_sk.dts
@@ -0,0 +1,150 @@
+/*
+ * 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/pinctrl/pic32mzda.h>
+
+#include "pic32mzda.dtsi"
+
+/ {
+ compatible = "microchip,pic32mzda-sk", "microchip,pic32mzda";
+ model = "Microchip PIC32MZDA Starter Kit";
+
+ memory {
+ device_type = "memory";
+ reg = <0x08000000 0x08000000>;
+ };
+
+ chosen {
+ bootargs = "earlyprintk=ttyS1,115200n8r console=ttyS1,115200n8";
+ };
+
+ leds0 {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&user_leds_s0>;
+
+ led@1 {
+ label = "pic32mzda_sk:red:led1";
+ gpios = <&pioH 0 GPIO_ACTIVE_HIGH>;
+ linux,default-trigger = "heartbeat";
+ };
+
+ led@2 {
+ label = "pic32mzda_sk:yellow:led2";
+ gpios = <&pioH 1 GPIO_ACTIVE_HIGH>;
+ linux,default-trigger = "mmc0";
+ };
+
+ led@3 {
+ label = "pic32mzda_sk:green:led3";
+ gpios = <&pioH 2 GPIO_ACTIVE_HIGH>;
+ default-state = "on";
+ };
+ };
+
+ keys0 {
+ compatible = "gpio-keys-polled";
+ pinctrl-0 = <&user_buttons_s0>;
+ pinctrl-names = "default";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ poll-interval = <300>;
+
+ button@sw1 {
+ label = "ESC";
+ linux,code = <1>;
+ gpios = <&pioB 12 0>;
+ };
+
+ button@sw2 {
+ label = "Home";
+ linux,code = <102>;
+ gpios = <&pioB 13 0>;
+ };
+
+ button@sw3 {
+ label = "Menu";
+ linux,code = <139>;
+ gpios = <&pioB 14 0>;
+ };
+ };
+};
+
+&usart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart2>;
+ status = "okay";
+};
+
+&usart4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart4>;
+ status = "okay";
+};
+
+&sdhci {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sdhc1>;
+ status = "okay";
+};
+
+&pic32_pinctrl {
+
+ sdhc1 {
+ pinctrl_sdhc1: sdhc1_pins0 {
+ /* SDCLK */
+ pic32,single-pins = <PORT_A 6 PIC32_PIN_CONF_DG>,
+ <PORT_D 4 PIC32_PIN_CONF_DG>, /* SDCMD */
+ <PORT_G 13 PIC32_PIN_CONF_DG>, /* SDDATA0 */
+ <PORT_G 12 PIC32_PIN_CONF_DG>, /* SDDATA1 */
+ <PORT_G 14 PIC32_PIN_CONF_DG>, /* SDDATA2 */
+ <PORT_A 7 PIC32_PIN_CONF_DG>, /* SDDATA3 */
+ <PORT_A 0 PIC32_PIN_CONF_DG>; /* SDCD */
+ };
+ };
+
+ leds {
+
+ user_leds_s0: user_leds_s0 {
+ pic32,single-pins = <PORT_H 0 PIC32_PIN_CONF_DG_OUT
+ PORT_H 1 PIC32_PIN_CONF_DG_OUT
+ PORT_H 2 PIC32_PIN_CONF_DG_OUT>;
+ };
+ };
+
+ buttons0 {
+ user_buttons_s0: user_buttons_s0 {
+ pic32,single-pins = <PORT_B 12 PIC32_PIN_CONF_PU_IN
+ PORT_B 13 PIC32_PIN_CONF_PU_IN
+ PORT_B 14 PIC32_PIN_CONF_PU_IN>;
+ };
+ };
+
+ uart2 {
+ pinctrl_uart2: uart2-0 {
+ pic32,pins = <PIC32_RP_OUT_RPG9 PIC32_PP_OUT_U2TX
+ PIC32_PIN_CONF_DG_OUT
+ PIC32_RP_IN_RPB0 PIC32_PP_IN_U2RX
+ PIC32_PIN_CONF_DG_IN>;
+ };
+ };
+
+ uart4 {
+ pinctrl_uart4: uart4-0 {
+ pic32,pins = <PIC32_RP_OUT_RPC3 PIC32_PP_OUT_U4TX
+ PIC32_PIN_CONF_DG_OUT
+ PIC32_RP_IN_RPE8 PIC32_PP_IN_U4RX
+ PIC32_PIN_CONF_DG_IN>;
+ };
+ };
+};
diff --git a/arch/mips/pic32/Kconfig b/arch/mips/pic32/Kconfig
index 0161f09..b1bd7ba 100644
--- a/arch/mips/pic32/Kconfig
+++ b/arch/mips/pic32/Kconfig
@@ -31,4 +31,20 @@ config PIC32MZDA
endchoice
+choice
+ prompt "Devicetree selection"
+ default DTB_PIC32_NONE
+ help
+ Select the devicetree.
+
+config DTB_PIC32_NONE
+ bool "None"
+
+config DTB_PIC32_MZDA_SK
+ bool "PIC32MZDA Starter Kit"
+ depends on PIC32MZDA
+ select BUILTIN_DTB
+
+endchoice
+
endif # MACH_PIC32
--
1.7.9.5
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 <[email protected]>
---
arch/mips/configs/pic32mzda_defconfig | 88 +++++++++++++++++++++++++++++++++
1 file changed, 88 insertions(+)
create mode 100644 arch/mips/configs/pic32mzda_defconfig
diff --git a/arch/mips/configs/pic32mzda_defconfig b/arch/mips/configs/pic32mzda_defconfig
new file mode 100644
index 0000000..17014ca
--- /dev/null
+++ b/arch/mips/configs/pic32mzda_defconfig
@@ -0,0 +1,88 @@
+CONFIG_MACH_PIC32=y
+CONFIG_DTB_PIC32_MZDA_SK=y
+CONFIG_HZ_100=y
+CONFIG_PREEMPT_VOLUNTARY=y
+# CONFIG_SECCOMP is not set
+CONFIG_SYSVIPC=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_RELAY=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_SLAB=y
+CONFIG_JUMP_LABEL=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_BLK_DEV_BSGLIB=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_SGI_PARTITION=y
+CONFIG_BINFMT_MISC=m
+# CONFIG_SUSPEND is not set
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+# CONFIG_ALLOW_DEV_COREDUMP is not set
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_SCAN_ASYNC=y
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_INPUT_LEDS=m
+CONFIG_INPUT_POLLDEV=y
+CONFIG_INPUT_MOUSEDEV=m
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_EVBUG=m
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO=m
+CONFIG_KEYBOARD_GPIO_POLLED=m
+# CONFIG_MOUSE_PS2 is not set
+# CONFIG_SERIO is not set
+CONFIG_SERIAL_PIC32=y
+CONFIG_SERIAL_PIC32_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_RAW_DRIVER=m
+CONFIG_GPIO_SYSFS=y
+# CONFIG_HWMON is not set
+CONFIG_HIDRAW=y
+# CONFIG_USB_SUPPORT is not set
+CONFIG_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_MICROCHIP_PIC32=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=m
+CONFIG_LEDS_TRIGGER_ONESHOT=m
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_GPIO=m
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+# CONFIG_MIPS_PLATFORM_DEVICES is not set
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_AUTOFS4_FS=m
+CONFIG_FUSE_FS=m
+CONFIG_FSCACHE=m
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_SQUASHFS=m
+CONFIG_SQUASHFS_XATTR=y
+CONFIG_SQUASHFS_LZ4=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
--
1.7.9.5
On Fri, 20 Nov 2015 17:17:18 -0700
Joshua Henderson <[email protected]> wrote:
> 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 <[email protected]>
>
>[...]
>
> diff --git a/arch/mips/include/asm/mach-pic32/gpio.h
> b/arch/mips/include/asm/mach-pic32/gpio.h new file mode 100644
Custom GPIO header are not used anymore, this file can be dropped.
Alban
Hello.
On 11/21/2015 3:17 AM, Joshua Henderson wrote:
> From: Andrei Pistirica <[email protected]>
>
> Document the devicetree bindings for the SDHC peripheral found on
> Microchip PIC32 class devices.
>
> Signed-off-by: Andrei Pistirica <[email protected]>
> Signed-off-by: Joshua Henderson <[email protected]>
> ---
> .../devicetree/bindings/mmc/sdhci-pic32.txt | 24 ++++++++++++++++++++
> 1 file changed, 24 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-pic32.txt
>
> diff --git a/Documentation/devicetree/bindings/mmc/sdhci-pic32.txt b/Documentation/devicetree/bindings/mmc/sdhci-pic32.txt
> new file mode 100644
> index 0000000..f16388c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mmc/sdhci-pic32.txt
> @@ -0,0 +1,24 @@
> +* 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,pic32-sdhci"
> +- reg: Should contain registers location and length
> +- interrupts: Should contain interrupt
> +- pinctrl: Should contain pinctrl for data and command lines
This is a required prop, yet the example doesn't contain it?
> +
> +Optional properties:
> +- no-1-8-v: 1.8V voltage selection not supported
> +- piomode: disable DMA support
> +
> +Example:
> +
> + sdhci@1f8ec000 {
> + compatible = "microchip,pic32-sdhci";
> + reg = <0x1f8ec000 0x100>;
> + interrupts = <SDHC_EVENT DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&REFCLKO4>, <&PBCLK5>;
> + clock-names = "base_clk", "sys_clk";
The "clocks" and "clock-names" props are not documented.
[...]
MBR, Sergei
Hello.
On 11/21/2015 3:17 AM, Joshua Henderson wrote:
> From: Andrei Pistirica <[email protected]>
>
> Document the devicetree bindings for the USART peripheral found on
> Microchip PIC32 class devices.
>
> Signed-off-by: Andrei Pistirica <[email protected]>
> Signed-off-by: Joshua Henderson <[email protected]>
> ---
> .../bindings/serial/microchip,pic32-usart.txt | 29 ++++++++++++++++++++
> 1 file changed, 29 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/serial/microchip,pic32-usart.txt
>
> diff --git a/Documentation/devicetree/bindings/serial/microchip,pic32-usart.txt b/Documentation/devicetree/bindings/serial/microchip,pic32-usart.txt
> new file mode 100644
> index 0000000..c87321c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/serial/microchip,pic32-usart.txt
> @@ -0,0 +1,29 @@
> +* Microchip Universal Synchronous Asynchronous Receiver/Transmitter (USART)
> +
> +Required properties:
> +- compatible: Should be "microchip,pic32-usart"
> +- reg: Should contain registers location and length
> +- interrupts: Should contain interrupt
> +- pinctrl: Should contain pinctrl for TX/RX/RTS/CTS
No such prop in the example.
> +
> +Optional properties:
> +- microchip,uart-has-rtscts : Indicate the uart has hardware flow control
> +- rts-gpios: RTS pin for USP-based UART if microchip,uart-has-rtscts
> +- cts-gpios: CTS pin for USP-based UART if microchip,uart-has-rtscts
> +
> +Example:
> + usart0: serial@1f822000 {
> + compatible = "microchip,pic32-usart";
> + reg = <0x1f822000 0x50>;
> + interrupts = <UART1_FAULT DEFAULT_INT_PRI IRQ_TYPE_NONE>,
> + <UART1_RECEIVE_DONE DEFAULT_INT_PRI IRQ_TYPE_NONE>,
> + <UART1_TRANSFER_DONE DEFAULT_INT_PRI IRQ_TYPE_NONE>;
> + pinctrl-names = "default";
> + pinctrl-0 = <
> + &pinctrl_uart1
> + &pinctrl_uart1_cts
> + &pinctrl_uart1_rts>;
The above 2 props are undocumented.
[...]
MBR, Sergei
On Friday 20 November 2015 17:17:13 Joshua Henderson wrote:
> +Example
> +-------
> +
> +evic: interrupt-controller@1f810000 {
> + compatible = "microchip,evic-v2";
> + interrupt-controller;
> + #interrupt-cells = <3>;
> + reg = <0x1f810000 0x1000>;
> + device_type="evic-v2";
> +};
This is not a correct use of device_type. Just drop that property.
> diff --git a/include/dt-bindings/interrupt-controller/microchip,pic32mz-evic.h b/include/dt-bindings/interrupt-controller/microchip,pic32mz-evic.h
> new file mode 100644
> index 0000000..2c466b8
> --- /dev/null
> +++ b/include/dt-bindings/interrupt-controller/microchip,pic32mz-evic.h
> @@ -0,0 +1,238 @@
> +/*
> + * This header provides constants for the MICROCHIP PIC32 EVIC.
> + */
> +
> +#ifndef _DT_BINDINGS_INTERRUPT_CONTROLLER_MICROCHIP_EVIC_H
> +#define _DT_BINDINGS_INTERRUPT_CONTROLLER_MICROCHIP_EVIC_H
> +
> +#include <dt-bindings/interrupt-controller/irq.h>
> +
> +/* Hardware interrupt number */
> +#define CORE_TIMER_INTERRUPT 0
> +#define CORE_SOFTWARE_INTERRUPT_0 1
> +#define CORE_SOFTWARE_INTERRUPT_1 2
> +#define EXTERNAL_INTERRUPT_0 3
> +#define TIMER1 4
A header file like this is just going to make everyone's life
miserable. Try to remove as much as possible here: normally
you can just use the numbers from the data sheet that match
the actual hardware registers, and put them into the dts file.
> +/* Interrupt priority bits */
> +#define PRI_0 0 /* Note:This priority disables the interrupt! */
> +#define PRI_1 1
> +#define PRI_2 2
> +#define PRI_3 3
> +#define PRI_4 4
> +#define PRI_5 5
> +#define PRI_6 6
> +#define PRI_7 7
> +/* Interrupt subpriority bits */
> +#define SUB_PRI_0 0
> +#define SUB_PRI_1 1
> +#define SUB_PRI_2 2
> +#define SUB_PRI_3 3
These are obviously silly and should be removed/
> +#define PRI_MASK 0x7 /* 3 bit priority mask */
> +#define SUBPRI_MASK 0x3 /* 2 bit subpriority mask */
> +#define INT_MASK 0x1F /* 5 bit pri and subpri mask */
> +#define NR_EXT_IRQS 5 /* 5 external interrupts sources */
> +
> +#define MICROCHIP_EVIC_MIN_PRIORITY 0
> +#define MICROCHIP_EVIC_MAX_PRIORITY INT_MASK
> +
> +#define INT_PRI(pri, subpri) \
> + (((pri & PRI_MASK) << 2) | (subpri & SUBPRI_MASK))
> +
> +#define DEFINE_INT(irq, pri) { irq, pri }
> +
> +#define DEFAULT_INT_PRI INT_PRI(2, 0)
Is it required to have a specific priority configured for each line?
If these are software selectable, it's probably better to not put
them into DT in the first place.
If you absolutely need them, I would suggest using two separate cells
for pri and subpri so you can avoid the macro.
Arnd
On Friday 20 November 2015 17:17:15 Joshua Henderson wrote:
> +/* PIC32 specific clks */
> +pic32_clktree {
> + #address-cells = <1>;
> + #size-cells = <1>;
> + reg = <0x1f801200 0x200>;
> + compatible = "microchip,pic32-clk";
> + interrupts = <12>;
> + ranges;
> +
> + /* secondary oscillator; external input on SOSCI pin */
> + SOSC:sosc_clk {
> + #clock-cells = <0>;
> + compatible = "microchip,pic32-sosc";
> + clock-frequency = <32768>;
> + reg = <0x1f801200 0x10 /* enable reg */
> + 0x1f801390 0x10>; /* status reg */
> + microchip,bit-mask = <0x02>; /* enable mask */
> + microchip,status-bit-mask = <0x10>; /* status-mask*/
> + };
>
If you want to use the reg property in this way for each cell,
at least use a 'ranges' that only translates the actual registers
like this
ranges = <0 0x1f801200 0x200>
sosc_clk {
...
reg = <0x000 0x10>, <0x190 0x10>;
...
};
Arnd
On Fri, 20 Nov 2015 17:17:14 -0700
Joshua Henderson <[email protected]> wrote:
Joshua, Cristian,
> From: Cristian Birsan <[email protected]>
>
> This adds support for the EVIC present on a PIC32MZDA.
>
> The following features are supported:
> - DT properties for EVIC and for devices that use interrupt lines
> - persistent and non-persistent interrupt handling
> - Priority, sub-priority and polariy settings for each interrupt line
> - irqdomain support
>
I haven't reviewed the code yet, but the fact that you allow (and
actually request) the interrupt priorities to be encoded in the DT
raises some concerns:
- Aren't priorities entirely under software control (and hence don't
belong in DT)?
- More crucially, how do you deal with nested interrupts when you have
interrupts running at different priorities? Most parts of Linux
cannot cope with that without additional support.
Thanks,
M.
--
Jazz is not dead. It just smells funny.
On 11/21/2015 04:37 AM, Alban wrote:
> On Fri, 20 Nov 2015 17:17:18 -0700
> Joshua Henderson <[email protected]> wrote:
>
>> 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 <[email protected]>
>>
>> [...]
>>
>> diff --git a/arch/mips/include/asm/mach-pic32/gpio.h
>> b/arch/mips/include/asm/mach-pic32/gpio.h new file mode 100644
>
> Custom GPIO header are not used anymore, this file can be dropped.
>
Ack. Will drop.
Josh
On Fri, Nov 20, 2015 at 05:17:13PM -0700, Joshua Henderson wrote:
> From: Cristian Birsan <[email protected]>
>
> Document the devicetree bindings for the interrupt controller on Microchip
> PIC32 class devices. This also adds a header defining associated interrupts
> and related settings.
>
> Signed-off-by: Cristian Birsan <[email protected]>
> Signed-off-by: Joshua Henderson <[email protected]>
> ---
> .../microchip,pic32mz-evic.txt | 65 ++++++
> .../interrupt-controller/microchip,pic32mz-evic.h | 238 ++++++++++++++++++++
> 2 files changed, 303 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/interrupt-controller/microchip,pic32mz-evic.txt
> create mode 100644 include/dt-bindings/interrupt-controller/microchip,pic32mz-evic.h
>
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/microchip,pic32mz-evic.txt b/Documentation/devicetree/bindings/interrupt-controller/microchip,pic32mz-evic.txt
> new file mode 100644
> index 0000000..12fb91f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/microchip,pic32mz-evic.txt
> @@ -0,0 +1,65 @@
> +Microchip PIC32MZ Interrupt Controller
> +======================================
> +
> +The Microchip PIC32MZ SOC contains an Enhanced Vectored Interrupt Controller
> +(EVIC) version 2. It handles internal and external interrupts and provides
> +support for priority, sub-priority, irq type and polarity.
> +
> +Required properties
> +-------------------
> +
> +- compatible: Should be "microchip,evic-v2"
This should be more specific like "microchip,pic32mz-evic". You can keep
this one in addition if you like for matching.
Rob
On Fri, Nov 20, 2015 at 05:17:15PM -0700, Joshua Henderson wrote:
> From: Purna Chandra Mandal <[email protected]>
>
> Document the devicetree bindings for the clock driver found on Microchip
> PIC32 class devices.
>
> Signed-off-by: Purna Chandra Mandal <[email protected]>
> Signed-off-by: Joshua Henderson <[email protected]>
> ---
> .../devicetree/bindings/clock/microchip,pic32.txt | 263 ++++++++++++++++++++
> 1 file changed, 263 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..4cef72d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/microchip,pic32.txt
> @@ -0,0 +1,263 @@
> +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.
[...]
> +Required properties:
> +- compatible : should have "microchip,pic32-clk".
> +- 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.
There is some discussion about this upstream with "critical-clocks"
binding. Can you use and wait for that?
> +- 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.
We've generally decided not to describe clocks at this level of detail
in DT. It's fine though for simple clock trees. This one seems to be
borderline IMO.
> +- microchip,slew-step: enable frequency slewing(stepping) during rate change;
> + applicable only to sys-clock subnode.
On Fri, Nov 20, 2015 at 05:17:17PM -0700, Joshua Henderson wrote:
> This adds support for the Microchip PIC32 platform along with the
> specific variant PIC32MZDA on a PIC32MZDA Starter Kit.
>
> Signed-off-by: Joshua Henderson <[email protected]>
Acked-by: Rob Herring <[email protected]>
> ---
> .../bindings/mips/pic32/microchip,pic32mzda.txt | 33 ++++++++++++++++++++
> 1 file changed, 33 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt
>
> diff --git a/Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt b/Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt
> new file mode 100644
> index 0000000..bcf3e04
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mips/pic32/microchip,pic32mzda.txt
> @@ -0,0 +1,33 @@
> +* Microchip PIC32MZDA Platforms
> +
> +PIC32MZDA Starter Kit
> +Required root node properties:
> + - compatible = "microchip,pic32mzda-sk", "microchip,pic32mzda"
> +
> +CPU nodes:
> +----------
> +A "cpus" node is required. Required properties:
> + - #address-cells: Must be 1.
> + - #size-cells: Must be 0.
> +A CPU sub-node is also required. Required properties:
> + - device_type: Must be "cpu".
> + - compatible: Must be "mti,mips14KEc".
> +Example:
> + cpus {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + cpu0: cpu@0 {
> + device_type = "cpu";
> + compatible = "mti,mips14KEc";
> + };
> + };
> +
> +Boot protocol
This probably belongs somewhere else if it is common.
> +--------------
> +In accordance with the MIPS UHI specification[1], the bootloader must pass the
> +following arguments to the kernel:
> + - $a0: -2.
> + - $a1: KSEG0 address of the flattened device-tree blob.
> +
> +[1] http://prplfoundation.org/wiki/MIPS_documentation
> --
> 1.7.9.5
>
On Fri, Nov 20, 2015 at 05:17:19PM -0700, Joshua Henderson wrote:
> From: Andrei Pistirica <[email protected]>
>
> Document the devicetree bindings for PINCTRL and GPIO found on Microchip
> PIC32 class devices. This also adds a header defining related port and
> peripheral pin select functionality.
>
> Signed-off-by: Andrei Pistirica <[email protected]>
> Signed-off-by: Joshua Henderson <[email protected]>
> ---
> .../bindings/gpio/microchip,pic32-gpio.txt | 33 ++
> .../bindings/pinctrl/microchip,pic32-pinctrl.txt | 100 +++++
> include/dt-bindings/pinctrl/pic32mzda.h | 404 ++++++++++++++++++++
> 3 files changed, 537 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt
> create mode 100644 Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt
> create mode 100644 include/dt-bindings/pinctrl/pic32mzda.h
>
> 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..f6eeb2f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt
> @@ -0,0 +1,33 @@
> +* Microchip PIC32 GPIO devices (PIO).
> +
> +Required properties:
> + - compatible: "microchip,pic32-gpio"
This should have a chip specific compatible string.
> + - reg: Base address and length for the device.
> + - interrupts: The port interrupt shared be 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 unused.
> + - 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:
> + PIC32_PIN_CN_RISING : low-to-high edge triggered.
> + PIC32_PIN_CN_FALLING : high-to-low edge triggered.
> + PIC32_PIN_CN_BOTH : low-to-high and high-to-low edges triggered.
Can't you use the standard flags?
> +
> +Note:
> + - If gpio-ranges is missing, then all the pins (32) related to the gpio bank
> + are enabled.
> +
> +Example:
> + pioA: gpio@1f860000 {
> + compatible = "microchip,pic32-gpio";
> + reg = <0x1f860000 0x24>;
> + interrupts = <PORTA_INPUT_CHANGE_INTERRUPT
> + DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
> + #gpio-cells = <2>;
> + gpio-controller;
> + interrupt-controller;
> + #interrupt-cells = <2>;
> + gpio-ranges = <&pic32_pinctrl 0 0 32>;
> + clocks = <&PBCLK4>;
> + };
> 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..7cf4167
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt
> @@ -0,0 +1,100 @@
> +* Microchip PIC32 Pinmux device.
> +
> +Please refer to pinctrl-bindings.txt for details of the pinctrl properties and
> +common 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
> +single pins or a pairs of mux and related configuration.
> +
> +Required properties for pic32 device:
> + - compatible: "microchip,pic32-pinctrl", "microchip,pic32mz-pinctrl"
Is this both or either one. For both, it should be most specific first.
> + - reg: Base address and length for pps:in and pps:out registers.
> +
> +Properties for 'pin configuration node':
> + - pic32,pins: each entry consists of 3 intergers and represents the mux and
> + config settings for one pin. The first integer represent the remappable pin,
> + the second represent the peripheral pin and the last the configuration.
> + The format is pic32,pins = <PIC32_RP_'dir'_'pin'
> + PIC32_PP_'dir'_'peripherl-pin' PIC32_PIN_CONF_'config'>. The configurations
> + are divided in 2 classes: IN and OUT and each in 4 buckets. Each entry must
> + contains items from the same class and bucket, otherwise the driver will
> + notify an error and the initialization will fail.
> + - pic32,single-pins: each entry consists of 3 intergers and represents a pin
> + (that is not remappable) and related configuraion. The format is
> + pic32,single-pins = <PORT_'x' 'pin' PIC32_PIN_CONF_'config'>. Each port has
> + 32 pins and please refer to chip documentation for details of remappable
> + pins.
> +
> +Available pin configurations (refer to dt-bindings/pinctrl/pic32.h):
> + PIC32_PIN_CONF_NONE : no configuration (default).
> + PIC32_PIN_CONF_OD : indicate this pin need a open-drain (no direction).
> + PIC32_PIN_CONF_OD_OUT : indicate this pin need a open-drain out.
> + PIC32_PIN_CONF_PU : indicate this pin need a pull up (no direction).
> + PIC32_PIN_CONF_PU_IN : indicate this pin need a pull up in.
> + PIC32_PIN_CONF_PD : indicate this pin need a pull down (no direction).
> + PIC32_PIN_CONF_PD_IN : indicate this pin need a pull down input.
> + PIC32_PIN_CONF_AN : indicate this pin as analogic (no direction).
> + PIC32_PIN_CONF_AN_IN : indicate this pin as analogic input.
> + PIC32_PIN_CONF_DG : indicate this pin as digital (no direction).
> + PIC32_PIN_CONF_DG_IN : indicate this pin as digital input.
> + PIC32_PIN_CONF_DG_OUT : indicate this pin as digital output.
> +
> +NOTEs:
> +1. The pins functions nods are defined under pic32 pinctrl node. The function's
> + pin groups are defined under functions node.
> +2. Each pin group can have both pic32,pins and pic32,single-pins properties to
> + specify re-mappable or non-remappable pins with related mux and configs or
> + at least one.
> +3. Each pin configuration node can have a phandle and devices can set pins
> + configurations by referring to the phandle of that pin configuration node.
> +4. The pinctrl bindings are listed in dt-bindings/pinctrl/pic32.h.
> +5. The gpio controller must be described in the pinctrl simple-bus.
> +
> +Example:
> +pinctrl@1f800000{
> + #address-cells = <1>;
> + #size-cells = <1>;
> + compatible = "microchip,pic32-pinctrl", "simple-bus";
> + ranges;
> + reg = <0x1f801404 0x3c>, /* in */
> + <0x1f801538 0x57>; /* out */
> +
> + pioA: gpio@1f860000 {
> + compatible = "microchip,pic32-gpio";
The gpio controller is a sub-function of the pinctrl? That doesn't
really seem to be the case based on the addresses.
> + reg = <0x1f860000 0x24>;
> + gpio-controller;
> + };
> +
> + /* functions */
> + sw1 {
> + pinctrl_sw1: sw1-0 {
> + pic32,single-pins = <PORT_B 12 PIC32_PIN_CONF_PULLUP>;
Why isn't this using standard pinctrl properties?
> + };
> + };
> +
> + uart1 {
> + pinctrl_uart1: uart1-0 {
> + pic32,pins =
> + <PIC32_RP_OUT_RPG7 PIC32_PP_OUT_U1TX PIC32_PIN_CONF_NONE
> + PIC32_RP_IN_RPG8 PIC32_PP_IN_U1RX PIC32_PIN_CONF_NONE>;
> + };
> + };
> +};
On Fri, Nov 20, 2015 at 05:17:21PM -0700, Joshua Henderson wrote:
> From: Andrei Pistirica <[email protected]>
>
> Document the devicetree bindings for the USART peripheral found on
> Microchip PIC32 class devices.
>
> Signed-off-by: Andrei Pistirica <[email protected]>
> Signed-off-by: Joshua Henderson <[email protected]>
> ---
> .../bindings/serial/microchip,pic32-usart.txt | 29 ++++++++++++++++++++
> 1 file changed, 29 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/serial/microchip,pic32-usart.txt
>
> diff --git a/Documentation/devicetree/bindings/serial/microchip,pic32-usart.txt b/Documentation/devicetree/bindings/serial/microchip,pic32-usart.txt
> new file mode 100644
> index 0000000..c87321c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/serial/microchip,pic32-usart.txt
> @@ -0,0 +1,29 @@
> +* Microchip Universal Synchronous Asynchronous Receiver/Transmitter (USART)
> +
> +Required properties:
> +- compatible: Should be "microchip,pic32-usart"
Again, should be more specific.
> +- reg: Should contain registers location and length
> +- interrupts: Should contain interrupt
> +- pinctrl: Should contain pinctrl for TX/RX/RTS/CTS
> +
> +Optional properties:
> +- microchip,uart-has-rtscts : Indicate the uart has hardware flow control
> +- rts-gpios: RTS pin for USP-based UART if microchip,uart-has-rtscts
> +- cts-gpios: CTS pin for USP-based UART if microchip,uart-has-rtscts
This appears to just be copied for Sirf UART.
Doesn't *-gpios being present imply having h/w
flow-control (i.e. microchip,uart-has-rtscts)?
Rob
> +
> +Example:
> + usart0: serial@1f822000 {
> + compatible = "microchip,pic32-usart";
> + reg = <0x1f822000 0x50>;
> + interrupts = <UART1_FAULT DEFAULT_INT_PRI IRQ_TYPE_NONE>,
> + <UART1_RECEIVE_DONE DEFAULT_INT_PRI IRQ_TYPE_NONE>,
> + <UART1_TRANSFER_DONE DEFAULT_INT_PRI IRQ_TYPE_NONE>;
> + pinctrl-names = "default";
> + pinctrl-0 = <
> + &pinctrl_uart1
> + &pinctrl_uart1_cts
> + &pinctrl_uart1_rts>;
> + microchip,uart-has-rtscts;
> + cts-gpios = <&pioB 15 0>;
> + rts-gpios = <&pioD 1 0>;
> + };
> --
> 1.7.9.5
>
On Fri, Nov 20, 2015 at 05:17:24PM -0700, Joshua Henderson wrote:
> From: Andrei Pistirica <[email protected]>
>
> Document the devicetree bindings for the SDHC peripheral found on
> Microchip PIC32 class devices.
>
> Signed-off-by: Andrei Pistirica <[email protected]>
> Signed-off-by: Joshua Henderson <[email protected]>
> ---
> .../devicetree/bindings/mmc/sdhci-pic32.txt | 24 ++++++++++++++++++++
> 1 file changed, 24 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-pic32.txt
>
> diff --git a/Documentation/devicetree/bindings/mmc/sdhci-pic32.txt b/Documentation/devicetree/bindings/mmc/sdhci-pic32.txt
> new file mode 100644
> index 0000000..f16388c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mmc/sdhci-pic32.txt
> @@ -0,0 +1,24 @@
> +* 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,pic32-sdhci"
> +- reg: Should contain registers location and length
> +- interrupts: Should contain interrupt
> +- pinctrl: Should contain pinctrl for data and command lines
> +
> +Optional properties:
> +- no-1-8-v: 1.8V voltage selection not supported
There's a standard property for this one.
> +- piomode: disable DMA support
Proably this one too IIRC.
> +
> +Example:
> +
> + sdhci@1f8ec000 {
> + compatible = "microchip,pic32-sdhci";
> + reg = <0x1f8ec000 0x100>;
> + interrupts = <SDHC_EVENT DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&REFCLKO4>, <&PBCLK5>;
> + clock-names = "base_clk", "sys_clk";
> + };
> --
> 1.7.9.5
>
On 21 November 2015 at 01:17, Joshua Henderson
<[email protected]> wrote:
> From: Andrei Pistirica <[email protected]>
>
> This driver supports the SDHCI host controller found on the PIC32 in DMA
> or PIO mode.
>
> Signed-off-by: Andrei Pistirica <[email protected]>
> Signed-off-by: Joshua Henderson <[email protected]>
> ---
> drivers/mmc/host/Kconfig | 11 ++
> drivers/mmc/host/Makefile | 1 +
> drivers/mmc/host/sdhci-pic32.c | 354 ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 366 insertions(+)
> create mode 100644 drivers/mmc/host/sdhci-pic32.c
>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index af71de5..75aaed1 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -784,3 +784,14 @@ config MMC_MTK
> If you have a machine with a integrated SD/MMC card reader, say Y or M here.
> This is needed if support for any SD/SDIO/MMC devices is required.
> If unsure, say N.
> +
> +config MMC_SDHCI_MICROCHIP_PIC32
> + tristate "Microchip PIC32MZDA SDHCI support"
> + depends on MMC_SDHCI && PIC32MZDA
> + help
> + This selects the Secure Digital Host Controller Interface (SDHCI)
> + for PIC32MZDA platform.
> +
> + If you have a controller with this interface, say Y or M here.
> +
> + If unsure, say N.
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index 3595f83..af918d2 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -75,6 +75,7 @@ obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
> obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
> obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
> obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o
> +obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o
>
> ifeq ($(CONFIG_CB710_DEBUG),y)
> CFLAGS-cb710-mmc += -DDEBUG
> diff --git a/drivers/mmc/host/sdhci-pic32.c b/drivers/mmc/host/sdhci-pic32.c
> new file mode 100644
> index 0000000..7ff23a9
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-pic32.c
> @@ -0,0 +1,354 @@
> +/*
> + * Support of SDHCI platform devices for Microchip PIC32.
> + *
> + * Copyright (C) 2015 Microchip
> + * Andrei Pistirica, Paul Thacker
> + *
> + * Inspired by sdhci-pltfm.c
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/highmem.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm.h>
> +#include <linux/slab.h>
> +#include <linux/mmc/host.h>
> +#include <linux/io.h>
> +#include "sdhci.h"
> +#include <linux/platform_data/sdhci-pic32.h>
Where does this include come from, any why do you need it?
> +
> +#define PIC32_MMC_OCR (MMC_VDD_32_33 | MMC_VDD_33_34)
Why these values?
Does the SDHCI controller manages the power to the card or do have an
external regulator available?
> +
> +#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
Is all the above registers/bits, specific for sdhci-pic32?
> +
> +#define DEV_NAME "pic32-sdhci"
> +
> +struct pic32_sdhci_pdata {
> + struct platform_device *pdev;
> + struct clk *sys_clk;
> + struct clk *base_clk;
> + bool support_vsel;
> + bool piomode;
> +};
> +
> +unsigned int pic32_sdhci_get_max_clock(struct sdhci_host *host)
> +{
> + struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
> + unsigned int clk_rate = clk_get_rate(sdhci_pdata->base_clk);
> + struct platform_device *pdev = sdhci_pdata->pdev;
> +
> + dev_dbg(&pdev->dev, "Sdhc max clock rate: %u\n", clk_rate);
You may want to remove "Sdhc", I think it isn't needed.
I find the similar at some more places in the driver, please remove all of them.
> + return clk_rate;
> +}
> +
> +unsigned int pic32_sdhci_get_min_clock(struct sdhci_host *host)
> +{
> + struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
> + unsigned int clk_rate = clk_get_rate(sdhci_pdata->base_clk);
> + struct platform_device *pdev = sdhci_pdata->pdev;
> +
> + dev_dbg(&pdev->dev, "Sdhc min clock rate: %u\n", clk_rate);
> + return clk_rate;
> +}
> +
> +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;
> + }
> + /*
> + * SDHC will not work if JTAG is not Connected.As a workaround fix,
> + * set Card Detect Signal Selection bit in SDHC Host Control
> + * register and clear Card Detect Test Level bit in SDHC Host
> + * Control register.
/s/SDHC/SDHCI
If there are more places, please fix them too.
> + */
> + 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,
> + .get_min_clock = pic32_sdhci_get_min_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,
> +};
> +
> +void pic32_sdhci_shared_bus(struct platform_device *pdev)
> +{
> + struct sdhci_host *host = platform_get_drvdata(pdev);
> + u32 bus = readl(host->ioaddr + SDH_SHARED_BUS_CTRL);
> + u32 clk_pins = (bus & SDH_SHARED_BUS_NR_CLK_PINS_MASK) >> 0;
> + u32 irq_pins = (bus & SDH_SHARED_BUS_NR_IRQ_PINS_MASK) >> 4;
> +
> + /* select first clock */
> + if (clk_pins & 0x1)
> + bus |= (0x1 << SDH_SHARED_BUS_CLK_PINS);
> +
> + /* select first interrupt */
> + if (irq_pins & 0x1)
> + bus |= (0x1 << SDH_SHARED_BUS_IRQ_PINS);
> +
> + writel(bus, host->ioaddr + SDH_SHARED_BUS_CTRL);
> +}
> +
> +static int pic32_sdhci_probe_platform(struct platform_device *pdev,
> + struct pic32_sdhci_pdata *pdata)
> +{
> + int ret = 0;
> + u32 caps_slot_type;
> + struct sdhci_host *host = platform_get_drvdata(pdev);
> +
> + /* Check card slot connected on shared bus. */
> + host->caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
> + caps_slot_type = (host->caps & SDH_CAPS_SDH_SLOT_TYPE_MASK) >> 30;
> + if (caps_slot_type == SDH_SLOT_TYPE_SHARED_BUS)
> + pic32_sdhci_shared_bus(pdev);
> +
> + return ret;
> +}
> +
> +#ifdef CONFIG_OF
> +static inline int
Why inline?
> +sdhci_pic32_probe_dts(struct platform_device *pdev,
> + struct pic32_sdhci_pdata *boarddata)
> +{
> + struct device_node *np = pdev->dev.of_node;
> +
> + if (!np)
> + return -ENODEV;
> +
> + if (of_find_property(np, "no-1-8-v", NULL))
Please don't use this property as it's broken. It has two different
purposes and those are conflicting as discussed here[1].
You have two options, either invent a new binding which provides
information about which signal voltage that *is* supported, or use the
current existing common MMC dt bindings to override the SDHCI
capabilities register.
> + boarddata->support_vsel = true;
> + else
> + boarddata->support_vsel = false;
> +
> + if (of_find_property(np, "piomode", NULL))
What's this? Are there different versions of the controller, one
supporting "piomode" another not?
Moreover, where is this being documented?
> + boarddata->piomode = true;
> + else
> + boarddata->piomode = false;
> +
> + return 0;
> +}
> +#else
> +static inline int
> +sdhci_pic32_probe_dts(struct platform_device *pdev,
> + struct pic32_sdhci_pdata *boarddata)
> +{
> + return -ENODEV;
> +}
> +#endif
I don't think you need this for the CONFIG_OF unset case. There should
be stub functions for of_find_property() etc.
> +
> +int pic32_sdhci_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct sdhci_host *host;
> + struct resource *iomem;
> + struct pic32_sdhci_pdata *sdhci_pdata;
> + struct pic32_sdhci_platform_data *plat_data;
> + unsigned int clk_rate = 0;
> + int ret;
> + struct pinctrl *pinctrl;
> +
> + host = sdhci_alloc_host(dev, sizeof(*sdhci_pdata));
> + if (IS_ERR(host)) {
> + ret = PTR_ERR(host);
> + dev_err(&pdev->dev, "cannot allocate memory for sdhci\n");
> + goto err;
> + }
> +
> + sdhci_pdata = sdhci_priv(host);
> + sdhci_pdata->pdev = pdev;
> + platform_set_drvdata(pdev, host);
> +
> + if (sdhci_pic32_probe_dts(pdev, sdhci_pdata) < 0) {
> + ret = -EINVAL;
> + dev_err(&pdev->dev, "no device tree information %d\n", ret);
> + goto err_host1;
> + }
> +
> + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
> + if (IS_ERR(host->ioaddr)) {
> + ret = PTR_ERR(host->ioaddr);
> + dev_err(&pdev->dev, "unable to map iomem: %d\n", ret);
> + goto err_host;
> + }
> +
> + if (!sdhci_pdata->piomode) {
> + plat_data = pdev->dev.platform_data;
> + if (plat_data && plat_data->setup_dma) {
> + ret = plat_data->setup_dma(ADMA_FIFO_RD_THSHLD,
> + ADMA_FIFO_WR_THSHLD);
> + if (ret)
> + goto err_host;
> + }
> + }
> +
> + pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
> + if (IS_ERR(pinctrl)) {
> + ret = PTR_ERR(pinctrl);
> + dev_warn(&pdev->dev, "No pinctrl provided %d\n", ret);
> + if (ret == -EPROBE_DEFER)
> + goto err_host;
> + }
> +
> + host->ops = &pic32_sdhci_ops;
> + host->irq = platform_get_irq(pdev, 0);
> +
> + sdhci_pdata->sys_clk = devm_clk_get(&pdev->dev, "sys_clk");
> + if (IS_ERR(sdhci_pdata->sys_clk)) {
> + ret = PTR_ERR(sdhci_pdata->sys_clk);
> + dev_err(&pdev->dev, "Error getting clock\n");
> + goto err_host;
> + }
> +
> + /* Enable clock when available! */
> + ret = clk_prepare_enable(sdhci_pdata->sys_clk);
> + if (ret) {
> + dev_dbg(&pdev->dev, "Error enabling clock\n");
> + goto err_host;
> + }
> +
> + /* SDH CLK enable */
> + sdhci_pdata->base_clk = devm_clk_get(&pdev->dev, "base_clk");
> + if (IS_ERR(sdhci_pdata->base_clk)) {
> + ret = PTR_ERR(sdhci_pdata->base_clk);
> + dev_err(&pdev->dev, "Error getting clock\n");
> + goto err_host;
> + }
> +
> + /* Enable clock when available! */
> + ret = clk_prepare_enable(sdhci_pdata->base_clk);
> + if (ret) {
> + dev_dbg(&pdev->dev, "Error enabling clock\n");
> + goto err_host;
> + }
> +
> + clk_rate = clk_get_rate(sdhci_pdata->base_clk);
> + dev_dbg(&pdev->dev, "base clock at: %u\n", clk_rate);
> + clk_rate = clk_get_rate(sdhci_pdata->sys_clk);
> + dev_dbg(&pdev->dev, "sys clock at: %u\n", clk_rate);
> +
> + if (sdhci_pdata->support_vsel)
> + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
> +
> + if (sdhci_pdata->piomode)
> + host->quirks |= SDHCI_QUIRK_BROKEN_ADMA |
> + SDHCI_QUIRK_BROKEN_DMA;
> +
> + host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
> +
> + host->mmc->ocr_avail = PIC32_MMC_OCR;
> +
> + ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to probe platform!\n");
> + goto err_host;
> + }
Any reason why you don't want to use mmc_of_parse() to parse for
common mmc DT bindings?
> +
> + ret = sdhci_add_host(host);
> + if (ret) {
> + dev_dbg(&pdev->dev, "error adding host\n");
> + goto err_host;
> + }
> +
> + dev_info(&pdev->dev, "Successfully added sdhci host\n");
> + return 0;
> +
> +err_host:
> + devm_iounmap(&pdev->dev, host->ioaddr);
> +err_host1:
> + sdhci_free_host(host);
> +err:
> + dev_err(&pdev->dev, "pic32-sdhci probe failed: %d\n", ret);
> + return ret;
> +}
> +
> +static int pic32_sdhci_remove(struct platform_device *pdev)
> +{
> + struct sdhci_host *host = platform_get_drvdata(pdev);
> + struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
> + int dead = 0;
> + u32 scratch;
> +
> + scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
> + if (scratch == (u32)-1)
> + dead = 1;
> +
> + sdhci_remove_host(host, dead);
> + clk_disable_unprepare(sdhci_pdata->base_clk);
> + clk_disable_unprepare(sdhci_pdata->sys_clk);
> + devm_iounmap(&pdev->dev, host->ioaddr);
> + sdhci_free_host(host);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id pic32_sdhci_id_table[] = {
> + { .compatible = "microchip,pic32-sdhci" },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, pic32_sdhci_id_table);
> +
> +static struct platform_driver pic32_sdhci_driver = {
> + .driver = {
> + .name = DEV_NAME,
> + .owner = THIS_MODULE,
> + .of_match_table = of_match_ptr(pic32_sdhci_id_table),
> + },
> + .probe = pic32_sdhci_probe,
> + .remove = pic32_sdhci_remove,
> +};
> +
> +module_platform_driver(pic32_sdhci_driver);
> +
> +MODULE_DESCRIPTION("Microchip PIC32 SDHCI driver");
> +MODULE_AUTHOR("Pistirica Sorin Andrei & Sandeep Sheriker");
> +MODULE_LICENSE("GPL v2");
> --
> 1.7.9.5
>
Kind regards
Uffe
[...]
>> +sdhci_pic32_probe_dts(struct platform_device *pdev,
>> + struct pic32_sdhci_pdata *boarddata)
>> +{
>> + struct device_node *np = pdev->dev.of_node;
>> +
>> + if (!np)
>> + return -ENODEV;
>> +
>> + if (of_find_property(np, "no-1-8-v", NULL))
>
> Please don't use this property as it's broken. It has two different
> purposes and those are conflicting as discussed here[1].
Realized that I forgot to attach the reference to the discussion, here it is.
[1]
http://comments.gmane.org/gmane.linux.kernel.mmc/32751
>
> You have two options, either invent a new binding which provides
> information about which signal voltage that *is* supported, or use the
> current existing common MMC dt bindings to override the SDHCI
> capabilities register.
>
[...]
Kind regards
Uffe
On 11/21/2015 1:47 PM, Arnd Bergmann wrote:
> On Friday 20 November 2015 17:17:13 Joshua Henderson wrote:
>
>> +Example
>> +-------
>> +
>> +evic: interrupt-controller@1f810000 {
>> + compatible = "microchip,evic-v2";
>> + interrupt-controller;
>> + #interrupt-cells = <3>;
>> + reg = <0x1f810000 0x1000>;
>> + device_type="evic-v2";
>> +};
>
> This is not a correct use of device_type. Just drop that property.
Ack.
>
>> diff --git a/include/dt-bindings/interrupt-controller/microchip,pic32mz-evic.h b/include/dt-bindings/interrupt-controller/microchip,pic32mz-evic.h
>> new file mode 100644
>> index 0000000..2c466b8
>> --- /dev/null
>> +++ b/include/dt-bindings/interrupt-controller/microchip,pic32mz-evic.h
>> @@ -0,0 +1,238 @@
>> +/*
>> + * This header provides constants for the MICROCHIP PIC32 EVIC.
>> + */
>> +
>> +#ifndef _DT_BINDINGS_INTERRUPT_CONTROLLER_MICROCHIP_EVIC_H
>> +#define _DT_BINDINGS_INTERRUPT_CONTROLLER_MICROCHIP_EVIC_H
>> +
>> +#include <dt-bindings/interrupt-controller/irq.h>
>> +
>> +/* Hardware interrupt number */
>> +#define CORE_TIMER_INTERRUPT 0
>> +#define CORE_SOFTWARE_INTERRUPT_0 1
>> +#define CORE_SOFTWARE_INTERRUPT_1 2
>> +#define EXTERNAL_INTERRUPT_0 3
>> +#define TIMER1 4
>
> A header file like this is just going to make everyone's life
> miserable. Try to remove as much as possible here: normally
> you can just use the numbers from the data sheet that match
> the actual hardware registers, and put them into the dts file.
>
Agreed. Removing these defines along with removing the priorities from the bindings as suggested makes sense. With doing that, this header file becomes pointless and it will be dropped.
>> +/* Interrupt priority bits */
>> +#define PRI_0 0 /* Note:This priority disables the interrupt! */
>> +#define PRI_1 1
>> +#define PRI_2 2
>> +#define PRI_3 3
>> +#define PRI_4 4
>> +#define PRI_5 5
>> +#define PRI_6 6
>> +#define PRI_7 7
>
>> +/* Interrupt subpriority bits */
>> +#define SUB_PRI_0 0
>> +#define SUB_PRI_1 1
>> +#define SUB_PRI_2 2
>> +#define SUB_PRI_3 3
>
> These are obviously silly and should be removed/
>
Ack.
>> +#define PRI_MASK 0x7 /* 3 bit priority mask */
>> +#define SUBPRI_MASK 0x3 /* 2 bit subpriority mask */
>> +#define INT_MASK 0x1F /* 5 bit pri and subpri mask */
>> +#define NR_EXT_IRQS 5 /* 5 external interrupts sources */
>> +
>> +#define MICROCHIP_EVIC_MIN_PRIORITY 0
>> +#define MICROCHIP_EVIC_MAX_PRIORITY INT_MASK
>> +
>> +#define INT_PRI(pri, subpri) \
>> + (((pri & PRI_MASK) << 2) | (subpri & SUBPRI_MASK))
>> +
>> +#define DEFINE_INT(irq, pri) { irq, pri }
>> +
>> +#define DEFAULT_INT_PRI INT_PRI(2, 0)
>
> Is it required to have a specific priority configured for each line?
> If these are software selectable, it's probably better to not put
> them into DT in the first place.
>
> If you absolutely need them, I would suggest using two separate cells
> for pri and subpri so you can avoid the macro.
>
These priorities are hardware priorities that arbitrate pending interrupts to the CPU. These are indeed software configurable and we can agree that DT is probably not the best place to put this configuration in light of this. We'll default to something sane instead. They will be removed from the binding.
Josh
On 11/22/2015 4:45 AM, Marc Zyngier wrote:
> On Fri, 20 Nov 2015 17:17:14 -0700
> Joshua Henderson <[email protected]> wrote:
>
> Joshua, Cristian,
>
>> From: Cristian Birsan <[email protected]>
>>
>> This adds support for the EVIC present on a PIC32MZDA.
>>
>> The following features are supported:
>> - DT properties for EVIC and for devices that use interrupt lines
>> - persistent and non-persistent interrupt handling
>> - Priority, sub-priority and polariy settings for each interrupt line
>> - irqdomain support
>>
>
> I haven't reviewed the code yet, but the fact that you allow (and
> actually request) the interrupt priorities to be encoded in the DT
> raises some concerns:
>
> - Aren't priorities entirely under software control (and hence don't
> belong in DT)?
These are hardware priorities configured by software. They arbitrate pending hardware interrupts to the CPU. We can agree that DT is probably not the best place for this configuration. They will be removed from the binding.
> - More crucially, how do you deal with nested interrupts when you have
> interrupts running at different priorities? Most parts of Linux
> cannot cope with that without additional support.
>
We do not support nested interrupts.
> Thanks,
>
> M.
>
Hi Arnd,
On 11/21/2015 1:49 PM, Arnd Bergmann wrote:
> On Friday 20 November 2015 17:17:15 Joshua Henderson wrote:
>> +/* PIC32 specific clks */
>> +pic32_clktree {
>> + #address-cells = <1>;
>> + #size-cells = <1>;
>> + reg = <0x1f801200 0x200>;
>> + compatible = "microchip,pic32-clk";
>> + interrupts = <12>;
>> + ranges;
>> +
>> + /* secondary oscillator; external input on SOSCI pin */
>> + SOSC:sosc_clk {
>> + #clock-cells = <0>;
>> + compatible = "microchip,pic32-sosc";
>> + clock-frequency = <32768>;
>> + reg = <0x1f801200 0x10 /* enable reg */
>> + 0x1f801390 0x10>; /* status reg */
>> + microchip,bit-mask = <0x02>; /* enable mask */
>> + microchip,status-bit-mask = <0x10>; /* status-mask*/
>> + };
>>
>
> If you want to use the reg property in this way for each cell,
> at least use a 'ranges' that only translates the actual registers
> like this
>
> ranges = <0 0x1f801200 0x200>
>
> sosc_clk {
> ...
> reg = <0x000 0x10>, <0x190 0x10>;
> ...
> };
>
> Arnd
>
This does indeed seem to be the correct way to use ranges in this case. Consider it done.
Thanks for the feedback,
Josh
Hi Sergei,
On 11/21/2015 8:19 AM, Sergei Shtylyov wrote:
> Hello.
>
> On 11/21/2015 3:17 AM, Joshua Henderson wrote:
>
>> From: Andrei Pistirica <[email protected]>
>>
>> Document the devicetree bindings for the SDHC peripheral found on
>> Microchip PIC32 class devices.
>>
>> Signed-off-by: Andrei Pistirica <[email protected]>
>> Signed-off-by: Joshua Henderson <[email protected]>
>> ---
>> .../devicetree/bindings/mmc/sdhci-pic32.txt | 24 ++++++++++++++++++++
>> 1 file changed, 24 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-pic32.txt
>>
>> diff --git a/Documentation/devicetree/bindings/mmc/sdhci-pic32.txt b/Documentation/devicetree/bindings/mmc/sdhci-pic32.txt
>> new file mode 100644
>> index 0000000..f16388c
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mmc/sdhci-pic32.txt
>> @@ -0,0 +1,24 @@
>> +* 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,pic32-sdhci"
>> +- reg: Should contain registers location and length
>> +- interrupts: Should contain interrupt
>> +- pinctrl: Should contain pinctrl for data and command lines
>
> This is a required prop, yet the example doesn't contain it?
>
Ack. Both the required properties and example need to contain pinctrl-names and pinctrl-0, not pinctrl.
>> +
>> +Optional properties:
>> +- no-1-8-v: 1.8V voltage selection not supported
>> +- piomode: disable DMA support
>> +
>> +Example:
>> +
>> + sdhci@1f8ec000 {
>> + compatible = "microchip,pic32-sdhci";
>> + reg = <0x1f8ec000 0x100>;
>> + interrupts = <SDHC_EVENT DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
>> + clocks = <&REFCLKO4>, <&PBCLK5>;
>> + clock-names = "base_clk", "sys_clk";
>
> The "clocks" and "clock-names" props are not documented.
>
> [...]
>
> MBR, Sergei
>
Ack.
Thanks for the feedback,
Josh
Hi Rob,
On 11/22/2015 2:14 PM, Rob Herring wrote:
> On Fri, Nov 20, 2015 at 05:17:13PM -0700, Joshua Henderson wrote:
>> From: Cristian Birsan <[email protected]>
>>
>> Document the devicetree bindings for the interrupt controller on Microchip
>> PIC32 class devices. This also adds a header defining associated interrupts
>> and related settings.
>>
>> Signed-off-by: Cristian Birsan <[email protected]>
>> Signed-off-by: Joshua Henderson <[email protected]>
>> ---
>> .../microchip,pic32mz-evic.txt | 65 ++++++
>> .../interrupt-controller/microchip,pic32mz-evic.h | 238 ++++++++++++++++++++
>> 2 files changed, 303 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/interrupt-controller/microchip,pic32mz-evic.txt
>> create mode 100644 include/dt-bindings/interrupt-controller/microchip,pic32mz-evic.h
>>
>> diff --git a/Documentation/devicetree/bindings/interrupt-controller/microchip,pic32mz-evic.txt b/Documentation/devicetree/bindings/interrupt-controller/microchip,pic32mz-evic.txt
>> new file mode 100644
>> index 0000000..12fb91f
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/interrupt-controller/microchip,pic32mz-evic.txt
>> @@ -0,0 +1,65 @@
>> +Microchip PIC32MZ Interrupt Controller
>> +======================================
>> +
>> +The Microchip PIC32MZ SOC contains an Enhanced Vectored Interrupt Controller
>> +(EVIC) version 2. It handles internal and external interrupts and provides
>> +support for priority, sub-priority, irq type and polarity.
>> +
>> +Required properties
>> +-------------------
>> +
>> +- compatible: Should be "microchip,evic-v2"
>
> This should be more specific like "microchip,pic32mz-evic". You can keep
> this one in addition if you like for matching.
>
> Rob
>
Agreed. Due to feedback, we are settling on microchip,pic32mzda-evic and similar for all compatible properties in this patch series. I don't see a need to keep a more abstract name around here if you don't.
Josh
Hi Rob,
On 11/22/2015 2:57 PM, Rob Herring wrote:
> On Fri, Nov 20, 2015 at 05:17:24PM -0700, Joshua Henderson wrote:
>> From: Andrei Pistirica <[email protected]>
>>
>> Document the devicetree bindings for the SDHC peripheral found on
>> Microchip PIC32 class devices.
>>
>> Signed-off-by: Andrei Pistirica <[email protected]>
>> Signed-off-by: Joshua Henderson <[email protected]>
>> ---
>> .../devicetree/bindings/mmc/sdhci-pic32.txt | 24 ++++++++++++++++++++
>> 1 file changed, 24 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-pic32.txt
>>
>> diff --git a/Documentation/devicetree/bindings/mmc/sdhci-pic32.txt b/Documentation/devicetree/bindings/mmc/sdhci-pic32.txt
>> new file mode 100644
>> index 0000000..f16388c
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mmc/sdhci-pic32.txt
>> @@ -0,0 +1,24 @@
>> +* 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,pic32-sdhci"
>> +- reg: Should contain registers location and length
>> +- interrupts: Should contain interrupt
>> +- pinctrl: Should contain pinctrl for data and command lines
>> +
>> +Optional properties:
>> +- no-1-8-v: 1.8V voltage selection not supported
>
> There's a standard property for this one.
>
Correct. This is indeed a standard property that should not be here. There is currently discussion to avoid using this property anyway.
>> +- piomode: disable DMA support
>
> Proably this one too IIRC.
>
We will be dropping this. There are other ways to accomplish the same thing outside of DT.
Josh
Hi Rob,
On 11/22/2015 2:31 PM, Rob Herring wrote:
> On Fri, Nov 20, 2015 at 05:17:15PM -0700, Joshua Henderson wrote:
>> From: Purna Chandra Mandal <[email protected]>
>>
>> Document the devicetree bindings for the clock driver found on Microchip
>> PIC32 class devices.
>>
>> Signed-off-by: Purna Chandra Mandal <[email protected]>
>> Signed-off-by: Joshua Henderson <[email protected]>
>> ---
>> .../devicetree/bindings/clock/microchip,pic32.txt | 263 ++++++++++++++++++++
>> 1 file changed, 263 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..4cef72d
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/clock/microchip,pic32.txt
>> @@ -0,0 +1,263 @@
>> +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.
>
> [...]
>
>> +Required properties:
>> +- compatible : should have "microchip,pic32-clk".
>> +- 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.
>
> There is some discussion about this upstream with "critical-clocks"
> binding. Can you use and wait for that?
>
The way this is going, we might not have to wait. :) Is there a patch available yet to try it out?
>> +- 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.
>
> We've generally decided not to describe clocks at this level of detail
> in DT. It's fine though for simple clock trees. This one seems to be
> borderline IMO.
>
The binding example is the entire clock tree. These masks are right from the datasheet. For reference, do you have an example of a better alternative?
>> +- microchip,slew-step: enable frequency slewing(stepping) during rate change;
>> + applicable only to sys-clock subnode.
>
Thanks,
Josh
On 11/25/2015 8:55 AM, Ulf Hansson wrote:
> On 21 November 2015 at 01:17, Joshua Henderson
> <[email protected]> wrote:
> > From: Andrei Pistirica <[email protected]>
> >
> > This driver supports the SDHCI host controller found on the PIC32 in
> > DMA or PIO mode.
> >
> > Signed-off-by: Andrei Pistirica <[email protected]>
> > Signed-off-by: Joshua Henderson <[email protected]>
> > ---
> > drivers/mmc/host/Kconfig | 11 ++
> > drivers/mmc/host/Makefile | 1 +
> > drivers/mmc/host/sdhci-pic32.c | 354
> > ++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 366 insertions(+)
> > create mode 100644 drivers/mmc/host/sdhci-pic32.c
> >
> > diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index
> > af71de5..75aaed1 100644
> > --- a/drivers/mmc/host/Kconfig
> > +++ b/drivers/mmc/host/Kconfig
> > @@ -784,3 +784,14 @@ config MMC_MTK
> > If you have a machine with a integrated SD/MMC card reader, say Y or M
> here.
> > This is needed if support for any SD/SDIO/MMC devices is required.
> > If unsure, say N.
> > +
> > +config MMC_SDHCI_MICROCHIP_PIC32
> > + tristate "Microchip PIC32MZDA SDHCI support"
> > + depends on MMC_SDHCI && PIC32MZDA
> > + help
> > + This selects the Secure Digital Host Controller Interface (SDHCI)
> > + for PIC32MZDA platform.
> > +
> > + If you have a controller with this interface, say Y or M here.
> > +
> > + If unsure, say N.
> > diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> > index 3595f83..af918d2 100644
> > --- a/drivers/mmc/host/Makefile
> > +++ b/drivers/mmc/host/Makefile
> > @@ -75,6 +75,7 @@ obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-
> bcm2835.o
> > obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
> > obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
> > obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o
> > +obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o
> >
> > ifeq ($(CONFIG_CB710_DEBUG),y)
> > CFLAGS-cb710-mmc += -DDEBUG
> > diff --git a/drivers/mmc/host/sdhci-pic32.c
> > b/drivers/mmc/host/sdhci-pic32.c new file mode 100644 index
> > 0000000..7ff23a9
> > --- /dev/null
> > +++ b/drivers/mmc/host/sdhci-pic32.c
> > @@ -0,0 +1,354 @@
> > +/*
> > + * Support of SDHCI platform devices for Microchip PIC32.
> > + *
> > + * Copyright (C) 2015 Microchip
> > + * Andrei Pistirica, Paul Thacker
> > + *
> > + * Inspired by sdhci-pltfm.c
> > + *
> > + * This file is licensed under the terms of the GNU General Public
> > + * License version 2. This program is licensed "as is" without any
> > + * warranty of any kind, whether express or implied.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/highmem.h>
> > +#include <linux/module.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/irq.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm.h>
> > +#include <linux/slab.h>
> > +#include <linux/mmc/host.h>
> > +#include <linux/io.h>
> > +#include "sdhci.h"
> > +#include <linux/platform_data/sdhci-pic32.h>
>
> Where does this include come from, any why do you need it?
It's included in patch 6/14 of this patch set, which adds platform support for PIC32. It's needed to provide safe access to a register that's shared by this driver and another driver.
>
> > +
> > +#define PIC32_MMC_OCR (MMC_VDD_32_33 | MMC_VDD_33_34)
>
> Why these values?
These are not needed, and will be removed.
>
> Does the SDHCI controller manages the power to the card or do have an
> external regulator available?
The SDHCI controller manages power to the card.
>
> > +
> > +#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
>
> Is all the above registers/bits, specific for sdhci-pic32?
The shared bus control register is in the v3.0 spec, but I don't see a define for it in the kernel. The FIFO threshold registers are specific to PIC32.
>
> > +
> > +#define DEV_NAME "pic32-sdhci"
> > +
> > +struct pic32_sdhci_pdata {
> > + struct platform_device *pdev;
> > + struct clk *sys_clk;
> > + struct clk *base_clk;
> > + bool support_vsel;
> > + bool piomode;
> > +};
> > +
> > +unsigned int pic32_sdhci_get_max_clock(struct sdhci_host *host) {
> > + struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
> > + unsigned int clk_rate = clk_get_rate(sdhci_pdata->base_clk);
> > + struct platform_device *pdev = sdhci_pdata->pdev;
> > +
> > + dev_dbg(&pdev->dev, "Sdhc max clock rate: %u\n", clk_rate);
>
> You may want to remove "Sdhc", I think it isn't needed.
>
> I find the similar at some more places in the driver, please remove all of them.
Ack.
>
> > + return clk_rate;
> > +}
> > +
> > +unsigned int pic32_sdhci_get_min_clock(struct sdhci_host *host) {
> > + struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
> > + unsigned int clk_rate = clk_get_rate(sdhci_pdata->base_clk);
> > + struct platform_device *pdev = sdhci_pdata->pdev;
> > +
> > + dev_dbg(&pdev->dev, "Sdhc min clock rate: %u\n", clk_rate);
> > + return clk_rate;
> > +}
> > +
> > +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;
> > + }
> > + /*
> > + * SDHC will not work if JTAG is not Connected.As a workaround fix,
> > + * set Card Detect Signal Selection bit in SDHC Host Control
> > + * register and clear Card Detect Test Level bit in SDHC Host
> > + * Control register.
>
> /s/SDHC/SDHCI
>
> If there are more places, please fix them too.
Ack.
>
> > + */
> > + 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,
> > + .get_min_clock = pic32_sdhci_get_min_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,
> > +};
> > +
> > +void pic32_sdhci_shared_bus(struct platform_device *pdev) {
> > + struct sdhci_host *host = platform_get_drvdata(pdev);
> > + u32 bus = readl(host->ioaddr + SDH_SHARED_BUS_CTRL);
> > + u32 clk_pins = (bus & SDH_SHARED_BUS_NR_CLK_PINS_MASK) >> 0;
> > + u32 irq_pins = (bus & SDH_SHARED_BUS_NR_IRQ_PINS_MASK) >> 4;
> > +
> > + /* select first clock */
> > + if (clk_pins & 0x1)
> > + bus |= (0x1 << SDH_SHARED_BUS_CLK_PINS);
> > +
> > + /* select first interrupt */
> > + if (irq_pins & 0x1)
> > + bus |= (0x1 << SDH_SHARED_BUS_IRQ_PINS);
> > +
> > + writel(bus, host->ioaddr + SDH_SHARED_BUS_CTRL); }
> > +
> > +static int pic32_sdhci_probe_platform(struct platform_device *pdev,
> > + struct pic32_sdhci_pdata *pdata)
> > +{
> > + int ret = 0;
> > + u32 caps_slot_type;
> > + struct sdhci_host *host = platform_get_drvdata(pdev);
> > +
> > + /* Check card slot connected on shared bus. */
> > + host->caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
> > + caps_slot_type = (host->caps & SDH_CAPS_SDH_SLOT_TYPE_MASK) >>
> 30;
> > + if (caps_slot_type == SDH_SLOT_TYPE_SHARED_BUS)
> > + pic32_sdhci_shared_bus(pdev);
> > +
> > + return ret;
> > +}
> > +
> > +#ifdef CONFIG_OF
> > +static inline int
>
> Why inline?
This function will be removed in favor of mmc_of_parse().
>
> > +sdhci_pic32_probe_dts(struct platform_device *pdev,
> > + struct pic32_sdhci_pdata *boarddata) {
> > + struct device_node *np = pdev->dev.of_node;
> > +
> > + if (!np)
> > + return -ENODEV;
> > +
> > + if (of_find_property(np, "no-1-8-v", NULL))
>
> Please don't use this property as it's broken. It has two different purposes and
> those are conflicting as discussed here[1].
Ack.
>
> You have two options, either invent a new binding which provides information
> about which signal voltage that *is* supported, or use the current existing
> common MMC dt bindings to override the SDHCI capabilities register.
>
We will use existing common mmc DT bindings to override the SDHCI capabilities register.
> > + boarddata->support_vsel = true;
> > + else
> > + boarddata->support_vsel = false;
> > +
> > + if (of_find_property(np, "piomode", NULL))
>
> What's this? Are there different versions of the controller, one supporting
> "piomode" another not?
>
> Moreover, where is this being documented?
This will be removed. We won't be supporting pio mode.
>
> > + boarddata->piomode = true;
> > + else
> > + boarddata->piomode = false;
> > +
> > + return 0;
> > +}
> > +#else
> > +static inline int
> > +sdhci_pic32_probe_dts(struct platform_device *pdev,
> > + struct pic32_sdhci_pdata *boarddata) {
> > + return -ENODEV;
> > +}
> > +#endif
>
> I don't think you need this for the CONFIG_OF unset case. There should be stub
> functions for of_find_property() etc.
This function will be removed.
>
> > +
> > +int pic32_sdhci_probe(struct platform_device *pdev) {
> > + struct device *dev = &pdev->dev;
> > + struct sdhci_host *host;
> > + struct resource *iomem;
> > + struct pic32_sdhci_pdata *sdhci_pdata;
> > + struct pic32_sdhci_platform_data *plat_data;
> > + unsigned int clk_rate = 0;
> > + int ret;
> > + struct pinctrl *pinctrl;
> > +
> > + host = sdhci_alloc_host(dev, sizeof(*sdhci_pdata));
> > + if (IS_ERR(host)) {
> > + ret = PTR_ERR(host);
> > + dev_err(&pdev->dev, "cannot allocate memory for sdhci\n");
> > + goto err;
> > + }
> > +
> > + sdhci_pdata = sdhci_priv(host);
> > + sdhci_pdata->pdev = pdev;
> > + platform_set_drvdata(pdev, host);
> > +
> > + if (sdhci_pic32_probe_dts(pdev, sdhci_pdata) < 0) {
> > + ret = -EINVAL;
> > + dev_err(&pdev->dev, "no device tree information %d\n", ret);
> > + goto err_host1;
> > + }
> > +
> > + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
> > + if (IS_ERR(host->ioaddr)) {
> > + ret = PTR_ERR(host->ioaddr);
> > + dev_err(&pdev->dev, "unable to map iomem: %d\n", ret);
> > + goto err_host;
> > + }
> > +
> > + if (!sdhci_pdata->piomode) {
> > + plat_data = pdev->dev.platform_data;
> > + if (plat_data && plat_data->setup_dma) {
> > + ret = plat_data->setup_dma(ADMA_FIFO_RD_THSHLD,
> > + ADMA_FIFO_WR_THSHLD);
> > + if (ret)
> > + goto err_host;
> > + }
> > + }
> > +
> > + pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
> > + if (IS_ERR(pinctrl)) {
> > + ret = PTR_ERR(pinctrl);
> > + dev_warn(&pdev->dev, "No pinctrl provided %d\n", ret);
> > + if (ret == -EPROBE_DEFER)
> > + goto err_host;
> > + }
> > +
> > + host->ops = &pic32_sdhci_ops;
> > + host->irq = platform_get_irq(pdev, 0);
> > +
> > + sdhci_pdata->sys_clk = devm_clk_get(&pdev->dev, "sys_clk");
> > + if (IS_ERR(sdhci_pdata->sys_clk)) {
> > + ret = PTR_ERR(sdhci_pdata->sys_clk);
> > + dev_err(&pdev->dev, "Error getting clock\n");
> > + goto err_host;
> > + }
> > +
> > + /* Enable clock when available! */
> > + ret = clk_prepare_enable(sdhci_pdata->sys_clk);
> > + if (ret) {
> > + dev_dbg(&pdev->dev, "Error enabling clock\n");
> > + goto err_host;
> > + }
> > +
> > + /* SDH CLK enable */
> > + sdhci_pdata->base_clk = devm_clk_get(&pdev->dev, "base_clk");
> > + if (IS_ERR(sdhci_pdata->base_clk)) {
> > + ret = PTR_ERR(sdhci_pdata->base_clk);
> > + dev_err(&pdev->dev, "Error getting clock\n");
> > + goto err_host;
> > + }
> > +
> > + /* Enable clock when available! */
> > + ret = clk_prepare_enable(sdhci_pdata->base_clk);
> > + if (ret) {
> > + dev_dbg(&pdev->dev, "Error enabling clock\n");
> > + goto err_host;
> > + }
> > +
> > + clk_rate = clk_get_rate(sdhci_pdata->base_clk);
> > + dev_dbg(&pdev->dev, "base clock at: %u\n", clk_rate);
> > + clk_rate = clk_get_rate(sdhci_pdata->sys_clk);
> > + dev_dbg(&pdev->dev, "sys clock at: %u\n", clk_rate);
> > +
> > + if (sdhci_pdata->support_vsel)
> > + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
> > +
> > + if (sdhci_pdata->piomode)
> > + host->quirks |= SDHCI_QUIRK_BROKEN_ADMA |
> > + SDHCI_QUIRK_BROKEN_DMA;
> > +
> > + host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
> > +
> > + host->mmc->ocr_avail = PIC32_MMC_OCR;
> > +
> > + ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
> > + if (ret) {
> > + dev_err(&pdev->dev, "failed to probe platform!\n");
> > + goto err_host;
> > + }
>
> Any reason why you don't want to use mmc_of_parse() to parse for common
> mmc DT bindings?
We will update the driver to use mmc_of_parse().
>
> > +
> > + ret = sdhci_add_host(host);
> > + if (ret) {
> > + dev_dbg(&pdev->dev, "error adding host\n");
> > + goto err_host;
> > + }
> > +
> > + dev_info(&pdev->dev, "Successfully added sdhci host\n");
> > + return 0;
> > +
> > +err_host:
> > + devm_iounmap(&pdev->dev, host->ioaddr);
> > +err_host1:
> > + sdhci_free_host(host);
> > +err:
> > + dev_err(&pdev->dev, "pic32-sdhci probe failed: %d\n", ret);
> > + return ret;
> > +}
> > +
> > +static int pic32_sdhci_remove(struct platform_device *pdev) {
> > + struct sdhci_host *host = platform_get_drvdata(pdev);
> > + struct pic32_sdhci_pdata *sdhci_pdata = sdhci_priv(host);
> > + int dead = 0;
> > + u32 scratch;
> > +
> > + scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
> > + if (scratch == (u32)-1)
> > + dead = 1;
> > +
> > + sdhci_remove_host(host, dead);
> > + clk_disable_unprepare(sdhci_pdata->base_clk);
> > + clk_disable_unprepare(sdhci_pdata->sys_clk);
> > + devm_iounmap(&pdev->dev, host->ioaddr);
> > + sdhci_free_host(host);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id pic32_sdhci_id_table[] = {
> > + { .compatible = "microchip,pic32-sdhci" },
> > + {}
> > +};
> > +MODULE_DEVICE_TABLE(of, pic32_sdhci_id_table);
> > +
> > +static struct platform_driver pic32_sdhci_driver = {
> > + .driver = {
> > + .name = DEV_NAME,
> > + .owner = THIS_MODULE,
> > + .of_match_table = of_match_ptr(pic32_sdhci_id_table),
> > + },
> > + .probe = pic32_sdhci_probe,
> > + .remove = pic32_sdhci_remove,
> > +};
> > +
> > +module_platform_driver(pic32_sdhci_driver);
> > +
> > +MODULE_DESCRIPTION("Microchip PIC32 SDHCI driver");
> > +MODULE_AUTHOR("Pistirica Sorin Andrei & Sandeep Sheriker");
> > +MODULE_LICENSE("GPL v2");
> > --
> > 1.7.9.5
> >
>
> Kind regards
> Uffe
Thanks,
Paul
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m????????????I?
On Sat, Nov 21, 2015 at 1:17 AM, Joshua Henderson
<[email protected]> wrote:
> From: Andrei Pistirica <[email protected]>
>
> 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: Andrei Pistirica <[email protected]>
> Signed-off-by: Joshua Henderson <[email protected]>
> +config PINCTRL_PIC32
> + bool "Microchip PIC32 pin controller driver"
> + depends on OF
> + depends on MACH_PIC32
> + select PINMUX
> + select GENERIC_PINCONF
> + select GPIOLIB_IRQCHIP
Nice but...
> +struct pic32_gpio_irq {
> + struct irq_chip gpio_irqchip;
> + struct irq_domain *domain;
If you're using GPIOLIB_IRQCHIP with a separate irq_domain
you are totally missing the point of GPIOLIB_IRQCHIP.
Look closer at other drivers using GPIOLIB_IRQCHIP and
get your driver to use the infrastructure properly.
> +#define to_pic32_gpio_chip(c) container_of(c, struct pic32_gpio_chip, chip)
Make this a static inline function instead. #defines are
hard to read.
> +static struct pic32_gpio_chip *gpio_chips[MAX_PIO_BANKS];
Why? You should not need to keep track of the chips
in some local array.
> +int pic32_pinconf_open_drain_runtime(unsigned pin_id, int value)
> +{
> + struct pic32_gpio_chip *pic32_chip = gpio_to_pic32_gpio_chip(pin_id);
> + unsigned pin = pin_id % PINS_PER_BANK;
> +
> + if (IS_ERR_OR_NULL(pic32_chip))
> + return -ENODEV;
> +
> + return pic32_pinconf_open_drain(pic32_chip, pin, value);
> +}
> +EXPORT_SYMBOL(pic32_pinconf_open_drain_runtime);
NO WAY are you going to call this from outside of the driver, even
less other modules.
Use pin control states to control the pins, no cross calling of
functions in the middle of everything thanks.
You shouldn't need one set of calls for "runtime" either, all pin
control states are possible to control at runtime, we have no
distinction between boot time and any other time.
> +static int pic32_gpio_request(struct gpio_chip *chip, unsigned offset)
> +{
> + int gpio = chip->base + offset;
> + int bank = chip->base / chip->ngpio;
> +
> + dev_dbg(chip->dev, "%s: request GPIO-%c:%d(%d)\n", __func__,
> + 'A' + bank, offset, gpio);
> +
> + return pinctrl_request_gpio(gpio);
> +}
> +
> +static void pic32_gpio_free(struct gpio_chip *chip, unsigned offset)
> +{
> + int gpio = chip->base + offset;
> + int bank = chip->base / chip->ngpio;
> +
> + dev_dbg(chip->dev, "%s: free GPIO-%c:%d(%d)\n", __func__,
> + 'A' + bank, offset, gpio);
> +
> + pinctrl_free_gpio(gpio);
> +}
This looks nice.
> +static int pic32_gpio_get(struct gpio_chip *chip, unsigned gpio)
> +{
> + struct pic32_gpio_chip *pic32_chip = to_pic32_gpio_chip(chip);
> + struct pic32_reg __iomem *port_reg = (struct pic32_reg __iomem *)
> + pic32_pio_get_reg(pic32_chip, PIC32_PORT);
> + u32 mask = BIT(gpio);
> +
> + if (WARN_ON(port_reg == NULL || gpio >= chip->ngpio))
> + return -EINVAL;
> +
> + return readl(&port_reg->val) & mask;
return !!(readl(&port_reg->val) & mask);
to clamp it to 0/1.
> +static int pic32_gpio_get_dir(struct gpio_chip *chip, unsigned offset)
> +{
> + struct pic32_gpio_chip *pic32_chip = to_pic32_gpio_chip(chip);
> + struct pic32_reg __iomem *tris_reg = (struct pic32_reg __iomem *)
> + pic32_pio_get_reg(pic32_chip, PIC32_TRIS);
> + u32 mask = BIT(offset);
> +
> + if (WARN_ON(tris_reg == NULL))
> + return -EINVAL;
> +
> + return readl(&tris_reg->val) & mask;
Dito.
> +static int pic32_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
> +{
> + struct pic32_gpio_chip *pic32_chip = to_pic32_gpio_chip(chip);
> + struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
> + int virq;
> +
> + if (offset < chip->ngpio)
> + virq = irq_create_mapping(gpio_irq->domain, offset);
> + else
> + virq = -ENXIO;
> +
> + dev_dbg(chip->dev, "%s: request IRQ for GPIO:%d, return:%d\n",
> + __func__, offset + chip->base, virq);
> +
> + return virq;
> +}
This is handled by GPIOLIB_IRQCHIP and you should not define
.to_irq() for a driver using that.
> +static void pic32_gpio_ranges_setup(struct platform_device *pdev,
> + struct pic32_gpio_chip *pic32_chip)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + struct pinctrl_gpio_range *range;
> + struct of_phandle_args args;
> + int ret;
> +
> + ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args);
> + pic32_chip->gpio_base = (ret == 0) ? args.args[1] + args.args[0] :
> + pic32_chip->pio_idx * PINS_PER_BANK;
> + pic32_chip->ngpio = (ret == 0) ? args.args[2] - args.args[0] :
> + PINS_PER_BANK;
> +
> + range = &pic32_chip->range;
> + range->name = dev_name(&pdev->dev);
> + range->id = pic32_chip->pio_idx;
> + range->pin_base = range->base = pic32_chip->gpio_base;
> +
> + range->npins = pic32_chip->ngpio;
> + range->gc = &pic32_chip->chip;
> +
> + dev_dbg(&pdev->dev, "%s: GPIO-%c ranges: (%d,%d)\n", __func__,
> + 'A' + range->id,
> + pic32_chip->gpio_base, pic32_chip->ngpio);
> +}
What is the point of this? We alread add the ranged in the gpiolib
core.
> +static unsigned int gpio_irq_startup(struct irq_data *d)
This is a too generic name for a function, rename all of these
prefixed with your custom name like pic32_irq_type() etc.
> +{
> + struct pic32_gpio_chip *pic32_chip = irq_data_get_irq_chip_data(d);
> + struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
> + unsigned pin = d->hwirq;
> + int ret;
> +
> + ret = gpiochip_lock_as_irq(&pic32_chip->chip, pin);
> + if (ret) {
> + dev_err(pic32_chip->chip.dev, "unable to lock pind %lu IRQ\n",
> + d->hwirq);
> + return ret;
> + }
This should not be called in .irq_startup() but in .irq_request_resources()
and will not be needed at all if you use GPIOLIB_IRQCHIP properly
since it defines these callbacks for you and handle them in the
gpiolib core.
> +
> + /* start CN */
> + switch (gpio_irq->type[pin]) {
> + case IRQ_TYPE_EDGE_RISING:
> + pic32_gpio_irq_rise_dset(pic32_chip, pin);
> + break;
> + case IRQ_TYPE_EDGE_FALLING:
> + pic32_gpio_irq_fall_dset(pic32_chip, pin);
> + break;
> + case IRQ_TYPE_EDGE_BOTH:
> + pic32_gpio_irq_rise_dset(pic32_chip, pin);
> + pic32_gpio_irq_fall_dset(pic32_chip, pin);
> + break;
> + default:
> + return -EINVAL;
> + }
Why are you doin this in the .startup() callback? This should be done
in .set_type().
> +static int gpio_irq_type(struct irq_data *d, unsigned type)
This is a too generic name for a function, rename all of these
prefixed with your custom name like pic32_irq_type() etc.
> +{
> + struct pic32_gpio_chip *pic32_chip = irq_data_get_irq_chip_data(d);
> + struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
> + unsigned pin = d->hwirq;
> +
> + dev_dbg(pic32_chip->chip.dev, "%s: irq type:%u\n", __func__, type);
> +
> + switch (type) {
> + case IRQ_TYPE_EDGE_RISING:
> + case IRQ_TYPE_EDGE_FALLING:
> + case IRQ_TYPE_EDGE_BOTH:
> + gpio_irq->type[pin] = type;
> + return IRQ_SET_MASK_OK;
> + default:
> + gpio_irq->type[pin] = IRQ_TYPE_NONE;
> + return -EINVAL;
> + }
> +}
Do you rely on semantic call order like this being called before startup?
I do't think that's good, why can't you write to the hardware directly
in this function?
> +/* map virtual irq on hw irq: domain translation */
> +static int pic32_gpio_irq_map(struct irq_domain *d,
> + unsigned int virq,
> + irq_hw_number_t hw)
> +/* decode irq number: base + pin */
> +static int pic32_gpio_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)
> +{
None of these should be needed if you use GPIOLIB_IRQCHIP.
> +static int pic32_gpio_irq_map(struct irq_domain *d,
> + unsigned int virq,
> + irq_hw_number_t hw)
> +{
> + struct pic32_gpio_chip *pic32_chip = d->host_data;
> + struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
> + struct irq_chip *irqchip = &gpio_irq->gpio_irqchip;
> +
> + dev_dbg(pic32_chip->chip.dev, "%s: GPIO-%c:%d map virq:%u\n", __func__,
> + 'A' + pic32_chip->pio_idx, virq, virq);
> +
> + /* set the gpioX chip */
> + irq_set_chip(virq, irqchip);
> + irq_set_chip_data(virq, pic32_chip);
> + irq_set_handler(virq, handle_simple_irq);
This driver should use handle_edge_irq() and implement .irq_ack().
> +static struct irq_domain_ops pic32_gpio_irqd_ops = {
> + .map = pic32_gpio_irq_map,
> + .xlate = pic32_gpio_irq_domain_xlate,
> +};
And no custom domain at all.
> +static int pic32_gpio_of_irq_setup(struct platform_device *pdev,
> + struct pic32_gpio_chip *pic32_chip)
> +{
> + struct device_node *node = pdev->dev.of_node;
> + struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
> + struct irq_chip *irqchip = &gpio_irq->gpio_irqchip;
> + int base_irq;
> +
> + /* set irqchip */
> + irqchip->name = kasprintf(GFP_KERNEL, "GPIO-%c",
> + pic32_chip->pio_idx + 'A');
> + irqchip->irq_startup = gpio_irq_startup;
> + irqchip->irq_shutdown = gpio_irq_shutdown;
> + irqchip->irq_set_type = gpio_irq_type;
Since you are supporting *only* edge IRQs you should
implement and ->irq_ack() callback that ACKs the IRQ.
Most likely the gpio controller is holding a level IRQ to the
next interrupt controller active until you do this.
You should also make sure to use handle_edge_irq() as IRQ
handler as this is what the chip supports.
> + base_irq = platform_get_irq(pdev, 0);
> + if (base_irq < 0)
> + return base_irq;
> +
> + gpio_irq->pio_irq = base_irq;
> +
> + /* Setup irq domain of ngpio lines */
> + gpio_irq->domain = irq_domain_add_linear(
> + node,
> + pic32_chip->chip.ngpio,
> + &pic32_gpio_irqd_ops, pic32_chip);
> + if (!gpio_irq->domain) {
> + dev_err(pic32_chip->chip.dev, "Couldn't allocate IRQ domain\n");
> + return -ENXIO;
> + }
This stuff should not be needed with GPIOLIB_IRQCHIP.
> + dev_dbg(&pdev->dev, "%s: irq GPIO-%c, base_irq:%d, domain:%d\n",
> + __func__, pic32_chip->pio_idx + 'A',
> + base_irq, pic32_chip->chip.ngpio);
> +
> + /* setup chained handler */
> + irq_set_chip_data(gpio_irq->pio_irq, pic32_chip);
> + irq_set_chained_handler(gpio_irq->pio_irq, gpio_irq_handler);
And you should add the GPIO irqchip with
gpiolib_add_irqchip().
> +int pic32_gpio_probe(struct platform_device *pdev,
> + unsigned (*reg_lookup_off)[],
> + unsigned lookup_size)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + int alias_idx = of_alias_get_id(np, "gpio");
> + struct pic32_gpio_chip *pic32_chip = NULL;
> + struct gpio_chip *chip;
> + struct resource *r;
> + int ret = 0;
> +
> + dev_dbg(&pdev->dev, "%s: probing...\n", __func__);
> +
> + if (!np)
> + return -ENODEV;
> +
> + if (WARN_ON(alias_idx >= ARRAY_SIZE(gpio_chips)))
> + return -EINVAL;
> +
> + if (gpio_chips[alias_idx]) {
> + dev_err(&pdev->dev, "Failure %i for GPIO %i\n", ret, alias_idx);
> + return -EBUSY;
> + }
> +
> + /* pic32 gpio chip - private data */
> + pic32_chip = devm_kzalloc(&pdev->dev, sizeof(*pic32_chip),
> + GFP_KERNEL);
> + if (!pic32_chip)
> + return -ENOMEM;
> +
> + /* base address of pio(alias_idx) registers */
> + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!r) {
> + ret = -EINVAL;
> + goto probe_err;
> + }
> + pic32_chip->pio_base = devm_ioremap_nocache(&pdev->dev, r->start,
> + resource_size(r));
> + if (IS_ERR(pic32_chip->pio_base)) {
> + ret = PTR_ERR(pic32_chip->pio_base);
> + goto probe_err;
> + }
> +
> + /* clocks */
> + pic32_chip->clk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(pic32_chip->clk)) {
> + ret = PTR_ERR(pic32_chip->clk);
> + dev_err(&pdev->dev, "clk get failed\n");
> + goto probe_err;
> + }
> +
> + ret = clk_prepare_enable(pic32_chip->clk);
> + if (ret) {
> + dev_err(&pdev->dev, "clk enable failed\n");
> + goto probe_err;
> + }
> +
> + pic32_chip->reg_lookup_off = *reg_lookup_off;
> + pic32_chip->lookup_size = lookup_size;
> + pic32_chip->pio_idx = alias_idx;
> + pic32_chip->chip = gpio_template;
> + pic32_gpio_ranges_setup(pdev, pic32_chip);/* pin_ranges: unsupported */
I can't see why you have this. The gpiolib core handles this.
Please fix the above and repost, and I can look at the pin control
parts. As it is now there are too many problems with the GPIO
parts.
Yours,
Linus Walleij
On Wed, Nov 25, 2015 at 10:36:55PM -0700, Joshua Henderson wrote:
> Hi Rob,
>
> On 11/22/2015 2:31 PM, Rob Herring wrote:
> > On Fri, Nov 20, 2015 at 05:17:15PM -0700, Joshua Henderson wrote:
> >> From: Purna Chandra Mandal <[email protected]>
> >>
> >> Document the devicetree bindings for the clock driver found on Microchip
> >> PIC32 class devices.
> >>
> >> Signed-off-by: Purna Chandra Mandal <[email protected]>
> >> Signed-off-by: Joshua Henderson <[email protected]>
> >> ---
> >> .../devicetree/bindings/clock/microchip,pic32.txt | 263 ++++++++++++++++++++
> >> 1 file changed, 263 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..4cef72d
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/clock/microchip,pic32.txt
> >> @@ -0,0 +1,263 @@
> >> +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.
> >
> > [...]
> >
> >> +Required properties:
> >> +- compatible : should have "microchip,pic32-clk".
BTW, this should list out the actual compatible strings.
> > There is some discussion about this upstream with "critical-clocks"
> > binding. Can you use and wait for that?
> >
>
> The way this is going, we might not have to wait. :) Is there a patch available yet to try it out?
Yes, googling "Lee Jones critical-clocks" should find it.
> >> +- 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.
> >
> > We've generally decided not to describe clocks at this level of detail
> > in DT. It's fine though for simple clock trees. This one seems to be
> > borderline IMO.
> >
>
> The binding example is the entire clock tree. These masks are right from the datasheet. For reference, do you have an example of a better alternative?
Okay, like I said, borderline. If this is complete, then it is fine.
Adding more clocks or a newer version of the SoC with more clocks would
change that opinion.
Rob
Rob,
On 11/30/2015 02:43 PM, Rob Herring wrote:
> On Wed, Nov 25, 2015 at 10:36:55PM -0700, Joshua Henderson wrote:
>> Hi Rob,
>>
>> On 11/22/2015 2:31 PM, Rob Herring wrote:
>>> On Fri, Nov 20, 2015 at 05:17:15PM -0700, Joshua Henderson wrote:
>>>> From: Purna Chandra Mandal <[email protected]>
>>>>
>>>> Document the devicetree bindings for the clock driver found on Microchip
>>>> PIC32 class devices.
>>>>
>>>> Signed-off-by: Purna Chandra Mandal <[email protected]>
>>>> Signed-off-by: Joshua Henderson <[email protected]>
>>>> ---
>>>> .../devicetree/bindings/clock/microchip,pic32.txt | 263 ++++++++++++++++++++
>>>> 1 file changed, 263 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..4cef72d
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/clock/microchip,pic32.txt
>>>> @@ -0,0 +1,263 @@
>>>> +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.
>>>
>>> [...]
>>>
>>>> +Required properties:
>>>> +- compatible : should have "microchip,pic32-clk".
>
> BTW, this should list out the actual compatible strings.
Ack. These are also being changed to microchip,pic32mzda-* due to other feedback.
>
>>> There is some discussion about this upstream with "critical-clocks"
>>> binding. Can you use and wait for that?
>>>
>>
>> The way this is going, we might not have to wait. :) Is there a patch available yet to try it out?
>
> Yes, googling "Lee Jones critical-clocks" should find it.
The change for this on our side is in the queue should the stars align on timing.
[...]
>
> Rob
>
Thanks,
Josh
Hi Rob,
Thank you for feedback. All the comments will be address in the next driver version.
We are currently working for a solution to use the standard bindings for pinctrl.
Regards,
Andrei
-----Original Message-----
From: Rob Herring [mailto:[email protected]]
Sent: Sunday, November 22, 2015 11:47 PM
To: Joshua Henderson - C16205
Cc: [email protected]; [email protected]; Andrei Pistirica - M16132; Pawel Moll; Mark Rutland; Ian Campbell; Kumar Gala; [email protected]
Subject: Re: [PATCH 07/14] DEVICETREE: Add bindings for PIC32 pin control and GPIO
On Fri, Nov 20, 2015 at 05:17:19PM -0700, Joshua Henderson wrote:
> From: Andrei Pistirica <[email protected]>
>
> Document the devicetree bindings for PINCTRL and GPIO found on
> Microchip
> PIC32 class devices. This also adds a header defining related port and
> peripheral pin select functionality.
>
> Signed-off-by: Andrei Pistirica <[email protected]>
> Signed-off-by: Joshua Henderson <[email protected]>
> ---
> .../bindings/gpio/microchip,pic32-gpio.txt | 33 ++
> .../bindings/pinctrl/microchip,pic32-pinctrl.txt | 100 +++++
> include/dt-bindings/pinctrl/pic32mzda.h | 404 ++++++++++++++++++++
> 3 files changed, 537 insertions(+)
> create mode 100644
> Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt
> create mode 100644
> Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt
> create mode 100644 include/dt-bindings/pinctrl/pic32mzda.h
>
> 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..f6eeb2f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt
> @@ -0,0 +1,33 @@
> +* Microchip PIC32 GPIO devices (PIO).
> +
> +Required properties:
> + - compatible: "microchip,pic32-gpio"
This should have a chip specific compatible string.
> + - reg: Base address and length for the device.
> + - interrupts: The port interrupt shared be 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 unused.
> + - 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:
> + PIC32_PIN_CN_RISING : low-to-high edge triggered.
> + PIC32_PIN_CN_FALLING : high-to-low edge triggered.
> + PIC32_PIN_CN_BOTH : low-to-high and high-to-low edges triggered.
Can't you use the standard flags?
> +
> +Note:
> + - If gpio-ranges is missing, then all the pins (32) related to the gpio bank
> + are enabled.
> +
> +Example:
> + pioA: gpio@1f860000 {
> + compatible = "microchip,pic32-gpio";
> + reg = <0x1f860000 0x24>;
> + interrupts = <PORTA_INPUT_CHANGE_INTERRUPT
> + DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
> + #gpio-cells = <2>;
> + gpio-controller;
> + interrupt-controller;
> + #interrupt-cells = <2>;
> + gpio-ranges = <&pic32_pinctrl 0 0 32>;
> + clocks = <&PBCLK4>;
> + };
> diff --git
> a/Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.tx
> t
> b/Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.tx
> t
> new file mode 100644
> index 0000000..7cf4167
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctr
> +++ l.txt
> @@ -0,0 +1,100 @@
> +* Microchip PIC32 Pinmux device.
> +
> +Please refer to pinctrl-bindings.txt for details of the pinctrl
> +properties and common 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 single pins or a pairs of mux and related configuration.
> +
> +Required properties for pic32 device:
> + - compatible: "microchip,pic32-pinctrl", "microchip,pic32mz-pinctrl"
Is this both or either one. For both, it should be most specific first.
> + - reg: Base address and length for pps:in and pps:out registers.
> +
> +Properties for 'pin configuration node':
> + - pic32,pins: each entry consists of 3 intergers and represents the mux and
> + config settings for one pin. The first integer represent the remappable pin,
> + the second represent the peripheral pin and the last the configuration.
> + The format is pic32,pins = <PIC32_RP_'dir'_'pin'
> + PIC32_PP_'dir'_'peripherl-pin' PIC32_PIN_CONF_'config'>. The configurations
> + are divided in 2 classes: IN and OUT and each in 4 buckets. Each entry must
> + contains items from the same class and bucket, otherwise the driver will
> + notify an error and the initialization will fail.
> + - pic32,single-pins: each entry consists of 3 intergers and represents a pin
> + (that is not remappable) and related configuraion. The format is
> + pic32,single-pins = <PORT_'x' 'pin' PIC32_PIN_CONF_'config'>. Each port has
> + 32 pins and please refer to chip documentation for details of remappable
> + pins.
> +
> +Available pin configurations (refer to dt-bindings/pinctrl/pic32.h):
> + PIC32_PIN_CONF_NONE : no configuration (default).
> + PIC32_PIN_CONF_OD : indicate this pin need a open-drain (no direction).
> + PIC32_PIN_CONF_OD_OUT : indicate this pin need a open-drain out.
> + PIC32_PIN_CONF_PU : indicate this pin need a pull up (no direction).
> + PIC32_PIN_CONF_PU_IN : indicate this pin need a pull up in.
> + PIC32_PIN_CONF_PD : indicate this pin need a pull down (no direction).
> + PIC32_PIN_CONF_PD_IN : indicate this pin need a pull down input.
> + PIC32_PIN_CONF_AN : indicate this pin as analogic (no direction).
> + PIC32_PIN_CONF_AN_IN : indicate this pin as analogic input.
> + PIC32_PIN_CONF_DG : indicate this pin as digital (no direction).
> + PIC32_PIN_CONF_DG_IN : indicate this pin as digital input.
> + PIC32_PIN_CONF_DG_OUT : indicate this pin as digital output.
> +
> +NOTEs:
> +1. The pins functions nods are defined under pic32 pinctrl node. The function's
> + pin groups are defined under functions node.
> +2. Each pin group can have both pic32,pins and pic32,single-pins properties to
> + specify re-mappable or non-remappable pins with related mux and configs or
> + at least one.
> +3. Each pin configuration node can have a phandle and devices can set pins
> + configurations by referring to the phandle of that pin configuration node.
> +4. The pinctrl bindings are listed in dt-bindings/pinctrl/pic32.h.
> +5. The gpio controller must be described in the pinctrl simple-bus.
> +
> +Example:
> +pinctrl@1f800000{
> + #address-cells = <1>;
> + #size-cells = <1>;
> + compatible = "microchip,pic32-pinctrl", "simple-bus";
> + ranges;
> + reg = <0x1f801404 0x3c>, /* in */
> + <0x1f801538 0x57>; /* out */
> +
> + pioA: gpio@1f860000 {
> + compatible = "microchip,pic32-gpio";
The gpio controller is a sub-function of the pinctrl? That doesn't really seem to be the case based on the addresses.
> + reg = <0x1f860000 0x24>;
> + gpio-controller;
> + };
> +
> + /* functions */
> + sw1 {
> + pinctrl_sw1: sw1-0 {
> + pic32,single-pins = <PORT_B 12 PIC32_PIN_CONF_PULLUP>;
Why isn't this using standard pinctrl properties?
> + };
> + };
> +
> + uart1 {
> + pinctrl_uart1: uart1-0 {
> + pic32,pins =
> + <PIC32_RP_OUT_RPG7 PIC32_PP_OUT_U1TX PIC32_PIN_CONF_NONE
> + PIC32_RP_IN_RPG8 PIC32_PP_IN_U1RX PIC32_PIN_CONF_NONE>;
> + };
> + };
> +};
Hi Rob,
On 11/22/2015 02:47 PM, Rob Herring wrote:
> On Fri, Nov 20, 2015 at 05:17:19PM -0700, Joshua Henderson wrote:
>> From: Andrei Pistirica <[email protected]>
>>
>> Document the devicetree bindings for PINCTRL and GPIO found on Microchip
>> PIC32 class devices. This also adds a header defining related port and
>> peripheral pin select functionality.
>>
>> Signed-off-by: Andrei Pistirica <[email protected]>
>> Signed-off-by: Joshua Henderson <[email protected]>
>> ---
>> .../bindings/gpio/microchip,pic32-gpio.txt | 33 ++
>> .../bindings/pinctrl/microchip,pic32-pinctrl.txt | 100 +++++
>> include/dt-bindings/pinctrl/pic32mzda.h | 404 ++++++++++++++++++++
>> 3 files changed, 537 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt
>> create mode 100644 Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt
>> create mode 100644 include/dt-bindings/pinctrl/pic32mzda.h
>>
>> 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..f6eeb2f
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt
>> @@ -0,0 +1,33 @@
>> +* Microchip PIC32 GPIO devices (PIO).
>> +
>> +Required properties:
>> + - compatible: "microchip,pic32-gpio"
>
> This should have a chip specific compatible string.
>
Will change to "microchip,pic32mzda-gpio".
>> + - reg: Base address and length for the device.
>> + - interrupts: The port interrupt shared be 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 unused.
>> + - 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:
>> + PIC32_PIN_CN_RISING : low-to-high edge triggered.
>> + PIC32_PIN_CN_FALLING : high-to-low edge triggered.
>> + PIC32_PIN_CN_BOTH : low-to-high and high-to-low edges triggered.
>
> Can't you use the standard flags?
>
Yes. This will be changed.
>> +
>> +Note:
>> + - If gpio-ranges is missing, then all the pins (32) related to the gpio bank
>> + are enabled.
>> +
>> +Example:
>> + pioA: gpio@1f860000 {
>> + compatible = "microchip,pic32-gpio";
>> + reg = <0x1f860000 0x24>;
>> + interrupts = <PORTA_INPUT_CHANGE_INTERRUPT
>> + DEFAULT_INT_PRI IRQ_TYPE_LEVEL_HIGH>;
>> + #gpio-cells = <2>;
>> + gpio-controller;
>> + interrupt-controller;
>> + #interrupt-cells = <2>;
>> + gpio-ranges = <&pic32_pinctrl 0 0 32>;
>> + clocks = <&PBCLK4>;
>> + };
>> 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..7cf4167
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/pinctrl/microchip,pic32-pinctrl.txt
>> @@ -0,0 +1,100 @@
>> +* Microchip PIC32 Pinmux device.
>> +
>> +Please refer to pinctrl-bindings.txt for details of the pinctrl properties and
>> +common 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
>> +single pins or a pairs of mux and related configuration.
>> +
>> +Required properties for pic32 device:
>> + - compatible: "microchip,pic32-pinctrl", "microchip,pic32mz-pinctrl"
>
> Is this both or either one. For both, it should be most specific first.
>
This is a mistake. Will change to only "microchip,pic32mzda-pinctrl".
>
>> + - reg: Base address and length for pps:in and pps:out registers.
>> +
>> +Properties for 'pin configuration node':
>> + - pic32,pins: each entry consists of 3 intergers and represents the mux and
>> + config settings for one pin. The first integer represent the remappable pin,
>> + the second represent the peripheral pin and the last the configuration.
>> + The format is pic32,pins = <PIC32_RP_'dir'_'pin'
>> + PIC32_PP_'dir'_'peripherl-pin' PIC32_PIN_CONF_'config'>. The configurations
>> + are divided in 2 classes: IN and OUT and each in 4 buckets. Each entry must
>> + contains items from the same class and bucket, otherwise the driver will
>> + notify an error and the initialization will fail.
>> + - pic32,single-pins: each entry consists of 3 intergers and represents a pin
>> + (that is not remappable) and related configuraion. The format is
>> + pic32,single-pins = <PORT_'x' 'pin' PIC32_PIN_CONF_'config'>. Each port has
>> + 32 pins and please refer to chip documentation for details of remappable
>> + pins.
>> +
>> +Available pin configurations (refer to dt-bindings/pinctrl/pic32.h):
>> + PIC32_PIN_CONF_NONE : no configuration (default).
>> + PIC32_PIN_CONF_OD : indicate this pin need a open-drain (no direction).
>> + PIC32_PIN_CONF_OD_OUT : indicate this pin need a open-drain out.
>> + PIC32_PIN_CONF_PU : indicate this pin need a pull up (no direction).
>> + PIC32_PIN_CONF_PU_IN : indicate this pin need a pull up in.
>> + PIC32_PIN_CONF_PD : indicate this pin need a pull down (no direction).
>> + PIC32_PIN_CONF_PD_IN : indicate this pin need a pull down input.
>> + PIC32_PIN_CONF_AN : indicate this pin as analogic (no direction).
>> + PIC32_PIN_CONF_AN_IN : indicate this pin as analogic input.
>> + PIC32_PIN_CONF_DG : indicate this pin as digital (no direction).
>> + PIC32_PIN_CONF_DG_IN : indicate this pin as digital input.
>> + PIC32_PIN_CONF_DG_OUT : indicate this pin as digital output.
>> +
>> +NOTEs:
>> +1. The pins functions nods are defined under pic32 pinctrl node. The function's
>> + pin groups are defined under functions node.
>> +2. Each pin group can have both pic32,pins and pic32,single-pins properties to
>> + specify re-mappable or non-remappable pins with related mux and configs or
>> + at least one.
>> +3. Each pin configuration node can have a phandle and devices can set pins
>> + configurations by referring to the phandle of that pin configuration node.
>> +4. The pinctrl bindings are listed in dt-bindings/pinctrl/pic32.h.
>> +5. The gpio controller must be described in the pinctrl simple-bus.
>> +
>> +Example:
>> +pinctrl@1f800000{
>> + #address-cells = <1>;
>> + #size-cells = <1>;
>> + compatible = "microchip,pic32-pinctrl", "simple-bus";
>> + ranges;
>> + reg = <0x1f801404 0x3c>, /* in */
>> + <0x1f801538 0x57>; /* out */
>> +
>> + pioA: gpio@1f860000 {
>> + compatible = "microchip,pic32-gpio";
>
> The gpio controller is a sub-function of the pinctrl? That doesn't
> really seem to be the case based on the addresses.
>
PIC32MZDA has a group of hardware registers that only handles assigning
functions to pins. We have related this set of hardware registers to a
pinctrl node. Separately, each bank of pins has its own set of registers
that handle setting input, output, pullup, gpio interrupts, etc. We have
mapped each of those banks to gpio-controller nodes. However, the
functionality in hardware for pinmux and pinconf spans both the pinctrl
and gpio node registers. It seems that there is overlap in hardware with
functionality that seems to usually be considered somewhat separate
definitions in standard bindings which is the source of some confusion.
We can make the gpio-controller and pinctrl nodes siblings, but they are
still tightly related. Does the following working example make more sense?
pic32_pinctrl: pinctrl@1f801400{
#address-cells = <1>;
#size-cells = <1>;
compatible = "microchip,pic32mzda-pinctrl";
reg = <0x1f801400 0x400>;
clocks = <&PBCLK1>;
};
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>;
gpio-ranges = <&pic32_pinctrl 0 0 16>;
};
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>;
gpio-ranges = <&pic32_pinctrl 0 16 16>;
};
>> + reg = <0x1f860000 0x24>;
>> + gpio-controller;
>> + };
>> +
>> + /* functions */
>> + sw1 {
>> + pinctrl_sw1: sw1-0 {
>> + pic32,single-pins = <PORT_B 12 PIC32_PIN_CONF_PULLUP>;
>
> Why isn't this using standard pinctrl properties?
>
There is no technical reason other than it was based off of existing
bindings in the kernel, albeit, non-standard ones. This will be reworked
to use standard bindings, with a couple additional microchip, properties
for digital and analog. The following example is the target:
pinctrl_uart2: uart2_0 {
uart2-tx {
pins = "G9";
function = "U2TX";
microchip,digital;
};
uart2-rx {
pins = "B0";
function = "U2RX";
microchip,digital;
};
};
pinctrl_adc1: adc1_0 {
pins = "A10";
microchip,analog-level;
input-enable;
};
>> + };
>> + };
>> +
>> + uart1 {
>> + pinctrl_uart1: uart1-0 {
>> + pic32,pins =
>> + <PIC32_RP_OUT_RPG7 PIC32_PP_OUT_U1TX PIC32_PIN_CONF_NONE
>> + PIC32_RP_IN_RPG8 PIC32_PP_IN_U1RX PIC32_PIN_CONF_NONE>;
>> + };
>> + };
>> +};
Thanks for your time,
Josh
Linus,
On 11/30/2015 06:41 AM, Linus Walleij wrote:
> On Sat, Nov 21, 2015 at 1:17 AM, Joshua Henderson
> <[email protected]> wrote:
>
>> From: Andrei Pistirica <[email protected]>
>>
>> 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: Andrei Pistirica <[email protected]>
>> Signed-off-by: Joshua Henderson <[email protected]>
>
>> +config PINCTRL_PIC32
>> + bool "Microchip PIC32 pin controller driver"
>> + depends on OF
>> + depends on MACH_PIC32
>> + select PINMUX
>> + select GENERIC_PINCONF
>> + select GPIOLIB_IRQCHIP
>
> Nice but...
>
>> +struct pic32_gpio_irq {
>> + struct irq_chip gpio_irqchip;
>> + struct irq_domain *domain;
>
>
> If you're using GPIOLIB_IRQCHIP with a separate irq_domain
> you are totally missing the point of GPIOLIB_IRQCHIP.
>
> Look closer at other drivers using GPIOLIB_IRQCHIP and
> get your driver to use the infrastructure properly.
>
Consider it done.
>> +#define to_pic32_gpio_chip(c) container_of(c, struct pic32_gpio_chip, chip)
>
> Make this a static inline function instead. #defines are
> hard to read.
>
Ack.
>> +static struct pic32_gpio_chip *gpio_chips[MAX_PIO_BANKS];
>
> Why? You should not need to keep track of the chips
> in some local array.
Ack.
>
>> +int pic32_pinconf_open_drain_runtime(unsigned pin_id, int value)
>> +{
>> + struct pic32_gpio_chip *pic32_chip = gpio_to_pic32_gpio_chip(pin_id);
>> + unsigned pin = pin_id % PINS_PER_BANK;
>> +
>> + if (IS_ERR_OR_NULL(pic32_chip))
>> + return -ENODEV;
>> +
>> + return pic32_pinconf_open_drain(pic32_chip, pin, value);
>> +}
>> +EXPORT_SYMBOL(pic32_pinconf_open_drain_runtime);
>
> NO WAY are you going to call this from outside of the driver, even
> less other modules.
>
> Use pin control states to control the pins, no cross calling of
> functions in the middle of everything thanks.
>
> You shouldn't need one set of calls for "runtime" either, all pin
> control states are possible to control at runtime, we have no
> distinction between boot time and any other time.
>
Ack.
>> +static int pic32_gpio_request(struct gpio_chip *chip, unsigned offset)
>> +{
>> + int gpio = chip->base + offset;
>> + int bank = chip->base / chip->ngpio;
>> +
>> + dev_dbg(chip->dev, "%s: request GPIO-%c:%d(%d)\n", __func__,
>> + 'A' + bank, offset, gpio);
>> +
>> + return pinctrl_request_gpio(gpio);
>> +}
>> +
>> +static void pic32_gpio_free(struct gpio_chip *chip, unsigned offset)
>> +{
>> + int gpio = chip->base + offset;
>> + int bank = chip->base / chip->ngpio;
>> +
>> + dev_dbg(chip->dev, "%s: free GPIO-%c:%d(%d)\n", __func__,
>> + 'A' + bank, offset, gpio);
>> +
>> + pinctrl_free_gpio(gpio);
>> +}
>
> This looks nice.
>
With existing rework, these are gone.
>> +static int pic32_gpio_get(struct gpio_chip *chip, unsigned gpio)
>> +{
>> + struct pic32_gpio_chip *pic32_chip = to_pic32_gpio_chip(chip);
>> + struct pic32_reg __iomem *port_reg = (struct pic32_reg __iomem *)
>> + pic32_pio_get_reg(pic32_chip, PIC32_PORT);
>> + u32 mask = BIT(gpio);
>> +
>> + if (WARN_ON(port_reg == NULL || gpio >= chip->ngpio))
>> + return -EINVAL;
>> +
>> + return readl(&port_reg->val) & mask;
>
> return !!(readl(&port_reg->val) & mask);
>
> to clamp it to 0/1.
Ack.
>
>> +static int pic32_gpio_get_dir(struct gpio_chip *chip, unsigned offset)
>> +{
>> + struct pic32_gpio_chip *pic32_chip = to_pic32_gpio_chip(chip);
>> + struct pic32_reg __iomem *tris_reg = (struct pic32_reg __iomem *)
>> + pic32_pio_get_reg(pic32_chip, PIC32_TRIS);
>> + u32 mask = BIT(offset);
>> +
>> + if (WARN_ON(tris_reg == NULL))
>> + return -EINVAL;
>> +
>> + return readl(&tris_reg->val) & mask;
>
> Dito.
Ack.
>
>> +static int pic32_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
>> +{
>> + struct pic32_gpio_chip *pic32_chip = to_pic32_gpio_chip(chip);
>> + struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
>> + int virq;
>> +
>> + if (offset < chip->ngpio)
>> + virq = irq_create_mapping(gpio_irq->domain, offset);
>> + else
>> + virq = -ENXIO;
>> +
>> + dev_dbg(chip->dev, "%s: request IRQ for GPIO:%d, return:%d\n",
>> + __func__, offset + chip->base, virq);
>> +
>> + return virq;
>> +}
>
> This is handled by GPIOLIB_IRQCHIP and you should not define
> .to_irq() for a driver using that.
>
Ack.
>> +static void pic32_gpio_ranges_setup(struct platform_device *pdev,
>> + struct pic32_gpio_chip *pic32_chip)
>> +{
>> + struct device_node *np = pdev->dev.of_node;
>> + struct pinctrl_gpio_range *range;
>> + struct of_phandle_args args;
>> + int ret;
>> +
>> + ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args);
>> + pic32_chip->gpio_base = (ret == 0) ? args.args[1] + args.args[0] :
>> + pic32_chip->pio_idx * PINS_PER_BANK;
>> + pic32_chip->ngpio = (ret == 0) ? args.args[2] - args.args[0] :
>> + PINS_PER_BANK;
>> +
>> + range = &pic32_chip->range;
>> + range->name = dev_name(&pdev->dev);
>> + range->id = pic32_chip->pio_idx;
>> + range->pin_base = range->base = pic32_chip->gpio_base;
>> +
>> + range->npins = pic32_chip->ngpio;
>> + range->gc = &pic32_chip->chip;
>> +
>> + dev_dbg(&pdev->dev, "%s: GPIO-%c ranges: (%d,%d)\n", __func__,
>> + 'A' + range->id,
>> + pic32_chip->gpio_base, pic32_chip->ngpio);
>> +}
>
> What is the point of this? We alread add the ranged in the gpiolib
> core.
>
Ack. It's no longer necessary when using gpiolib properly.
>> +static unsigned int gpio_irq_startup(struct irq_data *d)
>
> This is a too generic name for a function, rename all of these
> prefixed with your custom name like pic32_irq_type() etc.
>
Ack.
>> +{
>> + struct pic32_gpio_chip *pic32_chip = irq_data_get_irq_chip_data(d);
>> + struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
>> + unsigned pin = d->hwirq;
>> + int ret;
>> +
>> + ret = gpiochip_lock_as_irq(&pic32_chip->chip, pin);
>> + if (ret) {
>> + dev_err(pic32_chip->chip.dev, "unable to lock pind %lu IRQ\n",
>> + d->hwirq);
>> + return ret;
>> + }
>
> This should not be called in .irq_startup() but in .irq_request_resources()
> and will not be needed at all if you use GPIOLIB_IRQCHIP properly
> since it defines these callbacks for you and handle them in the
> gpiolib core.
>
Ack.
>> +
>> + /* start CN */
>> + switch (gpio_irq->type[pin]) {
>> + case IRQ_TYPE_EDGE_RISING:
>> + pic32_gpio_irq_rise_dset(pic32_chip, pin);
>> + break;
>> + case IRQ_TYPE_EDGE_FALLING:
>> + pic32_gpio_irq_fall_dset(pic32_chip, pin);
>> + break;
>> + case IRQ_TYPE_EDGE_BOTH:
>> + pic32_gpio_irq_rise_dset(pic32_chip, pin);
>> + pic32_gpio_irq_fall_dset(pic32_chip, pin);
>> + break;
>> + default:
>> + return -EINVAL;
>> + }
>
> Why are you doin this in the .startup() callback? This should be done
> in .set_type().
>
Agreed.
>> +static int gpio_irq_type(struct irq_data *d, unsigned type)
>
> This is a too generic name for a function, rename all of these
> prefixed with your custom name like pic32_irq_type() etc.
>
Ack.
>> +{
>> + struct pic32_gpio_chip *pic32_chip = irq_data_get_irq_chip_data(d);
>> + struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
>> + unsigned pin = d->hwirq;
>> +
>> + dev_dbg(pic32_chip->chip.dev, "%s: irq type:%u\n", __func__, type);
>> +
>> + switch (type) {
>> + case IRQ_TYPE_EDGE_RISING:
>> + case IRQ_TYPE_EDGE_FALLING:
>> + case IRQ_TYPE_EDGE_BOTH:
>> + gpio_irq->type[pin] = type;
>> + return IRQ_SET_MASK_OK;
>> + default:
>> + gpio_irq->type[pin] = IRQ_TYPE_NONE;
>> + return -EINVAL;
>> + }
>> +}
>
> Do you rely on semantic call order like this being called before startup?
>
> I do't think that's good, why can't you write to the hardware directly
> in this function?
Agreed. I see no reason why we can't write to the hardware directly. Simpler and cleaner.
>
>> +/* map virtual irq on hw irq: domain translation */
>> +static int pic32_gpio_irq_map(struct irq_domain *d,
>> + unsigned int virq,
>> + irq_hw_number_t hw)
>> +/* decode irq number: base + pin */
>> +static int pic32_gpio_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)
>> +{
>
> None of these should be needed if you use GPIOLIB_IRQCHIP.
Ack.
>
>> +static int pic32_gpio_irq_map(struct irq_domain *d,
>> + unsigned int virq,
>> + irq_hw_number_t hw)
>> +{
>> + struct pic32_gpio_chip *pic32_chip = d->host_data;
>> + struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
>> + struct irq_chip *irqchip = &gpio_irq->gpio_irqchip;
>> +
>> + dev_dbg(pic32_chip->chip.dev, "%s: GPIO-%c:%d map virq:%u\n", __func__,
>> + 'A' + pic32_chip->pio_idx, virq, virq);
>> +
>> + /* set the gpioX chip */
>> + irq_set_chip(virq, irqchip);
>> + irq_set_chip_data(virq, pic32_chip);
>> + irq_set_handler(virq, handle_simple_irq);
>
> This driver should use handle_edge_irq() and implement .irq_ack().
Ack.
>
>> +static struct irq_domain_ops pic32_gpio_irqd_ops = {
>> + .map = pic32_gpio_irq_map,
>> + .xlate = pic32_gpio_irq_domain_xlate,
>> +};
>
> And no custom domain at all.
Removed.
>
>> +static int pic32_gpio_of_irq_setup(struct platform_device *pdev,
>> + struct pic32_gpio_chip *pic32_chip)
>> +{
>> + struct device_node *node = pdev->dev.of_node;
>> + struct pic32_gpio_irq *gpio_irq = &pic32_chip->gpio_irq;
>> + struct irq_chip *irqchip = &gpio_irq->gpio_irqchip;
>> + int base_irq;
>> +
>> + /* set irqchip */
>> + irqchip->name = kasprintf(GFP_KERNEL, "GPIO-%c",
>> + pic32_chip->pio_idx + 'A');
>> + irqchip->irq_startup = gpio_irq_startup;
>> + irqchip->irq_shutdown = gpio_irq_shutdown;
>> + irqchip->irq_set_type = gpio_irq_type;
>
> Since you are supporting *only* edge IRQs you should
> implement and ->irq_ack() callback that ACKs the IRQ.
> Most likely the gpio controller is holding a level IRQ to the
> next interrupt controller active until you do this.
>
> You should also make sure to use handle_edge_irq() as IRQ
> handler as this is what the chip supports.
Ack. The hardware only supports edge interrupts for gpios. I am curious what impact only supporting edge here will be. Would you suggest emulating level interrupts in this case?
>
>> + base_irq = platform_get_irq(pdev, 0);
>> + if (base_irq < 0)
>> + return base_irq;
>> +
>> + gpio_irq->pio_irq = base_irq;
>> +
>> + /* Setup irq domain of ngpio lines */
>> + gpio_irq->domain = irq_domain_add_linear(
>> + node,
>> + pic32_chip->chip.ngpio,
>> + &pic32_gpio_irqd_ops, pic32_chip);
>> + if (!gpio_irq->domain) {
>> + dev_err(pic32_chip->chip.dev, "Couldn't allocate IRQ domain\n");
>> + return -ENXIO;
>> + }
>
> This stuff should not be needed with GPIOLIB_IRQCHIP.
>
Ack.
>> + dev_dbg(&pdev->dev, "%s: irq GPIO-%c, base_irq:%d, domain:%d\n",
>> + __func__, pic32_chip->pio_idx + 'A',
>> + base_irq, pic32_chip->chip.ngpio);
>> +
>> + /* setup chained handler */
>> + irq_set_chip_data(gpio_irq->pio_irq, pic32_chip);
>> + irq_set_chained_handler(gpio_irq->pio_irq, gpio_irq_handler);
>
> And you should add the GPIO irqchip with
> gpiolib_add_irqchip().
Ack.
>
>> +int pic32_gpio_probe(struct platform_device *pdev,
>> + unsigned (*reg_lookup_off)[],
>> + unsigned lookup_size)
>> +{
>> + struct device_node *np = pdev->dev.of_node;
>> + int alias_idx = of_alias_get_id(np, "gpio");
>> + struct pic32_gpio_chip *pic32_chip = NULL;
>> + struct gpio_chip *chip;
>> + struct resource *r;
>> + int ret = 0;
>> +
>> + dev_dbg(&pdev->dev, "%s: probing...\n", __func__);
>> +
>> + if (!np)
>> + return -ENODEV;
>> +
>> + if (WARN_ON(alias_idx >= ARRAY_SIZE(gpio_chips)))
>> + return -EINVAL;
>> +
>> + if (gpio_chips[alias_idx]) {
>> + dev_err(&pdev->dev, "Failure %i for GPIO %i\n", ret, alias_idx);
>> + return -EBUSY;
>> + }
>> +
>> + /* pic32 gpio chip - private data */
>> + pic32_chip = devm_kzalloc(&pdev->dev, sizeof(*pic32_chip),
>> + GFP_KERNEL);
>> + if (!pic32_chip)
>> + return -ENOMEM;
>> +
>> + /* base address of pio(alias_idx) registers */
>> + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!r) {
>> + ret = -EINVAL;
>> + goto probe_err;
>> + }
>> + pic32_chip->pio_base = devm_ioremap_nocache(&pdev->dev, r->start,
>> + resource_size(r));
>> + if (IS_ERR(pic32_chip->pio_base)) {
>> + ret = PTR_ERR(pic32_chip->pio_base);
>> + goto probe_err;
>> + }
>> +
>> + /* clocks */
>> + pic32_chip->clk = devm_clk_get(&pdev->dev, NULL);
>> + if (IS_ERR(pic32_chip->clk)) {
>> + ret = PTR_ERR(pic32_chip->clk);
>> + dev_err(&pdev->dev, "clk get failed\n");
>> + goto probe_err;
>> + }
>> +
>> + ret = clk_prepare_enable(pic32_chip->clk);
>> + if (ret) {
>> + dev_err(&pdev->dev, "clk enable failed\n");
>> + goto probe_err;
>> + }
>> +
>> + pic32_chip->reg_lookup_off = *reg_lookup_off;
>> + pic32_chip->lookup_size = lookup_size;
>> + pic32_chip->pio_idx = alias_idx;
>> + pic32_chip->chip = gpio_template;
>> + pic32_gpio_ranges_setup(pdev, pic32_chip);/* pin_ranges: unsupported */
>
> I can't see why you have this. The gpiolib core handles this.
>
> Please fix the above and repost, and I can look at the pin control
> parts. As it is now there are too many problems with the GPIO
> parts.
>
Agreed.
> Yours,
> Linus Walleij
>
Thanks,
Josh
Rob,
On 11/22/2015 02:56 PM, Rob Herring wrote:
> On Fri, Nov 20, 2015 at 05:17:21PM -0700, Joshua Henderson wrote:
>> From: Andrei Pistirica <[email protected]>
>>
>> Document the devicetree bindings for the USART peripheral found on
>> Microchip PIC32 class devices.
>>
>> Signed-off-by: Andrei Pistirica <[email protected]>
>> Signed-off-by: Joshua Henderson <[email protected]>
>> ---
>> .../bindings/serial/microchip,pic32-usart.txt | 29 ++++++++++++++++++++
>> 1 file changed, 29 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/serial/microchip,pic32-usart.txt
>>
>> diff --git a/Documentation/devicetree/bindings/serial/microchip,pic32-usart.txt b/Documentation/devicetree/bindings/serial/microchip,pic32-usart.txt
>> new file mode 100644
>> index 0000000..c87321c
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/serial/microchip,pic32-usart.txt
>> @@ -0,0 +1,29 @@
>> +* Microchip Universal Synchronous Asynchronous Receiver/Transmitter (USART)
>> +
>> +Required properties:
>> +- compatible: Should be "microchip,pic32-usart"
>
> Again, should be more specific.
>
Ack. In addition, will replace all instances of USART with UART.
>> +- reg: Should contain registers location and length
>> +- interrupts: Should contain interrupt
>> +- pinctrl: Should contain pinctrl for TX/RX/RTS/CTS
>> +
>> +Optional properties:
>> +- microchip,uart-has-rtscts : Indicate the uart has hardware flow control
>> +- rts-gpios: RTS pin for USP-based UART if microchip,uart-has-rtscts
>> +- cts-gpios: CTS pin for USP-based UART if microchip,uart-has-rtscts
>
> This appears to just be copied for Sirf UART.
>
> Doesn't *-gpios being present imply having h/w
> flow-control (i.e. microchip,uart-has-rtscts)?
>
> Rob
Agreed. microchip,uart-has-rtscts will be dropped and it turns out we don't really need the rtc-gpios property.
Josh
>
>> +
>> +Example:
>> + usart0: serial@1f822000 {
>> + compatible = "microchip,pic32-usart";
>> + reg = <0x1f822000 0x50>;
>> + interrupts = <UART1_FAULT DEFAULT_INT_PRI IRQ_TYPE_NONE>,
>> + <UART1_RECEIVE_DONE DEFAULT_INT_PRI IRQ_TYPE_NONE>,
>> + <UART1_TRANSFER_DONE DEFAULT_INT_PRI IRQ_TYPE_NONE>;
>> + pinctrl-names = "default";
>> + pinctrl-0 = <
>> + &pinctrl_uart1
>> + &pinctrl_uart1_cts
>> + &pinctrl_uart1_rts>;
>> + microchip,uart-has-rtscts;
>> + cts-gpios = <&pioB 15 0>;
>> + rts-gpios = <&pioD 1 0>;
>> + };
>> --
>> 1.7.9.5
>>