Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
/* pinmux_board_arduino_due.c - Arduino Due pinmux driver */

/*
 * Copyright (c) 2016 Intel Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <device.h>
#include <init.h>
#include <nanokernel.h>
#include <pinmux.h>
#include <soc.h>
#include <sys_io.h>

#include "pinmux/pinmux.h"

/**
 * @brief Pinmux driver for Arduino due
 *
 * The SAM3X8E on Arduion Due has 4 PIO controllers. These controllers
 * are responsible for pin muxing, input/output, pull-up, etc.
 *
 * All PIO controller pins are flatten into sequentially incrementing
 * pin numbers:
 *   Pins  0 -  31 are for PIOA
 *   Pins 32 -  63 are for PIOB
 *   Pins 64 -  95 are for PIOC
 *   Pins 96 - 127 are for PIOD
 *
 * For all the pin descriptions, refer to the Atmel datasheet, and
 * the Arduino Due schematics.
 */

/*
 * These is the mapping from the board pins to PIO controllers.
 * This mapping is created from the Arduino Due schematics.
 * Refer to the official schematics for the actual mapping,
 * as the following may not be accurate.
 *
 * IO_0  : PA8
 * IO_1  : PA9
 * IO_2  : PB25
 * IO_3  : PC28
 * IO_4  : PA29
 * IO_5  : PC25
 * IO_6  : PC24
 * IO_7  : PC23
 *
 * IO_8  : PC22
 * IO_9  : PC21
 * IO_10 : PA28 and PC29
 * IO_11 : PD7
 * IO_12 : PD8
 * IO_13 : PB27
 * SDA1  : PA17
 * SCL1  : PA18
 *
 * IO_14 : PD4
 * IO_15 : PD5
 * IO_16 : PA13
 * IO_17 : PA12
 * IO_18 : PA11
 * IO_19 : PA10
 * IO_20 : PB12
 * IO_21 : PB13
 *
 * A_0   : PA16
 * A_1   : PA24
 * A_2   : PA23
 * A_3   : PA22
 * A_4   : PA6
 * A_5   : PA4
 * A_6   : PA3
 * A_7   : PA2
 *
 * A_8   : PB17
 * A_9   : PB18
 * A_10  : PB19
 * A_11  : PB20
 * DAC0  : PB15
 * DAC1  : PB16
 * CANRX : PA1
 * CANTX : PA0
 *
 * IO_22 : PB26
 * IO_23 : PA14
 * IO_24 : PA15
 * IO_25 : PD0
 * IO_26 : PD1
 * IO_27 : PD2
 * IO_28 : PD3
 * IO_29 : PD6
 * IO_30 : PD9
 * IO_31 : PA7
 * IO_32 : PD10
 * IO_33 : PC1
 * IO_34 : PC2
 * IO_35 : PC3
 * IO_36 : PC4
 * IO_37 : PC5
 * IO_38 : PC6
 * IO_39 : PC7
 * IO_40 : PC8
 * IO_41 : PC9
 * IO_42 : PA19
 * IO_43 : PA20
 * IO_44 : PC19
 * IO_45 : PC18
 * IO_46 : PC17
 * IO_47 : PC16
 * IO_48 : PC15
 * IO_49 : PC14
 * IO_50 : PC13
 * IO_51 : PC12
 */

#define N_PIOA 0
#define N_PIOB 1
#define N_PIOC 2
#define N_PIOD 3

/*
 * This function sets the default for the following:
 * - Pin mux (peripheral A or B)
 * - Set pin as input or output
 * - Enable pull-up for pins
 *
 * At boot, all pins are outputs with pull-up enabled, and are set to be
 * peripheral A (with value 0). So only the peripherals that need to be
 * set to B (value 1) will be declared explicitly below.
 *
 * Note that all pins are set to be controlled by the PIO controllers
 * by default. For peripherals to work (e.g. UART), the PIO has to
 * be disabled for that pin (e.g. UART to take over those pins).
 */
static void __pinmux_defaults(void)
{
	uint32_t ab_select[4];	/* A/B selection   */
	uint32_t output_en[4];	/* output enabled  */
	uint32_t pull_up[4];	/* pull-up enabled */
	uint32_t pio_ctrl[4];	/* PIO enable      */
	uint32_t tmp;

	/* Read defaults at boot, as the bootloader may have already
	 * configured some pins.
	 */
	ab_select[N_PIOA] = __PIOA->absr;
	ab_select[N_PIOB] = __PIOB->absr;
	ab_select[N_PIOC] = __PIOC->absr;
	ab_select[N_PIOD] = __PIOD->absr;

	output_en[N_PIOA] = __PIOA->osr;
	output_en[N_PIOB] = __PIOB->osr;
	output_en[N_PIOC] = __PIOC->osr;
	output_en[N_PIOD] = __PIOD->osr;

	pio_ctrl[N_PIOA] = __PIOA->psr;
	pio_ctrl[N_PIOB] = __PIOB->psr;
	pio_ctrl[N_PIOC] = __PIOC->psr;
	pio_ctrl[N_PIOD] = __PIOD->psr;

	/* value 1 means pull-up disabled, so need to invert */
	pull_up[N_PIOA] = ~(__PIOA->pusr);
	pull_up[N_PIOB] = ~(__PIOB->pusr);
	pull_up[N_PIOC] = ~(__PIOC->pusr);
	pull_up[N_PIOD] = ~(__PIOD->pusr);

	/*
	 * Now modify as we wish
	 */

	/* Make sure JTAG pins are used for JTAG */
	pio_ctrl[N_PIOB] &= ~(BIT(28) | BIT(29) | BIT(30) | BIT(31));

	/* UART console:
	 * IO_0: PA8 (RX)
	 * IO_1: PA9 (TX)
	 */
	pio_ctrl[N_PIOA] &= ~(BIT(8) | BIT(9));

	/* I2C pins on TWI controller #0
	 *
	 * SDA1: PA17
	 * SCL1: PA18
	 *
	 * Note that these need external pull-up resistors.
	 */
	pio_ctrl[N_PIOA] &= ~(BIT(17) | BIT(18));

	/* I2C pins on TWI controller #1
	 *
	 * IO_20: PB12 (SDA)
	 * IO_21: PB13 (SCL)
	 *
	 * Board already have pull-up resistors.
	 */
	pio_ctrl[N_PIOB] &= ~(BIT(12) | BIT(13));

	/*
	 * Setup ADC pins.
	 *
	 * Note that the ADC is considered extra function
	 * for the pins (other than A or B). This extra
	 * pin function is enabled by enabling the ADC
	 * controller. Therefore, the following code
	 * only sets these pins as input, with pull-up
	 * disabled. This does not detach the PIO
	 * controller from the pins so the peripherals
	 * won't take over.
	 *
	 * A_0 : PA16
	 * A_1 : PA24
	 * A_2 : PA23
	 * A_3 : PA22
	 * A_4 : PA6
	 * A_5 : PA4
	 * A_6 : PA3
	 * A_7 : PA2
	 *
	 * A_8 : PB17
	 * A_9 : PB18
	 * A_10: PB19
	 * A_11: PB20
	 */
	tmp = BIT(16) | BIT(24) | BIT(23) | BIT(22)
	      | BIT(6) | BIT(4) | BIT(3) | BIT(2);

	pio_ctrl[N_PIOA] |= tmp;
	output_en[N_PIOA] &= ~(tmp);
	pull_up[N_PIOA] &= ~(tmp);

	tmp = BIT(17) | BIT(18) | BIT(19) | BIT(20);

	pio_ctrl[N_PIOB] |= tmp;
	output_en[N_PIOB] &= ~(tmp);
	pull_up[N_PIOB] &= ~(tmp);

	/*
	 * Write modifications back to those registers
	 */

	__PIOA->absr = ab_select[N_PIOA];
	__PIOB->absr = ab_select[N_PIOB];
	__PIOC->absr = ab_select[N_PIOC];
	__PIOD->absr = ab_select[N_PIOD];

	/* set output enable */
	__PIOA->oer = output_en[N_PIOA];
	__PIOB->oer = output_en[N_PIOB];
	__PIOC->oer = output_en[N_PIOC];
	__PIOD->oer = output_en[N_PIOD];

	/* set output disable */
	__PIOA->odr = ~(output_en[N_PIOA]);
	__PIOB->odr = ~(output_en[N_PIOB]);
	__PIOC->odr = ~(output_en[N_PIOC]);
	__PIOD->odr = ~(output_en[N_PIOD]);

	/* set PIO enable */
	__PIOA->per = pio_ctrl[N_PIOA];
	__PIOB->per = pio_ctrl[N_PIOB];
	__PIOC->per = pio_ctrl[N_PIOC];
	__PIOD->per = pio_ctrl[N_PIOD];

	/* set PIO disable */
	__PIOA->pdr = ~(pio_ctrl[N_PIOA]);
	__PIOB->pdr = ~(pio_ctrl[N_PIOB]);
	__PIOC->pdr = ~(pio_ctrl[N_PIOC]);
	__PIOD->pdr = ~(pio_ctrl[N_PIOD]);

	/* set pull-up enable */
	__PIOA->puer = pull_up[N_PIOA];
	__PIOB->puer = pull_up[N_PIOB];
	__PIOC->puer = pull_up[N_PIOC];
	__PIOD->puer = pull_up[N_PIOD];

	/* set pull-up disable */
	__PIOA->pudr = ~(pull_up[N_PIOA]);
	__PIOB->pudr = ~(pull_up[N_PIOB]);
	__PIOC->pudr = ~(pull_up[N_PIOC]);
	__PIOD->pudr = ~(pull_up[N_PIOD]);
}

static int pinmux_init(struct device *port)
{
	ARG_UNUSED(port);

	__pinmux_defaults();

	return 0;
}

SYS_INIT(pinmux_init, PRIMARY, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);