Aaron Lindsay
9d86813d8c
Create simple serial subsystem which makes use of initcalls, and convert existing serial drivers to its use.
122 lines
4.9 KiB
C
122 lines
4.9 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 = {
|
|
.putc = &mini_uart_putc
|
|
};
|
|
|
|
void mini_uart_init() {
|
|
mini_uart_device_init();
|
|
serial_register_device(&mini_uart_dev);
|
|
}
|
|
|
|
early_initcall(mini_uart_init);
|