439 lines
9.2 KiB
C
439 lines
9.2 KiB
C
/*
|
|
* 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 <nanokernel.h>
|
|
#include <board.h>
|
|
|
|
#include <device.h>
|
|
#include <init.h>
|
|
#include <pinmux.h>
|
|
|
|
#include <misc/util.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
|
|
*/
|
|
|
|
#ifndef CONFIG_PINMUX_DEV
|
|
#define PRINT(...) {; }
|
|
#else
|
|
#if defined(CONFIG_PRINTK)
|
|
#include <misc/printk.h>
|
|
#define PRINT printk
|
|
#elif defined(CONFIG_STDOUT_CONSOLE)
|
|
#define PRINT printf
|
|
#endif /* CONFIG_PRINTK */
|
|
#endif /*CONFIG_PINMUX_DEV */
|
|
|
|
static volatile struct __pio *_get_port(uint32_t pin)
|
|
{
|
|
uint32_t port_num = pin / 32;
|
|
|
|
switch (port_num) {
|
|
case 0:
|
|
return __PIOA;
|
|
case 1:
|
|
return __PIOB;
|
|
case 2:
|
|
return __PIOC;
|
|
case 3:
|
|
return __PIOD;
|
|
default:
|
|
/* return null if pin is outside range */
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_PINMUX_DEV
|
|
static uint32_t pinmux_set(struct device *dev, uint32_t pin, uint32_t func)
|
|
{
|
|
volatile struct __pio *port = _get_port(pin);
|
|
uint32_t tmp;
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
if (!port) {
|
|
return DEV_INVALID_CONF;
|
|
}
|
|
|
|
tmp = port->absr;
|
|
if (func) {
|
|
tmp |= (1 << (pin % 32));
|
|
} else {
|
|
tmp &= ~(1 << (pin % 32));
|
|
}
|
|
port->absr = tmp;
|
|
|
|
return DEV_OK;
|
|
}
|
|
|
|
static uint32_t pinmux_get(struct device *dev, uint32_t pin, uint32_t *func)
|
|
{
|
|
volatile struct __pio *port = _get_port(pin);
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
if (!port) {
|
|
return DEV_INVALID_CONF;
|
|
}
|
|
|
|
*func = (port->absr & (1 << (pin % 32))) ? 1 : 0;
|
|
|
|
return DEV_OK;
|
|
}
|
|
#else
|
|
static uint32_t pinmux_set(struct device *dev, uint32_t pin, uint32_t func)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
ARG_UNUSED(pin);
|
|
ARG_UNUSED(func);
|
|
|
|
PRINT("ERROR: %s is not enabled", __func__);
|
|
|
|
return DEV_NOT_CONFIG;
|
|
}
|
|
|
|
static uint32_t pinmux_get(struct device *dev, uint32_t pin, uint32_t *func)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
ARG_UNUSED(pin);
|
|
ARG_UNUSED(func);
|
|
|
|
PRINT("ERROR: %s is not enabled", __func__);
|
|
|
|
return DEV_NOT_CONFIG;
|
|
}
|
|
#endif /* CONFIG_PINMUX_DEV */
|
|
|
|
static uint32_t pinmux_pullup(struct device *dev, uint32_t pin, uint8_t func)
|
|
{
|
|
volatile struct __pio *port = _get_port(pin);
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
if (!port) {
|
|
return DEV_INVALID_CONF;
|
|
}
|
|
|
|
if (func) {
|
|
port->puer = (1 << (pin % 32));
|
|
} else {
|
|
port->pudr = (1 << (pin % 32));
|
|
}
|
|
|
|
return DEV_OK;
|
|
}
|
|
static uint32_t pinmux_input(struct device *dev, uint32_t pin, uint8_t func)
|
|
{
|
|
volatile struct __pio *port = _get_port(pin);
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
if (!port) {
|
|
return DEV_INVALID_CONF;
|
|
}
|
|
|
|
if (func) {
|
|
port->odr = (1 << (pin % 32));
|
|
} else {
|
|
port->oer = (1 << (pin % 32));
|
|
}
|
|
|
|
return DEV_OK;
|
|
}
|
|
|
|
|
|
#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 struct pinmux_driver_api api_funcs = {
|
|
.set = pinmux_set,
|
|
.get = pinmux_get,
|
|
.pullup = pinmux_pullup,
|
|
.input = pinmux_input
|
|
};
|
|
|
|
int pinmux_init(struct device *port)
|
|
{
|
|
port->driver_api = &api_funcs;
|
|
|
|
__pinmux_defaults();
|
|
|
|
return DEV_OK;
|
|
}
|
|
|
|
DEVICE_INIT(pmux, PINMUX_NAME, &pinmux_init, NULL, NULL,
|
|
PRIMARY, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|