1d7445676SArnd Bergmann // SPDX-License-Identifier: GPL-2.0-or-later 2d7445676SArnd Bergmann /* 3d7445676SArnd Bergmann * Copyright (C) 2000-2003 Deep Blue Solutions Ltd 4d7445676SArnd Bergmann */ 5d7445676SArnd Bergmann #include <linux/kernel.h> 6d7445676SArnd Bergmann #include <linux/init.h> 7d7445676SArnd Bergmann #include <linux/syscore_ops.h> 8d7445676SArnd Bergmann #include <linux/amba/bus.h> 9d7445676SArnd Bergmann #include <linux/io.h> 10d7445676SArnd Bergmann #include <linux/irqchip.h> 11d7445676SArnd Bergmann #include <linux/of_irq.h> 12d7445676SArnd Bergmann #include <linux/of_address.h> 13d7445676SArnd Bergmann #include <linux/of_platform.h> 14*89bbeb7eSAl Viro #include <linux/uaccess.h> 15d7445676SArnd Bergmann #include <linux/termios.h> 16d7445676SArnd Bergmann #include <linux/mfd/syscon.h> 17d7445676SArnd Bergmann #include <linux/regmap.h> 18d7445676SArnd Bergmann 19d7445676SArnd Bergmann #include <asm/mach/arch.h> 20d7445676SArnd Bergmann #include <asm/mach/map.h> 21d7445676SArnd Bergmann 22d7445676SArnd Bergmann #include "integrator-hardware.h" 23d7445676SArnd Bergmann #include "integrator-cm.h" 24d7445676SArnd Bergmann #include "integrator.h" 25d7445676SArnd Bergmann 26d7445676SArnd Bergmann /* Regmap to the AP system controller */ 27d7445676SArnd Bergmann static struct regmap *ap_syscon_map; 28d7445676SArnd Bergmann 29d7445676SArnd Bergmann /* 30d7445676SArnd Bergmann * All IO addresses are mapped onto VA 0xFFFx.xxxx, where x.xxxx 31d7445676SArnd Bergmann * is the (PA >> 12). 32d7445676SArnd Bergmann * 33d7445676SArnd Bergmann * Setup a VA for the Integrator interrupt controller (for header #0, 34d7445676SArnd Bergmann * just for now). 35d7445676SArnd Bergmann */ 36d7445676SArnd Bergmann #define VA_IC_BASE __io_address(INTEGRATOR_IC_BASE) 37d7445676SArnd Bergmann 38d7445676SArnd Bergmann /* 39d7445676SArnd Bergmann * Logical Physical 40d7445676SArnd Bergmann * f1400000 14000000 Interrupt controller 41d7445676SArnd Bergmann * f1600000 16000000 UART 0 42d7445676SArnd Bergmann */ 43d7445676SArnd Bergmann 44d7445676SArnd Bergmann static struct map_desc ap_io_desc[] __initdata __maybe_unused = { 45d7445676SArnd Bergmann { 46d7445676SArnd Bergmann .virtual = IO_ADDRESS(INTEGRATOR_IC_BASE), 47d7445676SArnd Bergmann .pfn = __phys_to_pfn(INTEGRATOR_IC_BASE), 48d7445676SArnd Bergmann .length = SZ_4K, 49d7445676SArnd Bergmann .type = MT_DEVICE 50d7445676SArnd Bergmann }, { 51d7445676SArnd Bergmann .virtual = IO_ADDRESS(INTEGRATOR_UART0_BASE), 52d7445676SArnd Bergmann .pfn = __phys_to_pfn(INTEGRATOR_UART0_BASE), 53d7445676SArnd Bergmann .length = SZ_4K, 54d7445676SArnd Bergmann .type = MT_DEVICE 55d7445676SArnd Bergmann } 56d7445676SArnd Bergmann }; 57d7445676SArnd Bergmann 58d7445676SArnd Bergmann static void __init ap_map_io(void) 59d7445676SArnd Bergmann { 60d7445676SArnd Bergmann iotable_init(ap_io_desc, ARRAY_SIZE(ap_io_desc)); 61d7445676SArnd Bergmann } 62d7445676SArnd Bergmann 63d7445676SArnd Bergmann #ifdef CONFIG_PM 64d7445676SArnd Bergmann static unsigned long ic_irq_enable; 65d7445676SArnd Bergmann 66d7445676SArnd Bergmann static int irq_suspend(void) 67d7445676SArnd Bergmann { 68d7445676SArnd Bergmann ic_irq_enable = readl(VA_IC_BASE + IRQ_ENABLE); 69d7445676SArnd Bergmann return 0; 70d7445676SArnd Bergmann } 71d7445676SArnd Bergmann 72d7445676SArnd Bergmann static void irq_resume(void) 73d7445676SArnd Bergmann { 74d7445676SArnd Bergmann /* disable all irq sources */ 75d7445676SArnd Bergmann cm_clear_irqs(); 76d7445676SArnd Bergmann writel(-1, VA_IC_BASE + IRQ_ENABLE_CLEAR); 77d7445676SArnd Bergmann writel(-1, VA_IC_BASE + FIQ_ENABLE_CLEAR); 78d7445676SArnd Bergmann 79d7445676SArnd Bergmann writel(ic_irq_enable, VA_IC_BASE + IRQ_ENABLE_SET); 80d7445676SArnd Bergmann } 81d7445676SArnd Bergmann #else 82d7445676SArnd Bergmann #define irq_suspend NULL 83d7445676SArnd Bergmann #define irq_resume NULL 84d7445676SArnd Bergmann #endif 85d7445676SArnd Bergmann 86d7445676SArnd Bergmann static struct syscore_ops irq_syscore_ops = { 87d7445676SArnd Bergmann .suspend = irq_suspend, 88d7445676SArnd Bergmann .resume = irq_resume, 89d7445676SArnd Bergmann }; 90d7445676SArnd Bergmann 91d7445676SArnd Bergmann static int __init irq_syscore_init(void) 92d7445676SArnd Bergmann { 93d7445676SArnd Bergmann register_syscore_ops(&irq_syscore_ops); 94d7445676SArnd Bergmann 95d7445676SArnd Bergmann return 0; 96d7445676SArnd Bergmann } 97d7445676SArnd Bergmann 98d7445676SArnd Bergmann device_initcall(irq_syscore_init); 99d7445676SArnd Bergmann 100d7445676SArnd Bergmann /* 101d7445676SArnd Bergmann * For the PL010 found in the Integrator/AP some of the UART control is 102d7445676SArnd Bergmann * implemented in the system controller and accessed using a callback 103d7445676SArnd Bergmann * from the driver. 104d7445676SArnd Bergmann */ 105d7445676SArnd Bergmann static void integrator_uart_set_mctrl(struct amba_device *dev, 106d7445676SArnd Bergmann void __iomem *base, unsigned int mctrl) 107d7445676SArnd Bergmann { 108d7445676SArnd Bergmann unsigned int ctrls = 0, ctrlc = 0, rts_mask, dtr_mask; 109d7445676SArnd Bergmann u32 phybase = dev->res.start; 110d7445676SArnd Bergmann int ret; 111d7445676SArnd Bergmann 112d7445676SArnd Bergmann if (phybase == INTEGRATOR_UART0_BASE) { 113d7445676SArnd Bergmann /* UART0 */ 114d7445676SArnd Bergmann rts_mask = 1 << 4; 115d7445676SArnd Bergmann dtr_mask = 1 << 5; 116d7445676SArnd Bergmann } else { 117d7445676SArnd Bergmann /* UART1 */ 118d7445676SArnd Bergmann rts_mask = 1 << 6; 119d7445676SArnd Bergmann dtr_mask = 1 << 7; 120d7445676SArnd Bergmann } 121d7445676SArnd Bergmann 122d7445676SArnd Bergmann if (mctrl & TIOCM_RTS) 123d7445676SArnd Bergmann ctrlc |= rts_mask; 124d7445676SArnd Bergmann else 125d7445676SArnd Bergmann ctrls |= rts_mask; 126d7445676SArnd Bergmann 127d7445676SArnd Bergmann if (mctrl & TIOCM_DTR) 128d7445676SArnd Bergmann ctrlc |= dtr_mask; 129d7445676SArnd Bergmann else 130d7445676SArnd Bergmann ctrls |= dtr_mask; 131d7445676SArnd Bergmann 132d7445676SArnd Bergmann ret = regmap_write(ap_syscon_map, 133d7445676SArnd Bergmann INTEGRATOR_SC_CTRLS_OFFSET, 134d7445676SArnd Bergmann ctrls); 135d7445676SArnd Bergmann if (ret) 136d7445676SArnd Bergmann pr_err("MODEM: unable to write PL010 UART CTRLS\n"); 137d7445676SArnd Bergmann 138d7445676SArnd Bergmann ret = regmap_write(ap_syscon_map, 139d7445676SArnd Bergmann INTEGRATOR_SC_CTRLC_OFFSET, 140d7445676SArnd Bergmann ctrlc); 141d7445676SArnd Bergmann if (ret) 142d7445676SArnd Bergmann pr_err("MODEM: unable to write PL010 UART CRTLC\n"); 143d7445676SArnd Bergmann } 144d7445676SArnd Bergmann 145d7445676SArnd Bergmann struct amba_pl010_data ap_uart_data = { 146d7445676SArnd Bergmann .set_mctrl = integrator_uart_set_mctrl, 147d7445676SArnd Bergmann }; 148d7445676SArnd Bergmann 149d7445676SArnd Bergmann static void __init ap_init_irq_of(void) 150d7445676SArnd Bergmann { 151d7445676SArnd Bergmann cm_init(); 152d7445676SArnd Bergmann irqchip_init(); 153d7445676SArnd Bergmann } 154d7445676SArnd Bergmann 155d7445676SArnd Bergmann /* For the Device Tree, add in the UART callbacks as AUXDATA */ 156d7445676SArnd Bergmann static struct of_dev_auxdata ap_auxdata_lookup[] __initdata = { 157d7445676SArnd Bergmann OF_DEV_AUXDATA("arm,primecell", INTEGRATOR_UART0_BASE, 158d7445676SArnd Bergmann "uart0", &ap_uart_data), 159d7445676SArnd Bergmann OF_DEV_AUXDATA("arm,primecell", INTEGRATOR_UART1_BASE, 160d7445676SArnd Bergmann "uart1", &ap_uart_data), 161d7445676SArnd Bergmann { /* sentinel */ }, 162d7445676SArnd Bergmann }; 163d7445676SArnd Bergmann 164d7445676SArnd Bergmann static const struct of_device_id ap_syscon_match[] = { 165d7445676SArnd Bergmann { .compatible = "arm,integrator-ap-syscon"}, 166d7445676SArnd Bergmann { }, 167d7445676SArnd Bergmann }; 168d7445676SArnd Bergmann 169d7445676SArnd Bergmann static void __init ap_init_of(void) 170d7445676SArnd Bergmann { 171d7445676SArnd Bergmann struct device_node *syscon; 172d7445676SArnd Bergmann 173d7445676SArnd Bergmann of_platform_default_populate(NULL, ap_auxdata_lookup, NULL); 174d7445676SArnd Bergmann 175d7445676SArnd Bergmann syscon = of_find_matching_node(NULL, ap_syscon_match); 176d7445676SArnd Bergmann if (!syscon) 177d7445676SArnd Bergmann return; 178d7445676SArnd Bergmann ap_syscon_map = syscon_node_to_regmap(syscon); 179d7445676SArnd Bergmann if (IS_ERR(ap_syscon_map)) { 180d7445676SArnd Bergmann pr_crit("could not find Integrator/AP system controller\n"); 181d7445676SArnd Bergmann return; 182d7445676SArnd Bergmann } 183d7445676SArnd Bergmann } 184d7445676SArnd Bergmann 185d7445676SArnd Bergmann static const char * ap_dt_board_compat[] = { 186d7445676SArnd Bergmann "arm,integrator-ap", 187d7445676SArnd Bergmann NULL, 188d7445676SArnd Bergmann }; 189d7445676SArnd Bergmann 190d7445676SArnd Bergmann DT_MACHINE_START(INTEGRATOR_AP_DT, "ARM Integrator/AP (Device Tree)") 191d7445676SArnd Bergmann .reserve = integrator_reserve, 192d7445676SArnd Bergmann .map_io = ap_map_io, 193d7445676SArnd Bergmann .init_irq = ap_init_irq_of, 194d7445676SArnd Bergmann .init_machine = ap_init_of, 195d7445676SArnd Bergmann .dt_compat = ap_dt_board_compat, 196d7445676SArnd Bergmann MACHINE_END 197