satip-axe/kernel/Documentation/stm/pad-manager.txt

709 lines
25 KiB
Plaintext

STMicroelectronics platforms pad management
===========================================
This document tries to explain rationale behind and ways of using
pad management API designed for STMicroelectronics platforms.
All the related functions and macros are declared in
"include/linux/stm/pad.h" and implemented in "drivers/stm/pad.c" file.
Table of contents
=================
1. What is it for?
2. Platform devices definition
2.1. PIOs
2.2. sysconfig bits
2.3. Custom calls
2.4. Modifying configurations in runtime
2.5. Building configurations in runtime
3. Driver API
4. GPIO driver relationship
1. What is it for?
==================
Most of the peripheral controller IPs, like UART (aka ASC), I2C cell (SSC),
Ethernet MAC etc., are connected to the external components (connectors,
other ICs...) via chip's pins (or rather "balls" in case of BGA packaging),
known in the "hardware speech" as pads.
Historically some of the pads were dedicated for the particular use
(especially MII pads in older SOCs) but nowadays more and more of the
functions are multiplexed with generic PIOs (and other functions).
When a PIO pad is used in a non-PIO context (eg. TXD/RXD signals driven
directly by the UART), it was traditionally described as an "alternative
function" for the PIO and the driver was supposed to use PIO API to set
this mode (basing on "port/pin" data provided via platform data).
With a rising SOCs complexity came need of assigning more then one
"alternative function" to each of the PIOs, so, for example, a given pad
can now act as a generic PIO, UART's TXD line or I2C SDA signal, depending
on the board design.
As the PIO bank design didn't provide support for more then one option
(and itself was considered as "immutable") additional multiplexers had
to be introduced, controlled by a loosely defined collections of bits
here and there, commonly known as "sysconfigs". Effectively every single
chips family has its own pads configuration scheme, usually very different
to any other.
As the PIO API also wasn't prepared to take additional responsibilities
the "solution" was to leave the PIO configuration in driver, while
the "sysconfig" bits were configured in chip-specific configuration
("BSP" as it is sometimes called). Obviously it was not a perfect solution
(word "hack" seems to be more suitable) and led to a number of problems.
Finally major changes in kernel allowed to rework all this model and
introduced "pad management" API, which role is to separate all the
PIO and other "magic bits" configuration from the IP (as in UART etc.)
driver implementation and provide it a nicely abstracted function which
can be described as:
"Please, do everything what is required so I could talk to a I2C device
potentially connected to SSC controller I am supposed to drive.
I don't care what PIOs are used, I don't want to know what sysconfig
bits should be set. I just want to put some data into SSC's FIFO and
expect the SDA and SCL lines to be changing from low to high level
and back when the SSC decides to do so."
2. Platform devices definition
==============================
All the elements involved in configuring pads in some particular mode
are encapsulated in "stm_pad_config" structure:
--------8<--------8<--------8<--------
struct stm_pad_config {
int gpios_num;
struct stm_pad_gpio *gpios;
int sysconfs_num;
struct stm_pad_sysconf *sysconfs;
int (*custom_claim)(struct stm_pad_state *state, void *priv);
void (*custom_release)(struct stm_pad_state *state, void *priv);
void *custom_priv;
};
--------8<--------8<--------8<--------
An example configuration could look like this:
--------8<--------8<--------8<--------
static struct stm_pad_config fli7510_usb_pad_config = {
.gpios_num = 2,
.gpios = (struct stm_pad_gpio []) {
STM_PAD_PIO_IN(27, 1, -1), /* USB_A_OVRCUR */
STM_PAD_PIO_OUT(27, 2, 1), /* USB_A_PWREN */
},
.sysconfs_num = 2,
.sysconfs = (struct stm_pad_sysconf []) {
/* usba_enable_pad_override */
STM_PAD_SYSCONF(CFG_COMMS_CONFIG_1, 12, 12, 0),
/* usba_ovrcur_polarity */
STM_PAD_SYSCONF(CFG_COMMS_CONFIG_1, 11, 11, 0),
},
.custom_claim = fli7510_usb_xtal_claim,
.custom_release = fli7510_usb_xtal_release,
}
--------8<--------8<--------8<--------
It says that in order to get USB host controller running correctly,
PIO27.1 should be configured as input, PIO27.2 as an "alternative function 1"
output, two sysconfig bits (CFG_COMMS_CONFIG_1.12 and .11) should be set
to 0 and additionally a special function "fli7510_usb_xtal_claim" called.
All used macros are described below.
2.1. PIOs
=========
Traditionally PIO "pins" in ST chips are grouped in "ports", 8 pins each.
Number of available ports, their alternative functions mapping and
configuration scheme differ a lot between SOCs. Therefore the SOC
initialization code is obliged to call pad manager's initialization
function before performing any pad/PIO related operation:
--------8<--------8<--------8<--------
int stm_pad_init(int gpios_num, int gpio_function,
int (*gpio_config)(unsigned gpio,
enum stm_pad_gpio_direction direction,
int function, void *priv));
--------8<--------8<--------8<--------
"gpios_num" parameter simply defines number of available PIO pins.
Effectively it is:
gpios_num = (max(PIO_ports) + 1) * 8
where max(PIO_ports) is the highest number of available PIO ports. Note
that it is _not_ just a number of available ports, as some SOCs (STx7141
and FLi7540 at the time of writing) "lack" a few ports at the beginning.
The first available port in STx7141 is 1 and in case FLi7540 - 5 (sic!).
And in such cases the code requires an explicit "gap" at the beginning
of all related structures.
The "gpio_config" is a callback function pointer that will be called in
order to configure any given PIO pin in one of three possible directions
(input, output or bi-directional) or custom mode (SOC and implementation
specific) and into a particular function.
The "function" numbering is completely SOC-dependent, as is both defined
(in the "stm_pad_config" structures) and used (in the "gpio_config"
callback) by the SOC setup code. The value that should be used to
configure a pin in generic PIO mode (so its value can be read using
"gpio_get_value()" or set using "gpio_set_value()") should be passed
to pad manager core as the "gpio_function" parameter of the above
initialization function.
The pad configuration as described in platform code and pass should
describe every PIO used by a particular IP as an array of "struct
stm_pad_gpio" pointed by field ".gpios" of "stm_pad_config" structure.
For example, in case of UART these are usually RXD, TXD, CTS and RTS
signals while for SSC in I2C mode these should be SCL and SDA lines.
To make the configurations easier to write (and read), the "pad.h" header
provides a family of helper macros. They all use PORT.PIN convention
(internally converted to contiguous "gpio space") to be in line with
SOC specs and board schematics. The function number, as mentioned above,
is SOC-specific. A PIO my be given a name, if it is supposed to be used
by a driver (see p. 3 below) or modified during initialization (see p. 2.4).
--------8<--------8<--------8<--------
#define STM_PAD_PIO_IN(_port, _pin, _function)
#define STM_PAD_PIO_IN_NAMED(_port, _pin, _function, _name)
#define STM_PAD_PIO_OUT(_port, _pin, _function)
#define STM_PAD_PIO_OUT_NAMED(_port, _pin, _function, _name)
#define STM_PAD_PIO_BIDIR(_port, _pin, _function)
#define STM_PAD_PIO_BIDIR_NAMED(_port, _pin, _function, _name)
--------8<--------8<--------8<--------
There is also a special variant, which defines a PIO description
without specified function nor direction - it is intended to create
a named "stub", configured later with methods described in p. 2.4:
--------8<--------8<--------8<--------
#define STM_PAD_PIO_STUB_NAMED(_port, _pin, _name)
--------8<--------8<--------8<--------
There is also a "priv" field in the "stm_pad_gpio" structure, which
is not being set by any of the standard macros. It may be used to pass
some custom information to the "gpio_config" function mentioned above.
The suggested way to use it defining a SOC-specific macro, as in
the example below:
--------8<--------8<--------8<--------
#define STX7108_PIO_ETH_DATA_IN(_gmac, _port, _pin) \
{ \
.gpio = stm_gpio(_port, _pin), \
.direction = stm_pad_gpio_direction_in, \
.function = _gmac + 1, \
.priv = &stx7108_ethernet_retime_data[_gmac], \
}
--------8<--------8<--------8<--------
and then used like this:
--------8<--------8<--------8<--------
static int stx7108_pio_config(unsigned gpio,
enum stm_pad_gpio_direction direction, int function,
void *priv)
{
<...>
struct stx7108_pio_retime_config *retime_config = priv;
<...>
if (retime_config)
stx7108_pio_config_retime(port, pin, retime_config);
return 0;
}
--------8<--------8<--------8<--------
2.2. sysconfig bits
===================
The term "sysconfigs" cover all generic-purpose registers used to set
different IP configurations values. Their location, organization and use
differ a lot between SOCs, but generally three main schemes exist and
are defined in SOC headers (eg. "include/linux/stm/stx7100.h"):
1. Single registers block with registered grouped in three categories:
SYS_DEV, SYS_STA and SYS_CFG (STx5206, STx7100/9, STx7105/6, STx7111,
STx7141, STx7200).
2. Multiple register blocks ("banks") with registered grouped similarly to
above: SYS_CFG_BANKx, SYS_STA_BANKx and SYS_CFG_BANKx (STx7108)
3. Multiple register blocks with registered semi-randomly located here
and there (STx5197, FLIx7510); in this case individual register names
are defined along the bank names, eg:
--------8<--------8<--------8<--------
#define HS_CFG 0
#define HD_CFG 1
#define CFG_CTRL_A HS_CFG, (0x00 / 4)
#define CFG_CTRL_B HS_CFG, (0x04 / 4)
--------8<--------8<--------8<--------
or
--------8<--------8<--------8<--------
#define PRB_PU_CFG_1 0
#define TRS_SPARE_REGS_0 2
#define CFG_RESET_CTL PRB_PU_CFG_1, (0x00 / 4)
#define CFG_COMMS_CONFIG_1 TRS_SPARE_REGS_0, (0x00 / 4)
--------8<--------8<--------8<--------
Different bits of these registers are responsible for a wide selection
of SOC internals, for example: setting bit 16 of SYS_CFG7 register to 1
in STx7105 enables Ethernet controller. Usually there is a few of such
things to be set per individual IP, sometimes in a different way
depending on required configuration, eg. to configure mentioned
Ethernet controller in MII or RMII.
Our kernel provides an API to work with these registers, defined
in "include/linux/stm/sysconf.h" header, however it should not be
used in any of devices drivers to keep them SOC-agnostic. The only
place that "sysconfigs" should be referenced to are SOC setup files,
either directly in some cases, or as arrays of "struct stm_pad_sysconf"
pointed by field ".sysconfs" in "stm_pad_config" structure, as shown
in p. 2.
Similarly to PIOs case, there are macros defined, helping to create an
array of required "sysconfigs" in a reader-friendly way. As the SYS_CFG
groups are most likely to be set (SYS_DEV & SYS_STA are usually read-only)
there are two macros available, to be used in first two of schemes
described above. Their definitions with examples:
--------8<--------8<--------8<--------
#define STM_PAD_SYS_CFG(_regnum, _lsb, _msb, _value)
/* ethernet_interface_on */
STM_PAD_SYS_CFG(7, 16, 16, 1),
/* enMII: 0 = reverse MII mode, 1 = MII mode */
STM_PAD_SYS_CFG(7, 27, 27, 1),
#define STM_PAD_SYS_CFG_BANK(_bank, _regnum, _lsb, _msb, _value)
/* EN_GMAC0 */
STM_PAD_SYS_CFG_BANK(2, 53, 0, 0, 1),
/* MIIx_PHY_SEL */
STM_PAD_SYS_CFG_BANK(2, 27, 2, 4, 0),
--------8<--------8<--------8<--------
SOCs with defined "sysconfig" register names (third scheme) should use
the following macro in similar way:
--------8<--------8<--------8<--------
#define STM_PAD_SYSCONF(_reg, _lsb, _msb, _value)
/* Ethernet interface on */
STM_PAD_SYSCONF(CFG_CTRL_E, 0, 0, 1),
/* RMII/MII pin mode */
STM_PAD_SYSCONF(CFG_CTRL_E, 7, 8, 3),
/* gmac_mii_enable */
STM_PAD_SYSCONF(CFG_COMMS_CONFIG_2, 8, 8, 1),
/* gmac_enable */
STM_PAD_SYSCONF(CFG_COMMS_CONFIG_2, 24, 24, 1),
--------8<--------8<--------8<--------
2.3. Custom calls
=================
In some cases operations required to set up some configuration are too
complex to be described by the fields above. It may be a case of
a configuration bit shared between a number of independent IPs, typical
example is a bit enabling clock driving couple independent
controller IPs.
To handle such situations "custom_claim" and "custom_release" function
pointers may be defined in "stm_pad_config" structure. They will be
called - respectively - after and before PIOs and sysconfig bits
(de)allocation and get pointer to private data, defined as "custom_priv"
field as well as state ("struct stm_pad_state") pointer, which can be
used to obtain "gpio" value (as described in p. 3), what may be necessary
in the most extreme cases.
2.4. Modifying configurations in runtime
====================================================
As mentioned above IPs may require different pad configuration, depending
on use case as defined in board setup code. For example this may be a
matter of configuring Ethernet in RMII or MII mode, or setting up SSC
in I2C vs SPI mode.
In most cases it is possible to prepare a set of different "stm_pad_config"
structures and assign them to platform data depending on data received
from boards setup. Sometimes however it would require a huge number
of variants, which may not be feasible for a number of reasons.
In such case one may go one of two ways: either create a "template"
configuration and modify some of the fields, or build entire configuration
"from scratch" in runtime.
The first possibility is especially suitable for changing small subset
of PIO information. The following helpers are defined:
--------8<--------8<--------8<--------
#define stm_pad_set_pio(config, name, port, pin)
#define stm_pad_set_pio_in(config, name, function)
#define stm_pad_set_pio_out(config, name, function)
#define stm_pad_set_pio_bidir(config, name, function)
#define stm_pad_set_pio_bidir(config, name, function)
--------8<--------8<--------8<--------
PIOs that are supposed to be modified must be defined with "_NAMED" macro.
Typical example is Ethernet's "PHYCLK" PIO direction, which may be
configured as input or output depending on board design. It is also
possible to change alternative function value, if required:
--------8<--------8<--------8<--------
static struct stm_pad_config *stx7105_ethernet_pad_configs[] = {
<...>
.gpios = (struct stm_pad_gpio []) {
<...>
STM_PAD_PIO_OUT_NAMED(9, 5, 1, "PHYCLK"),
},
<...>
void __init stx7105_configure_ethernet(int port,
struct stx7105_ethernet_config *config)
{
<...>
struct stm_pad_config *pad_config
<...>
pad_config = &stx7105_ethernet_pad_configs[port][config->mode];
<...>
if (config->ext_clk)
stm_pad_set_pio_in(pad_config, "PHYCLK", 2 + port);
else
stm_pad_set_pio_out(pad_config, "PHYCLK", 1 + port);
--------8<--------8<--------8<--------
In some cases this PIO may be also set as "ignored", which means that
it will now be claimed at all during "stm_pad_claim()":
--------8<--------8<--------8<--------
if (config->ext_clk)
stm_pad_set_pio_ignored(pad_config, "PHYCLK");
--------8<--------8<--------8<--------
In case when required function may be present on more then one PIO,
it is possible to define a PIO "STUB" and then set its information
in runtime:
--------8<--------8<--------8<--------
static struct stm_pad_config *stx7105_ethernet_pad_configs[] = {
<...>
.gpios = (struct stm_pad_gpio []) {
<...>
STM_PAD_PIO_STUB_NAMED(-1, -1, "MDIO"),
},
<...>
void __init stx7105_configure_ethernet(int port,
struct stx7105_ethernet_config *config)
{
<...>
struct stm_pad_config *pad_config
<...>
pad_config = &stx7105_ethernet_pad_configs[port][config->mode];
<...>
switch (config->routing.mii1.mdio) {
case stx7105_ethernet_mii1_mdio_pio3_4:
stm_pad_set_pio(pad_config, "MDIO", 3, 4);
stm_pad_set_pio_out(pad_config, "MDIO", 4);
break;
case stx7105_ethernet_mii1_mdio_pio11_0:
stm_pad_set_pio(pad_config, "MDIO", 11, 0);
stm_pad_set_pio_out(pad_config, "MDIO", 3);
break;
--------8<--------8<--------8<--------
There is no explicit support for modifying "sysconfig" definitions
in runtime, however it is possible as well:
--------8<--------8<--------8<--------
static struct stm_pad_config *stx7105_ethernet_pad_configs[] = {
<...>
.sysconfs = (struct stm_pad_sysconf []) {
/* eth1_mdiin_src_sel:
* 1 = mdi in is from PIO11(0)
* 0 = mdc in is from PIO3(4) */
STM_PAD_SYS_CFG(16, 4, 4, -1), /* set below */
<...>
void __init stx7105_configure_ethernet(int port,
struct stx7105_ethernet_config *config)
{
<...>
struct stm_pad_config *pad_config
<...>
pad_config = &stx7105_ethernet_pad_configs[port][config->mode];
<...>
switch (config->routing.mii1.mdio) {
case stx7105_ethernet_mii1_mdio_pio3_4:
/* eth1_mdiin_src_sel = 0 */
pad_config->sysconfs[0].value = 0;
break;
case stx7105_ethernet_mii1_mdio_pio11_0:
/* eth1_mdiin_src_sel = 1 */
pad_config->sysconfs[0].value = 1;
break;
--------8<--------8<--------8<--------
2.5. Building configurations in runtime
====================================================
Some of SOCs are "ultimately flexible" regarding PIOs function allocation.
For example, in case of STx7105, each of SSC2 (and SSC3) signals can be
routed to one of four (sic!) different PIOs. This means 4^2 different
configurations for I2C and 4^3 for SPI, all together 80 (!) variants.
Therefore it is more feasible to create required configuration in runtime.
First, such a structure must be allocated using the following function:
--------8<--------8<--------8<--------
struct stm_pad_config *stm_pad_config_alloc(int gpio_values_num,
int sysconf_values_num);
--------8<--------8<--------8<--------
Parameters define _maximum_ number of PIO and sysconfig entries that
may be added using any of the methods described below. Note that
"stm_pad_config_add_sys_cfg()" is a shortcut for defining SYS_CFG-related
bits with "stm_pad_config_add_sysconf()".
--------8<--------8<--------8<--------
int stm_pad_config_add_sysconf(struct stm_pad_config *config,
int regtype, int regnum, int lsb, int msb, int value);
#define stm_pad_config_add_sys_cfg(config, regnum, lsb, msb, value)
#define stm_pad_config_add_pio_in(config, port, pin, function)
#define stm_pad_config_add_pio_in_named(config, port, pin, function, name)
#define stm_pad_config_add_pio_out(config, port, pin, function)
#define stm_pad_config_add_pio_out_named(config, port, pin,
function, name)
#define stm_pad_config_add_pio_bidir(config, port, pin, function)
#define stm_pad_config_add_pio_bidir_named(config, port, pin,
function, name)
--------8<--------8<--------8<--------
An example of use may look like this:
--------8<--------8<--------8<--------
pad_config = stm_pad_config_alloc(2, 2);
/* SCL */
switch (config->routing.ssc2.sclk) {
case stx7105_ssc2_sclk_pio2_4: /* 7106 only! */
BUG_ON(cpu_data->type != CPU_STX7106);
stm_pad_config_add_pio_bidir_named(pad_config,
2, 4, 2, "SCL");
/* ssc2_sclk_in: 00 = PIO2.4 */
stm_pad_config_add_sys_cfg(pad_config, 16, 11, 12, 0);
break;
case stx7105_ssc2_sclk_pio3_4:
stm_pad_config_add_pio_bidir_named(pad_config,
3, 4, 2, "SCL");
/* ssc2_sclk_in: 01 = PIO3.4 */
stm_pad_config_add_sys_cfg(pad_config, 16, 11, 12, 1);
break;
}
/* SDA */
switch (config->routing.ssc2.mtsr) {
case stx7105_ssc2_mtsr_pio2_0:
stm_pad_config_add_pio_bidir_named(pad_config,
2, 0, 3, "SDA");
/* ssc2_mtsr_in: 00 = PIO2.0 */
stm_pad_config_add_sys_cfg(pad_config, 16, 9, 10, 0);
break;
case stx7105_ssc2_mtsr_pio3_5:
stm_pad_config_add_pio_bidir_named(pad_config,
3, 5, 2, "SDA");
/* ssc2_mtsr_in: 01 = PIO3.5 */
stm_pad_config_add_sys_cfg(pad_config, 16, 9, 10, 1);
break;
}
--------8<--------8<--------8<--------
3. Driver API
=============
From a driver developer's point of view the matter looks quite simple.
He can expect a "struct stm_pad_config *" value in a device platform
data, for example:
--------8<--------8<--------8<--------
struct mydevice_plat_data {
/* Some platform-dependent values */
struct stm_pad_config *pad_config;
};
static struct platform_device mydevice = {
.name = "mydevice",
.id = -1,
.dev.platform_data = &(struct mydevice_plat_data) {
.pad_config = &(struct pad_config) {
/* See p. 2 of this document */
}
}
}
--------8<--------8<--------8<--------
And now, all the driver is supposed to do while being "probed" is to call
the following function:
--------8<--------8<--------8<--------
struct stm_pad_state *stm_pad_claim(struct stm_pad_config *config,
const char *owner);
--------8<--------8<--------8<--------
The obtained "struct stm_pad_state *" is supposed to be released when the
device is being removed:
--------8<--------8<--------8<--------
void stm_pad_release(struct stm_pad_state *state);
--------8<--------8<--------8<--------
All together, a "mydevice" driver implementation could look like this:
(all error handling etc. skipped)
--------8<--------8<--------8<--------
struct mydevice {
struct stm_pad_state *pad_state;
};
static int __devinit mydevice_probe(struct platform_device *pdev)
{
struct mydevice_plat_data *plat_data = dev->platform_data;
struct mydevice *mydevice = mydevice_alloc();
mydevice->pad_state = stm_pad_claim(plat_data->pad_config,
dev_name(&pdev->dev));
platform_set_drvdata(pdev, mydevice);
return 0;
}
static int mydevice_remove(struct platform_device *pdev)
{
struct mydevice *mydevice = platform_get_drvdata(pdev);
stm_pad_release(mydevice->pad_state);
return 0;
}
static struct platform_driver mydevice_driver = {
.driver = {
.name = "mydevice",
.owner = THIS_MODULE,
},
.probe = mydevice_probe,
.remove = mydevice_remove,
}
--------8<--------8<--------8<--------
Alternatively one can decide to use "devm" style API, which releases
resources automatically on device removal:
--------8<--------8<--------8<--------
struct stm_pad_state *devm_stm_pad_claim(struct device *dev,
struct stm_pad_config *config, const char *owner);
void devm_stm_pad_release(struct device *dev, struct stm_pad_state *state);
--------8<--------8<--------8<--------
So the "mydevice_probe" would be:
--------8<--------8<--------8<--------
static int __devinit mydevice_probe(struct platform_device *pdev)
{
devm_stm_pad_claim(&pdev->dev, plat_data->pad_config,
dev_name(&pdev->dev));
return 0;
}
--------8<--------8<--------8<--------
and that's it! Calling "devm_stm_pad_release" is not necessary at all
(one may want to use in an error path in "probe").
The "state pointer" is also useful when the drivers temporarily wants
to switch some of the related pads to generic PIO mode.
--------8<--------8<--------8<--------
unsigned stm_pad_gpio_request_input(struct stm_pad_state *state,
const char *name);
unsigned stm_pad_gpio_request_output(struct stm_pad_state *state,
const char *name, int value);
void stm_pad_gpio_free(struct stm_pad_state *state, unsigned gpio);
--------8<--------8<--------8<--------
The "name" is defined by the platform configuration as described in p. 2.1
above, and should shared between the driver and all the BSPs (the "well
known" name, for the I2C signals should be _always_ described as "SCL"
and "SDA"). Value returned by the "request" functions is just a
"generic gpio" number, which can be used with standard "gpio_set_value()"
and "gpio_get_value()" calls.
This may be useful to perform some non-standard "bit-banging" operations,
for example as a hardware issue workaround or to implement operations not
supported by the hardware controller cell. For example, if "mydevice" driver
was supposed to pull the line down for 100us in case of error, as the
hardware wouldn't be doing this on its own:
--------8<--------8<--------8<--------
void mydevice_handle_error(struct mydevice *mydevice)
{
unsigned signal_gpio;
signal_gpio = stm_pad_gpio_request_output(mydevice->pad_state,
"SIGNAL");
gpio_set_value(signal_gpio, 0);
usleep(100);
gpio_set_value(signal_gpio, 1);
stm_pad_gpio_free(mydevice->pad_state, signal_gpio);
}
--------8<--------8<--------8<--------
4. GPIO driver relationship
===========================
As it is clear now, that pad management and PIO interface are closely
related to each other. As the "generic PIO" mode is effectively just
one of possible pad configuration, PIO driver is a pad manager client.
For a number of reasons it is somehow privileged though, and there is
a special, private API, for its sole use only:
--------8<--------8<--------8<--------
int stm_pad_claim_gpio(unsigned gpio);
void stm_pad_release_gpio(unsigned gpio);
const char *stm_pad_get_gpio_owner(unsigned gpio);
--------8<--------8<--------8<--------
*** Please, DO NOT USE THIS API in any other driver! ***