[U-Boot] [PATCH 1/4] s5pc1xx: support Samsung s5pc1xx SoC

Wolfgang Denk wd at denx.de
Fri Sep 4 12:13:50 CEST 2009


Dear Minkyu Kang,

In message <4AA0CE3B.7050904 at samsung.com> you wrote:
> This patch add support new SoCs that are s5pc100 and s5pc110
> s5pc1xx SoC is ARM Cortex A8 processor

Please change into:

        This patch adds support for the Samsung s5pc100 and s5pc110
	SoCs. The s5pc1xx SoC is an ARM Cortex A8 processor.

> --- /dev/null
> +++ b/cpu/arm_cortexa8/s5pc1xx/Makefile
...
> +LIB	= $(obj)lib$(SOC).a
> +
> +SOBJS	= reset.o
> +
> +COBJS	+= timer.o
> +COBJS	+= clock.o
> +COBJS	+= cpu_info.o
> +COBJS	+= cache.o

Please always sort such lists alphabetically.

> diff --git a/cpu/arm_cortexa8/s5pc1xx/clock.c b/cpu/arm_cortexa8/s5pc1xx/clock.c
> new file mode 100644
> index 0000000..59690d7
> --- /dev/null
> +++ b/cpu/arm_cortexa8/s5pc1xx/clock.c
...
> +/*
> + * This code should work for both the S3C2400 and the S3C2410
> + * as they seem to have the same PLL and clock machinery inside.
> + * The different address mapping is handled by the s3c24xx.h files below.
> + */

Please check the comment. Is this for s5pc1xx or for s3c24xx?


> +static int s5pc1xx_clock_read_reg(int offset)
> +{
> +	return readl(S5PC1XX_CLOCK_BASE + offset);
> +}

NAK. This needs to be reworked. We do not allow accesses to device
registers like this. Instead of using a base address + offset (which
carries absolutley no information about the type of the accessed
register - 8, 16 or 32 bit?) we require that you describe your
peripherals as C structs, so we can strict type checking by the C
compiler.

> +/* ------------------------------------------------------------------------- */
> +/*
> + * NOTE: This describes the proper use of this file.

Omit thsi comment, please.

> + * CONFIG_SYS_CLK_FREQ should be defined as the input frequency of the PLL.

CONFIG_SYS_CLK_FREQ is nowhere used in the whole file.

> +unsigned long get_pll_clk(int pllreg)
> +{
> +	unsigned long r, m, p, s, mask, fout;
> +	unsigned int freq;
> +
> +	switch (pllreg) {
> +	case APLL:
> +		if (cpu_is_s5pc110())
> +			r = s5pc1xx_clock_read_reg(S5PC110_APLL_CON_OFFSET);
> +		else
> +			r = s5pc1xx_clock_read_reg(S5PC100_APLL_CON_OFFSET);
> +		break;

I'm not sure what exactly you want to acchieve here.

I see two possibilities:

1) You want to creates some "multiboot" capable image, i. e. a single
   U-Boot binary image that is capable of running both on s5pc100 and
   on s5pc110 systems.  Only in this case such a run-time test makes
   sense.  But then it should be written in a more flexible way, so
   that it for example allows to add some (still hypothetical)
   s5pc120 and s5pc130 at a later time. 

2) The resulting U-Boot image will be able to run on only one type of
   SoC; in this case you should make sure that all such decisions can
   be performet at compile time, and no dead code gets added to the
   U-Boot image.


In any case, the code as written is unacceptable, as it extremely
cumbersome to maintain, and is a nightmare to extend for further SoCs.

But I think issue will be solved automatically when you rework your
code to use C structs instead of register offsets, ans you can then
just do a single cpu_is_s5pc???() test and set a pointer to the right
type of data structure.

> +	case HPLL:
> +		if (cpu_is_s5pc110())
> +			hang();

Arghh... It is an extremely bad idea to hang the system without any
indication about what was going on. Please add at least some error
message here.

> +		r = s5pc1xx_clock_read_reg(S5PC100_HPLL_CON_OFFSET);
> +		break;
> +	case VPLL:
> +		if (cpu_is_s5pc100())
> +			hang();

Ditto.

> +	default:
> +		hang();

Ditto.

> +
> +	if (cpu_is_s5pc110()) {
> +		if (pllreg == APLL || pllreg == MPLL)
> +			mask = 0x3ff;
> +		else
> +			mask = 0x1ff;
> +	} else {
> +		if (pllreg == APLL)
> +			mask = 0x3ff;
> +		else
> +			mask = 0x0ff;

Hm... all these magic numbers need some comments, and eventyally some
symbolic constants might be defined for them.

> +	}
> +
> +	m = (r >> 16) & mask;
> +	p = (r >> 8) & 0x3f;
> +	s = r & 0x7;
> +
> +	if (cpu_is_s5pc110()) {
> +		freq = CONFIG_SYS_CLK_FREQ_C110;
> +		if (pllreg == APLL) {
> +			if (s < 1)
> +				s = 1;
> +			fout = m * (freq / (p * (1 << (s - 1))));
> +		} else
> +			fout = m * (freq / (p * (1 << s)));
> +	} else {
> +		freq = CONFIG_SYS_CLK_FREQ_C100;
> +		fout = m * (freq / (p * (1 << s)));

All this black magic needs to be explained. Please add comments.

...
> +/* s5pc110: return HCLKs frequency */
> +unsigned long get_hclk_sys(int clk)
> +{
> +	unsigned long hclk;
> +	unsigned int div;
> +	unsigned int offset;
> +	unsigned int hclk_sys_ratio;
> +
> +	if (clk == CLK_M)
> +		return get_hclk();
> +
> +	div = s5pc1xx_clock_read_reg(S5P_CLK_DIV0_OFFSET);
> +
> +	/* HCLK_MSYS_RATIO: [10:8]
> +	 * HCLK_DSYS_RATIO: [19:16]
> +	 * HCLK_PSYS_RATIO: [27:24] */

Incorrect multiline comment style. Please fix globally.

...
> +#ifdef CONFIG_DISPLAY_CPUINFO
> +int print_cpuinfo(void)
> +{
> +	printf("CPU:\tS5P%X@%luMHz\n", s5pc1xx_cpu_id, get_arm_clk() / 1000000);
> +	if (cpu_is_s5pc110()) {
> +		printf("\tAPLL = %luMHz, MPLL = %luMHz, EPLL = %luMHz\n",
> +			get_fclk() / 1000000,
> +			get_mclk() / 1000000,
> +			get_uclk() / 1000000);
> +		printf("\tHclk: Msys %luMhz, Dsys %luMhz, Psys %luMhz\n",
> +			get_hclk_sys(CLK_M) / 1000000,
> +			get_hclk_sys(CLK_D) / 1000000,
> +			get_hclk_sys(CLK_P) / 1000000);
> +		printf("\tPclk: Msys %3luMhz, Dsys %3luMhz, Psys %3luMhz\n",
> +			get_pclk_sys(CLK_M) / 1000000,
> +			get_pclk_sys(CLK_D) / 1000000,
> +			get_pclk_sys(CLK_P) / 1000000);
> +	} else {
> +		printf("\tFclk = %luMHz, HclkD0 = %luMHz, PclkD0 = %luMHz,"
> +			" PclkD1 = %luMHz\n",
> +			get_fclk() / 1000000, get_hclk() / 1000000,
> +			get_pclkd0() / 1000000, get_pclkd1() / 1000000);

Please use strmhz() to format and print frequencies, as this will do
proper rounding.

...
> +static inline unsigned long READ_TIMER(void)
> +{

Please always use lower case identifier names.


> +/*
> + * This function is derived from PowerPC code (timebase clock frequency).
> + * On ARM it returns the number of timer ticks per second.
> + */
> +unsigned long get_tbclk(void)
> +{
> +	/* We overrun in 100s */
> +	return CONFIG_SYS_HZ * 100;
> +}

??? This look awkward to me. What exactly are you doing here, and why?


> --- /dev/null
> +++ b/include/asm-arm/arch-s5pc1xx/clock.h
...
> +/*
> + * Clock control
> + */
> +
> +/* Clock Register */
> +#define S5PC100_APLL_LOCK_OFFSET	0x0
> +#define S5PC100_MPLL_LOCK_OFFSET	0x4
> +#define S5PC100_EPLL_LOCK_OFFSET	0x8
> +#define S5PC100_HPLL_LOCK_OFFSET	0xc

Full NAK. Please declare C structs instead.

> --- /dev/null
> +++ b/include/asm-arm/arch-s5pc1xx/cpu.h
...
> +#define S5PC1XX_ADDR_BASE	0xe0000000
> +#define S5P_ADDR(x)		(S5PC1XX_ADDR_BASE + (x))

See previous comments about use of base plus register offsets.

> +/*
> + * Chip ID
> + */
> +#define S5PC1XX_CHIP_ID(x)	(0xE0000000 + (x))

Ditto.

> +#define S5PC1XX_PRO_ID		S5PC1XX_CHIP_ID(0)
> +#define S5PC1XX_OMR		S5PC1XX_CHIP_ID(4)

Ditto.

> +#define IS_SAMSUNG_TYPE(type, id)					\
> +static inline int is_##type(void)					\
> +{									\
> +	return (s5pc1xx_cpu_id == (id)) ? 1 : 0;			\
> +}
> +
> +IS_SAMSUNG_TYPE(s5pc100, 0xc100)
> +IS_SAMSUNG_TYPE(s5pc110, 0xc110)
> +
> +#define cpu_is_s5pc100()	is_s5pc100()
> +#define cpu_is_s5pc110()	is_s5pc110()
> +#endif

Check your intentions - is a runtime test really what you want? Or
would a compile time test be more appropriate?


> diff --git a/include/asm-arm/arch-s5pc1xx/gpio.h b/include/asm-arm/arch-s5pc1xx/gpio.h
> new file mode 100644
> index 0000000..26d0950
> --- /dev/null
> +++ b/include/asm-arm/arch-s5pc1xx/gpio.h
...
> +/* GPIO Bank Base */
> +#define S5PC100_GPIO_BASE(x)		(0xE0300000 + (x))
> +#define S5PC110_GPIO_BASE(x)		(0xE0200000 + (x))
> +
> +/* S5PC100 bank offset */
> +#define S5PC100_GPIO_A0_OFFSET		0x000
> +#define S5PC100_GPIO_A1_OFFSET		0x020
> +#define S5PC100_GPIO_B_OFFSET		0x040
> +#define S5PC100_GPIO_C_OFFSET		0x060

Full NAK. Use C structs. lease see above.

> diff --git a/include/asm-arm/arch-s5pc1xx/hardware.h b/include/asm-arm/arch-s5pc1xx/hardware.h
> new file mode 100644
> index 0000000..ca95c3d
> --- /dev/null
> +++ b/include/asm-arm/arch-s5pc1xx/hardware.h
...
> +#ifndef __ASSEMBLY__
> +#define UData(Data)	((unsigned long)(Data))
> +
> +#define __REG(x)	(*(vu_long *)(x))
> +#define __REGl(x)	(*(vu_long *)(x))
> +#define __REGw(x)	(*(vu_short *)(x))
> +#define __REGb(x)	(*(vu_char *)(x))
> +#define __REG2(x, y)	(*(vu_long *)((x) + (y)))
> +#else
> +#define UData(Data)	(Data)
> +
> +#define __REG(x)	(x)
> +#define __REGl(x)	(x)
> +#define __REGw(x)	(x)
> +#define __REGb(x)	(x)
> +#define __REG2(x, y)	((x) + (y))
> +#endif
> +
> +#define Fld(Size, Shft)	(((Size) << 16) + (Shft))
> +
> +#define FSize(Field)	((Field) >> 16)
> +#define FShft(Field)	((Field) & 0x0000FFFF)
> +#define FMsk(Field)	(((UData(1) << FSize(Field)) - 1) << FShft(Field))
> +#define FAlnMsk(Field)	((UData(1) << FSize(Field)) - 1)
> +#define F1stBit(Field)	(UData(1) << FShft(Field))
> +
> +#define FClrBit(Data, Bit)	(Data = (Data & ~(Bit)))
> +#define FClrFld(Data, Field)	(Data = (Data & ~FMsk(Field)))
> +
> +#define FInsrt(Value, Field) \
> +			(UData(Value) << FShft(Field))
> +
> +#define FExtr(Data, Field) \
> +			((UData(Data) >> FShft(Field)) & FAlnMsk(Field))

Please get rid of all these definitions. None of these will be
accepted in U-Boot.

Please use existent accessors and macros instead.

> diff --git a/include/asm-arm/arch-s5pc1xx/interrupt.h b/include/asm-arm/arch-s5pc1xx/interrupt.h
> new file mode 100644
> index 0000000..a3c8680
> --- /dev/null
> +++ b/include/asm-arm/arch-s5pc1xx/interrupt.h
...
> +/* Vector Interrupt Offset */
> +#define VIC_IRQSTATUS_OFFSET		0x0
> +#define VIC_FIQSTATUS_OFFSET		0x4
> +#define VIC_RAWINTR_OFFSET		0x8
> +#define VIC_INTSELECT_OFFSET		0xc
> +#define VIC_INTENABLE_OFFSET		0x10
> +#define VIC_INTENCLEAR_OFFSET		0x14
> +#define VIC_SOFTINT_OFFSET		0x18
> +#define VIC_SOFTINTCLEAR_OFFSET		0x1c
> +#define VIC_PROTECTION_OFFSET		0x20
> +#define VIC_SWPRIORITYMASK_OFFSET	0x24
> +#define VIC_PRIORITYDAISY_OFFSET	0x28
> +#define VIC_INTADDRESS_OFFSET		0xf00

Use a C struct instead?

> +#endif
> diff --git a/include/asm-arm/arch-s5pc1xx/keypad.h b/include/asm-arm/arch-s5pc1xx/keypad.h
> new file mode 100644
> index 0000000..0437b5e
> --- /dev/null
> +++ b/include/asm-arm/arch-s5pc1xx/keypad.h
...
> +#define S5PC100_KEYPAD_BASE		0xF3100000
> +#define S5PC110_KEYPAD_BASE		0xE1600000
> +
> +#define S5PC1XX_KEYIFCON_OFFSET		(0x00)
> +#define S5PC1XX_KEYIFSTSCLR_OFFSET	(0x04)
> +#define S5PC1XX_KEYIFCOL_OFFSET		(0x08)
> +#define S5PC1XX_KEYIFROW_OFFSET		(0x0C)
> +#define S5PC1XX_KEYIFFC_OFFSET		(0x10)

Use a C struct, please.

> diff --git a/include/asm-arm/arch-s5pc1xx/mem.h b/include/asm-arm/arch-s5pc1xx/mem.h
> new file mode 100644
> index 0000000..4b06aaf
> --- /dev/null
> +++ b/include/asm-arm/arch-s5pc1xx/mem.h
...
> +/*
> + * SROMC Controller
> + */
> +/* DRAM Memory Controller */
> +#define S5PC100_DMC_BASE	0xE6000000
> +#define S5PC110_DMC0_BASE	0xF0000000
> +#define S5PC110_DMC1_BASE	0xF1400000
> +
> +/* DMC offset */
> +#define CONCONTROL_OFFSET	0x00
> +#define MEMCONTROL_OFFSET	0x04
> +#define MEMCONFIG0_OFFSET	0x08
> +#define MEMCONFIG1_OFFSET	0x0c
> +#define DIRECTCMD_OFFSET	0x10
> +#define PRECHCONFIG_OFFSET	0x14
> +#define PHYCONTROL0_OFFSET	0x18
> +#define PHYCONTROL1_OFFSET	0x1c
> +#define PHYCONTROL2_OFFSET	0x20
> +#define PWRDNCONFIG_OFFSET	0x28
> +#define TIMINGAREF_OFFSET	0x30
> +#define TIMINGROW_OFFSET	0x34
> +#define TIMINGDATA_OFFSET	0x38
> +#define TIMINGPOWER_OFFSET	0x3c
> +#define PHYSTATUS0_OFFSET	0x40
> +#define PHYSTATUS1_OFFSET	0x44
> +#define CHIP0STATUS_OFFSET	0x48
> +#define CHIP1STATUS_OFFSET	0x4c
> +#define AREFSTATUS_OFFSET	0x50
> +#define MRSTATUS_OFFSET		0x54
> +#define PHYTEST0_OFFSET		0x58
> +#define PHYTEST1_OFFSET		0x5c

Use a C struct, please.

> diff --git a/include/asm-arm/arch-s5pc1xx/uart.h b/include/asm-arm/arch-s5pc1xx/uart.h
> new file mode 100644
> index 0000000..189bbff
> --- /dev/null
> +++ b/include/asm-arm/arch-s5pc1xx/uart.h
...
> +#ifndef __ASSEMBLY__
> +typedef struct s5pc1xx_uart {
> +	volatile unsigned long	ULCON;
> +	volatile unsigned long	UCON;
> +	volatile unsigned long	UFCON;
> +	volatile unsigned long	UMCON;
> +	volatile unsigned long	UTRSTAT;
> +	volatile unsigned long	UERSTAT;
> +	volatile unsigned long	UFSTAT;
> +	volatile unsigned long	UMSTAT;
> +#ifdef __BIG_ENDIAN
> +	volatile unsigned char	res1[3];
> +	volatile unsigned char	UTXH;
> +	volatile unsigned char	res2[3];
> +	volatile unsigned char	URXH;
> +#else /* Little Endian */
> +	volatile unsigned char	UTXH;
> +	volatile unsigned char	res1[3];
> +	volatile unsigned char	URXH;
> +	volatile unsigned char	res2[3];
> +#endif

Do you really want tto support both big endian and little endian
support for these SoCs? Is this conesequentlky done in the rest of all
other files, too?  Has this actually been tested?

Best regards,

Wolfgang Denk

-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
If it happens once, it's a bug.
If it happens twice, it's a feature.
If it happens more than twice, it's a design philosophy.


More information about the U-Boot mailing list