]>
Commit | Line | Data |
---|---|---|
c759a790 PM |
1 | /* |
2 | * SD card bus interface code. | |
3 | * | |
4 | * Copyright (c) 2015 Linaro Limited | |
5 | * | |
6 | * Author: | |
7 | * Peter Maydell <[email protected]> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify it | |
10 | * under the terms and conditions of the GNU General Public License, | |
11 | * version 2 or later, as published by the Free Software Foundation. | |
12 | * | |
13 | * This program is distributed in the hope it will be useful, but WITHOUT | |
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
16 | * more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License along with | |
19 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | */ | |
21 | ||
22 | #include "qemu/osdep.h" | |
23 | #include "hw/qdev-core.h" | |
c759a790 | 24 | #include "hw/sd/sd.h" |
0b8fa32f | 25 | #include "qemu/module.h" |
238cd935 PMD |
26 | #include "trace.h" |
27 | ||
28 | static inline const char *sdbus_name(SDBus *sdbus) | |
29 | { | |
30 | return sdbus->qbus.name; | |
31 | } | |
c759a790 PM |
32 | |
33 | static SDState *get_card(SDBus *sdbus) | |
34 | { | |
35 | /* We only ever have one child on the bus so just return it */ | |
36 | BusChild *kid = QTAILQ_FIRST(&sdbus->qbus.children); | |
37 | ||
38 | if (!kid) { | |
39 | return NULL; | |
40 | } | |
41 | return SD_CARD(kid->child); | |
42 | } | |
43 | ||
da346922 PMD |
44 | uint8_t sdbus_get_dat_lines(SDBus *sdbus) |
45 | { | |
46 | SDState *slave = get_card(sdbus); | |
47 | uint8_t dat_lines = 0b1111; /* 4 bit bus width */ | |
48 | ||
49 | if (slave) { | |
50 | SDCardClass *sc = SD_CARD_GET_CLASS(slave); | |
51 | ||
52 | if (sc->get_dat_lines) { | |
53 | dat_lines = sc->get_dat_lines(slave); | |
54 | } | |
55 | } | |
56 | trace_sdbus_get_dat_lines(sdbus_name(sdbus), dat_lines); | |
57 | ||
58 | return dat_lines; | |
59 | } | |
60 | ||
61 | bool sdbus_get_cmd_line(SDBus *sdbus) | |
62 | { | |
63 | SDState *slave = get_card(sdbus); | |
64 | bool cmd_line = true; | |
65 | ||
66 | if (slave) { | |
67 | SDCardClass *sc = SD_CARD_GET_CLASS(slave); | |
68 | ||
69 | if (sc->get_cmd_line) { | |
70 | cmd_line = sc->get_cmd_line(slave); | |
71 | } | |
72 | } | |
73 | trace_sdbus_get_cmd_line(sdbus_name(sdbus), cmd_line); | |
74 | ||
75 | return cmd_line; | |
76 | } | |
77 | ||
0034ebe6 PMD |
78 | void sdbus_set_voltage(SDBus *sdbus, uint16_t millivolts) |
79 | { | |
80 | SDState *card = get_card(sdbus); | |
81 | ||
82 | trace_sdbus_set_voltage(sdbus_name(sdbus), millivolts); | |
83 | if (card) { | |
84 | SDCardClass *sc = SD_CARD_GET_CLASS(card); | |
85 | ||
86 | assert(sc->set_voltage); | |
87 | sc->set_voltage(card, millivolts); | |
88 | } | |
89 | } | |
90 | ||
c759a790 PM |
91 | int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response) |
92 | { | |
93 | SDState *card = get_card(sdbus); | |
94 | ||
13606b99 | 95 | trace_sdbus_command(sdbus_name(sdbus), req->cmd, req->arg); |
c759a790 PM |
96 | if (card) { |
97 | SDCardClass *sc = SD_CARD_GET_CLASS(card); | |
98 | ||
99 | return sc->do_command(card, req, response); | |
100 | } | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
105 | void sdbus_write_data(SDBus *sdbus, uint8_t value) | |
106 | { | |
107 | SDState *card = get_card(sdbus); | |
108 | ||
238cd935 | 109 | trace_sdbus_write(sdbus_name(sdbus), value); |
c759a790 PM |
110 | if (card) { |
111 | SDCardClass *sc = SD_CARD_GET_CLASS(card); | |
112 | ||
113 | sc->write_data(card, value); | |
114 | } | |
115 | } | |
116 | ||
117 | uint8_t sdbus_read_data(SDBus *sdbus) | |
118 | { | |
119 | SDState *card = get_card(sdbus); | |
238cd935 | 120 | uint8_t value = 0; |
c759a790 PM |
121 | |
122 | if (card) { | |
123 | SDCardClass *sc = SD_CARD_GET_CLASS(card); | |
124 | ||
238cd935 | 125 | value = sc->read_data(card); |
c759a790 | 126 | } |
238cd935 | 127 | trace_sdbus_read(sdbus_name(sdbus), value); |
c759a790 | 128 | |
238cd935 | 129 | return value; |
c759a790 PM |
130 | } |
131 | ||
132 | bool sdbus_data_ready(SDBus *sdbus) | |
133 | { | |
134 | SDState *card = get_card(sdbus); | |
135 | ||
136 | if (card) { | |
137 | SDCardClass *sc = SD_CARD_GET_CLASS(card); | |
138 | ||
139 | return sc->data_ready(card); | |
140 | } | |
141 | ||
142 | return false; | |
143 | } | |
144 | ||
145 | bool sdbus_get_inserted(SDBus *sdbus) | |
146 | { | |
147 | SDState *card = get_card(sdbus); | |
148 | ||
149 | if (card) { | |
150 | SDCardClass *sc = SD_CARD_GET_CLASS(card); | |
151 | ||
152 | return sc->get_inserted(card); | |
153 | } | |
154 | ||
155 | return false; | |
156 | } | |
157 | ||
158 | bool sdbus_get_readonly(SDBus *sdbus) | |
159 | { | |
160 | SDState *card = get_card(sdbus); | |
161 | ||
162 | if (card) { | |
163 | SDCardClass *sc = SD_CARD_GET_CLASS(card); | |
164 | ||
165 | return sc->get_readonly(card); | |
166 | } | |
167 | ||
168 | return false; | |
169 | } | |
170 | ||
171 | void sdbus_set_inserted(SDBus *sdbus, bool inserted) | |
172 | { | |
173 | SDBusClass *sbc = SD_BUS_GET_CLASS(sdbus); | |
174 | BusState *qbus = BUS(sdbus); | |
175 | ||
176 | if (sbc->set_inserted) { | |
177 | sbc->set_inserted(qbus->parent, inserted); | |
178 | } | |
179 | } | |
180 | ||
181 | void sdbus_set_readonly(SDBus *sdbus, bool readonly) | |
182 | { | |
183 | SDBusClass *sbc = SD_BUS_GET_CLASS(sdbus); | |
184 | BusState *qbus = BUS(sdbus); | |
185 | ||
186 | if (sbc->set_readonly) { | |
187 | sbc->set_readonly(qbus->parent, readonly); | |
188 | } | |
189 | } | |
190 | ||
97fb87cc CD |
191 | void sdbus_reparent_card(SDBus *from, SDBus *to) |
192 | { | |
193 | SDState *card = get_card(from); | |
194 | SDCardClass *sc; | |
195 | bool readonly; | |
196 | ||
197 | /* We directly reparent the card object rather than implementing this | |
198 | * as a hotpluggable connection because we don't want to expose SD cards | |
199 | * to users as being hotpluggable, and we can get away with it in this | |
200 | * limited use case. This could perhaps be implemented more cleanly in | |
201 | * future by adding support to the hotplug infrastructure for "device | |
202 | * can be hotplugged only via code, not by user". | |
203 | */ | |
204 | ||
205 | if (!card) { | |
206 | return; | |
207 | } | |
208 | ||
209 | sc = SD_CARD_GET_CLASS(card); | |
210 | readonly = sc->get_readonly(card); | |
211 | ||
212 | sdbus_set_inserted(from, false); | |
213 | qdev_set_parent_bus(DEVICE(card), &to->qbus); | |
214 | sdbus_set_inserted(to, true); | |
215 | sdbus_set_readonly(to, readonly); | |
216 | } | |
217 | ||
c759a790 PM |
218 | static const TypeInfo sd_bus_info = { |
219 | .name = TYPE_SD_BUS, | |
220 | .parent = TYPE_BUS, | |
221 | .instance_size = sizeof(SDBus), | |
222 | .class_size = sizeof(SDBusClass), | |
223 | }; | |
224 | ||
225 | static void sd_bus_register_types(void) | |
226 | { | |
227 | type_register_static(&sd_bus_info); | |
228 | } | |
229 | ||
230 | type_init(sd_bus_register_types) |