Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework uart0-helloworld-sdboot to use SoC table #211

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

apritzel
Copy link
Contributor

Over time the seemingly simple uart0-helloworld-sdboot program became a bit of a mess: new SoCs tend to mix and match various properties, and people adding support for them needed to find the right group to put them into. This lead to long if (soc_is_xxx() || soc_is_yyy() ...) chains, which are not only hardly readable, but made any changes quite error-prone.

This series reworks the whole design, by adding a SoC table (inspired by soc_info.c), where we hold various values for each SoC: among the SoC ID and its name things like the UART base address, the clock base, and the UART0 pins.
This not only simplifies the whole code, but makes adding support for new SoCs a breeze: as a bonus the last three patches add support for the A83T, the A80 and the A523. And even though this table takes about 1KB of RAM (for all the 23 supported SoCs), the whole binary got actually much smaller:

   text    data     bss     dec     hex filename
   4207       0      24    4231    1087 uart0-helloworld-sdboot.elf (before)
   2441       0      20    2461     99d uart0-helloworld-sdboot.elf (after)

Please review, I split the patches up to help with that process.

The SRAM controller's version register, which we use to identify a SoC,
is mapped at different locations in the different SoC generations. To find
the right MMIO address, we try to read some GICD_IIDR register, which
tells us which SoC generation we are dealing with.

Looking forward, this will need to be more flexible, as the A80 and A523
will not work with this scheme anymore: the A80 uses a completely
different memory map, and the A523 has a GICv3.

Rework the generation detection to be more flexible and easier to extend:
- First read the MIDR, and check for a Cortex-A8 core. This is the early
  generation of SoCs (A10/A10s/A13), which don't have a GIC at all. Use
  the legacy SRAM controller address in this case.
- Next check the address of the GIC in the H6 generation ("NCAT"). Its
  base address is unmapped at all the other SoCs tested, so this is safe.
- Finally check the GIC address of the "legacy" generation of SoCs
  (A20-A64).
- If nothing matches, we are dealing with an unknown SoC, and must stop.

The A80 detection was a nice idea, but didn't work, since the boot
cluster is the one with the A7 cores, so the boot CPU looks like all the
other 32-bit SoCs. Remove that, as the A80 is not supported anyways.

Signed-off-by: Andre Przywara <[email protected]>
At the moment the various SoC specific settings are scattered throughout
the file, using mostly chains of "soc_is_xxx()" macros. While this might
have initially worked with just a few SoCs supported, this is becoming
more and more messy with our currently 22 supported SoCs. Also it is not
easy to support new SoCs, and it looks like some SoCs are already broken
(for instance the A80 is missing the GPIO setup).

The make the code more readable and allow easier addition of future
SoCs, let's add a table, where each entry holds all the various
information we will need to know for each chip: the UART base address,
the clock base address, the clock type, etc. This is conceptually
similar to what sunxi-fel does in soc_info.c.

To make review easier and avoid any regressions, add the table member by
member, and convert the various "subsystems" over one by one.
We start with the most simple table containing the SoC ID and the SoC
name (as a string), and use that already when printing the SoC name.
Since some SoCs share the same SoC ID, add a "flags" member and a in
there a variants bit, and set this accordingly for the four SoCs that
need it.
The soc_detection_init() function gets re-written (and renamed), to
return a pointer into the table, so that the other functions can use the
information there directly.

Signed-off-by: Andre Przywara <[email protected]>
While the actual UART IP has always been the same across all Allwinner
SoC generations so far, the base address changed a few times recently.

Add a field to the SoC to hold that UART0 base address, and fill the
value in for each SoC. This is then used in the uart0_init() function,
so we can remove the explicit setting in the bases_init() function.

Signed-off-by: Andre Przywara <[email protected]>
To make the UART work, we have to open the clock gate and de-assert the
reset for the UART0 peripheral. This is currently done by calling one of
four functions, even though there are actually only two different ways:
- Older SoCs need to set a bit in the APB1/2 bus clock register, and the
  same bit in the respective reset register.
- Newer SoCs combines both the clock and the reset gate in one register,
  so it's only one write, setting two bits.
Add a flag to the table to choose between the two methods for each SoC.
The offsets for these clock registers are very stable, but the CCU base
address changed a bit over time, so split this up and put the base address
separately into the table.

For the latest SoCs, the "new" clock structure is combined with an
updated GPIO IP, and the Allwinner BSP calls this SoC generation
"NCAT2". Add a symbolic name for this already, so we can mark those SoCs
now, and add the respective GPIO flag later when this gets added.

Signed-off-by: Andre Przywara <[email protected]>
To make the UART signal appear on the SoC pins, we have to select two
pins from the supported output pins and set up their pinmux to connect
to the UART peripheral.
As the name suggests, uart0-helloworld-sdboot always uses UART0, which
is used as a debug UART on most boards, and is also available as a
multiplex on the SD card pins, to be used with a breakout board.

At the moment there is a large if/else cascade, with each branch calling
three GPIO setup functions, with the right arguments for each SoC.
While this is easily extendable with copy&paste, it is very repetetive
and duplicates quite some code.

Use our new SoC table to put the pin number for the TX pin and the
required pinmux value in there, alongside the PIO base address. The RX
pin is so far always the pin after the TX pin, so we just need to store
one pin. We also store a flag marking the PIO IP generation for each
SoC, so we can just differentiate this easily in the GPIO setup
functions.
This also uses the opportunity to get rid of the idea to assign some
names to the pinmux values, when they are just seemingly random SoC
specific numbers. Instead just use a macro name with that number in it,
to make it obvious that this is the mux value, but don't try to put some
deeper meaning into them.

On one SoC we decided to use the SD card pinmux, which is always on pins
PF2 and PF4. Add a flag to allow a SoC to default to those pins.

Also remove the unneeded GPIO functions, and mark the rest of them
as static.

Signed-off-by: Andre Przywara <[email protected]>
After we have printed our greeting message, the code tries to figure out
from which media the devices has been booted, and prints that
information. When we were booted via FEL, we return there, so further
code can be uploaded.

To access this boot source information, we need to read from offset 0x28
of SRAM A1. The SRAM base address is different between the SoC
generations, so add this address to the table and use it directly.

Signed-off-by: Andre Przywara <[email protected]>
Now that every SoC specific aspect of our tool is covered by the SoC
table, we don't need the old macros testing for each SoC anymore.

Remove them alongside the no longer needed versions to differentiate
the few SoC variants, which are already covered by a flag in the table.

Signed-off-by: Andre Przywara <[email protected]>
Our uart0-helloworld-sdboot tools lives in a single source file, so we
don't need to export any functions we use. Nevertheless most functions
were in the past not marked as "static".

Mark the one function that hasn't been touched and updated in the last few
patches as "static", to avoid the compiler exporting them, and to help
it with inlining functions.

Signed-off-by: Andre Przywara <[email protected]>
The Allwinner A523 is an SoC with eight Arm Cortex-A55 cores, and some
updated peripherals. The basic IP is very close to other recent SoCs, so
we just have to add the respective flags and base addresses to the new
SoC table, to allow boards with this SoC to run uart0-helloworld-sdboot.

The A523 comes with a GICv3, at a different address, so add this to the
early generation detection code, and let it use the H6 SRAM controller
address for further SoC-ID detection.

Tested on an Avaota A1 board.

Signed-off-by: Andre Przywara <[email protected]>
The Allwinner A83T is an older SoC, with eight Arm Cortex-A7 cores, but
otherwise being very similar to the "classic" SoCs like the A20.

Add support for this SoC, by simply adding its SoC ID and the respective
SoC data into our SoC table.

Tested on a Cubietruck Plus.

Signed-off-by: Andre Przywara <[email protected]>
The Allwinner A80 is quite an oddball: it uses a big.LITTLE configuration
with Arm Cortex-A7 and A15 cores, and has a completely different memory
map compared to all the other Allwinner SoCs.

Add support for this SoC, by adding the respective base addresses for its
peripherals and the pinmux into our SoC table.
It also requires a small extension to the SoC detection routine, to
accommodate for the different GIC mapping.

Tested on an Cubieboard 4 (CC-A80).

Signed-off-by: Andre Przywara <[email protected]>
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.

1 participant