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)

Reply via email to