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