Skip to content

Commit 16fd11d

Browse files
WhatAmISupposedToPutHerejannau
authored andcommitted
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 <fnkl.kernel@gmail.com>
1 parent d62f36c commit 16fd11d

10 files changed

Lines changed: 394 additions & 0 deletions

File tree

drivers/iio/common/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# IIO common modules
44
#
55

6+
source "drivers/iio/common/aop_sensors/Kconfig"
67
source "drivers/iio/common/cros_ec_sensors/Kconfig"
78
source "drivers/iio/common/hid-sensors/Kconfig"
89
source "drivers/iio/common/inv_sensors/Kconfig"

drivers/iio/common/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#
99

1010
# When adding new entries keep the list in alphabetical order
11+
obj-y += aop_sensors/
1112
obj-y += cros_ec_sensors/
1213
obj-y += hid-sensors/
1314
obj-y += inv_sensors/
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
3+
config IIO_AOP_SENSOR_LAS
4+
tristate "AOP Lid angle sensor"
5+
depends on ARCH_APPLE || COMPILE_TEST
6+
depends on RUST
7+
depends on SYSFS
8+
select APPLE_AOP
9+
default m if ARCH_APPLE
10+
help
11+
Module to handle the lid angle sensor attached to the AOP
12+
coprocessor on Apple laptops.
13+
14+
config IIO_AOP_SENSOR_ALS
15+
tristate "AOP Ambient light sensor"
16+
depends on ARCH_APPLE || COMPILE_TEST
17+
depends on RUST
18+
depends on SYSFS
19+
select APPLE_AOP
20+
default m if ARCH_APPLE
21+
help
22+
Module to handle the ambient light sensor attached to the AOP
23+
coprocessor on Apple laptops.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
3+
obj-$(CONFIG_IIO_AOP_SENSOR_LAS) += aop_las.o
4+
obj-$(CONFIG_IIO_AOP_SENSOR_ALS) += aop_als.o
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
3+
//! Apple AOP ambient light sensor driver
4+
//!
5+
//! Copyright (C) The Asahi Linux Contributors
6+
7+
use kernel::{
8+
bindings, c_str,
9+
iio::common::aop_sensors::{AopSensorData, IIORegistration, MessageProcessor},
10+
module_platform_driver,
11+
of::{self, Node},
12+
platform,
13+
prelude::*,
14+
soc::apple::aop::{EPICService, AOP},
15+
sync::Arc,
16+
types::ForeignOwnable,
17+
};
18+
19+
const EPIC_SUBTYPE_SET_ALS_PROPERTY: u16 = 0x4;
20+
21+
fn enable_als(aop: &dyn AOP, dev: &platform::Device, of: &Node, svc: &EPICService) -> Result<()> {
22+
if let Some(prop) = of.find_property(c_str!("apple,als-calibration")) {
23+
set_als_property(aop, svc, 0xb, prop.value())?;
24+
set_als_property(aop, svc, 0, &200000u32.to_le_bytes())?;
25+
} else {
26+
dev_warn!(
27+
dev.as_ref(),
28+
"ALS Calibration not found, will not enable it"
29+
);
30+
}
31+
Ok(())
32+
}
33+
fn set_als_property(aop: &dyn AOP, svc: &EPICService, tag: u32, data: &[u8]) -> Result<u32> {
34+
let mut buf = KVec::new();
35+
buf.resize(data.len() + 8, 0, GFP_KERNEL)?;
36+
buf[8..].copy_from_slice(data);
37+
buf[4..8].copy_from_slice(&tag.to_le_bytes());
38+
aop.epic_call(svc, EPIC_SUBTYPE_SET_ALS_PROPERTY, &buf)
39+
}
40+
41+
fn f32_to_u32(f: u32) -> u32 {
42+
if f & 0x80000000 != 0 {
43+
return 0;
44+
}
45+
let exp = ((f & 0x7f800000) >> 23) as i32 - 127;
46+
if exp < 0 {
47+
return 0;
48+
}
49+
if exp == 128 && f & 0x7fffff != 0 {
50+
return 0;
51+
}
52+
let mant = f & 0x7fffff | 0x800000;
53+
if exp <= 23 {
54+
return mant >> (23 - exp);
55+
}
56+
if exp >= 32 {
57+
return u32::MAX;
58+
}
59+
mant << (exp - 23)
60+
}
61+
62+
struct MsgProc(usize);
63+
64+
impl MessageProcessor for MsgProc {
65+
fn process(&self, message: &[u8]) -> u32 {
66+
let offset = self.0;
67+
let raw = u32::from_le_bytes(message[offset..offset + 4].try_into().unwrap());
68+
f32_to_u32(raw)
69+
}
70+
}
71+
72+
#[repr(transparent)]
73+
struct IIOAopAlsDriver(IIORegistration<MsgProc>);
74+
75+
kernel::of_device_table!(OF_TABLE, MODULE_OF_TABLE, (), [] as [(of::DeviceId, ()); 0]);
76+
77+
impl platform::Driver for IIOAopAlsDriver {
78+
type IdInfo = ();
79+
80+
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
81+
82+
fn probe(
83+
pdev: &mut platform::Device,
84+
_info: Option<&()>,
85+
) -> Result<Pin<KBox<IIOAopAlsDriver>>> {
86+
let dev = pdev.as_ref();
87+
let parent = dev.parent().unwrap();
88+
// SAFETY: our parent is AOP, and AopDriver is repr(transparent) for Arc<dyn Aop>
89+
let adata_ptr = unsafe { Pin::<KBox<Arc<dyn AOP>>>::borrow(parent.get_drvdata()) };
90+
let adata = (&*adata_ptr).clone();
91+
// SAFETY: AOP sets the platform data correctly
92+
let service = unsafe { *((*dev.as_raw()).platform_data as *const EPICService) };
93+
let of = parent
94+
.of_node()
95+
.ok_or(EIO)?
96+
.get_child_by_name(c_str!("als"))
97+
.ok_or(EIO)?;
98+
let ty = bindings::BINDINGS_IIO_LIGHT;
99+
let data = AopSensorData::new(pdev.clone(), ty, MsgProc(40))?;
100+
adata.add_fakehid_listener(service, data.clone())?;
101+
enable_als(adata.as_ref(), pdev, &of, &service)?;
102+
let info_mask = 1 << bindings::BINDINGS_IIO_CHAN_INFO_PROCESSED;
103+
Ok(KBox::pin(
104+
IIOAopAlsDriver(IIORegistration::<MsgProc>::new(
105+
data,
106+
c_str!("aop-sensors-als"),
107+
ty,
108+
info_mask,
109+
&THIS_MODULE,
110+
)?),
111+
GFP_KERNEL,
112+
)?)
113+
}
114+
}
115+
116+
module_platform_driver! {
117+
type: IIOAopAlsDriver,
118+
name: "iio_aop_als",
119+
license: "Dual MIT/GPL",
120+
alias: ["platform:iio_aop_als"],
121+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
3+
//! Apple AOP lid angle sensor driver
4+
//!
5+
//! Copyright (C) The Asahi Linux Contributors
6+
7+
use kernel::{
8+
bindings, c_str,
9+
iio::common::aop_sensors::{AopSensorData, IIORegistration, MessageProcessor},
10+
module_platform_driver, of, platform,
11+
prelude::*,
12+
soc::apple::aop::{EPICService, AOP},
13+
sync::Arc,
14+
types::ForeignOwnable,
15+
};
16+
17+
struct MsgProc;
18+
19+
impl MessageProcessor for MsgProc {
20+
fn process(&self, message: &[u8]) -> u32 {
21+
message[1] as u32
22+
}
23+
}
24+
25+
#[repr(transparent)]
26+
struct IIOAopLasDriver(IIORegistration<MsgProc>);
27+
28+
kernel::of_device_table!(OF_TABLE, MODULE_OF_TABLE, (), [] as [(of::DeviceId, ()); 0]);
29+
30+
impl platform::Driver for IIOAopLasDriver {
31+
type IdInfo = ();
32+
33+
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
34+
35+
fn probe(
36+
pdev: &mut platform::Device,
37+
_info: Option<&()>,
38+
) -> Result<Pin<KBox<IIOAopLasDriver>>> {
39+
let dev = pdev.as_ref();
40+
let parent = dev.parent().unwrap();
41+
// SAFETY: our parent is AOP, and AopDriver is repr(transparent) for Arc<dyn Aop>
42+
let adata_ptr = unsafe { Pin::<KBox<Arc<dyn AOP>>>::borrow(parent.get_drvdata()) };
43+
let adata = (&*adata_ptr).clone();
44+
// SAFETY: AOP sets the platform data correctly
45+
let service = unsafe { *((*dev.as_raw()).platform_data as *const EPICService) };
46+
47+
let ty = bindings::BINDINGS_IIO_ANGL;
48+
let data = AopSensorData::new(pdev.clone(), ty, MsgProc)?;
49+
adata.add_fakehid_listener(service, data.clone())?;
50+
let info_mask = 1 << bindings::BINDINGS_IIO_CHAN_INFO_RAW;
51+
Ok(KBox::pin(
52+
IIOAopLasDriver(IIORegistration::<MsgProc>::new(
53+
data,
54+
c_str!("aop-sensors-las"),
55+
ty,
56+
info_mask,
57+
&THIS_MODULE,
58+
)?),
59+
GFP_KERNEL,
60+
)?)
61+
}
62+
}
63+
64+
module_platform_driver! {
65+
type: IIOAopLasDriver,
66+
name: "iio_aop_las",
67+
license: "Dual MIT/GPL",
68+
alias: ["platform:iio_aop_las"],
69+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
3+
//! Apple AOP sensors common code
4+
//!
5+
//! Copyright (C) The Asahi Linux Contributors
6+
7+
use core::marker::{PhantomData, PhantomPinned};
8+
use core::ptr;
9+
use core::sync::atomic::{AtomicU32, Ordering};
10+
11+
use kernel::{
12+
bindings, platform, prelude::*, soc::apple::aop::FakehidListener, sync::Arc,
13+
types::ForeignOwnable, ThisModule,
14+
};
15+
16+
pub trait MessageProcessor {
17+
fn process(&self, message: &[u8]) -> u32;
18+
}
19+
20+
pub struct AopSensorData<T: MessageProcessor> {
21+
dev: platform::Device,
22+
ty: u32,
23+
value: AtomicU32,
24+
msg_proc: T,
25+
}
26+
27+
impl<T: MessageProcessor> AopSensorData<T> {
28+
pub fn new(dev: platform::Device, ty: u32, msg_proc: T) -> Result<Arc<AopSensorData<T>>> {
29+
Ok(Arc::new(
30+
AopSensorData {
31+
dev,
32+
ty,
33+
value: AtomicU32::new(0),
34+
msg_proc,
35+
},
36+
GFP_KERNEL,
37+
)?)
38+
}
39+
}
40+
41+
impl<T: MessageProcessor> FakehidListener for AopSensorData<T> {
42+
fn process_fakehid_report(&self, data: &[u8]) -> Result<()> {
43+
self.value
44+
.store(self.msg_proc.process(data), Ordering::Relaxed);
45+
Ok(())
46+
}
47+
}
48+
49+
unsafe extern "C" fn aop_read_raw<T: MessageProcessor + 'static>(
50+
dev: *mut bindings::iio_dev,
51+
chan: *const bindings::iio_chan_spec,
52+
val: *mut i32,
53+
_: *mut i32,
54+
mask: isize,
55+
) -> i32 {
56+
let data = unsafe { Arc::<AopSensorData<T>>::borrow((*dev).priv_) };
57+
let ty = unsafe { (*chan).type_ };
58+
if mask != bindings::BINDINGS_IIO_CHAN_INFO_PROCESSED as isize
59+
&& mask != bindings::BINDINGS_IIO_CHAN_INFO_RAW as isize
60+
{
61+
return EINVAL.to_errno();
62+
}
63+
if data.ty != ty {
64+
return EINVAL.to_errno();
65+
}
66+
let value = data.value.load(Ordering::Relaxed);
67+
unsafe {
68+
*val = value as i32;
69+
}
70+
bindings::IIO_VAL_INT as i32
71+
}
72+
73+
struct IIOSpec {
74+
spec: [bindings::iio_chan_spec; 1],
75+
vtable: bindings::iio_info,
76+
_p: PhantomPinned,
77+
}
78+
79+
pub struct IIORegistration<T: MessageProcessor + 'static> {
80+
dev: *mut bindings::iio_dev,
81+
spec: Pin<KBox<IIOSpec>>,
82+
registered: bool,
83+
_p: PhantomData<AopSensorData<T>>,
84+
}
85+
86+
impl<T: MessageProcessor + 'static> IIORegistration<T> {
87+
pub fn new(
88+
data: Arc<AopSensorData<T>>,
89+
name: &'static CStr,
90+
ty: u32,
91+
info_mask: isize,
92+
module: &ThisModule,
93+
) -> Result<Self> {
94+
let spec = KBox::pin(
95+
IIOSpec {
96+
spec: [bindings::iio_chan_spec {
97+
type_: ty,
98+
__bindgen_anon_1: bindings::iio_chan_spec__bindgen_ty_1 {
99+
scan_type: bindings::iio_scan_type {
100+
sign: b'u' as _,
101+
realbits: 32,
102+
storagebits: 32,
103+
..Default::default()
104+
},
105+
},
106+
info_mask_separate: info_mask,
107+
..Default::default()
108+
}],
109+
vtable: bindings::iio_info {
110+
read_raw: Some(aop_read_raw::<T>),
111+
..Default::default()
112+
},
113+
_p: PhantomPinned,
114+
},
115+
GFP_KERNEL,
116+
)?;
117+
let mut this = IIORegistration {
118+
dev: ptr::null_mut(),
119+
spec,
120+
registered: false,
121+
_p: PhantomData,
122+
};
123+
this.dev = unsafe { bindings::iio_device_alloc(data.dev.as_ref().as_raw(), 0) };
124+
unsafe {
125+
(*this.dev).priv_ = data.clone().into_foreign() as _;
126+
(*this.dev).name = name.as_ptr() as _;
127+
// spec is now pinned
128+
(*this.dev).channels = this.spec.spec.as_ptr();
129+
(*this.dev).num_channels = this.spec.spec.len() as i32;
130+
(*this.dev).info = &this.spec.vtable;
131+
}
132+
let ret = unsafe { bindings::__iio_device_register(this.dev, module.as_ptr()) };
133+
if ret < 0 {
134+
dev_err!(data.dev.as_ref(), "Unable to register iio sensor");
135+
return Err(Error::from_errno(ret));
136+
}
137+
this.registered = true;
138+
Ok(this)
139+
}
140+
}
141+
142+
impl<T: MessageProcessor + 'static> Drop for IIORegistration<T> {
143+
fn drop(&mut self) {
144+
if self.dev != ptr::null_mut() {
145+
unsafe {
146+
if self.registered {
147+
bindings::iio_device_unregister(self.dev);
148+
}
149+
Arc::<AopSensorData<T>>::from_foreign((*self.dev).priv_);
150+
bindings::iio_device_free(self.dev);
151+
}
152+
}
153+
}
154+
}
155+
156+
unsafe impl<T: MessageProcessor> Send for IIORegistration<T> {}
157+
unsafe impl<T: MessageProcessor> Sync for IIORegistration<T> {}

0 commit comments

Comments
 (0)