1
0
Fork 0
aedrix-kernel/drivers/pi_mini_uart.c

123 lines
5.0 KiB
C

/*
Copyright (C) 2012, Aaron Lindsay <aaron@aclindsay.com>
This file is part of Aedrix.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <init.h>
#include <types.h>
#include <drivers/serial.h>
#define VC_MMU_IO_OFFSET(addr) (addr - 0x7E000000 + 0x20000000)
/* Auxiliary IO registers (includes both mini-UART and SPI) */
#define AUX_IRQ VC_MMU_IO_OFFSET(0x7E215000)
#define AUX_ENABLES VC_MMU_IO_OFFSET(0x7E215004)
#define AUX_MU_IO_REG VC_MMU_IO_OFFSET(0x7E215040)
#define AUX_MU_IER_REG VC_MMU_IO_OFFSET(0x7E215044)
#define AUX_MU_IIR_REG VC_MMU_IO_OFFSET(0x7E215048)
#define AUX_MU_LCR_REG VC_MMU_IO_OFFSET(0x7E21504C)
#define AUX_MU_MCR_REG VC_MMU_IO_OFFSET(0x7E215050)
#define AUX_MU_LSR_REG VC_MMU_IO_OFFSET(0x7E215054)
#define AUX_MU_MSR_REG VC_MMU_IO_OFFSET(0x7E215058)
#define AUX_MU_SCRATCH VC_MMU_IO_OFFSET(0x7E21505C)
#define AUX_MU_CNTL_REG VC_MMU_IO_OFFSET(0x7E215060)
#define AUX_MU_STAT_REG VC_MMU_IO_OFFSET(0x7E215064)
#define AUX_MU_BAUD_REG VC_MMU_IO_OFFSET(0x7E215068)
#define AUX_SPI0_CNTL0_REG VC_MMU_IO_OFFSET(0x7E215080)
#define AUX_SPI0_CNTL1_REG VC_MMU_IO_OFFSET(0x7E215084)
#define AUX_SPI0_STAT_REG VC_MMU_IO_OFFSET(0x7E215088)
#define AUX_SPI0_IO_REG VC_MMU_IO_OFFSET(0x7E215090)
#define AUX_SPI0_PEEK_REG VC_MMU_IO_OFFSET(0x7E215094)
#define AUX_SPI1_CNTL0_REG VC_MMU_IO_OFFSET(0x7E2150C0)
#define AUX_SPI1_CNTL1_REG VC_MMU_IO_OFFSET(0x7E2150C4)
#define AUX_SPI1_STAT_REG VC_MMU_IO_OFFSET(0x7E2150C8)
#define AUX_SPI1_IO_REG VC_MMU_IO_OFFSET(0x7E2150D0)
#define AUX_SPI1_PEEK_REG VC_MMU_IO_OFFSET(0x7E2150D4)
/* Constants for mini-UART operation */
#define MINI_UART_TX_EMPTY 0x20
/* General GPIO pin registers */
#define GPFSEL1 VC_MMU_IO_OFFSET(0x7E200004)
#define GPSET0 VC_MMU_IO_OFFSET(0x7E20001C)
#define GPCLR0 VC_MMU_IO_OFFSET(0x7E200028)
#define GPPUD VC_MMU_IO_OFFSET(0x7E200094)
#define GPPUDCLK0 VC_MMU_IO_OFFSET(0x7E200098)
void mini_uart_device_init() {
uint32 aux_enables;
uint32 gpfsel1;
unsigned int i;
/* Enable the mini-UART */
aux_enables = (*(uint32 *)AUX_ENABLES) & 0x7;
*(uint32 *)AUX_ENABLES = aux_enables | 0x1;
//TODO are these all necessary?
*(uint32 *)AUX_MU_IER_REG = 0; //disable interrupts
*(uint32 *)AUX_MU_CNTL_REG = 0; //disable RX and TX
*(uint32 *)AUX_MU_LCR_REG = 3; //set to 8-bit mode (though this only takes setting bit 0, no clue why bit 1 is set too)
*(uint32 *)AUX_MU_MCR_REG = 0; //"If clear the UART1_RTS line is high If set the UART1_RTS line is low"
*(uint32 *)AUX_MU_IER_REG = 0; //disable interrupts again?
*(uint32 *)AUX_MU_IIR_REG = 0xC6; //why are we setting the interrupt status - this looks like a read-only register?
//also, the manual says setting bits 2&3 simultaneously shouldn't be possible...
// baudrate = system_clock_freq / ( 8 * (baudrate_reg + 1))
// baudrate_reg = system_clock_freq / baudrate / 8 - 1
// baudrate_reg = 250000000 / 115200 / 8 - 1 = 270
*(uint32 *)AUX_MU_BAUD_REG = 270;
//set the GPIO pin to the alternate function for this UART
gpfsel1 = (*(uint32 *)GPFSEL1) & ~(7<<12); //get the GPIO Function Select 1 register, and keep all the bits except those that are for GPIO pin 14
gpfsel1 |= 2<<12; // set to 010 (GPIO Pin 14 takes alternate function 5)
*(uint32 *)GPFSEL1 = gpfsel1;
//Follow the procedure for change pull-up/pull-down status (see page 101 of the BCM2835 ARM Peripherals datasheet)
*(uint32 *)GPPUD = 0; //disable pull-up/pull-down for those pins which receive a write to their bit in GPPUDCLK0/1
for(i = 0; i < 150; i++) asm(""); //wait at least 150 cycles
*(uint32 *)GPPUDCLK0 = 1<<14; //"Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to modify" (in our case, this is 14)
for(i = 0; i < 150; i++) asm(""); //wait at least 150 cycles
// *(uint32 *)GPPUD = 0; //remove the signal (shouldn't really be necessary since it was 0 anyway in our case)
*(uint32 *)GPPUDCLK0 = 0; //clear the clock
*(uint32 *)AUX_MU_CNTL_REG = 2; //enable TX
}
int mini_uart_putc(char c) {
/* Wait until the serial buffer is empty */
while (!(*(volatile uint32*)AUX_MU_LSR_REG & MINI_UART_TX_EMPTY));
/* When it's empty, write our character */
*(volatile uint32*)AUX_MU_IO_REG = c;
return 0;
}
struct serial_dev mini_uart_dev = {
.name = "pi_mini_uart",
.putc = &mini_uart_putc
};
void mini_uart_init() {
mini_uart_device_init();
serial_register_device(&mini_uart_dev);
}
early_initcall(mini_uart_init);