]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Working SPD reading in fwstart.img
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Wed, 17 Feb 2010 20:10:58 +0000 (21:10 +0100)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Wed, 17 Feb 2010 20:10:58 +0000 (21:10 +0100)
bus/cs5536.c
conf/mips-yeeloong.rmk
include/grub/cs5536.h
include/grub/mips/yeeloong/boot.h
include/grub/mips/yeeloong/pci.h
include/grub/pci.h
include/grub/smbus.h
kern/mips/yeeloong/fwstart.S

index 3a7ac2bb10214920c69c7bcf48a18190cdfab335..f481397b726c9353bda7ad86ffa4e87e513bbb5a 100644 (file)
@@ -80,11 +80,11 @@ grub_cs5536_smbus_wait (grub_port_t smbbase)
     {
       grub_uint8_t status;
       status = grub_inb (smbbase + GRUB_CS5536_SMB_REG_STATUS);
-      if (status & (1 << 6))
+      if (status & GRUB_CS5536_SMB_REG_STATUS_SDAST)
        return GRUB_ERR_NONE;   
-      if (status & (1 << 5))
+      if (status & GRUB_CS5536_SMB_REG_STATUS_BER)
        return grub_error (GRUB_ERR_IO, "SM bus error");
-      if (status & (1 << 4))
+      if (status & GRUB_CS5536_SMB_REG_STATUS_NACK)
        return grub_error (GRUB_ERR_IO, "NACK received");
       if (grub_get_time_ms () > start + 40)
        return grub_error (GRUB_ERR_IO, "SM stalled");
index 901a63dd0ba746f32398d339199d8ff6277a6cb0..aa3e96fa0368646e264930846299fb399599bfa7 100644 (file)
@@ -30,6 +30,14 @@ kernel_img_LDFLAGS = $(COMMON_LDFLAGS) -static-libgcc -lgcc \
        -Wl,-N,-S,-Ttext,$(LINK_BASE),-Bstatic
 kernel_img_FORMAT = binary
 
+pkglib_IMAGES += fwstart.img
+fwstart_img_SOURCES = kern/$(target_cpu)/$(target_machine)/fwstart.S
+fwstart_img_CFLAGS = $(COMMON_CFLAGS)
+fwstart_img_ASFLAGS = $(COMMON_ASFLAGS)
+fwstart_img_LDFLAGS = $(COMMON_LDFLAGS) -static-libgcc -lgcc \
+       -Wl,-N,-S,-Ttext,0xbfc00000,-Bstatic
+fwstart_img_FORMAT = binary
+
 # For ata.mod.
 pkglib_MODULES += ata.mod
 ata_mod_SOURCES = disk/ata.c
index 233362160d45bd653d896371887db849edb3cad7..51a21eb6e456053c6541fc6a45c9fc80423ee3ee 100644 (file)
 #ifndef GRUB_CS5536_HEADER
 #define GRUB_CS5536_HEADER 1
 
+#ifndef ASM_FILE
 #include <grub/pci.h>
 #include <grub/err.h>
 #include <grub/smbus.h>
+#endif
 
 #define GRUB_CS5536_PCIID 0x208f1022
 #define GRUB_CS5536_MSR_MAILBOX_ADDR  0xf4
 #define GRUB_CS5536_MSR_MAILBOX_DATA0 0xf8
 #define GRUB_CS5536_MSR_MAILBOX_DATA1 0xfc
 #define GRUB_CS5536_MSR_SMB_BAR 0x8000000b
+#define GRUB_CS5536_MSR_GPIO_BAR 0x8000000c
 #define GRUB_CS5536_SMB_REG_DATA 0x0
 #define GRUB_CS5536_SMB_REG_STATUS 0x1
+#define GRUB_CS5536_SMB_REG_STATUS_SDAST (1 << 6)
+#define GRUB_CS5536_SMB_REG_STATUS_BER (1 << 5)
+#define GRUB_CS5536_SMB_REG_STATUS_NACK (1 << 4)
 #define GRUB_CS5536_SMB_REG_CTRL1 0x3
 #define GRUB_CS5536_SMB_REG_CTRL1_START 0x01
 #define GRUB_CS5536_SMB_REG_CTRL1_STOP  0x02
 #define GRUB_CS5536_SMB_REG_CTRL3 0x6
 
 #define GRUB_CS5536_LBAR_ADDR_MASK 0x000000000000fff8ULL
-#define GRUB_CS5536_LBAR_ADDR_OFF 3
 #define GRUB_CS5536_LBAR_ENABLE 0x0000000100000000ULL
-#define GRUB_SMB_RAM_START_ADDR 0x50
-#define GRUB_SMB_RAM_NUM_MAX 0x08
 
+/* PMON-compatible LBARs.  */
+#define GRUB_CS5536_LBAR_GPIO      0x0b000
+#define GRUB_CS5536_LBAR_SMBUS     0x0b390
+
+#define GRUB_GPIO_SMBUS_PINS ((1 << 14) | (1 << 15))
+#define GRUB_GPIO_REG_OUT_EN 0x4
+#define GRUB_GPIO_REG_OUT_AUX1 0x10
+#define GRUB_GPIO_REG_IN_EN 0x20
+#define GRUB_GPIO_REG_IN_AUX1 0x34
+
+#ifndef ASM_FILE
 int grub_cs5536_find (grub_pci_device_t *devp);
 
 grub_uint64_t grub_cs5536_read_msr (grub_pci_device_t dev, grub_uint32_t addr);
@@ -58,5 +72,6 @@ grub_err_t grub_cs5536_read_spd (grub_port_t smbbase, grub_uint8_t dev,
 grub_err_t grub_cs5536_smbus_wait (grub_port_t smbbase);
 grub_err_t grub_cs5536_init_smbus (grub_pci_device_t dev, grub_uint16_t divisor,
                                   grub_port_t *smbbase);
+#endif
 
 #endif
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..205c557fe47827f5e7daec5bb9d80aacbf6e1e1e 100644 (file)
@@ -0,0 +1,27 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2010  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_BOOT_MACHINE_HEADER
+#define GRUB_BOOT_MACHINE_HEADER       1
+
+#define GRUB_MACHINE_FLASH_START            0xbfc00000
+#define GRUB_MACHINE_FLASH_TLB_REFILL       0xbfc00200
+#define GRUB_MACHINE_FLASH_CACHE_ERROR      0xbfc00300
+#define GRUB_MACHINE_FLASH_OTHER_EXCEPTION  0xbfc00380
+
+#endif
index c7bd31d4f071cfd5c6abb119570582a673c74761..f50cdcb35bb025c493e0f387f59fda6c0768a2fa 100644 (file)
 #ifndef        GRUB_MACHINE_PCI_H
 #define        GRUB_MACHINE_PCI_H      1
 
+#ifndef ASM_FILE
 #include <grub/types.h>
 #include <grub/cpu/io.h>
+#endif
 
 #define GRUB_PCI_NUM_BUS        1
 #define GRUB_PCI_NUM_DEVICES    16
 
 #define GRUB_MACHINE_PCI_IO_BASE          0xbfd00000
 #define GRUB_MACHINE_PCI_CONFSPACE        0xbfe80000
+
+#ifndef ASM_FILE
 #define GRUB_MACHINE_PCI_CONF_CTRL_REG    (*(volatile grub_uint32_t *) 0xbfe00118)
 #define GRUB_MACHINE_PCI_IO_CTRL_REG      (*(volatile grub_uint32_t *) 0xbfe00110)
+#else
+#define GRUB_MACHINE_PCI_CONF_CTRL_REG_HI    0xbfe0
+#define GRUB_MACHINE_PCI_CONF_CTRL_REG_LO    0x0118
+#endif
 #define GRUB_MACHINE_PCI_WIN_MASK_SIZE    6
 #define GRUB_MACHINE_PCI_WIN_MASK         ((1 << GRUB_MACHINE_PCI_WIN_MASK_SIZE) - 1)
 
@@ -46,6 +54,7 @@
 #define GRUB_MACHINE_PCI_WIN2_ADDR        0xb4000000
 #define GRUB_MACHINE_PCI_WIN3_ADDR        0xb8000000
 
+#ifndef ASM_FILE
 static inline grub_uint32_t
 grub_pci_read (grub_pci_address_t addr)
 {
@@ -101,5 +110,6 @@ void
 grub_pci_device_unmap_range (grub_pci_device_t dev __attribute__ ((unused)),
                             volatile void *mem,
                             grub_size_t size __attribute__ ((unused)));
+#endif
 
 #endif /* GRUB_MACHINE_PCI_H */
index 1f3ac7fc795126b936c2564dc8b0cf0d0331b7df..17f769f4cb5244465478cb10e994ac7e13f1937f 100644 (file)
 #ifndef        GRUB_PCI_H
 #define        GRUB_PCI_H      1
 
+#ifndef ASM_FILE
 #include <grub/types.h>
 #include <grub/symbol.h>
+#endif
 
 #define  GRUB_PCI_ADDR_SPACE_MASK      0x01
 #define  GRUB_PCI_ADDR_SPACE_MEMORY    0x00
@@ -66,6 +68,7 @@
 #define  GRUB_PCI_REG_MIN_GNT      0x3e
 #define  GRUB_PCI_REG_MAX_LAT      0x3f
 
+#ifndef ASM_FILE
 typedef grub_uint32_t grub_pci_id_t;
 
 #ifdef GRUB_UTIL
@@ -106,5 +109,6 @@ grub_pci_address_t EXPORT_FUNC(grub_pci_make_address) (grub_pci_device_t dev,
                                                       int reg);
 
 void EXPORT_FUNC(grub_pci_iterate) (grub_pci_iteratefunc_t hook);
+#endif
 
 #endif /* GRUB_PCI_H */
index 6e9b5005c002ac380474f4f2a36cfd166eea84da..b9cc6ab9e4cd4b8d4f2c1c8303a628a2c41b5789 100644 (file)
@@ -19,6 +19,9 @@
 #ifndef GRUB_SMBUS_HEADER
 #define GRUB_SMBUS_HEADER 1
 
+#define GRUB_SMB_RAM_START_ADDR 0x50
+#define GRUB_SMB_RAM_NUM_MAX 0x08
+
 struct grub_smbus_spd
 {
   grub_uint8_t written_size;
index 5132d7584b99bacbaf4ac04c11f7a48c12491614..59fe4d3e6d4023210ec7518ce0a5d9caf117db96 100644 (file)
  */
 
 #include <grub/mips/yeeloong/serial.h>
+#include <grub/mips/yeeloong/pci.h>
+#include <grub/mips/yeeloong/boot.h>
+#include <grub/pci.h>
 #include <grub/serial.h>
+#include <grub/cs5536.h>
 
        .set noreorder
        .set noat
        .set nomacro
 
+       .global start,_start,__start
+start:
+_start:
+__start:       
        bal serial_hw_init
         nop
-self:  
-       b self
-        nop
+       /* Find CS5536 controller.  */
+       /* $t4 chooses device in priority encoding.  */
+       /* Resulting value is kept in GRUB_MACHINE_PCI_CONF_CTRL_REG.
+          This way we don't need to sacrifice a register for it.  */
+       /* We have only one bus (0). Function is 0.  */
+       lui $t0, GRUB_MACHINE_PCI_CONF_CTRL_REG_HI
+       lui $t1, %hi(GRUB_MACHINE_PCI_CONFSPACE)
+       lui $t3, %hi(GRUB_CS5536_PCIID)
+       addiu $t3, $t3, %lo(GRUB_CS5536_PCIID)
+       ori $t4, $zero, 1
+       lui $a0, %hi(no_cs5536)
+1:
+       andi $t4, $t4, 0xffff
+       beql  $t4, $zero, fatal
+        addiu $a0, $a0, %lo(no_cs5536)
+       sw   $t4, GRUB_MACHINE_PCI_CONF_CTRL_REG_LO($t0)
+       lw   $t2, (%lo(GRUB_MACHINE_PCI_CONFSPACE) + GRUB_PCI_REG_PCI_ID) ($t1)
+       bnel  $t2, $t3, 1b
+        sll $t4, $t4, 1
+
+       bal message
+        addiu $a0, $a0, %lo(cs5536_found)
+       bal printhex
+        move $a0, $t4
+
+       /* Initialise SMBus controller.  */
+       /* Set GPIO LBAR.  */
+       lui $a0, %hi(GRUB_CS5536_MSR_GPIO_BAR)
+       addiu $a0, $a0, %lo(GRUB_CS5536_MSR_GPIO_BAR)
+       ori $a1, $zero, GRUB_CS5536_LBAR_GPIO
+       /* Set mask to 0xf and enabled bit to 1.  */
+       bal wrmsr
+        ori $a2, $zero, 0xf001
+
+       /* Set SMBUS LBAR.  */
+       lui $a0, %hi(GRUB_CS5536_MSR_SMB_BAR)
+       addiu $a0, $a0, %lo(GRUB_CS5536_MSR_SMB_BAR)
+       ori $a1, $zero, GRUB_CS5536_LBAR_SMBUS
+       /* Set mask to 0xf and enabled bit to 1.  */
+       bal wrmsr
+        ori $a2, $zero, 0xf001
+
+       lui $a0, %hi(smbus_enabled)
+       bal message
+         addiu $a0, $a0, %lo(smbus_enabled)
+
+       /* Enable SMBus controller pins.  */
+       lui $t0, %hi(GRUB_MACHINE_PCI_IO_BASE + GRUB_CS5536_LBAR_GPIO)
+       ori $t1, $zero, GRUB_GPIO_SMBUS_PINS
+       sw  $t1, %lo(GRUB_MACHINE_PCI_IO_BASE + GRUB_CS5536_LBAR_GPIO + GRUB_GPIO_REG_OUT_EN) ($t0)
+       sw  $t1, %lo(GRUB_MACHINE_PCI_IO_BASE + GRUB_CS5536_LBAR_GPIO + GRUB_GPIO_REG_OUT_AUX1) ($t0)
+       sw  $t1, %lo(GRUB_MACHINE_PCI_IO_BASE + GRUB_CS5536_LBAR_GPIO + GRUB_GPIO_REG_IN_EN) ($t0)
+       sw  $t1, %lo(GRUB_MACHINE_PCI_IO_BASE + GRUB_CS5536_LBAR_GPIO + GRUB_GPIO_REG_IN_AUX1) ($t0)
+
+       lui $t0, %hi(GRUB_MACHINE_PCI_IO_BASE + GRUB_CS5536_LBAR_SMBUS)
+
+       /* Disable SMB.  */
+       sb $zero, %lo(GRUB_MACHINE_PCI_IO_BASE + GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL2) ($t0)
+
+       /* Disable interrupts.  */
+       sb $zero, %lo(GRUB_MACHINE_PCI_IO_BASE + GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1) ($t0)
+
+       /* Set as master.  */
+       sb $zero, %lo(GRUB_MACHINE_PCI_IO_BASE + GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_ADDR) ($t0)
+
+       /* Launch SMBus controller at slowest speed possible.  */
+       ori $t1, $zero, 0xff
+       sb $t1, %lo(GRUB_MACHINE_PCI_IO_BASE + GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL3) ($t0)
+       sb $t1, %lo(GRUB_MACHINE_PCI_IO_BASE + GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL2) ($t0)
+
+       ori $a0, $zero, 0x50
+       bal read_spd
+        move $a1, $zero
+       bal printhex
+        move $a0, $v0
+
+       ori $a0, $zero, 0x50
+       bal read_spd
+        ori $a1, $zero, 2
+       bal printhex
+        move $a0, $v0
+       
+       lui $a0, %hi(not_implemented)
+       b fatal
+        addiu $a0, $a0, %lo(not_implemented)
 
        /* Same as similarly named C function but in asm since
           we need it early.  */
@@ -70,12 +160,164 @@ serial_hw_init:
        /* In: $a0 = asciiz message. Out: none. Clobbered: $t0, $t1, $a0.  */
 message:
        lui $t0, GRUB_MACHINE_SERIAL_PORT_HI
-1:     
+1:
+       lb $t1, (GRUB_MACHINE_SERIAL_PORT_LO + UART_LSR)($t0)
+       andi $t1, $t1, UART_EMPTY_TRANSMITTER
+       beq $t1, $zero, 1b
+        nop
        lb  $t1, 0($a0)
-       addiu $a0, $a0, 1
+       sb  $t1, (GRUB_MACHINE_SERIAL_PORT_LO + UART_TX)($t0)
        bne $t1, $zero, 1b
-        sb  $t1, (GRUB_MACHINE_SERIAL_PORT_LO + UART_TX)($t0)
+        addiu $a0, $a0, 1
+       jr  $ra
+        nop
+
+       /* Print 32-bit hexadecimal on serial.
+           In: $a0. Out: None. Clobbered: $a0, $t0, $t1, $t2
+       */
+printhex:
+       lui $t0, GRUB_MACHINE_SERIAL_PORT_HI
+       ori $t2, $zero, 8
+1:
+       lb $t1, (GRUB_MACHINE_SERIAL_PORT_LO + UART_LSR)($t0)
+       andi $t1, $t1, UART_EMPTY_TRANSMITTER
+       beq $t1, $zero, 1b
+        nop
+       srl  $t1, $a0, 28
+       addiu $t1, $t1, -10
+       blt  $t1, $zero, 2f
+        sll  $a0, $a0, 4
+       addiu $t1, $t1, 'A'-10-'0' 
+2:     addiu $t1, $t1, '0'+10
+       sb  $t1, (GRUB_MACHINE_SERIAL_PORT_LO + UART_TX)($t0)
+       addiu $t2, $t2, -1
+       bne $t2, $zero, 1b
+        nop
        jr  $ra
         nop
 
+fatal:
+       bal message
+        nop
+self:
+       b self
+        nop
+
+       . = start + GRUB_MACHINE_FLASH_TLB_REFILL - GRUB_MACHINE_FLASH_START
+tlb_refill:    
+       lui $a0, %hi(unhandled_tlb_refill)
+       b fatal
+        addiu $a0, $a0, %lo(unhandled_tlb_refill)
+
+       . = start + GRUB_MACHINE_FLASH_CACHE_ERROR - GRUB_MACHINE_FLASH_START
+cache_error:
+       lui $a0, %hi(unhandled_cache_error)
+       b fatal
+        addiu $a0, $a0, %lo(unhandled_cache_error)
+
+       . = start + GRUB_MACHINE_FLASH_OTHER_EXCEPTION - GRUB_MACHINE_FLASH_START
+other_exception:
+       lui $a0, %hi(unhandled_exception)
+       b fatal
+        addiu $a0, $a0, %lo(unhandled_exception)
+
+       /* Write CS5536 MSR.
+           In:   $a0 address, $a1 lower word, $a2 upper word.
+           Out:         None
+           Clobbered:   $t0
+       */
+wrmsr:
+       lui $t0, %hi(GRUB_MACHINE_PCI_CONFSPACE)
+       sw  $a0, (%lo(GRUB_MACHINE_PCI_CONFSPACE) + GRUB_CS5536_MSR_MAILBOX_ADDR) ($t0)
+       sw  $a1, (%lo(GRUB_MACHINE_PCI_CONFSPACE) + GRUB_CS5536_MSR_MAILBOX_DATA0) ($t0)
+       jr $ra
+        sw  $a2, (%lo(GRUB_MACHINE_PCI_CONFSPACE) + GRUB_CS5536_MSR_MAILBOX_DATA1) ($t0)
+
+       /* Wait for SMBus data or empty transmitter.  */
+       /* In: $a0 = exception handler. Out: none. Clobbered: $t0, $t1  */
+smbus_wait:
+1:     
+       lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_STATUS + GRUB_MACHINE_PCI_IO_BASE)
+       lb $t0, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_STATUS + GRUB_MACHINE_PCI_IO_BASE) ($t0)
+       andi $t1, $t0, GRUB_CS5536_SMB_REG_STATUS_SDAST
+       bne $t1, $zero, return
+        nop
+       andi $t1, $t0, (GRUB_CS5536_SMB_REG_STATUS_BER | GRUB_CS5536_SMB_REG_STATUS_NACK)
+       beq $t1, $zero, 1b
+        nop
+       jr $a0
+        nop
+return:
+       jr $ra
+        nop
+       
+       /* Read SPD byte. In: $a0 device, $a1 byte. Out: $v0 read byte (0x100 on failure).
+           Clobbered: $t0, $t1, $t2, $t3, $a0. */
+read_spd:
+       move $t2, $a0
+       move $t3, $ra
+       lui $a0, %hi(read_spd_fail)
+       addiu $a0, $a0, %hi(read_spd_fail)
+
+       /* Send START.  */
+       lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE)
+       lb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE) ($t0)
+       ori $t1, $t1, GRUB_CS5536_SMB_REG_CTRL1_START
+       bal smbus_wait
+        sb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE) ($t0)
+       
+       /* Send device address.  */
+       lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_DATA + GRUB_MACHINE_PCI_IO_BASE)
+       sll $t1, $t2, 1
+       bal smbus_wait
+        sb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_DATA + GRUB_MACHINE_PCI_IO_BASE) ($t0)
+
+       /* Send ACK.  */
+       lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE)
+       lb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE) ($t0)
+       ori $t1, $t1, GRUB_CS5536_SMB_REG_CTRL1_ACK
+       sb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE) ($t0)
+
+       /* Send byte address.  */
+       lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_DATA + GRUB_MACHINE_PCI_IO_BASE)
+       bal smbus_wait
+        sb $a1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_DATA + GRUB_MACHINE_PCI_IO_BASE) ($t0)
+
+       /* Send START.  */
+       lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE)
+       lb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE) ($t0)
+       ori $t1, $t1, GRUB_CS5536_SMB_REG_CTRL1_START
+       bal smbus_wait
+        sb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE) ($t0)
+
+       /* Send device address.  */
+       lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_DATA + GRUB_MACHINE_PCI_IO_BASE)
+       sll $t1, $t2, 1
+       ori $t1, $t1, 1
+       bal smbus_wait
+        sb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_DATA + GRUB_MACHINE_PCI_IO_BASE) ($t0)
+
+       /* Send STOP.  */
+       lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE)
+       lb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE) ($t0)
+       ori $t1, $t1, GRUB_CS5536_SMB_REG_CTRL1_STOP
+       bal smbus_wait
+        sb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE) ($t0)
+
+       lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_DATA + GRUB_MACHINE_PCI_IO_BASE)
+       lb $v0, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_DATA + GRUB_MACHINE_PCI_IO_BASE) ($t0)
+       jr $t3
+        andi $v0, $v0, 0xff
+read_spd_fail:
+       jr $t3
+        ori $v0, $v0, 0x100
+
 notification_string:   .asciz "GRUB "
+no_cs5536:     .asciz "No CS5536 found.\n\r"
+cs5536_found:  .asciz "CS5536 at "
+sm_failed: .asciz "SM transaction failed.\n\r"
+not_implemented: .asciz "Nothing more is implemented. Bye.\n\r"
+unhandled_tlb_refill:  .asciz "Unhandled TLB refill.\n\r"
+unhandled_cache_error: .asciz "Unhandled cache error.\n\r"
+unhandled_exception:   .asciz "Unhandled exception.\n\r"
+smbus_enabled: .asciz "SMBus controller enabled.\n\r"