Skip to content

Commit 930452e

Browse files
authored
Merge pull request torvalds#571 from wedsonaf/gpio
gpio: pl061: add rust implementation of pl061 gpio driver.
2 parents c708e80 + 7ca4be5 commit 930452e

File tree

5 files changed

+373
-0
lines changed

5 files changed

+373
-0
lines changed

drivers/gpio/Kconfig

+8
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,14 @@ config GPIO_PL061
471471
help
472472
Say yes here to support the PrimeCell PL061 GPIO device.
473473

474+
config GPIO_PL061_RUST
475+
tristate "PrimeCell PL061 GPIO support written in Rust"
476+
depends on ARM_AMBA
477+
select IRQ_DOMAIN
478+
select GPIOLIB_IRQCHIP
479+
help
480+
Say yes here to support the PrimeCell PL061 GPIO device
481+
474482
config GPIO_PMIC_EIC_SPRD
475483
tristate "Spreadtrum PMIC EIC support"
476484
depends on MFD_SC27XX_PMIC || COMPILE_TEST

drivers/gpio/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ obj-$(CONFIG_GPIO_PCIE_IDIO_24) += gpio-pcie-idio-24.o
118118
obj-$(CONFIG_GPIO_PCI_IDIO_16) += gpio-pci-idio-16.o
119119
obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o
120120
obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
121+
obj-$(CONFIG_GPIO_PL061_RUST) += gpio_pl061_rust.o
121122
obj-$(CONFIG_GPIO_PMIC_EIC_SPRD) += gpio-pmic-eic-sprd.o
122123
obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
123124
obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o

drivers/gpio/gpio_pl061_rust.rs

+362
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061).
4+
//!
5+
//! Based on the C driver written by Baruch Siach <[email protected]>.
6+
7+
#![no_std]
8+
#![feature(global_asm, allocator_api)]
9+
10+
use kernel::{
11+
amba, bit, bits_iter, declare_amba_id_table, device, gpio,
12+
io_mem::IoMem,
13+
irq::{self, ExtraResult, IrqData, LockedIrqData},
14+
power,
15+
prelude::*,
16+
sync::{Ref, RefBorrow, SpinLock},
17+
};
18+
19+
const GPIODIR: usize = 0x400;
20+
const GPIOIS: usize = 0x404;
21+
const GPIOIBE: usize = 0x408;
22+
const GPIOIEV: usize = 0x40C;
23+
const GPIOIE: usize = 0x410;
24+
const GPIOMIS: usize = 0x418;
25+
const GPIOIC: usize = 0x41C;
26+
const GPIO_SIZE: usize = 0x1000;
27+
28+
const PL061_GPIO_NR: u16 = 8;
29+
30+
#[derive(Default)]
31+
struct ContextSaveRegs {
32+
gpio_data: u8,
33+
gpio_dir: u8,
34+
gpio_is: u8,
35+
gpio_ibe: u8,
36+
gpio_iev: u8,
37+
gpio_ie: u8,
38+
}
39+
40+
#[derive(Default)]
41+
struct PL061Data {
42+
csave_regs: ContextSaveRegs,
43+
}
44+
45+
struct PL061Resources {
46+
base: IoMem<GPIO_SIZE>,
47+
parent_irq: u32,
48+
}
49+
50+
type PL061Registrations = gpio::RegistrationWithIrqChip<PL061Device>;
51+
52+
type DeviceData = device::Data<PL061Registrations, PL061Resources, SpinLock<PL061Data>>;
53+
54+
struct PL061Device;
55+
56+
impl gpio::Chip for PL061Device {
57+
type Data = Ref<DeviceData>;
58+
59+
kernel::declare_gpio_chip_operations!(
60+
get_direction,
61+
direction_input,
62+
direction_output,
63+
get,
64+
set
65+
);
66+
67+
fn get_direction(data: RefBorrow<'_, DeviceData>, offset: u32) -> Result<gpio::LineDirection> {
68+
let pl061 = data.resources().ok_or(Error::ENXIO)?;
69+
Ok(if pl061.base.readb(GPIODIR) & bit(offset) != 0 {
70+
gpio::LineDirection::Out
71+
} else {
72+
gpio::LineDirection::In
73+
})
74+
}
75+
76+
fn direction_input(data: RefBorrow<'_, DeviceData>, offset: u32) -> Result {
77+
let _guard = data.lock_irqdisable();
78+
let pl061 = data.resources().ok_or(Error::ENXIO)?;
79+
let mut gpiodir = pl061.base.readb(GPIODIR);
80+
gpiodir &= !bit(offset);
81+
pl061.base.writeb(gpiodir, GPIODIR);
82+
Ok(())
83+
}
84+
85+
fn direction_output(data: RefBorrow<'_, DeviceData>, offset: u32, value: bool) -> Result {
86+
let woffset = bit(offset + 2).into();
87+
let _guard = data.lock_irqdisable();
88+
let pl061 = data.resources().ok_or(Error::ENXIO)?;
89+
pl061.base.try_writeb((value as u8) << offset, woffset)?;
90+
let mut gpiodir = pl061.base.readb(GPIODIR);
91+
gpiodir |= bit(offset);
92+
pl061.base.writeb(gpiodir, GPIODIR);
93+
94+
// gpio value is set again, because pl061 doesn't allow to set value of a gpio pin before
95+
// configuring it in OUT mode.
96+
pl061.base.try_writeb((value as u8) << offset, woffset)?;
97+
Ok(())
98+
}
99+
100+
fn get(data: RefBorrow<'_, DeviceData>, offset: u32) -> Result<bool> {
101+
let pl061 = data.resources().ok_or(Error::ENXIO)?;
102+
Ok(pl061.base.try_readb(bit(offset + 2).into())? != 0)
103+
}
104+
105+
fn set(data: RefBorrow<'_, DeviceData>, offset: u32, value: bool) {
106+
if let Some(pl061) = data.resources() {
107+
let woffset = bit(offset + 2).into();
108+
let _ = pl061.base.try_writeb((value as u8) << offset, woffset);
109+
}
110+
}
111+
}
112+
113+
impl gpio::ChipWithIrqChip for PL061Device {
114+
fn handle_irq_flow(
115+
data: RefBorrow<'_, DeviceData>,
116+
desc: &irq::Descriptor,
117+
domain: &irq::Domain,
118+
) {
119+
let chained = desc.enter_chained();
120+
121+
if let Some(pl061) = data.resources() {
122+
let pending = pl061.base.readb(GPIOMIS);
123+
for offset in bits_iter(pending) {
124+
domain.generic_handle_chained(offset, &chained);
125+
}
126+
}
127+
}
128+
}
129+
130+
impl irq::Chip for PL061Device {
131+
type Data = Ref<DeviceData>;
132+
133+
kernel::declare_irq_chip_operations!(set_type, set_wake);
134+
135+
fn set_type(
136+
data: RefBorrow<'_, DeviceData>,
137+
irq_data: &mut LockedIrqData,
138+
trigger: u32,
139+
) -> Result<ExtraResult> {
140+
let offset = irq_data.hwirq();
141+
let bit = bit(offset);
142+
143+
if offset >= PL061_GPIO_NR.into() {
144+
return Err(Error::EINVAL);
145+
}
146+
147+
if trigger & (irq::Type::LEVEL_HIGH | irq::Type::LEVEL_LOW) != 0
148+
&& trigger & (irq::Type::EDGE_RISING | irq::Type::EDGE_FALLING) != 0
149+
{
150+
pr_err!(
151+
"trying to configure line {} for both level and edge detection, choose one!\n",
152+
offset
153+
);
154+
return Err(Error::EINVAL);
155+
}
156+
157+
let _guard = data.lock_irqdisable();
158+
let pl061 = data.resources().ok_or(Error::ENXIO)?;
159+
160+
let mut gpioiev = pl061.base.readb(GPIOIEV);
161+
let mut gpiois = pl061.base.readb(GPIOIS);
162+
let mut gpioibe = pl061.base.readb(GPIOIBE);
163+
164+
if trigger & (irq::Type::LEVEL_HIGH | irq::Type::LEVEL_LOW) != 0 {
165+
let polarity = trigger & irq::Type::LEVEL_HIGH != 0;
166+
167+
// Disable edge detection.
168+
gpioibe &= !bit;
169+
// Enable level detection.
170+
gpiois |= bit;
171+
// Select polarity.
172+
if polarity {
173+
gpioiev |= bit;
174+
} else {
175+
gpioiev &= !bit;
176+
}
177+
irq_data.set_level_handler();
178+
pr_debug!(
179+
"line {}: IRQ on {} level\n",
180+
offset,
181+
if polarity { "HIGH" } else { "LOW" }
182+
);
183+
} else if (trigger & irq::Type::EDGE_BOTH) == irq::Type::EDGE_BOTH {
184+
// Disable level detection.
185+
gpiois &= !bit;
186+
// Select both edges, settings this makes GPIOEV be ignored.
187+
gpioibe |= bit;
188+
irq_data.set_edge_handler();
189+
pr_debug!("line {}: IRQ on both edges\n", offset);
190+
} else if trigger & (irq::Type::EDGE_RISING | irq::Type::EDGE_FALLING) != 0 {
191+
let rising = trigger & irq::Type::EDGE_RISING != 0;
192+
193+
// Disable level detection.
194+
gpiois &= !bit;
195+
// Clear detection on both edges.
196+
gpioibe &= !bit;
197+
// Select edge.
198+
if rising {
199+
gpioiev |= bit;
200+
} else {
201+
gpioiev &= !bit;
202+
}
203+
irq_data.set_edge_handler();
204+
pr_debug!(
205+
"line {}: IRQ on {} edge\n",
206+
offset,
207+
if rising { "RISING" } else { "FALLING}" }
208+
);
209+
} else {
210+
// No trigger: disable everything.
211+
gpiois &= !bit;
212+
gpioibe &= !bit;
213+
gpioiev &= !bit;
214+
irq_data.set_bad_handler();
215+
pr_warn!("no trigger selected for line {}\n", offset);
216+
}
217+
218+
pl061.base.writeb(gpiois, GPIOIS);
219+
pl061.base.writeb(gpioibe, GPIOIBE);
220+
pl061.base.writeb(gpioiev, GPIOIEV);
221+
222+
Ok(ExtraResult::None)
223+
}
224+
225+
fn mask(data: RefBorrow<'_, DeviceData>, irq_data: &IrqData) {
226+
let mask = bit(irq_data.hwirq() % u64::from(PL061_GPIO_NR));
227+
let _guard = data.lock();
228+
if let Some(pl061) = data.resources() {
229+
let gpioie = pl061.base.readb(GPIOIE) & !mask;
230+
pl061.base.writeb(gpioie, GPIOIE);
231+
}
232+
}
233+
234+
fn unmask(data: RefBorrow<'_, DeviceData>, irq_data: &IrqData) {
235+
let mask = bit(irq_data.hwirq() % u64::from(PL061_GPIO_NR));
236+
let _guard = data.lock();
237+
if let Some(pl061) = data.resources() {
238+
let gpioie = pl061.base.readb(GPIOIE) | mask;
239+
pl061.base.writeb(gpioie, GPIOIE);
240+
}
241+
}
242+
243+
// This gets called from the edge IRQ handler to ACK the edge IRQ in the GPIOIC
244+
// (interrupt-clear) register. For level IRQs this is not needed: these go away when the level
245+
// signal goes away.
246+
fn ack(data: RefBorrow<'_, DeviceData>, irq_data: &IrqData) {
247+
let mask = bit(irq_data.hwirq() % u64::from(PL061_GPIO_NR));
248+
let _guard = data.lock();
249+
if let Some(pl061) = data.resources() {
250+
pl061.base.writeb(mask.into(), GPIOIC);
251+
}
252+
}
253+
254+
fn set_wake(data: RefBorrow<'_, DeviceData>, _irq_data: &IrqData, on: bool) -> Result {
255+
let pl061 = data.resources().ok_or(Error::ENXIO)?;
256+
irq::set_wake(pl061.parent_irq, on)
257+
}
258+
}
259+
260+
impl amba::Driver for PL061Device {
261+
type Data = Ref<DeviceData>;
262+
type PowerOps = Self;
263+
264+
declare_amba_id_table! [
265+
{ id: 0x00041061, mask: 0x000fffff, data: () },
266+
];
267+
268+
fn probe(dev: &mut amba::Device, _id: &amba::DeviceId) -> Result<Ref<DeviceData>> {
269+
let res = dev.take_resource().ok_or(Error::ENXIO)?;
270+
let irq = dev.irq(0).ok_or(Error::ENXIO)?;
271+
272+
let mut data = kernel::new_device_data!(
273+
gpio::RegistrationWithIrqChip::new(),
274+
PL061Resources {
275+
// SAFETY: This device doesn't support DMA.
276+
base: unsafe { IoMem::try_new(res)? },
277+
parent_irq: irq,
278+
},
279+
// SAFETY: We call `spinlock_init` below.
280+
unsafe { SpinLock::new(PL061Data::default()) },
281+
"PL061::Registrations"
282+
)?;
283+
284+
// SAFETY: General part of the data is pinned when `data` is.
285+
let gen = unsafe { data.as_mut().map_unchecked_mut(|d| &mut **d) };
286+
kernel::spinlock_init!(gen, "PL061::General");
287+
288+
let data = Ref::<DeviceData>::from(data);
289+
290+
data.resources().ok_or(Error::ENXIO)?.base.writeb(0, GPIOIE); // disable irqs
291+
292+
data.registrations()
293+
.ok_or(Error::ENXIO)?
294+
.as_pinned_mut()
295+
.register::<Self>(PL061_GPIO_NR, None, dev, data.clone(), irq)?;
296+
297+
pr_info!("PL061 GPIO chip registered\n");
298+
299+
Ok(data)
300+
}
301+
}
302+
303+
impl power::Operations for PL061Device {
304+
type Data = Ref<DeviceData>;
305+
306+
fn suspend(data: RefBorrow<'_, DeviceData>) -> Result {
307+
let mut inner = data.lock();
308+
let pl061 = data.resources().ok_or(Error::ENXIO)?;
309+
inner.csave_regs.gpio_data = 0;
310+
inner.csave_regs.gpio_dir = pl061.base.readb(GPIODIR);
311+
inner.csave_regs.gpio_is = pl061.base.readb(GPIOIS);
312+
inner.csave_regs.gpio_ibe = pl061.base.readb(GPIOIBE);
313+
inner.csave_regs.gpio_iev = pl061.base.readb(GPIOIEV);
314+
inner.csave_regs.gpio_ie = pl061.base.readb(GPIOIE);
315+
316+
for offset in 0..PL061_GPIO_NR {
317+
if inner.csave_regs.gpio_dir & bit(offset) != 0 {
318+
if let Ok(v) = <Self as gpio::Chip>::get(data, offset.into()) {
319+
inner.csave_regs.gpio_data |= (v as u8) << offset;
320+
}
321+
}
322+
}
323+
324+
Ok(())
325+
}
326+
327+
fn resume(data: RefBorrow<'_, DeviceData>) -> Result {
328+
let inner = data.lock();
329+
let pl061 = data.resources().ok_or(Error::ENXIO)?;
330+
331+
for offset in 0..PL061_GPIO_NR {
332+
if inner.csave_regs.gpio_dir & bit(offset) != 0 {
333+
let value = inner.csave_regs.gpio_data & bit(offset) != 0;
334+
let _ = <Self as gpio::Chip>::direction_output(data, offset.into(), value);
335+
} else {
336+
let _ = <Self as gpio::Chip>::direction_input(data, offset.into());
337+
}
338+
}
339+
340+
pl061.base.writeb(inner.csave_regs.gpio_is, GPIOIS);
341+
pl061.base.writeb(inner.csave_regs.gpio_ibe, GPIOIBE);
342+
pl061.base.writeb(inner.csave_regs.gpio_iev, GPIOIEV);
343+
pl061.base.writeb(inner.csave_regs.gpio_ie, GPIOIE);
344+
345+
Ok(())
346+
}
347+
348+
fn freeze(data: RefBorrow<'_, DeviceData>) -> Result {
349+
Self::suspend(data)
350+
}
351+
352+
fn restore(data: RefBorrow<'_, DeviceData>) -> Result {
353+
Self::resume(data)
354+
}
355+
}
356+
357+
module_amba_driver! {
358+
type: PL061Device,
359+
name: b"pl061_gpio",
360+
author: b"Wedson Almeida Filho",
361+
license: b"GPL v2",
362+
}

rust/kernel/gpio.rs

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ macro_rules! declare_gpio_chip_operations {
109109
const TO_USE: $crate::gpio::ToUse = $crate::gpio::USE_NONE;
110110
};
111111
($($i:ident),+) => {
112+
#[allow(clippy::needless_update)]
112113
const TO_USE: $crate::gpio::ToUse =
113114
$crate::gpio::ToUse {
114115
$($i: true),+ ,

0 commit comments

Comments
 (0)