]>
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" |
238cd935 PMD |
25 | #include "trace.h" |
26 | ||
27 | static inline const char *sdbus_name(SDBus *sdbus) | |
28 | { | |
29 | return sdbus->qbus.name; | |
30 | } | |
c759a790 PM |
31 | |
32 | static SDState *get_card(SDBus *sdbus) | |
33 | { | |
34 | /* We only ever have one child on the bus so just return it */ | |
35 | BusChild *kid = QTAILQ_FIRST(&sdbus->qbus.children); | |
36 | ||
37 | if (!kid) { | |
38 | return NULL; | |
39 | } | |
40 | return SD_CARD(kid->child); | |
41 | } | |
42 | ||
da346922 PMD |
43 | uint8_t sdbus_get_dat_lines(SDBus *sdbus) |
44 | { | |
45 | SDState *slave = get_card(sdbus); | |
46 | uint8_t dat_lines = 0b1111; /* 4 bit bus width */ | |
47 | ||
48 | if (slave) { | |
49 | SDCardClass *sc = SD_CARD_GET_CLASS(slave); | |
50 | ||
51 | if (sc->get_dat_lines) { | |
52 | dat_lines = sc->get_dat_lines(slave); | |
53 | } | |
54 | } | |
55 | trace_sdbus_get_dat_lines(sdbus_name(sdbus), dat_lines); | |
56 | ||
57 | return dat_lines; | |
58 | } | |
59 | ||
60 | bool sdbus_get_cmd_line(SDBus *sdbus) | |
61 | { | |
62 | SDState *slave = get_card(sdbus); | |
63 | bool cmd_line = true; | |
64 | ||
65 | if (slave) { | |
66 | SDCardClass *sc = SD_CARD_GET_CLASS(slave); | |
67 | ||
68 | if (sc->get_cmd_line) { | |
69 | cmd_line = sc->get_cmd_line(slave); | |
70 | } | |
71 | } | |
72 | trace_sdbus_get_cmd_line(sdbus_name(sdbus), cmd_line); | |
73 | ||
74 | return cmd_line; | |
75 | } | |
76 | ||
0034ebe6 PMD |
77 | void sdbus_set_voltage(SDBus *sdbus, uint16_t millivolts) |
78 | { | |
79 | SDState *card = get_card(sdbus); | |
80 | ||
81 | trace_sdbus_set_voltage(sdbus_name(sdbus), millivolts); | |
82 | if (card) { | |
83 | SDCardClass *sc = SD_CARD_GET_CLASS(card); | |
84 | ||
85 | assert(sc->set_voltage); | |
86 | sc->set_voltage(card, millivolts); | |
87 | } | |
88 | } | |
89 | ||
c759a790 PM |
90 | int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response) |
91 | { | |
92 | SDState *card = get_card(sdbus); | |
93 | ||
13606b99 | 94 | trace_sdbus_command(sdbus_name(sdbus), req->cmd, req->arg); |
c759a790 PM |
95 | if (card) { |
96 | SDCardClass *sc = SD_CARD_GET_CLASS(card); | |
97 | ||
98 | return sc->do_command(card, req, response); | |
99 | } | |
100 | ||
101 | return 0; | |
102 | } | |
103 | ||
104 | void sdbus_write_data(SDBus *sdbus, uint8_t value) | |
105 | { | |
106 | SDState *card = get_card(sdbus); | |
107 | ||
238cd935 | 108 | trace_sdbus_write(sdbus_name(sdbus), value); |
c759a790 PM |
109 | if (card) { |
110 | SDCardClass *sc = SD_CARD_GET_CLASS(card); | |
111 | ||
112 | sc->write_data(card, value); | |
113 | } | |
114 | } | |
115 | ||
116 | uint8_t sdbus_read_data(SDBus *sdbus) | |
117 | { | |
118 | SDState *card = get_card(sdbus); | |
238cd935 | 119 | uint8_t value = 0; |
c759a790 PM |
120 | |
121 | if (card) { | |
122 | SDCardClass *sc = SD_CARD_GET_CLASS(card); | |
123 | ||
238cd935 | 124 | value = sc->read_data(card); |
c759a790 | 125 | } |
238cd935 | 126 | trace_sdbus_read(sdbus_name(sdbus), value); |
c759a790 | 127 | |
238cd935 | 128 | return value; |
c759a790 PM |
129 | } |
130 | ||
131 | bool sdbus_data_ready(SDBus *sdbus) | |
132 | { | |
133 | SDState *card = get_card(sdbus); | |
134 | ||
135 | if (card) { | |
136 | SDCardClass *sc = SD_CARD_GET_CLASS(card); | |
137 | ||
138 | return sc->data_ready(card); | |
139 | } | |
140 | ||
141 | return false; | |
142 | } | |
143 | ||
144 | bool sdbus_get_inserted(SDBus *sdbus) | |
145 | { | |
146 | SDState *card = get_card(sdbus); | |
147 | ||
148 | if (card) { | |
149 | SDCardClass *sc = SD_CARD_GET_CLASS(card); | |
150 | ||
151 | return sc->get_inserted(card); | |
152 | } | |
153 | ||
154 | return false; | |
155 | } | |
156 | ||
157 | bool sdbus_get_readonly(SDBus *sdbus) | |
158 | { | |
159 | SDState *card = get_card(sdbus); | |
160 | ||
161 | if (card) { | |
162 | SDCardClass *sc = SD_CARD_GET_CLASS(card); | |
163 | ||
164 | return sc->get_readonly(card); | |
165 | } | |
166 | ||
167 | return false; | |
168 | } | |
169 | ||
170 | void sdbus_set_inserted(SDBus *sdbus, bool inserted) | |
171 | { | |
172 | SDBusClass *sbc = SD_BUS_GET_CLASS(sdbus); | |
173 | BusState *qbus = BUS(sdbus); | |
174 | ||
175 | if (sbc->set_inserted) { | |
176 | sbc->set_inserted(qbus->parent, inserted); | |
177 | } | |
178 | } | |
179 | ||
180 | void sdbus_set_readonly(SDBus *sdbus, bool readonly) | |
181 | { | |
182 | SDBusClass *sbc = SD_BUS_GET_CLASS(sdbus); | |
183 | BusState *qbus = BUS(sdbus); | |
184 | ||
185 | if (sbc->set_readonly) { | |
186 | sbc->set_readonly(qbus->parent, readonly); | |
187 | } | |
188 | } | |
189 | ||
97fb87cc CD |
190 | void sdbus_reparent_card(SDBus *from, SDBus *to) |
191 | { | |
192 | SDState *card = get_card(from); | |
193 | SDCardClass *sc; | |
194 | bool readonly; | |
195 | ||
196 | /* We directly reparent the card object rather than implementing this | |
197 | * as a hotpluggable connection because we don't want to expose SD cards | |
198 | * to users as being hotpluggable, and we can get away with it in this | |
199 | * limited use case. This could perhaps be implemented more cleanly in | |
200 | * future by adding support to the hotplug infrastructure for "device | |
201 | * can be hotplugged only via code, not by user". | |
202 | */ | |
203 | ||
204 | if (!card) { | |
205 | return; | |
206 | } | |
207 | ||
208 | sc = SD_CARD_GET_CLASS(card); | |
209 | readonly = sc->get_readonly(card); | |
210 | ||
211 | sdbus_set_inserted(from, false); | |
212 | qdev_set_parent_bus(DEVICE(card), &to->qbus); | |
213 | sdbus_set_inserted(to, true); | |
214 | sdbus_set_readonly(to, readonly); | |
215 | } | |
216 | ||
c759a790 PM |
217 | static const TypeInfo sd_bus_info = { |
218 | .name = TYPE_SD_BUS, | |
219 | .parent = TYPE_BUS, | |
220 | .instance_size = sizeof(SDBus), | |
221 | .class_size = sizeof(SDBusClass), | |
222 | }; | |
223 | ||
224 | static void sd_bus_register_types(void) | |
225 | { | |
226 | type_register_static(&sd_bus_info); | |
227 | } | |
228 | ||
229 | type_init(sd_bus_register_types) |