Skip to content

Commit 7269b6c

Browse files
committed
core: pinctrl: add custom pinctrl state and new interfaces for pinmuxing
1 parent 74c061f commit 7269b6c

19 files changed

Lines changed: 720 additions & 154 deletions

File tree

cores/arduino/SerialUSB.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,6 @@ class SerialUSB_ : public ZephyrSerial {
4141
uint32_t baudrate;
4242
static void baudChangeHandler(const struct device *dev, uint32_t rate);
4343

44-
void _reinit_if_needed() override {
45-
/* prevent reinit: USB device is always available */
46-
}
47-
4844
private:
4945
bool started = false;
5046

cores/arduino/zephyrCommon.cpp

Lines changed: 168 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,145 @@
66

77
#include <Arduino.h>
88
#include "zephyrInternal.h"
9+
#include <zephyr/drivers/pinctrl.h>
910

1011
#include <zephyr/spinlock.h>
1112

12-
// create an array of arduino_pins with functions to reinitialize pins if needed
13-
static const struct device *pinmux_array[DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios)] = {
14-
nullptr};
13+
#if defined(ARDUINO)
14+
/*
15+
* The global ARDUINO macro is numeric (e.g. 10607) in Arduino builds.
16+
* Temporarily hide it so pinctrl token concatenation can use the literal
17+
* custom state name "ARDUINO" from devicetree pinctrl-names.
18+
* Otherwise, the generated pinctrl state identifiers would be like PINCTRL_STATE_10607 instead of
19+
* PINCTRL_STATE_ARDUINO.
20+
*/
21+
#pragma push_macro("ARDUINO")
22+
#undef ARDUINO
23+
#endif
24+
25+
/*
26+
* Pinctrl configuration structures for dynamic pin switching.
27+
*
28+
* Map deferred-init peripherals and zephyr,console (that is not deferred) with their
29+
* pinctrl configuration from devicetree.
30+
*/
31+
#define NODE_SELECTED(node_id) \
32+
UTIL_OR(DT_PROP(node_id, zephyr_deferred_init), \
33+
DT_SAME_NODE(node_id, DT_CHOSEN(zephyr_console)))
34+
35+
#define PINCTRL_DEFINE_IF_SELECTED(node_id) \
36+
COND_CODE_1(NODE_SELECTED(node_id), (PINCTRL_DT_DEFINE(node_id);), ())
37+
38+
DT_FOREACH_STATUS_OKAY_NODE(PINCTRL_DEFINE_IF_SELECTED)
39+
40+
struct pinctrl_map_entry {
41+
const struct device *dev;
42+
const struct pinctrl_dev_config *pcfg;
43+
};
44+
45+
#define PINCTRL_MAP_ENTRY(node_id) {DEVICE_DT_GET(node_id), PINCTRL_DT_DEV_CONFIG_GET(node_id)},
46+
#define PINCTRL_MAP_ENTRY_IF_PRESENT(node_id) \
47+
COND_CODE_1(NODE_SELECTED(node_id), (PINCTRL_MAP_ENTRY(node_id)), ())
48+
49+
static const struct pinctrl_map_entry pinctrl_map[] = {
50+
DT_FOREACH_STATUS_OKAY_NODE(PINCTRL_MAP_ENTRY_IF_PRESENT){NULL, NULL},
51+
};
52+
53+
#if defined(ARDUINO)
54+
#pragma pop_macro("ARDUINO")
55+
#endif
56+
57+
/* Get pinctrl_dev_config for a device from the generated map. */
58+
static const struct pinctrl_dev_config *get_known_pcfg(const struct device *dev) {
59+
for (size_t i = 0; i < ARRAY_SIZE(pinctrl_map); i++) {
60+
if (pinctrl_map[i].dev == dev) {
61+
return pinctrl_map[i].pcfg;
62+
}
63+
}
64+
65+
return nullptr;
66+
}
67+
68+
/**
69+
* @brief Initialize the peripheral and acquire a single pin to ARDUINO state.
70+
*
71+
* Switches peripheral pins back to ARDUINO pinctrl state (alternate function),
72+
* typically after a temporary transition to GPIO mode.
73+
*
74+
* @param dev Pointer to the peripheral device
75+
* @param state_pin_idx Index of the pin within the device's ARDUINO pinctrl state
76+
* @return 0 on success, negative on error
77+
*/
78+
int init_dev_apply_channel_pinctrl(const struct device *dev, size_t state_pin_idx) {
79+
80+
if (dev == nullptr) {
81+
return -EINVAL;
82+
}
83+
84+
if (!device_is_ready(dev)) {
85+
// init device for first usage, if not ready
86+
int err = device_init(dev);
87+
if (err < 0) {
88+
return err;
89+
}
90+
}
91+
92+
const struct pinctrl_state *state;
93+
const struct pinctrl_dev_config *pcfg = get_known_pcfg(dev);
94+
95+
if (pcfg == nullptr) {
96+
/* Device not in DT mapping - add to pinctrl_map if needed */
97+
return -ENOTSUP;
98+
}
99+
100+
int err = pinctrl_lookup_state(pcfg, PINCTRL_STATE_ARDUINO, &state);
101+
if (err < 0) {
102+
return err; /* Fails if the state is not defined in pinctrl-names */
103+
}
104+
105+
/* bounds check */
106+
if (state_pin_idx >= state->pin_cnt) {
107+
return -ERANGE;
108+
}
109+
110+
/*
111+
* On platforms without CONFIG_PINCTRL_STORE_REG (e.g. STM32) the pcfg->reg is not present but
112+
* the argument is ignored by their pinctrl driver, so passing PINCTRL_REG_NONE is safe.
113+
*/
114+
#ifdef CONFIG_PINCTRL_STORE_REG
115+
uintptr_t reg = pcfg->reg;
116+
#else
117+
uintptr_t reg = PINCTRL_REG_NONE;
118+
#endif
119+
120+
return pinctrl_configure_pins(&state->pins[state_pin_idx], 1, reg);
121+
}
122+
123+
/**
124+
* @brief Optimize peripheral transitions applying pinctrl state PINCTRL_STATE_DEFAULT.
125+
*
126+
* @param dev Target peripheral device to acquire pin for
127+
*/
128+
int init_dev_apply_pinctrl(const struct device *dev) {
129+
130+
if (dev == nullptr) {
131+
return -EINVAL;
132+
}
15133

16-
void _reinit_peripheral_if_needed(pin_size_t pin, const struct device *dev) {
17-
if (pinmux_array[pin] != dev) {
18-
pinmux_array[pin] = dev;
19-
if (dev != NULL) {
20-
device_init(dev);
134+
if (!device_is_ready(dev)) {
135+
int ret = device_init(dev);
136+
if (ret != 0 && ret != -EALREADY) {
137+
return ret;
21138
}
22139
}
140+
141+
const struct pinctrl_dev_config *pcfg = get_known_pcfg(dev);
142+
if (pcfg == nullptr) {
143+
/* Device not in DT mapping - add to pinctrl_map if needed */
144+
return -ENOTSUP;
145+
}
146+
147+
return pinctrl_apply_state(pcfg, PINCTRL_STATE_DEFAULT);
23148
}
24149

25150
static const struct gpio_dt_spec arduino_pins[] = {
@@ -143,6 +268,24 @@ void handleGpioCallback(const struct device *port, struct gpio_callback *cb, uin
143268
}
144269
}
145270

271+
/*
272+
* Resolve pin index in a device ARDUINO pinctrl state from a DT spec array.
273+
* The resulting index is the per-device ordinal at spec_idx.
274+
*/
275+
template <typename DT_SPEC, size_t N>
276+
static size_t state_pin_index_from_spec_index(const DT_SPEC (&specs)[N], size_t spec_idx) {
277+
const struct device *dev = specs[spec_idx].dev;
278+
size_t state_pin_idx = 0;
279+
280+
for (size_t i = 0; i < spec_idx; i++) {
281+
if (specs[i].dev == dev) {
282+
state_pin_idx++;
283+
}
284+
}
285+
286+
return state_pin_idx;
287+
}
288+
146289
#ifdef CONFIG_PWM
147290

148291
#define PWM_DT_SPEC(n, p, i) PWM_DT_SPEC_GET_BY_IDX(n, i),
@@ -216,7 +359,6 @@ static const struct device *const dac_dev = DEVICE_DT_GET(DAC_NODE);
216359
static const struct dac_channel_cfg dac_ch_cfg[] = {
217360
DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), dac_channels, DAC_CHANNEL_DEFINE)};
218361

219-
static bool dac_channel_initialized[NUM_OF_DACS];
220362
#endif
221363

222364
#endif
@@ -251,7 +393,6 @@ int digitalPinToPinIndex(pin_size_t pinNumber) {
251393
void pinMode(pin_size_t pinNumber, PinMode pinMode) {
252394
RETURN_ON_INVALID_PIN(pinNumber);
253395

254-
_reinit_peripheral_if_needed(pinNumber, NULL);
255396
if (pinMode == INPUT) { // input mode
256397
gpio_pin_configure_dt(&arduino_pins[pinNumber], GPIO_INPUT | GPIO_ACTIVE_HIGH);
257398
} else if (pinMode == INPUT_PULLUP) { // input with internal pull-up
@@ -486,13 +627,15 @@ void analogWrite(pin_size_t pinNumber, int value) {
486627
return;
487628
}
488629

630+
(void)init_dev_apply_channel_pinctrl(arduino_pwm[idx].dev,
631+
state_pin_index_from_spec_index(arduino_pwm, idx));
632+
489633
if (!pwm_is_ready_dt(&arduino_pwm[idx])) {
490634
pinMode(pinNumber, OUTPUT);
491635
digitalWrite(pinNumber, value > 127 ? HIGH : LOW);
492636
return;
493637
}
494638

495-
_reinit_peripheral_if_needed(pinNumber, arduino_pwm[idx].dev);
496639
value = CLAMP(value, 0, maxInput);
497640

498641
const uint32_t pulse = map64(value, 0, maxInput, 0, arduino_pwm[idx].period);
@@ -516,19 +659,13 @@ void analogWrite(enum dacPins dacName, int value) {
516659
return;
517660
}
518661

519-
if (!dac_channel_initialized[dacName]) {
520-
if (!device_is_ready(dac_dev)) {
521-
return;
522-
}
662+
// TODO: add reverse map to find pin name from DAC* define
663+
// In the meantime, consider A0 == DAC0
664+
(void)init_dev_apply_pinctrl(dac_dev);
523665

524-
// TODO: add reverse map to find pin name from DAC* define
525-
// In the meantime, consider A0 == DAC0
526-
_reinit_peripheral_if_needed((pin_size_t)(dacName + A0), dac_dev);
527-
ret = dac_channel_setup(dac_dev, &dac_ch_cfg[dacName]);
528-
if (ret != 0) {
529-
return;
530-
}
531-
dac_channel_initialized[dacName] = true;
666+
ret = dac_channel_setup(dac_dev, &dac_ch_cfg[dacName]);
667+
if (ret != 0) {
668+
return;
532669
}
533670

534671
value = CLAMP(value, 0, maxInput);
@@ -586,7 +723,14 @@ int analogRead(pin_size_t pinNumber) {
586723
return -ENOTSUP;
587724
}
588725

589-
_reinit_peripheral_if_needed(pinNumber, arduino_adc[idx].dev);
726+
/*
727+
* Init the ADC device for the first acquisition and restore only the required pin to analog
728+
* mode when transitioning from GPIO. The pin is selected from the ADC device "arduino" pinctrl
729+
* state. Not checking the return value because the device might not have pinctrl (e.g. nRF
730+
* SAADC).
731+
*/
732+
(void)init_dev_apply_channel_pinctrl(arduino_adc[idx].dev,
733+
state_pin_index_from_spec_index(arduino_adc, idx));
590734

591735
err = adc_channel_setup(arduino_adc[idx].dev, &arduino_adc[idx].channel_cfg);
592736
if (err < 0) {

cores/arduino/zephyrInternal.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ extern "C" {
1414

1515
void enableInterrupt(pin_size_t);
1616
void disableInterrupt(pin_size_t);
17-
void _reinit_peripheral_if_needed(pin_size_t pin, const struct device *dev);
17+
18+
int init_dev_apply_pinctrl(const struct device *dev);
1819

1920
#ifdef __cplusplus
2021
} // extern "C"

cores/arduino/zephyrSerial.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
* SPDX-License-Identifier: Apache-2.0
66
*/
77

8+
#include "zephyrInternal.h"
9+
810
#include <zephyr/devicetree.h>
911
#include <zephyr/drivers/uart.h>
1012

@@ -60,7 +62,10 @@ void arduino::ZephyrSerial::begin(unsigned long baud, uint16_t conf) {
6062
.flow_ctrl = UART_CFG_FLOW_CTRL_NONE,
6163
};
6264

63-
_reinit_if_needed();
65+
/* Re-apply DEFAULT pinctrl state so shared pins
66+
* are remuxed back to Serial after other peripherals have used them.
67+
*/
68+
(void)init_dev_apply_pinctrl(uart);
6469

6570
uart_configure(uart, &config);
6671
uart_irq_callback_user_data_set(uart, arduino::ZephyrSerial::IrqDispatch, this);

cores/arduino/zephyrSerial.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,6 @@ class ZephyrSerial : public HardwareSerial {
106106
const struct device *uart;
107107
ZephyrSerialBuffer<CONFIG_ARDUINO_API_SERIAL_BUFFER_SIZE> tx;
108108
ZephyrSerialBuffer<CONFIG_ARDUINO_API_SERIAL_BUFFER_SIZE> rx;
109-
110-
virtual void _reinit_if_needed() {
111-
device_init(uart);
112-
}
113109
};
114110

115111
} // namespace arduino

0 commit comments

Comments
 (0)