/* * \brief Driver base for the PrimeCell UART PL011 Revision r1p3 * \author Martin stein * \date 2011-10-17 */ /* * Copyright (C) 2011-2012 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ #ifndef _BASE__INCLUDE__DRIVERS__UART__PL011_BASE_H_ #define _BASE__INCLUDE__DRIVERS__UART__PL011_BASE_H_ /* Genode includes */ #include namespace Genode { /** * Driver base for the PrimeCell UART PL011 Revision r1p3 */ class Pl011_base : Mmio { protected: enum { MAX_BAUD_RATE = 0xfffffff, ASCII_LINE_FEED = 10, ASCII_CARRIAGE_RETURN = 13, }; /** * Data register */ struct Uartdr : public Register<0x00, 16> { struct Data : Bitfield<0,8> { }; struct Fe : Bitfield<8,1> { }; struct Pe : Bitfield<9,1> { }; struct Be : Bitfield<10,1> { }; struct Oe : Bitfield<11,1> { }; }; /** * Flag register */ struct Uartfr : public Register<0x18, 16> { struct Cts : Bitfield<0,1> { }; struct Dsr : Bitfield<1,1> { }; struct Dcd : Bitfield<2,1> { }; struct Busy : Bitfield<3,1> { }; struct Rxfe : Bitfield<4,1> { }; struct Txff : Bitfield<5,1> { }; struct Rxff : Bitfield<6,1> { }; struct Txfe : Bitfield<7,1> { }; struct Ri : Bitfield<8,1> { }; }; /** * Integer baud rate register */ struct Uartibrd : public Register<0x24, 16> { struct Ibrd : Bitfield<0,15> { }; }; /** * Fractional Baud Rate Register */ struct Uartfbrd : public Register<0x28, 8> { struct Fbrd : Bitfield<0,6> { }; }; /** * Line Control Register */ struct Uartlcrh : public Register<0x2c, 16> { struct Wlen : Bitfield<5,2> { enum { WORD_LENGTH_8BITS = 3, WORD_LENGTH_7BITS = 2, WORD_LENGTH_6BITS = 1, WORD_LENGTH_5BITS = 0, }; }; }; /** * Control Register */ struct Uartcr : public Register<0x30, 16> { struct Uarten : Bitfield<0,1> { }; struct Txe : Bitfield<8,1> { }; struct Rxe : Bitfield<9,1> { }; }; /** * Interrupt Mask Set/Clear */ struct Uartimsc : public Register<0x38, 16> { struct Imsc : Bitfield<0,11> { }; }; /** * Idle until the device is ready for action */ void _wait_until_ready() { while (read()) ; } public: /** * Constructor * \param base Device MMIO base * \param clock Device reference clock frequency * \param baud_rate Targeted UART baud rate */ inline Pl011_base(addr_t const base, uint32_t const clock, uint32_t const baud_rate); /** * Send ASCII char 'c' over the UART interface */ inline void put_char(char const c); }; } Genode::Pl011_base::Pl011_base(addr_t const base, uint32_t const clock, uint32_t const baud_rate) : Mmio(base) { write(Uartcr::Uarten::bits(1) | Uartcr::Txe::bits(1) | Uartcr::Rxe::bits(1)); /** * We can't print an error or throw C++ exceptions * because we must expect both to be uninitialized yet, * so its better to hold the program counter in here for debugging */ if (baud_rate > MAX_BAUD_RATE) while(1) ; /** * Calculate fractional and integer part of baud rate * divisor to initialize IBRD and FBRD */ uint32_t const adjusted_br = baud_rate << 4; double const divisor = (double)clock / adjusted_br; Uartibrd::access_t const ibrd = (Uartibrd::access_t)divisor; Uartfbrd::access_t const fbrd = (Uartfbrd::access_t)(((divisor - ibrd) * 64) + 0.5); write(fbrd); write(ibrd); write(Uartlcrh::Wlen::WORD_LENGTH_8BITS); /** * Unmask all interrupts */ write(0); _wait_until_ready(); } void Genode::Pl011_base::put_char(char const c) { /* Wait as long as the transmission buffer is full */ while (read()) ; /* Auto complete new line commands */ if (c == ASCII_LINE_FEED) write(ASCII_CARRIAGE_RETURN); /* Transmit character */ write(c); _wait_until_ready(); } #endif /* _BASE__INCLUDE__DRIVERS__UART__PL011_BASE_H_ */