@@ -28,11 +28,13 @@ digitalinout_result_t common_hal_digitalio_digitalinout_construct(
2828
2929 common_hal_mcu_pin_claim (pin );
3030 self -> pin = pin ;
31+ self -> open_drain = false;
32+ self -> vssel = MXC_GPIO_VSSEL_VDDIOH ;
3133
3234 mxc_gpio_cfg_t new_gpio_cfg = {
3335 .port = gpio_ports [self -> pin -> port ],
3436 .mask = (self -> pin -> mask ),
35- .vssel = self -> pin -> level ,
37+ .vssel = self -> vssel ,
3638 .func = MXC_GPIO_FUNC_IN ,
3739 .drvstr = MXC_GPIO_DRVSTR_0 ,
3840 .pad = MXC_GPIO_PAD_NONE ,
@@ -55,6 +57,7 @@ digitalinout_result_t common_hal_digitalio_digitalinout_switch_to_input(
5557 digitalio_digitalinout_obj_t * self , digitalio_pull_t pull ) {
5658 mxc_gpio_regs_t * port = gpio_ports [self -> pin -> port ];
5759 uint32_t mask = self -> pin -> mask ;
60+
5861 int err = E_NO_ERROR ;
5962
6063 if (self -> pin -> port == 4 ) {
@@ -76,21 +79,18 @@ digitalinout_result_t common_hal_digitalio_digitalinout_switch_to_output(
7679 mxc_gpio_regs_t * port = gpio_ports [self -> pin -> port ];
7780 uint32_t mask = self -> pin -> mask ;
7881
82+ self -> open_drain = (drive_mode == DRIVE_MODE_OPEN_DRAIN );
83+
84+ // Set GPIO(s) to output mode
7985 if (self -> pin -> port == 4 ) {
80- // Set GPIO(s) to output mode
8186 MXC_MCR -> gpio4_ctrl |= GPIO4_OUTEN_MASK (mask );
8287 MXC_MCR -> outen &= ~GPIO4_AFEN_MASK (mask );
8388 } else {
8489 MXC_GPIO_RevA_SetAF ((mxc_gpio_reva_regs_t * )port , MXC_GPIO_FUNC_OUT , mask );
8590 }
91+
8692 common_hal_digitalio_digitalinout_set_value (self , value );
8793
88- // todo (low): MSDK Hardware does not support open-drain configuration except
89- // todo (low): when directly managed by a peripheral such as I2C.
90- // todo (low): find a way to signal this to any upstream code
91- if (drive_mode != DRIVE_MODE_PUSH_PULL ) {
92- return DIGITALINOUT_INVALID_DRIVE_MODE ;
93- }
9494 return DIGITALINOUT_OK ;
9595}
9696
@@ -100,6 +100,11 @@ digitalio_direction_t common_hal_digitalio_digitalinout_get_direction(
100100 mxc_gpio_regs_t * port = gpio_ports [self -> pin -> port ];
101101 uint32_t mask = self -> pin -> mask ;
102102
103+ // Open drain must be considered output for CircuitPython API to work properly
104+ if (self -> open_drain ) {
105+ return DIRECTION_OUTPUT ;
106+ }
107+
103108 if (self -> pin -> port < 4 ) {
104109 // Check that I/O mode is enabled and we don't have in AND out on at the same time
105110 MP_STATIC_ASSERT (!((port -> en0 & mask ) && (port -> inen & mask ) && (port -> outen & mask )));
@@ -129,8 +134,30 @@ void common_hal_digitalio_digitalinout_set_value(
129134 mxc_gpio_regs_t * port = gpio_ports [self -> pin -> port ];
130135 uint32_t mask = self -> pin -> mask ;
131136
132- if (dir == DIRECTION_OUTPUT ) {
133- if (value == true) {
137+ MXC_GPIO_SetVSSEL (port , self -> vssel , mask );
138+
139+ if (self -> open_drain ) {
140+ // Open-drain can be done by setting to input mode, no pullup/pulldown
141+ // when the value is high (no sink current into GPIO)
142+ if (value ) {
143+ // set to input, no pull
144+ common_hal_digitalio_digitalinout_switch_to_input (self , PULL_NONE );
145+ }
146+ else {
147+ // can't use common_hal_switch_to_output b/c it calls this function
148+ // set the GPIO to output, low
149+ if (self -> pin -> port == 4 ) {
150+ MXC_MCR -> gpio4_ctrl |= GPIO4_OUTEN_MASK (mask );
151+ MXC_MCR -> outen &= ~GPIO4_AFEN_MASK (mask );
152+ } else {
153+ MXC_GPIO_RevA_SetAF ((mxc_gpio_reva_regs_t * )port , MXC_GPIO_FUNC_OUT , mask );
154+ }
155+ MXC_GPIO_OutClr (port , mask );
156+ }
157+ }
158+
159+ else if (dir == DIRECTION_OUTPUT ) {
160+ if (value ) {
134161 MXC_GPIO_OutSet (port , mask );
135162 } else {
136163 MXC_GPIO_OutClr (port , mask );
@@ -145,6 +172,10 @@ bool common_hal_digitalio_digitalinout_get_value(digitalio_digitalinout_obj_t *s
145172 mxc_gpio_regs_t * port = gpio_ports [self -> pin -> port ];
146173 uint32_t mask = self -> pin -> mask ;
147174
175+ if (self -> open_drain ) {
176+ return MXC_GPIO_InGet (port , mask ) && mask ;
177+ }
178+
148179 if (dir == DIRECTION_INPUT ) {
149180 if (self -> pin -> port == 4 ) {
150181 return (bool )(MXC_MCR -> gpio4_ctrl & GPIO4_DATAIN_MASK (mask ));
@@ -155,21 +186,29 @@ bool common_hal_digitalio_digitalinout_get_value(digitalio_digitalinout_obj_t *s
155186 }
156187}
157188
158- /** FIXME: Implement open-drain by switching to input WITHOUT re-labeling the pin */
159189digitalinout_result_t common_hal_digitalio_digitalinout_set_drive_mode (
160190 digitalio_digitalinout_obj_t * self , digitalio_drive_mode_t drive_mode ) {
161191
162- if (drive_mode == DRIVE_MODE_OPEN_DRAIN ) {
163- common_hal_digitalio_digitalinout_switch_to_input (self , PULL_NONE );
164- }
165- // On MAX32, drive mode is not configurable
166- // and should always be push-pull unless managed by a peripheral like I2C
192+ // Check what the current value is
193+ bool value = common_hal_digitalio_digitalinout_get_value (self );
194+ self -> open_drain = (drive_mode == DRIVE_MODE_OPEN_DRAIN );
195+
196+ // Re-set the value to account for different setting methods for drive types
197+ // Switch to output will both set the output config
198+ // AND set the value for the new drive type
199+ common_hal_digitalio_digitalinout_switch_to_output (self , value , drive_mode );
200+
167201 return DIGITALINOUT_OK ;
168202}
169203
170204digitalio_drive_mode_t common_hal_digitalio_digitalinout_get_drive_mode (
171205 digitalio_digitalinout_obj_t * self ) {
172- return DRIVE_MODE_PUSH_PULL ;
206+ if (self -> open_drain ) {
207+ return DRIVE_MODE_OPEN_DRAIN ;
208+ }
209+ else {
210+ return DRIVE_MODE_PUSH_PULL ;
211+ }
173212}
174213
175214digitalinout_result_t common_hal_digitalio_digitalinout_set_pull (
@@ -178,11 +217,31 @@ digitalinout_result_t common_hal_digitalio_digitalinout_set_pull(
178217 mxc_gpio_regs_t * port = gpio_ports [self -> pin -> port ];
179218 uint32_t mask = self -> pin -> mask ;
180219
181- // padctrl registers only work in input mode
220+ // GPIO4 handling
182221 if (self -> pin -> port == 4 ) {
183- MXC_MCR -> gpio4_ctrl &= ~(GPIO4_PULLDIS_MASK (mask ));
222+ switch (pull ) {
223+ case PULL_NONE :
224+ // disable pullup/pulldown
225+ MXC_MCR -> gpio4_ctrl |= GPIO4_PULLDIS_MASK (mask );
226+ break ;
227+ case PULL_UP :
228+ // enable pullup/pulldown (clear the mask)
229+ // then set output value to 1
230+ MXC_MCR -> gpio4_ctrl &= ~(GPIO4_PULLDIS_MASK (mask ));
231+ MXC_MCR -> gpio4_ctrl |= GPIO4_DATAOUT_MASK (mask );
232+ break ;
233+ case PULL_DOWN :
234+ // enable pullup/pulldown (clear the mask)
235+ // then clear output value to 0
236+ MXC_MCR -> gpio4_ctrl &= ~(GPIO4_PULLDIS_MASK (mask ));
237+ MXC_MCR -> gpio4_ctrl &= ~(GPIO4_DATAOUT_MASK (mask ));
238+ break ;
239+ default :
240+ break ;
241+ }
184242 return DIGITALINOUT_OK ;
185243 } else {
244+ // padctrl registers only work in input mode
186245 if ((mask & port -> en0 ) & (mask & ~(port -> outen ))) {
187246 // PULL_NONE, PULL_UP, or PULL_DOWN
188247 switch (pull ) {
@@ -193,10 +252,12 @@ digitalinout_result_t common_hal_digitalio_digitalinout_set_pull(
193252 case PULL_UP :
194253 port -> padctrl0 |= mask ;
195254 port -> padctrl1 &= ~(mask );
255+ port -> ps &= ~(mask );
196256 break ;
197257 case PULL_DOWN :
198- port -> padctrl0 &= ~( mask ) ;
258+ port -> padctrl0 &= ~mask ;
199259 port -> padctrl1 |= mask ;
260+ port -> ps &= ~mask ;
200261 break ;
201262 default :
202263 break ;
@@ -208,20 +269,37 @@ digitalinout_result_t common_hal_digitalio_digitalinout_set_pull(
208269 }
209270}
210271
211- /** FIXME: Account for GPIO4 handling here */
212272digitalio_pull_t common_hal_digitalio_digitalinout_get_pull (
213273 digitalio_digitalinout_obj_t * self ) {
214274
215- bool pin_padctrl0 = ( gpio_ports [self -> pin -> port ]-> padctrl0 ) & ( self -> pin -> mask ) ;
216- bool pin_padctrl1 = ( gpio_ports [ self -> pin -> port ] -> padctrl1 ) & ( self -> pin -> mask ) ;
275+ mxc_gpio_regs_t * port = gpio_ports [self -> pin -> port ];
276+ uint32_t mask = self -> pin -> mask ;
217277
218- if ((pin_padctrl0 ) && !(pin_padctrl1 )) {
219- return PULL_UP ;
220- } else if (!(pin_padctrl0 ) && pin_padctrl1 ) {
221- return PULL_DOWN ;
222- } else if (!(pin_padctrl0 ) && !(pin_padctrl1 )) {
223- return PULL_NONE ;
224- } else {
225- return PULL_NONE ;
278+ bool pin_padctrl0 = (port -> padctrl0 ) & (mask );
279+ bool pin_padctrl1 = (port -> padctrl1 ) & (mask );
280+
281+ if (self -> pin -> port == 4 ) {
282+ if (MXC_MCR -> gpio4_ctrl & GPIO4_PULLDIS_MASK (mask )) {
283+ return PULL_NONE ;
284+ }
285+ else {
286+ if (MXC_MCR -> gpio4_ctrl & GPIO4_DATAOUT_MASK (mask )) {
287+ return PULL_UP ;
288+ }
289+ else {
290+ return PULL_DOWN ;
291+ }
292+ }
293+ }
294+ else {
295+ if ((pin_padctrl0 ) && !(pin_padctrl1 )) {
296+ return PULL_UP ;
297+ } else if (!(pin_padctrl0 ) && pin_padctrl1 ) {
298+ return PULL_DOWN ;
299+ } else if (!(pin_padctrl0 ) && !(pin_padctrl1 )) {
300+ return PULL_NONE ;
301+ } else {
302+ return PULL_NONE ;
303+ }
226304 }
227305}
0 commit comments