From a0529ce2a9c1e239da4efe88bb266cf9ef20048c Mon Sep 17 00:00:00 2001 From: Lionel Flandrin Date: Sun, 12 Oct 2014 01:40:05 +0200 Subject: [PATCH] Basic pin control for tm4c123gh6pm. Can light up a LED! --- apps/app_blink_tm4c123gh6pm.rs | 37 +++++- src/platformtree/builder/mcu.rs | 4 +- src/platformtree/platformtree.rs | 1 + src/zinc/hal/isr.rs | 4 + src/zinc/hal/lpc17xx/pin.rs | 1 + src/zinc/hal/lpc17xx/pin_pt.rs | 4 +- src/zinc/hal/mod.rs | 1 + src/zinc/hal/tm4c123gh6pm/io.rs | 52 ++++++++ src/zinc/hal/tm4c123gh6pm/isr.rs | 147 ++++++++++++++++++++++ src/zinc/hal/tm4c123gh6pm/layout.ld | 3 +- src/zinc/hal/tm4c123gh6pm/mod.rs | 8 ++ src/zinc/hal/tm4c123gh6pm/pin.rs | 145 +++++++++++++++++++++ src/zinc/hal/tm4c123gh6pm/pin_pt.rs | 76 +++++++++++ src/zinc/hal/tm4c123gh6pm/platformtree.rs | 46 +++++++ src/zinc/hal/tm4c123gh6pm/sysctl.rs | 74 +++++++++++ 15 files changed, 595 insertions(+), 8 deletions(-) create mode 100644 src/zinc/hal/tm4c123gh6pm/io.rs create mode 100644 src/zinc/hal/tm4c123gh6pm/isr.rs create mode 100644 src/zinc/hal/tm4c123gh6pm/mod.rs create mode 100644 src/zinc/hal/tm4c123gh6pm/pin.rs create mode 100644 src/zinc/hal/tm4c123gh6pm/pin_pt.rs create mode 100644 src/zinc/hal/tm4c123gh6pm/platformtree.rs create mode 100644 src/zinc/hal/tm4c123gh6pm/sysctl.rs diff --git a/apps/app_blink_tm4c123gh6pm.rs b/apps/app_blink_tm4c123gh6pm.rs index 2cd6c16c..0f92cc75 100644 --- a/apps/app_blink_tm4c123gh6pm.rs +++ b/apps/app_blink_tm4c123gh6pm.rs @@ -4,10 +4,39 @@ extern crate core; extern crate zinc; +#[phase(plugin)] extern crate macro_platformtree; + +platformtree!( + tm4c123gh6pm@mcu { + clock { + source = "MOSC"; + /* Y2 16Mhz oscillator on launchpad board */ + source_frequency = 16_000_000; + } + + gpio { + PortF { + led1@1 { direction = "out"; } + led2@2 { direction = "out"; } + } + } + } + + os { + single_task { + loop = "run"; + args { + led1 = &led1; + led2 = &led2; + } + } + } +) -#[no_mangle] #[no_split_stack] -#[allow(unused_variable)] -#[allow(dead_code)] -pub unsafe fn main() { +pub fn run(args: &pt::run_args) { + use zinc::hal::pin::GPIO; + + args.led1.set_high(); + args.led2.set_high(); } diff --git a/src/platformtree/builder/mcu.rs b/src/platformtree/builder/mcu.rs index 97e1c9b2..db8541d5 100644 --- a/src/platformtree/builder/mcu.rs +++ b/src/platformtree/builder/mcu.rs @@ -17,6 +17,7 @@ use std::rc::Rc; use syntax::ext::base::ExtCtxt; use lpc17xx_pt; +use tm4c123gh6pm_pt; use node; use super::Builder; @@ -25,7 +26,8 @@ pub fn attach(builder: &mut Builder, cx: &mut ExtCtxt, node: Rc) { match node.name { Some(ref name) => { match name.as_slice() { - "lpc17xx" => lpc17xx_pt::attach(builder, cx, node.clone()), + "lpc17xx" => lpc17xx_pt::attach(builder, cx, node.clone()), + "tm4c123gh6pm" => tm4c123gh6pm_pt::attach(builder, cx, node.clone()), _ => node.materializer.set(Some(fail_build_mcu)), } }, diff --git a/src/platformtree/platformtree.rs b/src/platformtree/platformtree.rs index 8fec8064..57a4ed9f 100644 --- a/src/platformtree/platformtree.rs +++ b/src/platformtree/platformtree.rs @@ -28,6 +28,7 @@ pub mod node; pub mod parser; #[path="../zinc/hal/lpc17xx/platformtree.rs"] mod lpc17xx_pt; +#[path="../zinc/hal/tm4c123gh6pm/platformtree.rs"] mod tm4c123gh6pm_pt; #[path="../zinc/drivers/drivers_pt.rs"] mod drivers_pt; #[cfg(test)] mod test_helpers; diff --git a/src/zinc/hal/isr.rs b/src/zinc/hal/isr.rs index 0817fe41..3cb09baa 100644 --- a/src/zinc/hal/isr.rs +++ b/src/zinc/hal/isr.rs @@ -31,4 +31,8 @@ extern crate core; #[cfg(mcu_k20)] #[path="k20/isr.rs"] pub mod isr_k20; +#[cfg(mcu_tm4c123gh6pm)] +#[path="tm4c123gh6pm/isr.rs"] pub mod isr_tm4c123gh6pm; + + #[path="../util/lang_items.rs"] mod lang_items; diff --git a/src/zinc/hal/lpc17xx/pin.rs b/src/zinc/hal/lpc17xx/pin.rs index 286aa736..0cb95dcf 100644 --- a/src/zinc/hal/lpc17xx/pin.rs +++ b/src/zinc/hal/lpc17xx/pin.rs @@ -61,6 +61,7 @@ impl Pin { port: port, pin: pin_index, }; + pin.setup_regs(function, gpiodir); pin diff --git a/src/zinc/hal/lpc17xx/pin_pt.rs b/src/zinc/hal/lpc17xx/pin_pt.rs index d495020c..794c2cbc 100644 --- a/src/zinc/hal/lpc17xx/pin_pt.rs +++ b/src/zinc/hal/lpc17xx/pin_pt.rs @@ -44,7 +44,7 @@ fn build_pin(builder: &mut Builder, cx: &mut ExtCtxt, node: Rc) { 0...4 => port_path, other => { cx.parse_sess().span_diagnostic.span_err(port_node.path_span, - format!("unknown port `{}`, allowed values: 0..4", + format!("unknown port `{}`, allowed values: 0...4", other).as_slice()); return; } @@ -78,7 +78,7 @@ fn build_pin(builder: &mut Builder, cx: &mut ExtCtxt, node: Rc) { 0...31 => &node.path, other => { cx.parse_sess().span_diagnostic.span_err(node.path_span, - format!("unknown pin `{}`, allowed values: 0..31", + format!("unknown pin `{}`, allowed values: 0...31", other).as_slice()); return; } diff --git a/src/zinc/hal/mod.rs b/src/zinc/hal/mod.rs index 66fb9e4c..916d1c8b 100644 --- a/src/zinc/hal/mod.rs +++ b/src/zinc/hal/mod.rs @@ -25,6 +25,7 @@ and each such struct has a `setup()` method that configures the hardware pub mod lpc17xx; pub mod stm32f4; pub mod k20; +pub mod tm4c123gh6pm; mod cortex_common; pub mod cortex_m3; diff --git a/src/zinc/hal/tm4c123gh6pm/io.rs b/src/zinc/hal/tm4c123gh6pm/io.rs new file mode 100644 index 00000000..ccd62ebc --- /dev/null +++ b/src/zinc/hal/tm4c123gh6pm/io.rs @@ -0,0 +1,52 @@ +//! Custom register access interface + +use util::volatile_cell::VolatileCell; +use core::intrinsics::{volatile_load, volatile_store}; + +/// Hardware register interface +pub struct Reg { + /// Register address + addr: u32, +} + +impl Reg { + /// create a new Reg from a 32bit register address + pub fn new(addr: u32) -> Reg { + Reg { addr: addr } + } + + /// Write to a 32bit register + #[inline] + pub fn write32(&self, val: u32) { + unsafe { + let r = self.addr as *mut u32; + volatile_store(r, val); + } + } + + /// Read from a 32bit register + #[inline] + pub fn read32(&self) -> u32 { + unsafe { + let r = self.addr as *const u32; + volatile_load(r) + } + } + + /// Write single bit to a register using hardware bitbanding + #[inline] + pub fn bitband_write(&self, bit: u8, set: bool) { + /* bitband offset */ + let mut bitband = (self.addr & 0xf0000000) | 0x02000000; + + /* register offset */ + bitband |= (self.addr & 0x00fffff) << 5; + /* bit offset */ + bitband |= (bit as u32) << 2; + + unsafe { + let r = bitband as *mut u32; + volatile_store(r, set as u32); + } + } +} diff --git a/src/zinc/hal/tm4c123gh6pm/isr.rs b/src/zinc/hal/tm4c123gh6pm/isr.rs new file mode 100644 index 00000000..e4f3a5d6 --- /dev/null +++ b/src/zinc/hal/tm4c123gh6pm/isr.rs @@ -0,0 +1,147 @@ +use core::option::{Option, None}; + +static ISRCount: uint = 139; + +#[link_section=".isr_vector_nvic"] +#[no_mangle] +pub static NVIC_VECTOR: [Option, ..ISRCount] = [ + None, // GPIO Port A + None, // GPIO Port B + None, // GPIO Port C + None, // GPIO Port D + None, // GPIO Port E + None, // UART0 Rx and Tx + None, // UART1 Rx and Tx + None, // SSI0 Rx and Tx + None, // I2C0 Master and Slave + None, // PWM Fault + None, // PWM Generator 0 + None, // PWM Generator 1 + None, // PWM Generator 2 + None, // Quadrature Encoder 0 + None, // ADC Sequence 0 + None, // ADC Sequence 1 + None, // ADC Sequence 2 + None, // ADC Sequence 3 + None, // Watchdog timer + None, // Timer 0 subtimer A + None, // Timer 0 subtimer B + None, // Timer 1 subtimer A + None, // Timer 1 subtimer B + None, // Timer 2 subtimer A + None, // Timer 2 subtimer B + None, // Analog Comparator 0 + None, // Analog Comparator 1 + None, // Analog Comparator 2 + None, // System Control (PLL, OSC, BO) + None, // FLASH Control + None, // GPIO Port F + None, // GPIO Port G + None, // GPIO Port H + None, // UART2 Rx and Tx + None, // SSI1 Rx and Tx + None, // Timer 3 subtimer A + None, // Timer 3 subtimer B + None, // I2C1 Master and Slave + None, // Quadrature Encoder 1 + None, // CAN0 + None, // CAN1 + None, // Reserved + None, // Reserved + None, // Hibernate + None, // USB0 + None, // PWM Generator 3 + None, // uDMA Software Transfer + None, // uDMA Error + None, // ADC1 Sequence 0 + None, // ADC1 Sequence 1 + None, // ADC1 Sequence 2 + None, // ADC1 Sequence 3 + None, // Reserved + None, // Reserved + None, // GPIO Port J + None, // GPIO Port K + None, // GPIO Port L + None, // SSI2 Rx and Tx + None, // SSI3 Rx and Tx + None, // UART3 Rx and Tx + None, // UART4 Rx and Tx + None, // UART5 Rx and Tx + None, // UART6 Rx and Tx + None, // UART7 Rx and Tx + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // I2C2 Master and Slave + None, // I2C3 Master and Slave + None, // Timer 4 subtimer A + None, // Timer 4 subtimer B + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Reserved + None, // Timer 5 subtimer A + None, // Timer 5 subtimer B + None, // Wide Timer 0 subtimer A + None, // Wide Timer 0 subtimer B + None, // Wide Timer 1 subtimer A + None, // Wide Timer 1 subtimer B + None, // Wide Timer 2 subtimer A + None, // Wide Timer 2 subtimer B + None, // Wide Timer 3 subtimer A + None, // Wide Timer 3 subtimer B + None, // Wide Timer 4 subtimer A + None, // Wide Timer 4 subtimer B + None, // Wide Timer 5 subtimer A + None, // Wide Timer 5 subtimer B + None, // FPU + None, // Reserved + None, // Reserved + None, // I2C4 Master and Slave + None, // I2C5 Master and Slave + None, // GPIO Port M + None, // GPIO Port N + None, // Quadrature Encoder 2 + None, // Reserved + None, // Reserved + None, // GPIO Port P (Summary or P0) + None, // GPIO Port P1 + None, // GPIO Port P2 + None, // GPIO Port P3 + None, // GPIO Port P4 + None, // GPIO Port P5 + None, // GPIO Port P6 + None, // GPIO Port P7 + None, // GPIO Port Q (Summary or Q0) + None, // GPIO Port Q1 + None, // GPIO Port Q2 + None, // GPIO Port Q3 + None, // GPIO Port Q4 + None, // GPIO Port Q5 + None, // GPIO Port Q6 + None, // GPIO Port Q7 + None, // GPIO Port R + None, // GPIO Port S + None, // PWM 1 Generator 0 + None, // PWM 1 Generator 1 + None, // PWM 1 Generator 2 + None, // PWM 1 Generator 3 + None, // PWM 1 Fault*/ +]; diff --git a/src/zinc/hal/tm4c123gh6pm/layout.ld b/src/zinc/hal/tm4c123gh6pm/layout.ld index dccce5e5..e4b85399 100644 --- a/src/zinc/hal/tm4c123gh6pm/layout.ld +++ b/src/zinc/hal/tm4c123gh6pm/layout.ld @@ -1,4 +1,3 @@ -__STACK_BASE = 0x10002000; _boot_checksum = 0; /* TODO(farcaller): extract this to lpc code only */ _data_load = LOADADDR(.data); @@ -12,6 +11,8 @@ MEMORY ram(WAIL) : ORIGIN = 0x20000000, LENGTH = 0x8000 /* 32KB SRAM */ } +__STACK_BASE = ORIGIN(ram) + LENGTH(ram); + REGION_ALIAS("vectors", rom); INCLUDE ./src/zinc/hal/layout_common.ld diff --git a/src/zinc/hal/tm4c123gh6pm/mod.rs b/src/zinc/hal/tm4c123gh6pm/mod.rs new file mode 100644 index 00000000..58973b1e --- /dev/null +++ b/src/zinc/hal/tm4c123gh6pm/mod.rs @@ -0,0 +1,8 @@ +//! HAL for TI TM4C123GH6PM +//! This MCU is used on the TI stellaris and Tiva C launchpad development boards. + +pub mod io; +pub mod sysctl; +pub mod pin; + +#[path="../../util/ioreg.rs"] mod util; diff --git a/src/zinc/hal/tm4c123gh6pm/pin.rs b/src/zinc/hal/tm4c123gh6pm/pin.rs new file mode 100644 index 00000000..20e878c8 --- /dev/null +++ b/src/zinc/hal/tm4c123gh6pm/pin.rs @@ -0,0 +1,145 @@ +//! Pin configuration +//! Allows GPIO configuration +//! Pin muxing not implemented yet. + +use hal::pin::{GPIO, GPIODirection, In, Out, GPIOLevel, High, Low}; +use hal::tm4c123gh6pm::sysctl; +use hal::tm4c123gh6pm::io::Reg; + +/// The pins are accessed through ports. Each port has 8 pins and are identified +/// by a letter (PortA, PortB, etc...) +#[allow(missing_doc)] +pub enum PortID { + PortA, + PortB, + PortC, + PortD, + PortE, + PortF, +} + +/// Structure describing a single GPIO port. Each port contains 8 pins. +struct Port { + /// Base address of the port + base: u32, +} + +/// Structure describing a single HW pin +pub struct Pin { + /// Port containing this pin + port: Port, + /// Pin index in the port + index: u8, +} + +impl Pin { + /// Create and configure a Pin + pub fn new(pid: PortID, + pin_index: u8, + dir: GPIODirection) -> Pin { + + // Retrieve GPIO port peripheral to enable it + let (periph, port) = match pid { + PortA => (sysctl::periph::gpio::PORT_A, PORT_A), + PortB => (sysctl::periph::gpio::PORT_B, PORT_B), + PortC => (sysctl::periph::gpio::PORT_C, PORT_C), + PortD => (sysctl::periph::gpio::PORT_D, PORT_D), + PortE => (sysctl::periph::gpio::PORT_E, PORT_E), + PortF => (sysctl::periph::gpio::PORT_F, PORT_F), + }; + + periph.ensure_enabled(); + + let pin = Pin { port: port, index: pin_index }; + + pin.configure(dir); + + pin + } + + /// Configure GPIO pin + fn configure(&self, dir: GPIODirection) { + // Disable the GPIO during reconfig + let den = Reg::new(self.port.base + DEN); + den.bitband_write(self.index, false); + + self.set_direction(dir); + + // Configure the "alternate function". 0 means GPIO, 1 means the port is + // driven by another peripheral. + let afsel = Reg::new(self.port.base + AFSEL); + afsel.bitband_write(self.index, false); + + // We can chose to drive each GPIO at either 2, 4 or 8mA. Default to 2mA for + // now. + let drive = Reg::new(self.port.base + DR2R); + drive.bitband_write(self.index, true); + + // XXX TODO: configure open drain/pull up/pull down/slew rate if necessary + + // Enable GPIO + den.bitband_write(self.index, true); + } + + /// Returns a register containing the address to read and write the level of a + /// specific GPIO pin. + /// + /// Bits [9:2] of the address are a mask to address only specific pins in a + /// port. + fn data_reg(&self) -> Reg { + let off: u32 = 1u32 << ((self.index as uint) + 2); + + Reg::new(self.port.base + DATA + off) + } +} + +impl GPIO for Pin { + /// Sets output GPIO value to high. + fn set_high(&self) { + let r = self.data_reg(); + + r.write32(0xff); + } + + /// Sets output GPIO value to low. + fn set_low(&self) { + let r = self.data_reg(); + + r.write32(0x00); + } + + /// Returns input GPIO level. + fn level(&self) -> GPIOLevel { + let r = self.data_reg(); + + if r.read32() == 0 { + Low + } else { + High + } + } + + /// Sets output GPIO direction. + fn set_direction(&self, dir: GPIODirection) { + let reg = Reg::new(self.port.base + DIR); + reg.bitband_write(self.index, + match dir { + In => false, + Out => true, + }); + } +} + +static PORT_A: Port = Port { base: 0x40004000 }; +static PORT_B: Port = Port { base: 0x40005000 }; +static PORT_C: Port = Port { base: 0x40006000 }; +static PORT_D: Port = Port { base: 0x40007000 }; +static PORT_E: Port = Port { base: 0x40024000 }; +static PORT_F: Port = Port { base: 0x40025000 }; + +// Register offsets from port base +static DATA : u32 = 0x000; +static DIR : u32 = 0x400; +static AFSEL : u32 = 0x420; +static DR2R : u32 = 0x500; +static DEN : u32 = 0x51C; diff --git a/src/zinc/hal/tm4c123gh6pm/pin_pt.rs b/src/zinc/hal/tm4c123gh6pm/pin_pt.rs new file mode 100644 index 00000000..28f49ed4 --- /dev/null +++ b/src/zinc/hal/tm4c123gh6pm/pin_pt.rs @@ -0,0 +1,76 @@ +use std::rc::Rc; +use syntax::ext::base::ExtCtxt; + +use builder::{Builder, TokenString, add_node_dependency}; +use node; + +pub fn attach(builder: &mut Builder, _: &mut ExtCtxt, node: Rc) { + node.materializer.set(Some(verify)); + for port_node in node.subnodes().iter() { + port_node.materializer.set(Some(verify)); + add_node_dependency(&node, port_node); + for pin_node in port_node.subnodes().iter() { + pin_node.materializer.set(Some(build_pin)); + add_node_dependency(port_node, pin_node); + super::add_node_dependency_on_clock(builder, pin_node); + } + } +} + +pub fn verify(_: &mut Builder, cx: &mut ExtCtxt, node: Rc) { + node.expect_no_attributes(cx); +} + +fn build_pin(builder: &mut Builder, cx: &mut ExtCtxt, node: Rc) { + let port_node = node.parent.clone().unwrap().upgrade().unwrap(); + let ref port_path = port_node.path; + + let error = | err: &str | { + cx.parse_sess().span_diagnostic.span_err(port_node.path_span, err); + }; + + let port = TokenString(port_path.clone()); + + if node.name.is_none() { + error("pin node must have a name"); + return; + } + + let direction_str = + match node.get_string_attr("direction").unwrap().as_slice() { + "out" => "zinc::hal::pin::Out", + "in" => "zinc::hal::pin::In", + bad => { + let attr = node.get_attr("direction"); + error(format!("unknown direction `{}`, allowed values: `in`, `out`", + bad).as_slice()); + return; + } + }; + + let direction = TokenString(direction_str.to_string()); + + let pin_str = match from_str::(node.path.as_slice()).unwrap() { + 0 ...7 => &node.path, + other => { + cx.parse_sess().span_diagnostic.span_err(node.path_span, + format!("unknown pin `{}`, allowed values: 0...7", + other).as_slice()); + return; + } + }; + + let pin = TokenString(format!("{}u8", pin_str)); + let pin_name = TokenString(node.name.clone().unwrap()); + + node.set_type_name("zinc::hal::tm4c123gh6pm::pin::Pin".to_string()); + + /* XXX: need to handle pin muxing */ + let st = quote_stmt!(&*cx, + let $pin_name = zinc::hal::tm4c123gh6pm::pin::Pin::new( + zinc::hal::tm4c123gh6pm::pin::$port, + $pin, + $direction); + ); + builder.add_main_statement(st); +} diff --git a/src/zinc/hal/tm4c123gh6pm/platformtree.rs b/src/zinc/hal/tm4c123gh6pm/platformtree.rs new file mode 100644 index 00000000..41e9a9dd --- /dev/null +++ b/src/zinc/hal/tm4c123gh6pm/platformtree.rs @@ -0,0 +1,46 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Lionel Flandrin +// +// 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. + +use std::rc::Rc; +use syntax::ext::base::ExtCtxt; + +use builder::{Builder, add_node_dependency}; +use node; + +mod pin_pt; + +pub fn attach(builder: &mut Builder, cx: &mut ExtCtxt, node: Rc) { + node.materializer.set(Some(verify)); + for sub in node.subnodes().iter() { + add_node_dependency(&node, sub); + + match sub.path.as_slice() { + "gpio" => pin_pt::attach(builder, cx, sub.clone()), + _ => (), + } + } +} + +fn verify(_: &mut Builder, cx: &mut ExtCtxt, node: Rc) { + node.expect_no_attributes(cx); + node.expect_subnodes(cx, ["clock", "gpio"]); +} + +pub fn add_node_dependency_on_clock(builder: &mut Builder, + node: &Rc) { + let mcu_node = builder.pt().get_by_path("mcu").unwrap(); + let clock_node = mcu_node.get_by_path("clock").unwrap(); + add_node_dependency(node, &clock_node); +} diff --git a/src/zinc/hal/tm4c123gh6pm/sysctl.rs b/src/zinc/hal/tm4c123gh6pm/sysctl.rs new file mode 100644 index 00000000..17b23d3c --- /dev/null +++ b/src/zinc/hal/tm4c123gh6pm/sysctl.rs @@ -0,0 +1,74 @@ +//! Low level system control (PLL, clock gating, ...) + +/// SysCtl base address +static BASE: u32 = 0x400FE000; + +pub mod periph { + //! peripheral system control + + use core::iter::range; + use hal::tm4c123gh6pm::io::Reg; + + /// Run mode clock gating control offset + static RMCGC_OFFSET: u32 = 0x600; + + /// Sysctl can reset/clock gate each module, as well as set various sleep and + /// deep-sleep mode behaviour. + pub struct Periph { + /// Hardware register offset for this peripheral class within a system + /// control block. + class: u8, + /// Bit offset within the class register for this particular peripheral + id : u8, + } + + impl Periph { + + /// Retrieve the clock gating control register address + fn clock_gating_reg(&self) -> Reg { + Reg::new(super::BASE + RMCGC_OFFSET + (self.class as u32)) + } + + /// Enable a peripheral + pub fn enable(&self) { + let cgr = self.clock_gating_reg(); + + // Enable peripheral clock + cgr.bitband_write(self.id, true); + + // The manual says we have to wait for 3 clock cycles before we can access + // the peripheral. Waiting for 3 NOPs don't seem to be enough on my board, + // maybe because we also have to take the bus write time into account or + // the CPU is more clever than I think. Anyway, looping 5 times seems to + // work + for _ in range(0u, 5) { + unsafe { + asm!("nop" :::: "volatile"); + } + } + } + + /// Check if the peripheral is enabled. If not, enable it. + pub fn ensure_enabled(&self) { + let cgr = self.clock_gating_reg(); + + if (cgr.read32() & (1 << (self.id as uint))) == 0 { + self.enable(); + } + } + } + + pub mod gpio { + //! GPIO system control peripherals. Split into ports of 8 GPIO each. + + static CLASS: u8 = 0x8; + + pub static PORT_A: super::Periph = super::Periph { class: CLASS, id: 0 }; + pub static PORT_B: super::Periph = super::Periph { class: CLASS, id: 1 }; + pub static PORT_C: super::Periph = super::Periph { class: CLASS, id: 2 }; + pub static PORT_D: super::Periph = super::Periph { class: CLASS, id: 3 }; + pub static PORT_E: super::Periph = super::Periph { class: CLASS, id: 4 }; + pub static PORT_F: super::Periph = super::Periph { class: CLASS, id: 5 }; + } + +}