1 /**
2 * \file
3 *
4 * \brief SAM Pin Multiplexer Driver
5 *
6 * Copyright (C) 2012-2015 Atmel Corporation. All rights reserved.
7 *
8 * \asf_license_start
9 *
10 * \page License
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions are met:
14 *
15 * 1. Redistributions of source code must retain the above copyright notice,
16 * this list of conditions and the following disclaimer.
17 *
18 * 2. Redistributions in binary form must reproduce the above copyright notice,
19 * this list of conditions and the following disclaimer in the documentation
20 * and/or other materials provided with the distribution.
21 *
22 * 3. The name of Atmel may not be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * 4. This software may only be redistributed and used in connection with an
26 * Atmel microcontroller product.
27 *
28 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
29 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
31 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
32 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 * POSSIBILITY OF SUCH DAMAGE.
39 *
40 * \asf_license_stop
41 *
42 */
43 /*
44 * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
45 */
46 #include <pinmux.h>
47
48 /**
49 * \internal
50 * Writes out a given configuration of a Port pin configuration to the
51 * hardware module.
52 *
53 * \note If the pin direction is set as an output, the pull-up/pull-down input
54 * configuration setting is ignored.
55 *
56 * \param[in] port Base of the PORT module to configure
57 * \param[in] pin_mask Mask of the port pin to configure
58 * \param[in] config Configuration settings for the pin
59 */
_system_pinmux_config(PortGroup * const port,const uint32_t pin_mask,const struct system_pinmux_config * const config)60 static void _system_pinmux_config(
61 PortGroup *const port,
62 const uint32_t pin_mask,
63 const struct system_pinmux_config *const config)
64 {
65 Assert(port);
66 Assert(config);
67
68 /* Track the configuration bits into a temporary variable before writing */
69 uint32_t pin_cfg = 0;
70
71 /* Enabled powersave mode, don't create configuration */
72 if (!config->powersave) {
73 /* Enable the pin peripheral MUX flag if non-GPIO selected (pinmux will
74 * be written later) and store the new MUX mask */
75 if (config->mux_position != SYSTEM_PINMUX_GPIO) {
76 pin_cfg |= PORT_WRCONFIG_PMUXEN;
77 pin_cfg |= (config->mux_position << PORT_WRCONFIG_PMUX_Pos);
78 }
79
80 /* Check if the user has requested that the input buffer be enabled */
81 if ((config->direction == SYSTEM_PINMUX_PIN_DIR_INPUT) ||
82 (config->direction == SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK)) {
83 /* Enable input buffer flag */
84 pin_cfg |= PORT_WRCONFIG_INEN;
85
86 /* Enable pull-up/pull-down control flag if requested */
87 if (config->input_pull != SYSTEM_PINMUX_PIN_PULL_NONE) {
88 pin_cfg |= PORT_WRCONFIG_PULLEN;
89 }
90
91 /* Clear the port DIR bits to disable the output buffer */
92 port->DIRCLR.reg = pin_mask;
93 }
94
95 /* Check if the user has requested that the output buffer be enabled */
96 if ((config->direction == SYSTEM_PINMUX_PIN_DIR_OUTPUT) ||
97 (config->direction == SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK)) {
98 /* Cannot use a pull-up if the output driver is enabled,
99 * if requested the input buffer can only sample the current
100 * output state */
101 pin_cfg &= ~PORT_WRCONFIG_PULLEN;
102 }
103 } else {
104 port->DIRCLR.reg = pin_mask;
105 }
106
107 /* The Write Configuration register (WRCONFIG) requires the
108 * pins to to grouped into two 16-bit half-words - split them out here */
109 uint32_t lower_pin_mask = (pin_mask & 0xFFFF);
110 uint32_t upper_pin_mask = (pin_mask >> 16);
111
112 /* Configure the lower 16-bits of the port to the desired configuration,
113 * including the pin peripheral multiplexer just in case it is enabled */
114 port->WRCONFIG.reg
115 = (lower_pin_mask << PORT_WRCONFIG_PINMASK_Pos) |
116 pin_cfg | PORT_WRCONFIG_WRPMUX | PORT_WRCONFIG_WRPINCFG;
117
118 /* Configure the upper 16-bits of the port to the desired configuration,
119 * including the pin peripheral multiplexer just in case it is enabled */
120 port->WRCONFIG.reg
121 = (upper_pin_mask << PORT_WRCONFIG_PINMASK_Pos) |
122 pin_cfg | PORT_WRCONFIG_WRPMUX | PORT_WRCONFIG_WRPINCFG |
123 PORT_WRCONFIG_HWSEL;
124
125 if(!config->powersave) {
126 /* Set the pull-up state once the port pins are configured if one was
127 * requested and it does not violate the valid set of port
128 * configurations */
129 if (pin_cfg & PORT_WRCONFIG_PULLEN) {
130 /* Set the OUT register bits to enable the pull-up if requested,
131 * clear to enable pull-down */
132 if (config->input_pull == SYSTEM_PINMUX_PIN_PULL_UP) {
133 port->OUTSET.reg = pin_mask;
134 } else {
135 port->OUTCLR.reg = pin_mask;
136 }
137 }
138
139 /* Check if the user has requested that the output buffer be enabled */
140 if ((config->direction == SYSTEM_PINMUX_PIN_DIR_OUTPUT) ||
141 (config->direction == SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK)) {
142 /* Set the port DIR bits to enable the output buffer */
143 port->DIRSET.reg = pin_mask;
144 }
145 }
146 }
147
148 /**
149 * \brief Writes a Port pin configuration to the hardware module.
150 *
151 * Writes out a given configuration of a Port pin configuration to the hardware
152 * module.
153 *
154 * \note If the pin direction is set as an output, the pull-up/pull-down input
155 * configuration setting is ignored.
156 *
157 * \param[in] gpio_pin Index of the GPIO pin to configure
158 * \param[in] config Configuration settings for the pin
159 */
system_pinmux_pin_set_config(const uint8_t gpio_pin,const struct system_pinmux_config * const config)160 void system_pinmux_pin_set_config(
161 const uint8_t gpio_pin,
162 const struct system_pinmux_config *const config)
163 {
164 PortGroup *const port = system_pinmux_get_group_from_gpio_pin(gpio_pin);
165 uint32_t pin_mask = (1UL << (gpio_pin % 32));
166
167 _system_pinmux_config(port, pin_mask, config);
168 }
169
170 /**
171 * \brief Writes a Port pin group configuration to the hardware module.
172 *
173 * Writes out a given configuration of a Port pin group configuration to the
174 * hardware module.
175 *
176 * \note If the pin direction is set as an output, the pull-up/pull-down input
177 * configuration setting is ignored.
178 *
179 * \param[in] port Base of the PORT module to configure
180 * \param[in] mask Mask of the port pin(s) to configure
181 * \param[in] config Configuration settings for the pin
182 */
system_pinmux_group_set_config(PortGroup * const port,const uint32_t mask,const struct system_pinmux_config * const config)183 void system_pinmux_group_set_config(
184 PortGroup *const port,
185 const uint32_t mask,
186 const struct system_pinmux_config *const config)
187 {
188 Assert(port);
189
190 for (int i = 0; i < 32; i++) {
191 if (mask & (1UL << i)) {
192 _system_pinmux_config(port, (1UL << i), config);
193 }
194 }
195 }
196
197 /**
198 * \brief Configures the input sampling mode for a group of pins.
199 *
200 * Configures the input sampling mode for a group of pins, to
201 * control when the physical I/O pin value is sampled and
202 * stored inside the microcontroller.
203 *
204 * \param[in] port Base of the PORT module to configure
205 * \param[in] mask Mask of the port pin(s) to configure
206 * \param[in] mode New pin sampling mode to configure
207 */
system_pinmux_group_set_input_sample_mode(PortGroup * const port,const uint32_t mask,const enum system_pinmux_pin_sample mode)208 void system_pinmux_group_set_input_sample_mode(
209 PortGroup *const port,
210 const uint32_t mask,
211 const enum system_pinmux_pin_sample mode)
212 {
213 Assert(port);
214
215 if (mode == SYSTEM_PINMUX_PIN_SAMPLE_ONDEMAND) {
216 port->CTRL.reg |= mask;
217 } else {
218 port->CTRL.reg &= ~mask;
219 }
220 }
221
222 #ifdef FEATURE_SYSTEM_PINMUX_SLEWRATE_LIMITER
223 /**
224 * \brief Configures the output slew rate mode for a group of pins.
225 *
226 * Configures the output slew rate mode for a group of pins, to
227 * control the speed at which the physical output pin can react to
228 * logical changes of the I/O pin value.
229 *
230 * \param[in] port Base of the PORT module to configure
231 * \param[in] mask Mask of the port pin(s) to configure
232 * \param[in] mode New pin slew rate mode to configure
233 */
system_pinmux_group_set_output_slew_rate(PortGroup * const port,const uint32_t mask,const enum system_pinmux_pin_slew_rate mode)234 void system_pinmux_group_set_output_slew_rate(
235 PortGroup *const port,
236 const uint32_t mask,
237 const enum system_pinmux_pin_slew_rate mode)
238 {
239 Assert(port);
240
241 for (int i = 0; i < 32; i++) {
242 if (mask & (1UL << i)) {
243 if (mode == SYSTEM_PINMUX_PIN_SLEW_RATE_LIMITED) {
244 port->PINCFG[i].reg |= PORT_PINCFG_SLEWLIM;
245 } else {
246 port->PINCFG[i].reg &= ~PORT_PINCFG_SLEWLIM;
247 }
248 }
249 }
250 }
251 #endif
252
253 #ifdef FEATURE_SYSTEM_PINMUX_DRIVE_STRENGTH
254 /**
255 * \brief Configures the output driver strength mode for a group of pins.
256 *
257 * Configures the output drive strength for a group of pins, to
258 * control the amount of current the pad is able to sink/source.
259 *
260 * \param[in] port Base of the PORT module to configure
261 * \param[in] mask Mask of the port pin(s) to configure
262 * \param[in] mode New output driver strength mode to configure
263 */
system_pinmux_group_set_output_strength(PortGroup * const port,const uint32_t mask,const enum system_pinmux_pin_strength mode)264 void system_pinmux_group_set_output_strength(
265 PortGroup *const port,
266 const uint32_t mask,
267 const enum system_pinmux_pin_strength mode)
268 {
269 Assert(port);
270
271 for (int i = 0; i < 32; i++) {
272 if (mask & (1UL << i)) {
273 if (mode == SYSTEM_PINMUX_PIN_STRENGTH_HIGH) {
274 port->PINCFG[i].reg |= PORT_PINCFG_DRVSTR;
275 } else {
276 port->PINCFG[i].reg &= ~PORT_PINCFG_DRVSTR;
277 }
278 }
279 }
280 }
281 #endif
282
283 #ifdef FEATURE_SYSTEM_PINMUX_OPEN_DRAIN
284 /**
285 * \brief Configures the output driver mode for a group of pins.
286 *
287 * Configures the output driver mode for a group of pins, to
288 * control the pad behavior.
289 *
290 * \param[in] port Base of the PORT module to configure
291 * \param[in] mask Mask of the port pin(s) to configure
292 * \param[in] mode New pad output driver mode to configure
293 */
system_pinmux_group_set_output_drive(PortGroup * const port,const uint32_t mask,const enum system_pinmux_pin_drive mode)294 void system_pinmux_group_set_output_drive(
295 PortGroup *const port,
296 const uint32_t mask,
297 const enum system_pinmux_pin_drive mode)
298 {
299 Assert(port);
300
301 for (int i = 0; i < 32; i++) {
302 if (mask & (1UL << i)) {
303 if (mode == SYSTEM_PINMUX_PIN_DRIVE_OPEN_DRAIN) {
304 port->PINCFG[i].reg |= PORT_PINCFG_ODRAIN;
305 } else {
306 port->PINCFG[i].reg &= ~PORT_PINCFG_ODRAIN;
307 }
308 }
309 }
310 }
311 #endif
312