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