| /* |
| * 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, uint8_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, uint8_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, uint8_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, uint8_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); |