Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2003 Deep Blue Solutions Ltd */ #include <linux/kernel.h> #include <linux/amba/mmci.h> #include <linux/io.h> #include <linux/irqchip.h> #include <linux/of_irq.h> #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/sched_clock.h> #include <linux/regmap.h> #include <linux/mfd/syscon.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> #include "integrator-hardware.h" #include "integrator-cm.h" #include "integrator.h" /* Base address to the core module header */ static struct regmap *cm_map; /* Base address to the CP controller */ static void __iomem *intcp_con_base; #define CM_COUNTER_OFFSET 0x28 /* * Logical Physical * f1400000 14000000 Interrupt controller * f1600000 16000000 UART 0 * fca00000 ca000000 SIC */ static struct map_desc intcp_io_desc[] __initdata __maybe_unused = { { .virtual = IO_ADDRESS(INTEGRATOR_IC_BASE), .pfn = __phys_to_pfn(INTEGRATOR_IC_BASE), .length = SZ_4K, .type = MT_DEVICE }, { .virtual = IO_ADDRESS(INTEGRATOR_UART0_BASE), .pfn = __phys_to_pfn(INTEGRATOR_UART0_BASE), .length = SZ_4K, .type = MT_DEVICE }, { .virtual = IO_ADDRESS(INTEGRATOR_CP_SIC_BASE), .pfn = __phys_to_pfn(INTEGRATOR_CP_SIC_BASE), .length = SZ_4K, .type = MT_DEVICE } }; static void __init intcp_map_io(void) { iotable_init(intcp_io_desc, ARRAY_SIZE(intcp_io_desc)); } /* * It seems that the card insertion interrupt remains active after * we've acknowledged it. We therefore ignore the interrupt, and * rely on reading it from the SIC. This also means that we must * clear the latched interrupt. */ static unsigned int mmc_status(struct device *dev) { unsigned int status = readl(__io_address(0xca000000 + 4)); writel(8, intcp_con_base + 8); return status & 8; } static struct mmci_platform_data mmc_data = { .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, .status = mmc_status, }; static u64 notrace intcp_read_sched_clock(void) { unsigned int val; /* MMIO so discard return code */ regmap_read(cm_map, CM_COUNTER_OFFSET, &val); return val; } static void __init intcp_init_early(void) { cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); if (IS_ERR(cm_map)) return; sched_clock_register(intcp_read_sched_clock, 32, 24000000); } static void __init intcp_init_irq_of(void) { cm_init(); irqchip_init(); } /* * For the Device Tree, add in the UART, MMC and CLCD specifics as AUXDATA * and enforce the bus names since these are used for clock lookups. */ static struct of_dev_auxdata intcp_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("arm,primecell", INTEGRATOR_CP_MMC_BASE, "mmci", &mmc_data), { /* sentinel */ }, }; static const struct of_device_id intcp_syscon_match[] = { { .compatible = "arm,integrator-cp-syscon"}, { }, }; static void __init intcp_init_of(void) { struct device_node *cpcon; cpcon = of_find_matching_node(NULL, intcp_syscon_match); if (!cpcon) return; intcp_con_base = of_iomap(cpcon, 0); if (!intcp_con_base) return; of_platform_default_populate(NULL, intcp_auxdata_lookup, NULL); } static const char * intcp_dt_board_compat[] = { "arm,integrator-cp", NULL, }; DT_MACHINE_START(INTEGRATOR_CP_DT, "ARM Integrator/CP (Device Tree)") .reserve = integrator_reserve, .map_io = intcp_map_io, .init_early = intcp_init_early, .init_irq = intcp_init_irq_of, .init_machine = intcp_init_of, .dt_compat = intcp_dt_board_compat, MACHINE_END |