]> git.proxmox.com Git - grub2.git/blob - grub-core/bus/cs5536.c
Remove nested functions from PCI iterators.
[grub2.git] / grub-core / bus / cs5536.c
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2010 Free Software Foundation, Inc.
4 *
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <grub/types.h>
20 #include <grub/cs5536.h>
21 #include <grub/pci.h>
22 #include <grub/time.h>
23 #include <grub/ata.h>
24 #ifdef GRUB_MACHINE_MIPS_LOONGSON
25 #include <grub/machine/kernel.h>
26 #endif
27
28 #include <grub/dl.h>
29
30 GRUB_MOD_LICENSE ("GPLv3+");
31
32 /* Context for grub_cs5536_find. */
33 struct grub_cs5536_find_ctx
34 {
35 grub_pci_device_t *devp;
36 int found;
37 };
38
39 /* Helper for grub_cs5536_find. */
40 static int
41 grub_cs5536_find_iter (grub_pci_device_t dev, grub_pci_id_t pciid, void *data)
42 {
43 struct grub_cs5536_find_ctx *ctx = data;
44
45 if (pciid == GRUB_CS5536_PCIID)
46 {
47 *ctx->devp = dev;
48 ctx->found = 1;
49 return 1;
50 }
51 return 0;
52 }
53
54 int
55 grub_cs5536_find (grub_pci_device_t *devp)
56 {
57 struct grub_cs5536_find_ctx ctx = {
58 .devp = devp,
59 .found = 0
60 };
61
62 grub_pci_iterate (grub_cs5536_find_iter, &ctx);
63
64 return ctx.found;
65 }
66
67 grub_uint64_t
68 grub_cs5536_read_msr (grub_pci_device_t dev, grub_uint32_t addr)
69 {
70 grub_uint64_t ret = 0;
71 grub_pci_write (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_ADDR),
72 addr);
73 ret = (grub_uint64_t)
74 grub_pci_read (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_DATA0));
75 ret |= (((grub_uint64_t)
76 grub_pci_read (grub_pci_make_address (dev,
77 GRUB_CS5536_MSR_MAILBOX_DATA1)))
78 << 32);
79 return ret;
80 }
81
82 void
83 grub_cs5536_write_msr (grub_pci_device_t dev, grub_uint32_t addr,
84 grub_uint64_t val)
85 {
86 grub_pci_write (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_ADDR),
87 addr);
88 grub_pci_write (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_DATA0),
89 val & 0xffffffff);
90 grub_pci_write (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_DATA1),
91 val >> 32);
92 }
93
94 grub_err_t
95 grub_cs5536_smbus_wait (grub_port_t smbbase)
96 {
97 grub_uint64_t start = grub_get_time_ms ();
98 while (1)
99 {
100 grub_uint8_t status;
101 status = grub_inb (smbbase + GRUB_CS5536_SMB_REG_STATUS);
102 if (status & GRUB_CS5536_SMB_REG_STATUS_SDAST)
103 return GRUB_ERR_NONE;
104 if (status & GRUB_CS5536_SMB_REG_STATUS_BER)
105 return grub_error (GRUB_ERR_IO, "SM bus error");
106 if (status & GRUB_CS5536_SMB_REG_STATUS_NACK)
107 return grub_error (GRUB_ERR_IO, "NACK received");
108 if (grub_get_time_ms () > start + 40)
109 return grub_error (GRUB_ERR_IO, "SM stalled");
110 }
111 }
112
113 grub_err_t
114 grub_cs5536_read_spd_byte (grub_port_t smbbase, grub_uint8_t dev,
115 grub_uint8_t addr, grub_uint8_t *res)
116 {
117 grub_err_t err;
118
119 /* Send START. */
120 grub_outb (grub_inb (smbbase + GRUB_CS5536_SMB_REG_CTRL1)
121 | GRUB_CS5536_SMB_REG_CTRL1_START,
122 smbbase + GRUB_CS5536_SMB_REG_CTRL1);
123
124 /* Send device address. */
125 err = grub_cs5536_smbus_wait (smbbase);
126 if (err)
127 return err;
128 grub_outb (dev << 1, smbbase + GRUB_CS5536_SMB_REG_DATA);
129
130 /* Send ACK. */
131 err = grub_cs5536_smbus_wait (smbbase);
132 if (err)
133 return err;
134 grub_outb (grub_inb (smbbase + GRUB_CS5536_SMB_REG_CTRL1)
135 | GRUB_CS5536_SMB_REG_CTRL1_ACK,
136 smbbase + GRUB_CS5536_SMB_REG_CTRL1);
137
138 /* Send byte address. */
139 grub_outb (addr, smbbase + GRUB_CS5536_SMB_REG_DATA);
140
141 /* Send START. */
142 err = grub_cs5536_smbus_wait (smbbase);
143 if (err)
144 return err;
145 grub_outb (grub_inb (smbbase + GRUB_CS5536_SMB_REG_CTRL1)
146 | GRUB_CS5536_SMB_REG_CTRL1_START,
147 smbbase + GRUB_CS5536_SMB_REG_CTRL1);
148
149 /* Send device address. */
150 err = grub_cs5536_smbus_wait (smbbase);
151 if (err)
152 return err;
153 grub_outb ((dev << 1) | 1, smbbase + GRUB_CS5536_SMB_REG_DATA);
154
155 /* Send STOP. */
156 err = grub_cs5536_smbus_wait (smbbase);
157 if (err)
158 return err;
159 grub_outb (grub_inb (smbbase + GRUB_CS5536_SMB_REG_CTRL1)
160 | GRUB_CS5536_SMB_REG_CTRL1_STOP,
161 smbbase + GRUB_CS5536_SMB_REG_CTRL1);
162
163 err = grub_cs5536_smbus_wait (smbbase);
164 if (err)
165 return err;
166 *res = grub_inb (smbbase + GRUB_CS5536_SMB_REG_DATA);
167
168 return GRUB_ERR_NONE;
169 }
170
171 grub_err_t
172 grub_cs5536_init_smbus (grub_pci_device_t dev, grub_uint16_t divisor,
173 grub_port_t *smbbase)
174 {
175 grub_uint64_t smbbar;
176
177 smbbar = grub_cs5536_read_msr (dev, GRUB_CS5536_MSR_SMB_BAR);
178
179 /* FIXME */
180 if (!(smbbar & GRUB_CS5536_LBAR_ENABLE))
181 return grub_error(GRUB_ERR_IO, "SMB controller not enabled\n");
182 *smbbase = (smbbar & GRUB_CS5536_LBAR_ADDR_MASK) + GRUB_MACHINE_PCI_IO_BASE;
183
184 if (divisor < 8)
185 return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid divisor");
186
187 /* Disable SMB. */
188 grub_outb (0, *smbbase + GRUB_CS5536_SMB_REG_CTRL2);
189
190 /* Disable interrupts. */
191 grub_outb (0, *smbbase + GRUB_CS5536_SMB_REG_CTRL1);
192
193 /* Set as master. */
194 grub_outb (GRUB_CS5536_SMB_REG_ADDR_MASTER,
195 *smbbase + GRUB_CS5536_SMB_REG_ADDR);
196
197 /* Launch. */
198 grub_outb (((divisor >> 7) & 0xff), *smbbase + GRUB_CS5536_SMB_REG_CTRL3);
199 grub_outb (((divisor << 1) & 0xfe) | GRUB_CS5536_SMB_REG_CTRL2_ENABLE,
200 *smbbase + GRUB_CS5536_SMB_REG_CTRL2);
201
202 return GRUB_ERR_NONE;
203 }
204
205 grub_err_t
206 grub_cs5536_read_spd (grub_port_t smbbase, grub_uint8_t dev,
207 struct grub_smbus_spd *res)
208 {
209 grub_err_t err;
210 grub_size_t size;
211 grub_uint8_t b;
212 grub_size_t ptr;
213
214 err = grub_cs5536_read_spd_byte (smbbase, dev, 0, &b);
215 if (err)
216 return err;
217 if (b == 0)
218 return grub_error (GRUB_ERR_IO, "no SPD found");
219 size = b;
220
221 ((grub_uint8_t *) res)[0] = b;
222 for (ptr = 1; ptr < size; ptr++)
223 {
224 err = grub_cs5536_read_spd_byte (smbbase, dev, ptr,
225 &((grub_uint8_t *) res)[ptr]);
226 if (err)
227 return err;
228 }
229 return GRUB_ERR_NONE;
230 }
231
232 static inline void
233 set_io_space (grub_pci_device_t dev, int num, grub_uint16_t start,
234 grub_uint16_t len)
235 {
236 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_GL_REGIONS_START + num,
237 ((((grub_uint64_t) start + len - 4)
238 << GRUB_CS5536_MSR_GL_REGION_IO_TOP_SHIFT)
239 & GRUB_CS5536_MSR_GL_REGION_TOP_MASK)
240 | (((grub_uint64_t) start
241 << GRUB_CS5536_MSR_GL_REGION_IO_BASE_SHIFT)
242 & GRUB_CS5536_MSR_GL_REGION_BASE_MASK)
243 | GRUB_CS5536_MSR_GL_REGION_IO
244 | GRUB_CS5536_MSR_GL_REGION_ENABLE);
245 }
246
247 static inline void
248 set_iod (grub_pci_device_t dev, int num, int dest, int start, int mask)
249 {
250 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_GL_IOD_START + num,
251 ((grub_uint64_t) dest << GRUB_CS5536_IOD_DEST_SHIFT)
252 | (((grub_uint64_t) start & GRUB_CS5536_IOD_ADDR_MASK)
253 << GRUB_CS5536_IOD_BASE_SHIFT)
254 | ((mask & GRUB_CS5536_IOD_ADDR_MASK)
255 << GRUB_CS5536_IOD_MASK_SHIFT));
256 }
257
258 static inline void
259 set_p2d (grub_pci_device_t dev, int num, int dest, grub_uint32_t start)
260 {
261 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_GL_P2D_START + num,
262 (((grub_uint64_t) dest) << GRUB_CS5536_P2D_DEST_SHIFT)
263 | ((grub_uint64_t) (start >> GRUB_CS5536_P2D_LOG_ALIGN)
264 << GRUB_CS5536_P2D_BASE_SHIFT)
265 | (((1 << (32 - GRUB_CS5536_P2D_LOG_ALIGN)) - 1)
266 << GRUB_CS5536_P2D_MASK_SHIFT));
267 }
268
269 void
270 grub_cs5536_init_geode (grub_pci_device_t dev)
271 {
272 /* Enable more BARs. */
273 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_IRQ_MAP_BAR,
274 GRUB_CS5536_LBAR_TURN_ON | GRUB_CS5536_LBAR_IRQ_MAP);
275 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_MFGPT_BAR,
276 GRUB_CS5536_LBAR_TURN_ON | GRUB_CS5536_LBAR_MFGPT);
277 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_ACPI_BAR,
278 GRUB_CS5536_LBAR_TURN_ON | GRUB_CS5536_LBAR_ACPI);
279 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_PM_BAR,
280 GRUB_CS5536_LBAR_TURN_ON | GRUB_CS5536_LBAR_PM);
281
282 /* Setup DIVIL. */
283 #ifdef GRUB_MACHINE_MIPS_LOONGSON
284 switch (grub_arch_machine)
285 {
286 case GRUB_ARCH_MACHINE_YEELOONG:
287 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_DIVIL_LEG_IO,
288 GRUB_CS5536_MSR_DIVIL_LEG_IO_MODE_X86
289 | GRUB_CS5536_MSR_DIVIL_LEG_IO_F_REMAP
290 | GRUB_CS5536_MSR_DIVIL_LEG_IO_RTC_ENABLE0
291 | GRUB_CS5536_MSR_DIVIL_LEG_IO_RTC_ENABLE1);
292 break;
293 case GRUB_ARCH_MACHINE_FULOONG2F:
294 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_DIVIL_LEG_IO,
295 GRUB_CS5536_MSR_DIVIL_LEG_IO_UART2_COM3
296 | GRUB_CS5536_MSR_DIVIL_LEG_IO_UART1_COM1
297 | GRUB_CS5536_MSR_DIVIL_LEG_IO_MODE_X86
298 | GRUB_CS5536_MSR_DIVIL_LEG_IO_F_REMAP
299 | GRUB_CS5536_MSR_DIVIL_LEG_IO_RTC_ENABLE0
300 | GRUB_CS5536_MSR_DIVIL_LEG_IO_RTC_ENABLE1);
301 break;
302 }
303 #endif
304 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_DIVIL_IRQ_MAPPER_PRIMARY_MASK,
305 (~GRUB_CS5536_DIVIL_LPC_INTERRUPTS) & 0xffff);
306 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_DIVIL_IRQ_MAPPER_LPC_MASK,
307 GRUB_CS5536_DIVIL_LPC_INTERRUPTS);
308 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_DIVIL_LPC_SERIAL_IRQ_CONTROL,
309 GRUB_CS5536_MSR_DIVIL_LPC_SERIAL_IRQ_CONTROL_ENABLE);
310
311 /* Initialise USB controller. */
312 /* FIXME: assign adresses dynamically. */
313 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_USB_OHCI_BASE,
314 GRUB_CS5536_MSR_USB_BASE_BUS_MASTER
315 | GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE
316 | 0x05024000);
317 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_USB_EHCI_BASE,
318 GRUB_CS5536_MSR_USB_BASE_BUS_MASTER
319 | GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE
320 | (0x20ULL << GRUB_CS5536_MSR_USB_EHCI_BASE_FLDJ_SHIFT)
321 | 0x05023000);
322 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_USB_CONTROLLER_BASE,
323 GRUB_CS5536_MSR_USB_BASE_BUS_MASTER
324 | GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE | 0x05020000);
325 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_USB_OPTION_CONTROLLER_BASE,
326 GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE | 0x05022000);
327 set_p2d (dev, 0, GRUB_CS5536_DESTINATION_USB, 0x05020000);
328 set_p2d (dev, 1, GRUB_CS5536_DESTINATION_USB, 0x05022000);
329 set_p2d (dev, 5, GRUB_CS5536_DESTINATION_USB, 0x05024000);
330 set_p2d (dev, 6, GRUB_CS5536_DESTINATION_USB, 0x05023000);
331
332 {
333 volatile grub_uint32_t *oc;
334 oc = grub_pci_device_map_range (dev, 0x05022000,
335 GRUB_CS5536_USB_OPTION_REGS_SIZE);
336
337 oc[GRUB_CS5536_USB_OPTION_REG_UOCMUX] =
338 (oc[GRUB_CS5536_USB_OPTION_REG_UOCMUX]
339 & ~GRUB_CS5536_USB_OPTION_REG_UOCMUX_PMUX_MASK)
340 | GRUB_CS5536_USB_OPTION_REG_UOCMUX_PMUX_HC;
341 grub_pci_device_unmap_range (dev, oc, GRUB_CS5536_USB_OPTION_REGS_SIZE);
342 }
343
344 /* Setup IDE controller. */
345 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_IDE_IO_BAR,
346 GRUB_CS5536_LBAR_IDE
347 | GRUB_CS5536_MSR_IDE_IO_BAR_UNITS);
348 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_IDE_CFG,
349 GRUB_CS5536_MSR_IDE_CFG_CHANNEL_ENABLE);
350 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_IDE_TIMING,
351 (GRUB_CS5536_MSR_IDE_TIMING_PIO0
352 << GRUB_CS5536_MSR_IDE_TIMING_DRIVE0_SHIFT)
353 | (GRUB_CS5536_MSR_IDE_TIMING_PIO0
354 << GRUB_CS5536_MSR_IDE_TIMING_DRIVE1_SHIFT));
355 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_IDE_CAS_TIMING,
356 (GRUB_CS5536_MSR_IDE_CAS_TIMING_CMD_PIO0
357 << GRUB_CS5536_MSR_IDE_CAS_TIMING_CMD_SHIFT)
358 | (GRUB_CS5536_MSR_IDE_CAS_TIMING_PIO0
359 << GRUB_CS5536_MSR_IDE_CAS_TIMING_DRIVE0_SHIFT)
360 | (GRUB_CS5536_MSR_IDE_CAS_TIMING_PIO0
361 << GRUB_CS5536_MSR_IDE_CAS_TIMING_DRIVE1_SHIFT));
362
363 /* Setup Geodelink PCI. */
364 grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_GL_PCI_CTRL,
365 (4ULL << GRUB_CS5536_MSR_GL_PCI_CTRL_OUT_THR_SHIFT)
366 | (4ULL << GRUB_CS5536_MSR_GL_PCI_CTRL_IN_THR_SHIFT)
367 | (8ULL << GRUB_CS5536_MSR_GL_PCI_CTRL_LATENCY_SHIFT)
368 | GRUB_CS5536_MSR_GL_PCI_CTRL_IO_ENABLE
369 | GRUB_CS5536_MSR_GL_PCI_CTRL_MEMORY_ENABLE);
370
371 /* Setup windows. */
372 set_io_space (dev, 0, GRUB_CS5536_LBAR_SMBUS, GRUB_CS5536_SMBUS_REGS_SIZE);
373 set_io_space (dev, 1, GRUB_CS5536_LBAR_GPIO, GRUB_CS5536_GPIO_REGS_SIZE);
374 set_io_space (dev, 2, GRUB_CS5536_LBAR_MFGPT, GRUB_CS5536_MFGPT_REGS_SIZE);
375 set_io_space (dev, 3, GRUB_CS5536_LBAR_IRQ_MAP, GRUB_CS5536_IRQ_MAP_REGS_SIZE);
376 set_io_space (dev, 4, GRUB_CS5536_LBAR_PM, GRUB_CS5536_PM_REGS_SIZE);
377 set_io_space (dev, 5, GRUB_CS5536_LBAR_ACPI, GRUB_CS5536_ACPI_REGS_SIZE);
378 set_iod (dev, 0, GRUB_CS5536_DESTINATION_IDE, GRUB_ATA_CH0_PORT1, 0xffff8);
379 set_iod (dev, 1, GRUB_CS5536_DESTINATION_ACC, GRUB_CS5536_LBAR_ACC, 0xfff80);
380 set_iod (dev, 2, GRUB_CS5536_DESTINATION_IDE, GRUB_CS5536_LBAR_IDE, 0xffff0);
381 }