ESWIN's own evaluation board for this SoC. Where the StarPro64 is a
single board computer built around the chip, the EVB brings out most of
the SoC's interfaces.
Everything shared with the StarPro64 is already in the common directory,
so this carries the board's own facts: which UART reaches which connector,
which pads carry the boot straps, where its memory sits, and a
configuration starting from the same place the StarPro64's does.
The summary tables on sheet 3 of both boards' schematics are inherited
from ESWIN's reference design and describe that design rather than either
board. On this board the console is UART0 through the FT4232 bridge,
UART1 goes to the M.2 socket and a header, and UART2 reaches the RS232
port.
The documentation page follows the board template: a photograph, the
board's features, the console and its port on the FT4232 bridge, power,
the build and TFTP boot procedure, and what NuttX drives so far.
Boots to an NSH prompt over UART0 with all four harts running.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
The port had one board directory holding one board, with everything in it
whether it described the SoC or the PCB. A second EIC7700X board follows,
so this adopts the common-plus-board layout NuttX provides, as mpfs uses.
boards/risc-v/eic7700x/common holds what is true of the SoC: the boot path
that mounts the RAM disk and /proc before calling the board's own bring
up, the linker script, the start up scripts and the image builder.
ARCH_CHIP_EIC7700X selects ARCH_BOARD_COMMON, so the symlinks the build
makes always point at code that compiles.
The board directory keeps what is a fact about the PCB: its own board.h
and board_memorymap.h, since the include fallback is all or nothing, a
bring up that owns the order its devices register in, and a board_config.h
declaring what that bring up may call.
The image builder moves to common/tools and derives its output name from
the configuration. It computes the padding between the kernel and the RAM
disk from _ebss rather than assuming 64 KiB, which fails once BSS grows
past it: the disk lands below _ebss and the BSS clear zeroes it before
anything searches for it.
The StarPro64 configuration gains what the port now needs: four harts,
960 MiB of RAM, a larger task stack, a backtrace on assert, the system log
in RAM for dmesg, and ELF applications, for which ARCH_CHIP_EIC7700X now
selects ARCH_HAVE_ELF_EXECUTABLE.
board.h loses its LED definitions. CONFIG_ARCH_LEDS is not set, nothing
implements board_autoled_on(), and the indices they gave named no LED.
The documentation pages gain the tags the template asks for.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
Three faults in how the kernel maps itself.
The page pool covered the RAM disk, which the boot loader appends above
the kernel, so the BSS clear destroyed it before anything searched for it.
The pool now covers only pgram, and the RAM disk is mapped on its own
account.
The kernel data region is mapped with 2 MiB pages rather than 4 KiB ones.
Everything in it carries the same permissions, so the finer granularity
bought nothing while costing one L3 slab per 2 MiB from a pool of two.
The linker script and Kconfig describe the page pool separately and both
descriptions are used, so they now agree. When they disagree, pages
outside the smaller of the two get a virtual address of zero and are
written through, which on this SoC lands on identity mapped low memory
rather than faulting.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
External interrupts were enabled and claimed in the context of whichever
hart the firmware handed over on, which is not fixed. They reset routed
to wherever the boot loader left them, and an interrupt delivered to a
context nobody services cannot be told from a device that never raised
one.
Enable and claim in CPU0's S mode context, which is hart 0's. Steering a
source at another hart would mean choosing which, and NuttX has no way for
a driver to say.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
The port ran on whichever hart the firmware handed over on, with a single
idle stack. The firmware does not pick the same hart every boot, so
nothing may assume one.
The hart that arrives first records which it was, indexes its idle stack
by its own ID, and restarts on hart 0; NuttX runs CPU0 there because that
is the only hart riscv_set_inital_sp() gives a whole idle stack to. The
first hart in owns the one time setup, and that guard lives in .data
because it is read before the BSS is cleared.
CONFIG_SMP_NCPUS below four is refused in chip.h: the hart the firmware
picked would have no slot and would stop in __start before it could
restart, which fails on some resets and not others.
Secondary harts are released through the common SBI helper rather than a
private copy of the same call.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
The pins a board publishes are visible in /dev and each can be read
through its own node, but surveying a whole board that way means an open
and two ioctls per pin, and the signal and interrupt counters the upper
half keeps are not reachable through any of them.
Adds a list of registered pins and publishes it as /proc/gpio, behind
GPIO_PROCFS: a quality of life view of the same kind as /proc/pinctrl
and /proc/reset. Every common field comes from state the upper half
already holds: the pin type, the value through go_read(), how many times
the pin has been registered for signals, and how many interrupts it has
taken. Lines carry the same key:value tokens in the same order, so the
file is machine parseable.
Lower halves may supply an optional go_describe() adding what only they
can say, such as which pad carries the line or how its trigger is
configured. It writes text into a caller supplied buffer and the upper
half owns the line, so a lower half needs no procfs knowledge. A lower
half without it is listed with the common tokens alone.
The pin type index is bounded before use: it comes from the lower half
and the name table cannot cover a type the enumeration does not define.
A pin that cannot be read reports val:- rather than a zero that would
read as a real level.
procfs_register() appends without checking for duplicates, so the entry
is claimed once for the lifetime of the system rather than whenever the
list is empty; pins come and go at run time.
The name is held in a buffer as long as the one gpio_pin_register()
accepts, so a listing always names the same string as /dev.
The pin type name table is declared without an explicit size so that the
assertion beside it compares against the enumeration and can fail; sized
as [GPIO_NPINTYPES] it would have been tautological.
Documents the entry, its tokens, and how a lower half describes a pin.
Off by default: with GPIO_PROCFS unset the list, the lock and the procfs
entry are compiled out, and go_describe() is one more member at the end
of a structure existing lower halves do not reach.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
Add a defconfig that runs CPython on qemu-intel64 (flat build) and
place the .PyRuntime section (created by the apps CPython port) in
.data so it is covered by the kernel physical mapping.
Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
The TMP112 driver was character mode only, and carried the warning that
says so: a read returns a bare float, at a size the driver chose, and
nothing but code written for this one part can make sense of it.
Add the sensor framework version beside it, in the shape the tree
already uses for a part that has both. The old driver is untouched and
still builds by default; the new one replaces it when
SENSORS_TMP112_UORB is set, and the part then appears as a temperature
topic that the common sensor tools can read without knowing what a
TMP112 is.
It reads on the low priority work queue at whatever interval the caller
asks for. The part converts continuously out of reset, so nothing is
configured and the temperature register always holds the last completed
conversion: a reading is one bus transaction with nothing to wait for.
Reading faster than the part converts repeats a value, which costs bus
traffic and nothing else, so the interval is taken as given: the upper
half treats a lower half that hands back a longer interval than it was
given as a failed request, so clamping here would refuse a fast caller
rather than serve it slowly.
get_info reports what the part is and what its readings mean, so a
consumer need not know it is talking to a TMP112 to know the range and
the resolution.
It also sign extends the reading. The register holds twelve bits, and
the character mode driver treats them as unsigned, so anything below
freezing comes back as a large positive temperature; the part is
specified down to -40C. Fixing that in the old driver would change what
existing callers see, so it is fixed here, where there are no callers
yet to surprise.
This driver also covers the TMP102, which differs in accuracy rather
than in its registers: only the two both parts have are touched.
Documented under the sensors section, beside the other parts with a page
of their own, and listed among the uORB drivers.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
A driver author had to read the core to learn what a frequency table may
contain: the rules live in devfreq_table_validate() and in the four other
places that skip DEVFREQ_ENTRY_INVALID. State them where the special
values are defined instead. A table ascends, DEVFREQ_ENTRY_END terminates
it, DEVFREQ_ENTRY_INVALID stands in for a frequency the device cannot use
and is skipped by every consumer, and devfreq_register() rejects a table
whose usable entries do not ascend.
Say on get_table that it is called once, by devfreq_register(), which
keeps the returned table for the life of the device. That is a real
constraint on the caller, since the table cannot be on its stack, and
consumers outside the core retain the same pointer.
Comments only, no functional change.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
The dummy driver's frequency cooling device is now a devfreq one, so the
configuration that tests it needs devfreq built. This is a change in what
the configuration covers, not just a symbol rename: nothing in the tree
registered a devfreq device before, and this is now the one place the
thermal to devfreq path runs without hardware.
Walking the dummy zone from 45 to 90 degrees steps the device through
every entry of its table, 900 down to 100, and back up as it cools.
The documentation follows the same rename, and devfreq's own page now says
that thermal is a requester and that a driver expecting to be throttled
wants DEVFREQ_CONFLICT_PREFER_LOW.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
The cooling device renamed in the previous commit was written against a
cpufreq framework that never landed. It includes nuttx/cpufreq.h, which
does not exist, and THERMAL_CDEV_CPUFREQ depends on CPUFREQ, which no
Kconfig in the tree defines, so it has never been selectable and has never
been compiled. The cpufreq half of the dummy driver is orphaned the same
way behind THERMAL_DUMMY_CPUFREQ. Upstream noticed once already and
dropped cpufreq from the sim thermal configuration in 898a5d501f.
devfreq does the same job and is here. It carries the frequency table,
arbitrates windows through QoS, and its DEVFREQ_CONFLICT_PREFER_LOW is
documented as the policy for a device protecting a thermal budget, which
is exactly a cooling device's claim on it. Point the cooling device at
that instead, and the thermal framework can throttle again.
Two things change beyond the API. The cooling state now names a ceiling
rather than a two entry window, because devfreq resolves a conflicting
floor in the ceiling's favour, and that makes the whole table reachable:
max_state is one less than the number of usable entries, state zero leaves
the top entry available and the highest state holds the device at the
bottom one. A DEVFREQ_ENTRY_INVALID entry is a hole the driver has
punched and cannot be installed as a ceiling, so it earns no cooling
state; counting it would both advertise a state the device cannot deliver
and, on reaching it, install a ceiling of ~0u, which caps nothing. And the device is found by name, since devfreq is multi instance
where a cpufreq policy was singular, so THERMAL_CDEV_DEVFREQ_NAME says
which one to cool and what to call the cooling device in a zone's map.
The dummy driver gains a devfreq lower half in place of its cpufreq one,
which gives the tree its first devfreq consumer and makes the whole path
testable without hardware. On sim, walking the dummy zone from 45 to 90
degrees:
temp cooling state frequency
60 0 900
62 1 700
61 2 500
72 3 300
74 4 100
and back down again as it cools.
Also fixes two faults the file could not previously reveal: it called
therr and thinfo without including nuttx/debug.h, and it reached the
driver by casting the policy pointer, which worked only while driver was
the first member.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
This is the rename on its own, with no change to the contents, so that the
port that follows is readable as a diff against the file it came from rather
than as a deletion and a new file.
The reason for the move is in the next commit.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
Nothing in the controller driver is PCI-specific beyond finding the
registers and the interrupt, so an SoC wiring an xHCI controller
directly could not use any of it.
usbhost_xhci.c is now the driver; usbhost_xhci_pci.c is the PCI
attachment, holding the ID table, the BAR mapping and the MSI-X vector.
include/nuttx/usb/xhci.h carries what passes between them: the register
base, a way to attach the interrupt, and a name to report the controller
by, since a system may have more than one.
The interrupt belongs to the bus: the bus attaches and detaches it, and
the driver never holds an interrupt number.
USBHOST_XHCI is the driver and is not selectable on its own;
USBHOST_XHCI_PCI selects it. Another bus adds its own symbol beside it.
No functional change intended.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
Pure rename, so that the history of the driver follows it into the file
that keeps the bulk of the code. The PCI attachment is added back as a
new usbhost_xhci_pci.c in the commit that follows.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
The NXP PCF8563 is a battery backed I2C clock at the fixed address 0x51,
common on RISC-V and ARM boards, and NuttX had no driver for it. The
nearest part in the tree, the PCF85263, has a different register map.
The driver follows the shape of the external I2C clocks already here,
ds3231 and pcf85263: board logic calls pcf8563_rtc_initialize() once the
bus exists, and the chip then answers up_rtc_getdatetime() and
up_rtc_settime() for the system clock.
Three behaviours are deliberate.
The top bit of the seconds register means the oscillator has stopped since
the time was last set, so every register after it holds whatever it stopped
on. A read in that state returns -ENODATA rather than the contents: a
caller told the time is unknown can act on that, one told a wrong time
cannot. A flat backup cell and a clock that has never been set both arrive
here.
The top bit of the month register marks a century rollover, but which value
means which century is a convention that parts disagree on, so no century
is read from it. The chip is treated as a clock for 2000 to 2099, the bit
is written back as it was read, and a date outside those years is refused
rather than stored as one that would read back different. This is what the
mainline Linux driver does.
The counters are stopped across a write so a carry cannot land between the
seconds and the minutes, and restarted even when the write failed, since
the alternative is leaving the clock stopped.
The alarm and countdown timer are not implemented; their registers are
defined.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
The regulator framework has no way out to userspace: consumers reach a
rail by name from inside the kernel, which is the right interface for
controlling one, but it leaves a board with regulators offering no way
to see what they are doing, and a newly written regulator driver cannot
be looked at without writing a consumer for it first.
Adds /proc/regulator, behind REGULATOR_PROCFS, listing every registered
regulator: its present voltage, the range it will accept, whether it is
enabled, how many consumers hold and enable it, its supply, and whether
it is always on or expected on at boot. Lines carry the same key:value
tokens in the same order, so the file is machine parseable. The last
two are worth reading beside the consumer count, since a rail enabled
with no consumers is expected rather than suspect when either is set.
A part usually measures more than the framework has fields for, so
struct regulator_ops_s gains an optional describe method: it writes
key:value text and the renderer appends it to that rail's line. This is
how a driver reports what only it knows, an input voltage, an output
current, a temperature or a fault word, without the framework growing a
field per part or the driver growing procfs code of its own. It is
called with the list mutex held and never from interrupt context, so
reading the part over a bus is allowed.
The voltage and the enabled state are read back from the hardware rather
than recalled, so a rail the boot loader set and nothing has touched
since reads as it actually is. Both calls can fail, and a failure
reports - rather than an errno formatted as a voltage or a rail that
looks switched on.
Reading the hardware is also why this takes the list mutex directly
rather than calling regulator_list_lock(), which additionally disables
interrupts so that callers in interrupt or idle context are safe.
Asking a regulator on a bus what it is doing means a transfer, and a
transfer waits; a task reading a file can afford to wait and an
interrupt handler cannot.
procfs_register() appends without checking for duplicates, so the entry
is claimed once for the lifetime of the system rather than whenever the
list is empty. It also needs FS_PROCFS_REGISTER, which the option now
depends on rather than only FS_PROCFS.
Documents the framework, which had no page at all: the consumer
interface and what counted enables mean, what a driver supplies, and the
new entry.
The entry is read only. What voltage a rail may be is knowledge its
consumers hold, and arranging the order between them is what the
framework is for, so moving one from a shell would step around the part
that matters.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
Supports the UNIX setuid-on-exec sudo helper. Documents the model,
generates an extra ROMFS user and /etc/sudoers for a non-root test,
reports BINFS modes from the builtin table so ls -l matches execute
bits, and skips NULL environment entries when sanitizing a setuid exec.
Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
This can be used on small systems to save RAM if the SHM object
doesn't need to be cache-aligned.
Signed-off-by: Jukka Laitinen <jukka.laitinen@tii.ae>
Two defects in the CONFIG_TICKET_SPINLOCK paths of spinlock.h:
1. spin_trylock_notrace() passed &lock->owner as the "expected" pointer
of atomic_cmpxchg(). A failed compare-exchange writes the current
value of the target object back through that pointer, so a losing
trylock stores lock->next into lock->owner. owner then equals next,
which is the unlocked state: a lock still held by another CPU reports
itself as free, spin_is_locked() returns false and the lock can be
taken again. Every later unlock keeps incrementing owner past next,
so the ticket of a real waiter never matches and the lock stays
locked forever. Keep the expected value in a local variable.
2. spin_unlock() was wrapped in #ifdef __SP_UNLOCK_FUNCTION, a macro
that is never defined anywhere in the tree. The function body was
therefore dead code and spin_unlock() always expanded to
"do { *(l) = SP_UNLOCKED; } while (0)", which zeroes both ticket
counters instead of releasing one ticket with
atomic_fetch_add(&lock->owner, 1). That drops queued waiters, lets a
newcomer draw ticket 0 and enter the critical section, and also skips
the UP_DMB/UP_DSB/UP_SEV release barriers and the
sched_note_spinlock_unlock() note. Drop the dead #ifdef so
spin_unlock() is always the function.
Both were reproduced on qemu-armv7a:smp (cortex-a7 x4) with
CONFIG_TICKET_SPINLOCK=y, where the compare-exchange lowers to native
ldrex/strex. This confirms the root cause is the C-level aliasing of
the expected pointer, not the atomic implementation.
Refs: https://github.com/apache/nuttx/issues/19808
Signed-off-by: hujun5 <hujun5@xiaomi.com>
The SD path never performs the CMD6 switch its eMMC counterpart has
performed for years, so an SD card is left in default speed and every
host clocked accordingly, at 25MHz rather than the 50MHz the card
supports. A TODO in this file has asked for it since 2010; this removes
it.
A host asks for the switch by reporting SDIO_CAPS_SD_HS_MODE, which
mirrors the eMMC capability beside it. The switch is attempted once the
bus is at the default transfer rate and the wide bus is selected, and
the card's own answer decides the outcome: the 64 byte status block
reports the function actually selected, and a card that cannot do what
was asked says so there rather than failing the command. Cards below
version 1.10 of the physical layer specification are not asked, since
CMD6 postdates them.
Only a confirmed switch reaches the host, as the new
CLOCK_SD_TRANSFER_4BIT_HS rate. That is a rate rather than a flag on an
existing one because the host is clocked twice during initialization,
once before the switch can have happened, and a host that cannot tell
the two apart would run a card in default speed past its rated 25MHz.
The enumerator is added last, so no existing driver's switch statement
changes meaning, and the rate reaches only a host that reported the new
capability, which none in tree does.
Every failure path is survivable: a card that declines, a card too old
to ask, and a host that never asks all stay at the default rate.
Documents the two capabilities and the clock rates a lower half has to
handle.
The MMC/SD documentation was three sentences and a pointer to the SDIO
page, so it said nothing about how a card is registered, how the bus width
and clock are negotiated, or what any of the configuration options do. It
now covers those, the ioctl interface and /proc/mmcsd, and the high speed
switch this commit adds is described where somebody looking for it would
look rather than only in the SDIO lower half page.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
The SysTick timer logic of stm32h5, stm32h7, stm32l4, stm32l5, stm32u5
and stm32wl5 tests a family specific SYSTICK_HCLKd8 symbol, while
common/stm32 and stm32wb already test CONFIG_STM32_SYSTICK_HCLKd8. Use
the common name everywhere.
No Kconfig defines the symbol and the timer logic undefines it
unconditionally, so this does not change the generated code.
Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
The flash header documents CONFIG_STM32WB_FLASH_CONFIG_x and the RCC
tests CONFIG_STM32WB_AES1/AES2, which no Kconfig defines. Use the
common CONFIG_STM32_* names.
Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
CONFIG_STM32WL5_FLASHEN is not defined by any Kconfig, so the FLASH clock
enable is dead code. Use CONFIG_STM32_FLASH.
Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
CONFIG_STM32U5_I2C3EN, SDMMC1EN, SDMMC2EN, the STM32U5xxXX family names
and STM32U5A5ZJT are not defined by any Kconfig, so the RCC clock enables
and the STM32U5A5ZJT block in chip.h are dead code. Use the common
CONFIG_STM32_* symbols and CONFIG_ARCH_CHIP_STM32U5A5ZJT.
Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
CONFIG_STM32L5_FLASHEN, OTFDEC1EN, PKAEN, SDMMC1EN, WWDGEN and GTZCEN
are not defined by any Kconfig, so the RCC clock enables are dead code.
Use the common CONFIG_STM32_* peripheral symbols.
Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
CONFIG_STM32L4_OTGFS_SOFOUTPUT is not defined by any Kconfig, so the SOF
output pin is never enabled. Use CONFIG_STM32_OTG_SOFOUTPUT.
Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
CONFIG_STM32H7_LCD_BACKLIGHT is not defined by any Kconfig, so the LTDC
backlight control is dead code. Use CONFIG_STM32_LTDC_BACKLIGHT.
Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
The headers test CONFIG_STM32H5_* names that no Kconfig defines (the
STM32H5xXXX families, FLASHEN, OTFDEC1EN, DCACHE, STM32H2X/H3X/H7X), so
those branches are dead code. Use the common CONFIG_STM32_* symbols.
Fix what this exposes: derive the SRAM2/SRAM3 bases from the family SRAM
sizes, since the boot ECC init would otherwise write past the end of RAM
on smaller parts, correct the H52x/H53x SPI count to SPI1-4 and drop the
STM32_STM32H5X3XX select, which names no symbol.
Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
CONFIG_STM32F7_LCD_BACKLIGHT is not defined by any Kconfig, so the LTDC
backlight control is dead code. CONFIG_STM32F7_PLLSAI and
CONFIG_STM32F7_PLLI2S are defined by the board.h files, and the common
SPI test helper names its mode macros CONFIG_STM32F7_SPIx_TEST_MODE.
Use the common CONFIG_STM32_* names everywhere.
Also drop the misspelled CONFIG_STM32F7_STM33F75XX from the DMA chip
check, which already tests CONFIG_STM32_STM32F75XX.
The CAN section of the STM32F7 documentation names the options
CONFIG_STM32F7F7_CANx, which has a duplicated family prefix and never
existed. Use the common names there too.
Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
xhci_command() returned -ETIMEDOUT when a completion arrived without an
interrupt, although the fallback poll had already retrieved the event,
so callers unwound work the controller had completed.
Use the event's completion code whichever path retrieved it.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
Eight PORTSC bits are write-one-to-clear, so writing back a value just
read clears PED and every change bit that was set, disabling the port
being reset. Mask them out using the new XHCI_PORTSC_RW1C.
The wait after reset also decided on its own counter rather than on the
port, reporting a timeout for a port that enabled on the last iteration.
Test PED, and report PORTSC when it does time out.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
xhci_ctrl_halt() wrote USBCMD zero unconditionally and then waited for
HCH. A controller that was never started is already halted, so the wait
ran to its full length, and clearing the whole register also dropped
INTE and HSEE.
Test HCH first, clear only R/S when it is set, bound the wait with
XHCI_HALT_TIMEOUT_MS, and report USBCMD and USBSTS on failure.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
HCSPARAMS2 may report zero scratchpad buffers; QEMU's does. The driver
sized the array from that count unconditionally and read the NULL from a
zero byte kmm_memalign() as -ENOMEM, so such a controller never started.
Skip the allocation when no_scratch is zero, leaving DCBAA[0] clear.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
xHCI requires aligned accesses of each register's own size, and a
controller may ignore narrower ones; QEMU's does. volatile does not pin
the access width: GCC 16.1.0 at -Os narrows a 32-bit load feeding a
single bit test into a byte load, so polling USBSTS for HCH never
observes the halted state.
Launder each register value through a register with an empty asm, on
loads and stores both, so the access is the width the source specifies.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
Wire the shared Ameba PWM driver (arch/arm/src/common/ameba/ameba_pwm.c)
to RTL8721F (amebagreen2). The chip spreads PWM across four four-channel
timers (TIM4..TIM7); this port drives TIM4 as the single time base with
four compare channels, matching the shared driver's model. A new
ameba_pwm_chip.h supplies the RTL8721F specifics taken from the SDK
fwlib headers: TIM4 at the non-secure base 0x41000000, 40 MHz input
clock, IRQ 11 (TIMER4_IRQ), crossbar pad-mux codes 111..114
(PINMUX_FUNCTION_TIM4_PWM0..3) and the distinct function/clock enable
bits (APBPeriph_PWM0 / APBPeriph_PWM0_CLOCK).
The board registers one timer at /dev/pwm0 with channel 1 on PB18 and
channel 2 on PB19 for the pwm example; edit the table to match a board's
wiring. The common driver is not touched.
Signed-off-by: dechao_gong <dechao_gong@realsil.com.cn>
Assisted-by: Claude <noreply@anthropic.com>
Wire the shared Ameba PWM driver (arch/arm/src/common/ameba/ameba_pwm.c)
to RTL8720F. The chip spreads PWM across several four-channel timers
(TIM4/TIM5); this port drives TIM4 as the single time base with four
compare channels, matching the shared driver's model. A new
ameba_pwm_chip.h supplies the RTL8720F specifics taken from the SDK
fwlib headers: TIM4 at the non-secure base 0x401c7000, 40 MHz input
clock, IRQ 9 (TIMER4_IRQ), crossbar pad-mux codes 45..48
(PINMUX_FUNCTION_TIM4_PWM0..3) and the distinct function/clock enable
bits (APBPeriph_PWM0 / APBPeriph_PWM0_CLOCK).
The board registers one timer at /dev/pwm0 with channel 1 on PB18 and
channel 2 on PB19 for the pwm example; edit the table to match a board's
wiring. The common driver is not touched.
Signed-off-by: dechao_gong <dechao_gong@realsil.com.cn>
Assisted-by: Claude <noreply@anthropic.com>
Add a shared NuttX PWM lower-half for the Realtek Ameba PWM timer in
arch/arm/src/common/ameba, driven through the SDK fwlib. TIM8 provides a
single time base feeding eight compare channels (CCR0..CCR7) that share one
frequency while each carries its own duty, so a single /dev/pwm0 exposes the
multichannel output via CONFIG_PWM_NCHANNELS. The fwlib PWM routines are
split ROM/RAM: the time-base calls resolve from on-chip ROM, while the
capture/compare calls live in fwlib ram_common/ameba_tim.c, which the build
pulls into AMEBA_FWLIB_SRCS when CONFIG_AMEBA_PWM is set.
Per-chip wiring (timer index, channel count, register base, input clock,
IRQ, clock masks and the crossbar pad-mux code table) lives in
arch/arm/src/rtl8721dx/ameba_pwm_chip.h so a port to another Ameba chip only
supplies a same-named header; the pad-mux codes are a per-channel table
(AMEBA_PWM_PINMUX_FIDS) rather than a computed base, so chips with a single
shared code or codes grouped per timer are expressed by the header alone.
The timer registers as /dev/pwm0 from pke8721daf bring-up through the stock
PWM character driver; a dedicated `pwm` defconfig drives examples/pwm for
validation.
Signed-off-by: dechao_gong <dechao_gong@realsil.com.cn>
Assisted-by: Claude <noreply@anthropic.com>
memcmp, strncmp and strcmp reach their word loops only when both pointers
are already on a register boundary:
or t0, a0, a1
andi t0, t0, SZREG-1
That asks more than the loops need. They load from the two pointers at
the same boundary, so what matters is that the two agree about where a
boundary falls, not that either is already on one. A pair offset by the
same amount can be walked up to the boundary a byte at a time and
compared a register at a time from there.
The union also holds far less often than the difference. For arbitrary
pointers on RV64 it is true about one time in 64 against one in eight,
and the case it rejects, two strings carved out of the same buffer, is
the common one.
Test the difference of the pointers, and walk to the boundary first.
arch_strcpy.S and arch_memcpy.S already do this. Keeping every access
aligned is not only faster here: the base ISA does not require misaligned
loads and stores to be supported at all, so a routine in a machine
directory cannot assume one will work, whatever it costs.
Measured on a 1.4 GHz rv64, source and destination misaligned by one:
before after
memcmp 32K 34.4 458.0 MB/s
strncmp 32K 32.4 253.0 MB/s
strcmp 32K 41.0 280.0 MB/s
Each of those was the rate of the byte loop the word loop was meant to
replace. Pointers that genuinely disagree still take the byte loop, and
the aligned rates are unchanged.
The measurements come from the benchmark in apache/nuttx-apps#3706.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
The word loop walks src to a register boundary and then stores a whole
register at a time to dst, but nothing establishes that dst is on a
boundary too. Where the two pointers disagree about where a boundary
falls, every store in that loop is misaligned.
The base ISA does not require misaligned stores to be supported. Where
firmware emulates them each store traps into machine mode, and where
nothing emulates them the store faults, so this is not only a question of
speed. Measured on a 1.4 GHz rv64 that emulates them, with a 32 KB
string whose src and dst are misaligned by different amounts:
generic C 410.4 MB/s
this file 7.5 MB/s
which is around 178 cycles per byte, flat from 512 bytes to 32 KB.
Test the two pointers against each other before going wide, as
arch_strcpy.S already does. Pointers that agree still reach the word
loop, since walking src to a boundary walks dst to one as well; pointers
that disagree take the byte path, where no single boundary serves both.
After the change the misaligned case runs at 490 MB/s and the aligned
rates are unchanged.
The measurements come from the benchmark in apache/nuttx-apps#3706.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
The Triggers section previously stated that action (property) triggers
were not yet implemented; they are supported now, so describe the
fnmatch value matching and the edge-triggered semantics of on <event>.
The Examples section used the stale CONFIG_SYSTEM_INIT_* prefix and now
uses CONFIG_SYSTEM_NXINIT_* together with a property trigger example.
This documents the behavior added in
https://github.com/apache/nuttx-apps/pull/3726.
Assisted-by: GitHubCopilot:claude-opus-4.8
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
libc_data_t is 8 bytes wide, so a buffer which is 4-byte but not
8-byte aligned falls back to the byte at a time loop. Add a 32-bit
middle path so such buffers still handle four bytes per iteration.
* Add DETECTNULL32/DETECTCHAR32, UNALIGNED4/UNALIGNED4_X,
LITTLEBLOCKSIZE4/BIGBLOCKSIZE4 and TOO_SMALL4 to libs/libc/libc.h.
* Take the new path in memccpy, memcmp, memcpy, memset, stpcpy,
stpncpy, strcmp, strcpy, strncmp and strncpy when both pointers are
4-byte aligned but the 8-byte path can't be used.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
When the 'c' parameter has bit 7 set (e.g. 0x80), the int value gets
sign extended (to 0xffffff80 on the signed char platforms). The word
sized fill pattern was built without truncating to unsigned char
first, so the fast word aligned path wrote the wrong bytes.
Fix both lib_memset.c and lib_bsdmemset.c by casting 'c' to unsigned
char before building the fill pattern, as required by C11 7.24.6.1
which states that memset converts 'c' to unsigned char.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Bowen Wang <wangbowen6@xiaomi.com>
memrchr scans backward, so the original implementation aligned
(x + 1) rather than x:
#define UNALIGNED(x) ((long)(uintptr_t)((x) + 1) & (sizeof(long) - 1))
while the common UNALIGNED_X() macro checks the pointer itself. Pass
src0 + 1 to UNALIGNED_X() to restore the original behavior, otherwise
asrc is off by one byte and the word loop reads the wrong data.
Assisted-by: Claude:claude-opus-5
Signed-off-by: anjiahao <anjiahao@xiaomi.com>
Remove the incorrect address restoration logic in the memrchr fast
path. The UNALIGNED_X loop already ensures the proper alignment, so
the subsequent address recalculation is unnecessary and makes memrchr
return the wrong position.
This fixes the syslog message corruption where memrchr reports the
incorrect newline position.
Assisted-by: Claude:claude-opus-5
Signed-off-by: fangpeina <fangpeina@xiaomi.com>