Skip to content

改进bcm2835_rust #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/进度跟踪.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
- [ ] 完成代码编写并通过编译测试
4. [ ] 完成 i2c_msg 在 rust 下的实现
- [ ] 完成代码编写并通过编译测试
5. [ ] 完成 bcm2835_i2c_probe 函数的实现(依赖前几项工作)
5. [ ] 完成 of_node 部分功能的实现
- [ ] 完成of_node_full_name
6. [ ] 完成 interrupt mod 的部分功能实现
- [ ] 完成 request_irq 的封装
7. [ ] 完成 bcm2835_i2c_probe 函数的实现(依赖前几项工作)
- [ ] 完成代码编写并通过编译测试
- [ ] 使用真实的 i2c 设备进行功能测试
154 changes: 102 additions & 52 deletions drivers/i2c/busses/i2c_bcm2835_rust.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// SPDX-License-Identifier: GPL-2.0

//! BCM2835 master mode driver

use core::ops::Not;

use kernel::{
bindings,
clk::Clk,
Expand All @@ -13,7 +10,6 @@ use kernel::{
driver::DeviceRemoval,
error::to_result,
i2c::{self, I2cAdapter, I2cAdapterQuirks, I2cMsg, I2C_M_NOSTART, I2C_M_RD},
io_mem::IoMem,
irq,
of::DeviceId,
platform,
Expand Down Expand Up @@ -74,9 +70,6 @@ pub const BCM2835_I2C_REDL_SHIFT: u32 = 0;
pub const BCM2835_I2C_CDIV_MIN: u32 = 0x0002;
pub const BCM2835_I2C_CDIV_MAX: u32 = 0xFFFE;

// Debug and Clk_tout_ms is static mut in C code.
// Take as const for now.

pub const DEBUG: i32 = 0;

/// SMBUs-recommended 35ms
Expand All @@ -100,7 +93,7 @@ struct Bcm2835I2cDev {
reg_base: *mut u8,
irq: i32,
adapter: I2cAdapter,
completion: Completion,
completion: Arc<Completion>,
curr_msg: Option<Vec<I2cMsg>>,
bus_clk: Clk,
num_msgs: i32,
Expand All @@ -112,19 +105,29 @@ struct Bcm2835I2cDev {
debug_num_msgs: u32,
}

fn to_clk_bcm2835_i2c(hw_ptr: &ClkHw) -> &mut ClkBcm2835I2c {
unsafe { &mut *(container_of!(hw_ptr, ClkBcm2835I2c, hw) as *mut ClkBcm2835I2c) }
impl Bcm2835I2cDev {
unsafe fn from_ptr<'a>(ptr: *mut Self) -> &'a mut Self {
unsafe { &mut *ptr.cast() }
}

unsafe fn as_ptr(&self) -> *mut Self {
self as *const _ as *mut _
}
}

fn to_clk_bcm2835_i2c(hw_ptr: &ClkHw) -> &mut ClkBcm2835I2c<'_> {
unsafe { &mut *(container_of!(hw_ptr, ClkBcm2835I2c<'_>, hw) as *mut ClkBcm2835I2c<'_>) }
}

struct ClkBcm2835I2c {
struct ClkBcm2835I2c<'c> {
hw: ClkHw,
i2c_dev: &'static mut Bcm2835I2cDev,
i2c_dev: &'c Bcm2835I2cDev,
}

impl ClkBcm2835I2c {
impl<'c> ClkBcm2835I2c<'c> {
fn from_raw<'a>(ptr: *mut Self) -> &'a mut Self {
let prt = ptr.cast::<Self>();
unsafe { &mut *prt }
let ptr = ptr.cast::<Self>();
unsafe { &mut *ptr }
}
}

Expand Down Expand Up @@ -186,12 +189,12 @@ fn clk_bcm2835_i2c_set_rate(hw: &ClkHw, rate: u64, parent_rate: u64) -> Result<(
Ok(())
}

fn clk_bcm2835_i2c_round_rate(hw: &ClkHw, rate: u64, parent_rate: &mut u64) -> i32 {
fn clk_bcm2835_i2c_round_rate(hw: &ClkHw, rate: u64, parent_rate: &mut u64) -> i64 {
let Ok(divider) = clk_bcm2835_i2c_calc_divider(rate, *parent_rate) else {
return 0;
};

parent_rate.div_ceil(divider) as i32
parent_rate.div_ceil(divider) as i64
}

fn clk_bcm2835_i2c_recalc_rate(hw: &ClkHw, parent_rate: u64) -> u64 {
Expand Down Expand Up @@ -220,48 +223,38 @@ impl ClkOps for ClkBcm2835I2cOps {
}

impl Bcm2835I2cDev {
pub(crate) fn bcm2835_i2c_writel(&mut self, reg: usize, val: u32) {
pub(crate) fn bcm2835_i2c_writel(&self, reg: usize, val: u32) {
let i2c_reg = self.reg_base;
let addr = i2c_reg.wrapping_add(reg);
unsafe { bindings::writel(val as _, addr as _) }
}

pub(crate) fn bcm2835_i2c_readl(&mut self, reg: usize) -> u32 {
pub(crate) fn bcm2835_i2c_readl(&self, reg: usize) -> u32 {
let i2c_reg = self.reg_base;
let addr = i2c_reg.wrapping_add(reg);
unsafe { bindings::readl(addr as _) }
}

pub(crate) fn bcm2835_i2c_register_div(
dev: &'static mut Device,
mclk: &Clk,
i2c_dev: &'static mut Bcm2835I2cDev,
) -> Result<&'static mut Clk> {
let name = CString::try_from_fmt(fmt!("{}_div", dev.name()))?;
pub(crate) fn bcm2835_i2c_register_div(&mut self, mclk: &Clk) -> Result<&mut Clk> {
let name = CString::try_from_fmt(fmt!("{}_div", self.dev.name()))?;
let mclk_name = mclk.name();
let parent_names = [mclk_name.as_char_ptr()];
// Here: impl device.rs Device struct
// devm_alloc::<ClkBcm2835I2c>
let clk_i2c = unsafe {
let raw_ptr = dev.kzalloc::<ClkBcm2835I2c>()?;
let raw_ptr = self.dev.kzalloc::<ClkBcm2835I2c<'_>>()?;
let clk_i2c = ClkBcm2835I2c::from_raw(raw_ptr);
let init_data = ClkInitData::new()
.name_config(&name, &parent_names)
.set_ops::<ClkBcm2835I2cOps>()
.set_flags(0);
clk_i2c.hw.set_init_data(&init_data);
clk_i2c.i2c_dev = i2c_dev;
clk_i2c.i2c_dev = self;

clk_i2c.hw.register_clkdev(c_str!("div"), dev.name())?;
clk_i2c.hw.register_clkdev(c_str!("div"), self.dev.name())?;

clk_i2c
};

// Ensure these objects live long enough
// TODO: Try to achieve this in a more elegant way
// let _ = (name, parent_names, init_data);

dev.clk_register(&mut clk_i2c.hw)
self.dev.clk_register(&mut clk_i2c.hw)
}

pub(crate) fn bcm2835_fill_txfifo(&mut self) {
Expand Down Expand Up @@ -340,7 +333,7 @@ impl Bcm2835I2cDev {
}
}

fn bcm2835_i2c_isr(this_irq: i32, data: *mut core::ffi::c_void) -> irq::Return {
fn bcm2835_i2c_isr(this_irq: i32, data: &mut Bcm2835I2cDev) -> irq::Return {
let i2c_dev = unsafe { &mut *(data as *mut Bcm2835I2cDev) };

let mut val: u32 = i2c_dev.bcm2835_i2c_readl(BCM2835_I2C_S);
Expand Down Expand Up @@ -403,7 +396,7 @@ fn bcm2835_i2c_isr(this_irq: i32, data: *mut core::ffi::c_void) -> irq::Return {
}

unsafe extern "C" fn bcm2835_i2c_isr_cb(this_irq: i32, data: *mut core::ffi::c_void) -> u32 {
bcm2835_i2c_isr(this_irq, data) as u32
bcm2835_i2c_isr(this_irq, unsafe { &mut *data.cast() }) as u32
}

fn goto_complete(i2c_dev: &mut Bcm2835I2cDev) -> irq::Return {
Expand Down Expand Up @@ -500,6 +493,10 @@ fn bcm2835_i2c_func(adap: I2cAdapter) -> u32 {
struct Bcm2835I2cAlgo;

struct Bcm2835I2cData {}

unsafe impl Sync for Bcm2835I2cData {}
unsafe impl Send for Bcm2835I2cData {}

struct Bcm2835I2cDriver;

module_platform_driver! {
Expand Down Expand Up @@ -548,28 +545,41 @@ impl platform::Driver for Bcm2835I2cDriver {
pdev.name()
);

let dev = unsafe { Device::new(pdev.raw_device()) };
let dev = unsafe { Device::from_dev(pdev) };
let i2c_dev_ptr: *mut Bcm2835I2cDev = dev.kzalloc::<Bcm2835I2cDev>()?;

let i2c_dev = unsafe { &mut (*i2c_dev_ptr) };
i2c_dev.dev = dev;
i2c_dev.completion.reinit();
i2c_dev.reg_base = pdev.ioremap_resource(0)?;
dev_info!(pdev, "I2c bus device reg_base: {:?}\n", i2c_dev.reg_base);
let i2c_dev = unsafe { Bcm2835I2cDev::from_ptr(i2c_dev_ptr) };
i2c_dev.dev = dev.clone();
i2c_dev.completion = Arc::pin_init(new_completion!())?;

let mclk = i2c_dev.dev.clk_get()?;
let reg_base = pdev.ioremap_resource(0)?;
i2c_dev.reg_base = reg_base;
dev_info!(pdev, "I2c bus device reg_base: {:?}\n", reg_base);

// TODO: initialise i2c bus clock
// let bus_clk =
let mclk = dev.clk_get()?;

let bus_clk = i2c_dev.bcm2835_i2c_register_div(mclk)?;

let mut bus_clk_rate = 0;
let ret = i2c_dev
.dev
.of_property_read_u32(c_str!("clock-frequency"), &mut bus_clk_rate);
if let Err(_) = dev.of_property_read_u32(c_str!("clock-frequency"), &mut bus_clk_rate) {
bus_clk_rate = bindings::I2C_MAX_STANDARD_MODE_FREQ;
};
dev_info!(pdev, "I2c bus device clock-frequency: {}\n", bus_clk_rate);

let ret =
unsafe { bindings::clk_set_rate_exclusive(bus_clk.as_ptr(), bus_clk_rate as u64) };
if ret < 0 {
dev_err!(
pdev,
"Could not set clock frequency: {:?}\n",
to_result(ret)
);
to_result(ret)?;
}

i2c_dev.bus_clk.prepare_enable()?;

let irq = pdev.irq_resource(0)?;
i2c_dev.irq = irq;

let ret = unsafe {
bindings::request_threaded_irq(
Expand All @@ -581,13 +591,53 @@ impl platform::Driver for Bcm2835I2cDriver {
i2c_dev_ptr as *mut core::ffi::c_void,
)
};
if ret == 0 {
if ret < 0 {
dev_err!(pdev, "Could not request IRQ: {}\n", irq);
// TODO: disable bus clock
to_result(ret)?;
}
i2c_dev.irq = irq;

// TODO: setup i2c_adapter
let quirks = I2cAdapterQuirks::new().set_flags(i2c::I2C_AQ_NO_CLK_STRETCH as u64);
// let mut adap = i2c_dev.adapter;
unsafe {
i2c_dev.adapter.i2c_set_adapdata(i2c_dev.as_ptr());
// TODO: set owner
// i2c_dev.adapter.set_owner((&bindings::__this_module) as *const _ as *mut _);
i2c_dev.adapter.set_class(bindings::I2C_CLASS_DEPRECATED);
let full_name = bindings::of_node_full_name((*pdev.raw_device()).of_node);
let adap_name =
CString::try_from_fmt(fmt!("bcm2835 ({})", CStr::from_char_ptr(full_name)))?;
i2c_dev.adapter.set_name(&adap_name);
}
// i2c_dev.adapter = adap;

/*
* Disable the hardware clock stretching timeout. SMBUS
* specifies a limit for how long the device can stretch the
* clock, but core I2C doesn't.
*/
i2c_dev.bcm2835_i2c_writel(BCM2835_I2C_CLKT, 0);
i2c_dev.bcm2835_i2c_writel(BCM2835_I2C_C, 0);

let ret = unsafe { bindings::i2c_add_adapter(i2c_dev.adapter.as_ptr()) };
if ret < 0 {
dev_info!(pdev, "Could not add I2C adapter: {:?}\n", to_result(ret));
unsafe {
bindings::free_irq(irq as u32, i2c_dev_ptr as *mut core::ffi::c_void);
}
}
let _ = to_result(ret)?;

let dev_data =
kernel::new_device_data!((), (), Bcm2835I2cData {}, "BCM2835_I2C device data")?;
/*
* Disable the hardware clock stretching timeout. SMBUS
* specifies a limit for how long the device can stretch the
* clock, but core I2C doesn't.
*/
i2c_dev.bcm2835_i2c_writel(BCM2835_I2C_CLKT, 0);
i2c_dev.bcm2835_i2c_writel(BCM2835_I2C_C, 0);
Ok(dev_data.into())
}

Expand Down
2 changes: 2 additions & 0 deletions rust/bindings/bindings_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/swait.h>

/* `bindgen` gets confused at certain things. */
const size_t BINDINGS_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN;
Expand Down
6 changes: 6 additions & 0 deletions rust/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,12 @@ const struct of_device_id *rust_helper_of_match_device(
}
EXPORT_SYMBOL_GPL(rust_helper_of_match_device);

const char *rust_helper_of_node_full_name(const struct device_node *np)
{
return rust_helper_of_node_full_name(np);
}
EXPORT_SYMBOL_GPL(rust_helper_of_node_full_name);

void *rust_helper_platform_get_drvdata(const struct platform_device *pdev)
{
return platform_get_drvdata(pdev);
Expand Down
1 change: 1 addition & 0 deletions rust/kernel/clk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ impl Drop for Clk {
fn drop(&mut self) {
// SAFETY: The pointer is valid by the type invariant.
unsafe {
bindings::clk_unprepare(self.0.get());
bindings::clk_rate_exclusive_put(self.0.get());
bindings::clk_put(self.0.get());
}
Expand Down
2 changes: 1 addition & 1 deletion rust/kernel/clk_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl ClkHw {
}

// Register one clock lookup for a struct clk_hw
pub fn register_clkdev(&mut self, con_id: &'static CStr, dev_id: &'static CStr) -> Result {
pub fn register_clkdev(&mut self, con_id: &CStr, dev_id: &CStr) -> Result {
let ret = unsafe {
bindings::clk_hw_register_clkdev(
self.0.get(),
Expand Down
Loading
Loading