Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[PATCH v2 00/10] ARM: NUC900: Add NUC970 SoC support

98 views
Skip to first unread message

Wan Zongshun

unread,
Jul 10, 2016, 3:30:05 AM7/10/16
to
Hi,

This patch series added Nuvoton new SoC NUC970 development board
support, this nuc970 belongs to nuc900 series, but many features are
not compatible with old nuc900 SoCs like nuc910, nuc920.

Those patches are basing on old w90x900 codes, and are using standard
linux subsystem interface, such as dts, driver/clk, driver/clocksource
, driver/irqchip drivers.

The old w90x900 plat such as nuc910,nuc960 codes will also be changed
to new style according to nuc970 codes after those patches was accepted.

PATCH V2:
The V2 patches change some code style, re-archtect some drivers and add reset
and soc drivers, split some dts patches according to maillist's comments.

Wan Zongshun (10):
ARM: NUC900: Add nuc970 machine support
irqchip: add irqchip driver for nuc900
Clocksource: add nuc970 clocksource driver
clk: add Clock driver for nuc970
power/reset: Add reset driver support for nuc900
soc: Add SoC specific driver support for nuc900
ARM: dts: Add clock header file into dt-bindings
ARM: dts: nuc900: Add nuc970 dts files
Documentation: devicetree: Add dts description for nuc900
nuc900: add nuc970 platform defconfig file

.../devicetree/bindings/arm/nuvoton/nuc970.txt | 12 +
.../bindings/clock/nuvoton,nuc970-clk.txt | 13 +
.../interrupt-controller/nuvoton,nuc900-aic.txt | 15 +
.../bindings/reset/nuvoton,nuc900-reset.txt | 12 +
.../devicetree/bindings/serial/nuc970-uart.txt | 22 +
.../bindings/soc/nuvoton/nuvoton,nuc900-soc.txt | 12 +
.../bindings/timer/nuvoton,nuc970-tmr.txt | 20 +
arch/arm/boot/dts/Makefile | 1 +
arch/arm/boot/dts/nuc970-evb.dts | 34 +
arch/arm/boot/dts/nuc970.dtsi | 88 ++
arch/arm/configs/nuc970_defconfig | 76 ++
arch/arm/mach-w90x900/Kconfig | 20 +
arch/arm/mach-w90x900/Makefile | 3 +
arch/arm/mach-w90x900/include/mach/irqs.h | 5 +
arch/arm/mach-w90x900/nuc900.c | 41 +
drivers/clk/Makefile | 1 +
drivers/clk/nuc900/Makefile | 6 +
drivers/clk/nuc900/clk-apll.c | 168 ++++
drivers/clk/nuc900/clk-ccf.h | 53 ++
drivers/clk/nuc900/clk-nuc970.c | 925 +++++++++++++++++++++
drivers/clk/nuc900/clk-upll.c | 83 ++
drivers/clocksource/Kconfig | 8 +
drivers/clocksource/Makefile | 1 +
drivers/clocksource/timer-nuc900.c | 305 +++++++
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-nuc900.c | 150 ++++
drivers/power/reset/Kconfig | 7 +
drivers/power/reset/Makefile | 1 +
drivers/power/reset/nuc900-reset.c | 93 +++
drivers/soc/Kconfig | 1 +
drivers/soc/Makefile | 1 +
drivers/soc/nuvoton/Kconfig | 10 +
drivers/soc/nuvoton/Makefile | 1 +
drivers/soc/nuvoton/soc-nuc900.c | 100 +++
include/dt-bindings/clock/nuc970-clock.h | 233 ++++++
35 files changed, 2522 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/nuvoton/nuc970.txt
create mode 100644 Documentation/devicetree/bindings/clock/nuvoton,nuc970-clk.txt
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/nuvoton,nuc900-aic.txt
create mode 100644 Documentation/devicetree/bindings/reset/nuvoton,nuc900-reset.txt
create mode 100644 Documentation/devicetree/bindings/serial/nuc970-uart.txt
create mode 100644 Documentation/devicetree/bindings/soc/nuvoton/nuvoton,nuc900-soc.txt
create mode 100644 Documentation/devicetree/bindings/timer/nuvoton,nuc970-tmr.txt
create mode 100644 arch/arm/boot/dts/nuc970-evb.dts
create mode 100644 arch/arm/boot/dts/nuc970.dtsi
create mode 100644 arch/arm/configs/nuc970_defconfig
create mode 100644 arch/arm/mach-w90x900/nuc900.c
create mode 100644 drivers/clk/nuc900/Makefile
create mode 100644 drivers/clk/nuc900/clk-apll.c
create mode 100644 drivers/clk/nuc900/clk-ccf.h
create mode 100644 drivers/clk/nuc900/clk-nuc970.c
create mode 100644 drivers/clk/nuc900/clk-upll.c
create mode 100644 drivers/clocksource/timer-nuc900.c
create mode 100644 drivers/irqchip/irq-nuc900.c
create mode 100644 drivers/power/reset/nuc900-reset.c
create mode 100644 drivers/soc/nuvoton/Kconfig
create mode 100644 drivers/soc/nuvoton/Makefile
create mode 100644 drivers/soc/nuvoton/soc-nuc900.c
create mode 100644 include/dt-bindings/clock/nuc970-clock.h

--
2.7.4

Wan Zongshun

unread,
Jul 10, 2016, 3:30:05 AM7/10/16
to
This patch is to add nuc970 clock Macros header file
into include/dt-bindings/clock.

Signed-off-by: Wan Zongshun <mcuo...@gmail.com>
---
include/dt-bindings/clock/nuc970-clock.h | 233 +++++++++++++++++++++++++++++++
1 file changed, 233 insertions(+)
create mode 100644 include/dt-bindings/clock/nuc970-clock.h

diff --git a/include/dt-bindings/clock/nuc970-clock.h b/include/dt-bindings/clock/nuc970-clock.h
new file mode 100644
index 0000000..cbfcc77
--- /dev/null
+++ b/include/dt-bindings/clock/nuc970-clock.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2016 Wan Zongshun <mcuo...@gmail.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __DT_BINDINGS_CLOCK_NUC970_H
+#define __DT_BINDINGS_CLOCK_NUC970_H
+
+/*SOURCE*/
+
+#define XIN 0
+#define APLL 1
+#define UPLL 2
+#define XIN32K 3
+#define XIN128_DIV 4
+
+/*ECLK*/
+
+#define USB_APLLDIV 5
+#define USB_UPLLDIV 6
+#define USB_ECLK_MUX 7
+#define USB_ECLK_DIV 8
+#define USB_ECLK_GATE 9
+#define SD_APLLDIV 10
+#define SD_UPLLDIV 11
+#define SD_ECLK_MUX 12
+#define SD_ECLK_DIV 13
+#define SD_ECLK_GATE 14
+#define LCD_APLLDIV 15
+#define LCD_UPLLDIV 16
+#define LCD_ECLK_MUX 17
+#define LCD_ECLK_DIV 18
+#define LCD_ECLK_GATE 19
+#define ADC_APLLDIV 20
+#define ADC_UPLLDIV 21
+#define ADC_ECLK_MUX 22
+#define ADC_ECLK_DIV 23
+#define ADC_ECLK_GATE 24
+#define AUDIO_APLLDIV 25
+#define AUDIO_UPLLDIV 26
+#define AUDIO_ECLK_MUX 27
+#define AUDIO_ECLK_DIV 28
+#define AUDIO_ECLK_GATE 29
+#define CAP_APLLDIV 30
+#define CAP_UPLLDIV 31
+#define CAP_ECLK_MUX 32
+#define CAP_ECLK_DIV 33
+#define CAP_ECLK_GATE 34
+#define SDH_APLLDIV 35
+#define SDH_UPLLDIV 36
+#define SDH_ECLK_MUX 37
+#define SDH_ECLK_DIV 38
+#define SDH_ECLK_GATE 39
+#define EMMC_APLLDIV 40
+#define EMMC_UPLLDIV 41
+#define EMMC_ECLK_MUX 42
+#define EMMC_ECLK_DIV 43
+#define EMMC_ECLK_GATE 44
+#define UART0_APLLDIV 45
+#define UART0_UPLLDIV 46
+#define UART0_ECLK_MUX 47
+#define UART0_ECLK_DIV 48
+#define UART0_ECLK_GATE 49
+#define UART1_APLLDIV 50
+#define UART1_UPLLDIV 51
+#define UART1_ECLK_MUX 52
+#define UART1_ECLK_DIV 53
+#define UART1_ECLK_GATE 54
+#define UART2_APLLDIV 55
+#define UART2_UPLLDIV 56
+#define UART2_ECLK_MUX 57
+#define UART2_ECLK_DIV 58
+#define UART2_ECLK_GATE 59
+#define UART3_APLLDIV 60
+#define UART3_UPLLDIV 61
+#define UART3_ECLK_MUX 62
+#define UART3_ECLK_DIV 63
+#define UART3_ECLK_GATE 64
+#define UART4_APLLDIV 65
+#define UART4_UPLLDIV 66
+#define UART4_ECLK_MUX 67
+#define UART4_ECLK_DIV 68
+#define UART4_ECLK_GATE 69
+#define UART5_APLLDIV 70
+#define UART5_UPLLDIV 71
+#define UART5_ECLK_MUX 72
+#define UART5_ECLK_DIV 73
+#define UART5_ECLK_GATE 74
+#define UART6_APLLDIV 75
+#define UART6_UPLLDIV 76
+#define UART6_ECLK_MUX 77
+#define UART6_ECLK_DIV 78
+#define UART6_ECLK_GATE 79
+#define UART7_APLLDIV 80
+#define UART7_UPLLDIV 81
+#define UART7_ECLK_MUX 82
+#define UART7_ECLK_DIV 83
+#define UART7_ECLK_GATE 84
+#define UART8_APLLDIV 85
+#define UART8_UPLLDIV 86
+#define UART8_ECLK_MUX 87
+#define UART8_ECLK_DIV 88
+#define UART8_ECLK_GATE 89
+#define UART9_APLLDIV 90
+#define UART9_UPLLDIV 91
+#define UART9_ECLK_MUX 92
+#define UART9_ECLK_DIV 93
+#define UART9_ECLK_GATE 94
+#define UART10_APLLDIV 95
+#define UART10_UPLLDIV 96
+#define UART10_ECLK_MUX 97
+#define UART10_ECLK_DIV 98
+#define UART10_ECLK_GATE 99
+#define SYSTEM_APLLDIV 100
+#define SYSTEM_UPLLDIV 101
+#define SYSTEM_ECLK_MUX 102
+#define SYSTEM_ECLK_DIV 103
+#define SYSTEM_ECLK_GATE 104
+#define GPIO_ECLK_MUX 105
+#define GPIO_ECLK_DIV 106
+#define GPIO_ECLK_GATE 107
+#define KPI_ECLK_MUX 108
+#define KPI_ECLK_DIV 109
+#define KPI_ECLK_GATE 110
+#define ETIMER0_ECLK_MUX 111
+#define ETIMER0_ECLK_GATE 12
+#define ETIMER1_ECLK_MUX 113
+#define ETIMER1_ECLK_GATE 114
+#define ETIMER2_ECLK_MUX 115
+#define ETIMER2_ECLK_GATE 116
+#define ETIMER3_ECLK_MUX 117
+#define ETIMER3_ECLK_GATE 118
+#define WWDT_ECLK_MUX 119
+#define WWDT_ECLK_GATE 120
+#define WDT_ECLK_MUX 121
+#define WDT_ECLK_GATE 122
+#define SMC0_ECLK_DIV 123
+#define SMC0_ECLK_GATE 124
+#define SMC0_GATE 125
+#define SMC1_ECLK_DIV 126
+#define SMC1_ECLK_GATE 127
+#define SMC1_GATE 128
+
+/*SYS*/
+
+#define SYS_MUX 129
+#define SYS_DIV 130
+#define CPU_DIV 131
+#define CPU_GATE 132
+#define DDR_GATE 133
+
+/*HCLK*/
+
+#define HCLK_GATE 134
+#define HCLK1_DIV 135
+#define GDMA_GATE 136
+#define EBI_GATE 137
+#define TIC_GATE 138
+#define SRAM_GATE 139
+#define HCLKN_DIV 140
+#define DRAM_GATE 141
+#define HCLK234_DIV 142
+#define USBH_GATE 143
+#define EMAC1_GATE 144
+#define EMAC1_ECLK_DIV 145
+#define EMAC1_ECLK_GATE 146
+#define USBD_GATE 147
+#define FMI_GATE 148
+#define NAND_GATE 149
+#define EMMC_GATE 150
+#define CRYPTO_GATE 151
+#define JPEG_GATE 152
+#define JPEG_ECLK_DIV 153
+#define JPEG_ECLK_GATE 154
+#define GE2D_GATE 155
+#define GE2D_ECLK_DIV 156
+#define GE2D_ECLK_GATE 157
+#define EMAC0_GATE 158
+#define EMAC0_ECLK_DIV 159
+#define EMAC0_ECLK_GATE 160
+#define SDH_GATE 161
+#define AUDIO_GATE 162
+#define LCD_GATE 163
+#define CAP_GATE 164
+#define SENSOR_GATE 165
+
+/*PCLK*/
+
+#define PCLK_DIV 166
+#define PCLK4096_DIV 167
+#define I2C0_GATE 168
+#define I2C1_GATE 169
+#define SPI0_GATE 170
+#define SPI1_GATE 171
+#define UART0_GATE 172
+#define UART1_GATE 173
+#define UART2_GATE 174
+#define UART3_GATE 175
+#define UART4_GATE 176
+#define UART5_GATE 177
+#define UART6_GATE 178
+#define UART7_GATE 179
+#define UART8_GATE 180
+#define UART9_GATE 181
+#define UART10_GATE 182
+#define TIMER0_GATE 183
+#define TIMER1_GATE 184
+#define TIMER2_GATE 185
+#define TIMER3_GATE 186
+#define TIMER4_GATE 187
+#define WDT_GATE 188
+#define RTC_GATE 189
+#define WWDT_GATE 190
+#define GPIO_GATE 191
+#define ADC_GATE 192
+#define KPI_GATE 193
+#define MTPC_GATE 194
+#define PWM_GATE 195
+#define ETIMER0_GATE 196
+#define ETIMER1_GATE 197
+#define ETIMER2_GATE 198
+#define ETIMER3_GATE 199
+#define CAN0_GATE 200
+#define CAN1_GATE 201
+#define NUC970_CLK_MAX 202
+
+#endif
--
2.7.4

Wan Zongshun

unread,
Jul 10, 2016, 3:30:05 AM7/10/16
to
NUC970 is a new SoC of Nuvoton nuc900 series, this patch is
to add machine file support for it.

Signed-off-by: Wan Zongshun <mcuo...@gmail.com>
---
arch/arm/mach-w90x900/Kconfig | 20 ++++++++++++++++++++
arch/arm/mach-w90x900/Makefile | 3 +++
arch/arm/mach-w90x900/nuc900.c | 41 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 64 insertions(+)
create mode 100644 arch/arm/mach-w90x900/nuc900.c

diff --git a/arch/arm/mach-w90x900/Kconfig b/arch/arm/mach-w90x900/Kconfig
index 69bab32..80ab00c 100644
--- a/arch/arm/mach-w90x900/Kconfig
+++ b/arch/arm/mach-w90x900/Kconfig
@@ -15,6 +15,26 @@ config CPU_NUC960
help
Support for NUCP960 of Nuvoton NUC900 CPUs.

+config SOC_NUC970
+ bool "Nuvoton NUC970 SoC support"
+ select COMMON_CLK
+ select GENERIC_IRQ_CHIP
+ select HAVE_CLK_PREPARE
+ select IRQ_DOMAIN
+ select MULTI_IRQ_HANDLER
+ select MFD_SYSCON
+ select NUC900_TIMER
+ select SOC_NUC900
+ select USE_OF
+ help
+ Support for NUC970 of Nuvoton NUC900 SoCs.
+ The NUC970 series runs up to 300 MHz, with 16 KB I-cache,
+ 16 KB D-cache and MMU, 56KB embedded SRAM and 16 KB Internal
+ Boot ROM for booting from USB, NAND and SPI FLASH.
+ Detailed information please check the following link:
+ https://github.com/zswan/nuc900-document/blob/master/
+ NUC970_TechnicalReferenceManual_EN_Rev1.30.pdf
+
menu "W90P910 Machines"

config MACH_W90P910EVB
diff --git a/arch/arm/mach-w90x900/Makefile b/arch/arm/mach-w90x900/Makefile
index 828c032..d13ba5a 100644
--- a/arch/arm/mach-w90x900/Makefile
+++ b/arch/arm/mach-w90x900/Makefile
@@ -4,8 +4,10 @@

# Object file lists.

+ifeq ($(CONFIG_SOC_NUC970),)
obj-y := irq.o time.o mfp.o gpio.o clock.o
obj-y += clksel.o dev.o cpu.o
+endif
# W90X900 CPU support files

obj-$(CONFIG_CPU_W90P910) += nuc910.o
@@ -17,3 +19,4 @@ obj-$(CONFIG_CPU_NUC960) += nuc960.o
obj-$(CONFIG_MACH_W90P910EVB) += mach-nuc910evb.o
obj-$(CONFIG_MACH_W90P950EVB) += mach-nuc950evb.o
obj-$(CONFIG_MACH_W90N960EVB) += mach-nuc960evb.o
+obj-$(CONFIG_SOC_NUC970) += nuc900.o
diff --git a/arch/arm/mach-w90x900/nuc900.c b/arch/arm/mach-w90x900/nuc900.c
new file mode 100644
index 0000000..309c332
--- /dev/null
+++ b/arch/arm/mach-w90x900/nuc900.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2016 Wan Zongshun <mcuo...@gmail.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/reboot.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/sys_soc.h>
+#include <linux/semaphore.h>
+
+#include <asm/system_misc.h>
+#include <asm/mach/arch.h>
+
+static void __init nuc900_machine_init(void)
+{
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+}
+
+static const char *nuc900_dt_compat[] __initconst = {
+ "nuvoton,nuc970",
+ NULL,
+};
+
+DT_MACHINE_START(nuc900_dt, "Nuvoton NUC900 (Device Tree Support)")
+ .init_machine = nuc900_machine_init,
+ .dt_compat = nuc900_dt_compat,
+MACHINE_END
--
2.7.4

Wan Zongshun

unread,
Jul 10, 2016, 3:30:05 AM7/10/16
to
This patch is to add SoC specific driver for nuc970 SoC,
it is for getting nuc970 version id and chip id.

Signed-off-by: Wan Zongshun <mcuo...@gmail.com>
---
drivers/soc/Kconfig | 1 +
drivers/soc/Makefile | 1 +
drivers/soc/nuvoton/Kconfig | 10 ++++
drivers/soc/nuvoton/Makefile | 1 +
drivers/soc/nuvoton/soc-nuc900.c | 100 +++++++++++++++++++++++++++++++++++++++
5 files changed, 113 insertions(+)
create mode 100644 drivers/soc/nuvoton/Kconfig
create mode 100644 drivers/soc/nuvoton/Makefile
create mode 100644 drivers/soc/nuvoton/soc-nuc900.c

diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index cb58ef0..2119733 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -4,6 +4,7 @@ source "drivers/soc/bcm/Kconfig"
source "drivers/soc/brcmstb/Kconfig"
source "drivers/soc/fsl/qe/Kconfig"
source "drivers/soc/mediatek/Kconfig"
+source "drivers/soc/nuvoton/Kconfig"
source "drivers/soc/qcom/Kconfig"
source "drivers/soc/rockchip/Kconfig"
source "drivers/soc/samsung/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 380230f..bb1bfba 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_ARCH_DOVE) += dove/
obj-$(CONFIG_MACH_DOVE) += dove/
obj-y += fsl/
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
+obj-$(CONFIG_SOC_NUC900) += nuvoton/
obj-$(CONFIG_ARCH_QCOM) += qcom/
obj-$(CONFIG_ARCH_RENESAS) += renesas/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
diff --git a/drivers/soc/nuvoton/Kconfig b/drivers/soc/nuvoton/Kconfig
new file mode 100644
index 0000000..53c106c
--- /dev/null
+++ b/drivers/soc/nuvoton/Kconfig
@@ -0,0 +1,10 @@
+#
+# ARM Versatile SoC drivers
+#
+config SOC_NUC900
+ bool "SoC bus device for the nuvoton NUC900 platforms"
+ depends on ARCH_W90X900
+ select SOC_BUS
+ help
+ Include support for the SoC bus on the NUC900 platforms
+ providing some sysfs information about the ASIC variant.
diff --git a/drivers/soc/nuvoton/Makefile b/drivers/soc/nuvoton/Makefile
new file mode 100644
index 0000000..88f9b7e
--- /dev/null
+++ b/drivers/soc/nuvoton/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SOC_NUC900) += soc-nuc900.o
diff --git a/drivers/soc/nuvoton/soc-nuc900.c b/drivers/soc/nuvoton/soc-nuc900.c
new file mode 100644
index 0000000..034ef94
--- /dev/null
+++ b/drivers/soc/nuvoton/soc-nuc900.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2016 Wan Zongshun <mcuo...@gmail.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+
+/* System ID in syscon */
+#define GCR_CHIPID 0x00
+#define GCR_CHIPID_MASK 0x00ffffff
+
+static const struct of_device_id nuc900_soc_of_match[] = {
+ { .compatible = "nuvoton,nuc900-soc", },
+ { }
+};
+
+static u32 nuc900_chipid;
+
+static ssize_t nuc900_get_chipid(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "0x%x\n", nuc900_chipid & GCR_CHIPID_MASK);
+}
+
+static struct device_attribute nuc900_chipid_attr =
+ __ATTR(manufacturer, S_IRUGO, nuc900_get_chipid, NULL);
+
+static ssize_t nuc900_get_versionid(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "0x%x\n", (nuc900_chipid >> 24) & 0xff);
+}
+
+static struct device_attribute nuc900_version_attr =
+ __ATTR(board, S_IRUGO, nuc900_get_versionid, NULL);
+
+static int nuc900_soc_probe(struct platform_device *pdev)
+{
+ static struct regmap *syscon_regmap;
+ struct soc_device *soc_dev;
+ struct soc_device_attribute *soc_dev_attr;
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ syscon_regmap = syscon_regmap_lookup_by_phandle(np, "syscon");
+ if (IS_ERR(syscon_regmap))
+ return PTR_ERR(syscon_regmap);
+
+ soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+ if (!soc_dev_attr)
+ return -ENOMEM;
+
+ ret = of_property_read_string(np, "compatible", &soc_dev_attr->soc_id);
+ if (ret)
+ return -EINVAL;
+
+ soc_dev_attr->machine = "NUC900EVB";
+ soc_dev_attr->family = "NUC900";
+ soc_dev = soc_device_register(soc_dev_attr);
+ if (IS_ERR(soc_dev)) {
+ kfree(soc_dev_attr);
+ return -ENODEV;
+ }
+
+ ret = regmap_read(syscon_regmap, GCR_CHIPID, &nuc900_chipid);
+ if (ret)
+ return -ENODEV;
+
+ device_create_file(soc_device_to_device(soc_dev), &nuc900_chipid_attr);
+ device_create_file(soc_device_to_device(soc_dev), &nuc900_version_attr);
+
+ dev_info(&pdev->dev, "Nuvoton Chip ID: 0x%x, Version ID:0x%x\n",
+ nuc900_chipid & GCR_CHIPID_MASK,
+ (nuc900_chipid >> 24) & 0xff);
+
+ return 0;
+}
+
+static struct platform_driver nuc900_soc_driver = {
+ .probe = nuc900_soc_probe,
+ .driver = {
+ .name = "nuc900-soc",
+ .of_match_table = nuc900_soc_of_match,
+ },
+};
+builtin_platform_driver(nuc900_soc_driver);
--
2.7.4

Wan Zongshun

unread,
Jul 10, 2016, 3:30:06 AM7/10/16
to
This patch is to add irqchip driver support for nuc900 plat,
current this driver only supports nuc970 SoC.

Signed-off-by: Wan Zongshun <mcuo...@gmail.com>
---
arch/arm/mach-w90x900/include/mach/irqs.h | 5 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-nuc900.c | 150 ++++++++++++++++++++++++++++++
3 files changed, 156 insertions(+)
create mode 100644 drivers/irqchip/irq-nuc900.c

diff --git a/arch/arm/mach-w90x900/include/mach/irqs.h b/arch/arm/mach-w90x900/include/mach/irqs.h
index 9d5cba3..3b035c6 100644
--- a/arch/arm/mach-w90x900/include/mach/irqs.h
+++ b/arch/arm/mach-w90x900/include/mach/irqs.h
@@ -59,7 +59,12 @@
#define IRQ_KPI W90X900_IRQ(29)
#define IRQ_P2SGROUP W90X900_IRQ(30)
#define IRQ_ADC W90X900_IRQ(31)
+
+#if !defined(CONFIG_SOC_NUC900)
#define NR_IRQS (IRQ_ADC+1)
+#else
+#define NR_IRQS 62
+#endif

/*for irq group*/

diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 38853a1..9ccd5af8a 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -69,3 +69,4 @@ obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o
obj-$(CONFIG_MVEBU_ODMI) += irq-mvebu-odmi.o
obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o
obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o
+obj-$(CONFIG_SOC_NUC970) += irq-nuc900.o
diff --git a/drivers/irqchip/irq-nuc900.c b/drivers/irqchip/irq-nuc900.c
new file mode 100644
index 0000000..c4b2e39
--- /dev/null
+++ b/drivers/irqchip/irq-nuc900.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2016 Wan Zongshun <mcuo...@gmail.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#include <asm/exception.h>
+#include <asm/hardirq.h>
+
+#define REG_AIC_SCR1 0x00
+#define REG_AIC_SCR2 0x04
+#define REG_AIC_SCR3 0x08
+#define REG_AIC_SCR4 0x0C
+#define REG_AIC_SCR5 0x10
+#define REG_AIC_SCR6 0x14
+#define REG_AIC_SCR7 0x18
+#define REG_AIC_SCR8 0x1C
+#define REG_AIC_SCR9 0x20
+#define REG_AIC_SCR10 0x24
+#define REG_AIC_SCR11 0x28
+#define REG_AIC_SCR12 0x2C
+#define REG_AIC_SCR13 0x30
+#define REG_AIC_SCR14 0x34
+#define REG_AIC_SCR15 0x38
+#define REG_AIC_IRSR 0x100
+#define REG_AIC_IRSRH 0x104
+#define REG_AIC_IASR 0x108
+#define REG_AIC_IASRH 0x10C
+#define REG_AIC_ISR 0x110
+#define REG_AIC_ISRH 0x114
+#define REG_AIC_IPER 0x118
+#define REG_AIC_ISNR 0x120
+#define REG_AIC_OISR 0x124
+#define REG_AIC_IMR 0x128
+#define REG_AIC_IMRH 0x12C
+#define REG_AIC_MECR 0x130
+#define REG_AIC_MECRH 0x134
+#define REG_AIC_MDCR 0x138
+#define REG_AIC_MDCRH 0x13C
+#define REG_AIC_SSCR 0x140
+#define REG_AIC_SSCRH 0x144
+#define REG_AIC_SCCR 0x148
+#define REG_AIC_SCCRH 0x14C
+#define REG_AIC_EOSCR 0x150
+
+static void __iomem *aic_base;
+static struct irq_domain *aic_domain;
+
+static void nuc900_irq_mask(struct irq_data *d)
+{
+ if (d->irq < 32)
+ writel(1 << (d->irq), aic_base + REG_AIC_MDCR);
+ else
+ writel(1 << (d->irq - 32), aic_base + REG_AIC_MDCRH);
+}
+
+static void nuc900_irq_ack(struct irq_data *d)
+{
+ writel(0x01, aic_base + REG_AIC_EOSCR);
+}
+
+static void nuc900_irq_unmask(struct irq_data *d)
+{
+ if (d->irq < 32)
+ writel(1 << (d->irq), aic_base + REG_AIC_MECR);
+ else
+ writel(1 << (d->irq - 32), aic_base + REG_AIC_MECRH);
+}
+
+static struct irq_chip nuc900_irq_chip = {
+ .irq_ack = nuc900_irq_ack,
+ .irq_mask = nuc900_irq_mask,
+ .irq_unmask = nuc900_irq_unmask,
+};
+
+void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
+{
+ u32 hwirq;
+
+ hwirq = readl(aic_base + REG_AIC_IPER);
+ hwirq = readl(aic_base + REG_AIC_ISNR);
+ if (!hwirq)
+ writel(0x01, aic_base + REG_AIC_EOSCR);
+
+ handle_IRQ((irq_find_mapping(aic_domain, hwirq)), regs);
+}
+
+static int aic_irq_domain_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(virq, &nuc900_irq_chip, handle_level_irq);
+ irq_clear_status_flags(virq, IRQ_NOREQUEST);
+
+ return 0;
+}
+
+static struct irq_domain_ops aic_irq_domain_ops = {
+ .map = aic_irq_domain_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static int __init aic_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ int ret;
+
+ aic_base = of_iomap(node, 0);
+ if (!aic_base) {
+ ret = -ENOMEM;
+ pr_err("%s: unable to map registers\n", node->full_name);
+ goto err_iomap;
+ }
+
+ writel(0xFFFFFFFC, aic_base + REG_AIC_MDCR);
+ writel(0xFFFFFFFF, aic_base + REG_AIC_MDCRH);
+
+ aic_domain = irq_domain_add_linear(node, NR_IRQS,
+ &aic_irq_domain_ops, NULL);
+
+ if (!aic_domain) {
+ ret = -ENOMEM;
+ pr_err("%s: unable to create IRQ domain\n", node->full_name);
+ goto err_aic_domain;
+ }
+
+ set_handle_irq(aic_handle_irq);
+ return 0;
+
+err_aic_domain:
+ iounmap(aic_base);
+err_iomap:
+ return ret;
+}
+
+IRQCHIP_DECLARE(nuc900, "nuvoton,nuc900-aic", aic_of_init);
--
2.7.4

Wan Zongshun

unread,
Jul 10, 2016, 3:30:35 AM7/10/16
to
This patch is to add clock framework driver for nuc970.
The clock controller generates all clocks for Video, Audio,
CPU, system bus and all functionalities, nuc970 includes
two PLL modules.

Signed-off-by: Wan Zongshun <mcuo...@gmail.com>
---
drivers/clk/Makefile | 1 +
drivers/clk/nuc900/Makefile | 6 +
drivers/clk/nuc900/clk-apll.c | 168 ++++++++
drivers/clk/nuc900/clk-ccf.h | 53 +++
drivers/clk/nuc900/clk-nuc970.c | 925 ++++++++++++++++++++++++++++++++++++++++
drivers/clk/nuc900/clk-upll.c | 83 ++++
6 files changed, 1236 insertions(+)
create mode 100644 drivers/clk/nuc900/Makefile
create mode 100644 drivers/clk/nuc900/clk-apll.c
create mode 100644 drivers/clk/nuc900/clk-ccf.h
create mode 100644 drivers/clk/nuc900/clk-nuc970.c
create mode 100644 drivers/clk/nuc900/clk-upll.c

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index dcc5e69..042377d 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -88,3 +88,4 @@ obj-$(CONFIG_ARCH_ZX) += zte/
obj-$(CONFIG_ARCH_ZYNQ) += zynq/
obj-$(CONFIG_H8300) += h8300/
obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/
+obj-$(CONFIG_ARCH_W90X900) += nuc900/
diff --git a/drivers/clk/nuc900/Makefile b/drivers/clk/nuc900/Makefile
new file mode 100644
index 0000000..a6785ab
--- /dev/null
+++ b/drivers/clk/nuc900/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for nuvoton specific clk
+#
+
+obj-$(CONFIG_SOC_NUC970) += clk-apll.o clk-upll.o clk-nuc970.o
+
diff --git a/drivers/clk/nuc900/clk-apll.c b/drivers/clk/nuc900/clk-apll.c
new file mode 100644
index 0000000..a05aec7
--- /dev/null
+++ b/drivers/clk/nuc900/clk-apll.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2016 Wan Zongshun <mcuo...@gmail.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+
+#include "clk-ccf.h"
+
+struct clk_apll {
+ struct clk_hw hw;
+ void __iomem *base;
+};
+
+#define to_clk_apll(clk) (container_of(clk, struct clk_apll, clk))
+
+static int clk_apll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_apll *pll = to_clk_apll(hw);
+ unsigned long reg;
+
+ reg = readl(pll->base) & ~0x0FFFFFFF;
+
+ switch (rate) {
+ /*usbh*/
+ case 96000000:
+ reg |= 0x8027;
+ break;
+ /*i2s*/
+ case 98400000:
+ reg |= 0x8028;
+ break;
+ /*i2s*/
+ case 169500000:
+ reg |= 0x21f0;
+ break;
+ /*system default, 264MHz*/
+ case 264000000:
+ reg |= 0x15;
+ break;
+ case 300000000:
+ reg |= 0x18;
+ break;
+ default:
+ reg |= 0x15;
+ break;
+ }
+
+ writel(reg, pll->base);
+
+ return 0;
+}
+
+static unsigned long clk_apll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_apll *pll = to_clk_apll(hw);
+ unsigned long reg = readl(pll->base) & 0x0FFFFFFF;
+ unsigned long rate;
+
+ if (parent_rate != 12000000)
+ return 0;
+
+ switch (reg) {
+ /*system default, 264MHz*/
+ case 0x15:
+ rate = 264000000;
+ break;
+ case 0x18:
+ rate = 300000000;
+ break;
+ /*usbh*/
+ case 0x8027:
+ rate = 96000000;
+ break;
+ /*i2s*/
+ case 0x8028:
+ rate = 98400000;
+ break;
+ /*i2s*/
+ case 0x21f0:
+ rate = 169500000;
+ break;
+ default:
+ rate = 264000000;
+ break;
+ }
+
+ return rate;
+}
+
+static long clk_apll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ return rate;
+}
+
+static int clk_apll_enable(struct clk_hw *hw)
+{
+ struct clk_apll *pll = to_clk_apll(hw);
+ unsigned long val;
+
+ val = readl(pll->base);
+ val &= ~0x10000000;
+ val |= 0x40000000;
+ writel(val, pll->base);
+
+ return 0;
+}
+
+static void clk_apll_disable(struct clk_hw *hw)
+{
+ struct clk_apll *pll = to_clk_apll(hw);
+ unsigned long val;
+
+ val = readl(pll->base);
+ val |= 0x10000000;
+ val &= ~0x40000000;
+ writel(val, pll->base);
+}
+
+static struct clk_ops clk_apll_ops = {
+ .recalc_rate = clk_apll_recalc_rate,
+ .enable = clk_apll_enable,
+ .disable = clk_apll_disable,
+ .set_rate = clk_apll_set_rate,
+ .round_rate = clk_apll_round_rate,
+};
+
+struct clk *nuc970_clk_apll(const char *name, const char *parent,
+ void __iomem *base)
+{
+ struct clk_apll *pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ pll->base = base;
+
+ init.name = name;
+ init.ops = &clk_apll_ops;
+ init.flags = 0;
+ init.parent_names = &parent;
+ init.num_parents = 1;
+
+ pll->hw.init = &init;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
diff --git a/drivers/clk/nuc900/clk-ccf.h b/drivers/clk/nuc900/clk-ccf.h
new file mode 100644
index 0000000..2808933
--- /dev/null
+++ b/drivers/clk/nuc900/clk-ccf.h
@@ -0,0 +1,53 @@
+#ifndef __MACH_NUC970_CLK_H
+#define __MACH_NUC970_CLK_H
+
+#include <linux/spinlock.h>
+#include <linux/clk-provider.h>
+
+static spinlock_t nuc970_lock;
+
+extern struct clk *nuc970_clk_apll(const char *name, const char *parent,
+ void __iomem *base);
+extern struct clk *nuc970_clk_upll(const char *name, const char *parent,
+ void __iomem *base);
+
+static inline struct clk *nuc970_clk_fixed(const char *name, int rate)
+{
+ return clk_register_fixed_rate(NULL, name, NULL, 0, rate);
+}
+
+static inline struct clk *nuc970_clk_mux(const char *name, void __iomem *reg,
+ u8 shift, u8 width,
+ const char * const *parents,
+ int num_parents)
+{
+ return clk_register_mux(NULL, name, parents, num_parents, 0, reg, shift,
+ width, 0, &nuc970_lock);
+}
+
+static inline struct clk *nuc970_clk_divider(const char *name,
+ const char *parent,
+ void __iomem *reg, u8 shift,
+ u8 width)
+{
+ return clk_register_divider(NULL, name, parent, 0,
+ reg, shift, width, 0, &nuc970_lock);
+}
+
+static inline struct clk *nuc970_clk_fixed_factor(const char *name,
+ const char *parent,
+ unsigned int mult,
+ unsigned int div)
+{
+ return clk_register_fixed_factor(NULL, name, parent,
+ CLK_SET_RATE_PARENT, mult, div);
+}
+
+static inline struct clk *nuc970_clk_gate(const char *name, const char *parent,
+ void __iomem *reg, u8 shift)
+{
+ return clk_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
+ shift, 0, &nuc970_lock);
+}
+
+#endif
diff --git a/drivers/clk/nuc900/clk-nuc970.c b/drivers/clk/nuc900/clk-nuc970.c
new file mode 100644
index 0000000..55f02c8
--- /dev/null
+++ b/drivers/clk/nuc900/clk-nuc970.c
@@ -0,0 +1,925 @@
+/*
+ * Copyright 2016 Wan Zongshun <mcuo...@gmail.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/spinlock.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include <dt-bindings/clock/nuc970-clock.h>
+#include "clk-ccf.h"
+
+/* Clock Control Registers */
+static void __iomem *clkctrl;
+#define CLK_BA clkctrl
+
+#define REG_CLK_PMCON (CLK_BA + 0x000)
+#define REG_CLK_HCLKEN (CLK_BA + 0x010)
+#define REG_CLK_PCLKEN0 (CLK_BA + 0x018)
+#define REG_CLK_PCLKEN1 (CLK_BA + 0x01C)
+#define REG_CLK_DIV0 (CLK_BA + 0x020)
+#define REG_CLK_DIV1 (CLK_BA + 0x024)
+#define REG_CLK_DIV2 (CLK_BA + 0x028)
+#define REG_CLK_DIV3 (CLK_BA + 0x02C)
+#define REG_CLK_DIV4 (CLK_BA + 0x030)
+#define REG_CLK_DIV5 (CLK_BA + 0x034)
+#define REG_CLK_DIV6 (CLK_BA + 0x038)
+#define REG_CLK_DIV7 (CLK_BA + 0x03C)
+#define REG_CLK_DIV8 (CLK_BA + 0x040)
+#define REG_CLK_DIV9 (CLK_BA + 0x044)
+#define REG_CLK_APLLCON (CLK_BA + 0x060)
+#define REG_CLK_UPLLCON (CLK_BA + 0x064)
+
+static const char *const sys_sel_clks[] = { "xin",
+ "dummy",
+ "apll",
+ "upll" };
+
+static const char *const lcd_sel_clks[] = { "xin",
+ "dummy",
+ "lcd_aplldiv",
+ "lcd_uplldiv" };
+
+static const char *const audio_sel_clks[] = { "xin",
+ "dummy",
+ "audio_aplldiv",
+ "audio_uplldiv" };
+
+static const char *const usb_sel_clks[] = { "xin",
+ "dummy",
+ "usb_aplldiv",
+ "usb_uplldiv" };
+
+static const char *const adc_sel_clks[] = { "xin",
+ "dummy",
+ "adc_aplldiv",
+ "adc_uplldiv" };
+
+static const char *const cap_sel_clks[] = { "xin",
+ "dummy",
+ "cap_aplldiv",
+ "cap_uplldiv" };
+
+static const char *const sdh_sel_clks[] = { "xin",
+ "dummy",
+ "sdh_aplldiv",
+ "sdh_uplldiv" };
+
+static const char *const emmc_sel_clks[] = { "xin",
+ "dummy",
+ "emmc_aplldiv",
+ "emmc_uplldiv" };
+
+static const char *const uart0_sel_clks[] = { "xin",
+ "dummy",
+ "uart0_aplldiv",
+ "uart0_uplldiv" };
+
+static const char *const uart1_sel_clks[] = { "xin",
+ "dummy",
+ "uart1_aplldiv",
+ "uart1_uplldiv" };
+
+static const char *const uart2_sel_clks[] = { "xin",
+ "dummy",
+ "uart2_aplldiv",
+ "uart2_uplldiv" };
+
+static const char *const uart3_sel_clks[] = { "xin",
+ "dummy",
+ "uart3_aplldiv",
+ "uart3_uplldiv" };
+
+static const char *const uart4_sel_clks[] = { "xin",
+ "dummy",
+ "uart4_aplldiv",
+ "uart4_uplldiv" };
+
+static const char *const uart5_sel_clks[] = { "xin",
+ "dummy",
+ "uart5_aplldiv",
+ "uart5_uplldiv" };
+
+static const char *const uart6_sel_clks[] = { "xin",
+ "dummy",
+ "uart6_aplldiv",
+ "uart6_uplldiv" };
+
+static const char *const uart7_sel_clks[] = { "xin",
+ "dummy",
+ "uart7_aplldiv",
+ "uart7_uplldiv" };
+
+static const char *const uart8_sel_clks[] = { "xin",
+ "dummy",
+ "uart8_aplldiv",
+ "uart8_uplldiv" };
+
+static const char *const uart9_sel_clks[] = { "xin",
+ "dummy",
+ "uart9_aplldiv",
+ "uart9_uplldiv" };
+
+static const char *const uart10_sel_clks[] = { "xin",
+ "dummy",
+ "uart10_aplldiv",
+ "uart10_uplldiv" };
+
+static const char *const system_sel_clks[] = { "xin",
+ "dummy",
+ "system_aplldiv",
+ "system_uplldiv" };
+
+static const char *const gpio_sel_clks[] = { "xin", "xin32k"};
+static const char *const kpi_sel_clks[] = { "xin", "xin32k"};
+static const char *const etimer_sel_clks[] = { "xin",
+ "pclk_div",
+ "pclk4096_div",
+ "xin32k" };
+
+static const char *const wwdt_sel_clks[] = { "xin",
+ "xin128_div",
+ "pclk4096_div",
+ "xin32k" };
+
+static struct clk *clk[NUC970_CLK_MAX];
+static struct clk_onecell_data clk_data;
+
+static void __init nuc970_clocks_init(struct device_node *np)
+{
+ int i, ret;
+
+ clkctrl = of_iomap(np, 0);
+ if (!clkctrl)
+ pr_err("%s: unable to map registers\n", np->full_name);
+
+
+ /* source */
+ clk[XIN] = nuc970_clk_fixed("xin", 12000000);
+ clk[XIN32K] = nuc970_clk_fixed("xin32k", 32768);
+ clk[APLL] = nuc970_clk_apll("apll", "xin", REG_CLK_APLLCON);
+ clk[UPLL] = nuc970_clk_upll("upll", "xin", REG_CLK_UPLLCON);
+ clk[XIN128_DIV] = nuc970_clk_fixed_factor("xin128_div", "xin", 1, 128);
+ clk[SYS_MUX] = nuc970_clk_mux("sys_mux", REG_CLK_DIV0, 3, 2,
+ sys_sel_clks,
+ ARRAY_SIZE(sys_sel_clks));
+ clk[SYS_DIV] = nuc970_clk_divider("sys_div", "sys_mux",
+ REG_CLK_DIV0, 0, 2);
+ clk[DDR_GATE] = nuc970_clk_gate("ddr_gate", "sys_div",
+ REG_CLK_HCLKEN, 10);
+ /* CPU */
+ clk[CPU_DIV] = nuc970_clk_divider("cpu_div", "sys_div",
+ REG_CLK_DIV0, 16, 1);
+ clk[CPU_GATE] = nuc970_clk_gate("cpu_gate", "cpu_div",
+ REG_CLK_HCLKEN, 0);
+ /*HCLK1 & PCLK*/
+ clk[HCLK1_DIV] = nuc970_clk_fixed_factor("hclk1_div", "cpu_div", 1, 2);
+ clk[GDMA_GATE] = nuc970_clk_gate("gdma_hclk_gate", "hclk1_div",
+ REG_CLK_HCLKEN, 12);
+ clk[EBI_GATE] = nuc970_clk_gate("ebi_hclk_gate", "hclk1_div",
+ REG_CLK_HCLKEN, 9);
+ clk[TIC_GATE] = nuc970_clk_gate("tic_hclk_gate", "hclk1_div",
+ REG_CLK_HCLKEN, 7);
+ /* HCLK & HCLK234 */
+ clk[HCLKN_DIV] = nuc970_clk_fixed_factor("hclkn_div",
+ "sys_div", 1, 2);
+ clk[DRAM_GATE] = nuc970_clk_gate("dram_gate", "hclkn_div",
+ REG_CLK_HCLKEN, 10);
+ clk[HCLK_GATE] = nuc970_clk_gate("hclk_gate", "hclkn_div",
+ REG_CLK_HCLKEN, 1);
+ clk[SRAM_GATE] = nuc970_clk_gate("sram_gate", "hclk_gate",
+ REG_CLK_HCLKEN, 8);
+ clk[HCLK234_DIV] = nuc970_clk_divider("hclk234_div", "hclkn_div",
+ REG_CLK_DIV0, 20, 4);
+ /* HCLK3 */
+ clk[USBH_GATE] = nuc970_clk_gate("usbh_hclk_gate",
+ "hclk234_div",
+ REG_CLK_HCLKEN, 18);
+ clk[USBD_GATE] = nuc970_clk_gate("usbd_hclk_gate",
+ "hclk234_div",
+ REG_CLK_HCLKEN, 19);
+ clk[FMI_GATE] = nuc970_clk_gate("fmi_hclk_gate",
+ "hclk234_div",
+ REG_CLK_HCLKEN, 20);
+ clk[NAND_GATE] = nuc970_clk_gate("nand_hclk_gate",
+ "hclk234_div",
+ REG_CLK_HCLKEN, 21);
+ clk[EMMC_GATE] = nuc970_clk_gate("emmc_hclk_gate",
+ "hclk234_div",
+ REG_CLK_HCLKEN, 22);
+ clk[CRYPTO_GATE] = nuc970_clk_gate("crypto_hclk_gate",
+ "hclk234_div",
+ REG_CLK_HCLKEN, 23);
+ clk[EMAC1_GATE] = nuc970_clk_gate("emac1_hclk_gate",
+ "hclk234_div",
+ REG_CLK_HCLKEN, 17);
+ clk[EMAC1_ECLK_DIV] = nuc970_clk_divider("emac1_eclk_div",
+ "hclk234_div",
+ REG_CLK_DIV8, 0, 8);
+ clk[EMAC1_ECLK_GATE] = nuc970_clk_gate("emac1_eclk_gate",
+ "emac1_eclk_div",
+ REG_CLK_HCLKEN, 17);
+ clk[JPEG_GATE] = nuc970_clk_gate("jpeg_hclk_gate",
+ "hclk234_div",
+ REG_CLK_HCLKEN, 29);
+ clk[JPEG_ECLK_DIV] = nuc970_clk_divider("jpeg_eclk_div",
+ "hclk234_div",
+ REG_CLK_DIV3, 28, 3);
+ clk[JPEG_ECLK_GATE] = nuc970_clk_gate("jpeg_eclk_gate",
+ "jpeg_eclk_div",
+ REG_CLK_HCLKEN, 29);
+ clk[GE2D_GATE] = nuc970_clk_gate("ge2d_hclk_gate",
+ "hclk234_div",
+ REG_CLK_HCLKEN, 28);
+ clk[GE2D_ECLK_DIV] = nuc970_clk_divider("ge2d_eclk_div",
+ "hclk234_div",
+ REG_CLK_DIV2, 28, 2);
+ clk[GE2D_ECLK_GATE] = nuc970_clk_gate("ge2d_eclk_gate",
+ "ge2d_eclk_div",
+ REG_CLK_HCLKEN, 28);
+ /* HCLK4 */
+ clk[SDH_GATE] = nuc970_clk_gate("sdh_hclk_gate",
+ "hclk234_div",
+ REG_CLK_HCLKEN, 30);
+ clk[AUDIO_GATE] = nuc970_clk_gate("audio_hclk_gate",
+ "hclk234_div",
+ REG_CLK_HCLKEN, 24);
+ clk[LCD_GATE] = nuc970_clk_gate("lcd_hclk_gate",
+ "hclk234_div",
+ REG_CLK_HCLKEN, 25);
+ clk[CAP_GATE] = nuc970_clk_gate("cap_hclk_gate",
+ "hclk234_div",
+ REG_CLK_HCLKEN, 26);
+ clk[SENSOR_GATE] = nuc970_clk_gate("sensor_hclk_gate",
+ "hclk234_div",
+ REG_CLK_HCLKEN, 27);
+ clk[EMAC0_GATE] = nuc970_clk_gate("emac0_hclk_gate",
+ "hclk234_div",
+ REG_CLK_HCLKEN, 16);
+ clk[EMAC0_ECLK_DIV] = nuc970_clk_divider("emac0_eclk_div",
+ "hclk234_div",
+ REG_CLK_DIV8, 0, 8);
+ clk[EMAC0_ECLK_GATE] = nuc970_clk_gate("emac0_eclk_gate",
+ "emac0_eclk_div",
+ REG_CLK_HCLKEN, 16);
+ /* ECLK */
+ /* USB */
+ clk[USB_APLLDIV] = nuc970_clk_divider("usb_aplldiv", "apll",
+ REG_CLK_DIV2, 0, 3);
+ clk[USB_UPLLDIV] = nuc970_clk_divider("usb_uplldiv", "upll",
+ REG_CLK_DIV2, 0, 3);
+ clk[USB_ECLK_MUX] = nuc970_clk_mux("usb_eclk_mux", REG_CLK_DIV2,
+ 3, 2, usb_sel_clks,
+ ARRAY_SIZE(usb_sel_clks));
+ clk[USB_ECLK_DIV] = nuc970_clk_divider("usb_eclk_div",
+ "usb_eclk_mux",
+ REG_CLK_DIV2, 8, 4);
+ clk[USB_ECLK_GATE] = nuc970_clk_gate("usb_eclk_gate",
+ "usb_eclk_div",
+ REG_CLK_HCLKEN, 18);
+ /* SDH */
+ clk[SDH_APLLDIV] = nuc970_clk_divider("sdh_aplldiv", "apll",
+ REG_CLK_DIV9, 0, 3);
+ clk[SDH_UPLLDIV] = nuc970_clk_divider("sdh_uplldiv", "upll",
+ REG_CLK_DIV9, 0, 3);
+ clk[SDH_ECLK_MUX] = nuc970_clk_mux("sdh_eclk_mux", REG_CLK_DIV9,
+ 3, 2, sdh_sel_clks,
+ ARRAY_SIZE(sdh_sel_clks));
+ clk[SDH_ECLK_DIV] = nuc970_clk_divider("sdh_eclk_div",
+ "sdh_eclk_mux",
+ REG_CLK_DIV9, 8, 8);
+ clk[SDH_ECLK_GATE] = nuc970_clk_gate("sdh_eclk_gate",
+ "sdh_eclk_div",
+ REG_CLK_HCLKEN, 30);
+ /* EMMC */
+ clk[EMMC_APLLDIV] = nuc970_clk_divider("emmc_aplldiv", "apll",
+ REG_CLK_DIV3, 0, 3);
+ clk[EMMC_UPLLDIV] = nuc970_clk_divider("emmc_uplldiv", "upll",
+ REG_CLK_DIV3, 0, 3);
+ clk[EMMC_ECLK_MUX] = nuc970_clk_mux("emmc_eclk_mux", REG_CLK_DIV3,
+ 3, 2, emmc_sel_clks,
+ ARRAY_SIZE(emmc_sel_clks));
+ clk[EMMC_ECLK_DIV] = nuc970_clk_divider("emmc_eclk_div",
+ "emmc_eclk_mux",
+ REG_CLK_DIV3, 8, 8);
+ clk[EMMC_ECLK_GATE] = nuc970_clk_gate("emmc_eclk_gate",
+ "emmc_eclk_div",
+ REG_CLK_HCLKEN, 22);
+ /* ADC */
+ clk[ADC_APLLDIV] = nuc970_clk_divider("adc_aplldiv", "apll",
+ REG_CLK_DIV7, 16, 3);
+ clk[ADC_UPLLDIV] = nuc970_clk_divider("adc_uplldiv", "upll",
+ REG_CLK_DIV7, 16, 3);
+ clk[ADC_ECLK_MUX] = nuc970_clk_mux("adc_eclk_mux", REG_CLK_DIV7,
+ 19, 2, adc_sel_clks,
+ ARRAY_SIZE(adc_sel_clks));
+ clk[ADC_ECLK_DIV] = nuc970_clk_divider("adc_eclk_div",
+ "adc_eclk_mux",
+ REG_CLK_DIV7, 24, 8);
+ clk[ADC_ECLK_GATE] = nuc970_clk_gate("adc_eclk_gate",
+ "adc_eclk_div",
+ REG_CLK_PCLKEN1, 24);
+ /* LCD */
+ clk[LCD_APLLDIV] = nuc970_clk_divider("lcd_aplldiv", "apll",
+ REG_CLK_DIV1, 0, 3);
+ clk[LCD_UPLLDIV] = nuc970_clk_divider("lcd_uplldiv", "upll",
+ REG_CLK_DIV1, 0, 3);
+ clk[LCD_ECLK_MUX] = nuc970_clk_mux("lcd_eclk_mux", REG_CLK_DIV1,
+ 3, 2, lcd_sel_clks,
+ ARRAY_SIZE(lcd_sel_clks));
+ clk[LCD_ECLK_DIV] = nuc970_clk_divider("lcd_eclk_div",
+ "lcd_eclk_mux",
+ REG_CLK_DIV1, 8, 8);
+ clk[LCD_ECLK_GATE] = nuc970_clk_gate("lcd_eclk_gate",
+ "lcd_eclk_div",
+ REG_CLK_HCLKEN, 25);
+ /* AUDIO */
+ clk[AUDIO_APLLDIV] = nuc970_clk_divider("audio_aplldiv", "apll",
+ REG_CLK_DIV1, 16, 3);
+ clk[AUDIO_UPLLDIV] = nuc970_clk_divider("audio_uplldiv", "upll",
+ REG_CLK_DIV1, 16, 3);
+ clk[AUDIO_ECLK_MUX] = nuc970_clk_mux("audio_eclk_mux", REG_CLK_DIV1,
+ 19, 2, audio_sel_clks,
+ ARRAY_SIZE(audio_sel_clks));
+ clk[AUDIO_ECLK_DIV] = nuc970_clk_divider("audio_eclk_div",
+ "audio_eclk_mux",
+ REG_CLK_DIV1, 24, 8);
+ clk[AUDIO_ECLK_GATE] = nuc970_clk_gate("audio_eclk_gate",
+ "audio_eclk_div",
+ REG_CLK_HCLKEN, 24);
+ /* CAP */
+ clk[CAP_APLLDIV] = nuc970_clk_divider("cap_aplldiv", "apll",
+ REG_CLK_DIV3, 16, 3);
+ clk[CAP_UPLLDIV] = nuc970_clk_divider("cap_uplldiv", "upll",
+ REG_CLK_DIV3, 16, 3);
+ clk[CAP_ECLK_MUX] = nuc970_clk_mux("cap_eclk_mux", REG_CLK_DIV3,
+ 19, 2, cap_sel_clks,
+ ARRAY_SIZE(cap_sel_clks));
+ clk[CAP_ECLK_DIV] = nuc970_clk_divider("cap_eclk_div",
+ "cap_eclk_mux",
+ REG_CLK_DIV3, 24, 4);
+ clk[CAP_ECLK_GATE] = nuc970_clk_gate("cap_eclk_gate",
+ "cap_eclk_div",
+ REG_CLK_HCLKEN, 26);
+ /* UART0 */
+ clk[UART0_APLLDIV] = nuc970_clk_divider("uart0_aplldiv",
+ "apll", REG_CLK_DIV4,
+ 0, 3);
+ clk[UART0_UPLLDIV] = nuc970_clk_divider("uart0_uplldiv", "upll",
+ REG_CLK_DIV4, 0, 3);
+ clk[UART0_ECLK_MUX] = nuc970_clk_mux("uart0_eclk_mux", REG_CLK_DIV4,
+ 3, 2, uart0_sel_clks,
+ ARRAY_SIZE(uart0_sel_clks));
+ clk[UART0_ECLK_DIV] = nuc970_clk_divider("uart0_eclk_div",
+ "uart0_eclk_mux",
+ REG_CLK_DIV4, 5, 3);
+ clk[UART0_ECLK_GATE] = nuc970_clk_gate("uart0_eclk_gate",
+ "uart0_eclk_div",
+ REG_CLK_PCLKEN0, 16);
+ /* UART1 */
+ clk[UART1_APLLDIV] = nuc970_clk_divider("uart1_aplldiv", "apll",
+ REG_CLK_DIV4, 8, 3);
+ clk[UART1_UPLLDIV] = nuc970_clk_divider("uart1_uplldiv", "upll",
+ REG_CLK_DIV4, 8, 3);
+ clk[UART1_ECLK_MUX] = nuc970_clk_mux("uart1_eclk_mux", REG_CLK_DIV4,
+ 11, 2, uart1_sel_clks,
+ ARRAY_SIZE(uart1_sel_clks));
+ clk[UART1_ECLK_DIV] = nuc970_clk_divider("uart1_eclk_div",
+ "uart1_eclk_mux",
+ REG_CLK_DIV4, 13, 3);
+ clk[UART1_ECLK_GATE] = nuc970_clk_gate("uart1_eclk_gate",
+ "uart1_eclk_div",
+ REG_CLK_PCLKEN0, 17);
+ /* UART2 */
+ clk[UART2_APLLDIV] = nuc970_clk_divider("uart2_aplldiv", "apll",
+ REG_CLK_DIV4, 16, 3);
+ clk[UART2_UPLLDIV] = nuc970_clk_divider("uart2_uplldiv", "upll",
+ REG_CLK_DIV4, 16, 3);
+ clk[UART2_ECLK_MUX] = nuc970_clk_mux("uart2_eclk_mux", REG_CLK_DIV4,
+ 19, 2, uart2_sel_clks,
+ ARRAY_SIZE(uart2_sel_clks));
+ clk[UART2_ECLK_DIV] = nuc970_clk_divider("uart2_eclk_div",
+ "uart2_eclk_mux",
+ REG_CLK_DIV4, 21, 3);
+ clk[UART2_ECLK_GATE] = nuc970_clk_gate("uart2_eclk_gate",
+ "uart2_eclk_div",
+ REG_CLK_PCLKEN0, 18);
+ /* UART3 */
+ clk[UART3_APLLDIV] = nuc970_clk_divider("uart3_aplldiv", "apll",
+ REG_CLK_DIV4, 24, 3);
+ clk[UART3_UPLLDIV] = nuc970_clk_divider("uart3_uplldiv", "upll",
+ REG_CLK_DIV4, 24, 3);
+ clk[UART3_ECLK_MUX] = nuc970_clk_mux("uart3_eclk_mux", REG_CLK_DIV4,
+ 27, 2, uart3_sel_clks,
+ ARRAY_SIZE(uart3_sel_clks));
+ clk[UART3_ECLK_DIV] = nuc970_clk_divider("uart3_eclk_div",
+ "uart3_eclk_mux",
+ REG_CLK_DIV4, 29, 3);
+ clk[UART3_ECLK_GATE] = nuc970_clk_gate("uart3_eclk_gate",
+ "uart3_eclk_div",
+ REG_CLK_PCLKEN0, 19);
+ /* UART4 */
+ clk[UART4_APLLDIV] = nuc970_clk_divider("uart4_aplldiv", "apll",
+ REG_CLK_DIV5, 0, 3);
+ clk[UART4_UPLLDIV] = nuc970_clk_divider("uart4_uplldiv", "upll",
+ REG_CLK_DIV5, 0, 3);
+ clk[UART4_ECLK_MUX] = nuc970_clk_mux("uart4_eclk_mux", REG_CLK_DIV5,
+ 3, 2, uart4_sel_clks,
+ ARRAY_SIZE(uart4_sel_clks));
+ clk[UART4_ECLK_DIV] = nuc970_clk_divider("uart4_eclk_div",
+ "uart4_eclk_mux",
+ REG_CLK_DIV5, 5, 3);
+ clk[UART4_ECLK_GATE] = nuc970_clk_gate("uart4_eclk_gate",
+ "uart4_eclk_div",
+ REG_CLK_PCLKEN0, 20);
+ /* UART5 */
+ clk[UART5_APLLDIV] = nuc970_clk_divider("uart5_aplldiv", "apll",
+ REG_CLK_DIV5, 8, 3);
+ clk[UART5_UPLLDIV] = nuc970_clk_divider("uart5_uplldiv", "upll",
+ REG_CLK_DIV5, 8, 3);
+ clk[UART5_ECLK_MUX] = nuc970_clk_mux("uart5_eclk_mux", REG_CLK_DIV5,
+ 11, 2, uart5_sel_clks,
+ ARRAY_SIZE(uart5_sel_clks));
+ clk[UART5_ECLK_DIV] = nuc970_clk_divider("uart5_eclk_div",
+ "uart5_eclk_mux",
+ REG_CLK_DIV5, 13, 3);
+ clk[UART5_ECLK_GATE] = nuc970_clk_gate("uart5_eclk_gate",
+ "uart5_eclk_div",
+ REG_CLK_PCLKEN0, 21);
+ /* UART6 */
+ clk[UART6_APLLDIV] = nuc970_clk_divider("uart6_aplldiv", "apll",
+ REG_CLK_DIV5, 16, 3);
+ clk[UART6_UPLLDIV] = nuc970_clk_divider("uart6_uplldiv", "upll",
+ REG_CLK_DIV5, 16, 3);
+ clk[UART6_ECLK_MUX] = nuc970_clk_mux("uart6_eclk_mux", REG_CLK_DIV5,
+ 19, 2, uart6_sel_clks,
+ ARRAY_SIZE(uart6_sel_clks));
+ clk[UART6_ECLK_DIV] = nuc970_clk_divider("uart6_eclk_div",
+ "uart6_eclk_mux",
+ REG_CLK_DIV5, 21, 3);
+ clk[UART6_ECLK_GATE] = nuc970_clk_gate("uart6_eclk_gate",
+ "uart6_eclk_div",
+ REG_CLK_PCLKEN0, 22);
+ /* UART7 */
+ clk[UART7_APLLDIV] = nuc970_clk_divider("uart7_aplldiv", "apll",
+ REG_CLK_DIV5, 24, 3);
+ clk[UART7_UPLLDIV] = nuc970_clk_divider("uart7_uplldiv", "upll",
+ REG_CLK_DIV5, 24, 3);
+ clk[UART7_ECLK_MUX] = nuc970_clk_mux("uart7_eclk_mux", REG_CLK_DIV5,
+ 27, 2, uart7_sel_clks,
+ ARRAY_SIZE(uart7_sel_clks));
+ clk[UART7_ECLK_DIV] = nuc970_clk_divider("uart7_eclk_div",
+ "uart7_eclk_mux",
+ REG_CLK_DIV5, 29, 3);
+ clk[UART7_ECLK_GATE] = nuc970_clk_gate("uart7_eclk_gate",
+ "uart7_eclk_div",
+ REG_CLK_PCLKEN0, 23);
+ /* UART8 */
+ clk[UART8_APLLDIV] = nuc970_clk_divider("uart8_aplldiv", "apll",
+ REG_CLK_DIV6, 0, 3);
+ clk[UART8_UPLLDIV] = nuc970_clk_divider("uart8_uplldiv", "upll",
+ REG_CLK_DIV6, 0, 3);
+ clk[UART8_ECLK_MUX] = nuc970_clk_mux("uart8_eclk_mux", REG_CLK_DIV6,
+ 3, 2, uart8_sel_clks,
+ ARRAY_SIZE(uart8_sel_clks));
+ clk[UART8_ECLK_DIV] = nuc970_clk_divider("uart8_eclk_div",
+ "uart8_eclk_mux",
+ REG_CLK_DIV6, 5, 3);
+ clk[UART8_ECLK_GATE] = nuc970_clk_gate("uart8_eclk_gate",
+ "uart8_eclk_div",
+ REG_CLK_PCLKEN0, 24);
+ /* UART9 */
+ clk[UART9_APLLDIV] = nuc970_clk_divider("uart9_aplldiv", "apll",
+ REG_CLK_DIV6, 8, 3);
+ clk[UART9_UPLLDIV] = nuc970_clk_divider("uart9_uplldiv", "upll",
+ REG_CLK_DIV6, 8, 3);
+ clk[UART9_ECLK_MUX] = nuc970_clk_mux("uart9_eclk_mux", REG_CLK_DIV6,
+ 11, 2, uart9_sel_clks,
+ ARRAY_SIZE(uart9_sel_clks));
+ clk[UART9_ECLK_DIV] = nuc970_clk_divider("uart9_eclk_div",
+ "uart9_eclk_mux",
+ REG_CLK_DIV6, 13, 3);
+ clk[UART9_ECLK_GATE] = nuc970_clk_gate("uart9_eclk_gate",
+ "uart9_eclk_div",
+ REG_CLK_PCLKEN0, 25);
+ /* UART10 */
+ clk[UART10_APLLDIV] = nuc970_clk_divider("uart10_aplldiv", "apll",
+ REG_CLK_DIV6, 16, 3);
+ clk[UART10_UPLLDIV] = nuc970_clk_divider("uart10_uplldiv", "upll",
+ REG_CLK_DIV6, 16, 3);
+ clk[UART10_ECLK_MUX] = nuc970_clk_mux("uart10_eclk_mux",
+ REG_CLK_DIV6, 19, 2,
+ uart10_sel_clks,
+ ARRAY_SIZE(uart10_sel_clks));
+ clk[UART10_ECLK_DIV] = nuc970_clk_divider("uart10_eclk_div",
+ "uart10_eclk_mux",
+ REG_CLK_DIV6, 21, 3);
+ clk[UART10_ECLK_GATE] = nuc970_clk_gate("uart10_eclk_gate",
+ "uart10_eclk_div",
+ REG_CLK_PCLKEN0, 26);
+ /* SYSTEM */
+ clk[SYSTEM_APLLDIV] = nuc970_clk_divider("system_aplldiv", "apll",
+ REG_CLK_DIV0, 0, 3);
+ clk[SYSTEM_UPLLDIV] = nuc970_clk_divider("system_uplldiv", "upll",
+ REG_CLK_DIV0, 0, 3);
+ clk[SYSTEM_ECLK_MUX] = nuc970_clk_mux("system_eclk_mux",
+ REG_CLK_DIV0, 3, 2,
+ system_sel_clks,
+ ARRAY_SIZE(system_sel_clks));
+ clk[SYSTEM_ECLK_DIV] = nuc970_clk_divider("system_eclk_div",
+ "system_eclk_mux",
+ REG_CLK_DIV0, 8, 4);
+ /* GPIO */
+ clk[GPIO_ECLK_MUX] = nuc970_clk_mux("gpio_eclk_mux", REG_CLK_DIV7,
+ 7, 1, gpio_sel_clks,
+ ARRAY_SIZE(gpio_sel_clks));
+ clk[GPIO_ECLK_DIV] = nuc970_clk_divider("gpio_eclk_div",
+ "gpio_eclk_mux",
+ REG_CLK_DIV7, 0, 7);
+ clk[GPIO_ECLK_GATE] = nuc970_clk_gate("gpio_eclk_gate",
+ "gpio_eclk_div",
+ REG_CLK_PCLKEN0, 3);
+ /* KPI */
+ clk[KPI_ECLK_MUX] = nuc970_clk_mux("kpi_eclk_mux", REG_CLK_DIV7,
+ 15, 1, kpi_sel_clks,
+ ARRAY_SIZE(kpi_sel_clks));
+ clk[KPI_ECLK_DIV] = nuc970_clk_divider("kpi_eclk_div",
+ "kpi_eclk_mux",
+ REG_CLK_DIV7, 8, 7);
+ clk[KPI_ECLK_GATE] = nuc970_clk_gate("kpi_eclk_gate",
+ "kpi_eclk_div",
+ REG_CLK_PCLKEN1, 25);
+ /* ETIMER0 */
+ clk[ETIMER0_ECLK_MUX] = nuc970_clk_mux("etimer0_eclk_mux",
+ REG_CLK_DIV8, 16, 2,
+ etimer_sel_clks,
+ ARRAY_SIZE(etimer_sel_clks));
+ clk[ETIMER0_ECLK_GATE] = nuc970_clk_gate("etimer0_eclk_gate",
+ "etimer0_eclk_mux",
+ REG_CLK_PCLKEN0, 4);
+ /* ETIMER1 */
+ clk[ETIMER1_ECLK_MUX] = nuc970_clk_mux("etimer1_eclk_mux",
+ REG_CLK_DIV8, 18, 2,
+ etimer_sel_clks,
+ ARRAY_SIZE(etimer_sel_clks));
+ clk[ETIMER1_ECLK_GATE] = nuc970_clk_gate("etimer1_eclk_gate",
+ "etimer1_eclk_mux",
+ REG_CLK_PCLKEN0, 5);
+ /* ETIMER2 */
+ clk[ETIMER2_ECLK_MUX] = nuc970_clk_mux("etimer2_eclk_mux",
+ REG_CLK_DIV8, 20, 2,
+ etimer_sel_clks,
+ ARRAY_SIZE(etimer_sel_clks));
+ clk[ETIMER2_ECLK_GATE] = nuc970_clk_gate("etimer2_eclk_gate",
+ "etimer2_eclk_mux",
+ REG_CLK_PCLKEN0, 6);
+ /* ETIMER3 */
+ clk[ETIMER3_ECLK_MUX] = nuc970_clk_mux("etimer3_eclk_mux",
+ REG_CLK_DIV8, 22, 2,
+ etimer_sel_clks,
+ ARRAY_SIZE(etimer_sel_clks));
+ clk[ETIMER3_ECLK_GATE] = nuc970_clk_gate("etimer3_eclk_gate",
+ "etimer3_eclk_mux",
+ REG_CLK_PCLKEN0, 7);
+ /* WWDT */
+ clk[WWDT_ECLK_MUX] = nuc970_clk_mux("wwdt_eclk_mux", REG_CLK_DIV8,
+ 10, 2, wwdt_sel_clks,
+ ARRAY_SIZE(wwdt_sel_clks));
+ clk[WWDT_ECLK_GATE] = nuc970_clk_gate("wwdt_eclk_gate",
+ "wwdt_eclk_mux",
+ REG_CLK_PCLKEN0, 1);
+ /* WDT */
+ clk[WDT_ECLK_MUX] = nuc970_clk_mux("wdt_eclk_mux", REG_CLK_DIV8,
+ 8, 2, wwdt_sel_clks,
+ ARRAY_SIZE(wwdt_sel_clks));
+ clk[WDT_ECLK_GATE] = nuc970_clk_gate("wdt_eclk_gate",
+ "wdt_eclk_mux",
+ REG_CLK_PCLKEN0, 0);
+ /* SMARTCARD */
+ clk[SMC0_ECLK_DIV] = nuc970_clk_divider("smc0_eclk_div", "xin",
+ REG_CLK_DIV6, 24, 4);
+ clk[SMC0_ECLK_GATE] = nuc970_clk_gate("smc0_eclk_gate",
+ "smc0_eclk_div",
+ REG_CLK_PCLKEN1, 12);
+ clk[SMC1_ECLK_DIV] = nuc970_clk_divider("smc1_eclk_div", "xin",
+ REG_CLK_DIV6, 28, 4);
+ clk[SMC1_ECLK_GATE] = nuc970_clk_gate("smc1_eclk_gate",
+ "smc1_eclk_div",
+ REG_CLK_PCLKEN1, 13);
+ /* PCLK */
+ clk[PCLK_DIV] = nuc970_clk_divider("pclk_div", "hclk1_div",
+ REG_CLK_DIV0, 24, 4);
+ clk[PCLK4096_DIV] = nuc970_clk_fixed_factor("pclk4096_div",
+ "pclk_div", 1, 4096);
+ clk[I2C0_GATE] = nuc970_clk_gate("i2c0_gate", "pclk_div",
+ REG_CLK_PCLKEN1, 0);
+ clk[I2C1_GATE] = nuc970_clk_gate("i2c1_gate", "pclk_div",
+ REG_CLK_PCLKEN1, 1);
+ clk[SPI0_GATE] = nuc970_clk_gate("spi0_gate", "pclk_div",
+ REG_CLK_PCLKEN1, 4);
+ clk[SPI1_GATE] = nuc970_clk_gate("spi1_gate", "pclk_div",
+ REG_CLK_PCLKEN1, 5);
+ clk[UART0_GATE] = nuc970_clk_gate("uart0_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 16);
+ clk[UART1_GATE] = nuc970_clk_gate("uart1_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 17);
+ clk[UART2_GATE] = nuc970_clk_gate("uart2_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 18);
+ clk[UART3_GATE] = nuc970_clk_gate("uart3_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 19);
+ clk[UART4_GATE] = nuc970_clk_gate("uart4_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 20);
+ clk[UART5_GATE] = nuc970_clk_gate("uart5_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 21);
+ clk[UART6_GATE] = nuc970_clk_gate("uart6_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 22);
+ clk[UART7_GATE] = nuc970_clk_gate("uart7_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 23);
+ clk[UART8_GATE] = nuc970_clk_gate("uart8_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 24);
+ clk[UART9_GATE] = nuc970_clk_gate("uart9_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 25);
+ clk[UART10_GATE] = nuc970_clk_gate("uart10_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 26);
+ clk[WDT_GATE] = nuc970_clk_gate("wdt_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 0);
+ clk[WWDT_GATE] = nuc970_clk_gate("wwdt_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 1);
+ clk[RTC_GATE] = nuc970_clk_gate("rtc_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 2);
+ clk[GPIO_GATE] = nuc970_clk_gate("gpio_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 3);
+ clk[ADC_GATE] = nuc970_clk_gate("adc_gate", "pclk_div",
+ REG_CLK_PCLKEN1, 24);
+ clk[KPI_GATE] = nuc970_clk_gate("kpi_gate", "pclk_div",
+ REG_CLK_PCLKEN1, 25);
+ clk[MTPC_GATE] = nuc970_clk_gate("mtpc_gate", "pclk_div",
+ REG_CLK_PCLKEN1, 26);
+ clk[PWM_GATE] = nuc970_clk_gate("pwm_gate", "pclk_div",
+ REG_CLK_PCLKEN1, 27);
+ clk[ETIMER0_GATE] = nuc970_clk_gate("etimer0_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 4);
+ clk[ETIMER1_GATE] = nuc970_clk_gate("etimer1_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 5);
+ clk[ETIMER2_GATE] = nuc970_clk_gate("etimer2_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 6);
+ clk[ETIMER3_GATE] = nuc970_clk_gate("etimer3_gate", "pclk_div",
+ REG_CLK_PCLKEN0, 7);
+ clk[CAN0_GATE] = nuc970_clk_gate("can0_gate", "pclk_div",
+ REG_CLK_PCLKEN1, 8);
+ clk[CAN1_GATE] = nuc970_clk_gate("can1_gate", "pclk_div",
+ REG_CLK_PCLKEN1, 9);
+ clk[TIMER0_GATE] = nuc970_clk_gate("timer0_gate", "xin",
+ REG_CLK_PCLKEN0, 8);
+ clk[TIMER1_GATE] = nuc970_clk_gate("timer1_gate", "xin",
+ REG_CLK_PCLKEN0, 9);
+ clk[TIMER2_GATE] = nuc970_clk_gate("timer2_gate", "xin",
+ REG_CLK_PCLKEN0, 10);
+ clk[TIMER3_GATE] = nuc970_clk_gate("timer3_gate", "xin",
+ REG_CLK_PCLKEN0, 11);
+ clk[TIMER4_GATE] = nuc970_clk_gate("timer4_gate", "xin",
+ REG_CLK_PCLKEN0, 12);
+ clk[SMC0_GATE] = nuc970_clk_gate("smc0_gate", "pclk_div",
+ REG_CLK_PCLKEN1, 12);
+ clk[SMC1_GATE] = nuc970_clk_gate("smc1_gate", "pclk_div",
+ REG_CLK_PCLKEN1, 13);
+
+ for (i = 0; i < ARRAY_SIZE(clk); i++)
+ if (IS_ERR(clk[i]))
+ pr_err("nuc970 clk %d: register failed with %ld\n",
+ i, PTR_ERR(clk[i]));
+
+ clk_data.clks = clk;
+ clk_data.clk_num = ARRAY_SIZE(clk);
+
+ ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+ if (ret)
+ pr_err("Failed to register OF clock provider\n");
+
+ /* Register clock device */
+ clk_register_clkdev(clk[TIMER0_GATE], "timer0", NULL);
+ clk_register_clkdev(clk[TIMER1_GATE], "timer1", NULL);
+ clk_register_clkdev(clk[PCLK4096_DIV], "pclk4096_div", NULL);
+ clk_register_clkdev(clk[XIN], "xin", NULL);
+ clk_register_clkdev(clk[XIN32K], "xin32k", NULL);
+ clk_register_clkdev(clk[APLL], "apll", NULL);
+ clk_register_clkdev(clk[UPLL], "upll", NULL);
+ clk_register_clkdev(clk[SYS_MUX], "sysmux", NULL);
+ clk_register_clkdev(clk[SYS_DIV], "sysdiv", NULL);
+ clk_register_clkdev(clk[XIN128_DIV], "xin128div", NULL);
+ /* CPU */
+ clk_register_clkdev(clk[CPU_DIV], "cpudiv", NULL);
+ clk_register_clkdev(clk[CPU_GATE], "cpu", NULL);
+ /* HCLK1 */
+ clk_register_clkdev(clk[HCLK_GATE], "hclk", NULL);
+ clk_register_clkdev(clk[SRAM_GATE], "sram", NULL);
+ clk_register_clkdev(clk[HCLK1_DIV], "hclk1div", NULL);
+ clk_register_clkdev(clk[DDR_GATE], "ddr_hclk", NULL);
+ clk_register_clkdev(clk[GDMA_GATE], "gdma_hclk", NULL);
+ clk_register_clkdev(clk[EBI_GATE], "ebi_hclk", NULL);
+ clk_register_clkdev(clk[TIC_GATE], "tic_hclk", NULL);
+ /* HCLK234 */
+ clk_register_clkdev(clk[HCLKN_DIV], "hclkndiv", NULL);
+ clk_register_clkdev(clk[DRAM_GATE], "dram", NULL);
+ clk_register_clkdev(clk[HCLK234_DIV], "hclk234div", NULL);
+ /* HCLK3 */
+ clk_register_clkdev(clk[USBH_GATE], "usbh_hclk", NULL);
+ clk_register_clkdev(clk[EMAC1_GATE], "emac1_hclk", NULL);
+ clk_register_clkdev(clk[EMAC1_ECLK_DIV], "emac1_eclk_div", NULL);
+ clk_register_clkdev(clk[EMAC1_ECLK_GATE], "emac1_eclk", NULL);
+ clk_register_clkdev(clk[USBD_GATE], "usbd_hclk", NULL);
+ clk_register_clkdev(clk[FMI_GATE], "fmi_hclk", NULL);
+ clk_register_clkdev(clk[NAND_GATE], "nand_hclk", NULL);
+ clk_register_clkdev(clk[EMMC_GATE], "emmc_hclk", NULL);
+ clk_register_clkdev(clk[CRYPTO_GATE], "crypto_hclk", NULL);
+ clk_register_clkdev(clk[JPEG_GATE], "jpeg_hclk", NULL);
+ clk_register_clkdev(clk[JPEG_ECLK_DIV], "jpeg_eclk_div", NULL);
+ clk_register_clkdev(clk[JPEG_ECLK_GATE], "jpeg_eclk", NULL);
+ clk_register_clkdev(clk[GE2D_GATE], "ge2d_hclk", NULL);
+ clk_register_clkdev(clk[GE2D_ECLK_DIV], "ge2d_eclk_div", NULL);
+ clk_register_clkdev(clk[GE2D_ECLK_GATE], "ge2d_eclk", NULL);
+ /* HCLK4 */
+ clk_register_clkdev(clk[EMAC0_GATE], "emac0_hclk", NULL);
+ clk_register_clkdev(clk[EMAC0_ECLK_DIV], "emac0_eclk_div", NULL);
+ clk_register_clkdev(clk[EMAC0_ECLK_GATE], "emac0_eclk", NULL);
+ clk_register_clkdev(clk[SDH_GATE], "sdh_hclk", NULL);
+ clk_register_clkdev(clk[AUDIO_GATE], "audio_hclk", NULL);
+ clk_register_clkdev(clk[LCD_GATE], "lcd_hclk", NULL);
+ clk_register_clkdev(clk[SENSOR_GATE], "sensor_hclk", NULL);
+ clk_register_clkdev(clk[CAP_GATE], "cap_hclk", NULL);
+ /* ECLK */
+ clk_register_clkdev(clk[LCD_APLLDIV], "lcd_aplldiv", NULL);
+ clk_register_clkdev(clk[LCD_UPLLDIV], "lcd_uplldiv", NULL);
+ clk_register_clkdev(clk[LCD_ECLK_MUX], "lcd_eclk_mux", NULL);
+ clk_register_clkdev(clk[LCD_ECLK_DIV], "lcd_eclk_div", NULL);
+ clk_register_clkdev(clk[LCD_ECLK_GATE], "lcd_eclk", NULL);
+ clk_register_clkdev(clk[AUDIO_APLLDIV], "audio_aplldiv", NULL);
+ clk_register_clkdev(clk[AUDIO_UPLLDIV], "audio_uplldiv", NULL);
+ clk_register_clkdev(clk[AUDIO_ECLK_MUX], "audio_eclk_mux", NULL);
+ clk_register_clkdev(clk[AUDIO_ECLK_DIV], "audio_eclk_div", NULL);
+ clk_register_clkdev(clk[AUDIO_ECLK_GATE], "audio_eclk", NULL);
+ clk_register_clkdev(clk[USB_APLLDIV], "usb_aplldiv", NULL);
+ clk_register_clkdev(clk[USB_UPLLDIV], "usb_uplldiv", NULL);
+ clk_register_clkdev(clk[USB_ECLK_MUX], "usb_eclk_mux", NULL);
+ clk_register_clkdev(clk[USB_ECLK_DIV], "usb_eclk_div", NULL);
+ clk_register_clkdev(clk[USB_ECLK_GATE], "usb_eclk", NULL);
+ clk_register_clkdev(clk[SDH_APLLDIV], "sdh_aplldiv", NULL);
+ clk_register_clkdev(clk[SDH_UPLLDIV], "sdh_uplldiv", NULL);
+ clk_register_clkdev(clk[SDH_ECLK_MUX], "sdh_eclk_mux", NULL);
+ clk_register_clkdev(clk[SDH_ECLK_DIV], "sdh_eclk_div", NULL);
+ clk_register_clkdev(clk[SDH_ECLK_GATE], "sdh_eclk", NULL);
+ clk_register_clkdev(clk[EMMC_APLLDIV], "emmc_aplldiv", NULL);
+ clk_register_clkdev(clk[EMMC_UPLLDIV], "emmc_uplldiv", NULL);
+ clk_register_clkdev(clk[EMMC_ECLK_MUX], "emmc_eclk_mux", NULL);
+ clk_register_clkdev(clk[EMMC_ECLK_DIV], "emmc_eclk_div", NULL);
+ clk_register_clkdev(clk[EMMC_ECLK_GATE], "emmc_eclk", NULL);
+ clk_register_clkdev(clk[ADC_APLLDIV], "adc_aplldiv", NULL);
+ clk_register_clkdev(clk[ADC_UPLLDIV], "adc_uplldiv", NULL);
+ clk_register_clkdev(clk[ADC_ECLK_MUX], "adc_eclk_mux", NULL);
+ clk_register_clkdev(clk[ADC_ECLK_DIV], "adc_eclk_div", NULL);
+ clk_register_clkdev(clk[ADC_ECLK_GATE], "adc_eclk", NULL);
+ clk_register_clkdev(clk[CAP_APLLDIV], "cap_aplldiv", NULL);
+ clk_register_clkdev(clk[CAP_UPLLDIV], "cap_uplldiv", NULL);
+ clk_register_clkdev(clk[CAP_ECLK_MUX], "cap_eclk_mux", NULL);
+ clk_register_clkdev(clk[CAP_ECLK_DIV], "cap_eclk_div", NULL);
+ clk_register_clkdev(clk[CAP_ECLK_GATE], "cap_eclk", NULL);
+ clk_register_clkdev(clk[UART0_APLLDIV], "uart0_aplldiv", NULL);
+ clk_register_clkdev(clk[UART0_UPLLDIV], "uart0_uplldiv", NULL);
+ clk_register_clkdev(clk[UART0_ECLK_MUX], "uart0_eclk_mux", NULL);
+ clk_register_clkdev(clk[UART0_ECLK_DIV], "uart0_eclk_div", NULL);
+ clk_register_clkdev(clk[UART0_ECLK_GATE], "uart0_eclk", NULL);
+ clk_register_clkdev(clk[UART1_APLLDIV], "uart1_aplldiv", NULL);
+ clk_register_clkdev(clk[UART1_UPLLDIV], "uart1_uplldiv", NULL);
+ clk_register_clkdev(clk[UART1_ECLK_MUX], "uart1_eclk_mux", NULL);
+ clk_register_clkdev(clk[UART1_ECLK_DIV], "uart1_eclk_div", NULL);
+ clk_register_clkdev(clk[UART1_ECLK_GATE], "uart1_eclk", NULL);
+ clk_register_clkdev(clk[UART2_APLLDIV], "uart2_aplldiv", NULL);
+ clk_register_clkdev(clk[UART2_UPLLDIV], "uart2_uplldiv", NULL);
+ clk_register_clkdev(clk[UART2_ECLK_MUX], "uart2_eclk_mux", NULL);
+ clk_register_clkdev(clk[UART2_ECLK_DIV], "uart2_eclk_div", NULL);
+ clk_register_clkdev(clk[UART2_ECLK_GATE], "uart2_eclk", NULL);
+ clk_register_clkdev(clk[UART3_APLLDIV], "uart3_aplldiv", NULL);
+ clk_register_clkdev(clk[UART3_UPLLDIV], "uart3_uplldiv", NULL);
+ clk_register_clkdev(clk[UART3_ECLK_MUX], "uart3_eclk_mux", NULL);
+ clk_register_clkdev(clk[UART3_ECLK_DIV], "uart3_eclk_div", NULL);
+ clk_register_clkdev(clk[UART3_ECLK_GATE], "uart3_eclk", NULL);
+ clk_register_clkdev(clk[UART4_APLLDIV], "uart4_aplldiv", NULL);
+ clk_register_clkdev(clk[UART4_UPLLDIV], "uart4_uplldiv", NULL);
+ clk_register_clkdev(clk[UART4_ECLK_MUX], "uart4_eclk_mux", NULL);
+ clk_register_clkdev(clk[UART4_ECLK_DIV], "uart4_eclk_div", NULL);
+ clk_register_clkdev(clk[UART4_ECLK_GATE], "uart4_eclk", NULL);
+ clk_register_clkdev(clk[UART5_APLLDIV], "uart5_aplldiv", NULL);
+ clk_register_clkdev(clk[UART5_UPLLDIV], "uart5_uplldiv", NULL);
+ clk_register_clkdev(clk[UART5_ECLK_MUX], "uart5_eclk_mux", NULL);
+ clk_register_clkdev(clk[UART5_ECLK_DIV], "uart5_eclk_div", NULL);
+ clk_register_clkdev(clk[UART5_ECLK_GATE], "uart5_eclk", NULL);
+ clk_register_clkdev(clk[UART6_APLLDIV], "uart6_aplldiv", NULL);
+ clk_register_clkdev(clk[UART6_UPLLDIV], "uart6_uplldiv", NULL);
+ clk_register_clkdev(clk[UART6_ECLK_MUX], "uart6_eclk_mux", NULL);
+ clk_register_clkdev(clk[UART6_ECLK_DIV], "uart6_eclk_div", NULL);
+ clk_register_clkdev(clk[UART6_ECLK_GATE], "uart6_eclk", NULL);
+ clk_register_clkdev(clk[UART7_APLLDIV], "uart7_aplldiv", NULL);
+ clk_register_clkdev(clk[UART7_UPLLDIV], "uart7_uplldiv", NULL);
+ clk_register_clkdev(clk[UART7_ECLK_MUX], "uart7_eclk_mux", NULL);
+ clk_register_clkdev(clk[UART7_ECLK_DIV], "uart7_eclk_div", NULL);
+ clk_register_clkdev(clk[UART7_ECLK_GATE], "uart7_eclk", NULL);
+ clk_register_clkdev(clk[UART8_APLLDIV], "uart8_aplldiv", NULL);
+ clk_register_clkdev(clk[UART8_UPLLDIV], "uart8_uplldiv", NULL);
+ clk_register_clkdev(clk[UART8_ECLK_MUX], "uart8_eclk_mux", NULL);
+ clk_register_clkdev(clk[UART8_ECLK_DIV], "uart8_eclk_div", NULL);
+ clk_register_clkdev(clk[UART8_ECLK_GATE], "uart8_eclk", NULL);
+ clk_register_clkdev(clk[UART9_APLLDIV], "uart9_aplldiv", NULL);
+ clk_register_clkdev(clk[UART9_UPLLDIV], "uart9_uplldiv", NULL);
+ clk_register_clkdev(clk[UART9_ECLK_MUX], "uart9_eclk_mux", NULL);
+ clk_register_clkdev(clk[UART9_ECLK_DIV], "uart9_eclk_div", NULL);
+ clk_register_clkdev(clk[UART9_ECLK_GATE], "uart9_eclk", NULL);
+ clk_register_clkdev(clk[UART10_APLLDIV], "uart10_aplldiv", NULL);
+ clk_register_clkdev(clk[UART10_UPLLDIV], "uart10_uplldiv", NULL);
+ clk_register_clkdev(clk[UART10_ECLK_MUX], "uart10_eclk_mux", NULL);
+ clk_register_clkdev(clk[UART10_ECLK_DIV], "uart10_eclk_div", NULL);
+ clk_register_clkdev(clk[UART10_ECLK_GATE], "uart10_eclk", NULL);
+ clk_register_clkdev(clk[SYSTEM_APLLDIV], "system_aplldiv", NULL);
+ clk_register_clkdev(clk[SYSTEM_UPLLDIV], "system_uplldiv", NULL);
+ clk_register_clkdev(clk[SYSTEM_ECLK_MUX], "system_eclk_mux", NULL);
+ clk_register_clkdev(clk[SYSTEM_ECLK_DIV], "system_eclk_div", NULL);
+ clk_register_clkdev(clk[SYSTEM_ECLK_GATE], "system_eclk", NULL);
+ clk_register_clkdev(clk[GPIO_ECLK_MUX], "gpio_eclk_mux", NULL);
+ clk_register_clkdev(clk[GPIO_ECLK_DIV], "gpio_eclk_div", NULL);
+ clk_register_clkdev(clk[GPIO_ECLK_GATE], "gpio_eclk", NULL);
+ clk_register_clkdev(clk[KPI_ECLK_MUX], "kpi_eclk_mux", NULL);
+ clk_register_clkdev(clk[KPI_ECLK_DIV], "kpi_eclk_div", NULL);
+ clk_register_clkdev(clk[KPI_ECLK_GATE], "kpi_eclk", NULL);
+ clk_register_clkdev(clk[ETIMER0_ECLK_MUX], "etmr0_eclk_mux", NULL);
+ clk_register_clkdev(clk[ETIMER0_ECLK_GATE], "etmr0_eclk", NULL);
+ clk_register_clkdev(clk[ETIMER1_ECLK_MUX], "etmr1_eclk_mux", NULL);
+ clk_register_clkdev(clk[ETIMER1_ECLK_GATE], "etmr1_eclk", NULL);
+ clk_register_clkdev(clk[ETIMER2_ECLK_MUX], "etmr2_eclk_mux", NULL);
+ clk_register_clkdev(clk[ETIMER2_ECLK_GATE], "etmr2_eclk", NULL);
+ clk_register_clkdev(clk[ETIMER3_ECLK_MUX], "etmr3_eclk_mux", NULL);
+ clk_register_clkdev(clk[ETIMER3_ECLK_GATE], "etmr3_eclk", NULL);
+ clk_register_clkdev(clk[WWDT_ECLK_MUX], "wwdt_eclk_mux", NULL);
+ clk_register_clkdev(clk[WWDT_ECLK_GATE], "wwdt_eclk", NULL);
+ clk_register_clkdev(clk[WDT_ECLK_MUX], "wdt_eclk_mux", NULL);
+ clk_register_clkdev(clk[WDT_ECLK_GATE], "wdt_eclk", NULL);
+ clk_register_clkdev(clk[SMC0_ECLK_DIV], "smc0_eclk_div", NULL);
+ clk_register_clkdev(clk[SMC0_ECLK_GATE], "smc0_eclk", NULL);
+ clk_register_clkdev(clk[SMC1_ECLK_DIV], "smc1_eclk_div", NULL);
+ clk_register_clkdev(clk[SMC1_ECLK_GATE], "smc1_eclk", NULL);
+ /* PCLK */
+ clk_register_clkdev(clk[PCLK_DIV], "pclkdiv", NULL);
+ clk_register_clkdev(clk[RTC_GATE], "rtc", NULL);
+ clk_register_clkdev(clk[I2C0_GATE], "i2c0", NULL);
+ clk_register_clkdev(clk[I2C1_GATE], "i2c1", NULL);
+ clk_register_clkdev(clk[SPI0_GATE], "spi0", NULL);
+ clk_register_clkdev(clk[SPI1_GATE], "spi1", NULL);
+ clk_register_clkdev(clk[UART0_GATE], "uart0", NULL);
+ clk_register_clkdev(clk[UART1_GATE], "uart1", NULL);
+ clk_register_clkdev(clk[UART2_GATE], "uart2", NULL);
+ clk_register_clkdev(clk[UART3_GATE], "uart3", NULL);
+ clk_register_clkdev(clk[UART4_GATE], "uart4", NULL);
+ clk_register_clkdev(clk[UART5_GATE], "uart5", NULL);
+ clk_register_clkdev(clk[UART6_GATE], "uart6", NULL);
+ clk_register_clkdev(clk[UART7_GATE], "uart7", NULL);
+ clk_register_clkdev(clk[UART8_GATE], "uart8", NULL);
+ clk_register_clkdev(clk[UART9_GATE], "uart9", NULL);
+ clk_register_clkdev(clk[UART10_GATE], "uart10", NULL);
+ clk_register_clkdev(clk[WDT_GATE], "wdt", NULL);
+ clk_register_clkdev(clk[WWDT_GATE], "wwdt", NULL);
+ clk_register_clkdev(clk[GPIO_GATE], "gpio", NULL);
+ clk_register_clkdev(clk[SMC0_GATE], "smc0", NULL);
+ clk_register_clkdev(clk[SMC1_GATE], "smc1", NULL);
+ clk_register_clkdev(clk[ADC_GATE], "adc", NULL);
+ clk_register_clkdev(clk[KPI_GATE], "kpi", NULL);
+ clk_register_clkdev(clk[MTPC_GATE], "mtpc", NULL);
+ clk_register_clkdev(clk[PWM_GATE], "pwm", NULL);
+ clk_register_clkdev(clk[ETIMER0_GATE], "etimer0", NULL);
+ clk_register_clkdev(clk[ETIMER1_GATE], "etimer1", NULL);
+ clk_register_clkdev(clk[ETIMER2_GATE], "etimer2", NULL);
+ clk_register_clkdev(clk[ETIMER3_GATE], "etimer3", NULL);
+ clk_register_clkdev(clk[TIMER2_GATE], "timer2", NULL);
+ clk_register_clkdev(clk[TIMER3_GATE], "timer3", NULL);
+ clk_register_clkdev(clk[TIMER4_GATE], "timer4", NULL);
+ clk_register_clkdev(clk[CAN0_GATE], "can0", NULL);
+ clk_register_clkdev(clk[CAN1_GATE], "can1", NULL);
+
+ /* enable some important clocks */
+ clk_prepare_enable(clk_get(NULL, "cpu"));
+ clk_prepare_enable(clk_get(NULL, "hclk"));
+ clk_prepare_enable(clk_get(NULL, "sram"));
+ clk_prepare_enable(clk_get(NULL, "dram"));
+ clk_prepare_enable(clk_get(NULL, "ddr_hclk"));
+}
+
+CLK_OF_DECLARE(nuc970_clk, "nuvoton,nuc970-clk", nuc970_clocks_init);
diff --git a/drivers/clk/nuc900/clk-upll.c b/drivers/clk/nuc900/clk-upll.c
new file mode 100644
index 0000000..563fdcd
--- /dev/null
+++ b/drivers/clk/nuc900/clk-upll.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2016 Wan Zongshun <mcuo...@gmail.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+
+#include "clk-ccf.h"
+
+struct clk_upll {
+ struct clk_hw hw;
+ void __iomem *base;
+};
+
+#define to_clk_upll(clk) (container_of(clk, struct clk_upll, clk))
+
+static unsigned long clk_upll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_upll *pll = to_clk_upll(hw);
+ unsigned long rate;
+ unsigned long reg = readl(pll->base) & 0x0FFFFFFF;
+
+ if (parent_rate != 12000000)
+ return 0;
+
+ switch (reg) {
+ case 0x15:
+ rate = 264000000;
+ break;
+ case 0x18:
+ rate = 300000000;
+ break;
+ default:
+ rate = 264000000;
+ break;
+ }
+
+ return rate;
+}
+
+static struct clk_ops clk_upll_ops = {
+ .recalc_rate = clk_upll_recalc_rate,
+};
+
+struct clk *nuc970_clk_upll(const char *name, const char *parent,
+ void __iomem *base)
+{
+ struct clk_upll *pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ pll->base = base;
+
+ init.name = name;
+ init.ops = &clk_upll_ops;
+ init.flags = 0;
+ init.parent_names = &parent;
+ init.num_parents = 1;
+
+ pll->hw.init = &init;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
--
2.7.4

Paul Gortmaker

unread,
Jul 10, 2016, 6:00:07 PM7/10/16
to
Why do you include module.h when I don't see anything modular in
this driver?

Paul.
--

> +#include <linux/init.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqdomain.h>
> +#include <linux/io.h>
> +#include <linux/ioport.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +
> +#include <asm/exception.h>
> +#include <asm/hardirq.h>
> +

[...]

Arnd Bergmann

unread,
Jul 10, 2016, 6:10:05 PM7/10/16
to
On Sunday, July 10, 2016 3:27:21 PM CEST Wan Zongshun wrote:
> +
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/clkdev.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/gpio.h>
> +#include <linux/init.h>
> +#include <linux/reboot.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/sys_soc.h>
> +#include <linux/semaphore.h>
> +
> +#include <asm/system_misc.h>
> +#include <asm/mach/arch.h>
> +
> +static void __init nuc900_machine_init(void)
> +{
> + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
> +}


This is the default for .init_machine, so you can leave the function
undefined.

> +static const char *nuc900_dt_compat[] __initconst = {
> + "nuvoton,nuc970",
> + NULL,
> +};
> +
> +DT_MACHINE_START(nuc900_dt, "Nuvoton NUC900 (Device Tree Support)")
> + .init_machine = nuc900_machine_init,
> + .dt_compat = nuc900_dt_compat,
> +MACHINE_END
>

After that, you can probably remove most of the #include statements.
The file is still needed for now, until you move to ARCH_MULTIPLATFORM,
at which point the entire machine descriptor is redundant.

Arnd

Wan Zongshun

unread,
Jul 10, 2016, 10:20:05 PM7/10/16
to
Okay, I can delete it.

>
> Paul.
> --
>
>> +#include <linux/init.h>
>> +#include <linux/irq.h>
>> +#include <linux/irqchip.h>
>> +#include <linux/irqdomain.h>
>> +#include <linux/io.h>
>> +#include <linux/ioport.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_irq.h>
>> +
>> +#include <asm/exception.h>
>> +#include <asm/hardirq.h>
>> +
>
> [...]
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-ar...@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
>

Arnd Bergmann

unread,
Jul 11, 2016, 4:10:06 AM7/11/16
to
On Sunday, July 10, 2016 3:27:26 PM CEST Wan Zongshun wrote:
> + ret = of_property_read_string(np, "compatible", &soc_dev_attr->soc_id);
> + if (ret)
> return -EINVAL;
> +
> + soc_dev_attr->machine = "NUC900EVB";
> + soc_dev_attr->family = "NUC900";
> + soc_dev = soc_device_register(soc_dev_attr);
> + if (IS_ERR(soc_dev)) {
> + kfree(soc_dev_attr);
> + return -ENODEV;
> + }
> +
> + ret = regmap_read(syscon_regmap, GCR_CHIPID, &nuc900_chipid);
> + if (ret)
> + return -ENODEV;
> +
> + device_create_file(soc_device_to_device(soc_dev), &nuc900_chipid_attr);
> + device_create_file(soc_device_to_device(soc_dev), &nuc900_version_attr);
> +
> + dev_info(&pdev->dev, "Nuvoton Chip ID: 0x%x, Version ID:0x%x\n",
> + nuc900_chipid & GCR_CHIPID_MASK,
> + (nuc900_chipid >> 24) & 0xff);

I'm still a bit unsure about the set of attributes here.

- The "soc_id" is read from the device tree from the field that contains
the board name, I think for consistency you should try to map the
GCR_CHIPID to the name of the SoC and assign that here

- The "machine" is hardcoded to "NUC900EVB", which in turn looks like
a particular board but not the one you are running on. Maybe read
that from the DT instead?

- The "revision" is not filled at all, I would suggest using something
derived from the GCR_CHIPID register here

- you have two nonstandard attributes "chipid" and "version", which
I'd hope to avoid -- the set of standard attributes is supposed to
give enough information about the machine, and platform independent
user space will never read those.

Arnd

Wan Zongshun

unread,
Jul 11, 2016, 5:10:06 AM7/11/16
to
I will try to get chipid and map it to soc name like: “nuc970”, "nuc910".

And I will set this soc name to soc_id, ok?

>
> - The "machine" is hardcoded to "NUC900EVB", which in turn looks like
> a particular board but not the one you are running on. Maybe read
> that from the DT instead?

Should I read nuc970-evb.dts's "model" or "compatible" properties?

Wan ZongShun

unread,
Jul 11, 2016, 6:30:07 AM7/11/16
to
2016-07-11 18:24 GMT+08:00 Arnd Bergmann <ar...@arndb.de>:
> Ok.

Maybe I also can set versionid as soc name partly, like
nuc970-version1,nuc970-version2? and then set the to soc_id, make
sense?


>
>> > - The "machine" is hardcoded to "NUC900EVB", which in turn looks like
>> > a particular board but not the one you are running on. Maybe read
>> > that from the DT instead?
>>
>> Should I read nuc970-evb.dts's "model" or "compatible" properties?
>
> I think "model" is best here, but see what the others do.
>
> Arnd
>



--
---
Vincent Wan(Zongshun)
www.mcuos.com

Arnd Bergmann

unread,
Jul 11, 2016, 6:30:07 AM7/11/16
to
Ok.

> > - The "machine" is hardcoded to "NUC900EVB", which in turn looks like
> > a particular board but not the one you are running on. Maybe read
> > that from the DT instead?
>
> Should I read nuc970-evb.dts's "model" or "compatible" properties?

Arnd Bergmann

unread,
Jul 11, 2016, 6:40:11 AM7/11/16
to
I didn't exactly understand what the suggestion is, maybe send that
as code so I see what you mean.

Arnd

Arnd Bergmann

unread,
Jul 11, 2016, 11:50:07 AM7/11/16
to
On Sunday, July 10, 2016 3:27:22 PM CEST Wan Zongshun wrote:
> +
> +#if !defined(CONFIG_SOC_NUC900)
> #define NR_IRQS (IRQ_ADC+1)
> +#else
> +#define NR_IRQS 62
> +#endif
>

The Kconfig symbols are a bit confusing here: CONFIG_SOC_NUC900
controls the compilation of the soc_device driver, but I guess
what you actually mean here is CONFIG_SOC_NUC970, which is the
support for the actual chip.

Maybe rename the former to something less confusing and change
this to CONFIG_SOC_NUC970?

Ideally, this should just go away once we use SPARSE_IRQ.

Arnd

Arnd Bergmann

unread,
Jul 11, 2016, 12:10:07 PM7/11/16
to
On Sunday, July 10, 2016 3:27:21 PM CEST Wan Zongshun wrote:
> +ifeq ($(CONFIG_SOC_NUC970),)
> obj-y := irq.o time.o mfp.o gpio.o clock.o
> obj-y += clksel.o dev.o cpu.o
> +endif
> # W90X900 CPU support files

When mfp.o is disabled like this, I get a link error in two drivers
using the exported interface:

ERROR: "mfp_set_groupg" [drivers/spi/spi-nuc900.ko] undefined!
ERROR: "mfp_set_groupi" [drivers/input/keyboard/w90p910_keypad.ko] undefined!

Any idea for a better migration strategy?

Arnd

Michael Turquette

unread,
Jul 11, 2016, 6:20:05 PM7/11/16
to
Quoting Wan Zongshun (2016-07-10 00:27:24)
Maybe call it clk-nuc.h?

> +static struct clk_ops clk_apll_ops = {
> + .recalc_rate = clk_apll_recalc_rate,
> + .enable = clk_apll_enable,
> + .disable = clk_apll_disable,

Can you provide a .is_enabled?

> +static void __init nuc970_clocks_init(struct device_node *np)
> +{
> + int i, ret;
> +
> + clkctrl = of_iomap(np, 0);
> + if (!clkctrl)
> + pr_err("%s: unable to map registers\n", np->full_name);
> +
> +
> + /* source */
> + clk[XIN] = nuc970_clk_fixed("xin", 12000000);
> + clk[XIN32K] = nuc970_clk_fixed("xin32k", 32768);
> + clk[APLL] = nuc970_clk_apll("apll", "xin", REG_CLK_APLLCON);
> + clk[UPLL] = nuc970_clk_upll("upll", "xin", REG_CLK_UPLLCON);
> + clk[XIN128_DIV] = nuc970_clk_fixed_factor("xin128_div", "xin", 1, 128);
> + clk[SYS_MUX] = nuc970_clk_mux("sys_mux", REG_CLK_DIV0, 3, 2,
> + sys_sel_clks,
> + ARRAY_SIZE(sys_sel_clks));

Instead of executing all of these registration functions, how about
initializing your clock data statically, and then simply calling
clk_hw_register?

For an example see the recently merged drivers/clk/meson/gxbb.c

> + for (i = 0; i < ARRAY_SIZE(clk); i++)
> + if (IS_ERR(clk[i]))
> + pr_err("nuc970 clk %d: register failed with %ld\n",
> + i, PTR_ERR(clk[i]));

Better to fail quickly, bail out and unwind your clk registration
instead of trying to register everything and then walk the list looking
for failures.

> +
> + clk_data.clks = clk;
> + clk_data.clk_num = ARRAY_SIZE(clk);
> +
> + ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
> + if (ret)
> + pr_err("Failed to register OF clock provider\n");
> +
> + /* Register clock device */
> + clk_register_clkdev(clk[TIMER0_GATE], "timer0", NULL);
> + clk_register_clkdev(clk[TIMER1_GATE], "timer1", NULL);

Again, look at how the gxbb.c driver does this. Why do you need to call
clk_register_clkdev? You're using of_clk_add_provider above, so that
should be enough to perform lookups.

> + /* enable some important clocks */
> + clk_prepare_enable(clk_get(NULL, "cpu"));
> + clk_prepare_enable(clk_get(NULL, "hclk"));
> + clk_prepare_enable(clk_get(NULL, "sram"));
> + clk_prepare_enable(clk_get(NULL, "dram"));
> + clk_prepare_enable(clk_get(NULL, "ddr_hclk"));

You can use the CLK_IS_CRITICAL flag for these clocks if you want. It
would be better to have drivers that claim them and enable them of
course.

> +}
> +
> +CLK_OF_DECLARE(nuc970_clk, "nuvoton,nuc970-clk", nuc970_clocks_init);

Why do you need to use CLK_OF_DECLARE? Please convert this to a
platform_driver and load it at module_init.

Regards,
Mike

Wan Zongshun

unread,
Jul 12, 2016, 12:40:06 AM7/12/16
to
Why remove mfp modules? this multifunction pin driver should be used for
those two drivers, if no mfp_set_groupX, I don't think driver can work.

Now mfp has standard driver subsystem?

Wan Zongshun

unread,
Jul 12, 2016, 3:10:06 AM7/12/16
to


On 2016年07月11日 23:46, Arnd Bergmann wrote:
> On Sunday, July 10, 2016 3:27:22 PM CEST Wan Zongshun wrote:
>> +
>> +#if !defined(CONFIG_SOC_NUC900)
>> #define NR_IRQS (IRQ_ADC+1)
>> +#else
>> +#define NR_IRQS 62
>> +#endif
>>
>
> The Kconfig symbols are a bit confusing here: CONFIG_SOC_NUC900
> controls the compilation of the soc_device driver, but I guess
> what you actually mean here is CONFIG_SOC_NUC970, which is the
> support for the actual chip.
>
> Maybe rename the former to something less confusing and change
> this to CONFIG_SOC_NUC970?

You are right, it should _NUC970. Many thanks!

>
> Ideally, this should just go away once we use SPARSE_IRQ.

This platform also can use SPARSE_IRQ? this just a simple irq map and no
more irq number in this Soc.

>
> Arnd

Wan Zongshun

unread,
Jul 12, 2016, 3:20:06 AM7/12/16
to
Arnd, If you still think the mfp should be removed, we can send a series
patches to instead of using mfp interface quickly, and do mfp set in
local driver. Do you think it is ok?

Arnd Bergmann

unread,
Jul 12, 2016, 4:30:06 AM7/12/16
to
On Tuesday, July 12, 2016 3:04:42 PM CEST Wan Zongshun wrote:
> >
> > Ideally, this should just go away once we use SPARSE_IRQ.
>
> This platform also can use SPARSE_IRQ? this just a simple irq map and no
> more irq number in this Soc.
>

SPARSE_IRQ is implied by ARCH_MULTIPLATFORM, so we will have to
use it once that gets enabled.

Your new irqchip driver already handles IRQ domains, so it will
work out of the box with SPARSE_IRQ, but you have to change the
reference to "NR_IRQS" into something else.

I've prototyped a patch series to enable ARCH_MULTIPLATFORM,
I hope you can start working from what I have and get it to run.

Arnd

Arnd Bergmann

unread,
Jul 12, 2016, 4:30:07 AM7/12/16
to
On Tuesday, July 12, 2016 3:14:47 PM CEST Wan Zongshun wrote:
> On 2016年07月12日 12:30, Wan Zongshun wrote:
> >
> >
> > On 2016年07月12日 00:04, Arnd Bergmann wrote:
> >> On Sunday, July 10, 2016 3:27:21 PM CEST Wan Zongshun wrote:
> >>> +ifeq ($(CONFIG_SOC_NUC970),)
> >>> obj-y := irq.o time.o mfp.o gpio.o clock.o
> >>> obj-y += clksel.o dev.o cpu.o
> >>> +endif
> >>> # W90X900 CPU support files
> >>
> >> When mfp.o is disabled like this, I get a link error in two drivers
> >> using the exported interface:
> >>
> >> ERROR: "mfp_set_groupg" [drivers/spi/spi-nuc900.ko] undefined!
> >> ERROR: "mfp_set_groupi" [drivers/input/keyboard/w90p910_keypad.ko]
> >> undefined!
> >
> > Why remove mfp modules? this multifunction pin driver should be used for
> > those two drivers, if no mfp_set_groupX, I don't think driver can work.
> >
> > Now mfp has standard driver subsystem?
> >
> >>
> >> Any idea for a better migration strategy?
>
> Arnd, If you still think the mfp should be removed, we can send a series
> patches to instead of using mfp interface quickly, and do mfp set in
> local driver. Do you think it is ok?

I don't think setting it locally in the driver is a good idea.

In the long run, this should go through the pinctrl framework, but
there is no need to implement that right away. Until then, I think
using the existing mfp.o code is fine, it will just need to be
adapted slightly to understand the DT based device names.

Arnd

Wan Zongshun

unread,
Jul 12, 2016, 5:10:07 AM7/12/16
to


On 2016年07月11日 16:03, Arnd Bergmann wrote:
So, Maybe I can remove those two codes, no need push those information
to user space?

device_create_file(soc_device_to_device(soc_dev), &nuc900_chipid_attr);
device_create_file(soc_device_to_device(soc_dev), &nuc900_version_attr);

Arnd Bergmann

unread,
Jul 12, 2016, 6:00:06 AM7/12/16
to
Yes, that would be good.

Arnd

Jason Cooper

unread,
Jul 13, 2016, 4:10:06 PM7/13/16
to
Hi Wan Zongshun,

On Sun, Jul 10, 2016 at 03:27:22PM +0800, Wan Zongshun wrote:
> This patch is to add irqchip driver support for nuc900 plat,
> current this driver only supports nuc970 SoC.
>
> Signed-off-by: Wan Zongshun <mcuo...@gmail.com>
> ---
> arch/arm/mach-w90x900/include/mach/irqs.h | 5 +
> drivers/irqchip/Makefile | 1 +
> drivers/irqchip/irq-nuc900.c | 150 ++++++++++++++++++++++++++++++
> 3 files changed, 156 insertions(+)
> create mode 100644 drivers/irqchip/irq-nuc900.c
>
> diff --git a/arch/arm/mach-w90x900/include/mach/irqs.h b/arch/arm/mach-w90x900/include/mach/irqs.h
> index 9d5cba3..3b035c6 100644
> --- a/arch/arm/mach-w90x900/include/mach/irqs.h
> +++ b/arch/arm/mach-w90x900/include/mach/irqs.h
> @@ -59,7 +59,12 @@
> #define IRQ_KPI W90X900_IRQ(29)
> #define IRQ_P2SGROUP W90X900_IRQ(30)
> #define IRQ_ADC W90X900_IRQ(31)
> +
> +#if !defined(CONFIG_SOC_NUC900)
> #define NR_IRQS (IRQ_ADC+1)
> +#else
> +#define NR_IRQS 62
> +#endif

Arnd already covered this...
Please remove unused defines.
Could you explain what's going on here?
afaict, there is no 'nuc900', that's a family. If so, this compatible
needs to be 'nuvoton,nuc970-aic'.

thx,

Jason.

Wan Zongshun

unread,
Jul 13, 2016, 11:40:06 PM7/13/16
to
Okay.
AIC_IPER, When the AIC generates the interrupt, VECTOR represents the
interrupt channel number that is active, enabled, and has the highest
priority.

The value of VECTOR is copied to the register AIC_ISNR thereafter by the
AIC. This register was restored a value 0 after it was read by the
interrupt handler.

So I must read two registers for one irq number, after read IPER, the
ISNR(bit0:5) will be updated. ISNR value has no need do offset, it is
better than read IPER(bit2:7).

“writel(0x01, aic_base + REG_AIC_EOSCR);” seems not necessary here,
since irqchip has the ack interface. I will remove it.

This AIC_EOSCR register is used by the interrupt service routine to
indicate that it is completely served. Thus, the
interrupt handler can write any value to this register to indicate the
end of its interrupt service.
IRQCHIP_DECLARE(nuvoton, "nuvoton,nuc900-aic", aic_of_init);, change
nuc900 to nuvoton, ok?

> thx,
>
> Jason.
>
>

Wan Zongshun

unread,
Jul 14, 2016, 5:00:11 AM7/14/16
to
I go through the ARCH_MULTIPLATFORM and SPARSE_IRQ related codes, but I
find I also have to define the NUC900_NR_IRQS firstly like below, so
that I can init the .nr_irq.

+#if !defined(CONFIG_SOC_NUC970)
#define NUC900_NR_IRQS (IRQ_ADC+1)
+#else
+#define NUC900_NR_IRQS 62
+#endif

DT_MACHINE_START(nuc900_dt, "Nuvoton NUC900 (Device Tree Support)")
.dt_compat = nuc900_dt_compat,
+ .nr_irqs = NUC900_NR_IRQS,
MACHINE_END

and then in my irqchip driver, I will use the NUC900_NR_IRQS:

+aic_domain = irq_domain_add_linear(node, NUC900_NR_IRQS,
+ &aic_irq_domain_ops, NULL);


Is that a right usage?

Arnd Bergmann

unread,
Jul 14, 2016, 7:20:06 AM7/14/16
to
You don't need to set this for the DT based machines, this number
is just for the set of IRQ that have a hardcoded mapping. With DT,
they get dynamically allocated as required.

For the board files, you can hardcode the original definition of 32
IRQs, but I think you don't need that if you register a legacy IRQ
domain in mach-w90x900/irq.c.

> and then in my irqchip driver, I will use the NUC900_NR_IRQS:
>
> +aic_domain = irq_domain_add_linear(node, NUC900_NR_IRQS,
> + &aic_irq_domain_ops, NULL);
>
>
> Is that a right usage?

This does not look right when NUC900_NR_IRQS can have configuration
dependent values. I can see two ways of handling it:

a) register the maximum number of IRQs that the irqchip can handle.
There is no real cost for having a large number here, as SPARSE_IRQ
ensures we only need to allocate the descriptors that are actually
used.

b) make the number of interrupts dependent on the compatible string
for the irqchip, and handle NUC970 differently from the others
in the driver.

In the meantime, I also have a series to enable multiplatform support
for all of mach-w90x900 based on your patches, but lacking a proper
clk driver. I'll send that to you so you can include it in your
series (after verifying that it works, or fixing it where necessary).

Arnd

Jason Cooper

unread,
Jul 14, 2016, 10:00:06 AM7/14/16
to
Hi Wan Zongshun,
So, iiuc, the first readl() of AIC_IPER kicks the AIC to copy the value
of VECTOR into AIC_ISNR? Then we can read it?

If so, please add a comment above the code explaining that.

> So I must read two registers for one irq number, after read IPER,
> the ISNR(bit0:5) will be updated. ISNR value has no need do offset,
> it is better than read IPER(bit2:7).

But you aren't using the value from the first readl(...AIC_IPER)? You
overwrite hwirq with the value from the second readl(...AIC_ISNR)
...

The first part of your explaination makes sense, but this part isn't
clear to me.


> “writel(0x01, aic_base + REG_AIC_EOSCR);” seems not necessary here,
> since irqchip has the ack interface. I will remove it.

Ok, thanks.

> This AIC_EOSCR register is used by the interrupt service routine to
> indicate that it is completely served. Thus, the
> interrupt handler can write any value to this register to indicate
> the end of its interrupt service.

A short comment in the ack routine would be helpful.


> >>+IRQCHIP_DECLARE(nuc900, "nuvoton,nuc900-aic", aic_of_init);
> >
> >afaict, there is no 'nuc900', that's a family. If so, this compatible
> >needs to be 'nuvoton,nuc970-aic'.
> >
>
> IRQCHIP_DECLARE(nuvoton, "nuvoton,nuc900-aic", aic_of_init);, change
> nuc900 to nuvoton, ok?

No, I was only talking about the second argument. The compatible
string. For devicetree, it needs to refer to a specific model number.
I suspect 'nuc900' is equivalent to saying 'nuc9xx'. Meaning it refers
to a family of devices. The compatible string needs to refer
specifically to the first model that the binding is compatible with. In
this case, I presume that would be 'nuvoton,nuc970-aic'.

thx,

Jason.

Wan Zongshun

unread,
Jul 15, 2016, 1:20:05 AM7/15/16
to
Actually, I have two choice to implement this function:

option1:

void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
{
u32 hwirq;

(void)readl(aic_base + REG_AIC_IPER);
hwirq = readl(aic_base + REG_AIC_ISNR);

handle_IRQ((irq_find_mapping(aic_domain, hwirq)), regs);
}

option2:

void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
{
u32 hwirq;

hwirq = readl(aic_base + REG_AIC_IPER);
hwirq <<= 2;

handle_IRQ((irq_find_mapping(aic_domain, hwirq)), regs);
}

Though the option2 do shift for hwirq, but it seems better than do io
operation by readl,so I prefer to option2, agree?

Arnd Bergmann

unread,
Jul 15, 2016, 3:10:05 AM7/15/16
to
On Friday, July 15, 2016 1:15:58 PM CEST Wan Zongshun wrote:
>
> Actually, I have two choice to implement this function:
>
> option1:
>
> void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
> {
> u32 hwirq;
>
> (void)readl(aic_base + REG_AIC_IPER);
> hwirq = readl(aic_base + REG_AIC_ISNR);
>
> handle_IRQ((irq_find_mapping(aic_domain, hwirq)), regs);
> }

(side note: I think you want handle_domain_irq())

> option2:
>
> void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
> {
> u32 hwirq;
>
> hwirq = readl(aic_base + REG_AIC_IPER);
> hwirq <<= 2;
>
> handle_IRQ((irq_find_mapping(aic_domain, hwirq)), regs);
> }
>
> Though the option2 do shift for hwirq, but it seems better than do io
> operation by readl,so I prefer to option2, agree?

That will only return an irq number that is a multiple of four, which
seems wrong since the numbers are not that. Did you mean to write

hwirq = ilog2(hwirq); ?

That assumes that REG_AIC_IPER contains a 32-bit value with one single
bit set to indicate which IRQ was triggered.

If the difference is only in performance, you could try measuring which
of the two ends up being faster.

Arnd

Wan ZongShun

unread,
Jul 15, 2016, 5:50:10 AM7/15/16
to
2016-07-15 15:00 GMT+08:00 Arnd Bergmann <ar...@arndb.de>:
> On Friday, July 15, 2016 1:15:58 PM CEST Wan Zongshun wrote:
>>
>> Actually, I have two choice to implement this function:
>>
>> option1:
>>
>> void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
>> {
>> u32 hwirq;
>>
>> (void)readl(aic_base + REG_AIC_IPER);
>> hwirq = readl(aic_base + REG_AIC_ISNR);
>>
>> handle_IRQ((irq_find_mapping(aic_domain, hwirq)), regs);
>> }
>
> (side note: I think you want handle_domain_irq())
>
>> option2:
>>
>> void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
>> {
>> u32 hwirq;
>>
>> hwirq = readl(aic_base + REG_AIC_IPER);
>> hwirq <<= 2;
>>
>> handle_IRQ((irq_find_mapping(aic_domain, hwirq)), regs);
>> }
>>
>> Though the option2 do shift for hwirq, but it seems better than do io
>> operation by readl,so I prefer to option2, agree?
>
> That will only return an irq number that is a multiple of four, which
> seems wrong since the numbers are not that. Did you mean to write
>
> hwirq = ilog2(hwirq); ?

Sorry, my fault, I mean hwirq >>= 2, bit[7:2] indicates which irq is triggering.
so I have to do right shift 2 for IPER value.

>
> That assumes that REG_AIC_IPER contains a 32-bit value with one single
> bit set to indicate which IRQ was triggered.
>
> If the difference is only in performance, you could try measuring which
> of the two ends up being faster.

It seems hard to measure. I think Do IO operation should be slower
than shift 2. :)

Jason Cooper

unread,
Jul 15, 2016, 11:50:05 AM7/15/16
to
On Fri, Jul 15, 2016 at 05:44:50PM +0800, Wan ZongShun wrote:
> 2016-07-15 15:00 GMT+08:00 Arnd Bergmann <ar...@arndb.de>:
> > On Friday, July 15, 2016 1:15:58 PM CEST Wan Zongshun wrote:
> >>
> >> Actually, I have two choice to implement this function:
> >>
> >> option1:
> >>
> >> void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
> >> {
> >> u32 hwirq;
> >>
> >> (void)readl(aic_base + REG_AIC_IPER);
> >> hwirq = readl(aic_base + REG_AIC_ISNR);
> >>
> >> handle_IRQ((irq_find_mapping(aic_domain, hwirq)), regs);
> >> }
> >
> > (side note: I think you want handle_domain_irq())
> >
> >> option2:
> >>
> >> void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
> >> {
> >> u32 hwirq;
> >>
> >> hwirq = readl(aic_base + REG_AIC_IPER);
> >> hwirq <<= 2;
> >>
> >> handle_IRQ((irq_find_mapping(aic_domain, hwirq)), regs);
> >> }
> >>
> >> Though the option2 do shift for hwirq, but it seems better than do io
> >> operation by readl,so I prefer to option2, agree?
> >
> > That will only return an irq number that is a multiple of four, which
> > seems wrong since the numbers are not that. Did you mean to write
> >
> > hwirq = ilog2(hwirq); ?
>
> Sorry, my fault, I mean hwirq >>= 2, bit[7:2] indicates which irq is triggering.
> so I have to do right shift 2 for IPER value.

Ok, this makes a lot more sense now. :)

> > That assumes that REG_AIC_IPER contains a 32-bit value with one single
> > bit set to indicate which IRQ was triggered.
> >
> > If the difference is only in performance, you could try measuring which
> > of the two ends up being faster.
>
> It seems hard to measure. I think Do IO operation should be slower
> than shift 2. :)

Agreed.

thx,

Jason.

Arnd Bergmann

unread,
Jul 21, 2016, 7:00:06 AM7/21/16
to
On Friday, July 15, 2016 5:44:50 PM CEST Wan ZongShun wrote:
> 2016-07-15 15:00 GMT+08:00 Arnd Bergmann <ar...@arndb.de>:
> > On Friday, July 15, 2016 1:15:58 PM CEST Wan Zongshun wrote:
> >>
> >> Actually, I have two choice to implement this function:
> >>
> >> option1:
> >>
> >> void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
> >> {
> >> u32 hwirq;
> >>
> >> (void)readl(aic_base + REG_AIC_IPER);
> >> hwirq = readl(aic_base + REG_AIC_ISNR);
> >>
> >> handle_IRQ((irq_find_mapping(aic_domain, hwirq)), regs);
> >> }
> >
> > (side note: I think you want handle_domain_irq())
> >
> >> option2:
> >>
> >> void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
> >> {
> >> u32 hwirq;
> >>
> >> hwirq = readl(aic_base + REG_AIC_IPER);
> >> hwirq <<= 2;
> >>
> >> handle_IRQ((irq_find_mapping(aic_domain, hwirq)), regs);
> >> }
> >>
> >> Though the option2 do shift for hwirq, but it seems better than do io
> >> operation by readl,so I prefer to option2, agree?
> >
> > That will only return an irq number that is a multiple of four, which
> > seems wrong since the numbers are not that. Did you mean to write
> >
> > hwirq = ilog2(hwirq); ?
>
> Sorry, my fault, I mean hwirq >>= 2, bit[7:2] indicates which irq is triggering.
> so I have to do right shift 2 for IPER value.

Ok, got it.

> > That assumes that REG_AIC_IPER contains a 32-bit value with one single
> > bit set to indicate which IRQ was triggered.
> >
> > If the difference is only in performance, you could try measuring which
> > of the two ends up being faster.
>
> It seems hard to measure. I think Do IO operation should be slower
> than shift 2.

It depends on how fast that particular I/O path is. A lot of readl()
operations are awfully slow, but the hardware design for the interrupt
controller may in fact have optimized this to be reasonably fast.

Another option would be to avoid the shift and just use the raw value
of the REG_AIC_IPER register as the hwirq, with a custom map()
callback that turns shifts the number read from the DT two bits
so it matches the register value.

Arnd

Jason Cooper

unread,
Jul 21, 2016, 2:50:06 PM7/21/16
to
Wan ZongShun,

On Fri, Jul 15, 2016 at 12:02:55PM +0200, Arnd Bergmann wrote:
> On Friday, July 15, 2016 5:44:50 PM CEST Wan ZongShun wrote:
> > 2016-07-15 15:00 GMT+08:00 Arnd Bergmann <ar...@arndb.de>:
> > > On Friday, July 15, 2016 1:15:58 PM CEST Wan Zongshun wrote:
...
> > > That assumes that REG_AIC_IPER contains a 32-bit value with one single
> > > bit set to indicate which IRQ was triggered.
> > >
> > > If the difference is only in performance, you could try measuring which
> > > of the two ends up being faster.
> >
> > It seems hard to measure. I think Do IO operation should be slower
> > than shift 2.
>
> It depends on how fast that particular I/O path is. A lot of readl()
> operations are awfully slow, but the hardware design for the interrupt
> controller may in fact have optimized this to be reasonably fast.
>
> Another option would be to avoid the shift and just use the raw value
> of the REG_AIC_IPER register as the hwirq, with a custom map()
> callback that turns shifts the number read from the DT two bits
> so it matches the register value.

Good idea. Are the two lsb bits constant or do they need to be masked?

thx,

Jason.
0 new messages