Jens Rottmann | 3926b4c | 2013-03-01 19:41:41 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Initialize Broadcom 5785 GbE MAC embedded in AMD A55E (Hudson-E1) Southbridge |
| 3 | * by uploading a Selfboot Patch to the A55E's shadow ROM area. The patch |
| 4 | * itself supports the Broadcom 50610(M) PHY on the AMD Inagua. It is |
| 5 | * equivalent to Broadcom's SelfBoot patch V1.11 (sb5785m1.11). |
| 6 | * A modified variant, selected by CONFIG_BOARD_LIPPERT_FRONTRUNNER_AF supports |
| 7 | * the Micrel KSZ9021 PHY that was used on LiPPERT FrontRunner-AF (CFR-AF) |
| 8 | * revision 0v0, the first prototype. The board is history and this code now |
| 9 | * serves only to document the proprietary Selfboot Patch format and how to |
| 10 | * adapt it to a PHY unsupported by Broadcom. |
| 11 | * |
| 12 | * This file is part of the coreboot project. |
| 13 | * |
| 14 | * Copyright (C) 2012 LiPPERT ADLINK Technology GmbH |
| 15 | * (Written by Jens Rottmann <JRottmann@LiPPERTembedded.de>) |
| 16 | * |
| 17 | * This program is free software; you can redistribute it and/or modify |
| 18 | * it under the terms of the GNU General Public License as published by |
| 19 | * the Free Software Foundation; version 2 of the License. |
| 20 | * |
| 21 | * This program is distributed in the hope that it will be useful, |
| 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 24 | * GNU General Public License for more details. |
Jens Rottmann | 3926b4c | 2013-03-01 19:41:41 +0100 | [diff] [blame] | 25 | */ |
| 26 | |
| 27 | #include <types.h> |
Jens Rottmann | 3926b4c | 2013-03-01 19:41:41 +0100 | [diff] [blame] | 28 | #include <console/console.h> |
| 29 | #include <device/device.h> //Coreboot device access |
| 30 | #include <device/pci.h> |
| 31 | #include <delay.h> |
Julius Werner | 9ff8f6f | 2015-02-23 14:31:09 -0800 | [diff] [blame] | 32 | #include <endian.h> |
Jens Rottmann | 3926b4c | 2013-03-01 19:41:41 +0100 | [diff] [blame] | 33 | |
Paul Menzel | 5d74156 | 2013-03-29 11:07:22 +0100 | [diff] [blame] | 34 | void broadcom_init(void); |
| 35 | |
Jens Rottmann | 3926b4c | 2013-03-01 19:41:41 +0100 | [diff] [blame] | 36 | #define be16(x) cpu_to_be16(x) //a little easier to type |
| 37 | #define be(x) cpu_to_be32(x) //this is used a lot! |
| 38 | |
| 39 | /* C forces us to specify these before defining struct selfboot_patch :-( */ |
| 40 | #if !CONFIG_BOARD_LIPPERT_FRONTRUNNER_AF |
| 41 | #define INIT1_LENGTH 9 |
| 42 | #define INIT2_LENGTH 10 |
| 43 | #define INIT3_LENGTH 3 |
| 44 | #define INIT4_LENGTH 7 //this one may be 0 |
| 45 | #define PWRDN_LENGTH 5 |
| 46 | #else |
| 47 | #define INIT1_LENGTH 13 |
| 48 | #define INIT2_LENGTH 6 |
| 49 | #define INIT3_LENGTH 3 |
| 50 | #define INIT4_LENGTH 11 //this one may be 0 |
| 51 | #define PWRDN_LENGTH 4 |
| 52 | #endif |
| 53 | |
| 54 | |
| 55 | /* The AMD A55E (Hudson-E1) Southbridge contains an integrated Gigabit Ethernet |
| 56 | * MAC, however AMD's documentation merely defines the related balls (without |
| 57 | * fully describing their function) and states that only Broadcom 50610(M) PHYs |
| 58 | * will be supported, that's all. The Hudson register reference skips all MAC |
| 59 | * registers entirely, even AMD support doesn't seem to know more about it. |
| 60 | * |
| 61 | * As Broadcom refused to sell us any 50610 chips or provide any docs (or indeed |
| 62 | * even a price list) below $100K expected sales we had to figure out everything |
| 63 | * by ourselves. *Everything* below is the result of months of detective work, |
| 64 | * documented here lest it get lost: |
| 65 | * |
| 66 | * The AMD A55E's GbE MAC is a Broadcom 5785, which AMD obviously licensed as IP |
| 67 | * core. It uses a standard RGMII/MII interface and the Broadcom drivers will |
| 68 | * recognize it by its unchanged PCI ID 14E4:1699, however there are some |
| 69 | * specialties. |
| 70 | * |
| 71 | * The 5785 MAC can detect the link with 4 additional inputs, "phy_status[3:0]", |
| 72 | * 'snooping' on the PHY's LED outputs. Interpretation of the LEDs' patterns is |
| 73 | * programmed with register 0x5A4 of the MAC. AMD renamed them to "GBE_STAT" and |
| 74 | * won't say anything about their purpose. Appearently hardware designers are |
| 75 | * expected to blindly copy the Inagua reference schematic: GBE_STAT2: |
| 76 | * 0=activity; GBE_STAT[1:0]: 11=no link, 10=10Mbit, 01=100Mbit, 00=1Gbit. |
| 77 | * |
| 78 | * For package processing the 5785 also features a MIPS-based RISC CPU, booting |
| 79 | * from an internal ROM. The firmware loads config data and supplements (e.g. to |
| 80 | * support specific PHYs), named "Selfboot Patches", via the "NVRAM Interface", |
| 81 | * usually from an external EEPROM. The A55E doesn't have any balls for an ext. |
| 82 | * EEPROM, instead AMD added a small internal RAM. The BIOS is expected to copy |
| 83 | * the correct contents into this RAM (which only supports byte access!) upon |
| 84 | * each powerup. The A55E can trigger an SMI upon writes, enabling the BIOS to |
| 85 | * forward any changes to an actually 'NV' location, e.g. the BIOS's SPI flash, |
| 86 | * behind the scenes. AMD calls it "GEC shadow ROM", not describing what it's |
| 87 | * for nor mentioning the term "NVRAM". broadcom_init() below documents a |
| 88 | * procedure how to upload the patch. No SMI magic is installed, therefore |
| 89 | * 'NV'RAM writes won't be persistent. |
| 90 | * |
| 91 | * The "Selfboot Patch" can execute simple commands at various points during |
| 92 | * main firmware execution. This can be used to change config registers, |
| 93 | * initialize a specific PHY or work around firmware bugs. Broadcom provides |
| 94 | * suitable Patches only for their AC131 and 50610 PHYs (as binary blobs). I |
| 95 | * found them in DOS\sb_patch\5785\*\sb5785*.* in Driver_14_6_4_2.zip. (Note |
| 96 | * that every 32bit-word of these files must be byte-swapped before uploading |
| 97 | * them to the A55E.) |
| 98 | * |
| 99 | * Below is a derived Patch supporting the Micrel KSZ9021 PHY used on the |
| 100 | * LiPPERT CFR-AF PC/104 SBC instead, with detailled description of the format. |
| 101 | * (Here in correct order for upload.) |
| 102 | * |
| 103 | * This Patch made Ethernet work with Linux 3.3 - without having to modify the |
| 104 | * tg3.ko driver. Broadcom's Windows-Drivers still fail with "Code 10" however; |
| 105 | * disassembly showed they check the PHY ID and abort, because the Micrel PHY is |
| 106 | * not supported. |
| 107 | */ |
| 108 | |
| 109 | static struct selfboot_patch { //Watch out: all values are *BIG-ENDIAN*! |
| 110 | |
| 111 | struct { /* Global header */ |
| 112 | u8 signature; //0xA5 |
| 113 | u8 format; //bits 7-3: patch format; 2-0: revision |
| 114 | u8 mac_addr[6]; |
| 115 | u16 subsys_device; //IDs will be loaded into PCI config space |
| 116 | u16 subsys_vendor; |
| 117 | u16 pci_device; //PCI device ID; vendor is always Broadcom (0x14E4) |
| 118 | u8 unknown1[8]; //?, noticed no effect |
| 119 | u16 basic_config; //?, see below |
| 120 | u8 checksum; //byte sum of header == 0 |
| 121 | u8 unknown2; //?, patch rejected if changed |
| 122 | u16 patch_version; //10-8: major; 7-0: minor; 15-11: variant (1=a, 2=b, ...) |
| 123 | } header; |
| 124 | |
| 125 | struct { /* Init code */ |
| 126 | u8 checksum; //byte sum of init == 0 |
| 127 | u8 unknown; //?, looks unused |
| 128 | u8 num_hunks; //0x60 = 3 hunks, 0x80 = 4 hunks, other values not supported |
| 129 | u8 size; //total size of all hunk#_code[] in bytes |
| 130 | u8 hunk1_when; //mark when hunk1_code gets executed |
| 131 | u8 hunk1_size; //sizeof(hunk1_code) |
| 132 | u8 hunk2_when; |
| 133 | u8 hunk2_size; |
| 134 | u8 hunk3_when; |
| 135 | u8 hunk3_size; |
| 136 | u8 hunk4_when; //0x00 (padding) if only 3 hunks |
| 137 | u8 hunk4_size; //dito |
| 138 | u32 hunk1_code[INIT1_LENGTH]; //actual commands, see below |
| 139 | u32 hunk2_code[INIT2_LENGTH]; |
| 140 | u32 hunk3_code[INIT3_LENGTH]; |
| 141 | u32 hunk4_code[INIT4_LENGTH]; //missing (zero length) if only 3 hunks |
| 142 | } init; |
| 143 | |
| 144 | struct { /* Power down code */ |
| 145 | u8 checksum; //byte sum of powerdown == 0 |
| 146 | u8 unknown; //?, looks unused |
| 147 | u8 num_hunks; //0x20 = 1 hunk, other values not supported |
| 148 | u8 size; //total size of all hunk#_code[] in bytes |
| 149 | u8 hunk1_when; //mark when hunk1_code gets executed |
| 150 | u8 hunk1_size; //sizeof(hunk1_code) |
| 151 | u16 padding; //0x0000, hunk2 is not supported |
| 152 | u32 hunk1_code[PWRDN_LENGTH]; //commands, see below |
| 153 | } powerdown; |
| 154 | |
| 155 | } selfboot_patch = { |
| 156 | |
| 157 | /* Keep the following invariant for valid Selfboot patches */ |
| 158 | .header.signature = 0xA5, |
| 159 | .header.format = 0x23, //format 1 revision 3 |
| 160 | .header.unknown1 = { 0x61, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, |
| 161 | .header.checksum = 0, //calculated later |
| 162 | .header.unknown2 = 0x30, |
| 163 | .init.checksum = 0, //calculated later |
| 164 | .init.unknown = 0x00, |
| 165 | .init.num_hunks = sizeof(selfboot_patch.init.hunk4_code) ? 0x80 : 0x60, |
| 166 | .init.size = sizeof(selfboot_patch.init.hunk1_code) |
| 167 | + sizeof(selfboot_patch.init.hunk2_code) |
| 168 | + sizeof(selfboot_patch.init.hunk3_code) |
| 169 | + sizeof(selfboot_patch.init.hunk4_code), |
| 170 | .init.hunk1_size = sizeof(selfboot_patch.init.hunk1_code), |
| 171 | .init.hunk2_size = sizeof(selfboot_patch.init.hunk2_code), |
| 172 | .init.hunk3_size = sizeof(selfboot_patch.init.hunk3_code), |
| 173 | .init.hunk4_size = sizeof(selfboot_patch.init.hunk4_code), |
| 174 | .powerdown.checksum = 0, //calculated later |
| 175 | .powerdown.unknown = 0x00, |
| 176 | .powerdown.num_hunks = 0x20, |
| 177 | .powerdown.size = sizeof(selfboot_patch.powerdown.hunk1_code), |
| 178 | .powerdown.hunk1_size = sizeof(selfboot_patch.powerdown.hunk1_code), |
| 179 | .powerdown.padding = be16(0x0000), |
| 180 | |
| 181 | /* Only the lines below may be adapted to your needs ... */ |
| 182 | #if !CONFIG_BOARD_LIPPERT_FRONTRUNNER_AF |
| 183 | .header.mac_addr = { 0x00, 0x10, 0x18, 0x00, 0x00, 0x00 }, //Broadcom |
| 184 | .header.subsys_device = be16(0x1699), //same as pci_device |
| 185 | .header.subsys_vendor = be16(0x14E4), //Broadcom |
| 186 | #else |
| 187 | .header.mac_addr = { 0x00, 0x20, 0x9D, 0x00, 0x00, 0x00 }, //LiPPERT |
| 188 | .header.subsys_device = be16(0x1699), //simply kept this |
| 189 | .header.subsys_vendor = be16(0x121D), //LiPPERT |
| 190 | #endif |
| 191 | .header.pci_device = be16(0x1699), //Broadcom 5785 with GbE PHY |
| 192 | #if !CONFIG_BOARD_LIPPERT_FRONTRUNNER_AF |
| 193 | .header.patch_version = be16(0x010B), //1.11 (Broadcom's sb5785m1.11) |
| 194 | #else |
| 195 | .header.patch_version = be16(0x110B), //1.11b, i.e. hacked :-) |
| 196 | #endif |
| 197 | /* Bitfield enabling general features/codepaths in the firmware or |
| 198 | * selecting support for one of several supported PHYs? |
| 199 | * Bits not listed had no appearent effect: |
| 200 | * 14-11: any bit 1=firmware execution seemed delayed |
| 201 | * 10: 0=firmware execution seemed delayed |
| 202 | * 9,2,0: select PHY type, affects these registers, probably more |
| 203 | * 9 2 0 | reg 0x05A4 PHY reg 31 PHY 23,24,28 Notes |
| 204 | * -------+---------------------------------------------------------- |
| 205 | * 0 0 0 | 0x331C71C1 - changed Inband Status enabled |
| 206 | * 0 1 0 | 0x3210C500 - changed - |
| 207 | * 0 X 1 | 0x33FF66C0 changed - 10/100 Mbit only |
| 208 | * 1 X 0 | 0x330C5180 - - - |
| 209 | * 1 X 1 | 0x391C6140 - - - |
| 210 | */ |
| 211 | #if !CONFIG_BOARD_LIPPERT_FRONTRUNNER_AF |
| 212 | .header.basic_config = be16(0x0404), //original for B50610 |
| 213 | #else |
| 214 | .header.basic_config = be16(0x0604), //bit 9 set so not to mess up PHY regs, kept other bits unchanged |
| 215 | #endif |
| 216 | |
| 217 | /* Tag that defines when / on what occasion the commands are interpreted. |
| 218 | * Bits 2-0 = 0 i.e. possible values are 0x00, 08, 10, ..., F8. |
| 219 | * On a RISC CPU reset every tag except 0x38, A0, F0, F8 is used. 0x38 |
| 220 | * seems to be run before a reset is performed(?), the other 3 I have |
| 221 | * never seen used. Generally, lower values appear to be run earlier. |
| 222 | * An "ifconfig up" with Linux' "tg3" driver causes the tags 0x50, 60, |
| 223 | * 68, 20, 70, 80 to be interpreted in this order. |
| 224 | * All tests were performed with .basic_config=0x0604. |
| 225 | */ |
| 226 | .init.hunk1_when = 0x10, //only once at RISC CPU reset? |
| 227 | /* Instructions are obviously a specialized bytecode interpreted by the |
| 228 | * main firmware, rather than MIPS machine code. Commands consist of 1-3 |
| 229 | * 32-bit words. In the following, 0-9,A-F = hex literals, a-z,_ = variable |
| 230 | * parts, each character = 4 bits. |
| 231 | * 0610offs newvalue: write (32-bit) <newvalue> to 5785-internal shared mem at <offs> |
| 232 | * 08rgvalu: write <valu> to PHY register, <rg> = 0x20 + register number |
| 233 | * C610rgnr newvalue: write <newvalue> to MAC register <rgnr> |
| 234 | * C1F0rgnr andvalue or_value: modify MAC register <rgnr> by ANDing with <andvalue> and then ORing with <or_value> |
| 235 | * C4btrgnr: clear bit in 32-bit MAC register <rgnr>, <bt> = bit number << 3 |
| 236 | * C3btrgnr: set bit, see C4...; example: command 0xC3200454 sets bit 4 of 32-bit register 0x0454 |
| 237 | * CBbtrgnr: run next command only if bit (see C4...) == 1 (so far only seen before F7F0...) |
| 238 | * F7F0skip: unconditional jump i.e. skip next <skip> code bytes (only seen small positive <skip>) |
| 239 | * F7Fxaddr: call function at <addr> in main firmware? <x> = 3 or 4, bool parameter?? Wild guess! |
| 240 | * F7FFFadr somvalue: also call func. at <adr>, but with <somvalue> as parameter?? More guessing! |
| 241 | * More commands probably exist, but all code I've ever seen was kept |
| 242 | * included below, commented out if not suitable for the CFR-AF. v1.xx |
| 243 | * is Broadcom's Selfboot patch version sb5785m1.xx where the command |
| 244 | * was added, for reference see Broadcom's changelog. |
| 245 | */ |
| 246 | .init.hunk1_code = { |
| 247 | #if CONFIG_BOARD_LIPPERT_FRONTRUNNER_AF |
| 248 | be(0x082B8104), //CFR-AF: PHY0B: KSZ9021 select PHY104 |
| 249 | be(0x082CF0F0), //CFR-AF: PHY0C: KSZ9021 clk/ctl skew (advised by Micrel) |
| 250 | be(0x082B8105), //CFR-AF: PHY0B: KSZ9021 select PHY105 |
| 251 | be(0x082C3333), //CFR-AF: PHY0C: KSZ9021 RX data skew (empirical) |
| 252 | #endif |
| 253 | be(0xC1F005A0), be(0xFEFFEFFF), be(0x01001000), //v1.05 : 5A0.24,12=1: auto-clock-switch |
| 254 | be(0x06100D34), be(0x00000000), //v1.03 : MemD34: clear config vars |
| 255 | be(0x06100D38), be(0x00000000), //v1.03 : - | |
| 256 | be(0x06100D3C), be(0x00000000), //v1.03 : MemD3F| |
| 257 | }, //-->INIT1_LENGTH! |
| 258 | |
| 259 | .init.hunk2_when = 0x30, //after global reset, PHY reset |
| 260 | .init.hunk2_code = { |
| 261 | #if !CONFIG_BOARD_LIPPERT_FRONTRUNNER_AF |
| 262 | be(0x08370F08), //v1.06 : PHY17: B50610 select reg. 08 |
| 263 | be(0x08350001), //v1.06 : PHY15: B50610 slow link fix |
| 264 | be(0x08370F00), //v1.06 : PHY17: B50610 disable reg. 08 |
| 265 | be(0x083C2C00), //v1.11 : PHY1C: B50610 Shadow 0B |
| 266 | #endif |
| 267 | be(0xF7F301E6), //v1.09+: ?: subroutine calls to |
| 268 | be(0xF7FFF0B6), be(0x0000FFE7), //v1.09+: ?| restore Port Mode ??? |
| 269 | be(0xF7FFF0F6), be(0x00008000), //v1.09+: ?| |
| 270 | be(0xF7F401E6), //v1.09+: ?| |
| 271 | }, //-->INIT2_LENGTH! |
| 272 | |
| 273 | .init.hunk3_when = 0xA8, //?, I'd guess quite late |
| 274 | .init.hunk3_code = { |
| 275 | be(0xC1F03604), be(0xFFE0FFFF), be(0x00110000), //v1.08 : 3604.20-16: 10Mb clock = 12.5MHz |
| 276 | }, //-->INIT3_LENGTH! |
| 277 | |
| 278 | #if !CONFIG_BOARD_LIPPERT_FRONTRUNNER_AF |
| 279 | .init.hunk4_when = 0xD8, //original for B50610 |
| 280 | #else |
| 281 | .init.hunk4_when = 0x80, //run last, after Linux' "ifconfig up" |
| 282 | #endif |
| 283 | .init.hunk4_code = { |
| 284 | #if CONFIG_BOARD_LIPPERT_FRONTRUNNER_AF |
| 285 | be(0x083F4300), //CFR-AF: PHY1F: IRQ active high |
| 286 | be(0x083C0000), //CFR-AF: PHY1C: revert driver writes |
| 287 | be(0x08380000), //CFR-AF: PHY18| |
| 288 | be(0x083C0000), //CFR-AF: PHY1C| |
| 289 | #endif |
| 290 | be(0xCB0005A4), be(0xF7F0000C), //v1.01 : if 5A4.0==1 -->skip next 12 bytes |
| 291 | #if !CONFIG_BOARD_LIPPERT_FRONTRUNNER_AF |
| 292 | be(0xC61005A4), be(0x3210C500), //v1.01 : 5A4: PHY LED mode |
| 293 | #else |
| 294 | be(0xC61005A4), be(0x331C71CE), //CFR-AF: 5A4: fake LED mode |
| 295 | #endif |
| 296 | be(0xF7F00008), //v1.01 : -->skip next 8 bytes |
| 297 | be(0xC61005A4), be(0x331C71C1), //v1.01 : 5A4: inband LED mode |
| 298 | //be(0xC3200454), //CFR-AF: 454.4: auto link polling |
| 299 | }, //-->INIT4_LENGTH! |
| 300 | |
| 301 | .powerdown.hunk1_when = 0x50, //prior to IDDQ MAC |
| 302 | .powerdown.hunk1_code = { |
| 303 | #if !CONFIG_BOARD_LIPPERT_FRONTRUNNER_AF |
| 304 | be(0x083CB001), //v1.10 : PHY1C: IDDQ B50610 PHY |
| 305 | #endif |
| 306 | be(0xF7F30116), // IDDQ PHY |
| 307 | be(0xC40005A0), //v1.09 : 5A0.0=0: Port Mode = MII |
| 308 | be(0xC4180400), //v1.09 : 400.3=0| |
| 309 | be(0xC3100400), //v1.09 : 400.2=1| |
| 310 | }, //-->PWRDN_LENGTH! |
| 311 | |
| 312 | }; |
| 313 | |
| 314 | /* Upload 'NV'RAM contents for BCM5785 GbE MAC integrated in A55E. |
| 315 | * Call this from mainboard.c. |
| 316 | */ |
| 317 | void broadcom_init(void) |
| 318 | { |
| 319 | volatile u32 *gec_base; //Gigabit Ethernet Controller base addr |
| 320 | u8 *gec_shadow; //base addr of shadow 'NV'RAM for GbE MAC in A55E |
| 321 | u8 sum; |
| 322 | int i; |
| 323 | |
Stefan Reinauer | 302a2ec | 2015-06-19 14:59:54 -0700 | [diff] [blame] | 324 | gec_base = (u32*)(uintptr_t)dev_find_slot(0, PCI_DEVFN(0x14, 6))->resource_list->base; |
| 325 | gec_shadow = (u8*)((uintptr_t)pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x14, 3)), 0x9C) & 0xFFFFFC00); |
Jens Rottmann | 3926b4c | 2013-03-01 19:41:41 +0100 | [diff] [blame] | 326 | printk(BIOS_DEBUG, "Upload GbE 'NV'RAM contents @ 0x%08lx\n", (unsigned long)gec_shadow); |
| 327 | |
| 328 | /* Halt RISC CPU before uploading the firmware patch */ |
| 329 | for (i=10000; i > 0; i--) { |
| 330 | gec_base[0x5004/4] = 0xFFFFFFFF; //clear CPU state |
| 331 | gec_base[0x5000/4] |= (1<<10); //issue RISC halt |
| 332 | if (gec_base[0x5000/4] | (1<<10)) |
| 333 | break; |
| 334 | udelay(10); |
| 335 | } |
| 336 | if (!i) |
| 337 | printk(BIOS_ERR, "Failed to halt RISC CPU!\n"); |
| 338 | |
| 339 | /* Calculate checksums (standard byte sum) */ |
| 340 | for (sum = 0, i = 0; i < sizeof(selfboot_patch.header); i++) |
| 341 | sum -= ((u8*)&selfboot_patch.header)[i]; |
| 342 | selfboot_patch.header.checksum = sum; |
| 343 | for (sum = 0, i = 0; i < sizeof(selfboot_patch.init); i++) |
| 344 | sum -= ((u8*)&selfboot_patch.init)[i]; |
| 345 | selfboot_patch.init.checksum = sum; |
| 346 | for (sum = 0, i = 0; i < sizeof(selfboot_patch.powerdown); i++) |
| 347 | sum -= ((u8*)&selfboot_patch.powerdown)[i]; |
| 348 | selfboot_patch.powerdown.checksum = sum; |
| 349 | |
| 350 | /* Upload firmware patch to shadow 'NV'RAM */ |
| 351 | for (i = 0; i < sizeof(selfboot_patch); i++) |
| 352 | gec_shadow[i] = ((u8*)&selfboot_patch)[i]; //access byte-wise! |
| 353 | |
| 354 | /* Restart BCM5785's CPU */ |
| 355 | gec_base[0x5004/4] = 0xFFFFFFFF; //clear CPU state |
| 356 | gec_base[0x5000/4] = 0x00000001; //reset RISC processor |
| 357 | //usually we'd have to wait for the reset bit to clear again ... |
| 358 | } |