From a68bc08ca40169f3d8aee640e416a8b11626b824 Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Sat, 9 Nov 2024 18:27:59 +0100 Subject: [PATCH 1/7] rust: WIP(?): Various bits of rust glue for AOP series Signed-off-by: Sasha Finkelstein --- rust/bindgen_parameters | 3 +++ rust/bindings/bindings_helper.h | 12 ++++++++++++ rust/kernel/alloc/kvec.rs | 22 +++++++++++++++++++++- rust/kernel/device.rs | 9 ++++++++- rust/kernel/of.rs | 18 +++++++++++++++++- 5 files changed, 61 insertions(+), 3 deletions(-) diff --git a/rust/bindgen_parameters b/rust/bindgen_parameters index d204f4284111d0..63956a0defdd08 100644 --- a/rust/bindgen_parameters +++ b/rust/bindgen_parameters @@ -7,6 +7,9 @@ # Packed type cannot transitively contain a `#[repr(align)]` type. --opaque-type alt_instr +--opaque-type snd_codec_options +--opaque-type snd_codec +--opaque-type snd_compr_params --opaque-type x86_msi_data --opaque-type x86_msi_addr_lo diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index b74b6a1ff44c15..ddf914bb98ab82 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -43,6 +45,9 @@ #include #include #include +#include +#include +#include /* `bindgen` gets confused at certain things. */ const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN; @@ -72,3 +77,10 @@ const xa_mark_t BINDINGS_XA_MARK_2 = XA_MARK_2; const xa_mark_t BINDINGS_XA_PRESENT = XA_PRESENT; const xa_mark_t BINDINGS_XA_MARK_MAX = XA_MARK_MAX; const xa_mark_t BINDINGS_XA_FREE_MARK = XA_FREE_MARK; + +const u64 BINDINGS_SNDRV_PCM_FMTBIT_FLOAT_LE = SNDRV_PCM_FMTBIT_FLOAT_LE; + +const u32 BINDINGS_IIO_CHAN_INFO_RAW = IIO_CHAN_INFO_RAW; +const u32 BINDINGS_IIO_CHAN_INFO_PROCESSED = IIO_CHAN_INFO_PROCESSED; +const u32 BINDINGS_IIO_ANGL = IIO_ANGL; +const u32 BINDINGS_IIO_LIGHT = IIO_LIGHT; diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs index 053b3b905eb916..9ab16e3fdb61d2 100644 --- a/rust/kernel/alloc/kvec.rs +++ b/rust/kernel/alloc/kvec.rs @@ -460,7 +460,7 @@ where /// If `new_len` is greater than len, the Vec is extended by the difference, /// with each additional slot filled with `value`. /// If `new_len` is less than len, the Vec is simply truncated. - fn resize(&mut self, new_len: usize, value: T, flags: Flags) -> Result<(), AllocError> + pub fn resize(&mut self, new_len: usize, value: T, flags: Flags) -> Result<(), AllocError> where T: Clone, { @@ -625,6 +625,26 @@ where } } } + /// Removes an element from the vector and returns it. + /// + /// The removed element is replaced by the last element of the vector. + /// + /// This does not preserve ordering of the remaining elements, but is *O*(1). + /// If you need to preserve the element order, use [`remove`] instead. + pub fn swap_remove(&mut self, index: usize) -> T { + if index > self.len() { + panic!("Index out of range"); + } + // SAFETY: index is in range + // self.len() - 1 is in range since at last 1 element exists + unsafe { + let old = ptr::read(self.as_ptr().add(index)); + let last = ptr::read(self.as_ptr().add(self.len() - 1)); + ptr::write(self.as_mut_ptr().add(index), last); + self.set_len(self.len - 1); + old + } + } } impl Vec { diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index be5800b0190584..bb8448df1e050c 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -60,7 +60,7 @@ impl Device { } /// Obtain the raw `struct device *`. - pub(crate) fn as_raw(&self) -> *mut bindings::device { + pub fn as_raw(&self) -> *mut bindings::device { self.0.get() } @@ -75,6 +75,13 @@ impl Device { unsafe { Some(Self::get_device(pdev)) } } + /// Returns the driver_data pointer. + pub fn get_drvdata(&self) -> *mut T { + // SAFETY: dev_get_drvdata returns a field of the device, + // pointer to which is valid by type invariant + unsafe { bindings::dev_get_drvdata(self.as_raw()) as *mut T } + } + /// Convert a raw C `struct device` pointer to a `&'a Device`. /// /// # Safety diff --git a/rust/kernel/of.rs b/rust/kernel/of.rs index 72c136fec1a2e0..836e4d9c5aa09f 100644 --- a/rust/kernel/of.rs +++ b/rust/kernel/of.rs @@ -95,7 +95,7 @@ impl Node { } /// Returns a reference to the underlying C `device_node` structure. - fn node(&self) -> &bindings::device_node { + pub fn node(&self) -> &bindings::device_node { // SAFETY: `raw_node` is valid per the type invariant. unsafe { &*self.raw_node } } @@ -355,6 +355,22 @@ impl<'a> Property<'a> { pub fn is_empty(&self) -> bool { self.len() == 0 } + + pub fn copy_to_slice(&self, target: &mut [T]) -> Result<()> { + if self.len() % T::UNIT_SIZE != 0 { + return Err(EINVAL); + } + + if self.len() / T::UNIT_SIZE != target.len() { + return Err(EINVAL); + } + + let val = self.value(); + for (i, off) in (0..self.len()).step_by(T::UNIT_SIZE).enumerate() { + target[i] = T::from_bytes(&val[off..off + T::UNIT_SIZE])? + } + Ok(()) + } } /// A trait that represents a value decodable from a property with a fixed unit size. From b27685702a0624103d167cda684c4bff2dc37675 Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Sat, 9 Nov 2024 18:31:23 +0100 Subject: [PATCH 2/7] soc: apple: Add support for the AOP co-processor. This is the base device for a multi-function co-processor present on certain Apple SoCs. On M-series Macs it is in charge of internal microphones, and various environmental sensors. Signed-off-by: Sasha Finkelstein --- drivers/soc/apple/Kconfig | 12 + drivers/soc/apple/Makefile | 2 + drivers/soc/apple/aop.rs | 909 +++++++++++++++++++++++++++++++++++ rust/kernel/soc/apple/aop.rs | 42 ++ rust/kernel/soc/apple/mod.rs | 3 + 5 files changed, 968 insertions(+) create mode 100644 drivers/soc/apple/aop.rs create mode 100644 rust/kernel/soc/apple/aop.rs diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig index bcbd07afb45dcb..7a434f526b0b3d 100644 --- a/drivers/soc/apple/Kconfig +++ b/drivers/soc/apple/Kconfig @@ -46,6 +46,18 @@ config RUST_APPLE_RTKIT depends on RUST depends on APPLE_RTKIT +config APPLE_AOP + tristate "Apple \"Always-on\" Processor" + depends on ARCH_APPLE || COMPILE_TEST + depends on RUST + select RUST_APPLE_RTKIT + default m if ARCH_APPLE + help + A co-processor persent on certain Apple SoCs controlling accelerometers, + gyros, ambient light sensors and microphones. Is not actually always on. + + Say 'y' here if you have an Apple laptop. + endmenu endif diff --git a/drivers/soc/apple/Makefile b/drivers/soc/apple/Makefile index 4d9ab8f3037b71..17af8e2b82d298 100644 --- a/drivers/soc/apple/Makefile +++ b/drivers/soc/apple/Makefile @@ -8,3 +8,5 @@ apple-rtkit-y = rtkit.o rtkit-crashlog.o obj-$(CONFIG_APPLE_SART) += apple-sart.o apple-sart-y = sart.o + +obj-$(CONFIG_APPLE_AOP) += aop.o diff --git a/drivers/soc/apple/aop.rs b/drivers/soc/apple/aop.rs new file mode 100644 index 00000000000000..5258338f3d3cd3 --- /dev/null +++ b/drivers/soc/apple/aop.rs @@ -0,0 +1,909 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +#![recursion_limit = "2048"] + +//! Apple AOP driver +//! +//! Copyright (C) The Asahi Linux Contributors + +use core::{arch::asm, mem, ptr, slice}; + +use kernel::{ + bindings, c_str, device, dma, + error::from_err_ptr, + io_mem::IoMem, + module_platform_driver, new_condvar, new_mutex, of, platform, + prelude::*, + soc::apple::aop::{from_fourcc, EPICService, FakehidListener, AOP}, + soc::apple::rtkit, + sync::{Arc, ArcBorrow, CondVar, Mutex}, + types::{ARef, ForeignOwnable}, + workqueue::{self, impl_has_work, new_work, Work, WorkItem}, +}; + +const AOP_MMIO_SIZE: usize = 0x1e0000; +const ASC_MMIO_SIZE: usize = 0x4000; +const BOOTARGS_OFFSET: usize = 0x22c; +const BOOTARGS_SIZE: usize = 0x230; +const CPU_CONTROL: usize = 0x44; +const CPU_RUN: u32 = 0x1 << 4; +const AFK_ENDPOINT_START: u8 = 0x20; +const AFK_ENDPOINT_COUNT: u8 = 0xc; +const AFK_OPC_GET_BUF: u64 = 0x89; +const AFK_OPC_INIT: u64 = 0x80; +const AFK_OPC_INIT_RX: u64 = 0x8b; +const AFK_OPC_INIT_TX: u64 = 0x8a; +const AFK_OPC_INIT_UNK: u64 = 0x8c; +const AFK_OPC_SEND: u64 = 0xa2; +const AFK_OPC_START_ACK: u64 = 0x86; +const AFK_OPC_SHUTDOWN_ACK: u64 = 0xc1; +const AFK_OPC_RECV: u64 = 0x85; +const AFK_MSG_GET_BUF_ACK: u64 = 0xa1 << 48; +const AFK_MSG_INIT: u64 = AFK_OPC_INIT << 48; +const AFK_MSG_INIT_ACK: u64 = 0xa0 << 48; +const AFK_MSG_START: u64 = 0xa3 << 48; +const AFK_MSG_SHUTDOWN: u64 = 0xc0 << 48; +const AFK_RB_BLOCK_STEP: usize = 0x40; +const EPIC_TYPE_NOTIFY: u32 = 0; +const EPIC_CATEGORY_REPORT: u8 = 0x00; +const EPIC_CATEGORY_NOTIFY: u8 = 0x10; +const EPIC_CATEGORY_REPLY: u8 = 0x20; +const EPIC_SUBTYPE_STD_SERVICE: u16 = 0xc0; +const EPIC_SUBTYPE_FAKEHID_REPORT: u16 = 0xc4; +const EPIC_SUBTYPE_RETCODE: u16 = 0x84; +const EPIC_SUBTYPE_RETCODE_PAYLOAD: u16 = 0xa0; +const QE_MAGIC1: u32 = from_fourcc(b" POI"); +const QE_MAGIC2: u32 = from_fourcc(b" POA"); + +fn align_up(v: usize, a: usize) -> usize { + (v + a - 1) & !(a - 1) +} + +#[inline(always)] +fn mem_sync() { + unsafe { + asm!("dsb sy"); + } +} + +#[repr(C, packed)] +#[derive(Clone, Copy, Default)] +struct QEHeader { + magic: u32, + size: u32, + channel: u32, + ty: u32, +} + +#[repr(C, packed)] +#[derive(Clone, Copy, Default)] +struct EPICHeader { + version: u8, + seq: u16, + _pad0: u8, + _unk0: u32, + timestamp: u64, + // Subheader + length: u32, + sub_version: u8, + category: u8, + subtype: u16, + tag: u16, + _unk1: u16, + _pad1: u64, + inline_len: u32, +} + +#[repr(C, packed)] +struct EPICServiceAnnounce { + name: [u8; 20], + _unk0: u32, + retcode: u32, + _unk1: u32, + channel: u32, + _unk2: u32, + _unk3: u32, +} + +#[pin_data] +struct FutureValue { + #[pin] + val: Mutex>, + #[pin] + completion: CondVar, +} + +impl FutureValue { + fn pin_init() -> impl PinInit> { + pin_init!( + FutureValue { + val <- new_mutex!(None), + completion <- new_condvar!() + } + ) + } + fn complete(&self, val: T) { + *self.val.lock() = Some(val); + self.completion.notify_all(); + } + fn wait(&self) -> T { + let mut ret_guard = self.val.lock(); + while ret_guard.is_none() { + self.completion.wait(&mut ret_guard); + } + ret_guard.as_ref().unwrap().clone() + } + fn reset(&self) { + *self.val.lock() = None; + } +} + +struct AFKRingBuffer { + offset: usize, + block_size: usize, + buf_size: usize, +} + +struct AFKEndpoint { + index: u8, + iomem: Option>, + txbuf: Option, + rxbuf: Option, + seq: u16, + calls: [Option>>; 8], +} + +unsafe impl Send for AFKEndpoint {} + +impl AFKEndpoint { + fn new(index: u8) -> AFKEndpoint { + AFKEndpoint { + index, + iomem: None, + txbuf: None, + rxbuf: None, + seq: 0, + calls: [const { None }; 8], + } + } + + fn start(&self, rtkit: &mut rtkit::RtKit) -> Result<()> { + rtkit.send_message(self.index, AFK_MSG_INIT) + } + + fn stop(&self, rtkit: &mut rtkit::RtKit) -> Result<()> { + rtkit.send_message(self.index, AFK_MSG_SHUTDOWN) + } + + fn recv_message( + &mut self, + client: ArcBorrow<'_, AopData>, + rtkit: &mut rtkit::RtKit, + msg: u64, + ) -> Result<()> { + let opc = msg >> 48; + match opc { + AFK_OPC_INIT => { + rtkit.send_message(self.index, AFK_MSG_INIT_ACK)?; + } + AFK_OPC_GET_BUF => { + self.recv_get_buf(client.dev.clone(), rtkit, msg)?; + } + AFK_OPC_INIT_UNK => {} // no-op + AFK_OPC_START_ACK => {} + AFK_OPC_INIT_RX => { + if self.rxbuf.is_some() { + dev_err!( + client.dev, + "Got InitRX message with existing rxbuf at endpoint {}", + self.index + ); + return Err(EIO); + } + self.rxbuf = Some(self.parse_ring_buf(&client.dev, msg)?); + if self.txbuf.is_some() { + rtkit.send_message(self.index, AFK_MSG_START)?; + } + } + AFK_OPC_INIT_TX => { + if self.txbuf.is_some() { + dev_err!( + client.dev, + "Got InitTX message with existing txbuf at endpoint {}", + self.index + ); + return Err(EIO); + } + self.txbuf = Some(self.parse_ring_buf(&client.dev, msg)?); + if self.rxbuf.is_some() { + rtkit.send_message(self.index, AFK_MSG_START)?; + } + } + AFK_OPC_RECV => { + self.recv_rb(client)?; + } + AFK_OPC_SHUTDOWN_ACK => { + client.shutdown_complete(); + } + _ => dev_err!( + client.dev, + "AFK endpoint {} got unknown message {}", + self.index, + msg + ), + } + Ok(()) + } + + fn parse_ring_buf(&self, dev: &ARef, msg: u64) -> Result { + let msg = msg as usize; + let size = ((msg >> 16) & 0xFFFF) * AFK_RB_BLOCK_STEP; + let offset = ((msg >> 32) & 0xFFFF) * AFK_RB_BLOCK_STEP; + let buf_size = self.iomem_read32(dev, offset)? as usize; + let block_size = (size - buf_size) / 3; + Ok(AFKRingBuffer { + offset, + block_size, + buf_size, + }) + } + fn iomem_write32(&mut self, dev: &ARef, off: usize, data: u32) -> Result<()> { + let iomem = self.iomem.as_mut().unwrap(); + if off + mem::size_of::() > iomem.count() { + dev_err!(dev, "Out of bounds iomem write"); + return Err(EIO); + } + unsafe { + let ptr = iomem.first_ptr_mut().offset(off as isize) as *mut u32; + *ptr = data; + } + Ok(()) + } + fn iomem_read32(&self, dev: &ARef, off: usize) -> Result { + let iomem = self.iomem.as_ref().unwrap(); + if off + mem::size_of::() > iomem.count() { + dev_err!(dev, "Out of bounds iomem read"); + return Err(EIO); + } + // SAFETY: all bit patterns are valid u32s + unsafe { + let ptr = iomem.first_ptr().offset(off as isize) as *const u32; + Ok(*ptr) + } + } + fn memcpy_from_iomem( + &self, + dev: &ARef, + off: usize, + target: &mut [u8], + ) -> Result<()> { + let iomem = self.iomem.as_ref().unwrap(); + if off + target.len() > iomem.count() { + dev_err!(dev, "Out of bounds iomem read"); + return Err(EIO); + } + // SAFETY: We checked that it is in bounds above + unsafe { + let ptr = iomem.first_ptr().offset(off as isize); + let src = slice::from_raw_parts(ptr, target.len()); + target.copy_from_slice(src); + } + Ok(()) + } + + fn memcpy_to_iomem(&self, dev: &ARef, off: usize, src: &[u8]) -> Result<()> { + let iomem = self.iomem.as_ref().unwrap(); + if off + src.len() > iomem.count() { + dev_err!(dev, "Out of bounds iomem write"); + return Err(EIO); + } + // SAFETY: We checked that it is in bounds above + unsafe { + let ptr = iomem.first_ptr_mut().offset(off as isize); + let target = slice::from_raw_parts_mut(ptr, src.len()); + target.copy_from_slice(src); + } + Ok(()) + } + + fn recv_get_buf( + &mut self, + dev: ARef, + rtkit: &mut rtkit::RtKit, + msg: u64, + ) -> Result<()> { + let size = ((msg & 0xFFFF0000) >> 16) as usize * AFK_RB_BLOCK_STEP; + if self.iomem.is_some() { + dev_err!( + dev, + "Got GetBuf message with existing buffer on endpoint {}", + self.index + ); + return Err(EIO); + } + let iomem = dma::try_alloc_coherent(dev, size, false)?; + rtkit.send_message(self.index, AFK_MSG_GET_BUF_ACK | iomem.dma_handle)?; + self.iomem = Some(iomem); + Ok(()) + } + + fn recv_rb(&mut self, client: ArcBorrow<'_, AopData>) -> Result<()> { + let (buf_offset, block_size, buf_size) = match self.rxbuf.as_ref() { + Some(b) => (b.offset, b.block_size, b.buf_size), + None => { + dev_err!( + client.dev, + "Got Recv message with no rxbuf at endpoint {}", + self.index + ); + return Err(EIO); + } + }; + let mut rptr = self.iomem_read32(&client.dev, buf_offset + block_size)? as usize; + let mut wptr = self.iomem_read32(&client.dev, buf_offset + block_size * 2)?; + mem_sync(); + let base = buf_offset + block_size * 3; + let mut msg_buf = KVec::new(); + const QEH_SIZE: usize = mem::size_of::(); + while wptr as usize != rptr { + let mut qeh_bytes = [0; QEH_SIZE]; + self.memcpy_from_iomem(&client.dev, base + rptr, &mut qeh_bytes)?; + let mut qeh = unsafe { &*(qeh_bytes.as_ptr() as *const QEHeader) }; + if qeh.magic != QE_MAGIC1 && qeh.magic != QE_MAGIC2 { + let magic = qeh.magic; + dev_err!( + client.dev, + "Invalid magic on ep {}, got {:x}", + self.index, + magic + ); + return Err(EIO); + } + if qeh.size as usize > (buf_size - rptr - QEH_SIZE) { + rptr = 0; + self.memcpy_from_iomem(&client.dev, base + rptr, &mut qeh_bytes)?; + qeh = unsafe { &*(qeh_bytes.as_ptr() as *const QEHeader) }; + + if qeh.magic != QE_MAGIC1 && qeh.magic != QE_MAGIC2 { + let magic = qeh.magic; + dev_err!( + client.dev, + "Invalid magic on ep {}, got {:x}", + self.index, + magic + ); + return Err(EIO); + } + } + msg_buf.resize(qeh.size as usize, 0, GFP_KERNEL)?; + self.memcpy_from_iomem(&client.dev, base + rptr + QEH_SIZE, &mut msg_buf)?; + let (hdr_bytes, msg) = msg_buf.split_at(mem::size_of::()); + let header = unsafe { &*(hdr_bytes.as_ptr() as *const EPICHeader) }; + self.handle_ipc(client, qeh, header, msg)?; + rptr = align_up(rptr + QEH_SIZE + qeh.size as usize, block_size) % buf_size; + mem_sync(); + self.iomem_write32(&client.dev, buf_offset + block_size, rptr as u32)?; + wptr = self.iomem_read32(&client.dev, buf_offset + block_size * 2)?; + mem_sync(); + } + Ok(()) + } + fn handle_ipc( + &mut self, + client: ArcBorrow<'_, AopData>, + qhdr: &QEHeader, + ehdr: &EPICHeader, + data: &[u8], + ) -> Result<()> { + let subtype = ehdr.subtype; + if ehdr.category == EPIC_CATEGORY_REPORT { + if subtype == EPIC_SUBTYPE_STD_SERVICE { + let announce = unsafe { &*(data.as_ptr() as *const EPICServiceAnnounce) }; + let chan = announce.channel; + let name_len = announce + .name + .iter() + .position(|x| *x == 0) + .unwrap_or(announce.name.len()); + return Into::>::into(client).register_service( + self, + chan, + &announce.name[..name_len], + ); + } else if subtype == EPIC_SUBTYPE_FAKEHID_REPORT { + return client.process_fakehid_report(self, qhdr.channel, data); + } else { + dev_err!( + client.dev, + "Unexpected EPIC report subtype {:x} on endpoint {}", + subtype, + self.index + ); + return Err(EIO); + } + } else if ehdr.category == EPIC_CATEGORY_REPLY { + if subtype == EPIC_SUBTYPE_RETCODE_PAYLOAD || subtype == EPIC_SUBTYPE_RETCODE { + if data.len() < mem::size_of::() { + dev_err!( + client.dev, + "Retcode data too short on endpoint {}", + self.index + ); + return Err(EIO); + } + let retcode = u32::from_ne_bytes(data[..4].try_into().unwrap()); + let tag = ehdr.tag as usize; + if tag == 0 || tag - 1 > self.calls.len() || self.calls[tag - 1].is_none() { + dev_err!( + client.dev, + "Got a retcode with invalid tag {:?} on endpoint {}", + tag, + self.index + ); + return Err(EIO); + } + self.calls[tag - 1].take().unwrap().complete(retcode); + return Ok(()); + } else { + dev_err!( + client.dev, + "Unexpected EPIC reply subtype {:x} on endpoint {}", + subtype, + self.index + ); + return Err(EIO); + } + } + dev_err!( + client.dev, + "Unexpected EPIC category {:x} on endpoint {}", + ehdr.category, + self.index + ); + Err(EIO) + } + fn send_rb( + &mut self, + client: &AopData, + rtkit: &mut rtkit::RtKit, + channel: u32, + ty: u32, + header: &[u8], + data: &[u8], + ) -> Result<()> { + let (buf_offset, block_size, buf_size) = match self.txbuf.as_ref() { + Some(b) => (b.offset, b.block_size, b.buf_size), + None => { + dev_err!( + client.dev, + "Attempting to send message with no txbuf at endpoint {}", + self.index + ); + return Err(EIO); + } + }; + let base = buf_offset + block_size * 3; + mem_sync(); + let rptr = self.iomem_read32(&client.dev, buf_offset + block_size)? as usize; + let mut wptr = self.iomem_read32(&client.dev, buf_offset + block_size * 2)? as usize; + const QEH_SIZE: usize = mem::size_of::(); + if wptr < rptr && wptr + QEH_SIZE >= rptr { + dev_err!(client.dev, "Tx buffer full at endpoint {}", self.index); + return Err(EIO); + } + let payload_len = header.len() + data.len(); + let qeh = QEHeader { + magic: QE_MAGIC1, + size: payload_len as u32, + channel, + ty, + }; + let qeh_bytes = unsafe { + slice::from_raw_parts( + &qeh as *const QEHeader as *const u8, + mem::size_of::(), + ) + }; + self.memcpy_to_iomem(&client.dev, base + wptr, qeh_bytes)?; + if payload_len > buf_size - wptr - QEH_SIZE { + wptr = 0; + self.memcpy_to_iomem(&client.dev, base + wptr, qeh_bytes)?; + } + self.memcpy_to_iomem(&client.dev, base + wptr + QEH_SIZE, header)?; + self.memcpy_to_iomem(&client.dev, base + wptr + QEH_SIZE + header.len(), data)?; + wptr = align_up(wptr + QEH_SIZE + payload_len, block_size) % buf_size; + self.iomem_write32(&client.dev, buf_offset + block_size * 2, wptr as u32)?; + let msg = wptr as u64 | (AFK_OPC_SEND << 48); + rtkit.send_message(self.index, msg) + } + fn epic_notify( + &mut self, + client: &AopData, + rtkit: &mut rtkit::RtKit, + channel: u32, + subtype: u16, + data: &[u8], + ) -> Result>> { + let mut tag = 0; + for i in 0..self.calls.len() { + if self.calls[i].is_none() { + tag = i + 1; + break; + } + } + if tag == 0 { + dev_err!( + client.dev, + "Too many inflight calls on endpoint {}", + self.index + ); + return Err(EIO); + } + let call = Arc::pin_init(FutureValue::pin_init(), GFP_KERNEL)?; + let hdr = EPICHeader { + version: 2, + seq: self.seq, + length: data.len() as u32, + sub_version: 2, + category: EPIC_CATEGORY_NOTIFY, + subtype, + tag: tag as u16, + ..EPICHeader::default() + }; + self.send_rb( + client, + rtkit, + channel, + EPIC_TYPE_NOTIFY, + unsafe { + slice::from_raw_parts( + &hdr as *const EPICHeader as *const u8, + mem::size_of::(), + ) + }, + data, + )?; + self.seq = self.seq.wrapping_add(1); + self.calls[tag - 1] = Some(call.clone()); + Ok(call) + } +} + +struct ListenerEntry { + svc: EPICService, + listener: Arc, +} + +unsafe impl Send for ListenerEntry {} + +#[pin_data] +struct AopData { + dev: ARef, + aop_mmio: IoMem, + asc_mmio: IoMem, + #[pin] + rtkit: Mutex>>, + #[pin] + endpoints: [Mutex; AFK_ENDPOINT_COUNT as usize], + #[pin] + ep_shutdown: FutureValue<()>, + #[pin] + hid_listeners: Mutex>, + #[pin] + subdevices: Mutex>, +} + +unsafe impl Send for AopData {} +unsafe impl Sync for AopData {} + +#[pin_data] +struct AopServiceRegisterWork { + name: &'static CStr, + data: Arc, + service: EPICService, + #[pin] + work: Work, +} + +impl_has_work! { + impl HasWork for AopServiceRegisterWork { self.work } +} + +impl AopServiceRegisterWork { + fn new(name: &'static CStr, data: Arc, service: EPICService) -> Result> { + Arc::pin_init( + pin_init!(AopServiceRegisterWork { + name, data, service, + work <- new_work!("AopServiceRegisterWork::work"), + }), + GFP_KERNEL, + ) + } +} + +impl WorkItem for AopServiceRegisterWork { + type Pointer = Arc; + + fn run(this: Arc) { + let info = bindings::platform_device_info { + parent: this.data.dev.as_raw(), + name: this.name.as_ptr() as *const _, + id: bindings::PLATFORM_DEVID_AUTO, + res: ptr::null_mut(), + num_res: 0, + data: &this.service as *const EPICService as *const _, + size_data: mem::size_of::(), + dma_mask: 0, + fwnode: ptr::null_mut(), + properties: ptr::null_mut(), + of_node_reused: false, + }; + let pdev = unsafe { from_err_ptr(bindings::platform_device_register_full(&info)) }; + match pdev { + Err(e) => { + dev_err!( + this.data.dev, + "Failed to create device for service {:?}: {:?}", + this.name, + e + ); + } + Ok(pdev) => { + let res = this.data.subdevices.lock().push(pdev, GFP_KERNEL); + if res.is_err() { + dev_err!(this.data.dev, "Failed to store subdevice"); + } + } + } + } +} + +impl AopData { + fn new(pdev: &mut platform::Device) -> Result> { + let aop_mmio = unsafe { pdev.ioremap_resource(0)? }; + let asc_mmio = unsafe { pdev.ioremap_resource(1)? }; + Arc::pin_init( + pin_init!( + AopData { + dev: pdev.get_device(), + aop_mmio, + asc_mmio, + rtkit <- new_mutex!(None), + endpoints <- init::pin_init_array_from_fn(|i| { + new_mutex!(AFKEndpoint::new(AFK_ENDPOINT_START + i as u8)) + }), + ep_shutdown <- FutureValue::pin_init(), + hid_listeners <- new_mutex!(KVec::new()), + subdevices <- new_mutex!(KVec::new()), + } + ), + GFP_KERNEL, + ) + } + fn start(&self) -> Result<()> { + { + let mut guard = self.rtkit.lock(); + let rtk = guard.as_mut().unwrap(); + rtk.wake()?; + } + for ep in 0..AFK_ENDPOINT_COUNT { + let rtk_ep_num = AFK_ENDPOINT_START + ep; + let mut guard = self.rtkit.lock(); + let rtk = guard.as_mut().unwrap(); + if !rtk.has_endpoint(rtk_ep_num) { + continue; + } + rtk.start_endpoint(rtk_ep_num)?; + let ep_guard = self.endpoints[ep as usize].lock(); + ep_guard.start(rtk)?; + } + Ok(()) + } + fn register_service( + self: Arc, + ep: &mut AFKEndpoint, + channel: u32, + name: &[u8], + ) -> Result<()> { + let svc = EPICService { + channel, + endpoint: ep.index, + }; + let dev_name = match name { + b"aop-audio" => c_str!("snd_soc_apple_aop"), + b"las" => c_str!("iio_aop_las"), + b"als" => c_str!("iio_aop_als"), + _ => { + return Ok(()); + } + }; + // probe can call back into us, run it with locks dropped. + let work = AopServiceRegisterWork::new(dev_name, self, svc)?; + workqueue::system().enqueue(work).map_err(|_| ENOMEM) + } + + fn process_fakehid_report(&self, ep: &AFKEndpoint, ch: u32, data: &[u8]) -> Result<()> { + let guard = self.hid_listeners.lock(); + for entry in &*guard { + if entry.svc.endpoint == ep.index && entry.svc.channel == ch { + return entry.listener.process_fakehid_report(data); + } + } + Ok(()) + } + + fn shutdown_complete(&self) { + self.ep_shutdown.complete(()); + } + + fn stop(&self) -> Result<()> { + for ep in 0..AFK_ENDPOINT_COUNT { + { + let rtk_ep_num = AFK_ENDPOINT_START + ep; + let mut guard = self.rtkit.lock(); + let rtk = guard.as_mut().unwrap(); + if !rtk.has_endpoint(rtk_ep_num) { + continue; + } + let ep_guard = self.endpoints[ep as usize].lock(); + ep_guard.stop(rtk)?; + } + self.ep_shutdown.wait(); + self.ep_shutdown.reset(); + } + Ok(()) + } + + fn aop_read32(&self, off: usize) -> u32 { + self.aop_mmio.readl_relaxed(off) + } + + fn patch_bootargs(&self, patches: &[(u32, u32)]) -> Result<()> { + let offset = self.aop_read32(BOOTARGS_OFFSET) as usize; + let size = self.aop_read32(BOOTARGS_SIZE) as usize; + let mut arg_bytes = KVec::with_capacity(size, GFP_KERNEL)?; + for _ in 0..size { + arg_bytes.push(0, GFP_KERNEL).unwrap(); + } + self.aop_mmio.try_memcpy_fromio(&mut arg_bytes, offset)?; + let mut idx = 0; + while idx < size { + let key = u32::from_le_bytes(arg_bytes[idx..idx + 4].try_into().unwrap()); + let size = u32::from_le_bytes(arg_bytes[idx + 4..idx + 8].try_into().unwrap()) as usize; + idx += 8; + for (k, v) in patches.iter() { + if *k != key { + continue; + } + arg_bytes[idx..idx + size].copy_from_slice(&(*v as u64).to_le_bytes()[..size]); + break; + } + idx += size; + } + self.aop_mmio.try_memcpy_toio(offset, &arg_bytes) + } + + fn start_cpu(&self) { + let val = self.asc_mmio.readl_relaxed(CPU_CONTROL); + self.asc_mmio.writel_relaxed(val | CPU_RUN, CPU_CONTROL); + } +} + +impl AOP for AopData { + fn epic_call(&self, svc: &EPICService, subtype: u16, msg_bytes: &[u8]) -> Result { + let ep_idx = svc.endpoint - AFK_ENDPOINT_START; + let call = { + let mut rtk_guard = self.rtkit.lock(); + let rtk = rtk_guard.as_mut().unwrap(); + let mut ep_guard = self.endpoints[ep_idx as usize].lock(); + ep_guard.epic_notify(self, rtk, svc.channel, subtype, msg_bytes)? + }; + Ok(call.wait()) + } + fn add_fakehid_listener( + &self, + svc: EPICService, + listener: Arc, + ) -> Result<()> { + let mut guard = self.hid_listeners.lock(); + Ok(guard.push(ListenerEntry { svc, listener }, GFP_KERNEL)?) + } + fn remove_fakehid_listener(&self, svc: &EPICService) -> bool { + let mut guard = self.hid_listeners.lock(); + for i in 0..guard.len() { + if guard[i].svc == *svc { + guard.swap_remove(i); + return true; + } + } + false + } + fn remove(&self) { + if let Err(e) = self.stop() { + dev_err!(self.dev, "Failed to stop AOP {:?}", e); + } + *self.rtkit.lock() = None; + let guard = self.subdevices.lock(); + for pdev in &*guard { + unsafe { + bindings::platform_device_unregister(*pdev); + } + } + } +} + +struct NoBuffer; +impl rtkit::Buffer for NoBuffer { + fn iova(&self) -> Result { + unreachable!() + } + fn buf(&mut self) -> Result<&mut [u8]> { + unreachable!() + } +} + +#[vtable] +impl rtkit::Operations for AopData { + type Data = Arc; + type Buffer = NoBuffer; + + fn recv_message(data: ::Borrowed<'_>, ep: u8, msg: u64) { + let mut rtk = data.rtkit.lock(); + let mut ep_guard = data.endpoints[(ep - AFK_ENDPOINT_START) as usize].lock(); + let ret = ep_guard.recv_message(data, rtk.as_mut().unwrap(), msg); + if let Err(e) = ret { + dev_err!(data.dev, "Failed to handle rtkit message, error: {:?}", e); + } + } + + fn crashed(data: ::Borrowed<'_>) { + dev_err!(data.dev, "AOP firmware crashed"); + } +} + +#[repr(transparent)] +struct AopDriver(Arc); + +kernel::of_device_table!( + OF_TABLE, + MODULE_OF_TABLE, + (), + [(of::DeviceId::new(c_str!("apple,aop")), ())] +); + +impl platform::Driver for AopDriver { + type IdInfo = (); + + const ID_TABLE: platform::IdTable<()> = &OF_TABLE; + + fn probe(pdev: &mut platform::Device, _info: Option<&()>) -> Result>> { + let dev = pdev.get_device(); + let data = AopData::new(pdev)?; + let of = dev.of_node().ok_or(EIO)?; + let alig = of.get_property(c_str!("apple,aop-alignment"))?; + let aopt = of.get_property(c_str!("apple,aop-target"))?; + data.patch_bootargs(&[ + (from_fourcc(b"EC0p"), 0x20000), + (from_fourcc(b"nCal"), 0x0), + (from_fourcc(b"alig"), alig), + (from_fourcc(b"AOPt"), aopt), + ])?; + let rtkit = rtkit::RtKit::::new(&dev, None, 0, data.clone())?; + *data.rtkit.lock() = Some(rtkit); + data.start_cpu(); + data.start()?; + let data = data as Arc; + Ok(KBox::pin(AopDriver(data), GFP_KERNEL)?) + } +} + +impl Drop for AopDriver { + fn drop(&mut self) { + self.0.remove(); + } +} + +module_platform_driver! { + type: AopDriver, + name: "apple_aop", + license: "Dual MIT/GPL", +} diff --git a/rust/kernel/soc/apple/aop.rs b/rust/kernel/soc/apple/aop.rs new file mode 100644 index 00000000000000..37aae200b81eb4 --- /dev/null +++ b/rust/kernel/soc/apple/aop.rs @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT + +//! Common code for AOP endpoint drivers + +use kernel::{prelude::*, sync::Arc}; + +/// Representation of an "EPIC" service. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(C)] +pub struct EPICService { + /// Channel id + pub channel: u32, + /// RTKit endpoint + pub endpoint: u8, +} + +/// Listener for the "HID" events sent by aop +pub trait FakehidListener { + /// Process the event. + fn process_fakehid_report(&self, data: &[u8]) -> Result<()>; +} + +/// AOP communications manager. +pub trait AOP: Send + Sync { + /// Calls a method on a specified service + fn epic_call(&self, svc: &EPICService, subtype: u16, msg_bytes: &[u8]) -> Result; + /// Adds the listener for the specified service + fn add_fakehid_listener( + &self, + svc: EPICService, + listener: Arc, + ) -> Result<()>; + /// Remove the listener for the specified service + fn remove_fakehid_listener(&self, svc: &EPICService) -> bool; + /// Internal method to detach the device. + fn remove(&self); +} + +/// Converts a text representation of a FourCC to u32 +pub const fn from_fourcc(b: &[u8]) -> u32 { + b[3] as u32 | (b[2] as u32) << 8 | (b[1] as u32) << 16 | (b[0] as u32) << 24 +} diff --git a/rust/kernel/soc/apple/mod.rs b/rust/kernel/soc/apple/mod.rs index dd69db63677dd6..dd0b018a4f8db1 100644 --- a/rust/kernel/soc/apple/mod.rs +++ b/rust/kernel/soc/apple/mod.rs @@ -4,3 +4,6 @@ #[cfg(CONFIG_APPLE_RTKIT = "y")] pub mod rtkit; + +#[cfg(any(CONFIG_APPLE_AOP = "y", CONFIG_APPLE_AOP = "m"))] +pub mod aop; From f5e992519fb170f699869bfeb810fabc057280e2 Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Mon, 18 Nov 2024 00:03:20 +0100 Subject: [PATCH 3/7] ASoC: apple: Add aop_audio driver. Apple SoCs have their microphones connected to the AOP co-processor, in order to among other things implement the "voicetrigger" functionality. Add a driver for the "High power audio input" AOP endpoint. Signed-off-by: Sasha Finkelstein --- sound/soc/apple/Kconfig | 11 + sound/soc/apple/Makefile | 3 + sound/soc/apple/aop_audio.rs | 683 +++++++++++++++++++++++++++++++++++ 3 files changed, 697 insertions(+) create mode 100644 sound/soc/apple/aop_audio.rs diff --git a/sound/soc/apple/Kconfig b/sound/soc/apple/Kconfig index 793f7782e0d721..e24baa86dfd105 100644 --- a/sound/soc/apple/Kconfig +++ b/sound/soc/apple/Kconfig @@ -1,3 +1,14 @@ +config SND_SOC_APPLE_AOP_AUDIO + tristate "AOP audio driver" + depends on ARCH_APPLE || COMPILE_TEST + depends on RUST + select APPLE_AOP + default m if ARCH_APPLE + help + This option enables an ASoC driver for sound devices connected to the AOP + co-processor on ARM Macs. This includes the built-in microphone on those + machines. + config SND_SOC_APPLE_MCA tristate "Apple Silicon MCA driver" depends on ARCH_APPLE || COMPILE_TEST diff --git a/sound/soc/apple/Makefile b/sound/soc/apple/Makefile index 1eb8fbef60c617..040b002e728198 100644 --- a/sound/soc/apple/Makefile +++ b/sound/soc/apple/Makefile @@ -1,3 +1,6 @@ +snd-soc-aop-y := aop_audio.o +obj-$(CONFIG_SND_SOC_APPLE_AOP_AUDIO) += snd-soc-aop.o + snd-soc-apple-mca-y := mca.o obj-$(CONFIG_SND_SOC_APPLE_MCA) += snd-soc-apple-mca.o diff --git a/sound/soc/apple/aop_audio.rs b/sound/soc/apple/aop_audio.rs new file mode 100644 index 00000000000000..ae2a48babcb673 --- /dev/null +++ b/sound/soc/apple/aop_audio.rs @@ -0,0 +1,683 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +#![recursion_limit = "2048"] + +//! Apple AOP audio driver +//! +//! Copyright (C) The Asahi Linux Contributors + +use core::sync::atomic::{AtomicU32, Ordering}; +use core::{mem, ptr, slice}; + +use kernel::{ + bindings, c_str, device, + error::from_err_ptr, + init::Zeroable, + module_platform_driver, + of::{self, Node}, + platform, + prelude::*, + soc::apple::aop::{from_fourcc, EPICService, AOP}, + sync::Arc, + types::{ARef, ForeignOwnable}, +}; + +const EPIC_SUBTYPE_WRAPPED_CALL: u16 = 0x20; +const CALLTYPE_AUDIO_ATTACH_DEVICE: u32 = 0xc3000002; +const CALLTYPE_AUDIO_SET_PROP: u32 = 0xc3000005; +const PDM_NUM_COEFFS: usize = 120; +const AUDIO_DEV_PDM0: u32 = from_fourcc(b"pdm0"); +const AUDIO_DEV_LPAI: u32 = from_fourcc(b"lpai"); +const AUDIO_DEV_HPAI: u32 = from_fourcc(b"hpai"); +const POWER_STATE_OFF: u32 = from_fourcc(b"idle"); +const POWER_STATE_IDLE: u32 = from_fourcc(b"pw1 "); +const POWER_STATE_ON: u32 = from_fourcc(b"pwrd"); + +#[repr(C, packed)] +#[derive(Clone, Copy, Default)] +struct AudioAttachDevice { + _zero0: u32, + unk0: u32, + calltype: u32, + _zero1: u64, + _zero2: u64, + _pad0: u32, + len: u64, + dev_id: u32, + _pad1: u32, +} + +impl AudioAttachDevice { + fn new(dev_id: u32) -> AudioAttachDevice { + AudioAttachDevice { + unk0: 0xFFFFFFFF, + calltype: CALLTYPE_AUDIO_ATTACH_DEVICE, + dev_id, + len: 0x2c, + ..AudioAttachDevice::default() + } + } +} + +#[repr(C, packed)] +#[derive(Clone, Copy, Default)] +struct LpaiChannelConfig { + unk1: u32, + unk2: u32, + unk3: u32, + unk4: u32, +} + +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +struct PDMConfig { + bytes_per_sample: u32, + clock_source: u32, + pdm_frequency: u32, + pdmc_frequency: u32, + slow_clock_speed: u32, + fast_clock_speed: u32, + channel_polarity_select: u32, + channel_phase_select: u32, + unk1: u32, + unk2: u16, + ratio1: u8, + ratio2: u8, + ratio3: u8, + _pad0: u8, + filter_lengths: u32, + coeff_bulk: u32, + coeffs: [u8; PDM_NUM_COEFFS * mem::size_of::()], + unk3: u32, + mic_turn_on_time_ms: u32, + _zero0: u64, + _zero1: u64, + unk4: u32, + mic_settle_time_ms: u32, + _zero2: [u8; 69], // ????? +} + +unsafe impl Zeroable for PDMConfig {} + +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +struct DecimatorConfig { + latency: u32, + ratio1: u8, + ratio2: u8, + ratio3: u8, + _pad0: u8, + filter_lengths: u32, + coeff_bulk: u32, + coeffs: [u8; PDM_NUM_COEFFS * mem::size_of::()], +} + +unsafe impl Zeroable for DecimatorConfig {} + +#[repr(C, packed)] +#[derive(Clone, Copy, Default, Debug)] +struct PowerSetting { + dev_id: u32, + cookie: u32, + _unk0: u32, + _zero0: u64, + target_pstate: u32, + unk1: u32, + _zero1: [u8; 20], +} + +impl PowerSetting { + fn new(dev_id: u32, cookie: u32, target_pstate: u32, unk1: u32) -> PowerSetting { + PowerSetting { + dev_id, + cookie, + target_pstate, + unk1, + ..PowerSetting::default() + } + } +} + +#[repr(C, packed)] +#[derive(Clone, Copy, Default, Debug)] +struct AudioSetDeviceProp { + _zero0: u32, + unk0: u32, + calltype: u32, + _zero1: u64, + _zero2: u64, + _pad0: u32, + len: u64, + dev_id: u32, + modifier: u32, + len2: u32, + data: T, +} + +impl AudioSetDeviceProp { + fn new(dev_id: u32, modifier: u32, data: T) -> AudioSetDeviceProp { + AudioSetDeviceProp { + unk0: 0xFFFFFFFF, + calltype: CALLTYPE_AUDIO_SET_PROP, + dev_id, + modifier, + len: mem::size_of::() as u64 + 0x30, + len2: mem::size_of::() as u32, + data, + ..AudioSetDeviceProp::default() + } + } +} + +unsafe impl Zeroable for AudioSetDeviceProp {} + +impl AudioSetDeviceProp { + fn try_init( + dev_id: u32, + modifier: u32, + data: impl Init, + ) -> impl Init, Error> + where + Error: From, + { + try_init!( + AudioSetDeviceProp { + unk0: 0xFFFFFFFF, + calltype: CALLTYPE_AUDIO_SET_PROP, + dev_id, + modifier, + len: mem::size_of::() as u64 + 0x30, + len2: mem::size_of::() as u32, + data <- data, + ..Zeroable::zeroed() + } + ) + } +} + +struct SndSocAopData { + dev: ARef, + adata: Arc, + service: EPICService, + pstate_cookie: AtomicU32, + of: Node, +} + +impl SndSocAopData { + fn new( + dev: ARef, + adata: Arc, + service: EPICService, + of: Node, + ) -> Result> { + Ok(Arc::new( + SndSocAopData { + dev, + adata, + service, + of, + pstate_cookie: AtomicU32::new(1), + }, + GFP_KERNEL, + )?) + } + fn set_pdm_config(&self) -> Result<()> { + let pdm_cfg = try_init!(PDMConfig { + bytes_per_sample: self.of.get_property(c_str!("apple,bytes-per-sample"))?, + clock_source: self.of.get_property(c_str!("apple,clock-source"))?, + pdm_frequency: self.of.get_property(c_str!("apple,pdm-frequency"))?, + pdmc_frequency: self.of.get_property(c_str!("apple,pdmc-frequency"))?, + slow_clock_speed: self.of.get_property(c_str!("apple,slow-clock-speed"))?, + fast_clock_speed: self.of.get_property(c_str!("apple,fast-clock-speed"))?, + channel_polarity_select: self + .of + .get_property(c_str!("apple,channel-polarity-select"))?, + channel_phase_select: self.of.get_property(c_str!("apple,channel-phase-select"))?, + unk1: 0xf7600, + unk2: 0, + filter_lengths: self.of.get_property(c_str!("apple,filter-lengths"))?, + coeff_bulk: PDM_NUM_COEFFS as u32, + unk3: 1, + mic_turn_on_time_ms: self.of.get_property(c_str!("apple,mic-turn-on-time-ms"))?, + unk4: 1, + mic_settle_time_ms: self.of.get_property(c_str!("apple,mic-settle-time-ms"))?, + ..Zeroable::zeroed() + }) + .chain(|ret| { + let prop = self + .of + .find_property(c_str!("apple,decm-ratios")) + .ok_or(EIO)?; + let ratios = prop.value(); + ret.ratio1 = ratios[0]; + ret.ratio2 = ratios[1]; + ret.ratio3 = ratios[2]; + let n_coeffs = (ratios[0] + ratios[1] + ratios[2] + 3) as usize * 16; + self.of + .find_property(c_str!("apple,coefficients")) + .ok_or(EIO)? + .copy_to_slice(&mut ret.coeffs[..n_coeffs]) + }); + let set_prop = AudioSetDeviceProp::::try_init(AUDIO_DEV_PDM0, 200, pdm_cfg); + let msg = KBox::try_init(set_prop, GFP_KERNEL)?; + let ret = self.epic_wrapped_call(msg.as_ref())?; + if ret != 0 { + dev_err!(self.dev, "Unable to set pdm config, return code {}", ret); + return Err(EIO); + } else { + Ok(()) + } + } + fn set_decimator_config(&self) -> Result<()> { + let pdm_cfg = try_init!(DecimatorConfig { + latency: self.of.get_property(c_str!("apple,decm-latency"))?, + filter_lengths: self.of.get_property(c_str!("apple,filter-lengths"))?, + coeff_bulk: 120, + ..Zeroable::zeroed() + }) + .chain(|ret| { + let prop = self + .of + .find_property(c_str!("apple,decm-ratios")) + .ok_or(EIO)?; + let ratios = prop.value(); + ret.ratio1 = ratios[0]; + ret.ratio2 = ratios[1]; + ret.ratio3 = ratios[2]; + let n_coeffs = (ratios[0] + ratios[1] + ratios[2] + 3) as usize * 16; + self.of + .find_property(c_str!("apple,coefficients")) + .ok_or(EIO)? + .copy_to_slice(&mut ret.coeffs[..n_coeffs]) + }); + let set_prop = + AudioSetDeviceProp::::try_init(AUDIO_DEV_PDM0, 210, pdm_cfg); + let msg = KBox::try_init(set_prop, GFP_KERNEL)?; + let ret = self.epic_wrapped_call(msg.as_ref())?; + if ret != 0 { + dev_err!( + self.dev, + "Unable to set decimator config, return code {}", + ret + ); + return Err(EIO); + } else { + Ok(()) + } + } + fn set_lpai_channel_cfg(&self) -> Result<()> { + let cfg = LpaiChannelConfig { + unk1: 7, + unk2: 7, + unk3: 1, + unk4: 7, + }; + let msg = AudioSetDeviceProp::new(AUDIO_DEV_LPAI, 301, cfg); + let ret = self.epic_wrapped_call(&msg)?; + if ret != 0 { + dev_err!( + self.dev, + "Unable to set lpai channel config, return code {}", + ret + ); + return Err(EIO); + } else { + Ok(()) + } + } + fn audio_attach_device(&self, dev_id: u32) -> Result<()> { + let msg = AudioAttachDevice::new(dev_id); + let ret = self.epic_wrapped_call(&msg)?; + if ret != 0 { + dev_err!( + self.dev, + "Unable to attach device {:?}, return code {}", + dev_id, + ret + ); + return Err(EIO); + } else { + Ok(()) + } + } + fn set_audio_power(&self, pstate: u32, unk1: u32) -> Result<()> { + let set_pstate = PowerSetting::new( + AUDIO_DEV_HPAI, + self.pstate_cookie.fetch_add(1, Ordering::Relaxed), + pstate, + unk1, + ); + let msg = AudioSetDeviceProp::new(AUDIO_DEV_HPAI, 202, set_pstate); + let ret = self.epic_wrapped_call(&msg)?; + if ret != 0 { + dev_err!( + self.dev, + "Unable to set power state {:?}, return code {}", + pstate, + ret + ); + return Err(EIO); + } else { + Ok(()) + } + } + fn epic_wrapped_call(&self, data: &T) -> Result { + let msg_bytes = + unsafe { slice::from_raw_parts(data as *const T as *const u8, mem::size_of::()) }; + self.adata + .epic_call(&self.service, EPIC_SUBTYPE_WRAPPED_CALL, msg_bytes) + } + fn request_dma_channel(&self) -> Result<*mut bindings::dma_chan> { + let res = unsafe { + from_err_ptr(bindings::of_dma_request_slave_channel( + self.of.node() as *const _ as *mut _, + c_str!("dma").as_ptr() as _, + )) + }; + if res.is_err() { + dev_err!(self.dev, "Unable to get dma channel"); + } + res + } +} + +#[repr(transparent)] +struct SndSocAopDriver(*mut bindings::snd_card); + +fn copy_str(target: &mut [i8], source: &[u8]) { + for i in 0..source.len() { + target[i] = source[i] as _; + } +} + +unsafe fn dmaengine_slave_config( + chan: *mut bindings::dma_chan, + config: *mut bindings::dma_slave_config, +) -> i32 { + unsafe { + match (*(*chan).device).device_config { + Some(dc) => dc(chan, config), + None => ENOSYS.to_errno(), + } + } +} + +unsafe extern "C" fn aop_hw_params( + substream: *mut bindings::snd_pcm_substream, + params: *mut bindings::snd_pcm_hw_params, +) -> i32 { + let chan = unsafe { bindings::snd_dmaengine_pcm_get_chan(substream) }; + let mut slave_config = bindings::dma_slave_config::default(); + let ret = + unsafe { bindings::snd_hwparams_to_dma_slave_config(substream, params, &mut slave_config) }; + if ret < 0 { + return ret; + } + slave_config.src_port_window_size = 4; + unsafe { dmaengine_slave_config(chan, &mut slave_config) } +} + +unsafe extern "C" fn aop_pcm_open(substream: *mut bindings::snd_pcm_substream) -> i32 { + let data = unsafe { Arc::::borrow((*substream).private_data) }; + if let Err(e) = data.set_audio_power(POWER_STATE_IDLE, 0) { + dev_err!(data.dev, "Unable to enter 'pw1 ' state"); + return e.to_errno(); + } + let mut hwparams = bindings::snd_pcm_hardware { + info: bindings::SNDRV_PCM_INFO_MMAP + | bindings::SNDRV_PCM_INFO_MMAP_VALID + | bindings::SNDRV_PCM_INFO_INTERLEAVED, + formats: bindings::BINDINGS_SNDRV_PCM_FMTBIT_FLOAT_LE, + subformats: 0, + rates: bindings::SNDRV_PCM_RATE_48000, + rate_min: 48000, + rate_max: 48000, + channels_min: 3, + channels_max: 3, + periods_min: 2, + buffer_bytes_max: usize::MAX, + period_bytes_max: 0x4000, + periods_max: u32::MAX, + period_bytes_min: 256, + fifo_size: 16, + }; + let dma_chan = match data.request_dma_channel() { + Ok(dc) => dc, + Err(e) => return e.to_errno(), + }; + let ret = unsafe { + let mut dai_data = bindings::snd_dmaengine_dai_dma_data::default(); + bindings::snd_dmaengine_pcm_refine_runtime_hwparams( + substream, + &mut dai_data, + &mut hwparams, + dma_chan, + ) + }; + if ret != 0 { + dev_err!(data.dev, "Unable to refine hwparams"); + return ret; + } + if let Err(e) = data.set_audio_power(POWER_STATE_ON, 1) { + dev_err!(data.dev, "Unable to power mic on"); + return e.to_errno(); + } + unsafe { + (*(*substream).runtime).hw = hwparams; + bindings::snd_dmaengine_pcm_open(substream, dma_chan) + } +} + +unsafe extern "C" fn aop_pcm_prepare(_: *mut bindings::snd_pcm_substream) -> i32 { + 0 +} + +unsafe extern "C" fn aop_pcm_close(substream: *mut bindings::snd_pcm_substream) -> i32 { + let data = unsafe { Arc::::borrow((*substream).private_data) }; + if let Err(e) = data.set_audio_power(POWER_STATE_IDLE, 1) { + dev_err!(data.dev, "Unable to power mic off"); + return e.to_errno(); + } + let ret = unsafe { bindings::snd_dmaengine_pcm_close_release_chan(substream) }; + if ret != 0 { + dev_err!(data.dev, "Unable to close channel"); + return ret; + } + if let Err(e) = data.set_audio_power(POWER_STATE_OFF, 0) { + dev_err!(data.dev, "Unable to enter 'idle' power state"); + return e.to_errno(); + } + 0 +} + +unsafe extern "C" fn aop_pcm_free_private(pcm: *mut bindings::snd_pcm) { + unsafe { + Arc::::from_foreign((*pcm).private_data); + } +} + +impl SndSocAopDriver { + const VTABLE: bindings::snd_pcm_ops = bindings::snd_pcm_ops { + open: Some(aop_pcm_open), + close: Some(aop_pcm_close), + prepare: Some(aop_pcm_prepare), + trigger: Some(bindings::snd_dmaengine_pcm_trigger), + pointer: Some(bindings::snd_dmaengine_pcm_pointer), + ioctl: None, + hw_params: Some(aop_hw_params), + hw_free: None, + sync_stop: None, + get_time_info: None, + fill_silence: None, + copy: None, + page: None, + mmap: None, + ack: None, + }; + fn new(data: Arc) -> Result { + let mut this = SndSocAopDriver(ptr::null_mut()); + let ret = unsafe { + bindings::snd_card_new( + data.dev.as_raw(), + -1, + ptr::null(), + THIS_MODULE.as_ptr(), + 0, + &mut this.0, + ) + }; + if ret < 0 { + dev_err!(data.dev, "Unable to allocate sound card"); + return Err(Error::from_errno(ret)); + } + let chassis = data + .of + .find_property(c_str!("apple,chassis-name")) + .ok_or(EIO)?; + let machine_kind = data + .of + .find_property(c_str!("apple,machine-kind")) + .ok_or(EIO)?; + unsafe { + let name = b"aop_audio\0"; + let target = (*this.0).driver.as_mut(); + copy_str(target, name.as_ref()); + } + unsafe { + let prefix = b"Apple"; + let target = (*this.0).id.as_mut(); + copy_str(target, prefix.as_ref()); + let mut ptr = prefix.len(); + copy_str(&mut target[ptr..], chassis.value()); + ptr += chassis.len() - 1; + let suffix = b"HPAI\0"; + copy_str(&mut target[ptr..], suffix); + } + let longname_suffix = b"High-Power Audio Interface\0"; + let mut machine_name = KVec::with_capacity( + chassis.len() + 1 + machine_kind.len() + longname_suffix.len(), + GFP_KERNEL, + )?; + machine_name.extend_from_slice(machine_kind.value(), GFP_KERNEL)?; + let last_item = machine_name.len() - 1; + machine_name[last_item] = b' '; + machine_name.extend_from_slice(chassis.value(), GFP_KERNEL)?; + let last_item = machine_name.len() - 1; + machine_name[last_item] = b' '; + unsafe { + let target = (*this.0).shortname.as_mut(); + copy_str(target, machine_name.as_ref()); + let ptr = machine_name.len(); + let suffix = b"HPAI\0"; + copy_str(&mut target[ptr..], suffix); + } + machine_name.extend_from_slice(longname_suffix, GFP_KERNEL)?; + unsafe { + let target = (*this.0).longname.as_mut(); + copy_str(target, machine_name.as_ref()); + } + + let mut pcm = ptr::null_mut(); + let ret = + unsafe { bindings::snd_pcm_new(this.0, machine_name.as_ptr() as _, 0, 0, 1, &mut pcm) }; + if ret < 0 { + dev_err!(data.dev, "Unable to allocate PCM device"); + return Err(Error::from_errno(ret)); + } + + unsafe { + bindings::snd_pcm_set_ops( + pcm, + bindings::SNDRV_PCM_STREAM_CAPTURE as i32, + &Self::VTABLE, + ); + } + data.set_audio_power(POWER_STATE_IDLE, 0)?; + let dma_chan = data.request_dma_channel()?; + let ret = unsafe { + bindings::snd_pcm_set_managed_buffer_all( + pcm, + bindings::SNDRV_DMA_TYPE_DEV_IRAM as i32, + (*(*dma_chan).device).dev, + 0, + 0, + ) + }; + if ret < 0 { + dev_err!(data.dev, "Unable to allocate dma buffers"); + return Err(Error::from_errno(ret)); + } + unsafe { + bindings::dma_release_channel(dma_chan); + } + data.set_audio_power(POWER_STATE_OFF, 0)?; + + unsafe { + (*pcm).private_data = data.clone().into_foreign() as _; + (*pcm).private_free = Some(aop_pcm_free_private); + (*pcm).info_flags = 0; + let name = c_str!("aop_audio"); + copy_str((*pcm).name.as_mut(), name.as_ref()); + } + + let ret = unsafe { bindings::snd_card_register(this.0) }; + if ret < 0 { + dev_err!(data.dev, "Unable to register sound card"); + return Err(Error::from_errno(ret)); + } + Ok(this) + } +} + +impl Drop for SndSocAopDriver { + fn drop(&mut self) { + if self.0 != ptr::null_mut() { + unsafe { + bindings::snd_card_free(self.0); + } + } + } +} + +unsafe impl Send for SndSocAopDriver {} +unsafe impl Sync for SndSocAopDriver {} + +kernel::of_device_table!(OF_TABLE, MODULE_OF_TABLE, (), [] as [(of::DeviceId, ()); 0]); + +impl platform::Driver for SndSocAopDriver { + type IdInfo = (); + + const ID_TABLE: platform::IdTable<()> = &OF_TABLE; + + fn probe( + pdev: &mut platform::Device, + _info: Option<&()>, + ) -> Result>> { + let dev = pdev.get_device(); + let parent = dev.parent().unwrap(); + // SAFETY: our parent is AOP, and AopDriver is repr(transparent) for Arc + let adata_ptr = unsafe { Pin::>>::borrow(parent.get_drvdata()) }; + let adata = (&*adata_ptr).clone(); + // SAFETY: AOP sets the platform data correctly + let svc = unsafe { *((*dev.as_raw()).platform_data as *const EPICService) }; + let of = parent + .of_node() + .ok_or(EIO)? + .get_child_by_name(c_str!("audio")) + .ok_or(EIO)?; + let data = SndSocAopData::new(dev, adata, svc, of)?; + for dev in [AUDIO_DEV_PDM0, AUDIO_DEV_HPAI, AUDIO_DEV_LPAI] { + data.audio_attach_device(dev)?; + } + data.set_lpai_channel_cfg()?; + data.set_pdm_config()?; + data.set_decimator_config()?; + Ok(Box::pin(SndSocAopDriver::new(data)?, GFP_KERNEL)?) + } +} + +module_platform_driver! { + type: SndSocAopDriver, + name: "snd_soc_apple_aop", + license: "Dual MIT/GPL", + alias: ["platform:snd_soc_apple_aop"], +} From fc96b02eaf4159191764de85c4b7675281aa2dfe Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Sat, 9 Nov 2024 18:40:28 +0100 Subject: [PATCH 4/7] iio: common: Add AOP sensor drivers. The AOP co-processor present on certain Apple SoCs exposes various environmental sensors as "HID" (really not) devices. Add drivers for the ambient light and lid angle sensors exposed that way. Signed-off-by: Sasha Finkelstein --- drivers/iio/common/Kconfig | 1 + drivers/iio/common/Makefile | 1 + drivers/iio/common/aop_sensors/Kconfig | 23 ++++ drivers/iio/common/aop_sensors/Makefile | 4 + drivers/iio/common/aop_sensors/aop_als.rs | 123 +++++++++++++++++ drivers/iio/common/aop_sensors/aop_las.rs | 69 ++++++++++ rust/kernel/iio/common/aop_sensors.rs | 161 ++++++++++++++++++++++ rust/kernel/iio/common/mod.rs | 11 ++ rust/kernel/iio/mod.rs | 5 + rust/kernel/lib.rs | 2 + 10 files changed, 400 insertions(+) create mode 100644 drivers/iio/common/aop_sensors/Kconfig create mode 100644 drivers/iio/common/aop_sensors/Makefile create mode 100644 drivers/iio/common/aop_sensors/aop_als.rs create mode 100644 drivers/iio/common/aop_sensors/aop_las.rs create mode 100644 rust/kernel/iio/common/aop_sensors.rs create mode 100644 rust/kernel/iio/common/mod.rs create mode 100644 rust/kernel/iio/mod.rs diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig index 1ccb5ccf370660..e3818ef567822b 100644 --- a/drivers/iio/common/Kconfig +++ b/drivers/iio/common/Kconfig @@ -3,6 +3,7 @@ # IIO common modules # +source "drivers/iio/common/aop_sensors/Kconfig" source "drivers/iio/common/cros_ec_sensors/Kconfig" source "drivers/iio/common/hid-sensors/Kconfig" source "drivers/iio/common/inv_sensors/Kconfig" diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile index d3e952239a6219..5f99a429725d66 100644 --- a/drivers/iio/common/Makefile +++ b/drivers/iio/common/Makefile @@ -8,6 +8,7 @@ # # When adding new entries keep the list in alphabetical order +obj-y += aop_sensors/ obj-y += cros_ec_sensors/ obj-y += hid-sensors/ obj-y += inv_sensors/ diff --git a/drivers/iio/common/aop_sensors/Kconfig b/drivers/iio/common/aop_sensors/Kconfig new file mode 100644 index 00000000000000..10d6e720057609 --- /dev/null +++ b/drivers/iio/common/aop_sensors/Kconfig @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT + +config IIO_AOP_SENSOR_LAS + tristate "AOP Lid angle sensor" + depends on ARCH_APPLE || COMPILE_TEST + depends on RUST + depends on SYSFS + select APPLE_AOP + default m if ARCH_APPLE + help + Module to handle the lid angle sensor attached to the AOP + coprocessor on Apple laptops. + +config IIO_AOP_SENSOR_ALS + tristate "AOP Ambient light sensor" + depends on ARCH_APPLE || COMPILE_TEST + depends on RUST + depends on SYSFS + select APPLE_AOP + default m if ARCH_APPLE + help + Module to handle the ambient light sensor attached to the AOP + coprocessor on Apple laptops. diff --git a/drivers/iio/common/aop_sensors/Makefile b/drivers/iio/common/aop_sensors/Makefile new file mode 100644 index 00000000000000..8da5a19efe0f0c --- /dev/null +++ b/drivers/iio/common/aop_sensors/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT + +obj-$(CONFIG_IIO_AOP_SENSOR_LAS) += aop_las.o +obj-$(CONFIG_IIO_AOP_SENSOR_ALS) += aop_als.o diff --git a/drivers/iio/common/aop_sensors/aop_als.rs b/drivers/iio/common/aop_sensors/aop_als.rs new file mode 100644 index 00000000000000..e24698f785c50f --- /dev/null +++ b/drivers/iio/common/aop_sensors/aop_als.rs @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT + +//! Apple AOP ambient light sensor driver +//! +//! Copyright (C) The Asahi Linux Contributors + +use kernel::{ + bindings, c_str, device, + iio::common::aop_sensors::{AopSensorData, IIORegistration, MessageProcessor}, + module_platform_driver, + of::{self, Node}, + platform, + prelude::*, + soc::apple::aop::{EPICService, AOP}, + sync::Arc, + types::{ARef, ForeignOwnable}, +}; + +const EPIC_SUBTYPE_SET_ALS_PROPERTY: u16 = 0x4; + +fn enable_als( + aop: &dyn AOP, + dev: &ARef, + of: &Node, + svc: &EPICService, +) -> Result<()> { + if let Some(prop) = of.find_property(c_str!("apple,als-calibration")) { + set_als_property(aop, svc, 0xb, prop.value())?; + set_als_property(aop, svc, 0, &200000u32.to_le_bytes())?; + } else { + dev_warn!(dev, "ALS Calibration not found, will not enable it"); + } + Ok(()) +} +fn set_als_property(aop: &dyn AOP, svc: &EPICService, tag: u32, data: &[u8]) -> Result { + let mut buf = KVec::new(); + buf.resize(data.len() + 8, 0, GFP_KERNEL)?; + buf[8..].copy_from_slice(data); + buf[4..8].copy_from_slice(&tag.to_le_bytes()); + aop.epic_call(svc, EPIC_SUBTYPE_SET_ALS_PROPERTY, &buf) +} + +fn f32_to_u32(f: u32) -> u32 { + if f & 0x80000000 != 0 { + return 0; + } + let exp = ((f & 0x7f800000) >> 23) as i32 - 127; + if exp < 0 { + return 0; + } + if exp == 128 && f & 0x7fffff != 0 { + return 0; + } + let mant = f & 0x7fffff | 0x800000; + if exp <= 23 { + return mant >> (23 - exp); + } + if exp >= 32 { + return u32::MAX; + } + mant << (exp - 23) +} + +struct MsgProc(usize); + +impl MessageProcessor for MsgProc { + fn process(&self, message: &[u8]) -> u32 { + let offset = self.0; + let raw = u32::from_le_bytes(message[offset..offset + 4].try_into().unwrap()); + f32_to_u32(raw) + } +} + +#[repr(transparent)] +struct IIOAopAlsDriver(IIORegistration); + +kernel::of_device_table!(OF_TABLE, MODULE_OF_TABLE, (), [] as [(of::DeviceId, ()); 0]); + +impl platform::Driver for IIOAopAlsDriver { + type IdInfo = (); + + const ID_TABLE: platform::IdTable<()> = &OF_TABLE; + + fn probe( + pdev: &mut platform::Device, + _info: Option<&()>, + ) -> Result>> { + let dev = pdev.get_device(); + let parent = dev.parent().unwrap(); + // SAFETY: our parent is AOP, and AopDriver is repr(transparent) for Arc + let adata_ptr = unsafe { Pin::>>::borrow(parent.get_drvdata()) }; + let adata = (&*adata_ptr).clone(); + // SAFETY: AOP sets the platform data correctly + let service = unsafe { *((*dev.as_raw()).platform_data as *const EPICService) }; + let of = parent + .of_node() + .ok_or(EIO)? + .get_child_by_name(c_str!("als")) + .ok_or(EIO)?; + let ty = bindings::BINDINGS_IIO_LIGHT; + let data = AopSensorData::new(dev.clone(), ty, MsgProc(40))?; + adata.add_fakehid_listener(service, data.clone())?; + enable_als(adata.as_ref(), &dev, &of, &service)?; + let info_mask = 1 << bindings::BINDINGS_IIO_CHAN_INFO_PROCESSED; + Ok(KBox::pin( + IIOAopAlsDriver(IIORegistration::::new( + data, + c_str!("aop-sensors-als"), + ty, + info_mask, + &THIS_MODULE, + )?), + GFP_KERNEL, + )?) + } +} + +module_platform_driver! { + type: IIOAopAlsDriver, + name: "iio_aop_als", + license: "Dual MIT/GPL", + alias: ["platform:iio_aop_als"], +} diff --git a/drivers/iio/common/aop_sensors/aop_las.rs b/drivers/iio/common/aop_sensors/aop_las.rs new file mode 100644 index 00000000000000..05d3fe54b0f7c5 --- /dev/null +++ b/drivers/iio/common/aop_sensors/aop_las.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT + +//! Apple AOP lid angle sensor driver +//! +//! Copyright (C) The Asahi Linux Contributors + +use kernel::{ + bindings, c_str, + iio::common::aop_sensors::{AopSensorData, IIORegistration, MessageProcessor}, + module_platform_driver, of, platform, + prelude::*, + soc::apple::aop::{EPICService, AOP}, + sync::Arc, + types::ForeignOwnable, +}; + +struct MsgProc; + +impl MessageProcessor for MsgProc { + fn process(&self, message: &[u8]) -> u32 { + message[1] as u32 + } +} + +#[repr(transparent)] +struct IIOAopLasDriver(IIORegistration); + +kernel::of_device_table!(OF_TABLE, MODULE_OF_TABLE, (), [] as [(of::DeviceId, ()); 0]); + +impl platform::Driver for IIOAopLasDriver { + type IdInfo = (); + + const ID_TABLE: platform::IdTable<()> = &OF_TABLE; + + fn probe( + pdev: &mut platform::Device, + _info: Option<&()>, + ) -> Result>> { + let dev = pdev.get_device(); + let parent = dev.parent().unwrap(); + // SAFETY: our parent is AOP, and AopDriver is repr(transparent) for Arc + let adata_ptr = unsafe { Pin::>>::borrow(parent.get_drvdata()) }; + let adata = (&*adata_ptr).clone(); + // SAFETY: AOP sets the platform data correctly + let service = unsafe { *((*dev.as_raw()).platform_data as *const EPICService) }; + + let ty = bindings::BINDINGS_IIO_ANGL; + let data = AopSensorData::new(dev, ty, MsgProc)?; + adata.add_fakehid_listener(service, data.clone())?; + let info_mask = 1 << bindings::BINDINGS_IIO_CHAN_INFO_RAW; + Ok(KBox::pin( + IIOAopLasDriver(IIORegistration::::new( + data, + c_str!("aop-sensors-las"), + ty, + info_mask, + &THIS_MODULE, + )?), + GFP_KERNEL, + )?) + } +} + +module_platform_driver! { + type: IIOAopLasDriver, + name: "iio_aop_las", + license: "Dual MIT/GPL", + alias: ["platform:iio_aop_las"], +} diff --git a/rust/kernel/iio/common/aop_sensors.rs b/rust/kernel/iio/common/aop_sensors.rs new file mode 100644 index 00000000000000..5294f97aee769a --- /dev/null +++ b/rust/kernel/iio/common/aop_sensors.rs @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT + +//! Apple AOP sensors common code +//! +//! Copyright (C) The Asahi Linux Contributors + +use core::marker::{PhantomData, PhantomPinned}; +use core::ptr; +use core::sync::atomic::{AtomicU32, Ordering}; + +use kernel::{ + bindings, device, + prelude::*, + soc::apple::aop::FakehidListener, + sync::Arc, + types::{ARef, ForeignOwnable}, + ThisModule, +}; + +pub trait MessageProcessor { + fn process(&self, message: &[u8]) -> u32; +} + +pub struct AopSensorData { + dev: ARef, + ty: u32, + value: AtomicU32, + msg_proc: T, +} + +impl AopSensorData { + pub fn new(dev: ARef, ty: u32, msg_proc: T) -> Result>> { + Ok(Arc::new( + AopSensorData { + dev: dev.clone(), + ty, + value: AtomicU32::new(0), + msg_proc, + }, + GFP_KERNEL, + )?) + } +} + +impl FakehidListener for AopSensorData { + fn process_fakehid_report(&self, data: &[u8]) -> Result<()> { + self.value + .store(self.msg_proc.process(data), Ordering::Relaxed); + Ok(()) + } +} + +unsafe extern "C" fn aop_read_raw( + dev: *mut bindings::iio_dev, + chan: *const bindings::iio_chan_spec, + val: *mut i32, + _: *mut i32, + mask: i64, +) -> i32 { + let data = unsafe { Arc::>::borrow((*dev).priv_) }; + let ty = unsafe { (*chan).type_ }; + if mask != bindings::BINDINGS_IIO_CHAN_INFO_PROCESSED as i64 + && mask != bindings::BINDINGS_IIO_CHAN_INFO_RAW as i64 + { + return EINVAL.to_errno(); + } + if data.ty != ty { + return EINVAL.to_errno(); + } + let value = data.value.load(Ordering::Relaxed); + unsafe { + *val = value as i32; + } + bindings::IIO_VAL_INT as i32 +} + +struct IIOSpec { + spec: [bindings::iio_chan_spec; 1], + vtable: bindings::iio_info, + _p: PhantomPinned, +} + +pub struct IIORegistration { + dev: *mut bindings::iio_dev, + spec: Pin>, + registered: bool, + _p: PhantomData>, +} + +impl IIORegistration { + pub fn new( + data: Arc>, + name: &'static CStr, + ty: u32, + info_mask: i64, + module: &ThisModule, + ) -> Result { + let spec = KBox::pin( + IIOSpec { + spec: [bindings::iio_chan_spec { + type_: ty, + __bindgen_anon_1: bindings::iio_chan_spec__bindgen_ty_1 { + scan_type: bindings::iio_scan_type { + sign: b'u' as _, + realbits: 32, + storagebits: 32, + ..Default::default() + }, + }, + info_mask_separate: info_mask, + ..Default::default() + }], + vtable: bindings::iio_info { + read_raw: Some(aop_read_raw::), + ..Default::default() + }, + _p: PhantomPinned, + }, + GFP_KERNEL, + )?; + let mut this = IIORegistration { + dev: ptr::null_mut(), + spec, + registered: false, + _p: PhantomData, + }; + this.dev = unsafe { bindings::iio_device_alloc(data.dev.as_raw(), 0) }; + unsafe { + (*this.dev).priv_ = data.clone().into_foreign() as _; + (*this.dev).name = name.as_ptr() as _; + // spec is now pinned + (*this.dev).channels = this.spec.spec.as_ptr(); + (*this.dev).num_channels = this.spec.spec.len() as i32; + (*this.dev).info = &this.spec.vtable; + } + let ret = unsafe { bindings::__iio_device_register(this.dev, module.as_ptr()) }; + if ret < 0 { + dev_err!(data.dev, "Unable to register iio sensor"); + return Err(Error::from_errno(ret)); + } + this.registered = true; + Ok(this) + } +} + +impl Drop for IIORegistration { + fn drop(&mut self) { + if self.dev != ptr::null_mut() { + unsafe { + if self.registered { + bindings::iio_device_unregister(self.dev); + } + Arc::>::from_foreign((*self.dev).priv_); + bindings::iio_device_free(self.dev); + } + } + } +} + +unsafe impl Send for IIORegistration {} +unsafe impl Sync for IIORegistration {} diff --git a/rust/kernel/iio/common/mod.rs b/rust/kernel/iio/common/mod.rs new file mode 100644 index 00000000000000..570644ce0938a7 --- /dev/null +++ b/rust/kernel/iio/common/mod.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +//! IIO common modules + +#[cfg(any( + CONFIG_IIO_AOP_SENSOR_LAS = "y", + CONFIG_IIO_AOP_SENSOR_ALS = "m", + CONFIG_IIO_AOP_SENSOR_LAS = "y", + CONFIG_IIO_AOP_SENSOR_ALS = "m", +))] +pub mod aop_sensors; diff --git a/rust/kernel/iio/mod.rs b/rust/kernel/iio/mod.rs new file mode 100644 index 00000000000000..b0cb308f0b454c --- /dev/null +++ b/rust/kernel/iio/mod.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0 or MIT + +//! Industrial IO drivers + +pub mod common; diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 6a1a81906c5736..5d8d0e819f82e5 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -55,6 +55,8 @@ pub mod drm; pub mod error; #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)] pub mod firmware; +#[cfg(CONFIG_IIO)] +pub mod iio; pub mod init; pub mod io_buffer; pub mod io_mem; From e5f9f07daec31644e24d087b7864275b0e444006 Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Sun, 10 Nov 2024 23:22:21 +0100 Subject: [PATCH 5/7] rust: soc: apple: Add Apple mailbox abstractions Signed-off-by: Sasha Finkelstein --- drivers/soc/apple/mailbox.c | 2 +- drivers/soc/apple/rtkit-internal.h | 2 +- .../linux}/soc/apple/mailbox.h | 0 rust/bindings/bindings_helper.h | 1 + rust/kernel/soc/apple/mailbox.rs | 103 ++++++++++++++++++ rust/kernel/soc/apple/mod.rs | 3 + 6 files changed, 109 insertions(+), 2 deletions(-) rename {drivers => include/linux}/soc/apple/mailbox.h (100%) create mode 100644 rust/kernel/soc/apple/mailbox.rs diff --git a/drivers/soc/apple/mailbox.c b/drivers/soc/apple/mailbox.c index 49a0955e82d6cf..00a88c3d148ffc 100644 --- a/drivers/soc/apple/mailbox.c +++ b/drivers/soc/apple/mailbox.c @@ -28,9 +28,9 @@ #include #include #include +#include #include #include -#include "mailbox.h" #define APPLE_ASC_MBOX_CONTROL_FULL BIT(16) #define APPLE_ASC_MBOX_CONTROL_EMPTY BIT(17) diff --git a/drivers/soc/apple/rtkit-internal.h b/drivers/soc/apple/rtkit-internal.h index 27c9fa745fd528..71c81768a2f744 100644 --- a/drivers/soc/apple/rtkit-internal.h +++ b/drivers/soc/apple/rtkit-internal.h @@ -15,9 +15,9 @@ #include #include #include +#include #include #include -#include "mailbox.h" #define APPLE_RTKIT_APP_ENDPOINT_START 0x20 #define APPLE_RTKIT_MAX_ENDPOINTS 0x100 diff --git a/drivers/soc/apple/mailbox.h b/include/linux/soc/apple/mailbox.h similarity index 100% rename from drivers/soc/apple/mailbox.h rename to include/linux/soc/apple/mailbox.h diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index ddf914bb98ab82..f84f74b38a72b5 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/soc/apple/mailbox.rs b/rust/kernel/soc/apple/mailbox.rs new file mode 100644 index 00000000000000..ce1f4348ea4b93 --- /dev/null +++ b/rust/kernel/soc/apple/mailbox.rs @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT + +//! Support for Apple ASC Mailbox. +//! +//! C header: [`include/linux/soc/apple/mailbox.h`](../../../../include/linux/gpio/driver.h) + +use crate::{ + bindings, device, + error::{from_err_ptr, to_result, Result}, + str::CStr, + types::{ForeignOwnable, ScopeGuard}, +}; + +use core::marker::PhantomData; + +/// 96-bit message. What it means is up to the upper layer +pub type Message = bindings::apple_mbox_msg; + +/// Mailbox receive callback +pub trait MailCallback { + /// Callback context + type Data: ForeignOwnable + Send + Sync; + + /// The actual callback. Called in an interrupt context. + fn recv_message(data: ::Borrowed<'_>, msg: Message); +} + +/// Wrapper over `struct apple_mbox *` +#[repr(transparent)] +pub struct Mailbox { + mbox: *mut bindings::apple_mbox, + _p: PhantomData, +} + +extern "C" fn mailbox_rx_callback( + _mbox: *mut bindings::apple_mbox, + msg: Message, + cookie: *mut core::ffi::c_void, +) { + // SAFETY: cookie came from a call to `into_foreign` + T::recv_message(unsafe { T::Data::borrow(cookie) }, msg); +} + +impl Mailbox { + /// Creates a mailbox for the specified name. + pub fn new_byname( + dev: &device::Device, + mbox_name: &'static CStr, + data: T::Data, + ) -> Result> { + let ptr = data.into_foreign() as *mut _; + let guard = ScopeGuard::new(|| { + // SAFETY: `ptr` came from a previous call to `into_foreign`. + unsafe { T::Data::from_foreign(ptr) }; + }); + // SAFETY: Just calling the c function, all values are valid. + let mbox = unsafe { + from_err_ptr(bindings::apple_mbox_get_byname( + dev.as_raw(), + mbox_name.as_char_ptr(), + ))? + }; + // SAFETY: mbox is a valid pointer + unsafe { + (*mbox).cookie = ptr; + (*mbox).rx = Some(mailbox_rx_callback::); + to_result(unsafe { bindings::apple_mbox_start(mbox) })?; + } + guard.dismiss(); + Ok(Mailbox { + mbox, + _p: PhantomData, + }) + } + /// Sends the specified message + pub fn send(&self, msg: Message, atomic: bool) -> Result<()> { + // SAFETY: Calling the c function, `mbox` is a valid pointer + to_result(unsafe { bindings::apple_mbox_send(self.mbox, msg, atomic) }) + } +} + +impl Mailbox { + fn drop(&mut self) { + // SAFETY: mbox is a valid pointer + unsafe { bindings::apple_mbox_stop(self.mbox) }; + // SAFETY: `cookie` came from `into_foreign` + unsafe { T::Data::from_foreign((*self.mbox).cookie) }; + } +} + +unsafe impl Sync for Mailbox +where + T: MailCallback, + T::Data: Sync, +{ +} + +unsafe impl Send for Mailbox +where + T: MailCallback, + T::Data: Send, +{ +} diff --git a/rust/kernel/soc/apple/mod.rs b/rust/kernel/soc/apple/mod.rs index dd0b018a4f8db1..51149360872094 100644 --- a/rust/kernel/soc/apple/mod.rs +++ b/rust/kernel/soc/apple/mod.rs @@ -7,3 +7,6 @@ pub mod rtkit; #[cfg(any(CONFIG_APPLE_AOP = "y", CONFIG_APPLE_AOP = "m"))] pub mod aop; + +#[cfg(CONFIG_APPLE_MAILBOX = "y")] +pub mod mailbox; From 0f83d664cfa0af8fb1bc9d3d00b7c32c447ad898 Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Sun, 10 Nov 2024 23:24:07 +0100 Subject: [PATCH 6/7] soc: apple: Add SEP driver. This is a co-processor in charge of various security-related features on Apple SoCs. This driver only boots the firmware, which is needed to unlock the mic secure disable on certain laptop models. Signed-off-by: Sasha Finkelstein --- drivers/soc/apple/Kconfig | 12 ++ drivers/soc/apple/Makefile | 2 + drivers/soc/apple/sep.rs | 350 +++++++++++++++++++++++++++++++++++++ 3 files changed, 364 insertions(+) create mode 100644 drivers/soc/apple/sep.rs diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig index 7a434f526b0b3d..da6fd4bb3396f9 100644 --- a/drivers/soc/apple/Kconfig +++ b/drivers/soc/apple/Kconfig @@ -58,6 +58,18 @@ config APPLE_AOP Say 'y' here if you have an Apple laptop. +config APPLE_SEP + tristate "Apple Secure Element Processor" + depends on ARCH_APPLE || COMPILE_TEST + depends on RUST + select RUST_APPLE_RTKIT + default y if ARCH_APPLE + help + A security co-processor persent on Apple SoCs, controlling transparent + disk encryption, secure boot, HDCP, biometric auth and probably more. + + Say 'y' here if you have an Apple SoC. + endmenu endif diff --git a/drivers/soc/apple/Makefile b/drivers/soc/apple/Makefile index 17af8e2b82d298..eeeaa50eaaefb3 100644 --- a/drivers/soc/apple/Makefile +++ b/drivers/soc/apple/Makefile @@ -10,3 +10,5 @@ obj-$(CONFIG_APPLE_SART) += apple-sart.o apple-sart-y = sart.o obj-$(CONFIG_APPLE_AOP) += aop.o + +obj-$(CONFIG_APPLE_SEP) += sep.o diff --git a/drivers/soc/apple/sep.rs b/drivers/soc/apple/sep.rs new file mode 100644 index 00000000000000..26e66bfc8158ae --- /dev/null +++ b/drivers/soc/apple/sep.rs @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +#![recursion_limit = "2048"] + +//! Apple SEP driver +//! +//! Copyright (C) The Asahi Linux Contributors + +use core::slice; +use core::sync::atomic::{AtomicBool, Ordering}; + +use kernel::{ + bindings, c_str, device, dma, module_platform_driver, new_mutex, of, platform, + prelude::*, + soc::apple::mailbox::{MailCallback, Mailbox, Message}, + sync::{Arc, Mutex}, + types::{ARef, ForeignOwnable}, + workqueue::{self, impl_has_work, new_work, Work, WorkItem}, +}; + +const SHMEM_SIZE: usize = 0x30000; +const MSG_BOOT_TZ0: u64 = 0x5; +const MSG_BOOT_IMG4: u64 = 0x6; +const MSG_SET_SHMEM: u64 = 0x18; +const MSG_BOOT_TZ0_ACK1: u64 = 0x69; +const MSG_BOOT_TZ0_ACK2: u64 = 0xD2; +const MSG_BOOT_IMG4_ACK: u64 = 0x6A; +const MSG_ADVERTISE_EP: u64 = 0; +const EP_DISCOVER: u64 = 0xFD; +const EP_SHMEM: u64 = 0xFE; +const EP_BOOT: u64 = 0xFF; + +const MSG_TYPE_SHIFT: u32 = 16; +const MSG_TYPE_MASK: u64 = 0xFF; +//const MSG_PARAM_SHIFT: u32 = 24; +//const MSG_PARAM_MASK: u64 = 0xFF; + +const MSG_EP_MASK: u64 = 0xFF; +const MSG_DATA_SHIFT: u32 = 32; + +const IOVA_SHIFT: u32 = 0xC; + +type ShMem = dma::CoherentAllocation; + +fn align_up(v: usize, a: usize) -> usize { + (v + a - 1) & !(a - 1) +} + +fn memcpy_to_iomem( + iomem: &ShMem, + dev: &ARef, + off: usize, + src: &[u8], +) -> Result<()> { + if off + src.len() > iomem.count() { + dev_err!(dev, "Out of bounds iomem write"); + return Err(EIO); + } + // SAFETY: We checked that it is in bounds above + unsafe { + let ptr = iomem.first_ptr_mut().offset(off as isize); + let target = slice::from_raw_parts_mut(ptr, src.len()); + target.copy_from_slice(src); + } + Ok(()) +} + +fn build_shmem(dev: ARef) -> Result { + let of = dev.of_node().ok_or(EIO)?; + let iomem = dma::try_alloc_coherent(dev.clone(), SHMEM_SIZE, false)?; + + let panic_offset = 0x4000; + let panic_size = 0x8000; + memcpy_to_iomem(&iomem, &dev, panic_offset, &1u32.to_le_bytes())?; + + let lpol_offset = panic_offset + panic_size; + let lpol = of + .find_property(c_str!("local-policy-manifest")) + .ok_or(EIO)?; + memcpy_to_iomem( + &iomem, + &dev, + lpol_offset, + &(lpol.value().len() as u32).to_le_bytes(), + )?; + memcpy_to_iomem(&iomem, &dev, lpol_offset + 4, lpol.value())?; + let lpol_size = align_up(lpol.value().len() + 4, 0x4000); + + let ibot_offset = lpol_offset + lpol_size; + let ibot = of.find_property(c_str!("iboot-manifest")).ok_or(EIO)?; + memcpy_to_iomem( + &iomem, + &dev, + ibot_offset, + &(ibot.value().len() as u32).to_le_bytes(), + )?; + memcpy_to_iomem(&iomem, &dev, ibot_offset + 4, ibot.value())?; + let ibot_size = align_up(ibot.value().len() + 4, 0x4000); + + memcpy_to_iomem(&iomem, &dev, 0, b"CNIP")?; + memcpy_to_iomem(&iomem, &dev, 4, &(panic_size as u32).to_le_bytes())?; + memcpy_to_iomem(&iomem, &dev, 8, &(panic_offset as u32).to_le_bytes())?; + + memcpy_to_iomem(&iomem, &dev, 16, b"OPLA")?; + memcpy_to_iomem(&iomem, &dev, 16 + 4, &(lpol_size as u32).to_le_bytes())?; + memcpy_to_iomem(&iomem, &dev, 16 + 8, &(lpol_offset as u32).to_le_bytes())?; + + memcpy_to_iomem(&iomem, &dev, 32, b"IPIS")?; + memcpy_to_iomem(&iomem, &dev, 32 + 4, &(ibot_size as u32).to_le_bytes())?; + memcpy_to_iomem(&iomem, &dev, 32 + 8, &(ibot_offset as u32).to_le_bytes())?; + + memcpy_to_iomem(&iomem, &dev, 48, b"llun")?; + Ok(iomem) +} + +#[pin_data] +struct SepReceiveWork { + data: Arc, + msg: Message, + #[pin] + work: Work, +} + +impl_has_work! { + impl HasWork for SepReceiveWork { self.work } +} + +impl SepReceiveWork { + fn new(data: Arc, msg: Message) -> Result> { + Arc::pin_init( + pin_init!(SepReceiveWork { + data, + msg, + work <- new_work!("SepReceiveWork::work"), + }), + GFP_ATOMIC, + ) + } +} + +impl WorkItem for SepReceiveWork { + type Pointer = Arc; + + fn run(this: Arc) { + this.data.process_message(this.msg); + } +} + +struct FwRegionParams { + addr: u64, + size: usize, +} + +#[pin_data] +struct SepData { + dev: ARef, + #[pin] + mbox: Mutex>>, + shmem: ShMem, + region_params: FwRegionParams, + fw_mapped: AtomicBool, +} + +impl SepData { + fn new(dev: ARef, region_params: FwRegionParams) -> Result> { + Arc::pin_init( + try_pin_init!(SepData { + shmem: build_shmem(dev.clone())?, + dev, + mbox <- new_mutex!(None), + region_params, + fw_mapped: AtomicBool::new(false), + }), + GFP_KERNEL, + ) + } + fn start(&self) -> Result<()> { + self.mbox.lock().as_ref().unwrap().send( + Message { + msg0: EP_BOOT | (MSG_BOOT_TZ0 << MSG_TYPE_SHIFT), + msg1: 0, + }, + false, + ) + } + fn load_fw_and_shmem(&self) -> Result<()> { + let fw_addr = unsafe { + let res = bindings::dma_map_resource( + self.dev.as_raw(), + self.region_params.addr, + self.region_params.size, + bindings::dma_data_direction_DMA_TO_DEVICE, + 0, + ); + if bindings::dma_mapping_error(self.dev.as_raw(), res) != 0 { + dev_err!(self.dev, "Failed to map firmware"); + return Err(ENOMEM); + } + self.fw_mapped.store(true, Ordering::Relaxed); + res >> IOVA_SHIFT + }; + let guard = self.mbox.lock(); + let mbox = guard.as_ref().unwrap(); + mbox.send( + Message { + msg0: EP_BOOT | (MSG_BOOT_IMG4 << MSG_TYPE_SHIFT) | (fw_addr << MSG_DATA_SHIFT), + msg1: 0, + }, + false, + )?; + let shm_addr = self.shmem.dma_handle >> IOVA_SHIFT; + mbox.send( + Message { + msg0: EP_SHMEM | (MSG_SET_SHMEM << MSG_TYPE_SHIFT) | (shm_addr << MSG_DATA_SHIFT), + msg1: 0, + }, + false, + )?; + Ok(()) + } + fn process_boot_msg(&self, msg: Message) { + let ty = (msg.msg0 >> MSG_TYPE_SHIFT) & MSG_TYPE_MASK; + match ty { + MSG_BOOT_TZ0_ACK1 => {} + MSG_BOOT_TZ0_ACK2 => { + let res = self.load_fw_and_shmem(); + if let Err(e) = res { + dev_err!(self.dev, "Unable to load firmware: {:?}", e); + } + } + MSG_BOOT_IMG4_ACK => {} + _ => { + dev_err!(self.dev, "Unknown boot message type: {}", ty); + } + } + } + fn process_discover_msg(&self, msg: Message) { + let ty = (msg.msg0 >> MSG_TYPE_SHIFT) & MSG_TYPE_MASK; + //let data = (msg.msg0 >> MSG_DATA_SHIFT) as u32; + //let param = (msg.msg0 >> MSG_PARAM_SHIFT) & MSG_PARAM_MASK; + match ty { + MSG_ADVERTISE_EP => { + /*dev_info!( + self.dev, + "Got endpoint {:?} at {}", + core::str::from_utf8(&data.to_be_bytes()), + param + );*/ + } + _ => { + //dev_warn!(self.dev, "Unknown discovery message type: {}", ty); + } + } + } + fn process_message(&self, msg: Message) { + let ep = msg.msg0 & MSG_EP_MASK; + match ep { + EP_BOOT => self.process_boot_msg(msg), + EP_DISCOVER => self.process_discover_msg(msg), + _ => {} // dev_warn!(self.dev, "Message from unknown endpoint: {}", ep), + } + } + fn remove(&self) { + *self.mbox.lock() = None; + if self.fw_mapped.load(Ordering::Relaxed) { + unsafe { + bindings::dma_unmap_resource( + self.dev.as_raw(), + self.region_params.addr, + self.region_params.size, + bindings::dma_data_direction_DMA_TO_DEVICE, + 0, + ); + } + } + } +} + +impl MailCallback for SepData { + type Data = Arc; + fn recv_message(data: ::Borrowed<'_>, msg: Message) { + let work = SepReceiveWork::new(data.into(), msg); + if let Ok(work) = work { + let res = workqueue::system().enqueue(work); + if res.is_err() { + dev_err!( + data.dev, + "Unable to schedule work item for message {}", + msg.msg0 + ); + } + } else { + dev_err!( + data.dev, + "Unable to allocate work item for message {}", + msg.msg0 + ); + } + } +} + +unsafe impl Send for SepData {} +unsafe impl Sync for SepData {} + +struct SepDriver(Arc); + +kernel::of_device_table!( + OF_TABLE, + MODULE_OF_TABLE, + (), + [(of::DeviceId::new(c_str!("apple,sep")), ())] +); + +impl platform::Driver for SepDriver { + type IdInfo = (); + + const ID_TABLE: platform::IdTable<()> = &OF_TABLE; + + fn probe(pdev: &mut platform::Device, _info: Option<&()>) -> Result>> { + let dev = pdev.get_device(); + let of = dev.of_node().ok_or(EIO)?; + let fw_node = of.parse_phandle(c_str!("memory-region"), 0).ok_or(EIO)?; + let mut reg = [0u64, 0u64]; + fw_node + .find_property(c_str!("reg")) + .ok_or(EIO)? + .copy_to_slice(&mut reg)?; + let data = SepData::new( + dev.clone(), + FwRegionParams { + addr: reg[0], + size: reg[1] as usize, + }, + )?; + *data.mbox.lock() = Some(Mailbox::new_byname(&dev, c_str!("mbox"), data.clone())?); + data.start()?; + Ok(KBox::pin(SepDriver(data), GFP_KERNEL)?) + } +} + +impl Drop for SepDriver { + fn drop(&mut self) { + self.0.remove(); + } +} + +module_platform_driver! { + type: SepDriver, + name: "apple_sep", + license: "Dual MIT/GPL", +} From 407530ed885c5beebf8c98cbd012d16112efff3d Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 28 Nov 2024 08:18:45 +0100 Subject: [PATCH 7/7] rust: bindgen: Make snd_dec_flac opaque At least with certain some rust / bindgen combinations compilation fails with: error[E0587]: type has conflicting packed and align representation hints --> /Transit/build/linux/rust/bindings/bindings_generated.rs:102244:1 | 102244 | pub struct snd_dec_flac { | ^^^^^^^^^^^^^^^^^^^^^^^ Signed-off-by: Janne Grunau --- rust/bindgen_parameters | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rust/bindgen_parameters b/rust/bindgen_parameters index 63956a0defdd08..f95af82bd7e5f7 100644 --- a/rust/bindgen_parameters +++ b/rust/bindgen_parameters @@ -13,6 +13,9 @@ --opaque-type x86_msi_data --opaque-type x86_msi_addr_lo +# Packed types cannot have larger alignment than the maximal natural aligment of menbers +--opaque-type snd_dec_flac + # `try` is a reserved keyword since Rust 2018; solved in `bindgen` v0.59.2, # commit 2aed6b021680 ("context: Escape the try keyword properly"). --opaque-type kunit_try_catch