1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * arch/sh/kernel/machvec.c |
4 | * |
5 | * The SuperH machine vector setup handlers, yanked from setup.c |
6 | * |
7 | * Copyright (C) 1999 Niibe Yutaka |
8 | * Copyright (C) 2002 - 2007 Paul Mundt |
9 | */ |
10 | #include <linux/init.h> |
11 | #include <linux/string.h> |
12 | #include <asm/machvec.h> |
13 | #include <asm/sections.h> |
14 | #include <asm/addrspace.h> |
15 | #include <asm/setup.h> |
16 | #include <asm/io.h> |
17 | #include <asm/irq.h> |
18 | #include <asm/processor.h> |
19 | |
20 | #define MV_NAME_SIZE 32 |
21 | |
22 | #define for_each_mv(mv) \ |
23 | for ((mv) = (struct sh_machine_vector *)__machvec_start; \ |
24 | (mv) && (unsigned long)(mv) < (unsigned long)__machvec_end; \ |
25 | (mv)++) |
26 | |
27 | static struct sh_machine_vector * __init get_mv_byname(const char *name) |
28 | { |
29 | struct sh_machine_vector *mv; |
30 | |
31 | for_each_mv(mv) |
32 | if (strcasecmp(name, mv->mv_name) == 0) |
33 | return mv; |
34 | |
35 | return NULL; |
36 | } |
37 | |
38 | static unsigned int __initdata machvec_selected; |
39 | |
40 | static int __init early_parse_mv(char *from) |
41 | { |
42 | char mv_name[MV_NAME_SIZE] = "" ; |
43 | char *mv_end; |
44 | char *mv_comma; |
45 | int mv_len; |
46 | struct sh_machine_vector *mvp; |
47 | |
48 | mv_end = strchr(from, ' '); |
49 | if (mv_end == NULL) |
50 | mv_end = from + strlen(from); |
51 | |
52 | mv_comma = strchr(from, ','); |
53 | mv_len = mv_end - from; |
54 | if (mv_len > (MV_NAME_SIZE-1)) |
55 | mv_len = MV_NAME_SIZE-1; |
56 | memcpy(mv_name, from, mv_len); |
57 | mv_name[mv_len] = '\0'; |
58 | from = mv_end; |
59 | |
60 | machvec_selected = 1; |
61 | |
62 | /* Boot with the generic vector */ |
63 | if (strcmp(mv_name, "generic" ) == 0) |
64 | return 0; |
65 | |
66 | mvp = get_mv_byname(name: mv_name); |
67 | if (unlikely(!mvp)) { |
68 | pr_info("Available vectors:\n\n\t'%s', " , sh_mv.mv_name); |
69 | for_each_mv(mvp) |
70 | pr_cont("'%s', " , mvp->mv_name); |
71 | pr_cont("\n\n" ); |
72 | panic(fmt: "Failed to select machvec '%s' -- halting.\n" , |
73 | mv_name); |
74 | } else |
75 | sh_mv = *mvp; |
76 | |
77 | return 0; |
78 | } |
79 | early_param("sh_mv" , early_parse_mv); |
80 | |
81 | void __init sh_mv_setup(void) |
82 | { |
83 | /* |
84 | * Only overload the machvec if one hasn't been selected on |
85 | * the command line with sh_mv= |
86 | */ |
87 | if (!machvec_selected) { |
88 | unsigned long machvec_size; |
89 | |
90 | machvec_size = ((unsigned long)__machvec_end - |
91 | (unsigned long)__machvec_start); |
92 | |
93 | /* |
94 | * Sanity check for machvec section alignment. Ensure |
95 | * __initmv hasn't been misused. |
96 | */ |
97 | if (machvec_size % sizeof(struct sh_machine_vector)) |
98 | panic(fmt: "machvec misaligned, invalid __initmv use?" ); |
99 | |
100 | /* |
101 | * If the machvec hasn't been preselected, use the first |
102 | * vector (usually the only one) from .machvec.init. |
103 | */ |
104 | if (machvec_size >= sizeof(struct sh_machine_vector)) |
105 | sh_mv = *(struct sh_machine_vector *)__machvec_start; |
106 | } |
107 | |
108 | pr_notice("Booting machvec: %s\n" , get_system_type()); |
109 | |
110 | /* |
111 | * Manually walk the vec, fill in anything that the board hasn't yet |
112 | * by hand, wrapping to the generic implementation. |
113 | */ |
114 | #define mv_set(elem) do { \ |
115 | if (!sh_mv.mv_##elem) \ |
116 | sh_mv.mv_##elem = generic_##elem; \ |
117 | } while (0) |
118 | |
119 | mv_set(irq_demux); |
120 | mv_set(mode_pins); |
121 | mv_set(mem_init); |
122 | } |
123 | |