2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2010 Free Software Foundation, Inc.
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.
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.
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/>.
19 #include <grub/types.h>
20 #include <grub/cs5536.h>
22 #include <grub/time.h>
24 #ifdef GRUB_MACHINE_MIPS_LOONGSON
25 #include <grub/machine/kernel.h>
30 GRUB_MOD_LICENSE ("GPLv3+");
32 /* Context for grub_cs5536_find. */
33 struct grub_cs5536_find_ctx
35 grub_pci_device_t
*devp
;
39 /* Helper for grub_cs5536_find. */
41 grub_cs5536_find_iter (grub_pci_device_t dev
, grub_pci_id_t pciid
, void *data
)
43 struct grub_cs5536_find_ctx
*ctx
= data
;
45 if (pciid
== GRUB_CS5536_PCIID
)
55 grub_cs5536_find (grub_pci_device_t
*devp
)
57 struct grub_cs5536_find_ctx ctx
= {
62 grub_pci_iterate (grub_cs5536_find_iter
, &ctx
);
68 grub_cs5536_read_msr (grub_pci_device_t dev
, grub_uint32_t addr
)
70 grub_uint64_t ret
= 0;
71 grub_pci_write (grub_pci_make_address (dev
, GRUB_CS5536_MSR_MAILBOX_ADDR
),
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
)))
83 grub_cs5536_write_msr (grub_pci_device_t dev
, grub_uint32_t addr
,
86 grub_pci_write (grub_pci_make_address (dev
, GRUB_CS5536_MSR_MAILBOX_ADDR
),
88 grub_pci_write (grub_pci_make_address (dev
, GRUB_CS5536_MSR_MAILBOX_DATA0
),
90 grub_pci_write (grub_pci_make_address (dev
, GRUB_CS5536_MSR_MAILBOX_DATA1
),
95 grub_cs5536_smbus_wait (grub_port_t smbbase
)
97 grub_uint64_t start
= grub_get_time_ms ();
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");
114 grub_cs5536_read_spd_byte (grub_port_t smbbase
, grub_uint8_t dev
,
115 grub_uint8_t addr
, grub_uint8_t
*res
)
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
);
124 /* Send device address. */
125 err
= grub_cs5536_smbus_wait (smbbase
);
128 grub_outb (dev
<< 1, smbbase
+ GRUB_CS5536_SMB_REG_DATA
);
131 err
= grub_cs5536_smbus_wait (smbbase
);
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
);
138 /* Send byte address. */
139 grub_outb (addr
, smbbase
+ GRUB_CS5536_SMB_REG_DATA
);
142 err
= grub_cs5536_smbus_wait (smbbase
);
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
);
149 /* Send device address. */
150 err
= grub_cs5536_smbus_wait (smbbase
);
153 grub_outb ((dev
<< 1) | 1, smbbase
+ GRUB_CS5536_SMB_REG_DATA
);
156 err
= grub_cs5536_smbus_wait (smbbase
);
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
);
163 err
= grub_cs5536_smbus_wait (smbbase
);
166 *res
= grub_inb (smbbase
+ GRUB_CS5536_SMB_REG_DATA
);
168 return GRUB_ERR_NONE
;
172 grub_cs5536_init_smbus (grub_pci_device_t dev
, grub_uint16_t divisor
,
173 grub_port_t
*smbbase
)
175 grub_uint64_t smbbar
;
177 smbbar
= grub_cs5536_read_msr (dev
, GRUB_CS5536_MSR_SMB_BAR
);
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
;
185 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "invalid divisor");
188 grub_outb (0, *smbbase
+ GRUB_CS5536_SMB_REG_CTRL2
);
190 /* Disable interrupts. */
191 grub_outb (0, *smbbase
+ GRUB_CS5536_SMB_REG_CTRL1
);
194 grub_outb (GRUB_CS5536_SMB_REG_ADDR_MASTER
,
195 *smbbase
+ GRUB_CS5536_SMB_REG_ADDR
);
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
);
202 return GRUB_ERR_NONE
;
206 grub_cs5536_read_spd (grub_port_t smbbase
, grub_uint8_t dev
,
207 struct grub_smbus_spd
*res
)
214 err
= grub_cs5536_read_spd_byte (smbbase
, dev
, 0, &b
);
218 return grub_error (GRUB_ERR_IO
, "no SPD found");
221 ((grub_uint8_t
*) res
)[0] = b
;
222 for (ptr
= 1; ptr
< size
; ptr
++)
224 err
= grub_cs5536_read_spd_byte (smbbase
, dev
, ptr
,
225 &((grub_uint8_t
*) res
)[ptr
]);
229 return GRUB_ERR_NONE
;
233 set_io_space (grub_pci_device_t dev
, int num
, grub_uint16_t start
,
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
);
248 set_iod (grub_pci_device_t dev
, int num
, int dest
, int start
, int mask
)
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
));
259 set_p2d (grub_pci_device_t dev
, int num
, int dest
, grub_uint32_t start
)
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
));
270 grub_cs5536_init_geode (grub_pci_device_t dev
)
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
);
283 #ifdef GRUB_MACHINE_MIPS_LOONGSON
284 switch (grub_arch_machine
)
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
);
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
);
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
);
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
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
)
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);
333 volatile grub_uint32_t
*oc
;
334 oc
= grub_pci_device_map_range (dev
, 0x05022000,
335 GRUB_CS5536_USB_OPTION_REGS_SIZE
);
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
);
344 /* Setup IDE controller. */
345 grub_cs5536_write_msr (dev
, GRUB_CS5536_MSR_IDE_IO_BAR
,
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
));
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
);
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);