summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/cpufreq/rcpufreq_dt.rs5
-rw-r--r--drivers/cxl/core/mce.h2
-rw-r--r--drivers/gpu/drm/drm_panic_qr.rs4
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_log.c3
-rw-r--r--drivers/gpu/drm/nova/nova.rs2
-rw-r--r--drivers/gpu/drm/xe/xe_vm_types.h2
-rw-r--r--drivers/gpu/nova-core/driver.rs2
-rw-r--r--drivers/gpu/nova-core/firmware.rs5
-rw-r--r--drivers/gpu/nova-core/nova_core.rs2
-rw-r--r--drivers/gpu/nova-core/regs.rs2
-rw-r--r--drivers/gpu/nova-core/regs/macros.rs2
-rw-r--r--drivers/gpu/nova-core/util.rs4
-rw-r--r--drivers/i3c/device.c11
-rw-r--r--drivers/i3c/internals.h38
-rw-r--r--drivers/i3c/master.c38
-rw-r--r--drivers/i3c/master/Kconfig10
-rw-r--r--drivers/i3c/master/Makefile1
-rw-r--r--drivers/i3c/master/dw-i3c-master.c47
-rw-r--r--drivers/i3c/master/i3c-master-cdns.c90
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/core.c2
-rw-r--r--drivers/i3c/master/renesas-i3c.c1404
-rw-r--r--drivers/i3c/master/svc-i3c-master.c32
-rw-r--r--drivers/md/dm-flakey.c9
-rw-r--r--drivers/md/dm-ima.c42
-rw-r--r--drivers/md/dm-path-selector.c8
-rw-r--r--drivers/md/dm-path-selector.h2
-rw-r--r--drivers/md/dm-ps-historical-service-time.c9
-rw-r--r--drivers/md/dm-ps-io-affinity.c5
-rw-r--r--drivers/md/dm-ps-queue-length.c9
-rw-r--r--drivers/md/dm-ps-round-robin.c9
-rw-r--r--drivers/md/dm-ps-service-time.c9
-rw-r--r--drivers/md/dm-raid.c7
-rw-r--r--drivers/md/dm-table.c10
-rw-r--r--drivers/md/dm-thin.c7
-rw-r--r--drivers/md/dm-vdo/funnel-workqueue.c3
-rw-r--r--drivers/md/dm-verity-fec.c4
-rw-r--r--drivers/md/dm-verity-target.c185
-rw-r--r--drivers/md/dm-verity.h22
-rw-r--r--drivers/md/dm-zone.c2
-rw-r--r--drivers/md/dm-zoned-target.c2
-rw-r--r--drivers/md/dm.c11
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c2
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_trace.c3
-rw-r--r--drivers/net/wwan/t7xx/t7xx_port_trace.c2
-rw-r--r--drivers/pci/hotplug/pnv_php.c248
-rw-r--r--drivers/rtc/Kconfig21
-rw-r--r--drivers/rtc/Makefile2
-rw-r--r--drivers/rtc/lib.c40
-rw-r--r--drivers/rtc/rtc-ds1307.c30
-rw-r--r--drivers/rtc/rtc-ds1685.c4
-rw-r--r--drivers/rtc/rtc-hym8563.c15
-rw-r--r--drivers/rtc/rtc-m41t80.c25
-rw-r--r--drivers/rtc/rtc-max31335.c12
-rw-r--r--drivers/rtc/rtc-nct3018y.c15
-rw-r--r--drivers/rtc/rtc-pcf85063.c267
-rw-r--r--drivers/rtc/rtc-pcf8563.c15
-rw-r--r--drivers/rtc/rtc-rv3028.c15
-rw-r--r--drivers/rtc/rtc-rv3032.c21
-rw-r--r--drivers/rtc/rtc-s3c.c8
-rw-r--r--drivers/rtc/rtc-sh.c8
-rw-r--r--drivers/rtc/sysfs.c64
-rw-r--r--drivers/rtc/test_rtc_lib.c (renamed from drivers/rtc/lib_test.c)0
62 files changed, 2244 insertions, 636 deletions
diff --git a/drivers/cpufreq/rcpufreq_dt.rs b/drivers/cpufreq/rcpufreq_dt.rs
index 9ad85fe6fd05..7e1fbf9a091f 100644
--- a/drivers/cpufreq/rcpufreq_dt.rs
+++ b/drivers/cpufreq/rcpufreq_dt.rs
@@ -9,7 +9,6 @@ use kernel::{
cpumask::CpumaskVar,
device::{Core, Device},
error::code::*,
- fmt,
macros::vtable,
module_platform_driver, of, opp, platform,
prelude::*,
@@ -19,7 +18,7 @@ use kernel::{
/// Finds exact supply name from the OF node.
fn find_supply_name_exact(dev: &Device, name: &str) -> Option<CString> {
- let prop_name = CString::try_from_fmt(fmt!("{}-supply", name)).ok()?;
+ let prop_name = CString::try_from_fmt(fmt!("{name}-supply")).ok()?;
dev.fwnode()?
.property_present(&prop_name)
.then(|| CString::try_from_fmt(fmt!("{name}")).ok())
@@ -221,7 +220,7 @@ impl platform::Driver for CPUFreqDTDriver {
module_platform_driver! {
type: CPUFreqDTDriver,
name: "cpufreq-dt",
- author: "Viresh Kumar <viresh.kumar@linaro.org>",
+ authors: ["Viresh Kumar <viresh.kumar@linaro.org>"],
description: "Generic CPUFreq DT driver",
license: "GPL v2",
}
diff --git a/drivers/cxl/core/mce.h b/drivers/cxl/core/mce.h
index ace73424eeb6..ca272e8db6c7 100644
--- a/drivers/cxl/core/mce.h
+++ b/drivers/cxl/core/mce.h
@@ -7,7 +7,7 @@
#ifdef CONFIG_CXL_MCE
int devm_cxl_register_mce_notifier(struct device *dev,
- struct notifier_block *mce_notifer);
+ struct notifier_block *mce_notifier);
#else
static inline int
devm_cxl_register_mce_notifier(struct device *dev,
diff --git a/drivers/gpu/drm/drm_panic_qr.rs b/drivers/gpu/drm/drm_panic_qr.rs
index 18492daae4b3..09a9b452e8b7 100644
--- a/drivers/gpu/drm/drm_panic_qr.rs
+++ b/drivers/gpu/drm/drm_panic_qr.rs
@@ -404,7 +404,7 @@ impl DecFifo {
let mut out = 0;
let mut exp = 1;
for i in 0..poplen {
- out += self.decimals[self.len + i] as u16 * exp;
+ out += u16::from(self.decimals[self.len + i]) * exp;
exp *= 10;
}
Some((out, NUM_CHARS_BITS[poplen]))
@@ -425,7 +425,7 @@ impl Iterator for SegmentIterator<'_> {
match self.segment {
Segment::Binary(data) => {
if self.offset < data.len() {
- let byte = data[self.offset] as u16;
+ let byte = u16::from(data[self.offset]);
self.offset += 1;
Some((byte, 8))
} else {
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
index e8a04e476c57..09a64f224c49 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
@@ -220,8 +220,7 @@ static int guc_action_control_log(struct intel_guc *guc, bool enable,
*/
static int subbuf_start_callback(struct rchan_buf *buf,
void *subbuf,
- void *prev_subbuf,
- size_t prev_padding)
+ void *prev_subbuf)
{
/*
* Use no-overwrite mode by default, where relay will stop accepting
diff --git a/drivers/gpu/drm/nova/nova.rs b/drivers/gpu/drm/nova/nova.rs
index 902876aa14d1..64fd670e99e1 100644
--- a/drivers/gpu/drm/nova/nova.rs
+++ b/drivers/gpu/drm/nova/nova.rs
@@ -12,7 +12,7 @@ use crate::driver::NovaDriver;
kernel::module_auxiliary_driver! {
type: NovaDriver,
name: "Nova",
- author: "Danilo Krummrich",
+ authors: ["Danilo Krummrich"],
description: "Nova GPU driver",
license: "GPL v2",
}
diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h
index bed6088e1bb3..8a07feef503b 100644
--- a/drivers/gpu/drm/xe/xe_vm_types.h
+++ b/drivers/gpu/drm/xe/xe_vm_types.h
@@ -266,7 +266,7 @@ struct xe_vm {
* up for revalidation. Protected from access with the
* @invalidated_lock. Removing items from the list
* additionally requires @lock in write mode, and adding
- * items to the list requires either the @userptr.notifer_lock in
+ * items to the list requires either the @userptr.notifier_lock in
* write mode, OR @lock in write mode.
*/
struct list_head invalidated;
diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 5749bad9c285..274989ea1fb4 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -19,7 +19,7 @@ kernel::pci_device_table!(
MODULE_PCI_TABLE,
<NovaCore as pci::Driver>::IdInfo,
[(
- pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_NVIDIA, bindings::PCI_ANY_ID as _),
+ pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_NVIDIA, bindings::PCI_ANY_ID as u32),
()
)]
);
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index 0fdece652587..2931912ddba0 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -30,11 +30,12 @@ pub(crate) struct Firmware {
impl Firmware {
pub(crate) fn new(dev: &device::Device, chipset: Chipset, ver: &str) -> Result<Firmware> {
- let mut chip_name = CString::try_from_fmt(fmt!("{}", chipset))?;
+ let mut chip_name = CString::try_from_fmt(fmt!("{chipset}"))?;
chip_name.make_ascii_lowercase();
+ let chip_name = &*chip_name;
let request = |name_| {
- CString::try_from_fmt(fmt!("nvidia/{}/gsp/{}-{}.bin", &*chip_name, name_, ver))
+ CString::try_from_fmt(fmt!("nvidia/{chip_name}/gsp/{name_}-{ver}.bin"))
.and_then(|path| firmware::Firmware::request(&path, dev))
};
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index de14f2e92636..cb2bbb30cba1 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -18,7 +18,7 @@ pub(crate) const MODULE_NAME: &kernel::str::CStr = <LocalModule as kernel::Modul
kernel::module_pci_driver! {
type: driver::NovaCore,
name: "NovaCore",
- author: "Danilo Krummrich",
+ authors: ["Danilo Krummrich"],
description: "Nova Core GPU driver",
license: "GPL v2",
firmware: [],
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 5ccfb61f850a..d49fddf6a3c6 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -36,7 +36,7 @@ impl NV_PMC_BOOT_0 {
pub(crate) fn chipset(self) -> Result<Chipset> {
self.architecture()
.map(|arch| {
- ((arch as u32) << Self::IMPLEMENTATION.len()) | self.implementation() as u32
+ ((arch as u32) << Self::IMPLEMENTATION.len()) | u32::from(self.implementation())
})
.and_then(Chipset::try_from)
}
diff --git a/drivers/gpu/nova-core/regs/macros.rs b/drivers/gpu/nova-core/regs/macros.rs
index cdf668073480..a3e6de1779d4 100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -307,7 +307,7 @@ macro_rules! register {
pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
const MASK: u32 = $name::[<$field:upper _MASK>];
const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
- let value = ((value as u32) << SHIFT) & MASK;
+ let value = (u32::from(value) << SHIFT) & MASK;
self.0 = (self.0 & !MASK) | value;
self
diff --git a/drivers/gpu/nova-core/util.rs b/drivers/gpu/nova-core/util.rs
index 64fb13760764..76cedf3710d7 100644
--- a/drivers/gpu/nova-core/util.rs
+++ b/drivers/gpu/nova-core/util.rs
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
use kernel::prelude::*;
-use kernel::time::{Delta, Instant};
+use kernel::time::{Delta, Instant, Monotonic};
pub(crate) const fn to_lowercase_bytes<const N: usize>(s: &str) -> [u8; N] {
let src = s.as_bytes();
@@ -33,7 +33,7 @@ pub(crate) const fn const_bytes_to_str(bytes: &[u8]) -> &str {
/// TODO[DLAY]: replace with `read_poll_timeout` once it is available.
/// (https://lore.kernel.org/lkml/20250220070611.214262-8-fujita.tomonori@gmail.com/)
pub(crate) fn wait_on<R, F: Fn() -> Option<R>>(timeout: Delta, cond: F) -> Result<R> {
- let start_time = Instant::now();
+ let start_time = Instant::<Monotonic>::now();
loop {
if let Some(ret) = cond() {
diff --git a/drivers/i3c/device.c b/drivers/i3c/device.c
index e80e48756914..2396545763ff 100644
--- a/drivers/i3c/device.c
+++ b/drivers/i3c/device.c
@@ -26,11 +26,12 @@
*
* This function can sleep and thus cannot be called in atomic context.
*
- * Return: 0 in case of success, a negative error core otherwise.
- * -EAGAIN: controller lost address arbitration. Target
- * (IBI, HJ or controller role request) win the bus. Client
- * driver needs to resend the 'xfers' some time later.
- * See I3C spec ver 1.1.1 09-Jun-2021. Section: 5.1.2.2.3.
+ * Return:
+ * * 0 in case of success, a negative error core otherwise.
+ * * -EAGAIN: controller lost address arbitration. Target (IBI, HJ or
+ * controller role request) win the bus. Client driver needs to resend the
+ * 'xfers' some time later. See I3C spec ver 1.1.1 09-Jun-2021. Section:
+ * 5.1.2.2.3.
*/
int i3c_device_do_priv_xfers(struct i3c_device *dev,
struct i3c_priv_xfer *xfers,
diff --git a/drivers/i3c/internals.h b/drivers/i3c/internals.h
index 433f6088b7ce..0d857cc68cc5 100644
--- a/drivers/i3c/internals.h
+++ b/drivers/i3c/internals.h
@@ -9,6 +9,7 @@
#define I3C_INTERNALS_H
#include <linux/i3c/master.h>
+#include <linux/io.h>
void i3c_bus_normaluse_lock(struct i3c_bus *bus);
void i3c_bus_normaluse_unlock(struct i3c_bus *bus);
@@ -22,4 +23,41 @@ int i3c_dev_enable_ibi_locked(struct i3c_dev_desc *dev);
int i3c_dev_request_ibi_locked(struct i3c_dev_desc *dev,
const struct i3c_ibi_setup *req);
void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev);
+
+/**
+ * i3c_writel_fifo - Write data buffer to 32bit FIFO
+ * @addr: FIFO Address to write to
+ * @buf: Pointer to the data bytes to write
+ * @nbytes: Number of bytes to write
+ */
+static inline void i3c_writel_fifo(void __iomem *addr, const void *buf,
+ int nbytes)
+{
+ writesl(addr, buf, nbytes / 4);
+ if (nbytes & 3) {
+ u32 tmp = 0;
+
+ memcpy(&tmp, buf + (nbytes & ~3), nbytes & 3);
+ writel(tmp, addr);
+ }
+}
+
+/**
+ * i3c_readl_fifo - Read data buffer from 32bit FIFO
+ * @addr: FIFO Address to read from
+ * @buf: Pointer to the buffer to store read bytes
+ * @nbytes: Number of bytes to read
+ */
+static inline void i3c_readl_fifo(const void __iomem *addr, void *buf,
+ int nbytes)
+{
+ readsl(addr, buf, nbytes / 4);
+ if (nbytes & 3) {
+ u32 tmp;
+
+ tmp = readl(addr);
+ memcpy(buf + (nbytes & ~3), &tmp, nbytes & 3);
+ }
+}
+
#endif /* I3C_INTERNAL_H */
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index fd81871609d9..2ef898a8fd80 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -141,7 +141,7 @@ static ssize_t bcr_show(struct device *dev,
i3c_bus_normaluse_lock(bus);
desc = dev_to_i3cdesc(dev);
- ret = sprintf(buf, "%x\n", desc->info.bcr);
+ ret = sprintf(buf, "0x%02x\n", desc->info.bcr);
i3c_bus_normaluse_unlock(bus);
return ret;
@@ -158,7 +158,7 @@ static ssize_t dcr_show(struct device *dev,
i3c_bus_normaluse_lock(bus);
desc = dev_to_i3cdesc(dev);
- ret = sprintf(buf, "%x\n", desc->info.dcr);
+ ret = sprintf(buf, "0x%02x\n", desc->info.dcr);
i3c_bus_normaluse_unlock(bus);
return ret;
@@ -727,12 +727,12 @@ static int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode,
switch (i3cbus->mode) {
case I3C_BUS_MODE_PURE:
if (!i3cbus->scl_rate.i3c)
- i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ i3cbus->scl_rate.i3c = I3C_BUS_I3C_SCL_TYP_RATE;
break;
case I3C_BUS_MODE_MIXED_FAST:
case I3C_BUS_MODE_MIXED_LIMITED:
if (!i3cbus->scl_rate.i3c)
- i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ i3cbus->scl_rate.i3c = I3C_BUS_I3C_SCL_TYP_RATE;
if (!i3cbus->scl_rate.i2c)
i3cbus->scl_rate.i2c = max_i2c_scl_rate;
break;
@@ -754,8 +754,8 @@ static int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode,
* I3C/I2C frequency may have been overridden, check that user-provided
* values are not exceeding max possible frequency.
*/
- if (i3cbus->scl_rate.i3c > I3C_BUS_MAX_I3C_SCL_RATE ||
- i3cbus->scl_rate.i2c > I3C_BUS_I2C_FM_PLUS_SCL_RATE)
+ if (i3cbus->scl_rate.i3c > I3C_BUS_I3C_SCL_MAX_RATE ||
+ i3cbus->scl_rate.i2c > I3C_BUS_I2C_FM_PLUS_SCL_MAX_RATE)
return -EINVAL;
return 0;
@@ -837,14 +837,14 @@ static int i3c_master_send_ccc_cmd_locked(struct i3c_master_controller *master,
return -EINVAL;
if (!master->ops->send_ccc_cmd)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
if ((cmd->id & I3C_CCC_DIRECT) && (!cmd->dests || !cmd->ndests))
return -EINVAL;
if (master->ops->supports_ccc_cmd &&
!master->ops->supports_ccc_cmd(master, cmd))
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
ret = master->ops->send_ccc_cmd(master, cmd);
if (ret) {
@@ -1439,7 +1439,7 @@ static int i3c_master_retrieve_dev_info(struct i3c_dev_desc *dev)
if (dev->info.bcr & I3C_BCR_HDR_CAP) {
ret = i3c_master_gethdrcap_locked(master, &dev->info);
- if (ret)
+ if (ret && ret != -EOPNOTSUPP)
return ret;
}
@@ -2210,7 +2210,7 @@ of_i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master,
*/
if (boardinfo->base.flags & I2C_CLIENT_TEN) {
dev_err(dev, "I2C device with 10 bit address not supported.");
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
}
/* LVR is encoded in reg[2]. */
@@ -2340,13 +2340,13 @@ static int i3c_master_i2c_adapter_xfer(struct i2c_adapter *adap,
return -EINVAL;
if (!master->ops->i2c_xfers)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
/* Doing transfers to different devices is not supported. */
addr = xfers[0].addr;
for (i = 1; i < nxfers; i++) {
if (addr != xfers[i].addr)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
}
i3c_bus_normaluse_lock(&master->bus);
@@ -2467,6 +2467,8 @@ static int i3c_i2c_notifier_call(struct notifier_block *nb, unsigned long action
case BUS_NOTIFY_DEL_DEVICE:
ret = i3c_master_i2c_detach(adap, client);
break;
+ default:
+ ret = -EINVAL;
}
i3c_bus_maintenance_unlock(&master->bus);
@@ -2766,7 +2768,7 @@ static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops)
* controller)
* @ops: the master controller operations
* @secondary: true if you are registering a secondary master. Will return
- * -ENOTSUPP if set to true since secondary masters are not yet
+ * -EOPNOTSUPP if set to true since secondary masters are not yet
* supported
*
* This function takes care of everything for you:
@@ -2785,7 +2787,7 @@ int i3c_master_register(struct i3c_master_controller *master,
const struct i3c_master_controller_ops *ops,
bool secondary)
{
- unsigned long i2c_scl_rate = I3C_BUS_I2C_FM_PLUS_SCL_RATE;
+ unsigned long i2c_scl_rate = I3C_BUS_I2C_FM_PLUS_SCL_MAX_RATE;
struct i3c_bus *i3cbus = i3c_master_get_bus(master);
enum i3c_bus_mode mode = I3C_BUS_MODE_PURE;
struct i2c_dev_boardinfo *i2cbi;
@@ -2793,7 +2795,7 @@ int i3c_master_register(struct i3c_master_controller *master,
/* We do not support secondary masters yet. */
if (secondary)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
ret = i3c_master_check_ops(ops);
if (ret)
@@ -2844,7 +2846,7 @@ int i3c_master_register(struct i3c_master_controller *master,
}
if (i2cbi->lvr & I3C_LVR_I2C_FM_MODE)
- i2c_scl_rate = I3C_BUS_I2C_FM_SCL_RATE;
+ i2c_scl_rate = I3C_BUS_I2C_FM_SCL_MAX_RATE;
}
ret = i3c_bus_set_mode(i3cbus, mode, i2c_scl_rate);
@@ -2954,7 +2956,7 @@ int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
return -EINVAL;
if (!master->ops->priv_xfers)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
return master->ops->priv_xfers(dev, xfers, nxfers);
}
@@ -3004,7 +3006,7 @@ int i3c_dev_request_ibi_locked(struct i3c_dev_desc *dev,
int ret;
if (!master->ops->request_ibi)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
if (dev->ibi)
return -EBUSY;
diff --git a/drivers/i3c/master/Kconfig b/drivers/i3c/master/Kconfig
index 7b30db3253af..13df2944f2ec 100644
--- a/drivers/i3c/master/Kconfig
+++ b/drivers/i3c/master/Kconfig
@@ -64,3 +64,13 @@ config MIPI_I3C_HCI_PCI
This driver can also be built as a module. If so, the module will be
called mipi-i3c-hci-pci.
+
+config RENESAS_I3C
+ tristate "Renesas I3C controller driver"
+ depends on HAS_IOMEM
+ depends on ARCH_RENESAS || COMPILE_TEST
+ help
+ Support the Renesas I3C controller as found in some RZ variants.
+
+ This driver can also be built as a module. If so, the module will be
+ called renesas-i3c.
diff --git a/drivers/i3c/master/Makefile b/drivers/i3c/master/Makefile
index 3e97960160bc..aac74f3e3851 100644
--- a/drivers/i3c/master/Makefile
+++ b/drivers/i3c/master/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_DW_I3C_MASTER) += dw-i3c-master.o
obj-$(CONFIG_AST2600_I3C_MASTER) += ast2600-i3c-master.o
obj-$(CONFIG_SVC_I3C_MASTER) += svc-i3c-master.o
obj-$(CONFIG_MIPI_I3C_HCI) += mipi-i3c-hci/
+obj-$(CONFIG_RENESAS_I3C) += renesas-i3c.o
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
index 611c22b72c15..974122b2d20e 100644
--- a/drivers/i3c/master/dw-i3c-master.c
+++ b/drivers/i3c/master/dw-i3c-master.c
@@ -23,6 +23,7 @@
#include <linux/reset.h>
#include <linux/slab.h>
+#include "../internals.h"
#include "dw-i3c-master.h"
#define DEVICE_CTRL 0x0
@@ -336,37 +337,19 @@ static int dw_i3c_master_get_free_pos(struct dw_i3c_master *master)
static void dw_i3c_master_wr_tx_fifo(struct dw_i3c_master *master,
const u8 *bytes, int nbytes)
{
- writesl(master->regs + RX_TX_DATA_PORT, bytes, nbytes / 4);
- if (nbytes & 3) {
- u32 tmp = 0;
-
- memcpy(&tmp, bytes + (nbytes & ~3), nbytes & 3);
- writesl(master->regs + RX_TX_DATA_PORT, &tmp, 1);
- }
-}
-
-static void dw_i3c_master_read_fifo(struct dw_i3c_master *master,
- int reg, u8 *bytes, int nbytes)
-{
- readsl(master->regs + reg, bytes, nbytes / 4);
- if (nbytes & 3) {
- u32 tmp;
-
- readsl(master->regs + reg, &tmp, 1);
- memcpy(bytes + (nbytes & ~3), &tmp, nbytes & 3);
- }
+ i3c_writel_fifo(master->regs + RX_TX_DATA_PORT, bytes, nbytes);
}
static void dw_i3c_master_read_rx_fifo(struct dw_i3c_master *master,
u8 *bytes, int nbytes)
{
- return dw_i3c_master_read_fifo(master, RX_TX_DATA_PORT, bytes, nbytes);
+ i3c_readl_fifo(master->regs + RX_TX_DATA_PORT, bytes, nbytes);
}
static void dw_i3c_master_read_ibi_fifo(struct dw_i3c_master *master,
u8 *bytes, int nbytes)
{
- return dw_i3c_master_read_fifo(master, IBI_QUEUE_STATUS, bytes, nbytes);
+ i3c_readl_fifo(master->regs + IBI_QUEUE_STATUS, bytes, nbytes);
}
static struct dw_i3c_xfer *
@@ -622,14 +605,14 @@ static int dw_i2c_clk_cfg(struct dw_i3c_master *master)
core_period = DIV_ROUND_UP(1000000000, core_rate);
lcnt = DIV_ROUND_UP(I3C_BUS_I2C_FMP_TLOW_MIN_NS, core_period);
- hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_PLUS_SCL_RATE) - lcnt;
+ hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_PLUS_SCL_MAX_RATE) - lcnt;
scl_timing = SCL_I2C_FMP_TIMING_HCNT(hcnt) |
SCL_I2C_FMP_TIMING_LCNT(lcnt);
writel(scl_timing, master->regs + SCL_I2C_FMP_TIMING);
master->i2c_fmp_timing = scl_timing;
lcnt = DIV_ROUND_UP(I3C_BUS_I2C_FM_TLOW_MIN_NS, core_period);
- hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_SCL_RATE) - lcnt;
+ hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_SCL_MAX_RATE) - lcnt;
scl_timing = SCL_I2C_FM_TIMING_HCNT(hcnt) |
SCL_I2C_FM_TIMING_LCNT(lcnt);
writel(scl_timing, master->regs + SCL_I2C_FM_TIMING);
@@ -699,7 +682,6 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
dw_i3c_master_enable(master);
rpm_out:
- pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret;
}
@@ -829,7 +811,6 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
else
ret = dw_i3c_ccc_set(master, ccc);
- pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret;
}
@@ -912,7 +893,6 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
dw_i3c_master_free_xfer(xfer);
rpm_out:
- pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret;
}
@@ -932,7 +912,7 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
return 0;
if (i3c_nxfers > master->caps.cmdfifodepth)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
for (i = 0; i < i3c_nxfers; i++) {
if (i3c_xfers[i].rnw)
@@ -943,7 +923,7 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
if (ntxwords > master->caps.datafifodepth ||
nrxwords > master->caps.datafifodepth)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
xfer = dw_i3c_master_alloc_xfer(master, i3c_nxfers);
if (!xfer)
@@ -998,7 +978,6 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
ret = xfer->ret;
dw_i3c_master_free_xfer(xfer);
- pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret;
}
@@ -1093,7 +1072,7 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
return 0;
if (i2c_nxfers > master->caps.cmdfifodepth)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
for (i = 0; i < i2c_nxfers; i++) {
if (i2c_xfers[i].flags & I2C_M_RD)
@@ -1104,7 +1083,7 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
if (ntxwords > master->caps.datafifodepth ||
nrxwords > master->caps.datafifodepth)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
xfer = dw_i3c_master_alloc_xfer(master, i2c_nxfers);
if (!xfer)
@@ -1142,13 +1121,12 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
}
dw_i3c_master_enqueue_xfer(master, xfer);
- if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT))
+ if (!wait_for_completion_timeout(&xfer->comp, m->i2c.timeout))
dw_i3c_master_dequeue_xfer(master, xfer);
ret = xfer->ret;
dw_i3c_master_free_xfer(xfer);
- pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret;
}
@@ -1316,7 +1294,6 @@ static int dw_i3c_master_disable_hotjoin(struct i3c_master_controller *m)
writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
master->regs + DEVICE_CTRL);
- pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return 0;
}
@@ -1342,7 +1319,6 @@ static int dw_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
if (rc) {
dw_i3c_master_set_sir_enabled(master, dev, data->index, false);
- pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
}
@@ -1362,7 +1338,6 @@ static int dw_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
dw_i3c_master_set_sir_enabled(master, dev, data->index, false);
- pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return 0;
}
diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c
index fd3752cea654..97b151564d3d 100644
--- a/drivers/i3c/master/i3c-master-cdns.c
+++ b/drivers/i3c/master/i3c-master-cdns.c
@@ -23,6 +23,8 @@
#include <linux/spinlock.h>
#include <linux/workqueue.h>
+#include "../internals.h"
+
#define DEV_ID 0x0
#define DEV_ID_I3C_MASTER 0x5034
@@ -412,7 +414,6 @@ struct cdns_i3c_master {
} xferqueue;
void __iomem *regs;
struct clk *sysclk;
- struct clk *pclk;
struct cdns_i3c_master_caps caps;
unsigned long i3c_scl_lim;
const struct cdns_i3c_data *devdata;
@@ -427,25 +428,13 @@ to_cdns_i3c_master(struct i3c_master_controller *master)
static void cdns_i3c_master_wr_to_tx_fifo(struct cdns_i3c_master *master,
const u8 *bytes, int nbytes)
{
- writesl(master->regs + TX_FIFO, bytes, nbytes / 4);
- if (nbytes & 3) {
- u32 tmp = 0;
-
- memcpy(&tmp, bytes + (nbytes & ~3), nbytes & 3);
- writesl(master->regs + TX_FIFO, &tmp, 1);
- }
+ i3c_writel_fifo(master->regs + TX_FIFO, bytes, nbytes);
}
static void cdns_i3c_master_rd_from_rx_fifo(struct cdns_i3c_master *master,
u8 *bytes, int nbytes)
{
- readsl(master->regs + RX_FIFO, bytes, nbytes / 4);
- if (nbytes & 3) {
- u32 tmp;
-
- readsl(master->regs + RX_FIFO, &tmp, 1);
- memcpy(bytes + (nbytes & ~3), &tmp, nbytes & 3);
- }
+ i3c_readl_fifo(master->regs + RX_FIFO, bytes, nbytes);
}
static bool cdns_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m,
@@ -742,7 +731,7 @@ static int cdns_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
for (i = 0; i < nxfers; i++) {
if (xfers[i].len > CMD0_FIFO_PL_LEN_MAX)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
}
if (!nxfers)
@@ -750,7 +739,7 @@ static int cdns_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
if (nxfers > master->caps.cmdfifodepth ||
nxfers > master->caps.cmdrfifodepth)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
/*
* First make sure that all transactions (block of transfers separated
@@ -765,7 +754,7 @@ static int cdns_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
if (rxslots > master->caps.rxfifodepth ||
txslots > master->caps.txfifodepth)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
cdns_xfer = cdns_i3c_master_alloc_xfer(master, nxfers);
if (!cdns_xfer)
@@ -822,11 +811,11 @@ static int cdns_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
int i, ret = 0;
if (nxfers > master->caps.cmdfifodepth)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
for (i = 0; i < nxfers; i++) {
if (xfers[i].len > CMD0_FIFO_PL_LEN_MAX)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
if (xfers[i].flags & I2C_M_RD)
nrxwords += DIV_ROUND_UP(xfers[i].len, 4);
@@ -836,7 +825,7 @@ static int cdns_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
if (ntxwords > master->caps.txfifodepth ||
nrxwords > master->caps.rxfifodepth)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
xfer = cdns_i3c_master_alloc_xfer(master, nxfers);
if (!xfer)
@@ -863,7 +852,7 @@ static int cdns_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
}
cdns_i3c_master_queue_xfer(master, xfer);
- if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
+ if (!wait_for_completion_timeout(&xfer->comp, m->i2c.timeout))
cdns_i3c_master_unqueue_xfer(master, xfer);
ret = xfer->ret;
@@ -1330,12 +1319,7 @@ static void cdns_i3c_master_handle_ibi(struct cdns_i3c_master *master,
buf = slot->data;
nbytes = IBIR_XFER_BYTES(ibir);
- readsl(master->regs + IBI_DATA_FIFO, buf, nbytes / 4);
- if (nbytes % 3) {
- u32 tmp = __raw_readl(master->regs + IBI_DATA_FIFO);
-
- memcpy(buf + (nbytes & ~3), &tmp, nbytes & 3);
- }
+ i3c_readl_fifo(master->regs + IBI_DATA_FIFO, buf, nbytes);
slot->len = min_t(unsigned int, IBIR_XFER_BYTES(ibir),
dev->ibi->max_payload_len);
@@ -1566,6 +1550,7 @@ MODULE_DEVICE_TABLE(of, cdns_i3c_master_of_ids);
static int cdns_i3c_master_probe(struct platform_device *pdev)
{
struct cdns_i3c_master *master;
+ struct clk *pclk;
int ret, irq;
u32 val;
@@ -1581,11 +1566,11 @@ static int cdns_i3c_master_probe(struct platform_device *pdev)
if (IS_ERR(master->regs))
return PTR_ERR(master->regs);
- master->pclk = devm_clk_get(&pdev->dev, "pclk");
- if (IS_ERR(master->pclk))
- return PTR_ERR(master->pclk);
+ pclk = devm_clk_get_enabled(&pdev->dev, "pclk");
+ if (IS_ERR(pclk))
+ return PTR_ERR(pclk);
- master->sysclk = devm_clk_get(&pdev->dev, "sysclk");
+ master->sysclk = devm_clk_get_enabled(&pdev->dev, "sysclk");
if (IS_ERR(master->sysclk))
return PTR_ERR(master->sysclk);
@@ -1593,18 +1578,8 @@ static int cdns_i3c_master_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- ret = clk_prepare_enable(master->pclk);
- if (ret)
- return ret;
-
- ret = clk_prepare_enable(master->sysclk);
- if (ret)
- goto err_disable_pclk;
-
- if (readl(master->regs + DEV_ID) != DEV_ID_I3C_MASTER) {
- ret = -EINVAL;
- goto err_disable_sysclk;
- }
+ if (readl(master->regs + DEV_ID) != DEV_ID_I3C_MASTER)
+ return -EINVAL;
spin_lock_init(&master->xferqueue.lock);
INIT_LIST_HEAD(&master->xferqueue.list);
@@ -1615,7 +1590,7 @@ static int cdns_i3c_master_probe(struct platform_device *pdev)
ret = devm_request_irq(&pdev->dev, irq, cdns_i3c_master_interrupt, 0,
dev_name(&pdev->dev), master);
if (ret)
- goto err_disable_sysclk;
+ return ret;
platform_set_drvdata(pdev, master);
@@ -1637,29 +1612,15 @@ static int cdns_i3c_master_probe(struct platform_device *pdev)
master->ibi.slots = devm_kcalloc(&pdev->dev, master->ibi.num_slots,
sizeof(*master->ibi.slots),
GFP_KERNEL);
- if (!master->ibi.slots) {
- ret = -ENOMEM;
- goto err_disable_sysclk;
- }
+ if (!master->ibi.slots)
+ return -ENOMEM;
writel(IBIR_THR(1), master->regs + CMD_IBI_THR_CTRL);
writel(MST_INT_IBIR_THR, master->regs + MST_IER);
writel(DEVS_CTRL_DEV_CLR_ALL, master->regs + DEVS_CTRL);
- ret = i3c_master_register(&master->base, &pdev->dev,
- &cdns_i3c_master_ops, false);
- if (ret)
- goto err_disable_sysclk;
-
- return 0;
-
-err_disable_sysclk:
- clk_disable_unprepare(master->sysclk);
-
-err_disable_pclk:
- clk_disable_unprepare(master->pclk);
-
- return ret;
+ return i3c_master_register(&master->base, &pdev->dev,
+ &cdns_i3c_master_ops, false);
}
static void cdns_i3c_master_remove(struct platform_device *pdev)
@@ -1668,9 +1629,6 @@ static void cdns_i3c_master_remove(struct platform_device *pdev)
cancel_work_sync(&master->hj_work);
i3c_master_unregister(&master->base);
-
- clk_disable_unprepare(master->sysclk);
- clk_disable_unprepare(master->pclk);
}
static struct platform_driver cdns_i3c_master = {
diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
index bc4538694540..60f1175f1f37 100644
--- a/drivers/i3c/master/mipi-i3c-hci/core.c
+++ b/drivers/i3c/master/mipi-i3c-hci/core.c
@@ -395,7 +395,7 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
ret = hci->io->queue_xfer(hci, xfer, nxfers);
if (ret)
goto out;
- if (!wait_for_completion_timeout(&done, HZ) &&
+ if (!wait_for_completion_timeout(&done, m->i2c.timeout) &&
hci->io->dequeue_xfer(hci, xfer, nxfers)) {
ret = -ETIME;
goto out;
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
new file mode 100644
index 000000000000..174d3dc5d276
--- /dev/null
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -0,0 +1,1404 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas I3C Controller driver
+ * Copyright (C) 2023-25 Renesas Electronics Corp.
+ *
+ * TODO: IBI support, HotJoin support, Target support
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/i3c/master.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/iopoll.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include "../internals.h"
+
+#define PRTS 0x00
+#define PRTS_PRTMD BIT(0)
+
+#define BCTL 0x14
+#define BCTL_INCBA BIT(0)
+#define BCTL_HJACKCTL BIT(8)
+#define BCTL_ABT BIT(29)
+#define BCTL_BUSE BIT(31)
+
+#define MSDVAD 0x18
+#define MSDVAD_MDYAD(x) FIELD_PREP(GENMASK(21, 16), x)
+#define MSDVAD_MDYADV BIT(31)
+
+#define RSTCTL 0x20
+#define RSTCTL_RI3CRST BIT(0)
+#define RSTCTL_INTLRST BIT(16)
+
+#define INST 0x30
+
+#define IBINCTL 0x58
+#define IBINCTL_NRHJCTL BIT(0)
+#define IBINCTL_NRMRCTL BIT(1)
+#define IBINCTL_NRSIRCTL BIT(3)
+
+#define SVCTL 0x64
+
+#define REFCKCTL 0x70
+#define REFCKCTL_IREFCKS(x) FIELD_PREP(GENMASK(2, 0), x)
+
+#define STDBR 0x74
+#define STDBR_SBRLO(cond, x) FIELD_PREP(GENMASK(7, 0), (x) >> (cond))
+#define STDBR_SBRHO(cond, x) FIELD_PREP(GENMASK(15, 8), (x) >> (cond))
+#define STDBR_SBRLP(x) FIELD_PREP(GENMASK(21, 16), x)
+#define STDBR_SBRHP(x) FIELD_PREP(GENMASK(29, 24), x)
+#define STDBR_DSBRPO BIT(31)
+
+#define EXTBR 0x78
+#define EXTBR_EBRLO(x) FIELD_PREP(GENMASK(7, 0), x)
+#define EXTBR_EBRHO(x) FIELD_PREP(GENMASK(15, 8), x)
+#define EXTBR_EBRLP(x) FIELD_PREP(GENMASK(21, 16), x)
+#define EXTBR_EBRHP(x) FIELD_PREP(GENMASK(29, 24), x)
+
+#define BFRECDT 0x7c
+#define BFRECDT_FRECYC(x) FIELD_PREP(GENMASK(8, 0), x)
+
+#define BAVLCDT 0x80
+#define BAVLCDT_AVLCYC(x) FIELD_PREP(GENMASK(8, 0), x)
+
+#define BIDLCDT 0x84
+#define BIDLCDT_IDLCYC(x) FIELD_PREP(GENMASK(17, 0), x)
+
+#define ACKCTL 0xa0
+#define ACKCTL_ACKT BIT(1)
+#define ACKCTL_ACKTWP BIT(2)
+
+#define SCSTRCTL 0xa4
+#define SCSTRCTL_ACKTWE BIT(0)
+#define SCSTRCTL_RWE BIT(1)
+
+#define SCSTLCTL 0xb0
+
+#define CNDCTL 0x140
+#define CNDCTL_STCND BIT(0)
+#define CNDCTL_SRCND BIT(1)
+#define CNDCTL_SPCND BIT(2)
+
+#define NCMDQP 0x150 /* Normal Command Queue */
+#define NCMDQP_CMD_ATTR(x) FIELD_PREP(GENMASK(2, 0), x)
+#define NCMDQP_IMMED_XFER 0x01
+#define NCMDQP_ADDR_ASSGN 0x02
+#define NCMDQP_TID(x) FIELD_PREP(GENMASK(6, 3), x)
+#define NCMDQP_CMD(x) FIELD_PREP(GENMASK(14, 7), x)
+#define NCMDQP_CP BIT(15)
+#define NCMDQP_DEV_INDEX(x) FIELD_PREP(GENMASK(20, 16), x)
+#define NCMDQP_BYTE_CNT(x) FIELD_PREP(GENMASK(25, 23), x)
+#define NCMDQP_DEV_COUNT(x) FIELD_PREP(GENMASK(29, 26), x)
+#define NCMDQP_MODE(x) FIELD_PREP(GENMASK(28, 26), x)
+#define NCMDQP_RNW(x) FIELD_PREP(GENMASK(29, 29), x)
+#define NCMDQP_ROC BIT(30)
+#define NCMDQP_TOC BIT(31)
+#define NCMDQP_DATA_LENGTH(x) FIELD_PREP(GENMASK(31, 16), x)
+
+#define NRSPQP 0x154 /* Normal Respone Queue */
+#define NRSPQP_NO_ERROR 0
+#define NRSPQP_ERROR_CRC 1
+#define NRSPQP_ERROR_PARITY 2
+#define NRSPQP_ERROR_FRAME 3
+#define NRSPQP_ERROR_IBA_NACK 4
+#define NRSPQP_ERROR_ADDRESS_NACK 5
+#define NRSPQP_ERROR_OVER_UNDER_FLOW 6
+#define NRSPQP_ERROR_TRANSF_ABORT 8
+#define NRSPQP_ERROR_I2C_W_NACK_ERR 9
+#define NRSPQP_ERROR_UNSUPPORTED 10
+#define NRSPQP_DATA_LEN(x) FIELD_GET(GENMASK(15, 0), x)
+#define NRSPQP_ERR_STATUS(x) FIELD_GET(GENMASK(31, 28), x)
+
+#define NTDTBP0 0x158 /* Normal Transfer Data Buffer */
+#define NTDTBP0_DEPTH 16
+
+#define NQTHCTL 0x190
+#define NQTHCTL_CMDQTH(x) FIELD_PREP(GENMASK(1, 0), x)
+#define NQTHCTL_IBIDSSZ(x) FIELD_PREP(GENMASK(23, 16), x)
+
+#define NTBTHCTL0 0x194
+
+#define NRQTHCTL 0x1c0
+
+#define BST 0x1d0
+#define BST_STCNDDF BIT(0)
+#define BST_SPCNDDF BIT(1)
+#define BST_NACKDF BIT(4)
+#define BST_TENDF BIT(8)
+
+#define BSTE 0x1d4
+#define BSTE_STCNDDE BIT(0)
+#define BSTE_SPCNDDE BIT(1)
+#define BSTE_NACKDE BIT(4)
+#define BSTE_TENDE BIT(8)
+#define BSTE_ALE BIT(16)
+#define BSTE_TODE BIT(20)
+#define BSTE_WUCNDDE BIT(24)
+#define BSTE_ALL_FLAG (BSTE_STCNDDE | BSTE_SPCNDDE |\
+ BSTE_NACKDE | BSTE_TENDE |\
+ BSTE_ALE | BSTE_TODE | BSTE_WUCNDDE)
+
+#define BIE 0x1d8
+#define BIE_STCNDDIE BIT(0)
+#define BIE_SPCNDDIE BIT(1)
+#define BIE_NACKDIE BIT(4)
+#define BIE_TENDIE BIT(8)
+
+#define NTST 0x1e0
+#define NTST_TDBEF0 BIT(0)
+#define NTST_RDBFF0 BIT(1)
+#define NTST_CMDQEF BIT(3)
+#define NTST_RSPQFF BIT(4)
+#define NTST_TABTF BIT(5)
+#define NTST_TEF BIT(9)
+
+#define NTSTE 0x1e4
+#define NTSTE_TDBEE0 BIT(0)
+#define NTSTE_RDBFE0 BIT(1)
+#define NTSTE_IBIQEFE BIT(2)
+#define NTSTE_CMDQEE BIT(3)
+#define NTSTE_RSPQFE BIT(4)
+#define NTSTE_TABTE BIT(5)
+#define NTSTE_TEE BIT(9)
+#define NTSTE_RSQFE BIT(20)
+#define NTSTE_ALL_FLAG (NTSTE_TDBEE0 | NTSTE_RDBFE0 |\
+ NTSTE_IBIQEFE | NTSTE_CMDQEE |\
+ NTSTE_RSPQFE | NTSTE_TABTE |\
+ NTSTE_TEE | NTSTE_RSQFE)
+
+#define NTIE 0x1e8
+#define NTIE_TDBEIE0 BIT(0)
+#define NTIE_RDBFIE0 BIT(1)
+#define NTIE_IBIQEFIE BIT(2)
+#define NTIE_RSPQFIE BIT(4)
+#define NTIE_RSQFIE BIT(20)
+
+#define BCST 0x210
+#define BCST_BFREF BIT(0)
+
+#define DATBAS(x) (0x224 + 0x8 * (x))
+#define DATBAS_DVSTAD(x) FIELD_PREP(GENMASK(6, 0), x)
+#define DATBAS_DVDYAD(x) FIELD_PREP(GENMASK(23, 16), x)
+
+#define NDBSTLV0 0x398
+#define NDBSTLV0_RDBLV(x) FIELD_GET(GENMASK(15, 8), x)
+
+#define RENESAS_I3C_MAX_DEVS 8
+#define I2C_INIT_MSG -1
+
+enum i3c_internal_state {
+ I3C_INTERNAL_STATE_DISABLED,
+ I3C_INTERNAL_STATE_CONTROLLER_IDLE,
+ I3C_INTERNAL_STATE_CONTROLLER_ENTDAA,
+ I3C_INTERNAL_STATE_CONTROLLER_SETDASA,
+ I3C_INTERNAL_STATE_CONTROLLER_WRITE,
+ I3C_INTERNAL_STATE_CONTROLLER_READ,
+ I3C_INTERNAL_STATE_CONTROLLER_COMMAND_WRITE,
+ I3C_INTERNAL_STATE_CONTROLLER_COMMAND_READ,
+};
+
+enum renesas_i3c_event {
+ I3C_COMMAND_ADDRESS_ASSIGNMENT,
+ I3C_WRITE,
+ I3C_READ,
+ I3C_COMMAND_WRITE,
+ I3C_COMMAND_READ,
+};
+
+struct renesas_i3c_cmd {
+ u32 cmd0;
+ u32 len;
+ const void *tx_buf;
+ u32 tx_count;
+ void *rx_buf;
+ u32 rx_count;
+ u32 err;
+ u8 rnw;
+ /* i2c xfer */
+ int i2c_bytes_left;
+ int i2c_is_last;
+ u8 *i2c_buf;
+ const struct i2c_msg *msg;
+};
+
+struct renesas_i3c_xfer {
+ struct list_head node;
+ struct completion comp;
+ int ret;
+ bool is_i2c_xfer;
+ unsigned int ncmds;
+ struct renesas_i3c_cmd cmds[] __counted_by(ncmds);
+};
+
+struct renesas_i3c_xferqueue {
+ struct list_head list;
+ struct renesas_i3c_xfer *cur;
+ /* Lock for accessing the xfer queue */
+ spinlock_t lock;
+};
+
+struct renesas_i3c {
+ struct i3c_master_controller base;
+ enum i3c_internal_state internal_state;
+ u16 maxdevs;
+ u32 free_pos;
+ u32 i2c_STDBR;
+ u32 i3c_STDBR;
+ u8 addrs[RENESAS_I3C_MAX_DEVS];
+ struct renesas_i3c_xferqueue xferqueue;
+ void __iomem *regs;
+ struct clk *tclk;
+};
+
+struct renesas_i3c_i2c_dev_data {
+ u8 index;
+};
+
+struct renesas_i3c_irq_desc {
+ const char *name;
+ irq_handler_t isr;
+ const char *desc;
+};
+
+struct renesas_i3c_config {
+ unsigned int has_pclkrw:1;
+};
+
+static inline void renesas_i3c_reg_update(void __iomem *reg, u32 mask, u32 val)
+{
+ u32 data = readl(reg);
+
+ data &= ~mask;
+ data |= (val & mask);
+ writel(data, reg);
+}
+
+static inline u32 renesas_readl(void __iomem *base, u32 reg)
+{
+ return readl(base + reg);
+}
+
+static inline void renesas_writel(void __iomem *base, u32 reg, u32 val)
+{
+ writel(val, base + reg);
+}
+
+static void renesas_set_bit(void __iomem *base, u32 reg, u32 val)
+{
+ renesas_i3c_reg_update(base + reg, val, val);
+}
+
+static void renesas_clear_bit(void __iomem *base, u32 reg, u32 val)
+{
+ renesas_i3c_reg_update(base + reg, val, 0);
+}
+
+static inline struct renesas_i3c *to_renesas_i3c(struct i3c_master_controller *m)
+{
+ return container_of(m, struct renesas_i3c, base);
+}
+
+static inline u32 datbas_dvdyad_with_parity(u8 addr)
+{
+ return DATBAS_DVDYAD(addr | (parity8(addr) ? 0 : BIT(7)));
+}
+
+static int renesas_i3c_get_free_pos(struct renesas_i3c *i3c)
+{
+ if (!(i3c->free_pos & GENMASK(i3c->maxdevs - 1, 0)))
+ return -ENOSPC;
+
+ return ffs(i3c->free_pos) - 1;
+}
+
+static int renesas_i3c_get_addr_pos(struct renesas_i3c *i3c, u8 addr)
+{
+ int pos;
+
+ for (pos = 0; pos < i3c->maxdevs; pos++) {
+ if (addr == i3c->addrs[pos])
+ return pos;
+ }
+
+ return -EINVAL;
+}
+
+static struct renesas_i3c_xfer *renesas_i3c_alloc_xfer(struct renesas_i3c *i3c,
+ unsigned int ncmds)
+{
+ struct renesas_i3c_xfer *xfer;
+
+ xfer = kzalloc(struct_size(xfer, cmds, ncmds), GFP_KERNEL);
+ if (!xfer)
+ return NULL;
+
+ INIT_LIST_HEAD(&xfer->node);
+ xfer->ncmds = ncmds;
+ xfer->ret = -ETIMEDOUT;
+
+ return xfer;
+}
+
+static void renesas_i3c_start_xfer_locked(struct renesas_i3c *i3c)
+{
+ struct renesas_i3c_xfer *xfer = i3c->xferqueue.cur;
+ struct renesas_i3c_cmd *cmd;
+ u32 cmd1;
+
+ if (!xfer)
+ return;
+
+ cmd = xfer->cmds;
+
+ switch (i3c->internal_state) {
+ case I3C_INTERNAL_STATE_CONTROLLER_ENTDAA:
+ case I3C_INTERNAL_STATE_CONTROLLER_SETDASA:
+ renesas_set_bit(i3c->regs, NTIE, NTIE_RSPQFIE);
+ renesas_writel(i3c->regs, NCMDQP, cmd->cmd0);
+ renesas_writel(i3c->regs, NCMDQP, 0);
+ break;
+ case I3C_INTERNAL_STATE_CONTROLLER_WRITE:
+ case I3C_INTERNAL_STATE_CONTROLLER_COMMAND_WRITE:
+ renesas_set_bit(i3c->regs, NTIE, NTIE_RSPQFIE);
+ if (cmd->len <= 4) {
+ cmd->cmd0 |= NCMDQP_CMD_ATTR(NCMDQP_IMMED_XFER);
+ cmd->cmd0 |= NCMDQP_BYTE_CNT(cmd->len);
+ cmd->tx_count = cmd->len;
+ cmd1 = cmd->len == 0 ? 0 : *(u32 *)cmd->tx_buf;
+ } else {
+ cmd1 = NCMDQP_DATA_LENGTH(cmd->len);
+ }
+ renesas_writel(i3c->regs, NCMDQP, cmd->cmd0);
+ renesas_writel(i3c->regs, NCMDQP, cmd1);
+ break;
+ case I3C_INTERNAL_STATE_CONTROLLER_READ:
+ case I3C_INTERNAL_STATE_CONTROLLER_COMMAND_READ:
+ renesas_set_bit(i3c->regs, NTIE, NTIE_RDBFIE0);
+ cmd1 = NCMDQP_DATA_LENGTH(cmd->len);
+ renesas_writel(i3c->regs, NCMDQP, cmd->cmd0);
+ renesas_writel(i3c->regs, NCMDQP, cmd1);
+ break;
+ default:
+ break;
+ }
+
+ /* Clear the command queue empty flag */
+ renesas_clear_bit(i3c->regs, NTST, NTST_CMDQEF);
+}
+
+static void renesas_i3c_dequeue_xfer_locked(struct renesas_i3c *i3c,
+ struct renesas_i3c_xfer *xfer)
+{
+ if (i3c->xferqueue.cur == xfer)
+ i3c->xferqueue.cur = NULL;
+ else
+ list_del_init(&xfer->node);
+}
+
+static void renesas_i3c_dequeue_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xfer *xfer)
+{
+ scoped_guard(spinlock_irqsave, &i3c->xferqueue.lock)
+ renesas_i3c_dequeue_xfer_locked(i3c, xfer);
+}
+
+static void renesas_i3c_enqueue_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xfer *xfer)
+{
+ reinit_completion(&xfer->comp);
+ scoped_guard(spinlock_irqsave, &i3c->xferqueue.lock) {
+ if (i3c->xferqueue.cur) {
+ list_add_tail(&xfer->node, &i3c->xferqueue.list);
+ } else {
+ i3c->xferqueue.cur = xfer;
+ if (!xfer->is_i2c_xfer)
+ renesas_i3c_start_xfer_locked(i3c);
+ }
+ }
+}
+
+static void renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xfer *xfer)
+{
+ unsigned long time_left;
+
+ renesas_i3c_enqueue_xfer(i3c, xfer);
+
+ time_left = wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000));
+ if (!time_left)
+ renesas_i3c_dequeue_xfer(i3c, xfer);
+}
+
+static void renesas_i3c_set_prts(struct renesas_i3c *i3c, u32 val)
+{
+ /* Required sequence according to tnrza0140ae */
+ renesas_set_bit(i3c->regs, RSTCTL, RSTCTL_INTLRST);
+ renesas_writel(i3c->regs, PRTS, val);
+ renesas_clear_bit(i3c->regs, RSTCTL, RSTCTL_INTLRST);
+}
+
+static void renesas_i3c_bus_enable(struct i3c_master_controller *m, bool i3c_mode)
+{
+ struct renesas_i3c *i3c = to_renesas_i3c(m);
+
+ /* Setup either I3C or I2C protocol */
+ if (i3c_mode) {
+ renesas_i3c_set_prts(i3c, 0);
+ /* Revisit: INCBA handling, especially after I2C transfers */
+ renesas_set_bit(i3c->regs, BCTL, BCTL_HJACKCTL | BCTL_INCBA);
+ renesas_set_bit(i3c->regs, MSDVAD, MSDVAD_MDYADV);
+ renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
+ } else {
+ renesas_i3c_set_prts(i3c, PRTS_PRTMD);
+ renesas_writel(i3c->regs, STDBR, i3c->i2c_STDBR);
+ }
+
+ /* Enable I3C bus */
+ renesas_set_bit(i3c->regs, BCTL, BCTL_BUSE);
+}
+
+static int renesas_i3c_reset(struct renesas_i3c *i3c)
+{
+ u32 val;
+
+ renesas_writel(i3c->regs, BCTL, 0);
+ renesas_set_bit(i3c->regs, RSTCTL, RSTCTL_RI3CRST);
+
+ return read_poll_timeout(renesas_readl, val, !(val & RSTCTL_RI3CRST),
+ 0, 1000, false, i3c->regs, RSTCTL);
+}
+
+static int renesas_i3c_bus_init(struct i3c_master_controller *m)
+{
+ struct renesas_i3c *i3c = to_renesas_i3c(m);
+ struct i3c_bus *bus = i3c_master_get_bus(m);
+ struct i3c_device_info info = {};
+ struct i2c_timings t;
+ unsigned long rate;
+ u32 double_SBR, val;
+ int cks, pp_high_ticks, pp_low_ticks, i3c_total_ticks;
+ int od_high_ticks, od_low_ticks, i2c_total_ticks;
+ int ret;
+
+ rate = clk_get_rate(i3c->tclk);
+ if (!rate)
+ return -EINVAL;
+
+ ret = renesas_i3c_reset(i3c);
+ if (ret)
+ return ret;
+
+ i2c_total_ticks = DIV_ROUND_UP(rate, bus->scl_rate.i2c);
+ i3c_total_ticks = DIV_ROUND_UP(rate, bus->scl_rate.i3c);
+
+ i2c_parse_fw_timings(&m->dev, &t, true);
+
+ for (cks = 0; cks < 7; cks++) {
+ /* SCL low-period calculation in Open-drain mode */
+ od_low_ticks = ((i2c_total_ticks * 6) / 10);
+
+ /* SCL clock calculation in Push-Pull mode */
+ if (bus->mode == I3C_BUS_MODE_PURE)
+ pp_high_ticks = ((i3c_total_ticks * 5) / 10);
+ else
+ pp_high_ticks = DIV_ROUND_UP(I3C_BUS_THIGH_MIXED_MAX_NS,
+ NSEC_PER_SEC / rate);
+ pp_low_ticks = i3c_total_ticks - pp_high_ticks;
+
+ if ((od_low_ticks / 2) <= 0xFF && pp_low_ticks < 0x3F)
+ break;
+
+ i2c_total_ticks /= 2;
+ i3c_total_ticks /= 2;
+ rate /= 2;
+ }
+
+ /* SCL clock period calculation in Open-drain mode */
+ if ((od_low_ticks / 2) > 0xFF || pp_low_ticks > 0x3F) {
+ dev_err(&m->dev, "invalid speed (i2c-scl = %lu Hz, i3c-scl = %lu Hz). Too slow.\n",
+ (unsigned long)bus->scl_rate.i2c, (unsigned long)bus->scl_rate.i3c);
+ return -EINVAL;
+ }
+
+ /* SCL high-period calculation in Open-drain mode */
+ od_high_ticks = i2c_total_ticks - od_low_ticks;
+
+ /* Standard Bit Rate setting */
+ double_SBR = od_low_ticks > 0xFF ? 1 : 0;
+ i3c->i3c_STDBR = (double_SBR ? STDBR_DSBRPO : 0) |
+ STDBR_SBRLO(double_SBR, od_low_ticks) |
+ STDBR_SBRHO(double_SBR, od_high_ticks) |
+ STDBR_SBRLP(pp_low_ticks) |
+ STDBR_SBRHP(pp_high_ticks);
+
+ od_low_ticks -= t.scl_fall_ns / (NSEC_PER_SEC / rate) + 1;
+ od_high_ticks -= t.scl_rise_ns / (NSEC_PER_SEC / rate) + 1;
+ i3c->i2c_STDBR = (double_SBR ? STDBR_DSBRPO : 0) |
+ STDBR_SBRLO(double_SBR, od_low_ticks) |
+ STDBR_SBRHO(double_SBR, od_high_ticks) |
+ STDBR_SBRLP(pp_low_ticks) |
+ STDBR_SBRHP(pp_high_ticks);
+ renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
+
+ /* Extended Bit Rate setting */
+ renesas_writel(i3c->regs, EXTBR, EXTBR_EBRLO(od_low_ticks) |
+ EXTBR_EBRHO(od_high_ticks) |
+ EXTBR_EBRLP(pp_low_ticks) |
+ EXTBR_EBRHP(pp_high_ticks));
+
+ renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks));
+
+ /* Disable Slave Mode */
+ renesas_writel(i3c->regs, SVCTL, 0);
+
+ /* Initialize Queue/Buffer threshold */
+ renesas_writel(i3c->regs, NQTHCTL, NQTHCTL_IBIDSSZ(6) |
+ NQTHCTL_CMDQTH(1));
+
+ /* The only supported configuration is two entries*/
+ renesas_writel(i3c->regs, NTBTHCTL0, 0);
+ /* Interrupt when there is one entry in the queue */
+ renesas_writel(i3c->regs, NRQTHCTL, 0);
+
+ /* Enable all Bus/Transfer Status Flags */
+ renesas_writel(i3c->regs, BSTE, BSTE_ALL_FLAG);
+ renesas_writel(i3c->regs, NTSTE, NTSTE_ALL_FLAG);
+
+ /* Interrupt enable settings */
+ renesas_writel(i3c->regs, BIE, BIE_NACKDIE | BIE_TENDIE);
+ renesas_writel(i3c->regs, NTIE, 0);
+
+ /* Clear Status register */
+ renesas_writel(i3c->regs, NTST, 0);
+ renesas_writel(i3c->regs, INST, 0);
+ renesas_writel(i3c->regs, BST, 0);
+
+ /* Hot-Join Acknowlege setting. */
+ renesas_set_bit(i3c->regs, BCTL, BCTL_HJACKCTL);
+
+ renesas_writel(i3c->regs, IBINCTL, IBINCTL_NRHJCTL | IBINCTL_NRMRCTL |
+ IBINCTL_NRSIRCTL);
+
+ renesas_writel(i3c->regs, SCSTLCTL, 0);
+ renesas_set_bit(i3c->regs, SCSTRCTL, SCSTRCTL_ACKTWE);
+
+ /* Bus condition timing */
+ val = DIV_ROUND_UP(I3C_BUS_TBUF_MIXED_FM_MIN_NS, NSEC_PER_SEC / rate);
+ renesas_writel(i3c->regs, BFRECDT, BFRECDT_FRECYC(val));
+
+ val = DIV_ROUND_UP(I3C_BUS_TAVAL_MIN_NS, NSEC_PER_SEC / rate);
+ renesas_writel(i3c->regs, BAVLCDT, BAVLCDT_AVLCYC(val));
+
+ val = DIV_ROUND_UP(I3C_BUS_TIDLE_MIN_NS, NSEC_PER_SEC / rate);
+ renesas_writel(i3c->regs, BIDLCDT, BIDLCDT_IDLCYC(val));
+
+ ret = i3c_master_get_free_addr(m, 0);
+ if (ret < 0)
+ return ret;
+
+ renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYAD(ret) | MSDVAD_MDYADV);
+
+ memset(&info, 0, sizeof(info));
+ info.dyn_addr = ret;
+ return i3c_master_set_info(&i3c->base, &info);
+}
+
+static void renesas_i3c_bus_cleanup(struct i3c_master_controller *m)
+{
+ struct renesas_i3c *i3c = to_renesas_i3c(m);
+
+ renesas_i3c_reset(i3c);
+}
+
+static int renesas_i3c_daa(struct i3c_master_controller *m)
+{
+ struct renesas_i3c *i3c = to_renesas_i3c(m);
+ struct renesas_i3c_cmd *cmd;
+ u32 olddevs, newdevs;
+ u8 last_addr = 0, pos;
+ int ret;
+
+ struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1);
+ if (!xfer)
+ return -ENOMEM;
+
+ /* Enable I3C bus. */
+ renesas_i3c_bus_enable(m, true);
+
+ olddevs = ~(i3c->free_pos);
+ i3c->internal_state = I3C_INTERNAL_STATE_CONTROLLER_ENTDAA;
+
+ /* Setting DATBASn registers for target devices. */
+ for (pos = 0; pos < i3c->maxdevs; pos++) {
+ if (olddevs & BIT(pos))
+ continue;
+
+ ret = i3c_master_get_free_addr(m, last_addr + 1);
+ if (ret < 0)
+ return -ENOSPC;
+
+ i3c->addrs[pos] = ret;
+ last_addr = ret;
+
+ renesas_writel(i3c->regs, DATBAS(pos), datbas_dvdyad_with_parity(ret));
+ }
+
+ init_completion(&xfer->comp);
+ cmd = xfer->cmds;
+ cmd->rx_count = 0;
+
+ ret = renesas_i3c_get_free_pos(i3c);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Setup the command descriptor to start the ENTDAA command
+ * and starting at the selected device index.
+ */
+ cmd->cmd0 = NCMDQP_CMD_ATTR(NCMDQP_ADDR_ASSGN) | NCMDQP_ROC |
+ NCMDQP_TID(I3C_COMMAND_ADDRESS_ASSIGNMENT) |
+ NCMDQP_CMD(I3C_CCC_ENTDAA) | NCMDQP_DEV_INDEX(ret) |
+ NCMDQP_DEV_COUNT(i3c->maxdevs - ret) | NCMDQP_TOC;
+
+ renesas_i3c_wait_xfer(i3c, xfer);
+
+ newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, 0);
+ newdevs &= ~olddevs;
+
+ for (pos = 0; pos < i3c->maxdevs; pos++) {
+ if (newdevs & BIT(pos))
+ i3c_master_add_i3c_dev_locked(m, i3c->addrs[pos]);
+ }
+
+ return ret < 0 ? ret : 0;
+}
+
+static bool renesas_i3c_supports_ccc_cmd(struct i3c_master_controller *m,
+ const struct i3c_ccc_cmd *cmd)
+{
+ if (cmd->ndests > 1)
+ return false;
+
+ switch (cmd->id) {
+ case I3C_CCC_ENEC(true):
+ case I3C_CCC_ENEC(false):
+ case I3C_CCC_DISEC(true):
+ case I3C_CCC_DISEC(false):
+ case I3C_CCC_ENTAS(0, true):
+ case I3C_CCC_ENTAS(1, true):
+ case I3C_CCC_ENTAS(2, true):
+ case I3C_CCC_ENTAS(3, true):
+ case I3C_CCC_ENTAS(0, false):
+ case I3C_CCC_ENTAS(1, false):
+ case I3C_CCC_ENTAS(2, false):
+ case I3C_CCC_ENTAS(3, false):
+ case I3C_CCC_RSTDAA(true):
+ case I3C_CCC_RSTDAA(false):
+ case I3C_CCC_ENTDAA:
+ case I3C_CCC_DEFSLVS:
+ case I3C_CCC_SETMWL(true):
+ case I3C_CCC_SETMWL(false):
+ case I3C_CCC_SETMRL(true):
+ case I3C_CCC_SETMRL(false):
+ case I3C_CCC_ENTTM:
+ case I3C_CCC_SETDASA:
+ case I3C_CCC_SETNEWDA:
+ case I3C_CCC_GETMWL:
+ case I3C_CCC_GETMRL:
+ case I3C_CCC_GETPID:
+ case I3C_CCC_GETBCR:
+ case I3C_CCC_GETDCR:
+ case I3C_CCC_GETSTATUS:
+ case I3C_CCC_GETACCMST:
+ case I3C_CCC_GETMXDS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m,
+ struct i3c_ccc_cmd *ccc)
+{
+ struct renesas_i3c *i3c = to_renesas_i3c(m);
+ struct renesas_i3c_xfer *xfer;
+ struct renesas_i3c_cmd *cmd;
+ int ret, pos = 0;
+
+ if (ccc->id & I3C_CCC_DIRECT) {
+ pos = renesas_i3c_get_addr_pos(i3c, ccc->dests[0].addr);
+ if (pos < 0)
+ return pos;
+ }
+
+ xfer = renesas_i3c_alloc_xfer(i3c, 1);
+ if (!xfer)
+ return -ENOMEM;
+
+ renesas_i3c_bus_enable(m, true);
+
+ init_completion(&xfer->comp);
+ cmd = xfer->cmds;
+ cmd->rnw = ccc->rnw;
+ cmd->cmd0 = 0;
+
+ /* Calculate the command descriptor. */
+ switch (ccc->id) {
+ case I3C_CCC_SETDASA:
+ renesas_writel(i3c->regs, DATBAS(pos),
+ DATBAS_DVSTAD(ccc->dests[0].addr) |
+ DATBAS_DVDYAD(*(u8 *)ccc->dests[0].payload.data >> 1));
+ cmd->cmd0 = NCMDQP_CMD_ATTR(NCMDQP_ADDR_ASSGN) | NCMDQP_ROC |
+ NCMDQP_TID(I3C_COMMAND_ADDRESS_ASSIGNMENT) |
+ NCMDQP_CMD(I3C_CCC_SETDASA) | NCMDQP_DEV_INDEX(pos) |
+ NCMDQP_DEV_COUNT(0) | NCMDQP_TOC;
+ i3c->internal_state = I3C_INTERNAL_STATE_CONTROLLER_SETDASA;
+ break;
+ default:
+ /* Calculate the command descriptor. */
+ cmd->cmd0 = NCMDQP_TID(I3C_COMMAND_WRITE) | NCMDQP_MODE(0) |
+ NCMDQP_RNW(ccc->rnw) | NCMDQP_CMD(ccc->id) |
+ NCMDQP_ROC | NCMDQP_TOC | NCMDQP_CP |
+ NCMDQP_DEV_INDEX(pos);
+
+ if (ccc->rnw) {
+ cmd->rx_buf = ccc->dests[0].payload.data;
+ cmd->len = ccc->dests[0].payload.len;
+ cmd->rx_count = 0;
+ i3c->internal_state = I3C_INTERNAL_STATE_CONTROLLER_COMMAND_READ;
+ } else {
+ cmd->tx_buf = ccc->dests[0].payload.data;
+ cmd->len = ccc->dests[0].payload.len;
+ cmd->tx_count = 0;
+ i3c->internal_state = I3C_INTERNAL_STATE_CONTROLLER_COMMAND_WRITE;
+ }
+ }
+
+ renesas_i3c_wait_xfer(i3c, xfer);
+
+ ret = xfer->ret;
+ if (ret)
+ ccc->err = I3C_ERROR_M2;
+
+ kfree(xfer);
+
+ return ret;
+}
+
+static int renesas_i3c_priv_xfers(struct i3c_dev_desc *dev, struct i3c_priv_xfer *i3c_xfers,
+ int i3c_nxfers)
+{
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct renesas_i3c *i3c = to_renesas_i3c(m);
+ struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+ struct renesas_i3c_xfer *xfer;
+ int i;
+
+ /* Enable I3C bus. */
+ renesas_i3c_bus_enable(m, true);
+
+ xfer = renesas_i3c_alloc_xfer(i3c, 1);
+ if (!xfer)
+ return -ENOMEM;
+
+ init_completion(&xfer->comp);
+
+ for (i = 0; i < i3c_nxfers; i++) {
+ struct renesas_i3c_cmd *cmd = xfer->cmds;
+
+ /* Calculate the Transfer Command Descriptor */
+ cmd->rnw = i3c_xfers[i].rnw;
+ cmd->cmd0 = NCMDQP_DEV_INDEX(data->index) | NCMDQP_MODE(0) |
+ NCMDQP_RNW(cmd->rnw) | NCMDQP_ROC | NCMDQP_TOC;
+
+ if (i3c_xfers[i].rnw) {
+ cmd->rx_count = 0;
+ cmd->cmd0 |= NCMDQP_TID(I3C_READ);
+ cmd->rx_buf = i3c_xfers[i].data.in;
+ cmd->len = i3c_xfers[i].len;
+ i3c->internal_state = I3C_INTERNAL_STATE_CONTROLLER_READ;
+ } else {
+ cmd->tx_count = 0;
+ cmd->cmd0 |= NCMDQP_TID(I3C_WRITE);
+ cmd->tx_buf = i3c_xfers[i].data.out;
+ cmd->len = i3c_xfers[i].len;
+ i3c->internal_state = I3C_INTERNAL_STATE_CONTROLLER_WRITE;
+ }
+
+ if (!i3c_xfers[i].rnw && i3c_xfers[i].len > 4) {
+ i3c_writel_fifo(i3c->regs + NTDTBP0, cmd->tx_buf, cmd->len);
+ if (cmd->len > NTDTBP0_DEPTH * sizeof(u32))
+ renesas_set_bit(i3c->regs, NTIE, NTIE_TDBEIE0);
+ }
+
+ renesas_i3c_wait_xfer(i3c, xfer);
+ }
+
+ return 0;
+}
+
+static int renesas_i3c_attach_i3c_dev(struct i3c_dev_desc *dev)
+{
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct renesas_i3c *i3c = to_renesas_i3c(m);
+ struct renesas_i3c_i2c_dev_data *data;
+ int pos;
+
+ pos = renesas_i3c_get_free_pos(i3c);
+ if (pos < 0)
+ return pos;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->index = pos;
+ i3c->addrs[pos] = dev->info.dyn_addr ? : dev->info.static_addr;
+ i3c->free_pos &= ~BIT(pos);
+
+ renesas_writel(i3c->regs, DATBAS(pos), DATBAS_DVSTAD(dev->info.static_addr) |
+ datbas_dvdyad_with_parity(i3c->addrs[pos]));
+ i3c_dev_set_master_data(dev, data);
+
+ return 0;
+}
+
+static int renesas_i3c_reattach_i3c_dev(struct i3c_dev_desc *dev,
+ u8 old_dyn_addr)
+{
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct renesas_i3c *i3c = to_renesas_i3c(m);
+ struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+
+ i3c->addrs[data->index] = dev->info.dyn_addr ? dev->info.dyn_addr :
+ dev->info.static_addr;
+
+ return 0;
+}
+
+static void renesas_i3c_detach_i3c_dev(struct i3c_dev_desc *dev)
+{
+ struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct renesas_i3c *i3c = to_renesas_i3c(m);
+
+ i3c_dev_set_master_data(dev, NULL);
+ i3c->addrs[data->index] = 0;
+ i3c->free_pos |= BIT(data->index);
+ kfree(data);
+}
+
+static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev,
+ struct i2c_msg *i2c_xfers,
+ int i2c_nxfers)
+{
+ struct i3c_master_controller *m = i2c_dev_get_master(dev);
+ struct renesas_i3c *i3c = to_renesas_i3c(m);
+ struct renesas_i3c_cmd *cmd;
+ u8 start_bit = CNDCTL_STCND;
+ int i;
+
+ struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1);
+ if (!xfer)
+ return -ENOMEM;
+
+ if (!i2c_nxfers)
+ return 0;
+
+ renesas_i3c_bus_enable(m, false);
+
+ init_completion(&xfer->comp);
+ xfer->is_i2c_xfer = true;
+ cmd = xfer->cmds;
+
+ if (!(renesas_readl(i3c->regs, BCST) & BCST_BFREF)) {
+ cmd->err = -EBUSY;
+ return cmd->err;
+ }
+
+ renesas_writel(i3c->regs, BST, 0);
+
+ renesas_i3c_enqueue_xfer(i3c, xfer);
+
+ for (i = 0; i < i2c_nxfers; i++) {
+ cmd->i2c_bytes_left = I2C_INIT_MSG;
+ cmd->i2c_buf = i2c_xfers[i].buf;
+ cmd->msg = &i2c_xfers[i];
+ cmd->i2c_is_last = (i == i2c_nxfers - 1);
+
+ renesas_set_bit(i3c->regs, BIE, BIE_NACKDIE);
+ renesas_set_bit(i3c->regs, NTIE, NTIE_TDBEIE0);
+ renesas_set_bit(i3c->regs, BIE, BIE_STCNDDIE);
+
+ /* Issue Start condition */
+ renesas_set_bit(i3c->regs, CNDCTL, start_bit);
+
+ renesas_set_bit(i3c->regs, NTSTE, NTSTE_TDBEE0);
+
+ wait_for_completion_timeout(&xfer->comp, m->i2c.timeout);
+
+ if (cmd->err)
+ break;
+
+ start_bit = CNDCTL_SRCND;
+ }
+
+ renesas_i3c_dequeue_xfer(i3c, xfer);
+ return cmd->err;
+}
+
+static int renesas_i3c_attach_i2c_dev(struct i2c_dev_desc *dev)
+{
+ struct i3c_master_controller *m = i2c_dev_get_master(dev);
+ struct renesas_i3c *i3c = to_renesas_i3c(m);
+ struct renesas_i3c_i2c_dev_data *data;
+ int pos;
+
+ pos = renesas_i3c_get_free_pos(i3c);
+ if (pos < 0)
+ return pos;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->index = pos;
+ i3c->addrs[pos] = dev->addr;
+ i3c->free_pos &= ~BIT(pos);
+ i2c_dev_set_master_data(dev, data);
+
+ return 0;
+}
+
+static void renesas_i3c_detach_i2c_dev(struct i2c_dev_desc *dev)
+{
+ struct renesas_i3c_i2c_dev_data *data = i2c_dev_get_master_data(dev);
+ struct i3c_master_controller *m = i2c_dev_get_master(dev);
+ struct renesas_i3c *i3c = to_renesas_i3c(m);
+
+ i2c_dev_set_master_data(dev, NULL);
+ i3c->addrs[data->index] = 0;
+ i3c->free_pos |= BIT(data->index);
+ kfree(data);
+}
+
+static irqreturn_t renesas_i3c_tx_isr(int irq, void *data)
+{
+ struct renesas_i3c *i3c = data;
+ struct renesas_i3c_xfer *xfer;
+ struct renesas_i3c_cmd *cmd;
+ u8 val;
+
+ scoped_guard(spinlock, &i3c->xferqueue.lock) {
+ xfer = i3c->xferqueue.cur;
+ cmd = xfer->cmds;
+
+ if (xfer->is_i2c_xfer) {
+ if (!cmd->i2c_bytes_left)
+ return IRQ_NONE;
+
+ if (cmd->i2c_bytes_left != I2C_INIT_MSG) {
+ val = *cmd->i2c_buf;
+ cmd->i2c_buf++;
+ cmd->i2c_bytes_left--;
+ renesas_writel(i3c->regs, NTDTBP0, val);
+ }
+
+ if (cmd->i2c_bytes_left == 0) {
+ renesas_clear_bit(i3c->regs, NTIE, NTIE_TDBEIE0);
+ renesas_set_bit(i3c->regs, BIE, BIE_TENDIE);
+ }
+
+ /* Clear the Transmit Buffer Empty status flag. */
+ renesas_clear_bit(i3c->regs, NTST, NTST_TDBEF0);
+ } else {
+ i3c_writel_fifo(i3c->regs + NTDTBP0, cmd->tx_buf, cmd->len);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t renesas_i3c_resp_isr(int irq, void *data)
+{
+ struct renesas_i3c *i3c = data;
+ struct renesas_i3c_xfer *xfer;
+ struct renesas_i3c_cmd *cmd;
+ u32 resp_descriptor = renesas_readl(i3c->regs, NRSPQP);
+ u32 bytes_remaining = 0;
+ u32 ntst, data_len;
+ int ret = 0;
+
+ scoped_guard(spinlock, &i3c->xferqueue.lock) {
+ xfer = i3c->xferqueue.cur;
+ cmd = xfer->cmds;
+
+ /* Clear the Respone Queue Full status flag*/
+ renesas_clear_bit(i3c->regs, NTST, NTST_RSPQFF);
+
+ data_len = NRSPQP_DATA_LEN(resp_descriptor);
+
+ switch (i3c->internal_state) {
+ case I3C_INTERNAL_STATE_CONTROLLER_ENTDAA:
+ cmd->rx_count = data_len;
+ break;
+ case I3C_INTERNAL_STATE_CONTROLLER_WRITE:
+ case I3C_INTERNAL_STATE_CONTROLLER_COMMAND_WRITE:
+ /* Disable the transmit IRQ if it hasn't been disabled already. */
+ renesas_clear_bit(i3c->regs, NTIE, NTIE_TDBEIE0);
+ break;
+ case I3C_INTERNAL_STATE_CONTROLLER_READ:
+ case I3C_INTERNAL_STATE_CONTROLLER_COMMAND_READ:
+ if (NDBSTLV0_RDBLV(renesas_readl(i3c->regs, NDBSTLV0)) && !cmd->err)
+ bytes_remaining = data_len - cmd->rx_count;
+
+ i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, bytes_remaining);
+ renesas_clear_bit(i3c->regs, NTIE, NTIE_RDBFIE0);
+ break;
+ default:
+ break;
+ }
+
+ switch (NRSPQP_ERR_STATUS(resp_descriptor)) {
+ case NRSPQP_NO_ERROR:
+ break;
+ case NRSPQP_ERROR_PARITY:
+ case NRSPQP_ERROR_IBA_NACK:
+ case NRSPQP_ERROR_TRANSF_ABORT:
+ case NRSPQP_ERROR_CRC:
+ case NRSPQP_ERROR_FRAME:
+ ret = -EIO;
+ break;
+ case NRSPQP_ERROR_OVER_UNDER_FLOW:
+ ret = -ENOSPC;
+ break;
+ case NRSPQP_ERROR_UNSUPPORTED:
+ ret = -EOPNOTSUPP;
+ break;
+ case NRSPQP_ERROR_I2C_W_NACK_ERR:
+ case NRSPQP_ERROR_ADDRESS_NACK:
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ /*
+ * If the transfer was aborted, then the abort flag must be cleared
+ * before notifying the application that a transfer has completed.
+ */
+ ntst = renesas_readl(i3c->regs, NTST);
+ if (ntst & NTST_TABTF)
+ renesas_clear_bit(i3c->regs, BCTL, BCTL_ABT);
+
+ /* Clear error status flags. */
+ renesas_clear_bit(i3c->regs, NTST, NTST_TEF | NTST_TABTF);
+
+ xfer->ret = ret;
+ complete(&xfer->comp);
+
+ xfer = list_first_entry_or_null(&i3c->xferqueue.list,
+ struct renesas_i3c_xfer, node);
+ if (xfer)
+ list_del_init(&xfer->node);
+
+ i3c->xferqueue.cur = xfer;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t renesas_i3c_tend_isr(int irq, void *data)
+{
+ struct renesas_i3c *i3c = data;
+ struct renesas_i3c_xfer *xfer;
+ struct renesas_i3c_cmd *cmd;
+
+ scoped_guard(spinlock, &i3c->xferqueue.lock) {
+ xfer = i3c->xferqueue.cur;
+ cmd = xfer->cmds;
+
+ if (xfer->is_i2c_xfer) {
+ if (renesas_readl(i3c->regs, BST) & BST_NACKDF) {
+ /* We got a NACKIE */
+ renesas_readl(i3c->regs, NTDTBP0); /* dummy read */
+ renesas_clear_bit(i3c->regs, BST, BST_NACKDF);
+ cmd->err = -ENXIO;
+ } else if (cmd->i2c_bytes_left) {
+ renesas_set_bit(i3c->regs, NTIE, NTIE_TDBEIE0);
+ return IRQ_NONE;
+ }
+
+ if (cmd->i2c_is_last || cmd->err) {
+ renesas_clear_bit(i3c->regs, BIE, BIE_TENDIE);
+ renesas_set_bit(i3c->regs, BIE, BIE_SPCNDDIE);
+ renesas_set_bit(i3c->regs, CNDCTL, CNDCTL_SPCND);
+ } else {
+ /* Transfer is complete, but do not send STOP */
+ renesas_clear_bit(i3c->regs, NTSTE, NTSTE_TDBEE0);
+ renesas_clear_bit(i3c->regs, BIE, BIE_TENDIE);
+ xfer->ret = 0;
+ complete(&xfer->comp);
+ }
+ }
+
+ /* Clear the Transmit Buffer Empty status flag. */
+ renesas_clear_bit(i3c->regs, BST, BST_TENDF);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t renesas_i3c_rx_isr(int irq, void *data)
+{
+ struct renesas_i3c *i3c = data;
+ struct renesas_i3c_xfer *xfer;
+ struct renesas_i3c_cmd *cmd;
+ int read_bytes;
+
+ /* If resp_isr already read the data and updated 'xfer', we can just leave */
+ if (!(renesas_readl(i3c->regs, NTIE) & NTIE_RDBFIE0))
+ return IRQ_NONE;
+
+ scoped_guard(spinlock, &i3c->xferqueue.lock) {
+ xfer = i3c->xferqueue.cur;
+ cmd = xfer->cmds;
+
+ if (xfer->is_i2c_xfer) {
+ if (!cmd->i2c_bytes_left)
+ return IRQ_NONE;
+
+ if (cmd->i2c_bytes_left == I2C_INIT_MSG) {
+ cmd->i2c_bytes_left = cmd->msg->len;
+ renesas_set_bit(i3c->regs, SCSTRCTL, SCSTRCTL_RWE);
+ renesas_readl(i3c->regs, NTDTBP0); /* dummy read */
+ if (cmd->i2c_bytes_left == 1)
+ renesas_writel(i3c->regs, ACKCTL, ACKCTL_ACKT | ACKCTL_ACKTWP);
+ return IRQ_HANDLED;
+ }
+
+ if (cmd->i2c_bytes_left == 1) {
+ /* STOP must come before we set ACKCTL! */
+ if (cmd->i2c_is_last) {
+ renesas_set_bit(i3c->regs, BIE, BIE_SPCNDDIE);
+ renesas_clear_bit(i3c->regs, BST, BST_SPCNDDF);
+ renesas_set_bit(i3c->regs, CNDCTL, CNDCTL_SPCND);
+ }
+ renesas_writel(i3c->regs, ACKCTL, ACKCTL_ACKT | ACKCTL_ACKTWP);
+ } else {
+ renesas_writel(i3c->regs, ACKCTL, ACKCTL_ACKTWP);
+ }
+
+ /* Reading acks the RIE interrupt */
+ *cmd->i2c_buf = renesas_readl(i3c->regs, NTDTBP0);
+ cmd->i2c_buf++;
+ cmd->i2c_bytes_left--;
+ } else {
+ read_bytes = NDBSTLV0_RDBLV(renesas_readl(i3c->regs, NDBSTLV0)) * sizeof(u32);
+ i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, read_bytes);
+ cmd->rx_count = read_bytes;
+ }
+
+ /* Clear the Read Buffer Full status flag. */
+ renesas_clear_bit(i3c->regs, NTST, NTST_RDBFF0);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t renesas_i3c_stop_isr(int irq, void *data)
+{
+ struct renesas_i3c *i3c = data;
+ struct renesas_i3c_xfer *xfer;
+
+ scoped_guard(spinlock, &i3c->xferqueue.lock) {
+ xfer = i3c->xferqueue.cur;
+
+ /* read back registers to confirm writes have fully propagated */
+ renesas_writel(i3c->regs, BST, 0);
+ renesas_readl(i3c->regs, BST);
+ renesas_writel(i3c->regs, BIE, 0);
+ renesas_clear_bit(i3c->regs, NTST, NTST_TDBEF0 | NTST_RDBFF0);
+ renesas_clear_bit(i3c->regs, SCSTRCTL, SCSTRCTL_RWE);
+
+ xfer->ret = 0;
+ complete(&xfer->comp);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t renesas_i3c_start_isr(int irq, void *data)
+{
+ struct renesas_i3c *i3c = data;
+ struct renesas_i3c_xfer *xfer;
+ struct renesas_i3c_cmd *cmd;
+ u8 val;
+
+ scoped_guard(spinlock, &i3c->xferqueue.lock) {
+ xfer = i3c->xferqueue.cur;
+ cmd = xfer->cmds;
+
+ if (xfer->is_i2c_xfer) {
+ if (!cmd->i2c_bytes_left)
+ return IRQ_NONE;
+
+ if (cmd->i2c_bytes_left == I2C_INIT_MSG) {
+ if (cmd->msg->flags & I2C_M_RD) {
+ /* On read, switch over to receive interrupt */
+ renesas_clear_bit(i3c->regs, NTIE, NTIE_TDBEIE0);
+ renesas_set_bit(i3c->regs, NTIE, NTIE_RDBFIE0);
+ } else {
+ /* On write, initialize length */
+ cmd->i2c_bytes_left = cmd->msg->len;
+ }
+
+ val = i2c_8bit_addr_from_msg(cmd->msg);
+ renesas_writel(i3c->regs, NTDTBP0, val);
+ }
+ }
+
+ renesas_clear_bit(i3c->regs, BIE, BIE_STCNDDIE);
+ renesas_clear_bit(i3c->regs, BST, BST_STCNDDF);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct i3c_master_controller_ops renesas_i3c_ops = {
+ .bus_init = renesas_i3c_bus_init,
+ .bus_cleanup = renesas_i3c_bus_cleanup,
+ .attach_i3c_dev = renesas_i3c_attach_i3c_dev,
+ .reattach_i3c_dev = renesas_i3c_reattach_i3c_dev,
+ .detach_i3c_dev = renesas_i3c_detach_i3c_dev,
+ .do_daa = renesas_i3c_daa,
+ .supports_ccc_cmd = renesas_i3c_supports_ccc_cmd,
+ .send_ccc_cmd = renesas_i3c_send_ccc_cmd,
+ .priv_xfers = renesas_i3c_priv_xfers,
+ .attach_i2c_dev = renesas_i3c_attach_i2c_dev,
+ .detach_i2c_dev = renesas_i3c_detach_i2c_dev,
+ .i2c_xfers = renesas_i3c_i2c_xfers,
+};
+
+static const struct renesas_i3c_irq_desc renesas_i3c_irqs[] = {
+ { .name = "resp", .isr = renesas_i3c_resp_isr, .desc = "i3c-resp" },
+ { .name = "rx", .isr = renesas_i3c_rx_isr, .desc = "i3c-rx" },
+ { .name = "tx", .isr = renesas_i3c_tx_isr, .desc = "i3c-tx" },
+ { .name = "st", .isr = renesas_i3c_start_isr, .desc = "i3c-start" },
+ { .name = "sp", .isr = renesas_i3c_stop_isr, .desc = "i3c-stop" },
+ { .name = "tend", .isr = renesas_i3c_tend_isr, .desc = "i3c-tend" },
+ { .name = "nack", .isr = renesas_i3c_tend_isr, .desc = "i3c-nack" },
+};
+
+static int renesas_i3c_probe(struct platform_device *pdev)
+{
+ struct renesas_i3c *i3c;
+ struct reset_control *reset;
+ struct clk *clk;
+ const struct renesas_i3c_config *config = of_device_get_match_data(&pdev->dev);
+ int ret, i;
+
+ if (!config)
+ return -ENODATA;
+
+ i3c = devm_kzalloc(&pdev->dev, sizeof(*i3c), GFP_KERNEL);
+ if (!i3c)
+ return -ENOMEM;
+
+ i3c->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(i3c->regs))
+ return PTR_ERR(i3c->regs);
+
+ clk = devm_clk_get_enabled(&pdev->dev, "pclk");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ if (config->has_pclkrw) {
+ clk = devm_clk_get_enabled(&pdev->dev, "pclkrw");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+ }
+
+ i3c->tclk = devm_clk_get_enabled(&pdev->dev, "tclk");
+ if (IS_ERR(i3c->tclk))
+ return PTR_ERR(i3c->tclk);
+
+ reset = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "tresetn");
+ if (IS_ERR(reset))
+ return dev_err_probe(&pdev->dev, PTR_ERR(reset),
+ "Error: missing tresetn ctrl\n");
+
+ reset = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "presetn");
+ if (IS_ERR(reset))
+ return dev_err_probe(&pdev->dev, PTR_ERR(reset),
+ "Error: missing presetn ctrl\n");
+
+ spin_lock_init(&i3c->xferqueue.lock);
+ INIT_LIST_HEAD(&i3c->xferqueue.list);
+
+ ret = renesas_i3c_reset(i3c);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(renesas_i3c_irqs); i++) {
+ ret = platform_get_irq_byname(pdev, renesas_i3c_irqs[i].name);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_request_irq(&pdev->dev, ret, renesas_i3c_irqs[i].isr,
+ 0, renesas_i3c_irqs[i].desc, i3c);
+ if (ret)
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, i3c);
+
+ i3c->maxdevs = RENESAS_I3C_MAX_DEVS;
+ i3c->free_pos = GENMASK(i3c->maxdevs - 1, 0);
+
+ return i3c_master_register(&i3c->base, &pdev->dev, &renesas_i3c_ops, false);
+}
+
+static void renesas_i3c_remove(struct platform_device *pdev)
+{
+ struct renesas_i3c *i3c = platform_get_drvdata(pdev);
+
+ i3c_master_unregister(&i3c->base);
+}
+
+static const struct renesas_i3c_config empty_i3c_config = {
+};
+
+static const struct renesas_i3c_config r9a09g047_i3c_config = {
+ .has_pclkrw = 1,
+};
+
+static const struct of_device_id renesas_i3c_of_ids[] = {
+ { .compatible = "renesas,r9a08g045-i3c", .data = &empty_i3c_config },
+ { .compatible = "renesas,r9a09g047-i3c", .data = &r9a09g047_i3c_config },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, renesas_i3c_of_ids);
+
+static struct platform_driver renesas_i3c = {
+ .probe = renesas_i3c_probe,
+ .remove = renesas_i3c_remove,
+ .driver = {
+ .name = "renesas-i3c",
+ .of_match_table = renesas_i3c_of_ids,
+ },
+};
+module_platform_driver(renesas_i3c);
+
+MODULE_AUTHOR("Wolfram Sang <wsa+renesas@sang-engineering.com>");
+MODULE_AUTHOR("Renesas BSP teams");
+MODULE_DESCRIPTION("Renesas I3C controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 7e1a7cb94b43..701ae165b25b 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -104,6 +104,7 @@
#define SVC_I3C_MDATACTRL_TXTRIG_FIFO_NOT_FULL GENMASK(5, 4)
#define SVC_I3C_MDATACTRL_RXTRIG_FIFO_NOT_EMPTY 0
#define SVC_I3C_MDATACTRL_RXCOUNT(x) FIELD_GET(GENMASK(28, 24), (x))
+#define SVC_I3C_MDATACTRL_TXCOUNT(x) FIELD_GET(GENMASK(20, 16), (x))
#define SVC_I3C_MDATACTRL_TXFULL BIT(30)
#define SVC_I3C_MDATACTRL_RXEMPTY BIT(31)
@@ -664,7 +665,6 @@ static int svc_i3c_master_set_speed(struct i3c_master_controller *m,
}
rpm_out:
- pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret;
@@ -779,7 +779,6 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
goto rpm_out;
rpm_out:
- pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret;
@@ -801,7 +800,6 @@ static void svc_i3c_master_bus_cleanup(struct i3c_master_controller *m)
/* Disable master */
writel(0, master->regs + SVC_I3C_MCONFIG);
- pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
}
@@ -1207,7 +1205,6 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
dev_err(master->dev, "Cannot handle such a list of devices");
rpm_out:
- pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret;
@@ -1304,14 +1301,19 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
* FIFO start filling as soon as possible after EmitStartAddr.
*/
if (svc_has_quirk(master, SVC_I3C_QUIRK_FIFO_EMPTY) && !rnw && xfer_len) {
- u32 end = xfer_len > SVC_I3C_FIFO_SIZE ? 0 : SVC_I3C_MWDATAB_END;
- u32 len = min_t(u32, xfer_len, SVC_I3C_FIFO_SIZE);
-
- writesb(master->regs + SVC_I3C_MWDATAB1, out, len - 1);
- /* Mark END bit if this is the last byte */
- writel(out[len - 1] | end, master->regs + SVC_I3C_MWDATAB);
- xfer_len -= len;
- out += len;
+ u32 space, end, len;
+
+ reg = readl(master->regs + SVC_I3C_MDATACTRL);
+ space = SVC_I3C_FIFO_SIZE - SVC_I3C_MDATACTRL_TXCOUNT(reg);
+ if (space) {
+ end = xfer_len > space ? 0 : SVC_I3C_MWDATAB_END;
+ len = min_t(u32, xfer_len, space);
+ writesb(master->regs + SVC_I3C_MWDATAB1, out, len - 1);
+ /* Mark END bit if this is the last byte */
+ writel(out[len - 1] | end, master->regs + SVC_I3C_MWDATAB);
+ xfer_len -= len;
+ out += len;
+ }
}
ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
@@ -1511,7 +1513,6 @@ static void svc_i3c_master_enqueue_xfer(struct svc_i3c_master *master,
}
spin_unlock_irqrestore(&master->xferqueue.lock, flags);
- pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
}
@@ -1708,7 +1709,7 @@ static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
mutex_lock(&master->lock);
svc_i3c_master_enqueue_xfer(master, xfer);
- if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
+ if (!wait_for_completion_timeout(&xfer->comp, m->i2c.timeout))
svc_i3c_master_dequeue_xfer(master, xfer);
mutex_unlock(&master->lock);
@@ -1801,7 +1802,6 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
ret = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
- pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret;
@@ -1834,7 +1834,6 @@ static int svc_i3c_master_disable_hotjoin(struct i3c_master_controller *m)
if (!master->enabled_events)
svc_i3c_master_disable_interrupts(master);
- pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return 0;
@@ -1954,7 +1953,6 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
if (ret)
goto rpm_disable;
- pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c
index c711db6f8f5c..cf17fd46e255 100644
--- a/drivers/md/dm-flakey.c
+++ b/drivers/md/dm-flakey.c
@@ -215,16 +215,19 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
}
if (test_bit(DROP_WRITES, &fc->flags) &&
- (fc->corrupt_bio_rw == WRITE || fc->random_write_corrupt)) {
+ ((fc->corrupt_bio_byte && fc->corrupt_bio_rw == WRITE) ||
+ fc->random_write_corrupt)) {
ti->error = "drop_writes is incompatible with random_write_corrupt or corrupt_bio_byte with the WRITE flag set";
return -EINVAL;
} else if (test_bit(ERROR_WRITES, &fc->flags) &&
- (fc->corrupt_bio_rw == WRITE || fc->random_write_corrupt)) {
+ ((fc->corrupt_bio_byte && fc->corrupt_bio_rw == WRITE) ||
+ fc->random_write_corrupt)) {
ti->error = "error_writes is incompatible with random_write_corrupt or corrupt_bio_byte with the WRITE flag set";
return -EINVAL;
} else if (test_bit(ERROR_READS, &fc->flags) &&
- (fc->corrupt_bio_rw == READ || fc->random_read_corrupt)) {
+ ((fc->corrupt_bio_byte && fc->corrupt_bio_rw == READ) ||
+ fc->random_read_corrupt)) {
ti->error = "error_reads is incompatible with random_read_corrupt or corrupt_bio_byte with the READ flag set";
return -EINVAL;
}
diff --git a/drivers/md/dm-ima.c b/drivers/md/dm-ima.c
index b90f34259fbb..8b50c908c6f4 100644
--- a/drivers/md/dm-ima.c
+++ b/drivers/md/dm-ima.c
@@ -241,10 +241,11 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
/*
* First retrieve the target metadata.
*/
- scnprintf(target_metadata_buf, DM_IMA_TARGET_METADATA_BUF_LEN,
- "target_index=%d,target_begin=%llu,target_len=%llu,",
- i, ti->begin, ti->len);
- target_metadata_buf_len = strlen(target_metadata_buf);
+ target_metadata_buf_len =
+ scnprintf(target_metadata_buf,
+ DM_IMA_TARGET_METADATA_BUF_LEN,
+ "target_index=%d,target_begin=%llu,target_len=%llu,",
+ i, ti->begin, ti->len);
/*
* Then retrieve the actual target data.
@@ -448,11 +449,9 @@ void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap)
if (r)
goto error;
- scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN,
- "%sname=%s,uuid=%s;device_resume=no_data;",
- DM_IMA_VERSION_STR, dev_name, dev_uuid);
- l = strlen(device_table_data);
-
+ l = scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN,
+ "%sname=%s,uuid=%s;device_resume=no_data;",
+ DM_IMA_VERSION_STR, dev_name, dev_uuid);
}
capacity_len = strlen(capacity_str);
@@ -561,10 +560,9 @@ void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all)
if (dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio))
goto error;
- scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN,
- "%sname=%s,uuid=%s;device_remove=no_data;",
- DM_IMA_VERSION_STR, dev_name, dev_uuid);
- l = strlen(device_table_data);
+ l = scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN,
+ "%sname=%s,uuid=%s;device_remove=no_data;",
+ DM_IMA_VERSION_STR, dev_name, dev_uuid);
}
memcpy(device_table_data + l, remove_all_str, remove_all_len);
@@ -647,10 +645,9 @@ void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map)
if (dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio))
goto error2;
- scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN,
- "%sname=%s,uuid=%s;table_clear=no_data;",
- DM_IMA_VERSION_STR, dev_name, dev_uuid);
- l = strlen(device_table_data);
+ l = scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN,
+ "%sname=%s,uuid=%s;table_clear=no_data;",
+ DM_IMA_VERSION_STR, dev_name, dev_uuid);
}
capacity_len = strlen(capacity_str);
@@ -706,7 +703,7 @@ void dm_ima_measure_on_device_rename(struct mapped_device *md)
char *old_device_data = NULL, *new_device_data = NULL, *combined_device_data = NULL;
char *new_dev_name = NULL, *new_dev_uuid = NULL, *capacity_str = NULL;
bool noio = true;
- int r;
+ int r, len;
if (dm_ima_alloc_and_copy_device_data(md, &new_device_data,
md->ima.active_table.num_targets, noio))
@@ -728,12 +725,11 @@ void dm_ima_measure_on_device_rename(struct mapped_device *md)
md->ima.active_table.device_metadata = new_device_data;
md->ima.active_table.device_metadata_len = strlen(new_device_data);
- scnprintf(combined_device_data, DM_IMA_DEVICE_BUF_LEN * 2,
- "%s%snew_name=%s,new_uuid=%s;%s", DM_IMA_VERSION_STR, old_device_data,
- new_dev_name, new_dev_uuid, capacity_str);
+ len = scnprintf(combined_device_data, DM_IMA_DEVICE_BUF_LEN * 2,
+ "%s%snew_name=%s,new_uuid=%s;%s", DM_IMA_VERSION_STR, old_device_data,
+ new_dev_name, new_dev_uuid, capacity_str);
- dm_ima_measure_data("dm_device_rename", combined_device_data, strlen(combined_device_data),
- noio);
+ dm_ima_measure_data("dm_device_rename", combined_device_data, len, noio);
goto exit;
diff --git a/drivers/md/dm-path-selector.c b/drivers/md/dm-path-selector.c
index 3e4cb81ce512..d0b883fabfeb 100644
--- a/drivers/md/dm-path-selector.c
+++ b/drivers/md/dm-path-selector.c
@@ -117,16 +117,16 @@ int dm_register_path_selector(struct path_selector_type *pst)
}
EXPORT_SYMBOL_GPL(dm_register_path_selector);
-int dm_unregister_path_selector(struct path_selector_type *pst)
+void dm_unregister_path_selector(struct path_selector_type *pst)
{
struct ps_internal *psi;
down_write(&_ps_lock);
psi = __find_path_selector_type(pst->name);
- if (!psi) {
+ if (WARN_ON(!psi)) {
up_write(&_ps_lock);
- return -EINVAL;
+ return;
}
list_del(&psi->list);
@@ -134,7 +134,5 @@ int dm_unregister_path_selector(struct path_selector_type *pst)
up_write(&_ps_lock);
kfree(psi);
-
- return 0;
}
EXPORT_SYMBOL_GPL(dm_unregister_path_selector);
diff --git a/drivers/md/dm-path-selector.h b/drivers/md/dm-path-selector.h
index 3861b2d8b963..7b2270532e64 100644
--- a/drivers/md/dm-path-selector.h
+++ b/drivers/md/dm-path-selector.h
@@ -96,7 +96,7 @@ struct path_selector_type {
int dm_register_path_selector(struct path_selector_type *type);
/* Unregister a path selector */
-int dm_unregister_path_selector(struct path_selector_type *type);
+void dm_unregister_path_selector(struct path_selector_type *type);
/* Returns a registered path selector type */
struct path_selector_type *dm_get_path_selector(const char *name);
diff --git a/drivers/md/dm-ps-historical-service-time.c b/drivers/md/dm-ps-historical-service-time.c
index b49e10d76d03..f07e773d9cc0 100644
--- a/drivers/md/dm-ps-historical-service-time.c
+++ b/drivers/md/dm-ps-historical-service-time.c
@@ -541,8 +541,10 @@ static int __init dm_hst_init(void)
{
int r = dm_register_path_selector(&hst_ps);
- if (r < 0)
+ if (r < 0) {
DMERR("register failed %d", r);
+ return r;
+ }
DMINFO("version " HST_VERSION " loaded");
@@ -551,10 +553,7 @@ static int __init dm_hst_init(void)
static void __exit dm_hst_exit(void)
{
- int r = dm_unregister_path_selector(&hst_ps);
-
- if (r < 0)
- DMERR("unregister failed %d", r);
+ dm_unregister_path_selector(&hst_ps);
}
module_init(dm_hst_init);
diff --git a/drivers/md/dm-ps-io-affinity.c b/drivers/md/dm-ps-io-affinity.c
index 716807e511ee..80415a045c68 100644
--- a/drivers/md/dm-ps-io-affinity.c
+++ b/drivers/md/dm-ps-io-affinity.c
@@ -260,10 +260,7 @@ static int __init dm_ioa_init(void)
static void __exit dm_ioa_exit(void)
{
- int ret = dm_unregister_path_selector(&ioa_ps);
-
- if (ret < 0)
- DMERR("unregister failed %d", ret);
+ dm_unregister_path_selector(&ioa_ps);
}
module_init(dm_ioa_init);
diff --git a/drivers/md/dm-ps-queue-length.c b/drivers/md/dm-ps-queue-length.c
index e305f05ad1e5..9c68701ed7a4 100644
--- a/drivers/md/dm-ps-queue-length.c
+++ b/drivers/md/dm-ps-queue-length.c
@@ -260,8 +260,10 @@ static int __init dm_ql_init(void)
{
int r = dm_register_path_selector(&ql_ps);
- if (r < 0)
+ if (r < 0) {
DMERR("register failed %d", r);
+ return r;
+ }
DMINFO("version " QL_VERSION " loaded");
@@ -270,10 +272,7 @@ static int __init dm_ql_init(void)
static void __exit dm_ql_exit(void)
{
- int r = dm_unregister_path_selector(&ql_ps);
-
- if (r < 0)
- DMERR("unregister failed %d", r);
+ dm_unregister_path_selector(&ql_ps);
}
module_init(dm_ql_init);
diff --git a/drivers/md/dm-ps-round-robin.c b/drivers/md/dm-ps-round-robin.c
index d1745b123dc1..0c12f4073461 100644
--- a/drivers/md/dm-ps-round-robin.c
+++ b/drivers/md/dm-ps-round-robin.c
@@ -220,8 +220,10 @@ static int __init dm_rr_init(void)
{
int r = dm_register_path_selector(&rr_ps);
- if (r < 0)
+ if (r < 0) {
DMERR("register failed %d", r);
+ return r;
+ }
DMINFO("version " RR_VERSION " loaded");
@@ -230,10 +232,7 @@ static int __init dm_rr_init(void)
static void __exit dm_rr_exit(void)
{
- int r = dm_unregister_path_selector(&rr_ps);
-
- if (r < 0)
- DMERR("unregister failed %d", r);
+ dm_unregister_path_selector(&rr_ps);
}
module_init(dm_rr_init);
diff --git a/drivers/md/dm-ps-service-time.c b/drivers/md/dm-ps-service-time.c
index 969d31c40272..0543fe7969c4 100644
--- a/drivers/md/dm-ps-service-time.c
+++ b/drivers/md/dm-ps-service-time.c
@@ -341,8 +341,10 @@ static int __init dm_st_init(void)
{
int r = dm_register_path_selector(&st_ps);
- if (r < 0)
+ if (r < 0) {
DMERR("register failed %d", r);
+ return r;
+ }
DMINFO("version " ST_VERSION " loaded");
@@ -351,10 +353,7 @@ static int __init dm_st_init(void)
static void __exit dm_st_exit(void)
{
- int r = dm_unregister_path_selector(&st_ps);
-
- if (r < 0)
- DMERR("unregister failed %d", r);
+ dm_unregister_path_selector(&st_ps);
}
module_init(dm_st_init);
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index e8c0a8c6fb51..15c538ee9537 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -14,7 +14,6 @@
#include "raid5.h"
#include "raid10.h"
#include "md-bitmap.h"
-#include "dm-core.h"
#include <linux/device-mapper.h>
@@ -2532,6 +2531,10 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs)
struct md_rdev *rdev, *freshest;
struct mddev *mddev = &rs->md;
+ /* Respect resynchronization requested with "sync" argument. */
+ if (test_bit(__CTR_FLAG_SYNC, &rs->ctr_flags))
+ set_bit(MD_ARRAY_FIRST_USE, &mddev->flags);
+
freshest = NULL;
rdev_for_each(rdev, mddev) {
if (test_bit(Journal, &rdev->flags))
@@ -3305,7 +3308,7 @@ size_check:
/* Disable/enable discard support on raid set. */
configure_discard_support(rs);
- rs->md.dm_gendisk = ti->table->md->disk;
+ rs->md.dm_gendisk = dm_disk(dm_table_get_md(ti->table));
mddev_unlock(&rs->md);
return 0;
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index d9d5e6aa5707..ad0a60a07b93 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -899,17 +899,17 @@ static bool dm_table_supports_dax(struct dm_table *t,
return true;
}
-static int device_is_rq_stackable(struct dm_target *ti, struct dm_dev *dev,
- sector_t start, sector_t len, void *data)
+static int device_is_not_rq_stackable(struct dm_target *ti, struct dm_dev *dev,
+ sector_t start, sector_t len, void *data)
{
struct block_device *bdev = dev->bdev;
struct request_queue *q = bdev_get_queue(bdev);
/* request-based cannot stack on partitions! */
if (bdev_is_partition(bdev))
- return false;
+ return true;
- return queue_is_mq(q);
+ return !queue_is_mq(q);
}
static int dm_table_determine_type(struct dm_table *t)
@@ -1005,7 +1005,7 @@ verify_rq_based:
/* Non-request-stackable devices can't be used for request-based dm */
if (!ti->type->iterate_devices ||
- !ti->type->iterate_devices(ti, device_is_rq_stackable, NULL)) {
+ ti->type->iterate_devices(ti, device_is_not_rq_stackable, NULL)) {
DMERR("table load rejected: including non-request-stackable devices");
return -EINVAL;
}
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index 05cf4e3f2bbe..007bb93e5fca 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -4111,8 +4111,8 @@ static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
static struct target_type pool_target = {
.name = "thin-pool",
.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
- DM_TARGET_IMMUTABLE,
- .version = {1, 23, 0},
+ DM_TARGET_IMMUTABLE | DM_TARGET_PASSES_CRYPTO,
+ .version = {1, 24, 0},
.module = THIS_MODULE,
.ctr = pool_ctr,
.dtr = pool_dtr,
@@ -4497,7 +4497,8 @@ static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
static struct target_type thin_target = {
.name = "thin",
- .version = {1, 23, 0},
+ .features = DM_TARGET_PASSES_CRYPTO,
+ .version = {1, 24, 0},
.module = THIS_MODULE,
.ctr = thin_ctr,
.dtr = thin_dtr,
diff --git a/drivers/md/dm-vdo/funnel-workqueue.c b/drivers/md/dm-vdo/funnel-workqueue.c
index ae11941c90a9..0613c82bbe8e 100644
--- a/drivers/md/dm-vdo/funnel-workqueue.c
+++ b/drivers/md/dm-vdo/funnel-workqueue.c
@@ -252,8 +252,7 @@ static void service_work_queue(struct simple_work_queue *queue)
* This speeds up some performance tests; that "other work" might include other VDO
* threads.
*/
- if (need_resched())
- cond_resched();
+ cond_resched();
}
run_finish_hook(queue);
diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c
index 631a887b487c..d382a390d39a 100644
--- a/drivers/md/dm-verity-fec.c
+++ b/drivers/md/dm-verity-fec.c
@@ -191,7 +191,7 @@ static int fec_is_erasure(struct dm_verity *v, struct dm_verity_io *io,
u8 *want_digest, u8 *data)
{
if (unlikely(verity_hash(v, io, data, 1 << v->data_dev_block_bits,
- verity_io_real_digest(v, io), true)))
+ verity_io_real_digest(v, io))))
return 0;
return memcmp(verity_io_real_digest(v, io), want_digest,
@@ -392,7 +392,7 @@ static int fec_decode_rsb(struct dm_verity *v, struct dm_verity_io *io,
/* Always re-validate the corrected block against the expected hash */
r = verity_hash(v, io, fio->output, 1 << v->data_dev_block_bits,
- verity_io_real_digest(v, io), true);
+ verity_io_real_digest(v, io));
if (unlikely(r < 0))
return r;
diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index 81186bded1ce..66a00a8ccb39 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -19,7 +19,6 @@
#include "dm-audit.h"
#include <linux/module.h>
#include <linux/reboot.h>
-#include <linux/scatterlist.h>
#include <linux/string.h>
#include <linux/jump_label.h>
#include <linux/security.h>
@@ -61,9 +60,6 @@ module_param_array_named(use_bh_bytes, dm_verity_use_bh_bytes, uint, NULL, 0644)
static DEFINE_STATIC_KEY_FALSE(use_bh_wq_enabled);
-/* Is at least one dm-verity instance using ahash_tfm instead of shash_tfm? */
-static DEFINE_STATIC_KEY_FALSE(ahash_enabled);
-
struct dm_verity_prefetch_work {
struct work_struct work;
struct dm_verity *v;
@@ -118,100 +114,21 @@ static sector_t verity_position_at_level(struct dm_verity *v, sector_t block,
return block >> (level * v->hash_per_block_bits);
}
-static int verity_ahash_update(struct dm_verity *v, struct ahash_request *req,
- const u8 *data, size_t len,
- struct crypto_wait *wait)
-{
- struct scatterlist sg;
-
- if (likely(!is_vmalloc_addr(data))) {
- sg_init_one(&sg, data, len);
- ahash_request_set_crypt(req, &sg, NULL, len);
- return crypto_wait_req(crypto_ahash_update(req), wait);
- }
-
- do {
- int r;
- size_t this_step = min_t(size_t, len, PAGE_SIZE - offset_in_page(data));
-
- flush_kernel_vmap_range((void *)data, this_step);
- sg_init_table(&sg, 1);
- sg_set_page(&sg, vmalloc_to_page(data), this_step, offset_in_page(data));
- ahash_request_set_crypt(req, &sg, NULL, this_step);
- r = crypto_wait_req(crypto_ahash_update(req), wait);
- if (unlikely(r))
- return r;
- data += this_step;
- len -= this_step;
- } while (len);
-
- return 0;
-}
-
-/*
- * Wrapper for crypto_ahash_init, which handles verity salting.
- */
-static int verity_ahash_init(struct dm_verity *v, struct ahash_request *req,
- struct crypto_wait *wait, bool may_sleep)
-{
- int r;
-
- ahash_request_set_tfm(req, v->ahash_tfm);
- ahash_request_set_callback(req,
- may_sleep ? CRYPTO_TFM_REQ_MAY_SLEEP | CRYPTO_TFM_REQ_MAY_BACKLOG : 0,
- crypto_req_done, (void *)wait);
- crypto_init_wait(wait);
-
- r = crypto_wait_req(crypto_ahash_init(req), wait);
-
- if (unlikely(r < 0)) {
- if (r != -ENOMEM)
- DMERR("crypto_ahash_init failed: %d", r);
- return r;
- }
-
- if (likely(v->salt_size && (v->version >= 1)))
- r = verity_ahash_update(v, req, v->salt, v->salt_size, wait);
-
- return r;
-}
-
-static int verity_ahash_final(struct dm_verity *v, struct ahash_request *req,
- u8 *digest, struct crypto_wait *wait)
-{
- int r;
-
- if (unlikely(v->salt_size && (!v->version))) {
- r = verity_ahash_update(v, req, v->salt, v->salt_size, wait);
-
- if (r < 0) {
- DMERR("%s failed updating salt: %d", __func__, r);
- goto out;
- }
- }
-
- ahash_request_set_crypt(req, NULL, digest, 0);
- r = crypto_wait_req(crypto_ahash_final(req), wait);
-out:
- return r;
-}
-
int verity_hash(struct dm_verity *v, struct dm_verity_io *io,
- const u8 *data, size_t len, u8 *digest, bool may_sleep)
+ const u8 *data, size_t len, u8 *digest)
{
+ struct shash_desc *desc = &io->hash_desc;
int r;
- if (static_branch_unlikely(&ahash_enabled) && !v->shash_tfm) {
- struct ahash_request *req = verity_io_hash_req(v, io);
- struct crypto_wait wait;
-
- r = verity_ahash_init(v, req, &wait, may_sleep) ?:
- verity_ahash_update(v, req, data, len, &wait) ?:
- verity_ahash_final(v, req, digest, &wait);
+ desc->tfm = v->shash_tfm;
+ if (unlikely(v->initial_hashstate == NULL)) {
+ /* Version 0: salt at end */
+ r = crypto_shash_init(desc) ?:
+ crypto_shash_update(desc, data, len) ?:
+ crypto_shash_update(desc, v->salt, v->salt_size) ?:
+ crypto_shash_final(desc, digest);
} else {
- struct shash_desc *desc = verity_io_hash_req(v, io);
-
- desc->tfm = v->shash_tfm;
+ /* Version 1: salt at beginning */
r = crypto_shash_import(desc, v->initial_hashstate) ?:
crypto_shash_finup(desc, data, len, digest);
}
@@ -362,7 +279,7 @@ static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io,
}
r = verity_hash(v, io, data, 1 << v->hash_dev_block_bits,
- verity_io_real_digest(v, io), !io->in_bh);
+ verity_io_real_digest(v, io));
if (unlikely(r < 0))
goto release_ret_r;
@@ -465,7 +382,7 @@ static noinline int verity_recheck(struct dm_verity *v, struct dm_verity_io *io,
goto free_ret;
r = verity_hash(v, io, buffer, 1 << v->data_dev_block_bits,
- verity_io_real_digest(v, io), true);
+ verity_io_real_digest(v, io));
if (unlikely(r))
goto free_ret;
@@ -581,7 +498,7 @@ static int verity_verify_io(struct dm_verity_io *io)
}
r = verity_hash(v, io, data, block_size,
- verity_io_real_digest(v, io), !io->in_bh);
+ verity_io_real_digest(v, io));
if (unlikely(r < 0)) {
kunmap_local(data);
return r;
@@ -1092,12 +1009,7 @@ static void verity_dtr(struct dm_target *ti)
kfree(v->zero_digest);
verity_free_sig(v);
- if (v->ahash_tfm) {
- static_branch_dec(&ahash_enabled);
- crypto_free_ahash(v->ahash_tfm);
- } else {
- crypto_free_shash(v->shash_tfm);
- }
+ crypto_free_shash(v->shash_tfm);
kfree(v->alg_name);
@@ -1157,7 +1069,8 @@ static int verity_alloc_zero_digest(struct dm_verity *v)
if (!v->zero_digest)
return r;
- io = kmalloc(sizeof(*io) + v->hash_reqsize, GFP_KERNEL);
+ io = kmalloc(sizeof(*io) + crypto_shash_descsize(v->shash_tfm),
+ GFP_KERNEL);
if (!io)
return r; /* verity_dtr will free zero_digest */
@@ -1168,7 +1081,7 @@ static int verity_alloc_zero_digest(struct dm_verity *v)
goto out;
r = verity_hash(v, io, zero_data, 1 << v->data_dev_block_bits,
- v->zero_digest, true);
+ v->zero_digest);
out:
kfree(io);
@@ -1324,9 +1237,7 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v,
static int verity_setup_hash_alg(struct dm_verity *v, const char *alg_name)
{
struct dm_target *ti = v->ti;
- struct crypto_ahash *ahash;
- struct crypto_shash *shash = NULL;
- const char *driver_name;
+ struct crypto_shash *shash;
v->alg_name = kstrdup(alg_name, GFP_KERNEL);
if (!v->alg_name) {
@@ -1334,50 +1245,14 @@ static int verity_setup_hash_alg(struct dm_verity *v, const char *alg_name)
return -ENOMEM;
}
- /*
- * Allocate the hash transformation object that this dm-verity instance
- * will use. The vast majority of dm-verity users use CPU-based
- * hashing, so when possible use the shash API to minimize the crypto
- * API overhead. If the ahash API resolves to a different driver
- * (likely an off-CPU hardware offload), use ahash instead. Also use
- * ahash if the obsolete dm-verity format with the appended salt is
- * being used, so that quirk only needs to be handled in one place.
- */
- ahash = crypto_alloc_ahash(alg_name, 0,
- v->use_bh_wq ? CRYPTO_ALG_ASYNC : 0);
- if (IS_ERR(ahash)) {
+ shash = crypto_alloc_shash(alg_name, 0, 0);
+ if (IS_ERR(shash)) {
ti->error = "Cannot initialize hash function";
- return PTR_ERR(ahash);
- }
- driver_name = crypto_ahash_driver_name(ahash);
- if (v->version >= 1 /* salt prepended, not appended? */) {
- shash = crypto_alloc_shash(alg_name, 0, 0);
- if (!IS_ERR(shash) &&
- strcmp(crypto_shash_driver_name(shash), driver_name) != 0) {
- /*
- * ahash gave a different driver than shash, so probably
- * this is a case of real hardware offload. Use ahash.
- */
- crypto_free_shash(shash);
- shash = NULL;
- }
- }
- if (!IS_ERR_OR_NULL(shash)) {
- crypto_free_ahash(ahash);
- ahash = NULL;
- v->shash_tfm = shash;
- v->digest_size = crypto_shash_digestsize(shash);
- v->hash_reqsize = sizeof(struct shash_desc) +
- crypto_shash_descsize(shash);
- DMINFO("%s using shash \"%s\"", alg_name, driver_name);
- } else {
- v->ahash_tfm = ahash;
- static_branch_inc(&ahash_enabled);
- v->digest_size = crypto_ahash_digestsize(ahash);
- v->hash_reqsize = sizeof(struct ahash_request) +
- crypto_ahash_reqsize(ahash);
- DMINFO("%s using ahash \"%s\"", alg_name, driver_name);
+ return PTR_ERR(shash);
}
+ v->shash_tfm = shash;
+ v->digest_size = crypto_shash_digestsize(shash);
+ DMINFO("%s using \"%s\"", alg_name, crypto_shash_driver_name(shash));
if ((1 << v->hash_dev_block_bits) < v->digest_size * 2) {
ti->error = "Digest size too big";
return -EINVAL;
@@ -1402,7 +1277,7 @@ static int verity_setup_salt_and_hashstate(struct dm_verity *v, const char *arg)
return -EINVAL;
}
}
- if (v->shash_tfm) {
+ if (v->version) { /* Version 1: salt at beginning */
SHASH_DESC_ON_STACK(desc, v->shash_tfm);
int r;
@@ -1681,7 +1556,8 @@ static int verity_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto bad;
}
- ti->per_io_data_size = sizeof(struct dm_verity_io) + v->hash_reqsize;
+ ti->per_io_data_size = sizeof(struct dm_verity_io) +
+ crypto_shash_descsize(v->shash_tfm);
r = verity_fec_ctr(v);
if (r)
@@ -1788,10 +1664,7 @@ static int verity_preresume(struct dm_target *ti)
bdev = dm_disk(dm_table_get_md(ti->table))->part0;
root_digest.digest = v->root_digest;
root_digest.digest_len = v->digest_size;
- if (static_branch_unlikely(&ahash_enabled) && !v->shash_tfm)
- root_digest.alg = crypto_ahash_alg_name(v->ahash_tfm);
- else
- root_digest.alg = crypto_shash_alg_name(v->shash_tfm);
+ root_digest.alg = crypto_shash_alg_name(v->shash_tfm);
r = security_bdev_setintegrity(bdev, LSM_INT_DMVERITY_ROOTHASH, &root_digest,
sizeof(root_digest));
@@ -1817,7 +1690,7 @@ static struct target_type verity_target = {
.name = "verity",
/* Note: the LSMs depend on the singleton and immutable features */
.features = DM_TARGET_SINGLETON | DM_TARGET_IMMUTABLE,
- .version = {1, 11, 0},
+ .version = {1, 12, 0},
.module = THIS_MODULE,
.ctr = verity_ctr,
.dtr = verity_dtr,
diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h
index 8cbb57862ae1..6d141abd965c 100644
--- a/drivers/md/dm-verity.h
+++ b/drivers/md/dm-verity.h
@@ -39,11 +39,10 @@ struct dm_verity {
struct dm_target *ti;
struct dm_bufio_client *bufio;
char *alg_name;
- struct crypto_ahash *ahash_tfm; /* either this or shash_tfm is set */
- struct crypto_shash *shash_tfm; /* either this or ahash_tfm is set */
+ struct crypto_shash *shash_tfm;
u8 *root_digest; /* digest of the root block */
u8 *salt; /* salt: its size is salt_size */
- u8 *initial_hashstate; /* salted initial state, if shash_tfm is set */
+ u8 *initial_hashstate; /* salted initial state, if version >= 1 */
u8 *zero_digest; /* digest for a zero block */
#ifdef CONFIG_SECURITY
u8 *root_digest_sig; /* signature of the root digest */
@@ -61,7 +60,6 @@ struct dm_verity {
bool hash_failed:1; /* set if hash of any block failed */
bool use_bh_wq:1; /* try to verify in BH wq before normal work-queue */
unsigned int digest_size; /* digest size for the current hash algorithm */
- unsigned int hash_reqsize; /* the size of temporary space for crypto */
enum verity_mode mode; /* mode for handling verification errors */
enum verity_mode error_mode;/* mode for handling I/O errors */
unsigned int corrupted_errs;/* Number of errors for corrupted blocks */
@@ -100,19 +98,13 @@ struct dm_verity_io {
u8 want_digest[HASH_MAX_DIGESTSIZE];
/*
- * This struct is followed by a variable-sized hash request of size
- * v->hash_reqsize, either a struct ahash_request or a struct shash_desc
- * (depending on whether ahash_tfm or shash_tfm is being used). To
- * access it, use verity_io_hash_req().
+ * Temporary space for hashing. This is variable-length and must be at
+ * the end of the struct. struct shash_desc is just the fixed part;
+ * it's followed by a context of size crypto_shash_descsize(shash_tfm).
*/
+ struct shash_desc hash_desc;
};
-static inline void *verity_io_hash_req(struct dm_verity *v,
- struct dm_verity_io *io)
-{
- return io + 1;
-}
-
static inline u8 *verity_io_real_digest(struct dm_verity *v,
struct dm_verity_io *io)
{
@@ -126,7 +118,7 @@ static inline u8 *verity_io_want_digest(struct dm_verity *v,
}
extern int verity_hash(struct dm_verity *v, struct dm_verity_io *io,
- const u8 *data, size_t len, u8 *digest, bool may_sleep);
+ const u8 *data, size_t len, u8 *digest);
extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
sector_t block, u8 *digest, bool *is_zero);
diff --git a/drivers/md/dm-zone.c b/drivers/md/dm-zone.c
index 3d31b82e0730..78e17dd4d01b 100644
--- a/drivers/md/dm-zone.c
+++ b/drivers/md/dm-zone.c
@@ -467,8 +467,6 @@ void dm_zone_endio(struct dm_io *io, struct bio *clone)
bdev_offset_from_zone_start(disk->part0,
clone->bi_iter.bi_sector);
}
-
- return;
}
static int dm_zone_need_reset_cb(struct blk_zone *zone, unsigned int idx,
diff --git a/drivers/md/dm-zoned-target.c b/drivers/md/dm-zoned-target.c
index 5da3db06da10..9da329078ea4 100644
--- a/drivers/md/dm-zoned-target.c
+++ b/drivers/md/dm-zoned-target.c
@@ -1062,7 +1062,7 @@ static int dmz_iterate_devices(struct dm_target *ti,
struct dmz_target *dmz = ti->private;
unsigned int zone_nr_sectors = dmz_zone_nr_sectors(dmz->metadata);
sector_t capacity;
- int i, r;
+ int i, r = 0;
for (i = 0; i < dmz->nr_ddevs; i++) {
capacity = dmz->dev[i].capacity & ~(zone_nr_sectors - 1);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 2d8402778e5c..a44e8c2dccee 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1024,10 +1024,8 @@ static void dm_wq_requeue_work(struct work_struct *work)
*
* 2) io->orig_bio points to new cloned bio which matches the requeued dm_io.
*/
-static void dm_io_complete(struct dm_io *io)
+static inline void dm_io_complete(struct dm_io *io)
{
- bool first_requeue;
-
/*
* Only dm_io that has been split needs two stage requeue, otherwise
* we may run into long bio clone chain during suspend and OOM could
@@ -1036,12 +1034,7 @@ static void dm_io_complete(struct dm_io *io)
* Also flush data dm_io won't be marked as DM_IO_WAS_SPLIT, so they
* also aren't handled via the first stage requeue.
*/
- if (dm_io_flagged(io, DM_IO_WAS_SPLIT))
- first_requeue = true;
- else
- first_requeue = false;
-
- __dm_io_complete(io, first_requeue);
+ __dm_io_complete(io, dm_io_flagged(io, DM_IO_WAS_SPLIT));
}
/*
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index feab392ab2ee..476e73e502fe 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -4610,7 +4610,7 @@ static int mvneta_stop(struct net_device *dev)
/* Inform that we are stopping so we don't want to setup the
* driver for new CPUs in the notifiers. The code of the
* notifier for CPU online is protected by the same spinlock,
- * so when we get the lock, the notifer work is done.
+ * so when we get the lock, the notifier work is done.
*/
spin_lock(&pp->lock);
pp->is_stopped = true;
diff --git a/drivers/net/wwan/iosm/iosm_ipc_trace.c b/drivers/net/wwan/iosm/iosm_ipc_trace.c
index eeecfa3d10c5..9656254c1c6c 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_trace.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_trace.c
@@ -51,8 +51,7 @@ static int ipc_trace_remove_buf_file_handler(struct dentry *dentry)
}
static int ipc_trace_subbuf_start_handler(struct rchan_buf *buf, void *subbuf,
- void *prev_subbuf,
- size_t prev_padding)
+ void *prev_subbuf)
{
if (relay_buf_full(buf)) {
pr_err_ratelimited("Relay_buf full dropping traces");
diff --git a/drivers/net/wwan/t7xx/t7xx_port_trace.c b/drivers/net/wwan/t7xx/t7xx_port_trace.c
index 4ed8b4e29bf1..f16d3b01302c 100644
--- a/drivers/net/wwan/t7xx/t7xx_port_trace.c
+++ b/drivers/net/wwan/t7xx/t7xx_port_trace.c
@@ -33,7 +33,7 @@ static int t7xx_trace_remove_buf_file_handler(struct dentry *dentry)
}
static int t7xx_trace_subbuf_start_handler(struct rchan_buf *buf, void *subbuf,
- void *prev_subbuf, size_t prev_padding)
+ void *prev_subbuf)
{
if (relay_buf_full(buf)) {
pr_err_ratelimited("Relay_buf full dropping traces");
diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c
index 573a41869c15..c5345bff9a55 100644
--- a/drivers/pci/hotplug/pnv_php.c
+++ b/drivers/pci/hotplug/pnv_php.c
@@ -3,12 +3,15 @@
* PCI Hotplug Driver for PowerPC PowerNV platform.
*
* Copyright Gavin Shan, IBM Corporation 2016.
+ * Copyright (C) 2025 Raptor Engineering, LLC
+ * Copyright (C) 2025 Raptor Computing Systems, LLC
*/
#include <linux/bitfield.h>
#include <linux/libfdt.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/delay.h>
#include <linux/pci_hotplug.h>
#include <linux/of_fdt.h>
@@ -36,8 +39,10 @@ static void pnv_php_register(struct device_node *dn);
static void pnv_php_unregister_one(struct device_node *dn);
static void pnv_php_unregister(struct device_node *dn);
+static void pnv_php_enable_irq(struct pnv_php_slot *php_slot);
+
static void pnv_php_disable_irq(struct pnv_php_slot *php_slot,
- bool disable_device)
+ bool disable_device, bool disable_msi)
{
struct pci_dev *pdev = php_slot->pdev;
u16 ctrl;
@@ -53,19 +58,15 @@ static void pnv_php_disable_irq(struct pnv_php_slot *php_slot,
php_slot->irq = 0;
}
- if (php_slot->wq) {
- destroy_workqueue(php_slot->wq);
- php_slot->wq = NULL;
- }
-
- if (disable_device) {
+ if (disable_device || disable_msi) {
if (pdev->msix_enabled)
pci_disable_msix(pdev);
else if (pdev->msi_enabled)
pci_disable_msi(pdev);
+ }
+ if (disable_device)
pci_disable_device(pdev);
- }
}
static void pnv_php_free_slot(struct kref *kref)
@@ -74,7 +75,8 @@ static void pnv_php_free_slot(struct kref *kref)
struct pnv_php_slot, kref);
WARN_ON(!list_empty(&php_slot->children));
- pnv_php_disable_irq(php_slot, false);
+ pnv_php_disable_irq(php_slot, false, false);
+ destroy_workqueue(php_slot->wq);
kfree(php_slot->name);
kfree(php_slot);
}
@@ -391,6 +393,20 @@ static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state)
return 0;
}
+static int pcie_check_link_active(struct pci_dev *pdev)
+{
+ u16 lnk_status;
+ int ret;
+
+ ret = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
+ if (ret == PCIBIOS_DEVICE_NOT_FOUND || PCI_POSSIBLE_ERROR(lnk_status))
+ return -ENODEV;
+
+ ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA);
+
+ return ret;
+}
+
static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state)
{
struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
@@ -403,6 +419,19 @@ static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state)
*/
ret = pnv_pci_get_presence_state(php_slot->id, &presence);
if (ret >= 0) {
+ if (pci_pcie_type(php_slot->pdev) == PCI_EXP_TYPE_DOWNSTREAM &&
+ presence == OPAL_PCI_SLOT_EMPTY) {
+ /*
+ * Similar to pciehp_hpc, check whether the Link Active
+ * bit is set to account for broken downstream bridges
+ * that don't properly assert Presence Detect State, as
+ * was observed on the Microsemi Switchtec PM8533 PFX
+ * [11f8:8533].
+ */
+ if (pcie_check_link_active(php_slot->pdev) > 0)
+ presence = OPAL_PCI_SLOT_PRESENT;
+ }
+
*state = presence;
ret = 0;
} else {
@@ -412,10 +441,23 @@ static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state)
return ret;
}
+static int pnv_php_get_raw_indicator_status(struct hotplug_slot *slot, u8 *state)
+{
+ struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
+ struct pci_dev *bridge = php_slot->pdev;
+ u16 status;
+
+ pcie_capability_read_word(bridge, PCI_EXP_SLTCTL, &status);
+ *state = (status & (PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC)) >> 6;
+ return 0;
+}
+
+
static int pnv_php_get_attention_state(struct hotplug_slot *slot, u8 *state)
{
struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
+ pnv_php_get_raw_indicator_status(slot, &php_slot->attention_state);
*state = php_slot->attention_state;
return 0;
}
@@ -433,7 +475,7 @@ static int pnv_php_set_attention_state(struct hotplug_slot *slot, u8 state)
mask = PCI_EXP_SLTCTL_AIC;
if (state)
- new = PCI_EXP_SLTCTL_ATTN_IND_ON;
+ new = FIELD_PREP(PCI_EXP_SLTCTL_AIC, state);
else
new = PCI_EXP_SLTCTL_ATTN_IND_OFF;
@@ -442,6 +484,61 @@ static int pnv_php_set_attention_state(struct hotplug_slot *slot, u8 state)
return 0;
}
+static int pnv_php_activate_slot(struct pnv_php_slot *php_slot,
+ struct hotplug_slot *slot)
+{
+ int ret, i;
+
+ /*
+ * Issue initial slot activation command to firmware
+ *
+ * Firmware will power slot on, attempt to train the link, and
+ * discover any downstream devices. If this process fails, firmware
+ * will return an error code and an invalid device tree. Failure
+ * can be caused for multiple reasons, including a faulty
+ * downstream device, poor connection to the downstream device, or
+ * a previously latched PHB fence. On failure, issue fundamental
+ * reset up to three times before aborting.
+ */
+ ret = pnv_php_set_slot_power_state(slot, OPAL_PCI_SLOT_POWER_ON);
+ if (ret) {
+ SLOT_WARN(
+ php_slot,
+ "PCI slot activation failed with error code %d, possible frozen PHB",
+ ret);
+ SLOT_WARN(
+ php_slot,
+ "Attempting complete PHB reset before retrying slot activation\n");
+ for (i = 0; i < 3; i++) {
+ /*
+ * Slot activation failed, PHB may be fenced from a
+ * prior device failure.
+ *
+ * Use the OPAL fundamental reset call to both try a
+ * device reset and clear any potentially active PHB
+ * fence / freeze.
+ */
+ SLOT_WARN(php_slot, "Try %d...\n", i + 1);
+ pci_set_pcie_reset_state(php_slot->pdev,
+ pcie_warm_reset);
+ msleep(250);
+ pci_set_pcie_reset_state(php_slot->pdev,
+ pcie_deassert_reset);
+
+ ret = pnv_php_set_slot_power_state(
+ slot, OPAL_PCI_SLOT_POWER_ON);
+ if (!ret)
+ break;
+ }
+
+ if (i >= 3)
+ SLOT_WARN(php_slot,
+ "Failed to bring slot online, aborting!\n");
+ }
+
+ return ret;
+}
+
static int pnv_php_enable(struct pnv_php_slot *php_slot, bool rescan)
{
struct hotplug_slot *slot = &php_slot->slot;
@@ -504,7 +601,7 @@ static int pnv_php_enable(struct pnv_php_slot *php_slot, bool rescan)
goto scan;
/* Power is off, turn it on and then scan the slot */
- ret = pnv_php_set_slot_power_state(slot, OPAL_PCI_SLOT_POWER_ON);
+ ret = pnv_php_activate_slot(php_slot, slot);
if (ret)
return ret;
@@ -561,8 +658,58 @@ static int pnv_php_reset_slot(struct hotplug_slot *slot, bool probe)
static int pnv_php_enable_slot(struct hotplug_slot *slot)
{
struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
+ u32 prop32;
+ int ret;
+
+ ret = pnv_php_enable(php_slot, true);
+ if (ret)
+ return ret;
+
+ /* (Re-)enable interrupt if the slot supports surprise hotplug */
+ ret = of_property_read_u32(php_slot->dn, "ibm,slot-surprise-pluggable",
+ &prop32);
+ if (!ret && prop32)
+ pnv_php_enable_irq(php_slot);
+
+ return 0;
+}
+
+/*
+ * Disable any hotplug interrupts for all slots on the provided bus, as well as
+ * all downstream slots in preparation for a hot unplug.
+ */
+static int pnv_php_disable_all_irqs(struct pci_bus *bus)
+{
+ struct pci_bus *child_bus;
+ struct pci_slot *slot;
+
+ /* First go down child buses */
+ list_for_each_entry(child_bus, &bus->children, node)
+ pnv_php_disable_all_irqs(child_bus);
+
+ /* Disable IRQs for all pnv_php slots on this bus */
+ list_for_each_entry(slot, &bus->slots, list) {
+ struct pnv_php_slot *php_slot = to_pnv_php_slot(slot->hotplug);
- return pnv_php_enable(php_slot, true);
+ pnv_php_disable_irq(php_slot, false, true);
+ }
+
+ return 0;
+}
+
+/*
+ * Disable any hotplug interrupts for all downstream slots on the provided
+ * bus in preparation for a hot unplug.
+ */
+static int pnv_php_disable_all_downstream_irqs(struct pci_bus *bus)
+{
+ struct pci_bus *child_bus;
+
+ /* Go down child buses, recursively deactivating their IRQs */
+ list_for_each_entry(child_bus, &bus->children, node)
+ pnv_php_disable_all_irqs(child_bus);
+
+ return 0;
}
static int pnv_php_disable_slot(struct hotplug_slot *slot)
@@ -579,6 +726,13 @@ static int pnv_php_disable_slot(struct hotplug_slot *slot)
php_slot->state != PNV_PHP_STATE_REGISTERED)
return 0;
+ /*
+ * Free all IRQ resources from all child slots before remove.
+ * Note that we do not disable the root slot IRQ here as that
+ * would also deactivate the slot hot (re)plug interrupt!
+ */
+ pnv_php_disable_all_downstream_irqs(php_slot->bus);
+
/* Remove all devices behind the slot */
pci_lock_rescan_remove();
pci_hp_remove_devices(php_slot->bus);
@@ -647,6 +801,15 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn)
return NULL;
}
+ /* Allocate workqueue for this slot's interrupt handling */
+ php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name);
+ if (!php_slot->wq) {
+ SLOT_WARN(php_slot, "Cannot alloc workqueue\n");
+ kfree(php_slot->name);
+ kfree(php_slot);
+ return NULL;
+ }
+
if (dn->child && PCI_DN(dn->child))
php_slot->slot_no = PCI_SLOT(PCI_DN(dn->child)->devfn);
else
@@ -745,16 +908,63 @@ static int pnv_php_enable_msix(struct pnv_php_slot *php_slot)
return entry.vector;
}
+static void
+pnv_php_detect_clear_suprise_removal_freeze(struct pnv_php_slot *php_slot)
+{
+ struct pci_dev *pdev = php_slot->pdev;
+ struct eeh_dev *edev;
+ struct eeh_pe *pe;
+ int i, rc;
+
+ /*
+ * When a device is surprise removed from a downstream bridge slot,
+ * the upstream bridge port can still end up frozen due to related EEH
+ * events, which will in turn block the MSI interrupts for slot hotplug
+ * detection.
+ *
+ * Detect and thaw any frozen upstream PE after slot deactivation.
+ */
+ edev = pci_dev_to_eeh_dev(pdev);
+ pe = edev ? edev->pe : NULL;
+ rc = eeh_pe_get_state(pe);
+ if ((rc == -ENODEV) || (rc == -ENOENT)) {
+ SLOT_WARN(
+ php_slot,
+ "Upstream bridge PE state unknown, hotplug detect may fail\n");
+ } else {
+ if (pe->state & EEH_PE_ISOLATED) {
+ SLOT_WARN(
+ php_slot,
+ "Upstream bridge PE %02x frozen, thawing...\n",
+ pe->addr);
+ for (i = 0; i < 3; i++)
+ if (!eeh_unfreeze_pe(pe))
+ break;
+ if (i >= 3)
+ SLOT_WARN(
+ php_slot,
+ "Unable to thaw PE %02x, hotplug detect will fail!\n",
+ pe->addr);
+ else
+ SLOT_WARN(php_slot,
+ "PE %02x thawed successfully\n",
+ pe->addr);
+ }
+ }
+}
+
static void pnv_php_event_handler(struct work_struct *work)
{
struct pnv_php_event *event =
container_of(work, struct pnv_php_event, work);
struct pnv_php_slot *php_slot = event->php_slot;
- if (event->added)
+ if (event->added) {
pnv_php_enable_slot(&php_slot->slot);
- else
+ } else {
pnv_php_disable_slot(&php_slot->slot);
+ pnv_php_detect_clear_suprise_removal_freeze(php_slot);
+ }
kfree(event);
}
@@ -843,14 +1053,6 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
u16 sts, ctrl;
int ret;
- /* Allocate workqueue */
- php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name);
- if (!php_slot->wq) {
- SLOT_WARN(php_slot, "Cannot alloc workqueue\n");
- pnv_php_disable_irq(php_slot, true);
- return;
- }
-
/* Check PDC (Presence Detection Change) is broken or not */
ret = of_property_read_u32(php_slot->dn, "ibm,slot-broken-pdc",
&broken_pdc);
@@ -869,7 +1071,7 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
ret = request_irq(irq, pnv_php_interrupt, IRQF_SHARED,
php_slot->name, php_slot);
if (ret) {
- pnv_php_disable_irq(php_slot, true);
+ pnv_php_disable_irq(php_slot, true, true);
SLOT_WARN(php_slot, "Error %d enabling IRQ %d\n", ret, irq);
return;
}
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 9aec922613ce..64f6e9756aff 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -483,15 +483,6 @@ config RTC_DRV_PCF8523
This driver can also be built as a module. If so, the module
will be called rtc-pcf8523.
-config RTC_DRV_PCF85063
- tristate "NXP PCF85063"
- select REGMAP_I2C
- help
- If you say yes here you get support for the PCF85063 RTC chip
-
- This driver can also be built as a module. If so, the module
- will be called rtc-pcf85063.
-
config RTC_DRV_PCF85363
tristate "NXP PCF85363"
select REGMAP_I2C
@@ -971,6 +962,18 @@ config RTC_DRV_PCF2127
This driver can also be built as a module. If so, the module
will be called rtc-pcf2127.
+config RTC_DRV_PCF85063
+ tristate "NXP PCF85063"
+ depends on RTC_I2C_AND_SPI
+ select REGMAP_I2C if I2C
+ select REGMAP_SPI if SPI_MASTER
+ help
+ If you say yes here you get support for the PCF85063 and RV8063
+ RTC chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-pcf85063.
+
config RTC_DRV_RV3029C2
tristate "Micro Crystal RV3029/3049"
depends on RTC_I2C_AND_SPI
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 4619aa2ac469..789bddfea99d 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -15,7 +15,7 @@ rtc-core-$(CONFIG_RTC_INTF_DEV) += dev.o
rtc-core-$(CONFIG_RTC_INTF_PROC) += proc.o
rtc-core-$(CONFIG_RTC_INTF_SYSFS) += sysfs.o
-obj-$(CONFIG_RTC_LIB_KUNIT_TEST) += lib_test.o
+obj-$(CONFIG_RTC_LIB_KUNIT_TEST) += test_rtc_lib.o
# Keep the list ordered.
diff --git a/drivers/rtc/lib.c b/drivers/rtc/lib.c
index 13b5b1f20465..f7051592a6e3 100644
--- a/drivers/rtc/lib.c
+++ b/drivers/rtc/lib.c
@@ -51,7 +51,7 @@ EXPORT_SYMBOL(rtc_year_days);
*/
void rtc_time64_to_tm(time64_t time, struct rtc_time *tm)
{
- int days, secs;
+ int secs;
u64 u64tmp;
u32 u32tmp, udays, century, day_of_century, year_of_century, year,
@@ -59,28 +59,26 @@ void rtc_time64_to_tm(time64_t time, struct rtc_time *tm)
bool is_Jan_or_Feb, is_leap_year;
/*
- * Get days and seconds while preserving the sign to
- * handle negative time values (dates before 1970-01-01)
+ * The time represented by `time` is given in seconds since 1970-01-01
+ * (UTC). As the division done below might misbehave for negative
+ * values, we convert it to seconds since 0000-03-01 and then assume it
+ * will be non-negative.
+ * Below we do 4 * udays + 3 which should fit into a 32 bit unsigned
+ * variable. So the latest date this algorithm works for is 1073741823
+ * days after 0000-03-01 which is in the year 2939805.
*/
- days = div_s64_rem(time, 86400, &secs);
+ time += (u64)719468 * 86400;
+
+ udays = div_s64_rem(time, 86400, &secs);
/*
- * We need 0 <= secs < 86400 which isn't given for negative
- * values of time. Fixup accordingly.
+ * day of the week, 0000-03-01 was a Wednesday (in the proleptic
+ * Gregorian calendar)
*/
- if (secs < 0) {
- days -= 1;
- secs += 86400;
- }
-
- /* day of the week, 1970-01-01 was a Thursday */
- tm->tm_wday = (days + 4) % 7;
- /* Ensure tm_wday is always positive */
- if (tm->tm_wday < 0)
- tm->tm_wday += 7;
+ tm->tm_wday = (udays + 3) % 7;
/*
- * The following algorithm is, basically, Proposition 6.3 of Neri
+ * The following algorithm is, basically, Figure 12 of Neri
* and Schneider [1]. In a few words: it works on the computational
* (fictitious) calendar where the year starts in March, month = 2
* (*), and finishes in February, month = 13. This calendar is
@@ -100,15 +98,15 @@ void rtc_time64_to_tm(time64_t time, struct rtc_time *tm)
* (using just arithmetics) it's easy to convert it to the
* corresponding date in the Gregorian calendar.
*
- * [1] "Euclidean Affine Functions and Applications to Calendar
- * Algorithms". https://arxiv.org/abs/2102.06959
+ * [1] Neri C, Schneider L. Euclidean affine functions and their
+ * application to calendar algorithms. Softw Pract Exper.
+ * 2023;53(4):937-970. doi: 10.1002/spe.3172
+ * https://doi.org/10.1002/spe.3172
*
* (*) The numbering of months follows rtc_time more closely and
* thus, is slightly different from [1].
*/
- udays = days + 719468;
-
u32tmp = 4 * udays + 3;
century = u32tmp / 146097;
day_of_century = u32tmp % 146097 / 4;
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 5efbe69bf5ca..7205c59ff729 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -279,6 +279,13 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
if (tmp & DS1340_BIT_OSF)
return -EINVAL;
break;
+ case ds_1341:
+ ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp);
+ if (ret)
+ return ret;
+ if (tmp & DS1337_BIT_OSF)
+ return -EINVAL;
+ break;
case ds_1388:
ret = regmap_read(ds1307->regmap, DS1388_REG_FLAG, &tmp);
if (ret)
@@ -377,6 +384,10 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG,
DS1340_BIT_OSF, 0);
break;
+ case ds_1341:
+ regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS,
+ DS1337_BIT_OSF, 0);
+ break;
case ds_1388:
regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG,
DS1388_BIT_OSF, 0);
@@ -1456,16 +1467,21 @@ static unsigned long ds3231_clk_sqw_recalc_rate(struct clk_hw *hw,
return ds3231_clk_sqw_rates[rate_sel];
}
-static long ds3231_clk_sqw_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int ds3231_clk_sqw_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
int i;
for (i = ARRAY_SIZE(ds3231_clk_sqw_rates) - 1; i >= 0; i--) {
- if (ds3231_clk_sqw_rates[i] <= rate)
- return ds3231_clk_sqw_rates[i];
+ if (ds3231_clk_sqw_rates[i] <= req->rate) {
+ req->rate = ds3231_clk_sqw_rates[i];
+
+ return 0;
+ }
}
+ req->rate = ds3231_clk_sqw_rates[ARRAY_SIZE(ds3231_clk_sqw_rates) - 1];
+
return 0;
}
@@ -1525,7 +1541,7 @@ static const struct clk_ops ds3231_clk_sqw_ops = {
.unprepare = ds3231_clk_sqw_unprepare,
.is_prepared = ds3231_clk_sqw_is_prepared,
.recalc_rate = ds3231_clk_sqw_recalc_rate,
- .round_rate = ds3231_clk_sqw_round_rate,
+ .determine_rate = ds3231_clk_sqw_determine_rate,
.set_rate = ds3231_clk_sqw_set_rate,
};
@@ -1813,10 +1829,8 @@ static int ds1307_probe(struct i2c_client *client)
regmap_write(ds1307->regmap, DS1337_REG_CONTROL,
regs[0]);
- /* oscillator fault? clear flag, and warn */
+ /* oscillator fault? warn */
if (regs[1] & DS1337_BIT_OSF) {
- regmap_write(ds1307->regmap, DS1337_REG_STATUS,
- regs[1] & ~DS1337_BIT_OSF);
dev_warn(ds1307->dev, "SET TIME!\n");
}
break;
diff --git a/drivers/rtc/rtc-ds1685.c b/drivers/rtc/rtc-ds1685.c
index 38e25f63597a..97423f1d0361 100644
--- a/drivers/rtc/rtc-ds1685.c
+++ b/drivers/rtc/rtc-ds1685.c
@@ -3,7 +3,7 @@
* An rtc driver for the Dallas/Maxim DS1685/DS1687 and related real-time
* chips.
*
- * Copyright (C) 2011-2014 Joshua Kinard <kumba@gentoo.org>.
+ * Copyright (C) 2011-2014 Joshua Kinard <linux@kumba.dev>.
* Copyright (C) 2009 Matthias Fuchs <matthias.fuchs@esd-electronics.com>.
*
* References:
@@ -1436,7 +1436,7 @@ EXPORT_SYMBOL_GPL(ds1685_rtc_poweroff);
/* ----------------------------------------------------------------------- */
-MODULE_AUTHOR("Joshua Kinard <kumba@gentoo.org>");
+MODULE_AUTHOR("Joshua Kinard <linux@kumba.dev>");
MODULE_AUTHOR("Matthias Fuchs <matthias.fuchs@esd-electronics.com>");
MODULE_DESCRIPTION("Dallas/Maxim DS1685/DS1687-series RTC driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c
index 63f11ea3589d..7a170c0f9710 100644
--- a/drivers/rtc/rtc-hym8563.c
+++ b/drivers/rtc/rtc-hym8563.c
@@ -285,14 +285,19 @@ static unsigned long hym8563_clkout_recalc_rate(struct clk_hw *hw,
return clkout_rates[ret];
}
-static long hym8563_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int hym8563_clkout_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
int i;
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
- if (clkout_rates[i] <= rate)
- return clkout_rates[i];
+ if (clkout_rates[i] <= req->rate) {
+ req->rate = clkout_rates[i];
+
+ return 0;
+ }
+
+ req->rate = clkout_rates[0];
return 0;
}
@@ -363,7 +368,7 @@ static const struct clk_ops hym8563_clkout_ops = {
.unprepare = hym8563_clkout_unprepare,
.is_prepared = hym8563_clkout_is_prepared,
.recalc_rate = hym8563_clkout_recalc_rate,
- .round_rate = hym8563_clkout_round_rate,
+ .determine_rate = hym8563_clkout_determine_rate,
.set_rate = hym8563_clkout_set_rate,
};
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index c568639d2151..740cab013f59 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -72,7 +72,7 @@
static const struct i2c_device_id m41t80_id[] = {
{ "m41t62", M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT },
- { "m41t65", M41T80_FEATURE_HT | M41T80_FEATURE_WD },
+ { "m41t65", M41T80_FEATURE_WD },
{ "m41t80", M41T80_FEATURE_SQ },
{ "m41t81", M41T80_FEATURE_HT | M41T80_FEATURE_SQ},
{ "m41t81s", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
@@ -93,7 +93,7 @@ static const __maybe_unused struct of_device_id m41t80_of_match[] = {
},
{
.compatible = "st,m41t65",
- .data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_WD)
+ .data = (void *)(M41T80_FEATURE_WD)
},
{
.compatible = "st,m41t80",
@@ -484,16 +484,17 @@ static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw,
return sqw_to_m41t80_data(hw)->freq;
}
-static long m41t80_sqw_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int m41t80_sqw_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- if (rate >= M41T80_SQW_MAX_FREQ)
- return M41T80_SQW_MAX_FREQ;
- if (rate >= M41T80_SQW_MAX_FREQ / 4)
- return M41T80_SQW_MAX_FREQ / 4;
- if (!rate)
- return 0;
- return 1 << ilog2(rate);
+ if (req->rate >= M41T80_SQW_MAX_FREQ)
+ req->rate = M41T80_SQW_MAX_FREQ;
+ else if (req->rate >= M41T80_SQW_MAX_FREQ / 4)
+ req->rate = M41T80_SQW_MAX_FREQ / 4;
+ else if (req->rate)
+ req->rate = 1 << ilog2(req->rate);
+
+ return 0;
}
static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -564,7 +565,7 @@ static const struct clk_ops m41t80_sqw_ops = {
.unprepare = m41t80_sqw_unprepare,
.is_prepared = m41t80_sqw_is_prepared,
.recalc_rate = m41t80_sqw_recalc_rate,
- .round_rate = m41t80_sqw_round_rate,
+ .determine_rate = m41t80_sqw_determine_rate,
.set_rate = m41t80_sqw_set_rate,
};
diff --git a/drivers/rtc/rtc-max31335.c b/drivers/rtc/rtc-max31335.c
index a7bb37aaab9e..dfb5bad3a369 100644
--- a/drivers/rtc/rtc-max31335.c
+++ b/drivers/rtc/rtc-max31335.c
@@ -497,15 +497,17 @@ static unsigned long max31335_clkout_recalc_rate(struct clk_hw *hw,
return max31335_clkout_freq[reg & freq_mask];
}
-static long max31335_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int max31335_clkout_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
int index;
- index = find_closest(rate, max31335_clkout_freq,
+ index = find_closest(req->rate, max31335_clkout_freq,
ARRAY_SIZE(max31335_clkout_freq));
- return max31335_clkout_freq[index];
+ req->rate = max31335_clkout_freq[index];
+
+ return 0;
}
static int max31335_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -554,7 +556,7 @@ static int max31335_clkout_is_enabled(struct clk_hw *hw)
static const struct clk_ops max31335_clkout_ops = {
.recalc_rate = max31335_clkout_recalc_rate,
- .round_rate = max31335_clkout_round_rate,
+ .determine_rate = max31335_clkout_determine_rate,
.set_rate = max31335_clkout_set_rate,
.enable = max31335_clkout_enable,
.disable = max31335_clkout_disable,
diff --git a/drivers/rtc/rtc-nct3018y.c b/drivers/rtc/rtc-nct3018y.c
index 76c5f464b2da..cd4b1db902e9 100644
--- a/drivers/rtc/rtc-nct3018y.c
+++ b/drivers/rtc/rtc-nct3018y.c
@@ -367,14 +367,19 @@ static unsigned long nct3018y_clkout_recalc_rate(struct clk_hw *hw,
return clkout_rates[flags];
}
-static long nct3018y_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int nct3018y_clkout_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
int i;
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
- if (clkout_rates[i] <= rate)
- return clkout_rates[i];
+ if (clkout_rates[i] <= req->rate) {
+ req->rate = clkout_rates[i];
+
+ return 0;
+ }
+
+ req->rate = clkout_rates[0];
return 0;
}
@@ -446,7 +451,7 @@ static const struct clk_ops nct3018y_clkout_ops = {
.unprepare = nct3018y_clkout_unprepare,
.is_prepared = nct3018y_clkout_is_prepared,
.recalc_rate = nct3018y_clkout_recalc_rate,
- .round_rate = nct3018y_clkout_round_rate,
+ .determine_rate = nct3018y_clkout_determine_rate,
.set_rate = nct3018y_clkout_set_rate,
};
diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c
index 4fa5c4ecdd5a..f643e0bd7351 100644
--- a/drivers/rtc/rtc-pcf85063.c
+++ b/drivers/rtc/rtc-pcf85063.c
@@ -17,6 +17,7 @@
#include <linux/of.h>
#include <linux/pm_wakeirq.h>
#include <linux/regmap.h>
+#include <linux/spi/spi.h>
/*
* Information for this driver was pulled from the following datasheets.
@@ -29,6 +30,9 @@
*
* https://www.microcrystal.com/fileadmin/Media/Products/RTC/App.Manual/RV-8263-C7_App-Manual.pdf
* RV8263 -- Rev. 1.0 — January 2019
+ *
+ * https://www.microcrystal.com/fileadmin/Media/Products/RTC/App.Manual/RV-8063-C7_App-Manual.pdf
+ * RV8063 -- Rev. 1.1 - October 2018
*/
#define PCF85063_REG_CTRL1 0x00 /* status */
@@ -401,14 +405,19 @@ static unsigned long pcf85063_clkout_recalc_rate(struct clk_hw *hw,
return clkout_rates[buf];
}
-static long pcf85063_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int pcf85063_clkout_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
int i;
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
- if (clkout_rates[i] <= rate)
- return clkout_rates[i];
+ if (clkout_rates[i] <= req->rate) {
+ req->rate = clkout_rates[i];
+
+ return 0;
+ }
+
+ req->rate = clkout_rates[0];
return 0;
}
@@ -482,7 +491,7 @@ static const struct clk_ops pcf85063_clkout_ops = {
.unprepare = pcf85063_clkout_unprepare,
.is_prepared = pcf85063_clkout_is_prepared,
.recalc_rate = pcf85063_clkout_recalc_rate,
- .round_rate = pcf85063_clkout_round_rate,
+ .determine_rate = pcf85063_clkout_determine_rate,
.set_rate = pcf85063_clkout_set_rate,
};
@@ -524,47 +533,12 @@ static struct clk *pcf85063_clkout_register_clk(struct pcf85063 *pcf85063)
}
#endif
-static const struct pcf85063_config config_pcf85063 = {
- .regmap = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = 0x0a,
- },
-};
-
-static const struct pcf85063_config config_pcf85063tp = {
- .regmap = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = 0x0a,
- },
-};
-
-static const struct pcf85063_config config_pcf85063a = {
- .regmap = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = 0x11,
- },
- .has_alarms = 1,
-};
-
-static const struct pcf85063_config config_rv8263 = {
- .regmap = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = 0x11,
- },
- .has_alarms = 1,
- .force_cap_7000 = 1,
-};
-
-static int pcf85063_probe(struct i2c_client *client)
+static int pcf85063_probe(struct device *dev, struct regmap *regmap, int irq,
+ const struct pcf85063_config *config)
{
struct pcf85063 *pcf85063;
unsigned int tmp;
int err;
- const struct pcf85063_config *config;
struct nvmem_config nvmem_cfg = {
.name = "pcf85063_nvram",
.reg_read = pcf85063_nvmem_read,
@@ -573,28 +547,22 @@ static int pcf85063_probe(struct i2c_client *client)
.size = 1,
};
- dev_dbg(&client->dev, "%s\n", __func__);
+ dev_dbg(dev, "%s\n", __func__);
- pcf85063 = devm_kzalloc(&client->dev, sizeof(struct pcf85063),
+ pcf85063 = devm_kzalloc(dev, sizeof(struct pcf85063),
GFP_KERNEL);
if (!pcf85063)
return -ENOMEM;
- config = i2c_get_match_data(client);
- if (!config)
- return -ENODEV;
-
- pcf85063->regmap = devm_regmap_init_i2c(client, &config->regmap);
- if (IS_ERR(pcf85063->regmap))
- return PTR_ERR(pcf85063->regmap);
+ pcf85063->regmap = regmap;
- i2c_set_clientdata(client, pcf85063);
+ dev_set_drvdata(dev, pcf85063);
err = regmap_read(pcf85063->regmap, PCF85063_REG_SC, &tmp);
if (err)
- return dev_err_probe(&client->dev, err, "RTC chip is not present\n");
+ return dev_err_probe(dev, err, "RTC chip is not present\n");
- pcf85063->rtc = devm_rtc_allocate_device(&client->dev);
+ pcf85063->rtc = devm_rtc_allocate_device(dev);
if (IS_ERR(pcf85063->rtc))
return PTR_ERR(pcf85063->rtc);
@@ -605,19 +573,17 @@ static int pcf85063_probe(struct i2c_client *client)
* of the registers after the automatic power-on reset...
*/
if (tmp & PCF85063_REG_SC_OS) {
- dev_warn(&client->dev,
- "POR issue detected, sending a SW reset\n");
+ dev_warn(dev, "POR issue detected, sending a SW reset\n");
err = regmap_write(pcf85063->regmap, PCF85063_REG_CTRL1,
PCF85063_REG_CTRL1_SWR);
if (err < 0)
- dev_warn(&client->dev,
- "SW reset failed, trying to continue\n");
+ dev_warn(dev, "SW reset failed, trying to continue\n");
}
- err = pcf85063_load_capacitance(pcf85063, client->dev.of_node,
+ err = pcf85063_load_capacitance(pcf85063, dev->of_node,
config->force_cap_7000 ? 7000 : 0);
if (err < 0)
- dev_warn(&client->dev, "failed to set xtal load capacitance: %d",
+ dev_warn(dev, "failed to set xtal load capacitance: %d",
err);
pcf85063->rtc->ops = &pcf85063_rtc_ops;
@@ -627,13 +593,13 @@ static int pcf85063_probe(struct i2c_client *client)
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf85063->rtc->features);
clear_bit(RTC_FEATURE_ALARM, pcf85063->rtc->features);
- if (config->has_alarms && client->irq > 0) {
+ if (config->has_alarms && irq > 0) {
unsigned long irqflags = IRQF_TRIGGER_LOW;
- if (dev_fwnode(&client->dev))
+ if (dev_fwnode(dev))
irqflags = 0;
- err = devm_request_threaded_irq(&client->dev, client->irq,
+ err = devm_request_threaded_irq(dev, irq,
NULL, pcf85063_rtc_handle_irq,
irqflags | IRQF_ONESHOT,
"pcf85063", pcf85063);
@@ -642,8 +608,8 @@ static int pcf85063_probe(struct i2c_client *client)
"unable to request IRQ, alarms disabled\n");
} else {
set_bit(RTC_FEATURE_ALARM, pcf85063->rtc->features);
- device_init_wakeup(&client->dev, true);
- err = dev_pm_set_wake_irq(&client->dev, client->irq);
+ device_init_wakeup(dev, true);
+ err = dev_pm_set_wake_irq(dev, irq);
if (err)
dev_err(&pcf85063->rtc->dev,
"failed to enable irq wake\n");
@@ -661,6 +627,43 @@ static int pcf85063_probe(struct i2c_client *client)
return devm_rtc_register_device(pcf85063->rtc);
}
+#if IS_ENABLED(CONFIG_I2C)
+
+static const struct pcf85063_config config_pcf85063 = {
+ .regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x0a,
+ },
+};
+
+static const struct pcf85063_config config_pcf85063tp = {
+ .regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x0a,
+ },
+};
+
+static const struct pcf85063_config config_pcf85063a = {
+ .regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x11,
+ },
+ .has_alarms = 1,
+};
+
+static const struct pcf85063_config config_rv8263 = {
+ .regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x11,
+ },
+ .has_alarms = 1,
+ .force_cap_7000 = 1,
+};
+
static const struct i2c_device_id pcf85063_ids[] = {
{ "pca85073a", .driver_data = (kernel_ulong_t)&config_pcf85063a },
{ "pcf85063", .driver_data = (kernel_ulong_t)&config_pcf85063 },
@@ -683,16 +686,146 @@ static const struct of_device_id pcf85063_of_match[] = {
MODULE_DEVICE_TABLE(of, pcf85063_of_match);
#endif
+static int pcf85063_i2c_probe(struct i2c_client *client)
+{
+ const struct pcf85063_config *config;
+ struct regmap *regmap;
+
+ config = i2c_get_match_data(client);
+ if (!config)
+ return -ENODEV;
+
+ regmap = devm_regmap_init_i2c(client, &config->regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return pcf85063_probe(&client->dev, regmap, client->irq, config);
+}
+
static struct i2c_driver pcf85063_driver = {
.driver = {
.name = "rtc-pcf85063",
.of_match_table = of_match_ptr(pcf85063_of_match),
},
- .probe = pcf85063_probe,
+ .probe = pcf85063_i2c_probe,
.id_table = pcf85063_ids,
};
-module_i2c_driver(pcf85063_driver);
+static int pcf85063_register_driver(void)
+{
+ return i2c_add_driver(&pcf85063_driver);
+}
+
+static void pcf85063_unregister_driver(void)
+{
+ i2c_del_driver(&pcf85063_driver);
+}
+
+#else
+
+static int pcf85063_register_driver(void)
+{
+ return 0;
+}
+
+static void pcf85063_unregister_driver(void)
+{
+}
+
+#endif /* IS_ENABLED(CONFIG_I2C) */
+
+#if IS_ENABLED(CONFIG_SPI_MASTER)
+
+static const struct pcf85063_config config_rv8063 = {
+ .regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x11,
+ .read_flag_mask = BIT(7) | BIT(5),
+ .write_flag_mask = BIT(5),
+ },
+ .has_alarms = 1,
+ .force_cap_7000 = 1,
+};
+
+static const struct spi_device_id rv8063_id[] = {
+ { "rv8063" },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, rv8063_id);
+
+static const struct of_device_id rv8063_of_match[] = {
+ { .compatible = "microcrystal,rv8063" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rv8063_of_match);
+
+static int rv8063_probe(struct spi_device *spi)
+{
+ const struct pcf85063_config *config = &config_rv8063;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spi(spi, &config->regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return pcf85063_probe(&spi->dev, regmap, spi->irq, config);
+}
+
+static struct spi_driver rv8063_driver = {
+ .driver = {
+ .name = "rv8063",
+ .of_match_table = rv8063_of_match,
+ },
+ .probe = rv8063_probe,
+ .id_table = rv8063_id,
+};
+
+static int __init rv8063_register_driver(void)
+{
+ return spi_register_driver(&rv8063_driver);
+}
+
+static void __exit rv8063_unregister_driver(void)
+{
+ spi_unregister_driver(&rv8063_driver);
+}
+
+#else
+
+static int __init rv8063_register_driver(void)
+{
+ return 0;
+}
+
+static void __exit rv8063_unregister_driver(void)
+{
+}
+
+#endif /* IS_ENABLED(CONFIG_SPI_MASTER) */
+
+static int __init pcf85063_init(void)
+{
+ int ret;
+
+ ret = pcf85063_register_driver();
+ if (ret)
+ return ret;
+
+ ret = rv8063_register_driver();
+ if (ret)
+ pcf85063_unregister_driver();
+
+ return ret;
+}
+module_init(pcf85063_init);
+
+static void __exit pcf85063_exit(void)
+{
+ rv8063_unregister_driver();
+ pcf85063_unregister_driver();
+}
+module_exit(pcf85063_exit);
MODULE_AUTHOR("Søren Andersen <san@rosetechnology.dk>");
MODULE_DESCRIPTION("PCF85063 RTC driver");
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index b2611697fa5e..4e61011fb7a9 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -330,14 +330,19 @@ static unsigned long pcf8563_clkout_recalc_rate(struct clk_hw *hw,
return clkout_rates[buf];
}
-static long pcf8563_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int pcf8563_clkout_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
int i;
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
- if (clkout_rates[i] <= rate)
- return clkout_rates[i];
+ if (clkout_rates[i] <= req->rate) {
+ req->rate = clkout_rates[i];
+
+ return 0;
+ }
+
+ req->rate = clkout_rates[0];
return 0;
}
@@ -413,7 +418,7 @@ static const struct clk_ops pcf8563_clkout_ops = {
.unprepare = pcf8563_clkout_unprepare,
.is_prepared = pcf8563_clkout_is_prepared,
.recalc_rate = pcf8563_clkout_recalc_rate,
- .round_rate = pcf8563_clkout_round_rate,
+ .determine_rate = pcf8563_clkout_determine_rate,
.set_rate = pcf8563_clkout_set_rate,
};
diff --git a/drivers/rtc/rtc-rv3028.c b/drivers/rtc/rtc-rv3028.c
index 868d1b1eb0f4..c2a531f0e125 100644
--- a/drivers/rtc/rtc-rv3028.c
+++ b/drivers/rtc/rtc-rv3028.c
@@ -731,14 +731,19 @@ static unsigned long rv3028_clkout_recalc_rate(struct clk_hw *hw,
return clkout_rates[clkout];
}
-static long rv3028_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int rv3028_clkout_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
int i;
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
- if (clkout_rates[i] <= rate)
- return clkout_rates[i];
+ if (clkout_rates[i] <= req->rate) {
+ req->rate = clkout_rates[i];
+
+ return 0;
+ }
+
+ req->rate = clkout_rates[0];
return 0;
}
@@ -802,7 +807,7 @@ static const struct clk_ops rv3028_clkout_ops = {
.unprepare = rv3028_clkout_unprepare,
.is_prepared = rv3028_clkout_is_prepared,
.recalc_rate = rv3028_clkout_recalc_rate,
- .round_rate = rv3028_clkout_round_rate,
+ .determine_rate = rv3028_clkout_determine_rate,
.set_rate = rv3028_clkout_set_rate,
};
diff --git a/drivers/rtc/rtc-rv3032.c b/drivers/rtc/rtc-rv3032.c
index 2c6a8918acba..b8376bd1d905 100644
--- a/drivers/rtc/rtc-rv3032.c
+++ b/drivers/rtc/rtc-rv3032.c
@@ -646,19 +646,24 @@ static unsigned long rv3032_clkout_recalc_rate(struct clk_hw *hw,
return clkout_xtal_rates[FIELD_GET(RV3032_CLKOUT2_FD_MSK, clkout)];
}
-static long rv3032_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int rv3032_clkout_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
int i, hfd;
- if (rate < RV3032_HFD_STEP)
+ if (req->rate < RV3032_HFD_STEP)
for (i = 0; i < ARRAY_SIZE(clkout_xtal_rates); i++)
- if (clkout_xtal_rates[i] <= rate)
- return clkout_xtal_rates[i];
+ if (clkout_xtal_rates[i] <= req->rate) {
+ req->rate = clkout_xtal_rates[i];
- hfd = DIV_ROUND_CLOSEST(rate, RV3032_HFD_STEP);
+ return 0;
+ }
+
+ hfd = DIV_ROUND_CLOSEST(req->rate, RV3032_HFD_STEP);
- return RV3032_HFD_STEP * clamp(hfd, 0, 8192);
+ req->rate = RV3032_HFD_STEP * clamp(hfd, 0, 8192);
+
+ return 0;
}
static int rv3032_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -738,7 +743,7 @@ static const struct clk_ops rv3032_clkout_ops = {
.unprepare = rv3032_clkout_unprepare,
.is_prepared = rv3032_clkout_is_prepared,
.recalc_rate = rv3032_clkout_recalc_rate,
- .round_rate = rv3032_clkout_round_rate,
+ .determine_rate = rv3032_clkout_determine_rate,
.set_rate = rv3032_clkout_set_rate,
};
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 5dd575865adf..79b2a16f15ad 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -549,25 +549,25 @@ static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask)
writeb(mask, info->base + S3C2410_INTP);
}
-static struct s3c_rtc_data const s3c2410_rtc_data = {
+static const struct s3c_rtc_data s3c2410_rtc_data = {
.irq_handler = s3c24xx_rtc_irq,
.enable = s3c24xx_rtc_enable,
.disable = s3c24xx_rtc_disable,
};
-static struct s3c_rtc_data const s3c2416_rtc_data = {
+static const struct s3c_rtc_data s3c2416_rtc_data = {
.irq_handler = s3c24xx_rtc_irq,
.enable = s3c24xx_rtc_enable,
.disable = s3c24xx_rtc_disable,
};
-static struct s3c_rtc_data const s3c2443_rtc_data = {
+static const struct s3c_rtc_data s3c2443_rtc_data = {
.irq_handler = s3c24xx_rtc_irq,
.enable = s3c24xx_rtc_enable,
.disable = s3c24xx_rtc_disable,
};
-static struct s3c_rtc_data const s3c6410_rtc_data = {
+static const struct s3c_rtc_data s3c6410_rtc_data = {
.needs_src_clk = true,
.irq_handler = s3c6410_rtc_irq,
.enable = s3c24xx_rtc_enable,
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
index f15ef3aa82a0..619800a00479 100644
--- a/drivers/rtc/rtc-sh.c
+++ b/drivers/rtc/rtc-sh.c
@@ -455,7 +455,7 @@ static void __exit sh_rtc_remove(struct platform_device *pdev)
clk_disable(rtc->clk);
}
-static int __maybe_unused sh_rtc_suspend(struct device *dev)
+static int sh_rtc_suspend(struct device *dev)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
@@ -465,7 +465,7 @@ static int __maybe_unused sh_rtc_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused sh_rtc_resume(struct device *dev)
+static int sh_rtc_resume(struct device *dev)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
@@ -475,7 +475,7 @@ static int __maybe_unused sh_rtc_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(sh_rtc_pm_ops, sh_rtc_suspend, sh_rtc_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(sh_rtc_pm_ops, sh_rtc_suspend, sh_rtc_resume);
static const struct of_device_id sh_rtc_of_match[] = {
{ .compatible = "renesas,sh-rtc", },
@@ -492,7 +492,7 @@ MODULE_DEVICE_TABLE(of, sh_rtc_of_match);
static struct platform_driver sh_rtc_platform_driver __refdata = {
.driver = {
.name = DRV_NAME,
- .pm = &sh_rtc_pm_ops,
+ .pm = pm_sleep_ptr(&sh_rtc_pm_ops),
.of_match_table = sh_rtc_of_match,
},
.remove = __exit_p(sh_rtc_remove),
diff --git a/drivers/rtc/sysfs.c b/drivers/rtc/sysfs.c
index e3062c4d3f2c..4ab05e105a76 100644
--- a/drivers/rtc/sysfs.c
+++ b/drivers/rtc/sysfs.c
@@ -24,8 +24,8 @@
static ssize_t
name_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- return sprintf(buf, "%s %s\n", dev_driver_string(dev->parent),
- dev_name(dev->parent));
+ return sysfs_emit(buf, "%s %s\n", dev_driver_string(dev->parent),
+ dev_name(dev->parent));
}
static DEVICE_ATTR_RO(name);
@@ -39,7 +39,7 @@ date_show(struct device *dev, struct device_attribute *attr, char *buf)
if (retval)
return retval;
- return sprintf(buf, "%ptRd\n", &tm);
+ return sysfs_emit(buf, "%ptRd\n", &tm);
}
static DEVICE_ATTR_RO(date);
@@ -53,7 +53,7 @@ time_show(struct device *dev, struct device_attribute *attr, char *buf)
if (retval)
return retval;
- return sprintf(buf, "%ptRt\n", &tm);
+ return sysfs_emit(buf, "%ptRt\n", &tm);
}
static DEVICE_ATTR_RO(time);
@@ -64,21 +64,17 @@ since_epoch_show(struct device *dev, struct device_attribute *attr, char *buf)
struct rtc_time tm;
retval = rtc_read_time(to_rtc_device(dev), &tm);
- if (retval == 0) {
- time64_t time;
-
- time = rtc_tm_to_time64(&tm);
- retval = sprintf(buf, "%lld\n", time);
- }
+ if (retval)
+ return retval;
- return retval;
+ return sysfs_emit(buf, "%lld\n", rtc_tm_to_time64(&tm));
}
static DEVICE_ATTR_RO(since_epoch);
static ssize_t
max_user_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- return sprintf(buf, "%d\n", to_rtc_device(dev)->max_user_freq);
+ return sysfs_emit(buf, "%d\n", to_rtc_device(dev)->max_user_freq);
}
static ssize_t
@@ -118,9 +114,9 @@ hctosys_show(struct device *dev, struct device_attribute *attr, char *buf)
if (rtc_hctosys_ret == 0 &&
strcmp(dev_name(&to_rtc_device(dev)->dev),
CONFIG_RTC_HCTOSYS_DEVICE) == 0)
- return sprintf(buf, "1\n");
+ return sysfs_emit(buf, "1\n");
#endif
- return sprintf(buf, "0\n");
+ return sysfs_emit(buf, "0\n");
}
static DEVICE_ATTR_RO(hctosys);
@@ -128,7 +124,6 @@ static ssize_t
wakealarm_show(struct device *dev, struct device_attribute *attr, char *buf)
{
ssize_t retval;
- time64_t alarm;
struct rtc_wkalrm alm;
/* Don't show disabled alarms. For uniformity, RTC alarms are
@@ -140,12 +135,13 @@ wakealarm_show(struct device *dev, struct device_attribute *attr, char *buf)
* alarms after they trigger, to ensure one-shot semantics.
*/
retval = rtc_read_alarm(to_rtc_device(dev), &alm);
- if (retval == 0 && alm.enabled) {
- alarm = rtc_tm_to_time64(&alm.time);
- retval = sprintf(buf, "%lld\n", alarm);
- }
+ if (retval)
+ return retval;
- return retval;
+ if (alm.enabled)
+ return sysfs_emit(buf, "%lld\n", rtc_tm_to_time64(&alm.time));
+
+ return 0;
}
static ssize_t
@@ -222,10 +218,10 @@ offset_show(struct device *dev, struct device_attribute *attr, char *buf)
long offset;
retval = rtc_read_offset(to_rtc_device(dev), &offset);
- if (retval == 0)
- retval = sprintf(buf, "%ld\n", offset);
+ if (retval)
+ return retval;
- return retval;
+ return sysfs_emit(buf, "%ld\n", offset);
}
static ssize_t
@@ -246,8 +242,8 @@ static DEVICE_ATTR_RW(offset);
static ssize_t
range_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- return sprintf(buf, "[%lld,%llu]\n", to_rtc_device(dev)->range_min,
- to_rtc_device(dev)->range_max);
+ return sysfs_emit(buf, "[%lld,%llu]\n", to_rtc_device(dev)->range_min,
+ to_rtc_device(dev)->range_max);
}
static DEVICE_ATTR_RO(range);
@@ -302,11 +298,7 @@ static struct attribute_group rtc_attr_group = {
.is_visible = rtc_attr_is_visible,
.attrs = rtc_attrs,
};
-
-static const struct attribute_group *rtc_attr_groups[] = {
- &rtc_attr_group,
- NULL
-};
+__ATTRIBUTE_GROUPS(rtc_attr);
const struct attribute_group **rtc_get_dev_attribute_groups(void)
{
@@ -318,17 +310,21 @@ int rtc_add_groups(struct rtc_device *rtc, const struct attribute_group **grps)
size_t old_cnt = 0, add_cnt = 0, new_cnt;
const struct attribute_group **groups, **old;
- if (!grps)
+ if (grps) {
+ for (groups = grps; *groups; groups++)
+ add_cnt++;
+ /* No need to modify current groups if nothing new is provided */
+ if (add_cnt == 0)
+ return 0;
+ } else {
return -EINVAL;
+ }
groups = rtc->dev.groups;
if (groups)
for (; *groups; groups++)
old_cnt++;
- for (groups = grps; *groups; groups++)
- add_cnt++;
-
new_cnt = old_cnt + add_cnt + 1;
groups = devm_kcalloc(&rtc->dev, new_cnt, sizeof(*groups), GFP_KERNEL);
if (!groups)
diff --git a/drivers/rtc/lib_test.c b/drivers/rtc/test_rtc_lib.c
index 0eebad1fe2a0..0eebad1fe2a0 100644
--- a/drivers/rtc/lib_test.c
+++ b/drivers/rtc/test_rtc_lib.c