Jens Rottmann (JRottmann(a)LiPPERTembedded.de) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/2584
-gerrit
commit 98c4af86f296f4d086204c902f309e383d7b42cd
Author: Jens Rottmann <JRottmann(a)LiPPERTembedded.de>
Date: Mon Mar 4 21:13:57 2013 +0100
FrontRunner/Toucan-AF: drop unnecessary compile time CPU model selection
The first reason for selecting the CPU model at compile time was a
multi-second pause if booting a single core Fusion T40R with MAX_CPUS=2.
Recent tests show the pause has disappeared, someone must have fixed it.
The second reason was me not knowing how to make a single vgabios image
work with two different PCI IDs. Many thanks to Martin Roth for educating
me! Quote:
"The way to make coreboot use the same vbios for different video device IDs
is through the map_oprom_vendev function. In family 14 it's in
northbridge/amd/agesa/family14/amdfam14_conf.c You would name your video
bios 1002,9802 in the config and all the other device/vendor IDs for the
family 14h processors will fall through the initial check for the video
bios and will get remapped to use that vbios. This only works if you're
initializing the vbios inside coreboot. I don't know if you're using
SeaBios as a payload, but if you are you can add the vbios to cbfs as
vgaroms/vbios.rom and the rom will always be initialized."
I'd like to add the vgabios is added as type 'optionrom' when Coreboot make
adds it, however to work with SeaBios it has to be added manually with
cbfstool and with type 'raw', or it will hang.
Change-Id: I8190d0c3202a60dfccb77dde232f9ba7ce5ce318
Signed-off-by: Jens Rottmann <JRottmann(a)LiPPERTembedded.de>
---
src/mainboard/lippert/Kconfig | 31 ----------------------------
src/mainboard/lippert/frontrunner-af/Kconfig | 6 ++----
src/mainboard/lippert/toucan-af/Kconfig | 6 ++----
3 files changed, 4 insertions(+), 39 deletions(-)
diff --git a/src/mainboard/lippert/Kconfig b/src/mainboard/lippert/Kconfig
index cf5288e..1765460 100644
--- a/src/mainboard/lippert/Kconfig
+++ b/src/mainboard/lippert/Kconfig
@@ -20,37 +20,6 @@ config BOARD_LIPPERT_TOUCAN_AF
endchoice
-if BOARD_LIPPERT_FRONTRUNNER_AF || BOARD_LIPPERT_TOUCAN_AF
-# No code, but two Kconfig options need to know this ...
-choice
- prompt "AMD Fusion CPU model"
- default FUSION_G_T56N
- help
- The actual CPU model the board is fitted with:
-
- G-T40R: 1 GHz, single core
- G-T56N: 1.65 GHz, dual core
-
- If unsure check with "grep model /proc/cpuinfo".
-config FUSION_G_T40R
- bool "G-T40R"
- help
- The actual CPU model the board is fitted with:
-
- G-T40R: 1 GHz, single core
-
- If unsure check with "grep model /proc/cpuinfo".
-config FUSION_G_T56N
- bool "G-T56N"
- help
- The actual CPU model the board is fitted with:
-
- G-T56N: 1.65 GHz, dual core
-
- If unsure check with "grep model /proc/cpuinfo".
-endchoice
-endif # BOARD_LIPPERT_FRONTRUNNER_AF || BOARD_LIPPERT_TOUCAN_AF
-
source "src/mainboard/lippert/frontrunner/Kconfig"
source "src/mainboard/lippert/frontrunner-af/Kconfig"
source "src/mainboard/lippert/hurricane-lx/Kconfig"
diff --git a/src/mainboard/lippert/frontrunner-af/Kconfig b/src/mainboard/lippert/frontrunner-af/Kconfig
index 0cdf365..f56c048 100644
--- a/src/mainboard/lippert/frontrunner-af/Kconfig
+++ b/src/mainboard/lippert/frontrunner-af/Kconfig
@@ -62,8 +62,7 @@ config HW_MEM_HOLE_SIZEK
config MAX_CPUS
int
- default 1 if FUSION_G_T40R
- default 2 if FUSION_G_T56N
+ default 2
config MAX_PHYSICAL_CPUS
int
@@ -116,8 +115,7 @@ config VGA_BIOS
config VGA_BIOS_ID
string
- default "1002,9804" if FUSION_G_T40R
- default "1002,9806" if FUSION_G_T56N
+ default "1002,9802"
config SB800_AHCI_ROM
bool
diff --git a/src/mainboard/lippert/toucan-af/Kconfig b/src/mainboard/lippert/toucan-af/Kconfig
index 72e6a3b..6696ca6 100644
--- a/src/mainboard/lippert/toucan-af/Kconfig
+++ b/src/mainboard/lippert/toucan-af/Kconfig
@@ -64,8 +64,7 @@ config HW_MEM_HOLE_SIZEK
config MAX_CPUS
int
- default 1 if FUSION_G_T40R
- default 2 if FUSION_G_T56N
+ default 2
config MAX_PHYSICAL_CPUS
int
@@ -118,8 +117,7 @@ config VGA_BIOS
config VGA_BIOS_ID
string
- default "1002,9804" if FUSION_G_T40R
- default "1002,9806" if FUSION_G_T56N
+ default "1002,9802"
config SB800_AHCI_ROM
bool
the following patch was just integrated into master:
commit 026bbda071161ad56822dceaabea03bceefac9ac
Author: Ronald G. Minnich <rminnich(a)gmail.com>
Date: Mon Mar 4 09:46:31 2013 -0800
ARM: remove code that is IMHO a dangerous design
OK, this is tl;dr. But I need to write this in hopes we make
sure we don't put code like this into coreboot. Ever.
Our excuse in this case is that it was imported, not obviously wrong,
and easily changed. It made sense to get it in, make it work, then
do a cleanup pass, because changing everything up front is almost
impossible to debug.
The exynos code has bunch of base register values, e.g.
These are base addresses of things that look like a memory-mapped
struct. To get these to a pointer, they created the following macro,
which creates an inline function.
static inline unsigned int samsung_get_base_##device(void) \
{ \
return cpu_is_exynos5() ? EXYNOS5_##base : 0; \
}
And then invoke it 31 times in a .h file, e.g.:
SAMSUNG_BASE(clock, CLOCK_BASE)
to create 31 functions.
And then use it:
struct exynos5_clock *clk =
(struct exynos5_clock *)samsung_get_base_clock();
OK, what's wrong with this? It's easier to ask what's right with it. Answer: nothing.
I have a long list of what's wrong, and I may leave some things out,
but here goes:
1. the "function" can return a NULL if we're not on exynos5. Most uses of the code
don't check the return value.
2. And why would this function be running, if we're not on an exynos5? Why compile it in?
3. Note the cast everywhere a samsung_get_base_xxx is used.
The function returns an untyped variable, requiring the *user* to get two
things right: the cast, and the function invocation. One can replace that _clock(); with
_power(); in the code above, and they will be referencing the wrong registers, and
they'll never get an error!
We have a C compiler; use it to type data.
4. You're generating 31 functions using cpp each and every time the file is included.
The C compiler has to parse these each time. It's not at all like a simple cpp
macro which is only generated on use.
5. You can't tags or etags this code
6. In fact, any kind of analysis tool will be unable to do anything with this cpp magic.
That's only a partial list.
So what's the right way to do it? Just make typed constants, viz:
Or, since I expect people will want the lower case function syntax, I've left
it that way:
Now we've got something that is efficient, and we don't even need to protect with
any more.
Hence this change. We've got something that is type checked, does not require users to
cast on each use, will catch simple programming errors, can be analyzed with standard tools,
and builds faster.
So if we make a mistake:
struct exynos5_clock *clk =
samsung_get_base_adc();
We'll see it:
src/cpu/samsung/exynos5250/clock.c: In function 'get_pll_clk':
src/cpu/samsung/exynos5250/clock.c:183:3: error: initialization from incompatible pointer type [-Werror]
which we would not have seen before.
As a minor benefit, it shaves most of a second off the compilation.
Change-Id: Ie67bc4bc038a8dd1837b977d07332d7d7fd6be1f
Signed-off-by: Ronald G. Minnich <rminnich(a)gmail.com>
Reviewed-on: http://review.coreboot.org/2582
Tested-by: build bot (Jenkins)
Build-Tested: build bot (Jenkins) at Mon Mar 4 19:41:24 2013, giving +1
Reviewed-By: Ronald G. Minnich <rminnich(a)gmail.com> at Mon Mar 4 19:43:19 2013, giving +2
See http://review.coreboot.org/2582 for details.
-gerrit
Ronald G. Minnich (rminnich(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/2582
-gerrit
commit 860bbd9ce42e6037b1bbfb5492a2235df55adb3a
Author: Ronald G. Minnich <rminnich(a)gmail.com>
Date: Mon Mar 4 09:46:31 2013 -0800
ARM: remove code that is IMHO a dangerous design
OK, this is tl;dr. But I need to write this in hopes we make
sure we don't put code like this into coreboot. Ever.
Our excuse in this case is that it was imported, not obviously wrong,
and easily changed. It made sense to get it in, make it work, then
do a cleanup pass, because changing everything up front is almost
impossible to debug.
The exynos code has bunch of base register values, e.g.
These are base addresses of things that look like a memory-mapped
struct. To get these to a pointer, they created the following macro,
which creates an inline function.
static inline unsigned int samsung_get_base_##device(void) \
{ \
return cpu_is_exynos5() ? EXYNOS5_##base : 0; \
}
And then invoke it 31 times in a .h file, e.g.:
SAMSUNG_BASE(clock, CLOCK_BASE)
to create 31 functions.
And then use it:
struct exynos5_clock *clk =
(struct exynos5_clock *)samsung_get_base_clock();
OK, what's wrong with this? It's easier to ask what's right with it. Answer: nothing.
I have a long list of what's wrong, and I may leave some things out,
but here goes:
1. the "function" can return a NULL if we're not on exynos5. Most uses of the code
don't check the return value.
2. And why would this function be running, if we're not on an exynos5? Why compile it in?
3. Note the cast everywhere a samsung_get_base_xxx is used.
The function returns an untyped variable, requiring the *user* to get two
things right: the cast, and the function invocation. One can replace that _clock(); with
_power(); in the code above, and they will be referencing the wrong registers, and
they'll never get an error!
We have a C compiler; use it to type data.
4. You're generating 31 functions using cpp each and every time the file is included.
The C compiler has to parse these each time. It's not at all like a simple cpp
macro which is only generated on use.
5. You can't tags or etags this code
6. In fact, any kind of analysis tool will be unable to do anything with this cpp magic.
That's only a partial list.
So what's the right way to do it? Just make typed constants, viz:
Or, since I expect people will want the lower case function syntax, I've left
it that way:
Now we've got something that is efficient, and we don't even need to protect with
any more.
Hence this change. We've got something that is type checked, does not require users to
cast on each use, will catch simple programming errors, can be analyzed with standard tools,
and builds faster.
So if we make a mistake:
struct exynos5_clock *clk =
samsung_get_base_adc();
We'll see it:
src/cpu/samsung/exynos5250/clock.c: In function 'get_pll_clk':
src/cpu/samsung/exynos5250/clock.c:183:3: error: initialization from incompatible pointer type [-Werror]
which we would not have seen before.
As a minor benefit, it shaves most of a second off the compilation.
Change-Id: Ie67bc4bc038a8dd1837b977d07332d7d7fd6be1f
Signed-off-by: Ronald G. Minnich <rminnich(a)gmail.com>
---
src/cpu/samsung/exynos5-common/pwm.c | 10 ++--
src/cpu/samsung/exynos5-common/sromc.c | 2 +-
src/cpu/samsung/exynos5-common/timer.c | 2 +-
src/cpu/samsung/exynos5-common/wdt.c | 4 +-
src/cpu/samsung/exynos5250/clock.c | 20 ++++----
src/cpu/samsung/exynos5250/cpu.h | 85 +++++++++++++---------------------
src/cpu/samsung/exynos5250/power.c | 22 ++++-----
7 files changed, 62 insertions(+), 83 deletions(-)
diff --git a/src/cpu/samsung/exynos5-common/pwm.c b/src/cpu/samsung/exynos5-common/pwm.c
index da465d9..0fc571b 100644
--- a/src/cpu/samsung/exynos5-common/pwm.c
+++ b/src/cpu/samsung/exynos5-common/pwm.c
@@ -32,7 +32,7 @@
int pwm_enable(int pwm_id)
{
const struct s5p_timer *pwm =
- (struct s5p_timer *)samsung_get_base_timer();
+ samsung_get_base_timer();
unsigned long tcon;
tcon = readl(&pwm->tcon);
@@ -46,7 +46,7 @@ int pwm_enable(int pwm_id)
int pwm_check_enabled(int pwm_id)
{
const struct s5p_timer *pwm =
- (struct s5p_timer *)samsung_get_base_timer();
+ samsung_get_base_timer();
const unsigned long tcon = readl(&pwm->tcon);
return tcon & TCON_START(pwm_id);
@@ -55,7 +55,7 @@ int pwm_check_enabled(int pwm_id)
void pwm_disable(int pwm_id)
{
const struct s5p_timer *pwm =
- (struct s5p_timer *)samsung_get_base_timer();
+ samsung_get_base_timer();
unsigned long tcon;
tcon = readl(&pwm->tcon);
@@ -84,7 +84,7 @@ static unsigned long pwm_calc_tin(int pwm_id, unsigned long freq)
int pwm_config(int pwm_id, int duty_ns, int period_ns)
{
const struct s5p_timer *pwm =
- (struct s5p_timer *)samsung_get_base_timer();
+ samsung_get_base_timer();
unsigned int offset;
unsigned long tin_rate;
unsigned long tin_ns;
@@ -143,7 +143,7 @@ int pwm_init(int pwm_id, int div, int invert)
{
u32 val;
const struct s5p_timer *pwm =
- (struct s5p_timer *)samsung_get_base_timer();
+ samsung_get_base_timer();
unsigned long ticks_per_period;
unsigned int offset, prescaler;
diff --git a/src/cpu/samsung/exynos5-common/sromc.c b/src/cpu/samsung/exynos5-common/sromc.c
index 091e8d1..7bc93e7 100644
--- a/src/cpu/samsung/exynos5-common/sromc.c
+++ b/src/cpu/samsung/exynos5-common/sromc.c
@@ -36,7 +36,7 @@ void s5p_config_sromc(u32 srom_bank, u32 srom_bw_conf, u32 srom_bc_conf)
{
u32 tmp;
struct s5p_sromc *srom =
- (struct s5p_sromc *)samsung_get_base_sromc();
+ samsung_get_base_sromc();
/* Configure SMC_BW register to handle proper SROMC bank */
tmp = srom->bw;
diff --git a/src/cpu/samsung/exynos5-common/timer.c b/src/cpu/samsung/exynos5-common/timer.c
index 6436da2..6cd5f5d 100644
--- a/src/cpu/samsung/exynos5-common/timer.c
+++ b/src/cpu/samsung/exynos5-common/timer.c
@@ -39,7 +39,7 @@ static unsigned long lastinc;
/* macro to read the 16 bit timer */
static inline struct s5p_timer *s5p_get_base_timer(void)
{
- return (struct s5p_timer *)samsung_get_base_timer();
+ return samsung_get_base_timer();
}
/**
diff --git a/src/cpu/samsung/exynos5-common/wdt.c b/src/cpu/samsung/exynos5-common/wdt.c
index 6f435e5..dbeefec 100644
--- a/src/cpu/samsung/exynos5-common/wdt.c
+++ b/src/cpu/samsung/exynos5-common/wdt.c
@@ -30,7 +30,7 @@
void wdt_stop(void)
{
struct s5p_watchdog *wdt =
- (struct s5p_watchdog *)samsung_get_base_watchdog();
+ samsung_get_base_watchdog();
unsigned int wtcon;
wtcon = readl(&wdt->wtcon);
@@ -42,7 +42,7 @@ void wdt_stop(void)
void wdt_start(unsigned int timeout)
{
struct s5p_watchdog *wdt =
- (struct s5p_watchdog *)samsung_get_base_watchdog();
+ samsung_get_base_watchdog();
unsigned int wtcon;
wdt_stop();
diff --git a/src/cpu/samsung/exynos5250/clock.c b/src/cpu/samsung/exynos5250/clock.c
index 115d40d..6b79272 100644
--- a/src/cpu/samsung/exynos5250/clock.c
+++ b/src/cpu/samsung/exynos5250/clock.c
@@ -180,7 +180,7 @@ static struct st_epll_con_val epll_div[] = {
unsigned long get_pll_clk(int pllreg)
{
struct exynos5_clock *clk =
- (struct exynos5_clock *)samsung_get_base_clock();
+ samsung_get_base_clock();
unsigned long r, m, p, s, k = 0, mask, fout;
unsigned int freq;
@@ -246,7 +246,7 @@ unsigned long get_pll_clk(int pllreg)
unsigned long clock_get_periph_rate(enum periph_id peripheral)
{
struct exynos5_clock *clk =
- (struct exynos5_clock *)samsung_get_base_clock();
+ samsung_get_base_clock();
struct clk_bit_info *bit_info = &clk_bit_info[peripheral];
unsigned long sclk, sub_clk;
unsigned int src, div, sub_div;
@@ -341,7 +341,7 @@ unsigned long clock_get_periph_rate(enum periph_id peripheral)
unsigned long get_arm_clk(void)
{
struct exynos5_clock *clk =
- (struct exynos5_clock *)samsung_get_base_clock();
+ samsung_get_base_clock();
unsigned long div;
unsigned long armclk;
unsigned int arm_ratio;
@@ -378,7 +378,7 @@ struct arm_clk_ratios *get_arm_clk_ratios(void)
void set_mmc_clk(int dev_index, unsigned int div)
{
struct exynos5_clock *clk =
- (struct exynos5_clock *)samsung_get_base_clock();
+ samsung_get_base_clock();
unsigned int addr;
unsigned int val;
@@ -404,7 +404,7 @@ void set_mmc_clk(int dev_index, unsigned int div)
void clock_ll_set_pre_ratio(enum periph_id periph_id, unsigned divisor)
{
struct exynos5_clock *clk =
- (struct exynos5_clock *)samsung_get_base_clock();
+ samsung_get_base_clock();
unsigned shift;
unsigned mask = 0xff;
u32 *reg;
@@ -449,7 +449,7 @@ void clock_ll_set_pre_ratio(enum periph_id periph_id, unsigned divisor)
void clock_ll_set_ratio(enum periph_id periph_id, unsigned divisor)
{
struct exynos5_clock *clk =
- (struct exynos5_clock *)samsung_get_base_clock();
+ samsung_get_base_clock();
unsigned shift;
unsigned mask = 0xff;
u32 *reg;
@@ -574,7 +574,7 @@ int clock_set_rate(enum periph_id periph_id, unsigned int rate)
int clock_set_mshci(enum periph_id peripheral)
{
struct exynos5_clock *clk =
- (struct exynos5_clock *)samsung_get_base_clock();
+ samsung_get_base_clock();
u32 *addr;
unsigned int clock;
unsigned int tmp;
@@ -635,7 +635,7 @@ int clock_epll_set_rate(unsigned long rate)
unsigned int lockcnt;
unsigned int start;
struct exynos5_clock *clk =
- (struct exynos5_clock *)samsung_get_base_clock();
+ samsung_get_base_clock();
epll_con = readl(&clk->epll_con0);
epll_con &= ~((EPLL_CON0_LOCK_DET_EN_MASK <<
@@ -685,7 +685,7 @@ int clock_epll_set_rate(unsigned long rate)
void clock_select_i2s_clk_source(void)
{
struct exynos5_clock *clk =
- (struct exynos5_clock *)samsung_get_base_clock();
+ samsung_get_base_clock();
clrsetbits_le32(&clk->src_peric1, AUDIO1_SEL_MASK,
(CLK_SRC_SCLK_EPLL));
@@ -694,7 +694,7 @@ void clock_select_i2s_clk_source(void)
int clock_set_i2s_clk_prescaler(unsigned int src_frq, unsigned int dst_frq)
{
struct exynos5_clock *clk =
- (struct exynos5_clock *)samsung_get_base_clock();
+ samsung_get_base_clock();
unsigned int div ;
if ((dst_frq == 0) || (src_frq == 0)) {
diff --git a/src/cpu/samsung/exynos5250/cpu.h b/src/cpu/samsung/exynos5250/cpu.h
index b859823..772e591 100644
--- a/src/cpu/samsung/exynos5250/cpu.h
+++ b/src/cpu/samsung/exynos5250/cpu.h
@@ -79,59 +79,38 @@
/* Distance between each Trust Zone PC register set */
#define TZPC_BASE_OFFSET 0x10000
-#ifndef __ASSEMBLER__
-
-/* FIXME(dhendrix): cpu_is_exynos5() seems broken atm... */
-#if 0
-#define SAMSUNG_BASE(device, base) \
-static inline unsigned int samsung_get_base_##device(void) \
-{ \
- return cpu_is_exynos5() ? EXYNOS5_##base : 0; \
-}
-#endif
-#define SAMSUNG_BASE(device, base) \
-static inline unsigned int samsung_get_base_##device(void) \
-{ \
- return EXYNOS5_##base; \
-}
-
-SAMSUNG_BASE(adc, ADC_BASE)
-SAMSUNG_BASE(clock, CLOCK_BASE)
-SAMSUNG_BASE(ace_sfr, ACE_SFR_BASE)
-SAMSUNG_BASE(dsim, MIPI_DSI1_BASE)
-SAMSUNG_BASE(disp_ctrl, DISP1_CTRL_BASE)
-SAMSUNG_BASE(fimd, FIMD_BASE)
-SAMSUNG_BASE(gpio_part1, GPIO_PART1_BASE)
-SAMSUNG_BASE(gpio_part2, GPIO_PART2_BASE)
-SAMSUNG_BASE(gpio_part3, GPIO_PART3_BASE)
-SAMSUNG_BASE(gpio_part4, GPIO_PART4_BASE)
-SAMSUNG_BASE(gpio_part5, GPIO_PART5_BASE)
-SAMSUNG_BASE(gpio_part6, GPIO_PART6_BASE)
-SAMSUNG_BASE(pro_id, PRO_ID)
-
-#ifndef CONFIG_OF_CONTROL
-SAMSUNG_BASE(mmc, MMC_BASE)
-SAMSUNG_BASE(mshci, MSHC_BASE)
-#endif
-
-SAMSUNG_BASE(modem, MODEM_BASE)
-SAMSUNG_BASE(sromc, SROMC_BASE)
-SAMSUNG_BASE(swreset, SWRESET)
-SAMSUNG_BASE(sysreg, SYSREG_BASE)
-SAMSUNG_BASE(timer, PWMTIMER_BASE)
-SAMSUNG_BASE(uart, UART_BASE)
-SAMSUNG_BASE(usb_phy, USBPHY_BASE)
-SAMSUNG_BASE(usb_otg, USBOTG_BASE)
-SAMSUNG_BASE(watchdog, WATCHDOG_BASE)
-SAMSUNG_BASE(power, POWER_BASE)
-SAMSUNG_BASE(i2s, I2S_BASE)
-SAMSUNG_BASE(spi1, SPI1_BASE)
-#ifndef CONFIG_OF_CONTROL
-SAMSUNG_BASE(i2c, I2C_BASE)
-SAMSUNG_BASE(spi, SPI_BASE)
-SAMSUNG_BASE(spi_isp, SPI_ISP_BASE)
-#endif
-#endif
+#define samsung_get_base_adc() ((struct exynos5_adc *)EXYNOS5_ADC_BASE)
+#define samsung_get_base_clock() ((struct exynos5_clock *)EXYNOS5_CLOCK_BASE)
+#define samsung_get_base_ace_sfr() ((struct exynos5_ace_sfr *)EXYNOS5_ACE_SFR_BASE)
+#define samsung_get_base_dsim() ((struct exynos5_dsim *)EXYNOS5_MIPI_DSI1_BASE)
+#define samsung_get_base_disp_ctrl() ((struct exynos5_disp_ctrl *)EXYNOS5_DISP1_CTRL_BASE)
+#define samsung_get_base_fimd() ((struct exynos5_fimd *)EXYNOS5_FIMD_BASE)
+#define samsung_get_base_gpio_part1() ((struct exynos5_gpio_part1 *)EXYNOS5_GPIO_PART1_BASE)
+#define samsung_get_base_gpio_part2() ((struct exynos5_gpio_part2 *)EXYNOS5_GPIO_PART2_BASE)
+#define samsung_get_base_gpio_part3() ((struct exynos5_gpio_part3 *)EXYNOS5_GPIO_PART3_BASE)
+#define samsung_get_base_gpio_part4() ((struct exynos5_gpio_part4 *)EXYNOS5_GPIO_PART4_BASE)
+#define samsung_get_base_gpio_part5() ((struct exynos5_gpio_part5 *)EXYNOS5_GPIO_PART5_BASE)
+#define samsung_get_base_gpio_part6() ((struct exynos5_gpio_part6 *)EXYNOS5_GPIO_PART6_BASE)
+#define samsung_get_base_pro_id() ((struct exynos5_pro_id *)EXYNOS5_PRO_ID)
+
+#define samsung_get_base_mmc() ((struct exynos5_mmc *)EXYNOS5_MMC_BASE)
+#define samsung_get_base_mshci() ((struct exynos5_mshci *)EXYNOS5_MSHC_BASE)
+
+#define samsung_get_base_modem() ((struct exynos5_modem *)EXYNOS5_MODEM_BASE)
+#define samsung_get_base_sromc() ((struct exynos5_sromc *)EXYNOS5_SROMC_BASE)
+#define samsung_get_base_swreset() ((struct exynos5_swreset *)EXYNOS5_SWRESET)
+#define samsung_get_base_sysreg() ((struct exynos5_sysreg *)EXYNOS5_SYSREG_BASE)
+#define samsung_get_base_timer() ((struct s5p_timer *)EXYNOS5_PWMTIMER_BASE)
+#define samsung_get_base_uart() ((struct exynos5_uart *)EXYNOS5_UART_BASE)
+#define samsung_get_base_usb_phy() ((struct exynos5_usb_phy *)EXYNOS5_USBPHY_BASE)
+#define samsung_get_base_usb_otg() ((struct exynos5_usb_otg *)EXYNOS5_USBOTG_BASE)
+#define samsung_get_base_watchdog() ((struct exynos5_watchdog *)EXYNOS5_WATCHDOG_BASE)
+#define samsung_get_base_power() ((struct exynos5_power *)EXYNOS5_POWER_BASE)
+#define samsung_get_base_i2s() ((struct exynos5_i2s *)EXYNOS5_I2S_BASE)
+#define samsung_get_base_spi1() ((struct exynos5_spi1 *)EXYNOS5_SPI1_BASE)
+#define samsung_get_base_i2c() ((struct exynos5_i2c *)EXYNOS5_I2C_BASE)
+#define samsung_get_base_spi() ((struct exynos5_spi *)EXYNOS5_SPI_BASE)
+#define samsung_get_base_spi_isp() ((struct exynos5_spi_isp *)EXYNOS5_SPI_ISP_BASE)
#define EXYNOS5_SPI_NUM_CONTROLLERS 5
#define EXYNOS_I2C_MAX_CONTROLLERS 8
diff --git a/src/cpu/samsung/exynos5250/power.c b/src/cpu/samsung/exynos5250/power.c
index 41e1e1b..953bf5c 100644
--- a/src/cpu/samsung/exynos5250/power.c
+++ b/src/cpu/samsung/exynos5250/power.c
@@ -38,7 +38,7 @@
static void ps_hold_setup(void)
{
struct exynos5_power *power =
- (struct exynos5_power *)samsung_get_base_power();
+ samsung_get_base_power();
/* Set PS-Hold high */
setbits_le32(&power->ps_hold_ctrl, POWER_PS_HOLD_CONTROL_DATA_HIGH);
@@ -47,7 +47,7 @@ static void ps_hold_setup(void)
void power_reset(void)
{
struct exynos5_power *power =
- (struct exynos5_power *)samsung_get_base_power();
+ samsung_get_base_power();
/* Clear inform1 so there's no change we think we've got a wake reset */
power->inform1 = 0;
@@ -59,7 +59,7 @@ void power_reset(void)
void power_shutdown(void)
{
struct exynos5_power *power =
- (struct exynos5_power *)samsung_get_base_power();
+ samsung_get_base_power();
clrbits_le32(&power->ps_hold_ctrl, POWER_PS_HOLD_CONTROL_DATA_HIGH);
@@ -69,7 +69,7 @@ void power_shutdown(void)
void power_enable_dp_phy(void)
{
struct exynos5_power *power =
- (struct exynos5_power *)samsung_get_base_power();
+ samsung_get_base_power();
setbits_le32(&power->dptx_phy_control, DPTX_PHY_ENABLE);
}
@@ -77,9 +77,9 @@ void power_enable_dp_phy(void)
void power_enable_usb_phy(void)
{
struct exynos5_sysreg *sysreg =
- (struct exynos5_sysreg *)samsung_get_base_sysreg();
+ samsung_get_base_sysreg();
struct exynos5_power *power =
- (struct exynos5_power *)samsung_get_base_power();
+ samsung_get_base_power();
unsigned int phy_cfg;
/* Setting USB20PHY_CONFIG register to USB 2.0 HOST link */
@@ -98,7 +98,7 @@ void power_enable_usb_phy(void)
void power_disable_usb_phy(void)
{
struct exynos5_power *power =
- (struct exynos5_power *)samsung_get_base_power();
+ samsung_get_base_power();
/* Disabling USBHost_PHY */
clrbits_le32(&power->usb_host_phy_ctrl, POWER_USB_HOST_PHY_CTRL_EN);
@@ -107,7 +107,7 @@ void power_disable_usb_phy(void)
void power_enable_hw_thermal_trip(void)
{
struct exynos5_power *power =
- (struct exynos5_power *)samsung_get_base_power();
+ samsung_get_base_power();
/* Enable HW thermal trip */
setbits_le32(&power->ps_hold_ctrl, POWER_ENABLE_HW_TRIP);
@@ -116,7 +116,7 @@ void power_enable_hw_thermal_trip(void)
uint32_t power_read_reset_status(void)
{
struct exynos5_power *power =
- (struct exynos5_power *)samsung_get_base_power();
+ samsung_get_base_power();
return power->inform1;
}
@@ -124,7 +124,7 @@ uint32_t power_read_reset_status(void)
void power_exit_wakeup(void)
{
struct exynos5_power *power =
- (struct exynos5_power *)samsung_get_base_power();
+ samsung_get_base_power();
typedef void (*resume_func)(void);
((resume_func)power->inform0)();
@@ -193,7 +193,7 @@ int power_init(void)
void power_enable_xclkout(void)
{
struct exynos5_power *power =
- (struct exynos5_power *)samsung_get_base_power();
+ samsung_get_base_power();
/* use xxti for xclk out */
clrsetbits_le32(&power->pmu_debug, PMU_DEBUG_CLKOUT_SEL_MASK,
Jens Rottmann (JRottmann(a)LiPPERTembedded.de) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/2581
-gerrit
commit d89337c8fce0d214dd487fd63693be844acf1734
Author: Jens Rottmann <JRottmann(a)LiPPERTembedded.de>
Date: Fri Mar 1 19:41:41 2013 +0100
AMD Hudson-E1: document Broadcom BCM57xx secret sauce Selfboot Patch format
The Broadcom BCM5785 integrated in the AMD Hudson-E1 requires a proprietary
firmware blob to work. As Broadcom wasn't willing to send us any
documentation (or a firmware adapted to our Micrel PHY) I had to figure out
everything by myself in weeks of hard detective work.
In the end we had to settle for a different solution, this file is now only
needed for Ethernet support on our early FrontRunner-AF prototypes.
However the information contained here may be very useful for others, for
example it can easily be used in place of the usual firmware blob to get
Ethernet on AMD Inagua.
The fun thing is: as Broadcom refused to do any business with us at all,
or send us any documentation, we never had to sign an NDA with them. This
leaves me free to publish everything I have found out. :-)
Change-Id: Ifa628751d14143f277b27cfd34b1d2771ca1302f
Signed-off-by: Jens Rottmann <JRottmann(a)LiPPERTembedded.de>
---
src/mainboard/lippert/frontrunner-af/broadcom.c | 321 ++++++++++++++++++++++++
1 file changed, 321 insertions(+)
diff --git a/src/mainboard/lippert/frontrunner-af/broadcom.c b/src/mainboard/lippert/frontrunner-af/broadcom.c
new file mode 100644
index 0000000..48f1a42
--- /dev/null
+++ b/src/mainboard/lippert/frontrunner-af/broadcom.c
@@ -0,0 +1,321 @@
+/*
+ * Initialize Broadcom 5785 GbE MAC embedded in AMD A55E (Hudson-E1) Southbridge
+ * by uploading a Selfboot Patch to the A55E's shadow ROM area.
+ * The patch itself supports the Micrel KSZ9021 PHY that was used on LiPPERT
+ * FrontRunner-AF (CFR-AF) revision 0v0, the first prototype. The board is
+ * history and this code now serves only to document Broadcom's proprietary
+ * Selfboot Patch format.
+ *
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 LiPPERT ADLINK Technology GmbH
+ * (Written by Jens Rottmann <JRottmann(a)LiPPERTembedded.de>)
+ *
+ * This program 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; version 2 of the License.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <types.h>
+#include <arch/byteorder.h>
+#include <console/console.h>
+#include <device/device.h> //Coreboot device access
+#include <device/pci.h>
+#include <delay.h>
+
+#define be16(x) cpu_to_be16(x) //a little easier to type
+#define be(x) cpu_to_be32(x) //this is used a lot!
+
+/* C forces us to specify these before defining struct selfboot_patch :-( */
+#define INIT1_LENGTH 13
+#define INIT2_LENGTH 6
+#define INIT3_LENGTH 3
+#define INIT4_LENGTH 11 //this one may be 0
+#define PWRDN_LENGTH 4
+
+
+/* The AMD A55E (Hudson-E1) Southbridge contains an integrated Gigabit Ethernet
+ * MAC, however AMD's documentation merely defines the related balls (without
+ * fully describing their function) and states that only Broadcom 50610(M) PHYs
+ * will be supported, that's all. The Hudson register reference skips all MAC
+ * registers entirely, even AMD support doesn't seem to know more about it.
+ *
+ * As Broadcom refused to sell us any 50610 chips or provide any docs (or indeed
+ * even a price list) below $100K expected sales we had to figure out everything
+ * by ourselves. *Everything* below is the result of months of detective work,
+ * documented here lest it get lost:
+ *
+ * The AMD A55E's GbE MAC is a Broadcom 5785, which AMD obviously licensed as IP
+ * core. It uses a standard RGMII/MII interface and the Broadcom drivers will
+ * recognize it by its unchanged PCI ID 14E4:1699, however there are some
+ * specialties.
+ *
+ * The 5785 MAC can detect the link with 4 additional inputs, "phy_status[3:0]",
+ * 'snooping' on the PHY's LED outputs. Interpretation of the LEDs' patterns is
+ * programmed with register 0x5A4 of the MAC. AMD renamed them to "GBE_STAT" and
+ * won't say anything about their purpose. Appearently hardware designers are
+ * expected to blindly copy the Inagua reference schematic: GBE_STAT2:
+ * 0=activity; GBE_STAT[1:0]: 11=no link, 10=10Mbit, 01=100Mbit, 00=1Gbit.
+ *
+ * For package processing the 5785 also features a MIPS-based RISC CPU, booting
+ * from an internal ROM. The firmware loads config data and supplements (e.g. to
+ * support specific PHYs), named "Selfboot Patches", via the "NVRAM Interface",
+ * usually from an external EEPROM. The A55E doesn't have any balls for an ext.
+ * EEPROM, instead AMD added a small internal RAM. The BIOS is expected to copy
+ * the correct contents into this RAM (which only supports byte access!) upon
+ * each powerup. The A55E can trigger an SMI upon writes, enabling the BIOS to
+ * forward any changes to an actually 'NV' location, e.g. the BIOS's SPI flash,
+ * behind the scenes. AMD calls it "GEC shadow ROM", not describing what it's
+ * for nor mentioning the term "NVRAM". broadcom_init() below documents a
+ * procedure how to upload the patch. No SMI magic is installed, therefore
+ * 'NV'RAM writes won't be persistent.
+ *
+ * The "Selfboot Patch" can execute simple commands at various points during
+ * main firmware execution. This can be used to change config registers,
+ * initialize a specific PHY or work around firmware bugs. Broadcom provides
+ * suitable Patches only for their AC131 and 50610 PHYs (as binary blobs). I
+ * found them in DOS\sb_patch\5785\*\sb5785*.* in Driver_14_6_4_2.zip. (Note
+ * that every 32bit-word of these files must be byte-swapped before uploading
+ * them to the A55E.)
+ *
+ * Below is a derived Patch supporting the Micrel KSZ9021 PHY used on the
+ * LiPPERT CFR-AF PC/104 SBC instead, with detailled description of the format.
+ * (Here in correct order for upload.)
+ *
+ * This Patch made Ethernet work with Linux 3.3 - without having to modify the
+ * tg3.ko driver. Broadcom's Windows-Drivers still fail with "Code 10" however;
+ * disassembly showed they check the PHY ID and abort, because the Micrel PHY is
+ * not supported.
+ */
+
+static struct selfboot_patch { //Watch out: all values are *BIG-ENDIAN*!
+
+ struct { /* Global header */
+ u8 signature; //0xA5
+ u8 format; //bits 7-3: patch format; 2-0: revision
+ u8 mac_addr[6];
+ u16 subsys_device; //IDs will be loaded into PCI config space
+ u16 subsys_vendor;
+ u16 pci_device; //PCI device ID; vendor is always Broadcom (0x14E4)
+ u8 unknown1[8]; //?, noticed no effect
+ u16 basic_config; //?, see below
+ u8 checksum; //byte sum of header == 0
+ u8 unknown2; //?, patch rejected if changed
+ u16 patch_version; //10-8: major; 7-0: minor; 15-11: variant (1=a, 2=b, ...)
+ } header;
+
+ struct { /* Init code */
+ u8 checksum; //byte sum of init == 0
+ u8 unknown; //?, looks unused
+ u8 num_hunks; //0x60 = 3 hunks, 0x80 = 4 hunks, other values not supported
+ u8 size; //total size of all hunk#_code[] in bytes
+ u8 hunk1_when; //mark when hunk1_code gets executed
+ u8 hunk1_size; //sizeof(hunk1_code)
+ u8 hunk2_when;
+ u8 hunk2_size;
+ u8 hunk3_when;
+ u8 hunk3_size;
+ u8 hunk4_when; //0x00 (padding) if only 3 hunks
+ u8 hunk4_size; //dito
+ u32 hunk1_code[INIT1_LENGTH]; //actual commands, see below
+ u32 hunk2_code[INIT2_LENGTH];
+ u32 hunk3_code[INIT3_LENGTH];
+ u32 hunk4_code[INIT4_LENGTH]; //missing (zero length) if only 3 hunks
+ } init;
+
+ struct { /* Power down code */
+ u8 checksum; //byte sum of powerdown == 0
+ u8 unknown; //?, looks unused
+ u8 num_hunks; //0x20 = 1 hunk, other values not supported
+ u8 size; //total size of all hunk#_code[] in bytes
+ u8 hunk1_when; //mark when hunk1_code gets executed
+ u8 hunk1_size; //sizeof(hunk1_code)
+ u16 padding; //0x0000, hunk2 is not supported
+ u32 hunk1_code[PWRDN_LENGTH]; //commands, see below
+ } powerdown;
+
+} selfboot_patch = {
+
+/* Keep the following invariant for valid Selfboot patches */
+ .header.signature = 0xA5,
+ .header.format = 0x23, //format 1 revision 3
+ .header.unknown1 = { 0x61, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ .header.checksum = 0, //calculated later
+ .header.unknown2 = 0x30,
+ .init.checksum = 0, //calculated later
+ .init.unknown = 0x00,
+ .init.num_hunks = sizeof(selfboot_patch.init.hunk4_code) ? 0x80 : 0x60,
+ .init.size = sizeof(selfboot_patch.init.hunk1_code)
+ + sizeof(selfboot_patch.init.hunk2_code)
+ + sizeof(selfboot_patch.init.hunk3_code)
+ + sizeof(selfboot_patch.init.hunk4_code),
+ .init.hunk1_size = sizeof(selfboot_patch.init.hunk1_code),
+ .init.hunk2_size = sizeof(selfboot_patch.init.hunk2_code),
+ .init.hunk3_size = sizeof(selfboot_patch.init.hunk3_code),
+ .init.hunk4_size = sizeof(selfboot_patch.init.hunk4_code),
+ .powerdown.checksum = 0, //calculated later
+ .powerdown.unknown = 0x00,
+ .powerdown.num_hunks = 0x20,
+ .powerdown.size = sizeof(selfboot_patch.powerdown.hunk1_code),
+ .powerdown.hunk1_size = sizeof(selfboot_patch.powerdown.hunk1_code),
+ .powerdown.padding = be16(0x0000),
+
+/* Only the lines below may be adapted to your needs ... */
+ .header.mac_addr = { 0x00, 0x20, 0x9D, 0x00, 0x00, 0x00 }, //LiPPERT
+ .header.subsys_device = be16(0x1699), //simply kept this
+ .header.subsys_vendor = be16(0x121D), //LiPPERT
+ .header.pci_device = be16(0x1699), //Broadcom 5785 with GbE PHY
+ .header.patch_version = be16(0x110B), //1.11b, as this was based on Broadcom's sb5785m1.11 patch
+ /* Bitfield enabling general features/codepaths in the firmware or
+ * selecting support for one of several supported PHYs?
+ * Bits not listed had no appearent effect:
+ * 14-11: any bit 1=firmware execution seemed delayed
+ * 10: 0=firmware execution seemed delayed
+ * 9,2,0: select PHY type, affects these registers, probably more
+ * 9 2 0 | reg 0x05A4 PHY reg 31 PHY 23,24,28 Notes
+ * -------+----------------------------------------------------------
+ * 0 0 0 | 0x331C71C1 - changed Inband Status enabled
+ * 0 1 0 | 0x3210C500 - changed -
+ * 0 X 1 | 0x33FF66C0 changed - 10/100 Mbit only
+ * 1 X 0 | 0x330C5180 - - -
+ * 1 X 1 | 0x391C6140 - - -
+ */
+ .header.basic_config = be16(0x0604), //bit 9 set so not to mess up PHY regs, kept other bits unchanged
+
+ /* Tag that defines when / on what occasion the commands are interpreted.
+ * Bits 2-0 = 0 i.e. possible values are 0x00, 08, 10, ..., F8.
+ * On a RISC CPU reset every tag except 0x38, A0, F0, F8 is used. 0x38
+ * seems to be run before a reset is performed(?), the other 3 I have
+ * never seen used. Generally, lower values appear to be run earlier.
+ * An "ifconfig up" with Linux' "tg3" driver causes the tags 0x50, 60,
+ * 68, 20, 70, 80 to be interpreted in this order.
+ * All tests were performed with .basic_config=0x0604.
+ */
+ .init.hunk1_when = 0x10, //only once at RISC CPU reset?
+ /* Instructions are obviously a specialized bytecode interpreted by the
+ * main firmware, rather than MIPS machine code. Commands consist of 1-3
+ * 32-bit words. In the following, 0-9,A-F = hex literals, a-z,_ = variable
+ * parts, each character = 4 bits.
+ * 0610offs newvalue: write (32-bit) <newvalue> to 5785-internal shared mem at <offs>
+ * 08rgvalu: write <valu> to PHY register, <rg> = 0x20 + register number
+ * C610rgnr newvalue: write <newvalue> to MAC register <rgnr>
+ * C1F0rgnr andvalue or_value: modify MAC register <rgnr> by ANDing with <andvalue> and then ORing with <or_value>
+ * C4btrgnr: clear bit in 32-bit MAC register <rgnr>, <bt> = bit number << 3
+ * C3btrgnr: set bit, see C4...; example: command 0xC3200454 sets bit 4 of 32-bit register 0x0454
+ * CBbtrgnr: run next command only if bit (see C4...) == 1 (so far only seen before F7F0...)
+ * F7F0skip: unconditional jump i.e. skip next <skip> code bytes (only seen small positive <skip>)
+ * F7Fxaddr: call function at <addr> in main firmware? <x> = 3 or 4, bool parameter?? Wild guess!
+ * F7FFFadr somvalue: also call func. at <adr>, but with <somvalue> as parameter?? More guessing!
+ * More commands probably exist, but all code I've ever seen was kept
+ * included below, commented out if not suitable for the CFR-AF. v1.xx
+ * is Broadcom's Selfboot patch version sb5785m1.xx where the command
+ * was added, for reference see Broadcom's changelog.
+ */
+ .init.hunk1_code = {
+ be(0x082B8104), //CFR-AF: PHY0B: KSZ9021 select PHY104
+ be(0x082CF0F0), //CFR-AF: PHY0C: KSZ9021 clk/ctl skew (advised by Micrel)
+ be(0x082B8105), //CFR-AF: PHY0B: KSZ9021 select PHY105
+ be(0x082C3333), //CFR-AF: PHY0C: KSZ9021 RX data skew (empirical)
+ be(0xC1F005A0), be(0xFEFFEFFF), be(0x01001000), //v1.05 : 5A0.24,12=1: auto-clock-switch
+ be(0x06100D34), be(0x00000000), //v1.03 : MemD34: clear config vars
+ be(0x06100D38), be(0x00000000), //v1.03 : - |
+ be(0x06100D3C), be(0x00000000), //v1.03 : MemD3F|
+ }, //-->INIT1_LENGTH!
+
+ .init.hunk2_when = 0x30, //after global reset, PHY reset
+ .init.hunk2_code = {
+ //be(0x08370F08), //v1.06 : PHY17: B50610 select reg. 08
+ //be(0x08350001), //v1.06 : PHY15: B50610 slow link fix
+ //be(0x08370F00), //v1.06 : PHY17: B50610 disable reg. 08
+ //be(0x083C2C00), //v1.11 : PHY1C: B50610 Shadow 0B
+ be(0xF7F301E6), //v1.09+: ?: subroutine calls to
+ be(0xF7FFF0B6), be(0x0000FFE7), //v1.09+: ?| restore Port Mode ???
+ be(0xF7FFF0F6), be(0x00008000), //v1.09+: ?|
+ be(0xF7F401E6), //v1.09+: ?|
+ }, //-->INIT2_LENGTH!
+
+ .init.hunk3_when = 0xA8, //?, I'd guess quite late
+ .init.hunk3_code = {
+ be(0xC1F03604), be(0xFFE0FFFF), be(0x00110000), //v1.08 : 3604.20-16: 10Mb clock = 12.5MHz
+ }, //-->INIT3_LENGTH!
+
+ //.init.hunk4_when = 0xD8, //was used in B50610 GPHY Selfboot patch v1.11 this is based on
+ .init.hunk4_when = 0x80, //run last, after Linux' "ifconfig up"
+ .init.hunk4_code = {
+ be(0x083F4300), //CFR-AF: PHY1F: IRQ active high
+ be(0x083C0000), //CFR-AF: PHY1C: revert driver writes
+ be(0x08380000), //CFR-AF: PHY18|
+ be(0x083C0000), //CFR-AF: PHY1C|
+ be(0xCB0005A4), be(0xF7F0000C), //v1.01 : if 5A4.0==1 -->skip next 12 bytes
+ //be(0xC61005A4), be(0x3210C500), //v1.01 : 5A4: PHY LED mode
+ be(0xC61005A4), be(0x331C71CE), //CFR-AF: 5A4: fake LED mode
+ be(0xF7F00008), //v1.01 : -->skip next 8 bytes
+ be(0xC61005A4), be(0x331C71C1), //v1.01 : 5A4: inband LED mode
+ //be(0xC3200454), //CFR-AF: 454.4: auto link polling
+ }, //-->INIT4_LENGTH!
+
+ .powerdown.hunk1_when = 0x50, //prior to IDDQ MAC
+ .powerdown.hunk1_code = {
+ //be(0x083CB001), //v1.10 : PHY1C: IDDQ B50610 PHY
+ be(0xF7F30116), // IDDQ PHY
+ be(0xC40005A0), //v1.09 : 5A0.0=0: Port Mode = MII
+ be(0xC4180400), //v1.09 : 400.3=0|
+ be(0xC3100400), //v1.09 : 400.2=1|
+ }, //-->PWRDN_LENGTH!
+
+};
+
+/* Upload 'NV'RAM contents for BCM5785 GbE MAC integrated in A55E.
+ * Call this from mainboard.c.
+ */
+void broadcom_init(void)
+{
+ volatile u32 *gec_base; //Gigabit Ethernet Controller base addr
+ u8 *gec_shadow; //base addr of shadow 'NV'RAM for GbE MAC in A55E
+ u8 sum;
+ int i;
+
+ gec_base = (u32*)(long)dev_find_slot(0, PCI_DEVFN(0x14, 6))->resource_list->base;
+ gec_shadow = (u8*)(pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x14, 3)), 0x9C) & 0xFFFFFC00);
+ printk(BIOS_DEBUG, "Upload GbE 'NV'RAM contents @ 0x%08lx\n", (unsigned long)gec_shadow);
+
+ /* Halt RISC CPU before uploading the firmware patch */
+ for (i=10000; i > 0; i--) {
+ gec_base[0x5004/4] = 0xFFFFFFFF; //clear CPU state
+ gec_base[0x5000/4] |= (1<<10); //issue RISC halt
+ if (gec_base[0x5000/4] | (1<<10))
+ break;
+ udelay(10);
+ }
+ if (!i)
+ printk(BIOS_ERR, "Failed to halt RISC CPU!\n");
+
+ /* Calculate checksums (standard byte sum) */
+ for (sum = 0, i = 0; i < sizeof(selfboot_patch.header); i++)
+ sum -= ((u8*)&selfboot_patch.header)[i];
+ selfboot_patch.header.checksum = sum;
+ for (sum = 0, i = 0; i < sizeof(selfboot_patch.init); i++)
+ sum -= ((u8*)&selfboot_patch.init)[i];
+ selfboot_patch.init.checksum = sum;
+ for (sum = 0, i = 0; i < sizeof(selfboot_patch.powerdown); i++)
+ sum -= ((u8*)&selfboot_patch.powerdown)[i];
+ selfboot_patch.powerdown.checksum = sum;
+
+ /* Upload firmware patch to shadow 'NV'RAM */
+ for (i = 0; i < sizeof(selfboot_patch); i++)
+ gec_shadow[i] = ((u8*)&selfboot_patch)[i]; //access byte-wise!
+
+ /* Restart BCM5785's CPU */
+ gec_base[0x5004/4] = 0xFFFFFFFF; //clear CPU state
+ gec_base[0x5000/4] = 0x00000001; //reset RISC processor
+ //usually we'd have to wait for the reset bit to clear again ...
+}