Marketing name: Apple iPod nano (2nd generation) Signed-off-by: Vencislav Atanasov <[email protected]> --- MAINTAINERS | 2 + arch/arm/dts/s5l8701-n36.dts | 26 ++++++++ arch/arm/dts/s5l8701.dtsi | 62 ++++++++++++++++++ arch/arm/mach-s5l87xx/Kconfig | 2 + arch/arm/mach-s5l87xx/Makefile | 4 ++ arch/arm/mach-s5l87xx/s5l8701-otg-phy.c | 56 ++++++++++++++++ arch/arm/mach-s5l87xx/s5l8701-timer.c | 109 ++++++++++++++++++++++++++++++++ arch/arm/mach-s5l87xx/s5l8701.c | 26 ++++++++ arch/arm/mach-s5l87xx/s5l8701/Kconfig | 12 ++++ board/apple/n36/Kconfig | 15 +++++ board/apple/n36/Makefile | 7 ++ board/apple/n36/n36.c | 19 ++++++ configs/apple_n36_defconfig | 31 +++++++++ drivers/usb/gadget/dwc2_udc_otg.c | 16 +++++ include/configs/apple_n36.h | 15 +++++ 15 files changed, 402 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS index fccc5b2e886..a54987e8546 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -607,6 +607,8 @@ F: arch/arm/dts/s5l87* F: arch/arm/include/asm/arch-s5l87* F: arch/arm/mach-s5l87* F: board/apple/n* +F: configs/apple_n* +F: include/configs/apple_n* ARM SAMSUNG SOC DRIVERS M: Sam Protsenko <[email protected]> diff --git a/arch/arm/dts/s5l8701-n36.dts b/arch/arm/dts/s5l8701-n36.dts new file mode 100644 index 00000000000..f8d6cdc8dba --- /dev/null +++ b/arch/arm/dts/s5l8701-n36.dts @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Apple iPod nano (2nd generation) + * + * target-type: N36 + * + * Copyright (C) 2026 The freemyipod team (freemyipod.org) + */ + +/dts-v1/; + +#include "s5l8701.dtsi" +#include "s5l87xx-nxx.dtsi" + +/ { + compatible = "apple,n36", "samsung,s5l8701", "apple,arm-platform"; + model = "Apple iPod nano (2nd generation)"; +}; + +&memory { + reg = <0x08000000 0x02000000>; /* 32 MiB SDRAM */ +}; + +&clkref { + clock-frequency = <24000000>; +}; diff --git a/arch/arm/dts/s5l8701.dtsi b/arch/arm/dts/s5l8701.dtsi new file mode 100644 index 00000000000..df14e80f083 --- /dev/null +++ b/arch/arm/dts/s5l8701.dtsi @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Samsung S5L8701 SoC + * + * Other names: M1 + * + * Copyright (C) 2026 The freemyipod team (freemyipod.org) + */ + +#include "s5l87xx.dtsi" + +/ { + compatible = "samsung,s5l8701", "apple,arm-platform"; + + chosen { + stdout-path = &uart0; + }; + + soc { + icu: interrupt-controller@39c00000 { + compatible = "samsung,s5l8701-icu"; + interrupt-controller; + reg = <0x39c00000 0x1000>; + #interrupt-cells = <1>; + }; + + usbotg_hs: usb@38800000 { + compatible = "apple,s5l87xx-usb"; + reg = <0x38800000 0x40000>; + interrupt-parent = <&icu>; + interrupts = <16>; + phys = <&usbphy>; + phy-names = "usb2-phy"; + clocks = <&clkref>; + clock-names = "otg"; + g-rx-fifo-size = <256>; + g-np-tx-fifo-size = <128>; + g-tx-fifo-size = <0>; + dr_mode = "peripheral"; + }; + }; +}; + +&cpu0 { + compatible = "arm,arm940t"; +}; + +&clkgates { + /* + * Clock-gate name -> {gate, bit} table consumed by s5l87xx_enable_clkgate(). + * Each entry is two cells: gate * 32 + bit, with 0xffffffff for an unused + * second gate. The Nano2G (S5L8701) uses single gates (PWRCON registers). + * See https://freemyipod.org/wiki/Nano2G_clock_gates + */ + clock-gate-names = "timer", "uart", "usb2-phy", "usb-otg"; + samsung,clock-gates = <4 0xffffffff>, <8 0xffffffff>, + <14 0xffffffff>, <43 0xffffffff>; +}; + +&uart0 { + status = "okay"; +}; diff --git a/arch/arm/mach-s5l87xx/Kconfig b/arch/arm/mach-s5l87xx/Kconfig index 39de1039495..ec35a293998 100644 --- a/arch/arm/mach-s5l87xx/Kconfig +++ b/arch/arm/mach-s5l87xx/Kconfig @@ -57,4 +57,6 @@ config S5L8740 endchoice +source "arch/arm/mach-s5l87xx/s5l8701/Kconfig" + endif diff --git a/arch/arm/mach-s5l87xx/Makefile b/arch/arm/mach-s5l87xx/Makefile index c8d200479c3..68da781df74 100644 --- a/arch/arm/mach-s5l87xx/Makefile +++ b/arch/arm/mach-s5l87xx/Makefile @@ -5,3 +5,7 @@ obj-y += s5l87xx.o obj-y += s5l87xx-clk.o + +obj-$(CONFIG_S5L8701) += s5l8701.o +obj-$(CONFIG_S5L8701) += s5l8701-otg-phy.o +obj-$(CONFIG_S5L8701) += s5l8701-timer.o diff --git a/arch/arm/mach-s5l87xx/s5l8701-otg-phy.c b/arch/arm/mach-s5l87xx/s5l8701-otg-phy.c new file mode 100644 index 00000000000..9584d662de5 --- /dev/null +++ b/arch/arm/mach-s5l87xx/s5l8701-otg-phy.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2026 The freemyipod team (freemyipod.org) + */ + +#include <init.h> +#include <asm/io.h> +#include <asm/arch-s5l87xx/s5l87xx.h> +#include <asm/arch-s5l87xx/clk.h> +#include <linux/delay.h> + +struct s5l87xx_otgphy { + u32 pwr; // 0x00 + u32 con; // 0x04 + u32 rstcon; // 0x08 + u32 unk[4]; // 0x0c, 0x10, 0x14, 0x18 + u32 unkcon; // 0x1c + u32 pad[36]; // 0x20 - 0x44 + u32 unk44; // 0x44 +}; + +void otg_phy_init(void *unused) +{ + struct s5l87xx_otgphy *otgphy = (struct s5l87xx_otgphy *)S5L87XX_PHY_BASE; + + log_debug("s5l8701_otgphy: turning on\n"); + s5l87xx_enable_clkgate("usb-otg"); + s5l87xx_enable_clkgate("usb2-phy"); + mdelay(10); + + // Disable USB suspend. + writel(0, S5L87XX_OTG_BASE + 0xe00); + + writel(0, &otgphy->pwr); /* PHY: Power up */ + udelay(10); + writel(1, &otgphy->unkcon); + writel(0xe3f, &otgphy->unk44); + writel(1, &otgphy->rstcon); /* PHY: Assert Software Reset */ + udelay(10); + writel(0, &otgphy->rstcon); /* PHY: Deassert Software Reset */ + udelay(10); + writel(0x600, &otgphy->unk[3]); + writel(0, &otgphy->con); + udelay(400); +} + +void otg_phy_off(void *unused) +{ + struct s5l87xx_otgphy *otgphy = (struct s5l87xx_otgphy *)S5L87XX_PHY_BASE; + + log_debug("s5l8701_otgphy: turning off\n"); + writel(0x0F, &otgphy->pwr); /* PHY: Power down */ + udelay(10); + writel(0x07, &otgphy->rstcon); /* PHY: Assert Software Reset */ + udelay(10); +} diff --git a/arch/arm/mach-s5l87xx/s5l8701-timer.c b/arch/arm/mach-s5l87xx/s5l8701-timer.c new file mode 100644 index 00000000000..ca0854c6acf --- /dev/null +++ b/arch/arm/mach-s5l87xx/s5l8701-timer.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2026 The freemyipod team (freemyipod.org) + */ + +#include <init.h> +#include <asm/io.h> +#include <asm/arch-s5l87xx/s5l87xx.h> +#include <asm/arch-s5l87xx/clk.h> + +struct s5l87xx_timer { + u32 con; // 0x00 + u32 cmd; // 0x04 + u32 data0; // 0x08 + u32 data1; // 0x0c + u32 pre; // 0x10 + u32 cnt; // 0x14 +}; + +enum s5l87xx_timer_id { + // Timers A, B, C, D: 16-bit + S5L87XX_TIMER_A = 0, + S5L87XX_TIMER_B = 1, + S5L87XX_TIMER_C = 2, + S5L87XX_TIMER_D = 3, +}; + +#define SYSTEM_TIMER S5L87XX_TIMER_C + +enum s5l87xx_timer_cmd { + S5L87XX_TIMER_CMD_STOP = 0, + S5L87XX_TIMER_CMD_START = 1, + S5L87XX_TIMER_CMD_CLR = 2, +}; + +static struct s5l87xx_timer *s5l87xx_timer_registers(enum s5l87xx_timer_id id) +{ + switch (id) { + case S5L87XX_TIMER_A: + return (struct s5l87xx_timer *)S5L87XX_TIMER_BASE; + case S5L87XX_TIMER_B: + return (struct s5l87xx_timer *)(S5L87XX_TIMER_BASE + 0x20); + case S5L87XX_TIMER_C: + return (struct s5l87xx_timer *)(S5L87XX_TIMER_BASE + 0x40); + case S5L87XX_TIMER_D: + return (struct s5l87xx_timer *)(S5L87XX_TIMER_BASE + 0x60); + default: + panic("requested invalid timer id %d", id); + } +} + +static const char *s5l87xx_timer_clockgate(enum s5l87xx_timer_id id) +{ + /* S5L8701 and S5L8702 gate all timers behind a single clock gate. */ + return "timer"; +} + +static void s5l87xx_timer_configure_interval(enum s5l87xx_timer_id id) +{ + struct s5l87xx_timer *timer = s5l87xx_timer_registers(id); + + log_debug("s5l87xx_timer: configuring %d in interval mode\n", id); + s5l87xx_enable_clkgate(s5l87xx_timer_clockgate(id)); + + writel(S5L87XX_TIMER_CMD_STOP, &timer->cmd); + /* configure timer for 1000 Hz??? */ + writel((3 << 8) | (1 << 4), &timer->con); + writel(511, &timer->pre); + writel(0xffff, &timer->data0); + writel(0xffff, &timer->data1); + writel(S5L87XX_TIMER_CMD_CLR, &timer->cmd); +} + +static void s5l87xx_timer_start(enum s5l87xx_timer_id id) +{ + struct s5l87xx_timer *timer = s5l87xx_timer_registers(id); + + log_debug("s5l87xx_timer: starting %d\n", id); + writel(S5L87XX_TIMER_CMD_START, &timer->cmd); +} + +static u32 s5l87xx_timer_read(enum s5l87xx_timer_id id) +{ + struct s5l87xx_timer *timer = s5l87xx_timer_registers(id); + + return readl(&timer->cnt); +} + +int timer_init(void) +{ + s5l87xx_timer_configure_interval(SYSTEM_TIMER); + s5l87xx_timer_start(SYSTEM_TIMER); + + return 0; +} + +unsigned long timer_read_counter(void) +{ + static u16 last, high; + + u16 now = s5l87xx_timer_read(SYSTEM_TIMER); + + if (now < last) + high++; + + last = now; + + return ((u32)high << 16) | (u32)now; +} diff --git a/arch/arm/mach-s5l87xx/s5l8701.c b/arch/arm/mach-s5l87xx/s5l8701.c new file mode 100644 index 00000000000..9cacbf3eafb --- /dev/null +++ b/arch/arm/mach-s5l87xx/s5l8701.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2026 The freemyipod team (freemyipod.org) + */ + +#include <init.h> + +int print_cpuinfo(void) +{ + printf("CPU: Samsung S5L8701 (ARM940T)\n"); + return 0; +} + +void enable_caches(void) +{ + // Caches already enabled by the bootloader + // This is here so that we don't see + // WARNING: Caches not enabled +} + +ulong get_tbclk(void) +{ + // Corresponds to timer setup from s5l8701-timer.c. + // Is this correct? + return 1416; +} diff --git a/arch/arm/mach-s5l87xx/s5l8701/Kconfig b/arch/arm/mach-s5l87xx/s5l8701/Kconfig new file mode 100644 index 00000000000..7a12d3c4d20 --- /dev/null +++ b/arch/arm/mach-s5l87xx/s5l8701/Kconfig @@ -0,0 +1,12 @@ +if S5L8701 + +config TARGET_N36 + bool "Apple N36 - iPod nano (2nd generation)" + default y + help + Apple iPod nano (2nd generation) is a digital audio player, introduced in + September 2006. It has a NAND flash of either 2, 4 or 8 GiB. + +source "board/apple/n36/Kconfig" + +endif diff --git a/board/apple/n36/Kconfig b/board/apple/n36/Kconfig new file mode 100644 index 00000000000..19190eddbc1 --- /dev/null +++ b/board/apple/n36/Kconfig @@ -0,0 +1,15 @@ +if TARGET_N36 + +config SYS_BOARD + default "n36" + +config SYS_VENDOR + default "apple" + +config SYS_SOC + default "s5l8701" + +config SYS_CONFIG_NAME + default "apple_n36" + +endif diff --git a/board/apple/n36/Makefile b/board/apple/n36/Makefile new file mode 100644 index 00000000000..a8fde44f930 --- /dev/null +++ b/board/apple/n36/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2026 The freemyipod team (freemyipod.org) +# + +obj-y += ../nxx-common/nxx.o +obj-y += n36.o diff --git a/board/apple/n36/n36.c b/board/apple/n36/n36.c new file mode 100644 index 00000000000..663b213247c --- /dev/null +++ b/board/apple/n36/n36.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2026 The freemyipod team (freemyipod.org) + */ + +#include <asm/arch-s5l87xx/clk.h> + +static void board_clock_init(void) +{ + s5l87xx_enable_clkgate("uart"); +} + +int board_early_init_f(void) +{ + /* enabling UART clocking */ + board_clock_init(); + + return 0; +} diff --git a/configs/apple_n36_defconfig b/configs/apple_n36_defconfig new file mode 100644 index 00000000000..e2c9ff308f7 --- /dev/null +++ b/configs/apple_n36_defconfig @@ -0,0 +1,31 @@ +CONFIG_ARM=y +CONFIG_SKIP_LOWLEVEL_INIT_ONLY=y +CONFIG_POSITION_INDEPENDENT=y +CONFIG_ARCH_S5L87XX=y +CONFIG_TEXT_BASE=0x08000000 +CONFIG_SYS_MALLOC_LEN=0x00200000 +CONFIG_DEFAULT_DEVICE_TREE="s5l8701-n36" +CONFIG_SYS_LOAD_ADDR=0x08000000 +CONFIG_DEBUG_UART_BASE=0x3CC00000 +CONFIG_DEBUG_UART_CLOCK=24000000 +CONFIG_DEBUG_UART=y +CONFIG_STACK_SIZE=0x00100000 +CONFIG_FIT=y +CONFIG_BOOTDELAY=3 +CONFIG_USE_BOOTCOMMAND=y +CONFIG_BOOTCOMMAND="dfu 0 ram 0; bootm" +CONFIG_BOARD_EARLY_INIT_F=y +CONFIG_CMD_DFU=y +CONFIG_OF_CONTROL=y +CONFIG_CLK=y +CONFIG_DFU_RAM=y +CONFIG_SET_DFU_ALT_INFO=y +CONFIG_SYS_DFU_DATA_BUF_SIZE=0x00100000 +CONFIG_DEBUG_UART_ANNOUNCE=y +CONFIG_USB=y +CONFIG_DM_USB_GADGET=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VENDOR_NUM=0x05ac +CONFIG_USB_GADGET_PRODUCT_NUM=0xff00 +CONFIG_USB_GADGET_DWC2_OTG=y +CONFIG_USB_GADGET_DOWNLOAD=y diff --git a/drivers/usb/gadget/dwc2_udc_otg.c b/drivers/usb/gadget/dwc2_udc_otg.c index ac36e5e2d87..09f18c64408 100644 --- a/drivers/usb/gadget/dwc2_udc_otg.c +++ b/drivers/usb/gadget/dwc2_udc_otg.c @@ -287,6 +287,14 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) enable_irq(IRQ_OTG); + if (IS_ENABLED(CONFIG_S5L8701)) { + u32 value; + + value = readl(S5L87XX_INTMSK); + value |= 1 << IRQ_USB_FUNC; + writel(value, S5L87XX_INTMSK); + } + debug_cond(DEBUG_SETUP != 0, "Registered gadget driver %s\n", dev->gadget.name); udc_enable(dev); @@ -316,6 +324,14 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) disable_irq(IRQ_OTG); + if (IS_ENABLED(CONFIG_S5L8701)) { + u32 value; + + value = readl(S5L87XX_INTMSK); + value &= ~(1 << IRQ_USB_FUNC); + writel(value, S5L87XX_INTMSK); + } + udc_disable(dev); return 0; } diff --git a/include/configs/apple_n36.h b/include/configs/apple_n36.h new file mode 100644 index 00000000000..cad28a3cc8e --- /dev/null +++ b/include/configs/apple_n36.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2026 The freemyipod team (freemyipod.org) + */ + +#ifndef __CONFIG_H +#define __CONFIG_H + +#define CFG_SYS_UBOOT_BASE CONFIG_TEXT_BASE + +// 176K SRAM total +#define CFG_SYS_INIT_RAM_ADDR 0x22000000 +#define CFG_SYS_INIT_RAM_SIZE 0x0002C000 + +#endif /* __CONFIG_H */ -- Git-157)
