Skip to content

n31: EIC, GPIO, PMIC keys, I2C, USB gadget - #1

Draft
andrew867 wants to merge 20 commits into
freemyipod:s5l87xxfrom
andrew867:n31/glass-peripherals
Draft

n31: EIC, GPIO, PMIC keys, I2C, USB gadget#1
andrew867 wants to merge 20 commits into
freemyipod:s5l87xxfrom
andrew867:n31/glass-peripherals

Conversation

@andrew867

@andrew867 andrew867 commented Aug 23, 2026

Copy link
Copy Markdown

N31 peripherals that run on a real nano 7G with single commit per device, on top of s5l87xx.

Home, Sleep, Play, Vol±, the panel, backlight, I2C, and Windows RNDIS all work on glass. Kernel 6.14.0 #90.

What we tested

  • DFU → 3-image (zImage:ramdisk:dtb) → tty0 # shell
  • Home / Sleep / Play from D1830 nIRQ (GPIO 86, level-low into the EIC)
  • Vol+ / Vol- from SoC GPIO 40/41. The driver polls DIN. gpio-keys-polled is off on purpose: that path issues GPIOCMD 0xFFFE and the pads go quiet
  • Sleep held ~2s → pm_power_off (D1830 reg 13 bit 0)
  • TinyDRM + MMIO backlight. CON/PHTIME are logged and left as U-Boot wrote them
  • Windows 10/11 inbox RNDIS, 1d6b:0106. SSH and telnet to 192.168.7.2

I2C completion on this SoC is IICCON bit 4 (IRQPEND), not the +0x20 INT word. STAT bit 0 is not NAK here; treating it as NAK broke PMIC@0x73.

What we left out

I2S / CS42 PCM is not in this series. We can program the analog path. The SoC serializer still does not drain the FIFO, so there is nothing useful to review yet.

Also out: PL080 tone, Nimbus, BCM2078, Tristar-as-working, LIS3 “fixes”, and dlg,apply-sec-rails. That last one writes the power-off latch at probe.

Other iPods

N46 and N20 still bind apple,s5l87xx-usb to dwc2_set_s5l8702_params (slave PIO, session-valid GINTMSK quirk). N31 uses apple,s5l8740-usb / apple,s5l8740-otgphy so it does not inherit the nano3 PHY ramp or that quirk.

Clock ungate is limited to samsung,s5l8740. We do not write CLKCON +0x00/+0x04 (SYS / DRAM) or +0x50 (fatal latch).

Commits

  1. irqchip: S5L8740 GPIO EIC
  2. gpio: banked S5L8740 + GPIOCMD
  3. i2c: s5l8702 IRQPEND
  4. gpio: d1830 nIRQ / keys / power-off
  5. phy: s5l87xx OTG sequence
  6. usb: N31 DWC2 params + Windows RNDIS
  7. clk: PWRCON ungate on N31
  8. video: backlight + TinyDRM pitch / handoff
  9. dts/defconfig: wire the above
  10. arm: early WDT disarm (CON=0, CNT=0)

Happy to split any of these into their own PRs if that is easier to review.

andrew867 and others added 20 commits August 23, 2026 20:58
N31 GPIO interrupts go through an EIC at 0x39700000, then into the
PL192 VIC EXT lines. Group is gpio >> 5. Chain only the parents
listed in DT; wiring every EXT0..6 hung boot.

GPIO 86 (PMIC nIRQ) is group 2 on VIC EXT3. Vol± stay on SoC GPIO
and do not need this chip.

Tested on iPod nano 7G.
RetailOS programs pads with a word at 0x3CF001E0:
  (bank << 16) | (pin << 8) | cmd
DIN is bank+0x04, DIR is bank+0x14. The 2-line bcm6345 hack on
0x3CF000A4 is not enough for Vol± or for EIC to_irq.

apple,skip-sec-pinmux leaves U-Boot/SEC leftovers alone and only
GPIOCMDs the IIS0 pair plus GPIO 86. Replaying the full SEC table
from Linux broke buttons and USB on glass.

gpio-keys-polled on GPIO 40/41 issues mode 0xFFFE and the pads
go quiet. This driver polls DIN itself.

Tested on iPod nano 7G: Vol+ / Vol- report KEY_VOLUMEUP/DOWN.
N31 IIC1 never sets the +0x20 INT word. Byte done is IICCON bit 4
(S3C IRQPEND). The old ISR read +0x20, returned IRQ_NONE, and left
SCL stretched.

Clear IRQPEND to resume the bus. Do not treat STAT bit 0 as NAK;
that aborted every PMIC@0x73 transfer with -EIO even when VIC 22
fired. On reads, the first IRQPEND is address-complete and DS still
holds the address byte. Skip that one, then take one IRQPEND per
RX byte.

PIO mode stays available but is not required. IIC1 at 0x3C900000
is the PMIC bus.

Tested on iPod nano 7G: D1830 @0x73 and LIS3 @0x18 complete.
Home / Sleep / Play are PMIC bits (regs 7 and 8), not SoC GPIO.
OSOS unmasks them and uses GPIO 86 as an active-low nIRQ into the
EIC. gpio-keys-polled on these lines hammers I2C and was already
disabled in the N31 DTS.

Register an input device and a threaded nIRQ. Keep a slow poll so
a missed EIC edge still shows up.

Do not write register 13 at probe. Bit 0 is the power-off latch
and it cuts Vbat. The old default rail sequence did that. Rail
bring-up stays behind dlg,apply-sec-rails, which N31 must not set.

pm_power_off writes reg 13 bit 0. Sleep held ~2s still uses that
path.

Tested on iPod nano 7G: Home, Sleep, Play, and Sleep-hold poweroff.
The 8702 analog-stage ramp (CTRL1/CTRL2 walk) is for nano 3G. On
nano 7G it drops the Lightning link while DWC2 still probes.
Windows then sees VID_0000&PID_0002.

N31 matches U-Boot s5l87xx-otg-phy: drop D+ (DCTL SFTDISCON),
clear PCGCCTL, then PWR/RSTCON/MODE/CLK. Compatible strings
apple,s5l87xx-otgphy and apple,s5l8740-otgphy select that path.
apple,s5l8702-otgphy keeps the old ramp for N46.

Tested on iPod nano 7G: gadget enumerates after DFU.
N46 and N20 keep apple,s5l87xx-usb → dwc2_set_s5l8702_params
(slave PIO, session_valid_gintmsk_quirk). N31 uses
apple,s5l8740-usb → dwc2_set_s5l87xx_params.

That setter follows RetailOS sub_1B543A: NP=32, first dedicated
IN capped at 512 words, buffer DMA, GAHBCFG INCR8. The 8702
quirk masks USBRST/EP0 until SessReqInt, which does not arrive
after the 87xx PHY reset, so GET_DESCRIPTOR dies as 0000:0002.

RNDIS already leaves cdc_filter=0 until SET. Apply the Windows
inbox bits: IAD class/subclass/protocol on the control iface
(usbccgp / rndiscmp.inf want EF/04/01 there), INIT reports
media connected, LINK_SPEED stays non-zero.

Tested on Windows 10/11 against 1d6b:0106 from our gadget:
Remote NDIS Compatible Device, SSH and telnet to 192.168.7.2.
N31 boots with WTF/U-Boot leftovers. clk_disable_unused then
writes SET_TO_DISABLE gates and peripherals drop. Clear the
known PWRCON banks and the CG16 enable bits in the divider
regs, and mark the published gates CLK_IS_CRITICAL |
CLK_IGNORE_UNUSED.

Never write CLKCON+0x00/+0x04 (SYS PLL / DRAM) or +0x50
(fatal latch). The ungate walk is limited to samsung,s5l8740
so N46 keeps the old probe.

Tested on iPod nano 7G: I2C, GPIO, LCDIF, and DWC2 stay
clocked after late init.
Backlight is a separate MMIO block at 0x3E000000. Level is
1..62 at +0x08. This driver does not touch LCDIF CON or
PHTIME.

TinyDRM was blitting fb->width * fb->height as a linear
buffer. fbcon pitch is wider than 240, so the panel tore.
Walk each row with pitches[0]/4. Log CON/PHTIME at probe
and leave the values U-Boot wrote.

Tested on iPod nano 7G: tty0 shell and backlight at 62.
Replace the 2-line bcm6345 GPIO hack with the banked
s5l8740 GPIO and the EIC. Home/Sleep/Play come from D1830
nIRQ on GPIO 86; do not poll those bits over I2C.
gpio-keys-polled stays disabled so it cannot GPIOCMD 0xFFFE
the Vol pads.

USB uses apple,s5l8740-usb / apple,s5l8740-otgphy so N31
does not inherit the nano3 PHY ramp or the 8702 DWC2 quirk.
syscon-reboot is disabled: 0x100000 arms the watchdog.

No I2S, PL080, CS42, or dlg,apply-sec-rails. Those are not
ready.

Tested on iPod nano 7G with apple_n31_defconfig.
WTF and U-Boot leave CON/CNT armed at 0x3C800000. A bigger
zImage loses the race and resets in the middle of decompress
or early device probe.

Write CON=0 then CNT=0, twice. Never CLKCON+0x50.

Tested on iPod nano 7G: kernel #90 reaches tty0.
Land the in-tree module set used on device (#90+): FMSS CS/META weave
scan and FTL helper, Nimbus touch, PL080 DMA, CS42L81/I2S audio stubs,
Tristar mux, plus DTS/defconfig wiring and RNDIS/gadget tweaks from the
lab tree. Keep FMSS/FTL as loadable modules (CONFIG_FMSS_S5L8740=m).
Peripheral does not keep chaining state between operations. IV needs to be set explicitly before CMD_START.
Fail probe if apple,cpu or CS42 node is missing instead of falling
back to string names. Codec DAI remains cs42l81-hifi (no COMP_DUMMY).
Match tools/linux-n31/Kconfig: SND_SOC_APPLE_NANO7 selects
SND_SOC_APPLE_CS42L81_SPI; CS42 depends on SPI && SND_SOC.
Sustained IIS0/PL080/ALSA playback at 44.1 kHz, CS42 5707D8 path, MikeyBus jack helper, and current FMSS/FTL sources. Analog HP still unproven on UCA222.
Quiet Tristar I2C -110 storm (poll_ms=0, NACK stops poll), DMA-meta
storage path with meta_dma_read default off until CS kick is safe,
PMIC/CS42 glass fixes, and nodrm DTS.
Replace retired fmss monolith with nand-s5l8740 FIL and multi-object
ftl (core/csmap/vecmap) including CXT→BTOC→L2V recover on CS META.
Sync MikeyBus, Tristar, Nimbus, and N31 DTS from live glass bring-up.
Storage bring-up milestone for the iPod nano 7 (S5L8740), plus the code
cleanup that should have accompanied the earlier syncs.

FTL / NAND
----------
Use the SFTL context block as the primary map source instead of replaying
every open superblock. On the glass this takes a cold recover from 498 s to
69 s while mapping more of the volume (642652 -> 811554 LBAs), and only 8 of
1803 superblocks then need replaying.

The key finding is that CXT VBAs are not in the address space
whimory_pack_vba() builds. The FTL counts one superblock as the same virtual
block across every (ce, cau) plane, so a superblock holds 2048 VBAs and the
plane index sits between the page and the slot:

  vba = vblock * (pages_per_sb * planes * vbas_per_page)
      + page * (planes * vbas_per_page) + plane * vbas_per_page + slot

Untranslated, those VBAs land on unrelated and often erased pages, which is
why the context looked unusable. A run of consecutive CXT VBAs is only
contiguous here within one 4-slot group, so extents are split at plane
boundaries on import. The TREE is also partitioned by logical range across
the context superblocks, and only the oldest carries the BASE marker, so all
of them are merged rather than taking the first that parses.

Two quadratic paths are gone: whimory_range_update() walked the interval map
from rb_first() on every L2V update, and the packed L2V repacked an entire
root per update while collecting a root walked the whole map. A binary-search
lower bound plus deferring the pack until replay finishes took a full
brute-force recover from 1005 s to 504 s with byte-identical output.

Ranges now coalesce across weaves when they are contiguous in both LBA and
VBA, keeping the older weave so a later claim is never wrongly rejected as
stale. That cut 208134 interval nodes to 52352 and is what made a full replay
fit in 55 MiB of RAM.

Recovery is now a small state machine: a rebuild cannot tear down a live map,
and re-binding an already-registered disk no longer leaves the gendisk at
capacity 0 and fails every read.

Diagnostics are quiet by default; diag=1 restores the bring-up dumps.

PMIC
----
Reports that looked like kernel crashes were the d1830 100 ms I2C button
sweep misreading r7 during NAND activity, emitting KEY_POWER from a single
sample, and userspace powering the device off. Buttons are interrupt-driven
by default now (btn_poll_ms=0) with a confirming re-read (btn_confirm_ms).

DMA
---
s5l_pl080_desc_free() ran dma_free_coherent() after an in_atomic() test.
That test cannot see spinlock context on a non-preempt build, and the driver
openly leaked when it guessed wrong. Descriptors holding a coherent LLI block
are now queued to a workqueue; pool-backed ones are still released inline.
Compile-tested only: audio on this board is broken for unrelated reasons and
is the next work item.

Cleanup
-------
Cross-driver declarations were repeated as bare externs in six files and had
begun to disagree. They now live in include/linux/apple-n31.h.

A previous automated pass had mangled comments across these drivers, leaving
fragments such as a comment opening with a bare colon, and continuation lines
unindented at column 0. Twelve were rewritten as prose and roughly a thousand
continuation lines re-aligned; the resulting .ko files are byte-identical, so
that part is comment-only.

checkpatch on the touched files goes from 2 errors and 172 warnings to
0 errors and 142 warnings. What remains is deliberate: split format strings
and deep nesting in the NAND sequencer, both of which need real refactoring;
msleep values that come from hardware timing; and sysfs_emit false positives
where the trailing newline is inside a %s argument.

Verified on hardware: read-only FAT mount with 50 Fxx directories, 496 music
files, zero read misses and zero VFAT bread failures. An fsck of the volume
reports 702 files, 91 directories, 2.9 GB, clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Audio, two independent faults either of which is silence on its own.
GPIO 7 was never muxed: sub_BCB60 claims both IIS0 pads together on every
TX enable and releases both on disable, but pad 7 was being skipped in
the belief it drove the display. It does not -- the panel is driven
entirely from the LCDIF, no display code touches GPIO, and RetailOS holds
pad 7 at function 3 while playing music with the panel lit. And register
0x0227, the live output gain, sat at its bring-up minimum forever:
sub_D2C98 encodes code = dB from -50 to +12 and -50 + (dB+50)/2 below
that, floor -63 = -76 dB, which is where the bring-up sequence leaves it
and matches the -66 dBFS measured on glass. The mapping added here
reproduces sub_D2C98 exactly across its whole input range. The write also
sign-extended bit 6 into bit 7 instead of masking to seven bits as
sub_400330 does, and the mixer control only reached a software PCM scaler
that the DMA path never consulted. Volume and mute now drive the hardware
as Headphones Playback Volume/Switch.

IIS2 folded into s5l8740-i2s. The two ports share the audio clock gate at
CLKCON+0x30 and each wrote it directly, so stopping FM capture idled the
clock under music that was still playing; a per-port wanted flag now
idles it only when neither wants it. IIS2 also gains the pad group it
never had: sub_15DD5C claims GPIO 97/98/119 at function 2 on FM power-on
beside programming audio device 2 and kicking RXCOM, and releases them on
power-off. Those pins had been described as BCM control lines and given
to hci_bcm, which drove the capture bus as GPIOs.

I2C pads are muxed for the first time. Nothing in this port ever did,
which is invisible on a bus the bootloader leaves configured and fatal on
one it does not: i2c1 (PMIC) works while i2c0 (Tristar) reads as noise.
sub_5714EE gives the per-bus pairs and sub_1860 fixes the indices.

FM is named from BlueTool's own FM_RDS_Command definition rather than raw
hex, and gains a read path, RSSI, SNR, RDS group decoding for PS, RT, PI
and PTY, and raw register plus arbitrary-HCI passthrough for userspace.
ROUTE_PCM was only ever read back and never written, leaving the route to
the port IIS2 captures wherever the last owner left it.

Also: the touch download follows the stock tail and no longer interleaves
24 KB of diagnostic reads into the HBPP sequence; SPI2 engine setup has a
single owner again instead of two drivers programming different dividers;
the playback DAI link is nonatomic because its trigger reaches the codec
over SPI; screen sleep, LCD power cycling and PMU rail arbitration land,
with the rail-held mask stopping the global repair from dropping a rail a
driver is using -- the real cause of the white screen.

None of this is hardware-validated yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants