| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * linux/arch/unicore32/kernel/puv3-core.c |
| * |
| * Code specific to PKUnity SoC and UniCore ISA |
| * |
| * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> |
| * Copyright (C) 2001-2010 Guan Xuetao |
| */ |
| |
| #include <linux/init.h> |
| #include <linux/device.h> |
| #include <linux/amba/bus.h> |
| #include <linux/platform_device.h> |
| #include <linux/io.h> |
| #include <linux/cnt32_to_63.h> |
| #include <linux/usb/musb.h> |
| |
| #include <asm/irq.h> |
| #include <mach/hardware.h> |
| #include <mach/pm.h> |
| |
| /* |
| * This is the PKUnity sched_clock implementation. This has |
| * a resolution of 271ns, and a maximum value of 32025597s (370 days). |
| * |
| * The return value is guaranteed to be monotonic in that range as |
| * long as there is always less than 582 seconds between successive |
| * calls to this function. |
| * |
| * ( * 1E9 / CLOCK_TICK_RATE ) -> about 2235/32 |
| */ |
| unsigned long long sched_clock(void) |
| { |
| unsigned long long v = cnt32_to_63(readl(OST_OSCR)); |
| |
| /* original conservative method, but overflow frequently |
| * v *= NSEC_PER_SEC >> 12; |
| * do_div(v, CLOCK_TICK_RATE >> 12); |
| */ |
| v = ((v & 0x7fffffffffffffffULL) * 2235) >> 5; |
| |
| return v; |
| } |
| |
| static struct resource puv3_usb_resources[] = { |
| /* order is significant! */ |
| { |
| .start = io_v2p(PKUNITY_USB_BASE), |
| .end = io_v2p(PKUNITY_USB_BASE) + 0x3ff, |
| .flags = IORESOURCE_MEM, |
| }, { |
| .start = IRQ_USB, |
| .flags = IORESOURCE_IRQ, |
| }, { |
| .start = IRQ_USB, |
| .flags = IORESOURCE_IRQ, |
| }, |
| }; |
| |
| static struct musb_hdrc_config puv3_usb_config[] = { |
| { |
| .num_eps = 16, |
| .multipoint = 1, |
| #ifdef CONFIG_USB_INVENTRA_DMA |
| .dma = 1, |
| .dma_channels = 8, |
| #endif |
| }, |
| }; |
| |
| static struct musb_hdrc_platform_data puv3_usb_plat = { |
| .mode = MUSB_HOST, |
| .min_power = 100, |
| .clock = 0, |
| .config = puv3_usb_config, |
| }; |
| |
| static struct resource puv3_mmc_resources[] = { |
| [0] = { |
| .start = io_v2p(PKUNITY_SDC_BASE), |
| .end = io_v2p(PKUNITY_SDC_BASE) + 0xfff, |
| .flags = IORESOURCE_MEM, |
| }, |
| [1] = { |
| .start = IRQ_SDC, |
| .end = IRQ_SDC, |
| .flags = IORESOURCE_IRQ, |
| }, |
| }; |
| |
| static struct resource puv3_unigfx_resources[] = { |
| [0] = { |
| .start = io_v2p(PKUNITY_UNIGFX_BASE), |
| .end = io_v2p(PKUNITY_UNIGFX_BASE) + 0xfff, |
| .flags = IORESOURCE_MEM, |
| }, |
| }; |
| |
| static struct resource puv3_rtc_resources[] = { |
| [0] = { |
| .start = io_v2p(PKUNITY_RTC_BASE), |
| .end = io_v2p(PKUNITY_RTC_BASE) + 0xff, |
| .flags = IORESOURCE_MEM, |
| }, |
| [1] = { |
| .start = IRQ_RTCAlarm, |
| .end = IRQ_RTCAlarm, |
| .flags = IORESOURCE_IRQ, |
| }, |
| [2] = { |
| .start = IRQ_RTC, |
| .end = IRQ_RTC, |
| .flags = IORESOURCE_IRQ |
| } |
| }; |
| |
| static struct resource puv3_pwm_resources[] = { |
| [0] = { |
| .start = io_v2p(PKUNITY_OST_BASE) + 0x80, |
| .end = io_v2p(PKUNITY_OST_BASE) + 0xff, |
| .flags = IORESOURCE_MEM, |
| }, |
| }; |
| |
| static struct resource puv3_uart0_resources[] = { |
| [0] = { |
| .start = io_v2p(PKUNITY_UART0_BASE), |
| .end = io_v2p(PKUNITY_UART0_BASE) + 0xff, |
| .flags = IORESOURCE_MEM, |
| }, |
| [1] = { |
| .start = IRQ_UART0, |
| .end = IRQ_UART0, |
| .flags = IORESOURCE_IRQ |
| } |
| }; |
| |
| static struct resource puv3_uart1_resources[] = { |
| [0] = { |
| .start = io_v2p(PKUNITY_UART1_BASE), |
| .end = io_v2p(PKUNITY_UART1_BASE) + 0xff, |
| .flags = IORESOURCE_MEM, |
| }, |
| [1] = { |
| .start = IRQ_UART1, |
| .end = IRQ_UART1, |
| .flags = IORESOURCE_IRQ |
| } |
| }; |
| |
| static struct resource puv3_umal_resources[] = { |
| [0] = { |
| .start = io_v2p(PKUNITY_UMAL_BASE), |
| .end = io_v2p(PKUNITY_UMAL_BASE) + 0x1fff, |
| .flags = IORESOURCE_MEM, |
| }, |
| [1] = { |
| .start = IRQ_UMAL, |
| .end = IRQ_UMAL, |
| .flags = IORESOURCE_IRQ |
| } |
| }; |
| |
| #ifdef CONFIG_PUV3_PM |
| |
| #define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x |
| #define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x] |
| |
| /* |
| * List of global PXA peripheral registers to preserve. |
| * More ones like CP and general purpose register values are preserved |
| * with the stack pointer in sleep.S. |
| */ |
| enum { |
| SLEEP_SAVE_PM_PLLDDRCFG, |
| SLEEP_SAVE_COUNT |
| }; |
| |
| |
| static void puv3_cpu_pm_save(unsigned long *sleep_save) |
| { |
| /* SAVE(PM_PLLDDRCFG); */ |
| } |
| |
| static void puv3_cpu_pm_restore(unsigned long *sleep_save) |
| { |
| /* RESTORE(PM_PLLDDRCFG); */ |
| } |
| |
| static int puv3_cpu_pm_prepare(void) |
| { |
| /* set resume return address */ |
| writel(virt_to_phys(puv3_cpu_resume), PM_DIVCFG); |
| return 0; |
| } |
| |
| static void puv3_cpu_pm_enter(suspend_state_t state) |
| { |
| /* Clear reset status */ |
| writel(RESETC_RSSR_HWR | RESETC_RSSR_WDR |
| | RESETC_RSSR_SMR | RESETC_RSSR_SWR, RESETC_RSSR); |
| |
| switch (state) { |
| /* case PM_SUSPEND_ON: |
| puv3_cpu_idle(); |
| break; */ |
| case PM_SUSPEND_MEM: |
| puv3_cpu_pm_prepare(); |
| puv3_cpu_suspend(PM_PMCR_SFB); |
| break; |
| } |
| } |
| |
| static int puv3_cpu_pm_valid(suspend_state_t state) |
| { |
| return state == PM_SUSPEND_MEM; |
| } |
| |
| static void puv3_cpu_pm_finish(void) |
| { |
| /* ensure not to come back here if it wasn't intended */ |
| /* PSPR = 0; */ |
| } |
| |
| static struct puv3_cpu_pm_fns puv3_cpu_pm_fnss = { |
| .save_count = SLEEP_SAVE_COUNT, |
| .valid = puv3_cpu_pm_valid, |
| .save = puv3_cpu_pm_save, |
| .restore = puv3_cpu_pm_restore, |
| .enter = puv3_cpu_pm_enter, |
| .prepare = puv3_cpu_pm_prepare, |
| .finish = puv3_cpu_pm_finish, |
| }; |
| |
| static void __init puv3_init_pm(void) |
| { |
| puv3_cpu_pm_fns = &puv3_cpu_pm_fnss; |
| } |
| #else |
| static inline void puv3_init_pm(void) {} |
| #endif |
| |
| void puv3_ps2_init(void) |
| { |
| struct clk *bclk32; |
| |
| bclk32 = clk_get(NULL, "BUS32_CLK"); |
| writel(clk_get_rate(bclk32) / 200000, PS2_CNT); /* should > 5us */ |
| } |
| |
| void __init puv3_core_init(void) |
| { |
| puv3_init_pm(); |
| puv3_ps2_init(); |
| |
| platform_device_register_simple("PKUnity-v3-RTC", -1, |
| puv3_rtc_resources, ARRAY_SIZE(puv3_rtc_resources)); |
| platform_device_register_simple("PKUnity-v3-UMAL", -1, |
| puv3_umal_resources, ARRAY_SIZE(puv3_umal_resources)); |
| platform_device_register_simple("PKUnity-v3-MMC", -1, |
| puv3_mmc_resources, ARRAY_SIZE(puv3_mmc_resources)); |
| platform_device_register_simple("PKUnity-v3-UNIGFX", -1, |
| puv3_unigfx_resources, ARRAY_SIZE(puv3_unigfx_resources)); |
| platform_device_register_simple("PKUnity-v3-PWM", -1, |
| puv3_pwm_resources, ARRAY_SIZE(puv3_pwm_resources)); |
| platform_device_register_simple("PKUnity-v3-UART", 0, |
| puv3_uart0_resources, ARRAY_SIZE(puv3_uart0_resources)); |
| platform_device_register_simple("PKUnity-v3-UART", 1, |
| puv3_uart1_resources, ARRAY_SIZE(puv3_uart1_resources)); |
| platform_device_register_simple("PKUnity-v3-AC97", -1, NULL, 0); |
| platform_device_register_resndata(NULL, "musb_hdrc", -1, |
| puv3_usb_resources, ARRAY_SIZE(puv3_usb_resources), |
| &puv3_usb_plat, sizeof(puv3_usb_plat)); |
| } |
| |