Repository: tock/tock (language: rust)

Issue:
Support for VirtualSpiMasterDevice with Active-High Chip Select
Currently we assume that virtual SPI devices expect an active-low chip-select pin. While this is the de-facto standard, it is not universally the case (e.g. the LPM0130m126 TFT display expects an active-high chip-select).

The way our types are currently structured, there is no way to generally model this behavior. Specifically, the `SpiMaster` trait has an associated type, `ChipSelect`, which the chip-select gpio embedded inside a `VirtualSpiDevice` must be of that type and the `VirtualSpiDevice` and its Mux toggles this chip high/low when selecting the relevant device to talk to. Unfortunately, unless the hardware implementation uses a `ChipSelect` that _happens_ to be generic (e.g. the nordics and sam4l do this, but none others), there is no way to correctly "wrap" the chip select in a type that reverses the polarity.


Discussion / hints:
I think the right solution is retain `ChipSelect` specialization for the hardware and add a flag to individual `VirtualSpiDevice` that encodes the active direction of the chip-select.

Fix patch:
diff --git a/boards/apollo3/lora_things_plus/src/main.rs b/boards/apollo3/lora_things_plus/src/main.rs
index 5776586a88..96c69c9492 100644
--- a/boards/apollo3/lora_things_plus/src/main.rs
+++ b/boards/apollo3/lora_things_plus/src/main.rs
@@ -511,7 +511,9 @@ unsafe fn setup() -> (
     let external_spi_controller = components::spi::SpiSyscallComponent::new(
         board_kernel,
         external_mux_spi,
-        &peripherals.gpio_port[11], // A5
+        kernel::hil::spi::cs::IntoChipSelect::<_, kernel::hil::spi::cs::ActiveLow>::into_cs(
+            &peripherals.gpio_port[11], // A5
+        ),
         capsules_core::spi_controller::DRIVER_NUM,
     )
     .finalize(components::spi_syscall_component_static!(
@@ -526,7 +528,9 @@ unsafe fn setup() -> (
     let sx1262_spi_controller = components::spi::SpiSyscallComponent::new(
         board_kernel,
         sx1262_mux_spi,
-        &peripherals.gpio_port[36], // H6 - SX1262 Slave Select
+        kernel::hil::spi::cs::IntoChipSelect::<_, kernel::hil::spi::cs::ActiveLow>::into_cs(
+            &peripherals.gpio_port[36], // H6 - SX1262 Slave Select
+        ),
         LORA_SPI_DRIVER_NUM,
     )
     .finalize(components::spi_syscall_component_static!(
@@ -534,7 +538,12 @@ unsafe fn setup() -> (
     ));
     peripherals
         .iom3
-        .specify_chip_select(&peripherals.gpio_port[36])
+        .specify_chip_select(kernel::hil::spi::cs::IntoChipSelect::<
+            _,
+            kernel::hil::spi::cs::ActiveLow,
+        >::into_cs(
+            &peripherals.gpio_port[36], // H6 - SX1262 Slave Select
+        ))
         .unwrap();
 
     let sx1262_gpio = components::gpio::GpioComponent::new(
diff --git a/boards/apollo3/redboard_artemis_atp/src/main.rs b/boards/apollo3/redboard_artemis_atp/src/main.rs
index 47ecc7da74..59b052987b 100644
--- a/boards/apollo3/redboard_artemis_atp/src/main.rs
+++ b/boards/apollo3/redboard_artemis_atp/src/main.rs
@@ -326,7 +326,9 @@ unsafe fn setup() -> (
     let spi_controller = components::spi::SpiSyscallComponent::new(
         board_kernel,
         mux_spi,
-        &peripherals.gpio_port[13], // A13
+        kernel::hil::spi::cs::IntoChipSelect::<_, kernel::hil::spi::cs::ActiveLow>::into_cs(
+            &peripherals.gpio_port[13], // A13
+        ),
         capsules_core::spi_controller::DRIVER_NUM,
     )
     .finalize(components::spi_syscall_component_static!(
diff --git a/boards/apollo3/redboard_artemis_nano/src/main.rs b/boards/apollo3/redboard_artemis_nano/src/main.rs
index 7c97011013..8a1763a63c 100644
--- a/boards/apollo3/redboard_artemis_nano/src/main.rs
+++ b/boards/apollo3/redboard_artemis_nano/src/main.rs
@@ -350,7 +350,9 @@ unsafe fn setup() -> (
     let spi_controller = components::spi::SpiSyscallComponent::new(
         board_kernel,
         mux_spi,
-        &peripherals.gpio_port[35], // A14
+        kernel::hil::spi::cs::IntoChipSelect::<_, kernel::hil::spi::cs::ActiveLow>::into_cs(
+            &peripherals.gpio_port[35], // A14
+        ),
         capsules_core::spi_controller::DRIVER_NUM,
     )
     .finalize(components::spi_syscall_component_static!(
diff --git a/boards/clue_nrf52840/src/main.rs b/boards/clue_nrf52840/src/main.rs
index 10283610a9..eddf2abb07 100644
--- a/boards/clue_nrf52840/src/main.rs
+++ b/boards/clue_nrf52840/src/main.rs
@@ -19,6 +19,7 @@ use capsules_core::virtualizers::virtual_aes_ccm::MuxAES128CCM;
 
 use kernel::capabilities;
 use kernel::component::Component;
+use kernel::hil;
 use kernel::hil::buzzer::Buzzer;
 use kernel::hil::i2c::I2CMaster;
 use kernel::hil::led::LedHigh;
@@ -652,7 +653,9 @@ unsafe fn start() -> (
 
     let bus = components::bus::SpiMasterBusComponent::new(
         spi_mux,
-        &nrf52840_peripherals.gpio_port[ST7789H2_CS],
+        hil::spi::cs::IntoChipSelect::<_, hil::spi::cs::ActiveLow>::into_cs(
+            &nrf52840_peripherals.gpio_port[ST7789H2_CS],
+        ),
         20_000_000,
         kernel::hil::spi::ClockPhase::SampleLeading,
         kernel::hil::spi::ClockPolarity::IdleLow,
diff --git a/boards/components/src/fm25cl.rs b/boards/components/src/fm25cl.rs
index 6f0b72eb95..fd5a57f8f4 100644
--- a/boards/components/src/fm25cl.rs
+++ b/boards/components/src/fm25cl.rs
@@ -40,24 +40,32 @@ macro_rules! fm25cl_component_static {
     };};
 }
 
-pub struct Fm25clComponent<S: 'static + spi::SpiMaster<'static>> {
+pub struct Fm25clComponent<
+    S: 'static + spi::SpiMaster<'static>,
+    CS: spi::cs::IntoChipSelect<S::ChipSelect, spi::cs::ActiveLow>,
+> {
     spi_mux: &'static MuxSpiMaster<'static, S>,
-    chip_select: S::ChipSelect,
+    chip_select: CS,
 }
 
-impl<S: 'static + spi::SpiMaster<'static>> Fm25clComponent<S> {
-    pub fn new(
-        spi_mux: &'static MuxSpiMaster<'static, S>,
-        chip_select: S::ChipSelect,
-    ) -> Fm25clComponent<S> {
-        Fm25clComponent {
+impl<
+        S: 'static + spi::SpiMaster<'static>,
+        CS: spi::cs::IntoChipSelect<S::ChipSelect, spi::cs::ActiveLow>,
+    > Fm25clComponent<S, CS>
+{
+    pub fn new(spi_mux: &'static MuxSpiMaster<'static, S>, chip_select: CS) -> Self {
+        Self {
             spi_mux,
             chip_select,
         }
     }
 }
 
-impl<S: 'static + spi::SpiMaster<'static>> Component for Fm25clComponent<S> {
+impl<
+        S: 'static + spi::SpiMaster<'static>,
+        CS: spi::cs::IntoChipSelect<S::ChipSelect, spi::cs::ActiveLow>,
+    > Component for Fm25clComponent<S, CS>
+{
     type StaticInput = (
         &'static mut MaybeUninit<VirtualSpiMasterDevice<'static, S>>,
         &'static mut MaybeUninit<FM25CL<'static, VirtualSpiMasterDevice<'static, S>>>,
@@ -67,9 +75,10 @@ impl<S: 'static + spi::SpiMaster<'static>> Component for Fm25clComponent<S> {
     type Output = &'static FM25CL<'static, VirtualSpiMasterDevice<'static, S>>;
 
     fn finalize(self, static_buffer: Self::StaticInput) -> Self::Output {
-        let spi_device = static_buffer
-            .0
-            .write(VirtualSpiMasterDevice::new(self.spi_mux, self.chip_select));
+        let spi_device = static_buffer.0.write(VirtualSpiMasterDevice::new(
+            self.spi_mux,
+            self.chip_select.into_cs(),
+        ));
         spi_device.setup();
 
         let txbuffer = static_buffer.2.write([0; capsules_extra::fm25cl::BUF_LEN]);
diff --git a/boards/components/src/l3gd20.rs b/boards/components/src/l3gd20.rs
index 49180dedd9..642ab93a78 100644
--- a/boards/components/src/l3gd20.rs
+++ b/boards/components/src/l3gd20.rs
@@ -45,21 +45,28 @@ macro_rules! l3gd20_component_static {
 
 pub type L3gd20ComponentType<S> = capsules_extra::l3gd20::L3gd20Spi<'static, S>;
 
-pub struct L3gd20Component<S: 'static + spi::SpiMaster<'static>> {
+pub struct L3gd20Component<
+    S: 'static + spi::SpiMaster<'static>,
+    CS: spi::cs::IntoChipSelect<S::ChipSelect, spi::cs::ActiveLow>,
+> {
     spi_mux: &'static MuxSpiMaster<'static, S>,
-    chip_select: S::ChipSelect,
+    chip_select: CS,
     board_kernel: &'static kernel::Kernel,
     driver_num: usize,
 }
 
-impl<S: 'static + spi::SpiMaster<'static>> L3gd20Component<S> {
+impl<
+        S: 'static + spi::SpiMaster<'static>,
+        CS: spi::cs::IntoChipSelect<S::ChipSelect, spi::cs::ActiveLow>,
+    > L3gd20Component<S, CS>
+{
     pub fn new(
         spi_mux: &'static MuxSpiMaster<'static, S>,
-        chip_select: S::ChipSelect,
+        chip_select: CS,
         board_kernel: &'static kernel::Kernel,
         driver_num: usize,
-    ) -> L3gd20Component<S> {
-        L3gd20Component {
+    ) -> Self {
+        Self {
             spi_mux,
             chip_select,
             board_kernel,
@@ -68,7 +75,11 @@ impl<S: 'static + spi::SpiMaster<'static>> L3gd20Component<S> {
     }
 }
 
-impl<S: 'static + spi::SpiMaster<'static>> Component for L3gd20Component<S> {
+impl<
+        S: 'static + spi::SpiMaster<'static>,
+        CS: spi::cs::IntoChipSelect<S::ChipSelect, spi::cs::ActiveLow>,
+    > Component for L3gd20Component<S, CS>
+{
     type StaticInput = (
         &'static mut MaybeUninit<VirtualSpiMasterDevice<'static, S>>,
         &'static mut MaybeUninit<L3gd20Spi<'static, VirtualSpiMasterDevice<'static, S>>>,
@@ -81,9 +92,10 @@ impl<S: 'static + spi::SpiMaster<'static>> Component for L3gd20Component<S> {
         let grant_cap = create_capability!(capabilities::MemoryAllocationCapability);
         let grant = self.board_kernel.create_grant(self.driver_num, &grant_cap);
 
-        let spi_device = static_buffer
-            .0
-            .write(VirtualSpiMasterDevice::new(self.spi_mux, self.chip_select));
+        let spi_device = static_buffer.0.write(VirtualSpiMasterDevice::new(
+            self.spi_mux,
+            self.chip_select.into_cs(),
+        ));
         spi_device.setup();
 
         let txbuffer = static_buffer
diff --git a/boards/components/src/lpm013m126.rs b/boards/components/src/lpm013m126.rs
index 1fc893209a..f4fe13970b 100644
--- a/boards/components/src/lpm013m126.rs
+++ b/boards/components/src/lpm013m126.rs
@@ -40,57 +40,9 @@ use capsules_core::virtualizers::virtual_spi::{MuxSpiMaster, VirtualSpiMasterDev
 use capsules_extra::lpm013m126::Lpm013m126;
 use core::mem::MaybeUninit;
 use kernel::component::Component;
-use kernel::hil::gpio;
 use kernel::hil::spi::{SpiMaster, SpiMasterDevice};
 use kernel::hil::time::Alarm;
-
-/// CS is active high
-pub struct Inverted<'a, P: gpio::Pin>(pub &'a P);
-
-impl<'a, P: gpio::Pin> gpio::Configure for Inverted<'a, P> {
-    fn configuration(&self) -> gpio::Configuration {
-        self.0.configuration()
-    }
-    fn make_output(&self) -> gpio::Configuration {
-        self.0.make_output()
-    }
-    fn disable_output(&self) -> gpio::Configuration {
-        self.0.disable_output()
-    }
-    fn make_input(&self) -> gpio::Configuration {
-        self.0.make_input()
-    }
-    fn disable_input(&self) -> gpio::Configuration {
-        self.0.disable_input()
-    }
-    fn deactivate_to_low_power(&self) {
-        self.0.deactivate_to_low_power()
-    }
-    fn set_floating_state(&self, _: gpio::FloatingState) {
-        unimplemented!() // not sure what it looks like with inversion
-    }
-    fn floating_state(&self) -> gpio::FloatingState {
-        unimplemented!() // not sure what it looks like with inversion
-    }
-}
-
-impl<'a, P: gpio::Pin> gpio::Output for Inverted<'a, P> {
-    fn set(&self) {
-        self.0.clear()
-    }
-    fn clear(&self) {
-        self.0.set()
-    }
-    fn toggle(&self) -> bool {
-        self.0.toggle()
-    }
-}
-
-impl<'a, P: gpio::Pin> gpio::Input for Inverted<'a, P> {
-    fn read(&self) -> bool {
-        !self.0.read()
-    }
-}
+use kernel::hil::{self, gpio};
 
 /// Setup static space for the driver and its requirements.
 #[macro_export]
@@ -135,17 +87,17 @@ where
     P: 'static + gpio::Pin,
     S: 'static + SpiMaster<'static>,
 {
-    pub fn new(
+    pub fn new<I: kernel::hil::spi::cs::IntoChipSelect<S::ChipSelect, hil::spi::cs::ActiveHigh>>(
         spi: &'static MuxSpiMaster<'static, S>,
 
-        chip_select: S::ChipSelect,
+        chip_select: I,
         disp: &'static P,
         extcomin: &'static P,
         alarm_mux: &'static MuxAlarm<'static, A>,
     ) -> Self {
         Self {
             spi,
-            chip_select,
+            chip_select: chip_select.into_cs(),
             disp,
             extcomin,
             alarm_mux,
diff --git a/boards/components/src/mx25r6435f.rs b/boards/components/src/mx25r6435f.rs
index fc52d8d848..452a4d8415 100644
--- a/boards/components/src/mx25r6435f.rs
+++ b/boards/components/src/mx25r6435f.rs
@@ -81,17 +81,17 @@ impl<
         A: 'static + hil::time::Alarm<'static>,
     > Mx25r6435fComponent<S, P, A>
 {
-    pub fn new(
+    pub fn new<CS: kernel::hil::spi::cs::IntoChipSelect<S::ChipSelect, hil::spi::cs::ActiveLow>>(
         write_protect_pin: Option<&'static P>,
         hold_pin: Option<&'static P>,
-        chip_select: S::ChipSelect,
+        chip_select: CS,
         mux_alarm: &'static MuxAlarm<'static, A>,
         mux_spi: &'static MuxSpiMaster<'static, S>,
     ) -> Mx25r6435fComponent<S, P, A> {
         Mx25r6435fComponent {
             write_protect_pin,
             hold_pin,
-            chip_select,
+            chip_select: chip_select.into_cs(),
             mux_alarm,
             mux_spi,
         }
diff --git a/boards/components/src/spi.rs b/boards/components/src/spi.rs
index b824c05454..51463d8e28 100644
--- a/boards/components/src/spi.rs
+++ b/boards/components/src/spi.rs
@@ -28,6 +28,7 @@
 // Author: Philip Levis <pal@cs.stanford.edu>
 // Last modified: 6/20/2018
 
+use core::marker::PhantomData;
 use core::mem::MaybeUninit;
 
 use capsules_core::spi_controller::{Spi, DEFAULT_READ_BUF_LENGTH, DEFAULT_WRITE_BUF_LENGTH};
@@ -125,9 +126,14 @@ pub struct SpiSyscallPComponent<S: 'static + spi::SpiSlave<'static>> {
     driver_num: usize,
 }
 
-pub struct SpiComponent<S: 'static + spi::SpiMaster<'static>> {
+pub struct SpiComponent<
+    S: 'static + spi::SpiMaster<'static>,
+    CS: spi::cs::IntoChipSelect<S::ChipSelect, AP>,
+    AP: spi::cs::ChipSelectActivePolarity,
+> {
     spi_mux: &'static MuxSpiMaster<'static, S>,
-    chip_select: S::ChipSelect,
+    chip_select: CS,
+    _phantom: PhantomData<AP>,
 }
 
 impl<S: 'static + spi::SpiMaster<'static>> SpiMuxComponent<S> {
@@ -246,22 +252,35 @@ impl<S: 'static + spi::SpiSlave<'static>> Component for SpiSyscallPComponent<S>
     }
 }
 
-impl<S: 'static + spi::SpiMaster<'static>> SpiComponent<S> {
-    pub fn new(mux: &'static MuxSpiMaster<'static, S>, chip_select: S::ChipSelect) -> Self {
+impl<
+        S: 'static + spi::SpiMaster<'static>,
+        CS: spi::cs::IntoChipSelect<S::ChipSelect, AP>,
+        AP: spi::cs::ChipSelectActivePolarity,
+    > SpiComponent<S, CS, AP>
+{
+    pub fn new(mux: &'static MuxSpiMaster<'static, S>, chip_select: CS) -> Self {
         SpiComponent {
             spi_mux: mux,
+            _phantom: PhantomData,
             chip_select,
         }
     }
 }
 
-impl<S: 'static + spi::SpiMaster<'static>> Component for SpiComponent<S> {
+impl<
+        S: 'static + spi::SpiMaster<'static>,
+        CS: spi::cs::IntoChipSelect<S::ChipSelect, AP>,
+        AP: spi::cs::ChipSelectActivePolarity,
+    > Component for SpiComponent<S, CS, AP>
+{
     type StaticInput = &'static mut MaybeUninit<VirtualSpiMasterDevice<'static, S>>;
     type Output = &'static VirtualSpiMasterDevice<'static, S>;
 
     fn finalize(self, static_buffer: Self::StaticInput) -> Self::Output {
-        let spi_device =
-            static_buffer.write(VirtualSpiMasterDevice::new(self.spi_mux, self.chip_select));
+        let spi_device = static_buffer.write(VirtualSpiMasterDevice::new(
+            self.spi_mux,
+            self.chip_select.into_cs(),
+        ));
         spi_device.setup();
         spi_device
     }
diff --git a/boards/hail/src/main.rs b/boards/hail/src/main.rs
index 6abe00cdd7..d35a407148 100644
--- a/boards/hail/src/main.rs
+++ b/boards/hail/src/main.rs
@@ -381,7 +381,7 @@ unsafe fn start() -> (
     let spi_syscalls = components::spi::SpiSyscallComponent::new(
         board_kernel,
         mux_spi,
-        0,
+        sam4l::spi::Peripheral::Peripheral0,
         capsules_core::spi_controller::DRIVER_NUM,
     )
     .finalize(components::spi_syscall_component_static!(sam4l::spi::SpiHw));
diff --git a/boards/imix/src/main.rs b/boards/imix/src/main.rs
index b3aaf14afd..c665ef2dd1 100644
--- a/boards/imix/src/main.rs
+++ b/boards/imix/src/main.rs
@@ -465,15 +465,15 @@ unsafe fn start() -> (
     let spi_syscalls = SpiSyscallComponent::new(
         board_kernel,
         mux_spi,
-        2,
+        sam4l::spi::Peripheral::Peripheral2,
         capsules_core::spi_controller::DRIVER_NUM,
     )
     .finalize(components::spi_syscall_component_static!(
         sam4l::spi::SpiHw<'static>
     ));
-    let rf233_spi = SpiComponent::new(mux_spi, 3).finalize(components::spi_component_static!(
-        sam4l::spi::SpiHw<'static>
-    ));
+    let rf233_spi = SpiComponent::new(mux_spi, sam4l::spi::Peripheral::Peripheral3).finalize(
+        components::spi_component_static!(sam4l::spi::SpiHw<'static>),
+    );
     let rf233 = components::rf233::RF233Component::new(
         rf233_spi,
         &peripherals.pa[09], // reset
diff --git a/boards/nordic/nrf52840dk/src/lib.rs b/boards/nordic/nrf52840dk/src/lib.rs
index 1166341986..42870f337e 100644
--- a/boards/nordic/nrf52840dk/src/lib.rs
+++ b/boards/nordic/nrf52840dk/src/lib.rs
@@ -697,7 +697,9 @@ pub unsafe fn start() -> (
     let spi_controller = components::spi::SpiSyscallComponent::new(
         board_kernel,
         mux_spi,
-        &gpio_port[SPI_CS],
+        kernel::hil::spi::cs::IntoChipSelect::<_, kernel::hil::spi::cs::ActiveLow>::into_cs(
+            &gpio_port[SPI_CS],
+        ),
         capsules_core::spi_controller::DRIVER_NUM,
     )
     .finalize(components::spi_syscall_component_static!(
@@ -717,7 +719,7 @@ pub unsafe fn start() -> (
     let mx25r6435f = components::mx25r6435f::Mx25r6435fComponent::new(
         Some(&gpio_port[SPI_MX25R6435F_WRITE_PROTECT_PIN]),
         Some(&gpio_port[SPI_MX25R6435F_HOLD_PIN]),
-        &gpio_port[SPI_MX25R6435F_CHIP_SELECT] as &dyn kernel::hil::gpio::Pin,
+        &gpio_port[SPI_MX25R6435F_CHIP_SELECT],
         mux_alarm,
         mux_spi,
     )
diff --git a/boards/opentitan/src/main.rs b/boards/opentitan/src/main.rs
index 505d396323..5a921fe13f 100644
--- a/boards/opentitan/src/main.rs
+++ b/boards/opentitan/src/main.rs
@@ -550,7 +550,7 @@ unsafe fn setup() -> (
     let spi_controller = components::spi::SpiSyscallComponent::new(
         board_kernel,
         mux_spi,
-        0,
+        lowrisc::spi_host::CS(0),
         capsules_core::spi_controller::DRIVER_NUM,
     )
     .finalize(components::spi_syscall_component_static!(
diff --git a/boards/pico_explorer_base/src/main.rs b/boards/pico_explorer_base/src/main.rs
index 3f944ddea9..4944f57c8d 100644
--- a/boards/pico_explorer_base/src/main.rs
+++ b/boards/pico_explorer_base/src/main.rs
@@ -19,12 +19,12 @@ use components::gpio::GpioComponent;
 use components::led::LedsComponent;
 use enum_primitive::cast::FromPrimitive;
 use kernel::component::Component;
-use kernel::debug;
 use kernel::hil::led::LedHigh;
 use kernel::hil::usb::Client;
 use kernel::platform::{KernelResources, SyscallDriverLookup};
 use kernel::scheduler::round_robin::RoundRobinSched;
 use kernel::{capabilities, create_capability, static_init, Kernel};
+use kernel::{debug, hil};
 
 use rp2040::adc::{Adc, Channel};
 use rp2040::chip::{Rp2040, Rp2040DefaultPeripherals};
@@ -465,7 +465,9 @@ pub unsafe fn start() -> (
 
     let bus = components::bus::SpiMasterBusComponent::new(
         mux_spi,
-        peripherals.pins.get_pin(RPGpio::GPIO17),
+        hil::spi::cs::IntoChipSelect::<_, hil::spi::cs::ActiveLow>::into_cs(
+            peripherals.pins.get_pin(RPGpio::GPIO17),
+        ),
         20_000_000,
         kernel::hil::spi::ClockPhase::SampleLeading,
         kernel::hil::spi::ClockPolarity::IdleLow,
diff --git a/boards/sma_q3/src/main.rs b/boards/sma_q3/src/main.rs
index 65d4f24bc0..85f99d5d66 100644
--- a/boards/sma_q3/src/main.rs
+++ b/boards/sma_q3/src/main.rs
@@ -454,10 +454,7 @@ pub unsafe fn start() -> (
         );
 
         let disp_pin = &nrf52840_peripherals.gpio_port[Pin::P0_07];
-        let cs_pin = static_init!(
-            components::lpm013m126::Inverted<'static, nrf52840::gpio::GPIOPin>,
-            components::lpm013m126::Inverted(&nrf52840_peripherals.gpio_port[Pin::P0_05])
-        );
+        let cs_pin = &nrf52840_peripherals.gpio_port[Pin::P0_05];
 
         let display = components::lpm013m126::Lpm013m126Component::new(
             mux_spi,
diff --git a/boards/wm1110dev/src/main.rs b/boards/wm1110dev/src/main.rs
index f0a22861d6..dc925163cf 100644
--- a/boards/wm1110dev/src/main.rs
+++ b/boards/wm1110dev/src/main.rs
@@ -17,6 +17,7 @@ use core::ptr::addr_of_mut;
 
 use kernel::capabilities;
 use kernel::component::Component;
+use kernel::hil;
 use kernel::hil::gpio::Configure;
 use kernel::hil::gpio::Output;
 use kernel::hil::led::LedHigh;
@@ -372,7 +373,9 @@ pub unsafe fn start() -> (
     let lr1110_spi = components::spi::SpiSyscallComponent::new(
         board_kernel,
         mux_spi,
-        &nrf52840_peripherals.gpio_port[SPI_CS_PIN],
+        hil::spi::cs::IntoChipSelect::<_, hil::spi::cs::ActiveLow>::into_cs(
+            &nrf52840_peripherals.gpio_port[SPI_CS_PIN],
+        ),
         LORA_SPI_DRIVER_NUM,
     )
     .finalize(components::spi_syscall_component_static!(
@@ -387,7 +390,11 @@ pub unsafe fn start() -> (
 
     base_peripherals
         .spim0
-        .specify_chip_select(&nrf52840_peripherals.gpio_port[SPI_CS_PIN])
+        .specify_chip_select(
+            hil::spi::cs::IntoChipSelect::<_, hil::spi::cs::ActiveLow>::into_cs(
+                &nrf52840_peripherals.gpio_port[SPI_CS_PIN],
+            ),
+        )
         .unwrap();
 
     // Pin mappings from the original WM1110 source code.
diff --git a/chips/apollo3/src/iom.rs b/chips/apollo3/src/iom.rs
index 70953337e3..722cb6cbfa 100644
--- a/chips/apollo3/src/iom.rs
+++ b/chips/apollo3/src/iom.rs
@@ -6,8 +6,9 @@
 
 use core::cell::Cell;
 use kernel::hil;
-use kernel::hil::gpio::{Configure, Output};
+use kernel::hil::gpio::Configure;
 use kernel::hil::i2c;
+use kernel::hil::spi::cs::ChipSelectPolar;
 use kernel::hil::spi::{ClockPhase, ClockPolarity, SpiMaster, SpiMasterClient};
 use kernel::utilities::cells::OptionalCell;
 use kernel::utilities::cells::TakeCell;
@@ -289,7 +290,7 @@ pub struct Iom<'a> {
 
     op: Cell<Operation>,
     spi_phase: Cell<ClockPhase>,
-    spi_cs: OptionalCell<&'a crate::gpio::GpioPin<'a>>,
+    spi_cs: OptionalCell<ChipSelectPolar<'a, crate::gpio::GpioPin<'a>>>,
     smbus: Cell<bool>,
 }
 
@@ -558,7 +559,7 @@ impl<'a> Iom<'_> {
                 regs.inten.set(0x00);
 
                 // Clear CS
-                self.spi_cs.map(|cs| cs.set());
+                self.spi_cs.map(|cs| cs.deactivate());
 
                 self.op.set(Operation::None);
 
@@ -696,7 +697,7 @@ impl<'a> Iom<'_> {
                 regs.inten.set(0x00);
 
                 // Clear CS
-                self.spi_cs.map(|cs| cs.set());
+                self.spi_cs.map(|cs| cs.deactivate());
 
                 self.op.set(Operation::None);
 
@@ -1175,7 +1176,7 @@ impl<'a> hil::i2c::SMBusMaster<'a> for Iom<'a> {
 }
 
 impl<'a> SpiMaster<'a> for Iom<'a> {
-    type ChipSelect = &'a crate::gpio::GpioPin<'a>;
+    type ChipSelect = ChipSelectPolar<'a, crate::gpio::GpioPin<'a>>;
 
     fn init(&self) -> Result<(), ErrorCode> {
         self.op.set(Operation::SPI);
@@ -1238,7 +1239,7 @@ impl<'a> SpiMaster<'a> for Iom<'a> {
         self.registers.intclr.set(0xFFFF_FFFF);
 
         // Trigger CS
-        self.spi_cs.map(|cs| cs.clear());
+        self.spi_cs.map(|cs| cs.activate());
 
         // Start the transfer
         self.registers.cmd.write(
@@ -1354,7 +1355,7 @@ impl<'a> SpiMaster<'a> for Iom<'a> {
         self.registers.intclr.set(0xFFFF_FFFF);
 
         // Trigger CS
-        self.spi_cs.map(|cs| cs.clear());
+        self.spi_cs.map(|cs| cs.activate());
 
         // Start the transfer
         self.registers.cmd.write(
@@ -1368,7 +1369,7 @@ impl<'a> SpiMaster<'a> for Iom<'a> {
 
         self.registers.fifopush.set(val as u32);
 
-        self.spi_cs.map(|cs| cs.set());
+        self.spi_cs.map(|cs| cs.deactivate());
 
         Ok(())
     }
@@ -1386,7 +1387,7 @@ impl<'a> SpiMaster<'a> for Iom<'a> {
         self.registers.intclr.set(0xFFFF_FFFF);
 
         // Trigger CS
-        self.spi_cs.map(|cs| cs.clear());
+        self.spi_cs.map(|cs| cs.activate());
 
         // Start the transfer
         self.registers.cmd.write(
@@ -1401,11 +1402,11 @@ impl<'a> SpiMaster<'a> for Iom<'a> {
         if self.registers.fifoptr.read(FIFOPTR::FIFO1SIZ) > 0 {
             let d = self.registers.fifopop.get().to_ne_bytes();
 
-            self.spi_cs.map(|cs| cs.set());
+            self.spi_cs.map(|cs| cs.deactivate());
             return Ok(d[0]);
         }
 
-        self.spi_cs.map(|cs| cs.set());
+        self.spi_cs.map(|cs| cs.deactivate());
 
         Err(ErrorCode::FAIL)
     }
@@ -1423,7 +1424,7 @@ impl<'a> SpiMaster<'a> for Iom<'a> {
         self.registers.intclr.set(0xFFFF_FFFF);
 
         // Trigger CS
-        self.spi_cs.map(|cs| cs.clear());
+        self.spi_cs.map(|cs| cs.activate());
 
         // Start the transfer
         self.registers.cmd.write(
@@ -1440,18 +1441,18 @@ impl<'a> SpiMaster<'a> for Iom<'a> {
         if self.registers.fifoptr.read(FIFOPTR::FIFO1SIZ) > 0 {
             let d = self.registers.fifopop.get().to_ne_bytes();
 
-            self.spi_cs.map(|cs| cs.set());
+            self.spi_cs.map(|cs| cs.deactivate());
             return Ok(d[0]);
         }
 
-        self.spi_cs.map(|cs| cs.set());
+        self.spi_cs.map(|cs| cs.deactivate());
 
         Err(ErrorCode::FAIL)
     }
 
     fn specify_chip_select(&self, cs: Self::ChipSelect) -> Result<(), ErrorCode> {
-        cs.make_output();
-        cs.set();
+        cs.pin.make_output();
+        cs.deactivate();
         self.spi_cs.set(cs);
 
         Ok(())
@@ -1569,10 +1570,10 @@ impl<'a> SpiMaster<'a> for Iom<'a> {
     }
 
     fn hold_low(&self) {
-        self.spi_cs.map(|cs| cs.clear());
+        self.spi_cs.map(|cs| cs.activate());
     }
 
     fn release_low(&self) {
-        self.spi_cs.map(|cs| cs.set());
+        self.spi_cs.map(|cs| cs.deactivate());
     }
 }
diff --git a/chips/lowrisc/src/spi_host.rs b/chips/lowrisc/src/spi_host.rs
index 7e013eef36..b5366a7939 100644
--- a/chips/lowrisc/src/spi_host.rs
+++ b/chips/lowrisc/src/spi_host.rs
@@ -143,7 +143,6 @@ pub struct SpiHost<'a> {
     registers: StaticRef<SpiHostRegisters>,
     client: OptionalCell<&'a dyn hil::spi::SpiMasterClient>,
     busy: Cell<bool>,
-    chip_select: Cell<u32>,
     cpu_clk: u32,
     tsclk: Cell<u32>,
     tx_buf: TakeCell<'static, [u8]>,
@@ -164,7 +163,6 @@ impl<'a> SpiHost<'a> {
             registers: base,
             client: OptionalCell::empty(),
             busy: Cell::new(false),
-            chip_select: Cell::new(0),
             cpu_clk,
             tsclk: Cell::new(0),
             tx_buf: TakeCell::empty(),
@@ -552,8 +550,17 @@ impl<'a> SpiHost<'a> {
     }
 }
 
+#[derive(Copy, Clone)]
+pub struct CS(pub u32);
+
+impl hil::spi::cs::IntoChipSelect<CS, hil::spi::cs::ActiveLow> for CS {
+    fn into_cs(self) -> CS {
+        self
+    }
+}
+
 impl<'a> hil::spi::SpiMaster<'a> for SpiHost<'a> {
-    type ChipSelect = u32;
+    type ChipSelect = CS;
 
     fn init(&self) -> Result<(), ErrorCode> {
         let regs = self.registers;
@@ -660,8 +667,7 @@ impl<'a> hil::spi::SpiMaster<'a> for SpiHost<'a> {
         let regs = self.registers;
 
         //CSID will index the CONFIGOPTS multi-register
-        regs.csid.write(csid_ctrl::CSID.val(cs));
-        self.chip_select.set(cs);
+        regs.csid.write(csid_ctrl::CSID.val(cs.0));
 
         Ok(())
     }
diff --git a/chips/nrf52/src/spi.rs b/chips/nrf52/src/spi.rs
index a83ea17640..c1874853cc 100644
--- a/chips/nrf52/src/spi.rs
+++ b/chips/nrf52/src/spi.rs
@@ -36,6 +36,8 @@
 use core::cell::Cell;
 use core::{cmp, ptr};
 use kernel::hil;
+use kernel::hil::gpio::Configure;
+use kernel::hil::spi::cs::ChipSelectPolar;
 use kernel::utilities::cells::{OptionalCell, TakeCell, VolatileCell};
 use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
 use kernel::utilities::registers::{register_bitfields, ReadWrite, WriteOnly};
@@ -240,7 +242,7 @@ impl Frequency {
 pub struct SPIM<'a> {
     registers: StaticRef<SpimRegisters>,
     client: OptionalCell<&'a dyn hil::spi::SpiMasterClient>,
-    chip_select: OptionalCell<&'a dyn hil::gpio::Pin>,
+    chip_select: OptionalCell<ChipSelectPolar<'a, crate::gpio::GPIOPin<'a>>>,
     busy: Cell<bool>,
     tx_buf: TakeCell<'static, [u8]>,
     rx_buf: TakeCell<'static, [u8]>,
@@ -270,7 +272,7 @@ impl<'a> SPIM<'a> {
                 return;
             }
 
-            self.chip_select.map(|cs| cs.set());
+            self.chip_select.map(|cs| cs.deactivate());
             self.registers.events_end.write(EVENT::EVENT::CLEAR);
 
             // When we are no longer active or busy we can disable the
@@ -337,7 +339,7 @@ impl<'a> SPIM<'a> {
 }
 
 impl<'a> hil::spi::SpiMaster<'a> for SPIM<'a> {
-    type ChipSelect = &'a dyn hil::gpio::Pin;
+    type ChipSelect = ChipSelectPolar<'a, crate::gpio::GPIOPin<'a>>;
 
     fn set_client(&self, client: &'a dyn hil::spi::SpiMasterClient) {
         self.client.set(client);
@@ -365,7 +367,7 @@ impl<'a> hil::spi::SpiMaster<'a> for SPIM<'a> {
         if self.chip_select.is_none() {
             return Err((ErrorCode::NODEVICE, tx_buf, rx_buf));
         }
-        self.chip_select.map(|cs| cs.clear());
+        self.chip_select.map(|cs| cs.activate());
 
         // Setup transmit data registers
         let tx_len: u32 = cmp::min(len, tx_buf.len()) as u32;
@@ -418,8 +420,8 @@ impl<'a> hil::spi::SpiMaster<'a> for SPIM<'a> {
     // The type of the argument is based on what makes sense for the
     // peripheral when this trait is implemented.
     fn specify_chip_select(&self, cs: Self::ChipSelect) -> Result<(), ErrorCode> {
-        cs.make_output();
-        cs.set();
+        cs.pin.make_output();
+        cs.deactivate();
         self.chip_select.set(cs);
         Ok(())
     }
diff --git a/chips/rp2040/src/spi.rs b/chips/rp2040/src/spi.rs
index 0660b0ebb3..fb1c7596c6 100644
--- a/chips/rp2040/src/spi.rs
+++ b/chips/rp2040/src/spi.rs
@@ -6,7 +6,7 @@ use crate::clocks;
 use core::cell::Cell;
 use core::cmp;
 use kernel::hil;
-use kernel::hil::gpio::Output;
+use kernel::hil::spi::cs::ChipSelectPolar;
 use kernel::hil::spi::SpiMaster;
 use kernel::hil::spi::SpiMasterClient;
 use kernel::hil::spi::{ClockPhase, ClockPolarity};
@@ -238,7 +238,7 @@ pub struct Spi<'a> {
     registers: StaticRef<SpiRegisters>,
     clocks: OptionalCell<&'a clocks::Clocks>,
     master_client: OptionalCell<&'a dyn hil::spi::SpiMasterClient>,
-    active_slave: OptionalCell<&'a crate::gpio::RPGpioPin<'a>>,
+    active_slave: OptionalCell<ChipSelectPolar<'a, crate::gpio::RPGpioPin<'a>>>,
 
     tx_buffer: TakeCell<'static, [u8]>,
     tx_position: Cell<usize>,
@@ -346,7 +346,7 @@ impl<'a> Spi<'a> {
         if self.transfers.get() == SPI_IN_PROGRESS {
             if !self.active_after.get() {
                 self.active_slave.map(|p| {
-                    p.set();
+                    p.deactivate();
                 });
             }
             self.master_client.map(|client| {
@@ -383,7 +383,7 @@ impl<'a> Spi<'a> {
             self.registers.sspimsc.modify(SSPIMSC::TXIM::CLEAR);
             self.registers.sspimsc.modify(SSPIMSC::RXIM::CLEAR);
             self.active_slave.map(|p| {
-                p.clear();
+                p.activate();
             });
 
             self.transfers.set(SPI_IN_PROGRESS);
@@ -474,10 +474,6 @@ impl<'a> Spi<'a> {
         }
     }
 
-    fn set_active_slave(&self, slave_pin: &'a crate::gpio::RPGpioPin<'a>) {
-        self.active_slave.set(slave_pin);
-    }
-
     fn set_format(&self) {
         self.registers.sspcr0.modify(SSPCR0::DSS::DATA_8_BIT);
         self.registers.sspcr0.modify(SSPCR0::SPO::CLEAR);
@@ -486,7 +482,7 @@ impl<'a> Spi<'a> {
 }
 
 impl<'a> SpiMaster<'a> for Spi<'a> {
-    type ChipSelect = &'a crate::gpio::RPGpioPin<'a>;
+    type ChipSelect = ChipSelectPolar<'a, crate::gpio::RPGpioPin<'a>>;
 
     fn set_client(&self, client: &'a dyn SpiMasterClient) {
         self.master_client.set(client);
@@ -564,7 +560,7 @@ impl<'a> SpiMaster<'a> for Spi<'a> {
 
     fn specify_chip_select(&self, cs: Self::ChipSelect) -> Result<(), ErrorCode> {
         if !self.is_busy() {
-            self.set_active_slave(cs);
+            self.active_slave.set(cs);
             Ok(())
         } else {
             Err(ErrorCode::BUSY)
diff --git a/chips/sam4l/src/spi.rs b/chips/sam4l/src/spi.rs
index a0dd44d949..eedc471a4e 100644
--- a/chips/sam4l/src/spi.rs
+++ b/chips/sam4l/src/spi.rs
@@ -179,6 +179,12 @@ pub enum Peripheral {
     Peripheral3,
 }
 
+impl spi::cs::IntoChipSelect<Peripheral, spi::cs::ActiveLow> for Peripheral {
+    fn into_cs(self) -> Peripheral {
+        self
+    }
+}
+
 #[derive(Copy, Clone, PartialEq)]
 pub enum SpiRole {
     SpiMaster,
@@ -375,20 +381,6 @@ impl<'a> SpiHw<'a> {
         }
     }
 
-    pub fn set_active_peripheral(&self, peripheral: Peripheral) {
-        // Slave cannot set active peripheral
-        if self.role.get() == SpiRole::SpiMaster {
-            let spi = &SpiRegisterManager::new(self);
-            let mr = match peripheral {
-                Peripheral::Peripheral0 => Mode::PCS::PCS0,
-                Peripheral::Peripheral1 => Mode::PCS::PCS1,
-                Peripheral::Peripheral2 => Mode::PCS::PCS2,
-                Peripheral::Peripheral3 => Mode::PCS::PCS3,
-            };
-            spi.registers.mr.modify(mr);
-        }
-    }
-
     /// Returns the currently active peripheral
     fn get_active_peripheral(&self, spi: &SpiRegisterManager<'a, '_>) -> Peripheral {
         if self.role.get() == SpiRole::SpiMaster {
@@ -520,7 +512,7 @@ impl<'a> SpiHw<'a> {
 }
 
 impl<'a> spi::SpiMaster<'a> for SpiHw<'a> {
-    type ChipSelect = u8;
+    type ChipSelect = Peripheral;
 
     fn set_client(&self, client: &'a dyn SpiMasterClient) {
         self.client.set(client);
@@ -636,18 +628,19 @@ impl<'a> spi::SpiMaster<'a> for SpiHw<'a> {
     }
 
     fn specify_chip_select(&self, cs: Self::ChipSelect) -> Result<(), ErrorCode> {
-        match match cs {
-            0 => Some(Peripheral::Peripheral0),
-            1 => Some(Peripheral::Peripheral1),
-            2 => Some(Peripheral::Peripheral2),
-            3 => Some(Peripheral::Peripheral3),
-            _ => None,
-        } {
-            Some(peripheral_number) => {
-                self.set_active_peripheral(peripheral_number);
-                Ok(())
-            }
-            None => Err(ErrorCode::INVAL),
+        // Slave cannot set active peripheral
+        if self.role.get() == SpiRole::SpiMaster {
+            let spi = &SpiRegisterManager::new(self);
+            let mr = match cs {
+                Peripheral::Peripheral0 => Mode::PCS::PCS0,
+                Peripheral::Peripheral1 => Mode::PCS::PCS1,
+                Peripheral::Peripheral2 => Mode::PCS::PCS2,
+                Peripheral::Peripheral3 => Mode::PCS::PCS3,
+            };
+            spi.registers.mr.modify(mr);
+            Ok(())
+        } else {
+            Err(ErrorCode::INVAL)
         }
     }
 }
diff --git a/chips/sam4l/src/usart.rs b/chips/sam4l/src/usart.rs
index 7a33d26542..8081401152 100644
--- a/chips/sam4l/src/usart.rs
+++ b/chips/sam4l/src/usart.rs
@@ -10,8 +10,8 @@ use core::cell::Cell;
 use core::cmp;
 use core::sync::atomic::{AtomicBool, Ordering};
 use kernel::deferred_call::{DeferredCall, DeferredCallClient};
-use kernel::hil;
 use kernel::hil::spi;
+use kernel::hil::spi::cs::ChipSelectPolar;
 use kernel::hil::uart;
 use kernel::utilities::cells::OptionalCell;
 use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
@@ -406,7 +406,7 @@ pub struct USART<'a> {
 
     client: OptionalCell<UsartClient<'a>>,
 
-    spi_chip_select: OptionalCell<&'a dyn hil::gpio::Pin>,
+    spi_chip_select: OptionalCell<ChipSelectPolar<'a, crate::gpio::GPIOPin<'a>>>,
     pm: &'a pm::PowerManager,
     dc_state: OptionalCell<DeferredCallState>,
     deferred_call: DeferredCall,
@@ -685,7 +685,7 @@ impl<'a> USART<'a> {
                                 self.rts_disable_spi_deassert_cs(usart);
                             },
                             |cs| {
-                                cs.set();
+                                cs.deactivate();
                             },
                         );
 
@@ -1051,7 +1051,7 @@ impl<'a> uart::ReceiveAdvanced<'a> for USART<'a> {
 
 /// SPI
 impl<'a> spi::SpiMaster<'a> for USART<'a> {
-    type ChipSelect = Option<&'static dyn hil::gpio::Pin>;
+    type ChipSelect = ChipSelectPolar<'a, crate::gpio::GPIOPin<'a>>;
 
     fn init(&self) -> Result<(), ErrorCode> {
         let usart = &USARTRegManager::new(self);
@@ -1111,7 +1111,7 @@ impl<'a> spi::SpiMaster<'a> for USART<'a> {
                 self.rts_enable_spi_assert_cs(usart);
             },
             |cs| {
-                cs.clear();
+                cs.activate();
             },
         );
 
@@ -1185,7 +1185,7 @@ impl<'a> spi::SpiMaster<'a> for USART<'a> {
 
     /// Pass in a None to use the HW chip select pin on the USART (RTS).
     fn specify_chip_select(&self, cs: Self::ChipSelect) -> Result<(), ErrorCode> {
-        self.spi_chip_select.insert(cs);
+        self.spi_chip_select.set(cs);
         Ok(())
     }
 
diff --git a/chips/stm32f303xc/src/spi.rs b/chips/stm32f303xc/src/spi.rs
index e3b95d58b7..32faee5435 100644
--- a/chips/stm32f303xc/src/spi.rs
+++ b/chips/stm32f303xc/src/spi.rs
@@ -7,7 +7,6 @@ use core::cmp;
 use kernel::ErrorCode;
 
 use kernel::hil;
-use kernel::hil::gpio::Output;
 use kernel::hil::spi::{self, ClockPhase, ClockPolarity, SpiMasterClient};
 use kernel::platform::chip::ClockInterface;
 use kernel::utilities::cells::{OptionalCell, TakeCell};
@@ -192,7 +191,7 @@ pub struct Spi<'a> {
     // SPI slave support not yet implemented
     master_client: OptionalCell<&'a dyn hil::spi::SpiMasterClient>,
 
-    active_slave: OptionalCell<&'a crate::gpio::Pin<'a>>,
+    active_slave: OptionalCell<spi::cs::ChipSelectPolar<'a, crate::gpio::Pin<'a>>>,
 
     tx_buffer: TakeCell<'static, [u8]>,
     tx_position: Cell<usize>,
@@ -289,7 +288,7 @@ impl<'a> Spi<'a> {
             // initiate another SPI transfer right away
             if !self.active_after.get() {
                 self.active_slave.map(|p| {
-                    p.set();
+                    p.deactivate();
                 });
             }
             self.transfers.set(SPI_IDLE);
@@ -302,10 +301,6 @@ impl<'a> Spi<'a> {
         }
     }
 
-    fn set_active_slave(&self, slave_pin: &'a crate::gpio::Pin<'a>) {
-        self.active_slave.set(slave_pin);
-    }
-
     fn set_cr<F>(&self, f: F)
     where
         F: FnOnce(),
@@ -369,7 +364,7 @@ impl<'a> Spi<'a> {
         if self.transfers.get() == 0 {
             self.registers.cr2.modify(CR2::RXNEIE::CLEAR);
             self.active_slave.map(|p| {
-                p.clear();
+                p.activate();
             });
 
             self.transfers.set(self.transfers.get() | SPI_IN_PROGRESS);
@@ -416,7 +411,7 @@ impl<'a> Spi<'a> {
 }
 
 impl<'a> spi::SpiMaster<'a> for Spi<'a> {
-    type ChipSelect = &'a crate::gpio::Pin<'a>;
+    type ChipSelect = spi::cs::ChipSelectPolar<'a, crate::gpio::Pin<'a>>;
 
     fn set_client(&self, client: &'a dyn SpiMasterClient) {
         self.master_client.set(client);
@@ -539,7 +534,7 @@ impl<'a> spi::SpiMaster<'a> for Spi<'a> {
     }
 
     fn specify_chip_select(&self, cs: Self::ChipSelect) -> Result<(), ErrorCode> {
-        self.set_active_slave(cs);
+        self.active_slave.set(cs);
         Ok(())
     }
 }
diff --git a/kernel/src/hil/spi.rs b/kernel/src/hil/spi.rs
index 9731ebbc17..e6904c0202 100644
--- a/kernel/src/hil/spi.rs
+++ b/kernel/src/hil/spi.rs
@@ -40,6 +40,146 @@ pub enum ClockPhase {
     SampleTrailing,
 }
 
+/// Utility types for modeling chip select pins in a [SpiMaster] implementation.
+pub mod cs {
+
+    /// Represents the Polarity of a chip-select pin (i.e. whether
+    /// high or low indicates the peripheral is active)
+    #[derive(Copy, Clone)]
+    pub enum Polarity {
+        High,
+        Low,
+    }
+
+    mod private {
+        pub trait Sealed {}
+    }
+
+    /// Marker trait indicating whether a peripheral requires active
+    /// high or low polarity as well as whether a [SpiMaster](super::SpiMaster)
+    /// implementation can support either or both polarities.
+    ///
+    /// This trait is sealed and only implemented for [ActiveLow] and
+    /// [ActiveHigh].
+    pub trait ChipSelectActivePolarity: private::Sealed {
+        const POLARITY: Polarity;
+    }
+
+    /// Marks a peripheral as requiring or controller as supprting
+    /// active low chip select pins
+    pub enum ActiveLow {}
+    /// Marks a peripheral as requiring or controller as supprting
+    /// active high chip select pins
+    pub enum ActiveHigh {}
+
+    impl private::Sealed for ActiveLow {}
+    impl private::Sealed for ActiveHigh {}
+
+    impl ChipSelectActivePolarity for ActiveLow {
+        const POLARITY: Polarity = Polarity::Low;
+    }
+
+    impl ChipSelectActivePolarity for ActiveHigh {
+        const POLARITY: Polarity = Polarity::High;
+    }
+
+    /// A type that can be converted to the appropriate type for
+    /// [SpiMaster::ChipSelect](super::SpiMaster::ChipSelect) for a
+    /// particular `POLARITY`.
+    ///
+    /// Instantiating a driver for any SPI peripheral should require a type that
+    /// implements [IntoChipSelect]. That enforces that whatever object is used
+    /// as the chip select can support the correct polarity for the particular SPI
+    /// peripheral. This is mostly commonly handled by the component for the
+    /// peripheral, which requires an object with type [IntoChipSelect] and then
+    /// converts the object to the [SpiMaster::ChipSelect](super::SpiMaster::ChipSelect)
+    /// type.
+    /// # Examples:
+    ///
+    /// Some SPI host controllers only support active low or active
+    /// high chip select pins. Such a controller might provide a unit
+    /// implementation of this trait _only_ for the [ActiveLow] marker.
+    ///
+    /// ```rust
+    /// use kernel::hil::spi::cs::*;
+    ///
+    /// #[derive(Copy, Clone)]
+    /// enum PeripheralSelect {
+    ///     Peripheral0,
+    ///     Peripheral1,
+    /// }
+    ///
+    /// impl IntoChipSelect<PeripheralSelect, ActiveLow> for PeripheralSelect {
+    ///     fn into_cs(self) -> Self { self }
+    /// }
+    /// ```
+    ///
+    /// Many other controllers can handle both active low and active
+    /// high chip select pins, in which case, they should implement
+    /// both the [ActiveLow] and [ActiveHigh] variants, for example,
+    /// using the [ChipSelectPolar] wrapper struct (which implements
+    /// both).
+    pub trait IntoChipSelect<T, POLARITY> {
+        fn into_cs(self) -> T;
+    }
+
+    /// A convenience wrapper type around
+    /// [Output](crate::hil::gpio::Output) GPIO pins that implements
+    /// [IntoChipSelect] for both [ActiveLow] and [ActiveHigh].
+    pub struct ChipSelectPolar<'a, P: crate::hil::gpio::Output> {
+        /// The underlying chip select "pin"
+        pub pin: &'a P,
+        /// The polarity from which this wrapper was derived using
+        /// [IntoChipSelect]
+        pub polarity: Polarity,
+    }
+
+    impl<'a, P: crate::hil::gpio::Output> Clone for ChipSelectPolar<'a, P> {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
+
+    impl<'a, P: crate::hil::gpio::Output> Copy for ChipSelectPolar<'a, P> {}
+
+    impl<'a, P: crate::hil::gpio::Output, A: ChipSelectActivePolarity>
+        IntoChipSelect<ChipSelectPolar<'a, P>, A> for &'a P
+    {
+        fn into_cs(self) -> ChipSelectPolar<'a, P> {
+            ChipSelectPolar {
+                pin: self,
+                polarity: A::POLARITY,
+            }
+        }
+    }
+
+    /// When wrapping a GPIO pin that implements
+    /// [gpio::Output](crate::hil::gpio::Output), users can use the
+    /// `activate` and `deactivate` methods to automatically set or
+    /// clear the chip select pin based on the stored polarity.
+    impl<'a, P: crate::hil::gpio::Output> ChipSelectPolar<'a, P> {
+        /// Deactive the chip select pin
+        ///
+        /// High if active low, low if active high
+        pub fn deactivate(&self) {
+            match self.polarity {
+                Polarity::Low => self.pin.set(),
+                Polarity::High => self.pin.clear(),
+            }
+        }
+
+        /// Active the chip select pin
+        ///
+        /// Low if active low, high if active high
+        pub fn activate(&self) {
+            match self.polarity {
+                Polarity::Low => self.pin.clear(),
+                Polarity::High => self.pin.set(),
+            }
+        }
+    }
+}
+
 /// Trait for clients of a SPI bus in master mode.
 pub trait SpiMasterClient {
     /// Callback when a read/write operation finishes: `read_buffer`


Test patch:
diff --git a/boards/apollo3/lora_things_plus/src/tests/spi_controller.rs b/boards/apollo3/lora_things_plus/src/tests/spi_controller.rs
index c36eecd760..23c518be32 100644
--- a/boards/apollo3/lora_things_plus/src/tests/spi_controller.rs
+++ b/boards/apollo3/lora_things_plus/src/tests/spi_controller.rs
@@ -6,7 +6,7 @@ use crate::tests::run_kernel_op;
 use crate::PERIPHERALS;
 use core::cell::Cell;
 #[allow(unused_imports)]
-use kernel::hil::spi::{ClockPhase, ClockPolarity};
+use kernel::hil::spi::{self, ClockPhase, ClockPolarity};
 use kernel::hil::spi::{SpiMaster, SpiMasterClient};
 use kernel::static_init;
 use kernel::utilities::cells::TakeCell;
@@ -150,9 +150,9 @@ fn spi_host_transfer_partial() {
 
     // Set iom2 Configs
     spi_host
-        .specify_chip_select(
+        .specify_chip_select(spi::cs::IntoChipSelect::<_, spi::cs::ActiveLow>::into_cs(
             &perf.gpio_port[11], // A5
-        )
+        ))
         .ok();
     spi_host.set_rate(100000).ok();
     spi_host.set_polarity(ClockPolarity::IdleLow).ok();
diff --git a/boards/apollo3/redboard_artemis_nano/src/tests/spi_controller.rs b/boards/apollo3/redboard_artemis_nano/src/tests/spi_controller.rs
index c6cd758d65..3579596c94 100644
--- a/boards/apollo3/redboard_artemis_nano/src/tests/spi_controller.rs
+++ b/boards/apollo3/redboard_artemis_nano/src/tests/spi_controller.rs
@@ -6,7 +6,7 @@ use crate::tests::run_kernel_op;
 use crate::PERIPHERALS;
 use core::cell::Cell;
 #[allow(unused_imports)]
-use kernel::hil::spi::{ClockPhase, ClockPolarity};
+use kernel::hil::spi::{self, ClockPhase, ClockPolarity};
 use kernel::hil::spi::{SpiMaster, SpiMasterClient};
 use kernel::static_init;
 use kernel::utilities::cells::TakeCell;
@@ -150,9 +150,9 @@ fn spi_host_transfer_partial() {
 
     // Set iom0 Configs
     spi_host
-        .specify_chip_select(
+        .specify_chip_select(spi::cs::IntoChipSelect::<_, spi::cs::ActiveLow>::into_cs(
             &perf.gpio_port[35], // A14
-        )
+        ))
         .ok();
     spi_host.set_rate(100000).ok();
     spi_host.set_polarity(ClockPolarity::IdleLow).ok();
diff --git a/boards/imix/src/test/spi_dummy.rs b/boards/imix/src/test/spi_dummy.rs
index c7d7ae1181..3d1ce362f0 100644
--- a/boards/imix/src/test/spi_dummy.rs
+++ b/boards/imix/src/test/spi_dummy.rs
@@ -80,7 +80,8 @@ pub unsafe fn spi_dummy_test(spi: &'static sam4l::spi::SpiHw<'static>) {
     pin2.set();
 
     let spicb = kernel::static_init!(DummyCB, DummyCB::new(spi));
-    spi.set_active_peripheral(sam4l::spi::Peripheral::Peripheral0);
+    spi.specify_chip_select(sam4l::spi::Peripheral::Peripheral0)
+        .unwrap();
     spi.set_client(spicb);
 
     spi.init().unwrap();
diff --git a/boards/imix/src/test/spi_loopback.rs b/boards/imix/src/test/spi_loopback.rs
index 71c355bdc3..73f5147a85 100644
--- a/boards/imix/src/test/spi_loopback.rs
+++ b/boards/imix/src/test/spi_loopback.rs
@@ -112,10 +112,10 @@ pub unsafe fn spi_loopback_test(
 #[inline(never)]
 #[allow(unused_variables, dead_code)]
 pub unsafe fn spi_two_loopback_test(mux: &'static MuxSpiMaster<'static, sam4l::spi::SpiHw>) {
-    let spi_fast =
-        SpiComponent::new(mux, 0).finalize(components::spi_component_static!(sam4l::spi::SpiHw));
-    let spi_slow =
-        SpiComponent::new(mux, 1).finalize(components::spi_component_static!(sam4l::spi::SpiHw));
+    let spi_fast = SpiComponent::new(mux, sam4l::spi::Peripheral::Peripheral0)
+        .finalize(components::spi_component_static!(sam4l::spi::SpiHw));
+    let spi_slow = SpiComponent::new(mux, sam4l::spi::Peripheral::Peripheral1)
+        .finalize(components::spi_component_static!(sam4l::spi::SpiHw));
 
     let spicb_fast = kernel::static_init!(SpiLoopback, SpiLoopback::new(spi_fast, 0, 0x80));
     let spicb_slow = kernel::static_init!(SpiLoopback, SpiLoopback::new(spi_slow, 1, 0x00));
diff --git a/boards/opentitan/src/tests/spi_host.rs b/boards/opentitan/src/tests/spi_host.rs
index bb53e86283..3b2da3f4df 100644
--- a/boards/opentitan/src/tests/spi_host.rs
+++ b/boards/opentitan/src/tests/spi_host.rs
@@ -149,7 +149,7 @@ fn spi_host_transfer_partial() {
     cb.tx_len.set(tx.len());
 
     //Set SPI_HOST0 Configs
-    spi_host.specify_chip_select(0).ok();
+    spi_host.specify_chip_select(lowrisc::spi_host::CS(0)).ok();
     spi_host.set_rate(100000).ok();
     spi_host.set_polarity(ClockPolarity::IdleLow).ok();
     spi_host.set_phase(ClockPhase::SampleLeading).ok();
@@ -187,7 +187,7 @@ fn spi_host_transfer_single() {
     cb.tx_len.set(tx.len());
 
     //Set SPI_HOST0 Configs
-    spi_host.specify_chip_select(0).ok();
+    spi_host.specify_chip_select(lowrisc::spi_host::CS(0)).ok();
     spi_host.set_rate(100000).ok();
     spi_host.set_polarity(ClockPolarity::IdleLow).ok();
     spi_host.set_phase(ClockPhase::SampleLeading).ok();