I3C for 6.20

Subsystem:
  - add sysfs entry and attribute for Device NACK Retry count
 
 Drivers:
  - dw: Device NACK Retry configuration knob
  - mipi-i3c-hci: support for Multi-Bus Instances, Runtime PM support, System
    Suspend support
  - renesas: suspend/resume support
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmmChesACgkQY6TcMGxw
 OjIIgg/8CXIGljGhD2UwxuIwf31Py54XmXVxGip7PygfkuJMlxe27JwkPvuQ9TIF
 OXB342TpovAxfbEGphalRRXcYf95nLbmqhvyjt+h8ndJlfJeYV+iDfTxhSgr4bY/
 hxMTog4OmWi8rAskp0sR0OEY1fOE3Nggx+s7W62fnuGsNzsRg8ABXp2nhJ1Q7ZxV
 ZZUcnRtCLq6zn+Xlv13vMRaTIRkiPwnqdXGbra4MMYfe2QijQKKaoelt1zW14ioO
 VgAQudxVU+ZH6t/NRJ7oqAN19HNqH8sJ0tOo5IDO8eDq0H36/n9v+AK0rUoEZ77q
 Tyt7lksv/fxauIsaW8Zs6iqpT43CkwXhSn2t6yPT8aEczqi7L+4fpenqFYyHbw5k
 i9iGMfAgxyxVSklokS9PABBfGlOK5lr0PX1lHEGpdMAeLWv0pj0FQ/15f5vzZusk
 Pfd8nU2cFuNx5Zenyd5/OSSeqo/nvuF07xrWRvh8tDw45S/D7el7meJc9rQr0pO1
 LVriRVCP17Tu0sC36GKXI1x1y/uoKnREzU2wogLgZaWdynXK2MMNTJY6aWUhchco
 rIUQpnTW54iQkCimwzR+uUAXexx2rUkfLROTbAhgkpbaRmSYabRWy/cdDxr/Y6nr
 cqrZ0QWKP203T2McpYAGSjvYLCk+Oan30GMesDOgWpASk8yqUKU=
 =SukE
 -----END PGP SIGNATURE-----

Merge tag 'i3c/for-6.20' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux

Pull i3c updates from Alexandre Belloni:
 "Subsystem:
   - add sysfs entry and attribute for Device NACK Retry count

  Drivers:
   - dw: Device NACK Retry configuration knob
   - mipi-i3c-hci: support multi-bus instances, runtime PM, and suspend
   - renesas: suspend/resume support"

* tag 'i3c/for-6.20' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux: (52 commits)
  i3c: dw-i3c-master: fix SIR reject bit mapping for dynamic addresses
  i3c: dw-i3c-master: convert spinlock usage to scoped guards
  i3c: dw: Fix memory leak in dw_i3c_master_i2c_xfers()
  i3c: mipi-i3c-hci-pci: Add System Suspend support
  i3c: mipi-i3c-hci: Add optional System Suspend support
  i3c: master: Add i3c_master_do_daa_ext() for post-hibernation address recovery
  i3c: dw: Initialize spinlock to avoid upsetting lockdep
  i3c: mipi-i3c-hci-pci: Add Runtime PM support
  i3c: mipi-i3c-hci: Add optional Runtime PM support
  i3c: master: Introduce optional Runtime PM support
  i3c: mipi-i3c-hci: Factor out master dynamic address setting into helper
  i3c: mipi-i3c-hci: Allow core re-initialization for Runtime PM support
  i3c: mipi-i3c-hci: Factor out core initialization into helper
  i3c: mipi-i3c-hci: Factor out IO mode setting into helper
  i3c: mipi-i3c-hci: Factor out software reset into helper
  i3c: mipi-i3c-hci: Add PIO suspend and resume support
  i3c: mipi-i3c-hci: Refactor PIO register initialization
  i3c: mipi-i3c-hci: Add DMA suspend and resume support
  i3c: mipi-i3c-hci: Extract ring initialization from hci_dma_init()
  i3c: mipi-i3c-hci: Introduce helper to restore DAT
  ...
This commit is contained in:
Linus Torvalds 2026-02-09 10:06:44 -08:00
commit 2f81bdbdb3
22 changed files with 1269 additions and 514 deletions

View file

@ -161,3 +161,14 @@ Contact: linux-i3c@vger.kernel.org
Description:
These directories are just symbolic links to
/sys/bus/i3c/devices/i3c-<bus-id>/<bus-id>-<device-pid>.
What: /sys/bus/i3c/devices/i3c-<bus-id>/<bus-id>-<device-pid>/dev_nack_retry_count
KernelVersion: 6.18
Contact: linux-i3c@vger.kernel.org
Description:
Expose the dev_nak_retry_count which controls the number of
automatic retries that will be performed by the controller when
the target device returns a NACK response. A value of 0 disables
the automatic retries. Exist only when I3C constroller supports
this retry on nack feature.

View file

@ -46,10 +46,16 @@ int i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers,
return -EINVAL;
}
ret = i3c_bus_rpm_get(dev->bus);
if (ret)
return ret;
i3c_bus_normaluse_lock(dev->bus);
ret = i3c_dev_do_xfers_locked(dev->desc, xfers, nxfers, mode);
i3c_bus_normaluse_unlock(dev->bus);
i3c_bus_rpm_put(dev->bus);
return ret;
}
EXPORT_SYMBOL_GPL(i3c_device_do_xfers);
@ -66,10 +72,16 @@ int i3c_device_do_setdasa(struct i3c_device *dev)
{
int ret;
ret = i3c_bus_rpm_get(dev->bus);
if (ret)
return ret;
i3c_bus_normaluse_lock(dev->bus);
ret = i3c_dev_setdasa_locked(dev->desc);
i3c_bus_normaluse_unlock(dev->bus);
i3c_bus_rpm_put(dev->bus);
return ret;
}
EXPORT_SYMBOL_GPL(i3c_device_do_setdasa);
@ -106,16 +118,27 @@ EXPORT_SYMBOL_GPL(i3c_device_get_info);
*/
int i3c_device_disable_ibi(struct i3c_device *dev)
{
int ret = -ENOENT;
int ret;
if (i3c_bus_rpm_ibi_allowed(dev->bus)) {
ret = i3c_bus_rpm_get(dev->bus);
if (ret)
return ret;
}
i3c_bus_normaluse_lock(dev->bus);
if (dev->desc) {
mutex_lock(&dev->desc->ibi_lock);
ret = i3c_dev_disable_ibi_locked(dev->desc);
mutex_unlock(&dev->desc->ibi_lock);
} else {
ret = -ENOENT;
}
i3c_bus_normaluse_unlock(dev->bus);
if (!ret || i3c_bus_rpm_ibi_allowed(dev->bus))
i3c_bus_rpm_put(dev->bus);
return ret;
}
EXPORT_SYMBOL_GPL(i3c_device_disable_ibi);
@ -135,16 +158,25 @@ EXPORT_SYMBOL_GPL(i3c_device_disable_ibi);
*/
int i3c_device_enable_ibi(struct i3c_device *dev)
{
int ret = -ENOENT;
int ret;
ret = i3c_bus_rpm_get(dev->bus);
if (ret)
return ret;
i3c_bus_normaluse_lock(dev->bus);
if (dev->desc) {
mutex_lock(&dev->desc->ibi_lock);
ret = i3c_dev_enable_ibi_locked(dev->desc);
mutex_unlock(&dev->desc->ibi_lock);
} else {
ret = -ENOENT;
}
i3c_bus_normaluse_unlock(dev->bus);
if (ret || i3c_bus_rpm_ibi_allowed(dev->bus))
i3c_bus_rpm_put(dev->bus);
return ret;
}
EXPORT_SYMBOL_GPL(i3c_device_enable_ibi);
@ -163,19 +195,27 @@ EXPORT_SYMBOL_GPL(i3c_device_enable_ibi);
int i3c_device_request_ibi(struct i3c_device *dev,
const struct i3c_ibi_setup *req)
{
int ret = -ENOENT;
int ret;
if (!req->handler || !req->num_slots)
return -EINVAL;
ret = i3c_bus_rpm_get(dev->bus);
if (ret)
return ret;
i3c_bus_normaluse_lock(dev->bus);
if (dev->desc) {
mutex_lock(&dev->desc->ibi_lock);
ret = i3c_dev_request_ibi_locked(dev->desc, req);
mutex_unlock(&dev->desc->ibi_lock);
} else {
ret = -ENOENT;
}
i3c_bus_normaluse_unlock(dev->bus);
i3c_bus_rpm_put(dev->bus);
return ret;
}
EXPORT_SYMBOL_GPL(i3c_device_request_ibi);

View file

@ -11,6 +11,10 @@
#include <linux/i3c/master.h>
#include <linux/io.h>
int __must_check i3c_bus_rpm_get(struct i3c_bus *bus);
void i3c_bus_rpm_put(struct i3c_bus *bus);
bool i3c_bus_rpm_ibi_allowed(struct i3c_bus *bus);
void i3c_bus_normaluse_lock(struct i3c_bus *bus);
void i3c_bus_normaluse_unlock(struct i3c_bus *bus);

View file

@ -106,6 +106,38 @@ static struct i3c_master_controller *dev_to_i3cmaster(struct device *dev)
return container_of(dev, struct i3c_master_controller, dev);
}
static int __must_check i3c_master_rpm_get(struct i3c_master_controller *master)
{
int ret = master->rpm_allowed ? pm_runtime_resume_and_get(master->dev.parent) : 0;
if (ret < 0) {
dev_err(master->dev.parent, "runtime resume failed, error %d\n", ret);
return ret;
}
return 0;
}
static void i3c_master_rpm_put(struct i3c_master_controller *master)
{
if (master->rpm_allowed)
pm_runtime_put_autosuspend(master->dev.parent);
}
int i3c_bus_rpm_get(struct i3c_bus *bus)
{
return i3c_master_rpm_get(i3c_bus_to_i3c_master(bus));
}
void i3c_bus_rpm_put(struct i3c_bus *bus)
{
i3c_master_rpm_put(i3c_bus_to_i3c_master(bus));
}
bool i3c_bus_rpm_ibi_allowed(struct i3c_bus *bus)
{
return i3c_bus_to_i3c_master(bus)->rpm_ibi_allowed;
}
static const struct device_type i3c_device_type;
static struct i3c_bus *dev_to_i3cbus(struct device *dev)
@ -611,6 +643,12 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable)
if (!master->ops->enable_hotjoin || !master->ops->disable_hotjoin)
return -EINVAL;
if (enable || master->rpm_ibi_allowed) {
ret = i3c_master_rpm_get(master);
if (ret)
return ret;
}
i3c_bus_normaluse_lock(&master->bus);
if (enable)
@ -618,10 +656,14 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable)
else
ret = master->ops->disable_hotjoin(master);
master->hotjoin = enable;
if (!ret)
master->hotjoin = enable;
i3c_bus_normaluse_unlock(&master->bus);
if ((enable && ret) || (!enable && !ret) || master->rpm_ibi_allowed)
i3c_master_rpm_put(master);
return ret;
}
@ -683,6 +725,39 @@ static ssize_t hotjoin_show(struct device *dev, struct device_attribute *da, cha
static DEVICE_ATTR_RW(hotjoin);
static ssize_t dev_nack_retry_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sysfs_emit(buf, "%u\n", dev_to_i3cmaster(dev)->dev_nack_retry_count);
}
static ssize_t dev_nack_retry_count_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i3c_bus *i3cbus = dev_to_i3cbus(dev);
struct i3c_master_controller *master = dev_to_i3cmaster(dev);
unsigned long val;
int ret;
ret = kstrtoul(buf, 0, &val);
if (ret)
return ret;
i3c_bus_maintenance_lock(i3cbus);
ret = master->ops->set_dev_nack_retry(master, val);
i3c_bus_maintenance_unlock(i3cbus);
if (ret)
return ret;
master->dev_nack_retry_count = val;
return count;
}
static DEVICE_ATTR_RW(dev_nack_retry_count);
static struct attribute *i3c_masterdev_attrs[] = {
&dev_attr_mode.attr,
&dev_attr_current_master.attr,
@ -1692,37 +1767,69 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master)
}
}
/**
* i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version)
* @master: controller
* @rstdaa: whether to first perform Reset of Dynamic Addresses (RSTDAA)
*
* Perform Dynamic Address Assignment with optional support for System
* Hibernation (@rstdaa is true).
*
* After System Hibernation, Dynamic Addresses can have been reassigned at boot
* time to different values. A simple strategy is followed to handle that.
* Perform a Reset of Dynamic Addresses (RSTDAA) followed by the normal DAA
* procedure which has provision for reassigning addresses that differ from the
* previously recorded addresses.
*
* Return: a 0 in case of success, an negative error code otherwise.
*/
int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa)
{
int rstret = 0;
int ret;
ret = i3c_master_rpm_get(master);
if (ret)
return ret;
i3c_bus_maintenance_lock(&master->bus);
if (rstdaa) {
rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
if (rstret == I3C_ERROR_M2)
rstret = 0;
}
ret = master->ops->do_daa(master);
i3c_bus_maintenance_unlock(&master->bus);
if (ret)
goto out;
i3c_bus_normaluse_lock(&master->bus);
i3c_master_register_new_i3c_devs(master);
i3c_bus_normaluse_unlock(&master->bus);
out:
i3c_master_rpm_put(master);
return rstret ?: ret;
}
EXPORT_SYMBOL_GPL(i3c_master_do_daa_ext);
/**
* i3c_master_do_daa() - do a DAA (Dynamic Address Assignment)
* @master: master doing the DAA
*
* This function is instantiating an I3C device object and adding it to the
* I3C device list. All device information are automatically retrieved using
* This function instantiates I3C device objects and adds them to the
* I3C device list. All device information is automatically retrieved using
* standard CCC commands.
*
* The I3C device object is returned in case the master wants to attach
* private data to it using i3c_dev_set_master_data().
*
* This function must be called with the bus lock held in write mode.
*
* Return: a 0 in case of success, an negative error code otherwise.
*/
int i3c_master_do_daa(struct i3c_master_controller *master)
{
int ret;
i3c_bus_maintenance_lock(&master->bus);
ret = master->ops->do_daa(master);
i3c_bus_maintenance_unlock(&master->bus);
if (ret)
return ret;
i3c_bus_normaluse_lock(&master->bus);
i3c_master_register_new_i3c_devs(master);
i3c_bus_normaluse_unlock(&master->bus);
return 0;
return i3c_master_do_daa_ext(master, false);
}
EXPORT_SYMBOL_GPL(i3c_master_do_daa);
@ -2064,8 +2171,17 @@ err_detach_devs:
static void i3c_master_bus_cleanup(struct i3c_master_controller *master)
{
if (master->ops->bus_cleanup)
master->ops->bus_cleanup(master);
if (master->ops->bus_cleanup) {
int ret = i3c_master_rpm_get(master);
if (ret) {
dev_err(&master->dev,
"runtime resume error: master bus_cleanup() not done\n");
} else {
master->ops->bus_cleanup(master);
i3c_master_rpm_put(master);
}
}
i3c_master_detach_free_devs(master);
}
@ -2370,19 +2486,16 @@ static int of_populate_i3c_bus(struct i3c_master_controller *master)
{
struct device *dev = &master->dev;
struct device_node *i3cbus_np = dev->of_node;
struct device_node *node;
int ret;
u32 val;
if (!i3cbus_np)
return 0;
for_each_available_child_of_node(i3cbus_np, node) {
for_each_available_child_of_node_scoped(i3cbus_np, node) {
ret = of_i3c_master_add_dev(master, node);
if (ret) {
of_node_put(node);
if (ret)
return ret;
}
}
/*
@ -2420,6 +2533,10 @@ static int i3c_master_i2c_adapter_xfer(struct i2c_adapter *adap,
return -EOPNOTSUPP;
}
ret = i3c_master_rpm_get(master);
if (ret)
return ret;
i3c_bus_normaluse_lock(&master->bus);
dev = i3c_master_find_i2c_dev_by_addr(master, addr);
if (!dev)
@ -2428,6 +2545,8 @@ static int i3c_master_i2c_adapter_xfer(struct i2c_adapter *adap,
ret = master->ops->i2c_xfers(dev, xfers, nxfers);
i3c_bus_normaluse_unlock(&master->bus);
i3c_master_rpm_put(master);
return ret ? ret : nxfers;
}
@ -2530,6 +2649,10 @@ static int i3c_i2c_notifier_call(struct notifier_block *nb, unsigned long action
master = i2c_adapter_to_i3c_master(adap);
ret = i3c_master_rpm_get(master);
if (ret)
return ret;
i3c_bus_maintenance_lock(&master->bus);
switch (action) {
case BUS_NOTIFY_ADD_DEVICE:
@ -2543,6 +2666,8 @@ static int i3c_i2c_notifier_call(struct notifier_block *nb, unsigned long action
}
i3c_bus_maintenance_unlock(&master->bus);
i3c_master_rpm_put(master);
return ret;
}
@ -2880,8 +3005,11 @@ int i3c_master_register(struct i3c_master_controller *master,
INIT_LIST_HEAD(&master->boardinfo.i2c);
INIT_LIST_HEAD(&master->boardinfo.i3c);
ret = i3c_master_rpm_get(master);
if (ret)
return ret;
device_initialize(&master->dev);
dev_set_name(&master->dev, "i3c-%d", i3cbus->id);
master->dev.dma_mask = parent->dma_mask;
master->dev.coherent_dma_mask = parent->coherent_dma_mask;
@ -2891,6 +3019,8 @@ int i3c_master_register(struct i3c_master_controller *master,
if (ret)
goto err_put_dev;
dev_set_name(&master->dev, "i3c-%d", i3cbus->id);
ret = of_populate_i3c_bus(master);
if (ret)
goto err_put_dev;
@ -2959,6 +3089,11 @@ int i3c_master_register(struct i3c_master_controller *master,
i3c_master_register_new_i3c_devs(master);
i3c_bus_normaluse_unlock(&master->bus);
if (master->ops->set_dev_nack_retry)
device_create_file(&master->dev, &dev_attr_dev_nack_retry_count);
i3c_master_rpm_put(master);
return 0;
err_del_dev:
@ -2968,6 +3103,7 @@ err_cleanup_bus:
i3c_master_bus_cleanup(master);
err_put_dev:
i3c_master_rpm_put(master);
put_device(&master->dev);
return ret;
@ -2984,6 +3120,9 @@ void i3c_master_unregister(struct i3c_master_controller *master)
{
i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE);
if (master->ops->set_dev_nack_retry)
device_remove_file(&master->dev, &dev_attr_dev_nack_retry_count);
i3c_master_i2c_adapter_cleanup(master);
i3c_master_unregister_i3c_devs(master);
i3c_master_bus_cleanup(master);
@ -3112,8 +3251,18 @@ void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev)
if (!dev->ibi)
return;
if (WARN_ON(dev->ibi->enabled))
WARN_ON(i3c_dev_disable_ibi_locked(dev));
if (dev->ibi->enabled) {
int ret;
dev_err(&master->dev, "Freeing IBI that is still enabled\n");
ret = i3c_master_rpm_get(master);
if (!ret) {
ret = i3c_dev_disable_ibi_locked(dev);
i3c_master_rpm_put(master);
}
if (ret)
dev_err(&master->dev, "Failed to disable IBI before freeing\n");
}
master->ops->free_ibi(dev);

View file

@ -69,6 +69,7 @@ config MIPI_I3C_HCI_PCI
tristate "MIPI I3C Host Controller Interface PCI support"
depends on MIPI_I3C_HCI
depends on PCI
select MFD_CORE
help
Support for MIPI I3C Host Controller Interface compatible hardware
on the PCI bus.

View file

@ -5,6 +5,7 @@
* Author: Vitor Soares <vitor.soares@synopsys.com>
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/completion.h>
@ -204,12 +205,23 @@
#define EXTENDED_CAPABILITY 0xe8
#define SLAVE_CONFIG 0xec
#define DYN_ADDR_LO_MASK GENMASK(4, 0)
#define DYN_ADDR_HI_MASK GENMASK(6, 5)
#define IBI_SIR_BIT_MOD 32 /* 32-bit vector */
#define DW_I3C_DEV_NACK_RETRY_CNT_MAX 0x3
#define DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK GENMASK(30, 29)
#define DEV_ADDR_TABLE_DYNAMIC_MASK GENMASK(23, 16)
#define DEV_ADDR_TABLE_STATIC_MASK GENMASK(6, 0)
#define DEV_ADDR_TABLE_IBI_MDB BIT(12)
#define DEV_ADDR_TABLE_SIR_REJECT BIT(13)
#define DEV_ADDR_TABLE_DEV_NACK_RETRY_CNT(x) \
FIELD_PREP(DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK, (x))
#define DEV_ADDR_TABLE_LEGACY_I2C_DEV BIT(31)
#define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) (((x) << 16) & GENMASK(23, 16))
#define DEV_ADDR_TABLE_STATIC_ADDR(x) ((x) & GENMASK(6, 0))
#define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) FIELD_PREP(DEV_ADDR_TABLE_DYNAMIC_MASK, x)
#define DEV_ADDR_TABLE_STATIC_ADDR(x) FIELD_PREP(DEV_ADDR_TABLE_STATIC_MASK, x)
#define DEV_ADDR_TABLE_LOC(start, idx) ((start) + ((idx) << 2))
#define DEV_ADDR_TABLE_GET_DYNAMIC_ADDR(x) FIELD_GET(DEV_ADDR_TABLE_DYNAMIC_MASK, x)
#define I3C_BUS_SDR1_SCL_RATE 8000000
#define I3C_BUS_SDR2_SCL_RATE 6000000
@ -257,6 +269,14 @@ struct dw_i3c_drvdata {
u32 flags;
};
static inline u32 get_ibi_sir_bit_index(u8 addr)
{
u32 lo = FIELD_GET(DYN_ADDR_LO_MASK, addr);
u32 hi = FIELD_GET(DYN_ADDR_HI_MASK, addr);
return (lo + hi) % IBI_SIR_BIT_MOD;
}
static bool dw_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m,
const struct i3c_ccc_cmd *cmd)
{
@ -409,17 +429,14 @@ static void dw_i3c_master_start_xfer_locked(struct dw_i3c_master *master)
static void dw_i3c_master_enqueue_xfer(struct dw_i3c_master *master,
struct dw_i3c_xfer *xfer)
{
unsigned long flags;
init_completion(&xfer->comp);
spin_lock_irqsave(&master->xferqueue.lock, flags);
guard(spinlock_irqsave)(&master->xferqueue.lock);
if (master->xferqueue.cur) {
list_add_tail(&xfer->node, &master->xferqueue.list);
} else {
master->xferqueue.cur = xfer;
dw_i3c_master_start_xfer_locked(master);
}
spin_unlock_irqrestore(&master->xferqueue.lock, flags);
}
static void dw_i3c_master_dequeue_xfer_locked(struct dw_i3c_master *master,
@ -444,11 +461,8 @@ static void dw_i3c_master_dequeue_xfer_locked(struct dw_i3c_master *master,
static void dw_i3c_master_dequeue_xfer(struct dw_i3c_master *master,
struct dw_i3c_xfer *xfer)
{
unsigned long flags;
spin_lock_irqsave(&master->xferqueue.lock, flags);
guard(spinlock_irqsave)(&master->xferqueue.lock);
dw_i3c_master_dequeue_xfer_locked(master, xfer);
spin_unlock_irqrestore(&master->xferqueue.lock, flags);
}
static void dw_i3c_master_end_xfer_locked(struct dw_i3c_master *master, u32 isr)
@ -1099,6 +1113,7 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
dev_err(master->dev,
"<%s> cannot resume i3c bus master, err: %d\n",
__func__, ret);
dw_i3c_master_free_xfer(xfer);
return ret;
}
@ -1187,15 +1202,13 @@ static int dw_i3c_master_request_ibi(struct i3c_dev_desc *dev,
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct dw_i3c_master *master = to_dw_i3c_master(m);
unsigned long flags;
data->ibi_pool = i3c_generic_ibi_alloc_pool(dev, req);
if (IS_ERR(data->ibi_pool))
return PTR_ERR(data->ibi_pool);
spin_lock_irqsave(&master->devs_lock, flags);
guard(spinlock_irqsave)(&master->devs_lock);
master->devs[data->index].ibi_dev = dev;
spin_unlock_irqrestore(&master->devs_lock, flags);
return 0;
}
@ -1205,11 +1218,10 @@ static void dw_i3c_master_free_ibi(struct i3c_dev_desc *dev)
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct dw_i3c_master *master = to_dw_i3c_master(m);
unsigned long flags;
spin_lock_irqsave(&master->devs_lock, flags);
master->devs[data->index].ibi_dev = NULL;
spin_unlock_irqrestore(&master->devs_lock, flags);
scoped_guard(spinlock_irqsave, &master->devs_lock) {
master->devs[data->index].ibi_dev = NULL;
}
i3c_generic_ibi_free_pool(data->ibi_pool);
data->ibi_pool = NULL;
@ -1236,14 +1248,21 @@ static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master,
struct i3c_dev_desc *dev,
u8 idx, bool enable)
{
unsigned long flags;
u32 dat_entry, reg;
bool global;
u8 dynamic_addr;
dat_entry = DEV_ADDR_TABLE_LOC(master->datstartaddr, idx);
spin_lock_irqsave(&master->devs_lock, flags);
guard(spinlock_irqsave)(&master->devs_lock);
reg = readl(master->regs + dat_entry);
dynamic_addr = DEV_ADDR_TABLE_GET_DYNAMIC_ADDR(reg);
if (!dynamic_addr)
dev_warn(master->dev,
"<%s> unassigned slave device, dynamic addr:%x\n",
__func__, dynamic_addr);
if (enable) {
reg &= ~DEV_ADDR_TABLE_SIR_REJECT;
if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
@ -1256,20 +1275,17 @@ static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master,
if (enable) {
global = (master->sir_rej_mask == IBI_REQ_REJECT_ALL);
master->sir_rej_mask &= ~BIT(idx);
master->sir_rej_mask &= ~BIT(get_ibi_sir_bit_index(dynamic_addr));
} else {
bool hj_rejected = !!(readl(master->regs + DEVICE_CTRL) & DEV_CTRL_HOT_JOIN_NACK);
master->sir_rej_mask |= BIT(idx);
master->sir_rej_mask |= BIT(get_ibi_sir_bit_index(dynamic_addr));
global = (master->sir_rej_mask == IBI_REQ_REJECT_ALL) && hj_rejected;
}
writel(master->sir_rej_mask, master->regs + IBI_SIR_REQ_REJECT);
if (global)
dw_i3c_master_enable_sir_signal(master, enable);
spin_unlock_irqrestore(&master->devs_lock, flags);
}
static int dw_i3c_master_enable_hotjoin(struct i3c_master_controller *m)
@ -1370,7 +1386,6 @@ static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master,
struct dw_i3c_i2c_dev_data *data;
struct i3c_ibi_slot *slot;
struct i3c_dev_desc *dev;
unsigned long flags;
u8 addr, len;
int idx;
@ -1388,7 +1403,7 @@ static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master,
* a new platform op to validate it.
*/
spin_lock_irqsave(&master->devs_lock, flags);
guard(spinlock_irqsave)(&master->devs_lock);
idx = dw_i3c_master_get_addr_pos(master, addr);
if (idx < 0) {
dev_dbg_ratelimited(&master->base.dev,
@ -1424,14 +1439,10 @@ static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master,
}
i3c_master_queue_ibi(dev, slot);
spin_unlock_irqrestore(&master->devs_lock, flags);
return;
err_drain:
dw_i3c_master_drain_ibi_queue(master, len);
spin_unlock_irqrestore(&master->devs_lock, flags);
}
/* "ibis": referring to In-Band Interrupts, and not
@ -1489,6 +1500,40 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
static int dw_i3c_master_set_dev_nack_retry(struct i3c_master_controller *m,
unsigned long dev_nack_retry_cnt)
{
struct dw_i3c_master *master = to_dw_i3c_master(m);
u32 reg;
int i;
if (dev_nack_retry_cnt > DW_I3C_DEV_NACK_RETRY_CNT_MAX) {
dev_err(&master->base.dev,
"Value %ld exceeds maximum %d\n",
dev_nack_retry_cnt, DW_I3C_DEV_NACK_RETRY_CNT_MAX);
return -ERANGE;
}
/*
* Update DAT entries for all currently attached devices.
* We directly iterate through the master's device array.
*/
for (i = 0; i < master->maxdevs; i++) {
/* Skip free/empty slots */
if (master->free_pos & BIT(i))
continue;
reg = readl(master->regs +
DEV_ADDR_TABLE_LOC(master->datstartaddr, i));
reg &= ~DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK;
reg |= DEV_ADDR_TABLE_DEV_NACK_RETRY_CNT(dev_nack_retry_cnt);
writel(reg, master->regs +
DEV_ADDR_TABLE_LOC(master->datstartaddr, i));
}
return 0;
}
static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
.bus_init = dw_i3c_master_bus_init,
.bus_cleanup = dw_i3c_master_bus_cleanup,
@ -1509,6 +1554,7 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
.recycle_ibi_slot = dw_i3c_master_recycle_ibi_slot,
.enable_hotjoin = dw_i3c_master_enable_hotjoin,
.disable_hotjoin = dw_i3c_master_disable_hotjoin,
.set_dev_nack_retry = dw_i3c_master_set_dev_nack_retry,
};
/* default platform ops implementations */
@ -1570,6 +1616,8 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
spin_lock_init(&master->xferqueue.lock);
INIT_LIST_HEAD(&master->xferqueue.list);
spin_lock_init(&master->devs_lock);
writel(INTR_ALL, master->regs + INTR_STATUS);
irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(&pdev->dev, irq,
@ -1676,11 +1724,16 @@ static void dw_i3c_master_restore_addrs(struct dw_i3c_master *master)
if (master->free_pos & BIT(pos))
continue;
if (master->devs[pos].is_i2c_addr)
reg_val = DEV_ADDR_TABLE_LEGACY_I2C_DEV |
reg_val = readl(master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
if (master->devs[pos].is_i2c_addr) {
reg_val &= ~DEV_ADDR_TABLE_STATIC_MASK;
reg_val |= DEV_ADDR_TABLE_LEGACY_I2C_DEV |
DEV_ADDR_TABLE_STATIC_ADDR(master->devs[pos].addr);
else
reg_val = DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr);
} else {
reg_val &= ~DEV_ADDR_TABLE_DYNAMIC_MASK;
reg_val |= DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr);
}
writel(reg_val, master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
}

View file

@ -15,7 +15,6 @@
#include "dat.h"
#include "dct.h"
/*
* Address Assignment Command
*/
@ -100,7 +99,6 @@
#define CMD_M0_VENDOR_INFO_PRESENT W0_BIT_( 7)
#define CMD_M0_TID(v) FIELD_PREP(W0_MASK( 6, 3), v)
/* Data Transfer Speed and Mode */
enum hci_cmd_mode {
MODE_I3C_SDR0 = 0x0,

View file

@ -16,7 +16,6 @@
#include "cmd.h"
#include "xfer_mode_rate.h"
/*
* Unified Data Transfer Command
*/
@ -62,7 +61,6 @@
#define CMD_A0_ASSIGN_ADDRESS(v) FIELD_PREP(W0_MASK( 14, 8), v)
#define CMD_A0_TID(v) FIELD_PREP(W0_MASK( 6, 3), v)
static unsigned int get_i3c_rate_idx(struct i3c_hci *hci)
{
struct i3c_bus *bus = i3c_master_get_bus(&hci->master);

View file

@ -14,14 +14,15 @@
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/platform_data/mipi-i3c-hci.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include "hci.h"
#include "ext_caps.h"
#include "cmd.h"
#include "dat.h"
/*
* Host Controller Capabilities and Operation Registers
*/
@ -109,12 +110,17 @@
#define DEV_CTX_BASE_LO 0x60
#define DEV_CTX_BASE_HI 0x64
static inline struct i3c_hci *to_i3c_hci(struct i3c_master_controller *m)
{
return container_of(m, struct i3c_hci, master);
}
static void i3c_hci_set_master_dyn_addr(struct i3c_hci *hci)
{
reg_write(MASTER_DEVICE_ADDR,
MASTER_DYNAMIC_ADDR(hci->dyn_addr) | MASTER_DYNAMIC_ADDR_VALID);
}
static int i3c_hci_bus_init(struct i3c_master_controller *m)
{
struct i3c_hci *hci = to_i3c_hci(m);
@ -130,10 +136,10 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m)
ret = i3c_master_get_free_addr(m, 0);
if (ret < 0)
return ret;
reg_write(MASTER_DEVICE_ADDR,
MASTER_DYNAMIC_ADDR(ret) | MASTER_DYNAMIC_ADDR_VALID);
hci->dyn_addr = ret;
i3c_hci_set_master_dyn_addr(hci);
memset(&info, 0, sizeof(info));
info.dyn_addr = ret;
info.dyn_addr = hci->dyn_addr;
ret = i3c_master_set_info(m, &info);
if (ret)
return ret;
@ -152,16 +158,41 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m)
return 0;
}
/* Bus disable should never fail, so be generous with the timeout */
#define BUS_DISABLE_TIMEOUT_US (500 * USEC_PER_MSEC)
static int i3c_hci_bus_disable(struct i3c_hci *hci)
{
u32 regval;
int ret;
reg_clear(HC_CONTROL, HC_CONTROL_BUS_ENABLE);
/* Ensure controller is disabled */
ret = readx_poll_timeout(reg_read, HC_CONTROL, regval,
!(regval & HC_CONTROL_BUS_ENABLE), 0, BUS_DISABLE_TIMEOUT_US);
if (ret)
dev_err(&hci->master.dev, "%s: Failed to disable bus\n", __func__);
return ret;
}
void i3c_hci_sync_irq_inactive(struct i3c_hci *hci)
{
struct platform_device *pdev = to_platform_device(hci->master.dev.parent);
int irq = platform_get_irq(pdev, 0);
reg_write(INTR_SIGNAL_ENABLE, 0x0);
hci->irq_inactive = true;
synchronize_irq(irq);
}
static void i3c_hci_bus_cleanup(struct i3c_master_controller *m)
{
struct i3c_hci *hci = to_i3c_hci(m);
struct platform_device *pdev = to_platform_device(m->dev.parent);
reg_clear(HC_CONTROL, HC_CONTROL_BUS_ENABLE);
synchronize_irq(platform_get_irq(pdev, 0));
i3c_hci_bus_disable(hci);
hci->io->cleanup(hci);
if (hci->cmd == &mipi_i3c_hci_cmd_v1)
mipi_i3c_hci_dat_v1.cleanup(hci);
}
void mipi_i3c_hci_resume(struct i3c_hci *hci)
@ -535,6 +566,14 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
irqreturn_t result = IRQ_NONE;
u32 val;
/*
* The IRQ can be shared, so the handler may be called when the IRQ is
* due to a different device. That could happen when runtime suspended,
* so exit immediately if IRQs are not expected for this device.
*/
if (hci->irq_inactive)
return IRQ_NONE;
val = reg_read(INTR_STATUS);
reg_write(INTR_STATUS, val);
dev_dbg(&hci->master.dev, "INTR_STATUS %#x", val);
@ -562,87 +601,66 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
return result;
}
static int i3c_hci_init(struct i3c_hci *hci)
static int i3c_hci_software_reset(struct i3c_hci *hci)
{
bool size_in_dwords, mode_selector;
u32 regval, offset;
u32 regval;
int ret;
/* Validate HCI hardware version */
regval = reg_read(HCI_VERSION);
hci->version_major = (regval >> 8) & 0xf;
hci->version_minor = (regval >> 4) & 0xf;
hci->revision = regval & 0xf;
dev_notice(&hci->master.dev, "MIPI I3C HCI v%u.%u r%02u\n",
hci->version_major, hci->version_minor, hci->revision);
/* known versions */
switch (regval & ~0xf) {
case 0x100: /* version 1.0 */
case 0x110: /* version 1.1 */
case 0x200: /* version 2.0 */
break;
default:
dev_err(&hci->master.dev, "unsupported HCI version\n");
return -EPROTONOSUPPORT;
}
hci->caps = reg_read(HC_CAPABILITIES);
dev_dbg(&hci->master.dev, "caps = %#x", hci->caps);
size_in_dwords = hci->version_major < 1 ||
(hci->version_major == 1 && hci->version_minor < 1);
regval = reg_read(DAT_SECTION);
offset = FIELD_GET(DAT_TABLE_OFFSET, regval);
hci->DAT_regs = offset ? hci->base_regs + offset : NULL;
hci->DAT_entries = FIELD_GET(DAT_TABLE_SIZE, regval);
hci->DAT_entry_size = FIELD_GET(DAT_ENTRY_SIZE, regval) ? 0 : 8;
if (size_in_dwords)
hci->DAT_entries = 4 * hci->DAT_entries / hci->DAT_entry_size;
dev_info(&hci->master.dev, "DAT: %u %u-bytes entries at offset %#x\n",
hci->DAT_entries, hci->DAT_entry_size, offset);
regval = reg_read(DCT_SECTION);
offset = FIELD_GET(DCT_TABLE_OFFSET, regval);
hci->DCT_regs = offset ? hci->base_regs + offset : NULL;
hci->DCT_entries = FIELD_GET(DCT_TABLE_SIZE, regval);
hci->DCT_entry_size = FIELD_GET(DCT_ENTRY_SIZE, regval) ? 0 : 16;
if (size_in_dwords)
hci->DCT_entries = 4 * hci->DCT_entries / hci->DCT_entry_size;
dev_info(&hci->master.dev, "DCT: %u %u-bytes entries at offset %#x\n",
hci->DCT_entries, hci->DCT_entry_size, offset);
regval = reg_read(RING_HEADERS_SECTION);
offset = FIELD_GET(RING_HEADERS_OFFSET, regval);
hci->RHS_regs = offset ? hci->base_regs + offset : NULL;
dev_info(&hci->master.dev, "Ring Headers at offset %#x\n", offset);
regval = reg_read(PIO_SECTION);
offset = FIELD_GET(PIO_REGS_OFFSET, regval);
hci->PIO_regs = offset ? hci->base_regs + offset : NULL;
dev_info(&hci->master.dev, "PIO section at offset %#x\n", offset);
regval = reg_read(EXT_CAPS_SECTION);
offset = FIELD_GET(EXT_CAPS_OFFSET, regval);
hci->EXTCAPS_regs = offset ? hci->base_regs + offset : NULL;
dev_info(&hci->master.dev, "Extended Caps at offset %#x\n", offset);
ret = i3c_hci_parse_ext_caps(hci);
if (ret)
return ret;
/*
* Now let's reset the hardware.
* SOFT_RST must be clear before we write to it.
* Then we must wait until it clears again.
*/
ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval,
!(regval & SOFT_RST), 1, 10000);
if (ret)
return -ENXIO;
!(regval & SOFT_RST), 0, 10 * USEC_PER_MSEC);
if (ret) {
dev_err(&hci->master.dev, "%s: Software reset stuck\n", __func__);
return ret;
}
reg_write(RESET_CONTROL, SOFT_RST);
ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval,
!(regval & SOFT_RST), 1, 10000);
!(regval & SOFT_RST), 0, 10 * USEC_PER_MSEC);
if (ret) {
dev_err(&hci->master.dev, "%s: Software reset failed\n", __func__);
return ret;
}
return 0;
}
static inline bool is_version_1_1_or_newer(struct i3c_hci *hci)
{
return hci->version_major > 1 || (hci->version_major == 1 && hci->version_minor > 0);
}
static int i3c_hci_set_io_mode(struct i3c_hci *hci, bool dma)
{
bool pio_mode;
if (dma)
reg_clear(HC_CONTROL, HC_CONTROL_PIO_MODE);
else
reg_set(HC_CONTROL, HC_CONTROL_PIO_MODE);
if (!is_version_1_1_or_newer(hci))
return 0;
pio_mode = reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE;
if ((dma && pio_mode) || (!dma && !pio_mode)) {
dev_err(&hci->master.dev, "%s mode is stuck\n", pio_mode ? "PIO" : "DMA");
return -EIO;
}
return 0;
}
static int i3c_hci_reset_and_init(struct i3c_hci *hci)
{
u32 regval;
int ret;
ret = i3c_hci_software_reset(hci);
if (ret)
return -ENXIO;
@ -679,6 +697,203 @@ static int i3c_hci_init(struct i3c_hci *hci)
}
}
if (hci->io) {
ret = i3c_hci_set_io_mode(hci, hci->io == &mipi_i3c_hci_dma);
} else {
/* Try activating DMA operations first */
if (hci->RHS_regs) {
ret = i3c_hci_set_io_mode(hci, true);
if (!ret) {
hci->io = &mipi_i3c_hci_dma;
dev_dbg(&hci->master.dev, "Using DMA\n");
}
}
/* If no DMA, try PIO */
if (!hci->io && hci->PIO_regs) {
ret = i3c_hci_set_io_mode(hci, false);
if (!ret) {
hci->io = &mipi_i3c_hci_pio;
dev_dbg(&hci->master.dev, "Using PIO\n");
}
}
if (!hci->io) {
dev_err(&hci->master.dev, "neither DMA nor PIO can be used\n");
ret = ret ?: -EINVAL;
}
}
if (ret)
return ret;
/* Configure OD and PP timings for AMD platforms */
if (hci->quirks & HCI_QUIRK_OD_PP_TIMING)
amd_set_od_pp_timing(hci);
return 0;
}
static int i3c_hci_runtime_suspend(struct device *dev)
{
struct i3c_hci *hci = dev_get_drvdata(dev);
int ret;
ret = i3c_hci_bus_disable(hci);
if (ret)
return ret;
hci->io->suspend(hci);
return 0;
}
static int i3c_hci_runtime_resume(struct device *dev)
{
struct i3c_hci *hci = dev_get_drvdata(dev);
int ret;
ret = i3c_hci_reset_and_init(hci);
if (ret)
return -EIO;
i3c_hci_set_master_dyn_addr(hci);
mipi_i3c_hci_dat_v1.restore(hci);
hci->irq_inactive = false;
hci->io->resume(hci);
reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE);
return 0;
}
static int i3c_hci_suspend(struct device *dev)
{
struct i3c_hci *hci = dev_get_drvdata(dev);
if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
return 0;
return pm_runtime_force_suspend(dev);
}
static int i3c_hci_resume_common(struct device *dev, bool rstdaa)
{
struct i3c_hci *hci = dev_get_drvdata(dev);
int ret;
if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
return 0;
ret = pm_runtime_force_resume(dev);
if (ret)
return ret;
ret = i3c_master_do_daa_ext(&hci->master, rstdaa);
if (ret)
dev_err(dev, "Dynamic Address Assignment failed on resume, error %d\n", ret);
/*
* I3C devices may have retained their dynamic address anyway. Do not
* fail the resume because of DAA error.
*/
return 0;
}
static int i3c_hci_resume(struct device *dev)
{
return i3c_hci_resume_common(dev, false);
}
static int i3c_hci_restore(struct device *dev)
{
return i3c_hci_resume_common(dev, true);
}
#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
static void i3c_hci_rpm_enable(struct device *dev)
{
struct i3c_hci *hci = dev_get_drvdata(dev);
pm_runtime_set_autosuspend_delay(dev, DEFAULT_AUTOSUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
devm_pm_runtime_set_active_enabled(dev);
hci->master.rpm_allowed = true;
}
static int i3c_hci_init(struct i3c_hci *hci)
{
bool size_in_dwords;
u32 regval, offset;
int ret;
/* Validate HCI hardware version */
regval = reg_read(HCI_VERSION);
hci->version_major = (regval >> 8) & 0xf;
hci->version_minor = (regval >> 4) & 0xf;
hci->revision = regval & 0xf;
dev_notice(&hci->master.dev, "MIPI I3C HCI v%u.%u r%02u\n",
hci->version_major, hci->version_minor, hci->revision);
/* known versions */
switch (regval & ~0xf) {
case 0x100: /* version 1.0 */
case 0x110: /* version 1.1 */
case 0x200: /* version 2.0 */
break;
default:
dev_err(&hci->master.dev, "unsupported HCI version\n");
return -EPROTONOSUPPORT;
}
hci->caps = reg_read(HC_CAPABILITIES);
dev_dbg(&hci->master.dev, "caps = %#x", hci->caps);
size_in_dwords = hci->version_major < 1 ||
(hci->version_major == 1 && hci->version_minor < 1);
regval = reg_read(DAT_SECTION);
offset = FIELD_GET(DAT_TABLE_OFFSET, regval);
hci->DAT_regs = offset ? hci->base_regs + offset : NULL;
hci->DAT_entries = FIELD_GET(DAT_TABLE_SIZE, regval);
hci->DAT_entry_size = FIELD_GET(DAT_ENTRY_SIZE, regval) ? 0 : 8;
if (size_in_dwords)
hci->DAT_entries = 4 * hci->DAT_entries / hci->DAT_entry_size;
dev_dbg(&hci->master.dev, "DAT: %u %u-bytes entries at offset %#x\n",
hci->DAT_entries, hci->DAT_entry_size, offset);
regval = reg_read(DCT_SECTION);
offset = FIELD_GET(DCT_TABLE_OFFSET, regval);
hci->DCT_regs = offset ? hci->base_regs + offset : NULL;
hci->DCT_entries = FIELD_GET(DCT_TABLE_SIZE, regval);
hci->DCT_entry_size = FIELD_GET(DCT_ENTRY_SIZE, regval) ? 0 : 16;
if (size_in_dwords)
hci->DCT_entries = 4 * hci->DCT_entries / hci->DCT_entry_size;
dev_dbg(&hci->master.dev, "DCT: %u %u-bytes entries at offset %#x\n",
hci->DCT_entries, hci->DCT_entry_size, offset);
regval = reg_read(RING_HEADERS_SECTION);
offset = FIELD_GET(RING_HEADERS_OFFSET, regval);
hci->RHS_regs = offset ? hci->base_regs + offset : NULL;
dev_dbg(&hci->master.dev, "Ring Headers at offset %#x\n", offset);
regval = reg_read(PIO_SECTION);
offset = FIELD_GET(PIO_REGS_OFFSET, regval);
hci->PIO_regs = offset ? hci->base_regs + offset : NULL;
dev_dbg(&hci->master.dev, "PIO section at offset %#x\n", offset);
regval = reg_read(EXT_CAPS_SECTION);
offset = FIELD_GET(EXT_CAPS_OFFSET, regval);
hci->EXTCAPS_regs = offset ? hci->base_regs + offset : NULL;
dev_dbg(&hci->master.dev, "Extended Caps at offset %#x\n", offset);
ret = i3c_hci_parse_ext_caps(hci);
if (ret)
return ret;
/* Select our command descriptor model */
switch (FIELD_GET(HC_CAP_CMD_SIZE, hci->caps)) {
case 0:
@ -692,68 +907,44 @@ static int i3c_hci_init(struct i3c_hci *hci)
return -EINVAL;
}
mode_selector = hci->version_major > 1 ||
(hci->version_major == 1 && hci->version_minor > 0);
/* Quirk for HCI_QUIRK_PIO_MODE on AMD platforms */
if (hci->quirks & HCI_QUIRK_PIO_MODE)
hci->RHS_regs = NULL;
/* Try activating DMA operations first */
if (hci->RHS_regs) {
reg_clear(HC_CONTROL, HC_CONTROL_PIO_MODE);
if (mode_selector && (reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE)) {
dev_err(&hci->master.dev, "PIO mode is stuck\n");
ret = -EIO;
} else {
hci->io = &mipi_i3c_hci_dma;
dev_info(&hci->master.dev, "Using DMA\n");
}
}
/* If no DMA, try PIO */
if (!hci->io && hci->PIO_regs) {
reg_set(HC_CONTROL, HC_CONTROL_PIO_MODE);
if (mode_selector && !(reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE)) {
dev_err(&hci->master.dev, "DMA mode is stuck\n");
ret = -EIO;
} else {
hci->io = &mipi_i3c_hci_pio;
dev_info(&hci->master.dev, "Using PIO\n");
}
}
if (!hci->io) {
dev_err(&hci->master.dev, "neither DMA nor PIO can be used\n");
if (!ret)
ret = -EINVAL;
return ret;
}
/* Configure OD and PP timings for AMD platforms */
if (hci->quirks & HCI_QUIRK_OD_PP_TIMING)
amd_set_od_pp_timing(hci);
return 0;
return i3c_hci_reset_and_init(hci);
}
static int i3c_hci_probe(struct platform_device *pdev)
{
const struct mipi_i3c_hci_platform_data *pdata = pdev->dev.platform_data;
struct i3c_hci *hci;
int irq, ret;
hci = devm_kzalloc(&pdev->dev, sizeof(*hci), GFP_KERNEL);
if (!hci)
return -ENOMEM;
hci->base_regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hci->base_regs))
return PTR_ERR(hci->base_regs);
/*
* Multi-bus instances share the same MMIO address range, but not
* necessarily in separate contiguous sub-ranges. To avoid overlapping
* mappings, provide base_regs from the parent mapping.
*/
if (pdata)
hci->base_regs = pdata->base_regs;
if (!hci->base_regs) {
hci->base_regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hci->base_regs))
return PTR_ERR(hci->base_regs);
}
platform_set_drvdata(pdev, hci);
/* temporary for dev_printk's, to be replaced in i3c_master_register */
hci->master.dev.init_name = dev_name(&pdev->dev);
hci->quirks = (unsigned long)device_get_match_data(&pdev->dev);
if (!hci->quirks && platform_get_device_id(pdev))
hci->quirks = platform_get_device_id(pdev)->driver_data;
ret = i3c_hci_init(hci);
if (ret)
@ -761,16 +952,14 @@ static int i3c_hci_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(&pdev->dev, irq, i3c_hci_irq_handler,
0, NULL, hci);
IRQF_SHARED, NULL, hci);
if (ret)
return ret;
ret = i3c_master_register(&hci->master, &pdev->dev,
&i3c_hci_ops, false);
if (ret)
return ret;
if (hci->quirks & HCI_QUIRK_RPM_ALLOWED)
i3c_hci_rpm_enable(&pdev->dev);
return 0;
return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false);
}
static void i3c_hci_remove(struct platform_device *pdev)
@ -792,13 +981,31 @@ static const struct acpi_device_id i3c_hci_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, i3c_hci_acpi_match);
static const struct platform_device_id i3c_hci_driver_ids[] = {
{ .name = "intel-lpss-i3c", HCI_QUIRK_RPM_ALLOWED },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, i3c_hci_driver_ids);
static const struct dev_pm_ops i3c_hci_pm_ops = {
.suspend = pm_sleep_ptr(i3c_hci_suspend),
.resume = pm_sleep_ptr(i3c_hci_resume),
.freeze = pm_sleep_ptr(i3c_hci_suspend),
.thaw = pm_sleep_ptr(i3c_hci_resume),
.poweroff = pm_sleep_ptr(i3c_hci_suspend),
.restore = pm_sleep_ptr(i3c_hci_restore),
RUNTIME_PM_OPS(i3c_hci_runtime_suspend, i3c_hci_runtime_resume, NULL)
};
static struct platform_driver i3c_hci_driver = {
.probe = i3c_hci_probe,
.remove = i3c_hci_remove,
.id_table = i3c_hci_driver_ids,
.driver = {
.name = "mipi-i3c-hci",
.of_match_table = of_match_ptr(i3c_hci_of_match),
.acpi_match_table = i3c_hci_acpi_match,
.pm = pm_ptr(&i3c_hci_pm_ops),
},
};
module_platform_driver(i3c_hci_driver);

View file

@ -17,7 +17,6 @@
struct hci_dat_ops {
int (*init)(struct i3c_hci *hci);
void (*cleanup)(struct i3c_hci *hci);
int (*alloc_entry)(struct i3c_hci *hci);
void (*free_entry)(struct i3c_hci *hci, unsigned int dat_idx);
void (*set_dynamic_addr)(struct i3c_hci *hci, unsigned int dat_idx, u8 addr);
@ -25,6 +24,7 @@ struct hci_dat_ops {
void (*set_flags)(struct i3c_hci *hci, unsigned int dat_idx, u32 w0, u32 w1);
void (*clear_flags)(struct i3c_hci *hci, unsigned int dat_idx, u32 w0, u32 w1);
int (*get_index)(struct i3c_hci *hci, u8 address);
void (*restore)(struct i3c_hci *hci);
};
extern const struct hci_dat_ops mipi_i3c_hci_dat_v1;

View file

@ -15,7 +15,6 @@
#include "hci.h"
#include "dat.h"
/*
* Device Address Table Structure
*/
@ -35,13 +34,26 @@
/* DAT_0_IBI_PAYLOAD W0_BIT_(12) */
#define DAT_0_STATIC_ADDRESS W0_MASK(6, 0)
#define dat_w0_read(i) readl(hci->DAT_regs + (i) * 8)
#define dat_w1_read(i) readl(hci->DAT_regs + (i) * 8 + 4)
#define dat_w0_write(i, v) writel(v, hci->DAT_regs + (i) * 8)
#define dat_w1_write(i, v) writel(v, hci->DAT_regs + (i) * 8 + 4)
#define dat_w0_read(i) hci->DAT[i].w0
#define dat_w1_read(i) hci->DAT[i].w1
#define dat_w0_write(i, v) hci_dat_w0_write(hci, i, v)
#define dat_w1_write(i, v) hci_dat_w1_write(hci, i, v)
static inline void hci_dat_w0_write(struct i3c_hci *hci, int i, u32 v)
{
hci->DAT[i].w0 = v;
writel(v, hci->DAT_regs + i * 8);
}
static inline void hci_dat_w1_write(struct i3c_hci *hci, int i, u32 v)
{
hci->DAT[i].w1 = v;
writel(v, hci->DAT_regs + i * 8 + 4);
}
static int hci_dat_v1_init(struct i3c_hci *hci)
{
struct device *dev = hci->master.dev.parent;
unsigned int dat_idx;
if (!hci->DAT_regs) {
@ -55,9 +67,15 @@ static int hci_dat_v1_init(struct i3c_hci *hci)
return -EOPNOTSUPP;
}
if (!hci->DAT) {
hci->DAT = devm_kcalloc(dev, hci->DAT_entries, hci->DAT_entry_size, GFP_KERNEL);
if (!hci->DAT)
return -ENOMEM;
}
if (!hci->DAT_data) {
/* use a bitmap for faster free slot search */
hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL);
hci->DAT_data = devm_bitmap_zalloc(dev, hci->DAT_entries, GFP_KERNEL);
if (!hci->DAT_data)
return -ENOMEM;
@ -71,12 +89,6 @@ static int hci_dat_v1_init(struct i3c_hci *hci)
return 0;
}
static void hci_dat_v1_cleanup(struct i3c_hci *hci)
{
bitmap_free(hci->DAT_data);
hci->DAT_data = NULL;
}
static int hci_dat_v1_alloc_entry(struct i3c_hci *hci)
{
unsigned int dat_idx;
@ -169,9 +181,16 @@ static int hci_dat_v1_get_index(struct i3c_hci *hci, u8 dev_addr)
return -ENODEV;
}
static void hci_dat_v1_restore(struct i3c_hci *hci)
{
for (int i = 0; i < hci->DAT_entries; i++) {
writel(hci->DAT[i].w0, hci->DAT_regs + i * 8);
writel(hci->DAT[i].w1, hci->DAT_regs + i * 8 + 4);
}
}
const struct hci_dat_ops mipi_i3c_hci_dat_v1 = {
.init = hci_dat_v1_init,
.cleanup = hci_dat_v1_cleanup,
.alloc_entry = hci_dat_v1_alloc_entry,
.free_entry = hci_dat_v1_free_entry,
.set_dynamic_addr = hci_dat_v1_set_dynamic_addr,
@ -179,4 +198,5 @@ const struct hci_dat_ops mipi_i3c_hci_dat_v1 = {
.set_flags = hci_dat_v1_set_flags,
.clear_flags = hci_dat_v1_clear_flags,
.get_index = hci_dat_v1_get_index,
.restore = hci_dat_v1_restore,
};

View file

@ -20,7 +20,6 @@
#include "cmd.h"
#include "ibi.h"
/*
* Software Parameter Values (somewhat arb itrary for now).
* Some of them could be determined at run time eventually.
@ -124,7 +123,6 @@
#define DATA_BUF_IOC BIT(30) /* Interrupt on Completion */
#define DATA_BUF_BLOCK_SIZE GENMASK(15, 0)
struct hci_rh_data {
void __iomem *regs;
void *xfer, *resp, *ibi_status, *ibi_data;
@ -162,8 +160,31 @@ static void hci_dma_cleanup(struct i3c_hci *hci)
rh_reg_write(INTR_SIGNAL_ENABLE, 0);
rh_reg_write(RING_CONTROL, 0);
}
i3c_hci_sync_irq_inactive(hci);
for (i = 0; i < rings->total; i++) {
rh = &rings->headers[i];
rh_reg_write(CR_SETUP, 0);
rh_reg_write(IBI_SETUP, 0);
}
rhs_reg_write(CONTROL, 0);
}
static void hci_dma_free(void *data)
{
struct i3c_hci *hci = data;
struct hci_rings_data *rings = hci->io_data;
struct hci_rh_data *rh;
if (!rings)
return;
for (int i = 0; i < rings->total; i++) {
rh = &rings->headers[i];
if (rh->xfer)
dma_free_coherent(rings->sysdev,
@ -185,12 +206,98 @@ static void hci_dma_cleanup(struct i3c_hci *hci)
kfree(rh->ibi_data);
}
rhs_reg_write(CONTROL, 0);
kfree(rings);
hci->io_data = NULL;
}
static void hci_dma_init_rh(struct i3c_hci *hci, struct hci_rh_data *rh, int i)
{
u32 regval;
rh_reg_write(CMD_RING_BASE_LO, lower_32_bits(rh->xfer_dma));
rh_reg_write(CMD_RING_BASE_HI, upper_32_bits(rh->xfer_dma));
rh_reg_write(RESP_RING_BASE_LO, lower_32_bits(rh->resp_dma));
rh_reg_write(RESP_RING_BASE_HI, upper_32_bits(rh->resp_dma));
regval = FIELD_PREP(CR_RING_SIZE, rh->xfer_entries);
rh_reg_write(CR_SETUP, regval);
rh_reg_write(INTR_STATUS_ENABLE, 0xffffffff);
rh_reg_write(INTR_SIGNAL_ENABLE, INTR_IBI_READY |
INTR_TRANSFER_COMPLETION |
INTR_RING_OP |
INTR_TRANSFER_ERR |
INTR_IBI_RING_FULL |
INTR_TRANSFER_ABORT);
if (i >= IBI_RINGS)
goto ring_ready;
rh_reg_write(IBI_STATUS_RING_BASE_LO, lower_32_bits(rh->ibi_status_dma));
rh_reg_write(IBI_STATUS_RING_BASE_HI, upper_32_bits(rh->ibi_status_dma));
rh_reg_write(IBI_DATA_RING_BASE_LO, lower_32_bits(rh->ibi_data_dma));
rh_reg_write(IBI_DATA_RING_BASE_HI, upper_32_bits(rh->ibi_data_dma));
regval = FIELD_PREP(IBI_STATUS_RING_SIZE, rh->ibi_status_entries) |
FIELD_PREP(IBI_DATA_CHUNK_SIZE, ilog2(rh->ibi_chunk_sz) - 2) |
FIELD_PREP(IBI_DATA_CHUNK_COUNT, rh->ibi_chunks_total);
rh_reg_write(IBI_SETUP, regval);
regval = rh_reg_read(INTR_SIGNAL_ENABLE);
regval |= INTR_IBI_READY;
rh_reg_write(INTR_SIGNAL_ENABLE, regval);
ring_ready:
/*
* The MIPI I3C HCI specification does not document reset values for
* RING_OPERATION1 fields and some controllers (e.g. Intel controllers)
* do not reset the values, so ensure the ring pointers are set to zero
* here.
*/
rh_reg_write(RING_OPERATION1, 0);
rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE);
rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE | RING_CTRL_RUN_STOP);
rh->done_ptr = 0;
rh->ibi_chunk_ptr = 0;
}
static void hci_dma_init_rings(struct i3c_hci *hci)
{
struct hci_rings_data *rings = hci->io_data;
u32 regval;
regval = FIELD_PREP(MAX_HEADER_COUNT, rings->total);
rhs_reg_write(CONTROL, regval);
for (int i = 0; i < rings->total; i++)
hci_dma_init_rh(hci, &rings->headers[i], i);
}
static void hci_dma_suspend(struct i3c_hci *hci)
{
struct hci_rings_data *rings = hci->io_data;
int n = rings ? rings->total : 0;
for (int i = 0; i < n; i++) {
struct hci_rh_data *rh = &rings->headers[i];
rh_reg_write(INTR_SIGNAL_ENABLE, 0);
rh_reg_write(RING_CONTROL, 0);
}
i3c_hci_sync_irq_inactive(hci);
}
static void hci_dma_resume(struct i3c_hci *hci)
{
struct hci_rings_data *rings = hci->io_data;
if (rings)
hci_dma_init_rings(hci);
}
static int hci_dma_init(struct i3c_hci *hci)
{
struct hci_rings_data *rings;
@ -214,7 +321,7 @@ static int hci_dma_init(struct i3c_hci *hci)
regval = rhs_reg_read(CONTROL);
nr_rings = FIELD_GET(MAX_HEADER_COUNT_CAP, regval);
dev_info(&hci->master.dev, "%d DMA rings available\n", nr_rings);
dev_dbg(&hci->master.dev, "%d DMA rings available\n", nr_rings);
if (unlikely(nr_rings > 8)) {
dev_err(&hci->master.dev, "number of rings should be <= 8\n");
nr_rings = 8;
@ -228,13 +335,10 @@ static int hci_dma_init(struct i3c_hci *hci)
rings->total = nr_rings;
rings->sysdev = sysdev;
regval = FIELD_PREP(MAX_HEADER_COUNT, rings->total);
rhs_reg_write(CONTROL, regval);
for (i = 0; i < rings->total; i++) {
u32 offset = rhs_reg_read(RHn_OFFSET(i));
dev_info(&hci->master.dev, "Ring %d at offset %#x\n", i, offset);
dev_dbg(&hci->master.dev, "Ring %d at offset %#x\n", i, offset);
ret = -EINVAL;
if (!offset)
goto err_out;
@ -265,26 +369,10 @@ static int hci_dma_init(struct i3c_hci *hci)
if (!rh->xfer || !rh->resp || !rh->src_xfers)
goto err_out;
rh_reg_write(CMD_RING_BASE_LO, lower_32_bits(rh->xfer_dma));
rh_reg_write(CMD_RING_BASE_HI, upper_32_bits(rh->xfer_dma));
rh_reg_write(RESP_RING_BASE_LO, lower_32_bits(rh->resp_dma));
rh_reg_write(RESP_RING_BASE_HI, upper_32_bits(rh->resp_dma));
regval = FIELD_PREP(CR_RING_SIZE, rh->xfer_entries);
rh_reg_write(CR_SETUP, regval);
rh_reg_write(INTR_STATUS_ENABLE, 0xffffffff);
rh_reg_write(INTR_SIGNAL_ENABLE, INTR_IBI_READY |
INTR_TRANSFER_COMPLETION |
INTR_RING_OP |
INTR_TRANSFER_ERR |
INTR_IBI_RING_FULL |
INTR_TRANSFER_ABORT);
/* IBIs */
if (i >= IBI_RINGS)
goto ring_ready;
continue;
regval = rh_reg_read(IBI_SETUP);
rh->ibi_status_sz = FIELD_GET(IBI_STATUS_STRUCT_SIZE, regval);
@ -323,33 +411,18 @@ static int hci_dma_init(struct i3c_hci *hci)
ret = -ENOMEM;
goto err_out;
}
rh_reg_write(IBI_STATUS_RING_BASE_LO, lower_32_bits(rh->ibi_status_dma));
rh_reg_write(IBI_STATUS_RING_BASE_HI, upper_32_bits(rh->ibi_status_dma));
rh_reg_write(IBI_DATA_RING_BASE_LO, lower_32_bits(rh->ibi_data_dma));
rh_reg_write(IBI_DATA_RING_BASE_HI, upper_32_bits(rh->ibi_data_dma));
regval = FIELD_PREP(IBI_STATUS_RING_SIZE,
rh->ibi_status_entries) |
FIELD_PREP(IBI_DATA_CHUNK_SIZE,
ilog2(rh->ibi_chunk_sz) - 2) |
FIELD_PREP(IBI_DATA_CHUNK_COUNT,
rh->ibi_chunks_total);
rh_reg_write(IBI_SETUP, regval);
regval = rh_reg_read(INTR_SIGNAL_ENABLE);
regval |= INTR_IBI_READY;
rh_reg_write(INTR_SIGNAL_ENABLE, regval);
ring_ready:
rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE |
RING_CTRL_RUN_STOP);
}
ret = devm_add_action(hci->master.dev.parent, hci_dma_free, hci);
if (ret)
goto err_out;
hci_dma_init_rings(hci);
return 0;
err_out:
hci_dma_cleanup(hci);
hci_dma_free(hci);
return ret;
}
@ -815,4 +888,6 @@ const struct hci_io_ops mipi_i3c_hci_dma = {
.request_ibi = hci_dma_request_ibi,
.free_ibi = hci_dma_free_ibi,
.recycle_ibi_slot = hci_dma_recycle_ibi_slot,
.suspend = hci_dma_suspend,
.resume = hci_dma_resume,
};

View file

@ -16,7 +16,6 @@
#include "ext_caps.h"
#include "xfer_mode_rate.h"
/* Extended Capability Header */
#define CAP_HEADER_LENGTH GENMASK(23, 8)
#define CAP_HEADER_ID GENMASK(7, 0)
@ -27,9 +26,9 @@ static int hci_extcap_hardware_id(struct i3c_hci *hci, void __iomem *base)
hci->vendor_version_id = readl(base + 0x08);
hci->vendor_product_id = readl(base + 0x0c);
dev_info(&hci->master.dev, "vendor MIPI ID: %#x\n", hci->vendor_mipi_id);
dev_info(&hci->master.dev, "vendor version ID: %#x\n", hci->vendor_version_id);
dev_info(&hci->master.dev, "vendor product ID: %#x\n", hci->vendor_product_id);
dev_dbg(&hci->master.dev, "vendor MIPI ID: %#x\n", hci->vendor_mipi_id);
dev_dbg(&hci->master.dev, "vendor version ID: %#x\n", hci->vendor_version_id);
dev_dbg(&hci->master.dev, "vendor product ID: %#x\n", hci->vendor_product_id);
/* ought to go in a table if this grows too much */
switch (hci->vendor_mipi_id) {
@ -49,7 +48,7 @@ static int hci_extcap_master_config(struct i3c_hci *hci, void __iomem *base)
static const char * const functionality[] = {
"(unknown)", "master only", "target only",
"primary/secondary master" };
dev_info(&hci->master.dev, "operation mode: %s\n", functionality[operation_mode]);
dev_dbg(&hci->master.dev, "operation mode: %s\n", functionality[operation_mode]);
if (operation_mode & 0x1)
return 0;
dev_err(&hci->master.dev, "only master mode is currently supported\n");
@ -61,7 +60,7 @@ static int hci_extcap_multi_bus(struct i3c_hci *hci, void __iomem *base)
u32 bus_instance = readl(base + 0x04);
unsigned int count = FIELD_GET(GENMASK(3, 0), bus_instance);
dev_info(&hci->master.dev, "%d bus instances\n", count);
dev_dbg(&hci->master.dev, "%d bus instances\n", count);
return 0;
}
@ -71,8 +70,7 @@ static int hci_extcap_xfer_modes(struct i3c_hci *hci, void __iomem *base)
u32 entries = FIELD_GET(CAP_HEADER_LENGTH, header) - 1;
unsigned int index;
dev_info(&hci->master.dev, "transfer mode table has %d entries\n",
entries);
dev_dbg(&hci->master.dev, "transfer mode table has %d entries\n", entries);
base += 4; /* skip header */
for (index = 0; index < entries; index++) {
u32 mode_entry = readl(base);
@ -95,7 +93,7 @@ static int hci_extcap_xfer_rates(struct i3c_hci *hci, void __iomem *base)
base += 4; /* skip header */
dev_info(&hci->master.dev, "available data rates:\n");
dev_dbg(&hci->master.dev, "available data rates:\n");
for (index = 0; index < entries; index++) {
rate_entry = readl(base);
dev_dbg(&hci->master.dev, "entry %d: 0x%08x",
@ -103,12 +101,12 @@ static int hci_extcap_xfer_rates(struct i3c_hci *hci, void __iomem *base)
rate = FIELD_GET(XFERRATE_ACTUAL_RATE_KHZ, rate_entry);
rate_id = FIELD_GET(XFERRATE_RATE_ID, rate_entry);
mode_id = FIELD_GET(XFERRATE_MODE_ID, rate_entry);
dev_info(&hci->master.dev, "rate %d for %s = %d kHz\n",
rate_id,
mode_id == XFERRATE_MODE_I3C ? "I3C" :
mode_id == XFERRATE_MODE_I2C ? "I2C" :
"unknown mode",
rate);
dev_dbg(&hci->master.dev, "rate %d for %s = %d kHz\n",
rate_id,
mode_id == XFERRATE_MODE_I3C ? "I3C" :
mode_id == XFERRATE_MODE_I2C ? "I2C" :
"unknown mode",
rate);
base += 4;
}
@ -122,8 +120,8 @@ static int hci_extcap_auto_command(struct i3c_hci *hci, void __iomem *base)
u32 autocmd_ext_config = readl(base + 0x08);
unsigned int count = FIELD_GET(GENMASK(3, 0), autocmd_ext_config);
dev_info(&hci->master.dev, "%d/%d active auto-command entries\n",
count, max_count);
dev_dbg(&hci->master.dev, "%d/%d active auto-command entries\n",
count, max_count);
/* remember auto-command register location for later use */
hci->AUTOCMD_regs = base;
return 0;
@ -131,46 +129,46 @@ static int hci_extcap_auto_command(struct i3c_hci *hci, void __iomem *base)
static int hci_extcap_debug(struct i3c_hci *hci, void __iomem *base)
{
dev_info(&hci->master.dev, "debug registers present\n");
dev_dbg(&hci->master.dev, "debug registers present\n");
hci->DEBUG_regs = base;
return 0;
}
static int hci_extcap_scheduled_cmd(struct i3c_hci *hci, void __iomem *base)
{
dev_info(&hci->master.dev, "scheduled commands available\n");
dev_dbg(&hci->master.dev, "scheduled commands available\n");
/* hci->schedcmd_regs = base; */
return 0;
}
static int hci_extcap_non_curr_master(struct i3c_hci *hci, void __iomem *base)
{
dev_info(&hci->master.dev, "Non-Current Master support available\n");
dev_dbg(&hci->master.dev, "Non-Current Master support available\n");
/* hci->NCM_regs = base; */
return 0;
}
static int hci_extcap_ccc_resp_conf(struct i3c_hci *hci, void __iomem *base)
{
dev_info(&hci->master.dev, "CCC Response Configuration available\n");
dev_dbg(&hci->master.dev, "CCC Response Configuration available\n");
return 0;
}
static int hci_extcap_global_DAT(struct i3c_hci *hci, void __iomem *base)
{
dev_info(&hci->master.dev, "Global DAT available\n");
dev_dbg(&hci->master.dev, "Global DAT available\n");
return 0;
}
static int hci_extcap_multilane(struct i3c_hci *hci, void __iomem *base)
{
dev_info(&hci->master.dev, "Master Multi-Lane support available\n");
dev_dbg(&hci->master.dev, "Master Multi-Lane support available\n");
return 0;
}
static int hci_extcap_ncm_multilane(struct i3c_hci *hci, void __iomem *base)
{
dev_info(&hci->master.dev, "NCM Multi-Lane support available\n");
dev_dbg(&hci->master.dev, "NCM Multi-Lane support available\n");
return 0;
}
@ -203,7 +201,7 @@ static const struct hci_ext_caps ext_capabilities[] = {
static int hci_extcap_vendor_NXP(struct i3c_hci *hci, void __iomem *base)
{
hci->vendor_data = (__force void *)base;
dev_info(&hci->master.dev, "Build Date Info = %#x\n", readl(base + 1*4));
dev_dbg(&hci->master.dev, "Build Date Info = %#x\n", readl(base + 1 * 4));
/* reset the FPGA */
writel(0xdeadbeef, base + 1*4);
return 0;
@ -241,9 +239,8 @@ static int hci_extcap_vendor_specific(struct i3c_hci *hci, void __iomem *base,
}
if (!vendor_cap_entry) {
dev_notice(&hci->master.dev,
"unknown ext_cap 0x%02x for vendor 0x%02x\n",
cap_id, hci->vendor_mipi_id);
dev_dbg(&hci->master.dev, "unknown ext_cap 0x%02x for vendor 0x%02x\n",
cap_id, hci->vendor_mipi_id);
return 0;
}
if (cap_length < vendor_cap_entry->min_length) {
@ -272,7 +269,7 @@ int i3c_hci_parse_ext_caps(struct i3c_hci *hci)
cap_length = FIELD_GET(CAP_HEADER_LENGTH, cap_header);
dev_dbg(&hci->master.dev, "id=0x%02x length=%d",
cap_id, cap_length);
if (!cap_length)
if (!cap_id || !cap_length)
break;
if (curr_cap + cap_length * 4 >= end) {
dev_err(&hci->master.dev,
@ -296,8 +293,7 @@ int i3c_hci_parse_ext_caps(struct i3c_hci *hci)
}
}
if (!cap_entry) {
dev_notice(&hci->master.dev,
"unknown ext_cap 0x%02x\n", cap_id);
dev_dbg(&hci->master.dev, "unknown ext_cap 0x%02x\n", cap_id);
} else if (cap_length < cap_entry->min_length) {
dev_err(&hci->master.dev,
"ext_cap 0x%02x has size %d (expecting >= %d)\n",

View file

@ -13,7 +13,6 @@
/* MIPI vendor IDs */
#define MIPI_VENDOR_NXP 0x11b
int i3c_hci_parse_ext_caps(struct i3c_hci *hci);
#endif

View file

@ -31,6 +31,11 @@
struct hci_cmd_ops;
struct dat_words {
u32 w0;
u32 w1;
};
/* Our main structure */
struct i3c_hci {
struct i3c_master_controller master;
@ -46,23 +51,25 @@ struct i3c_hci {
void *io_data;
const struct hci_cmd_ops *cmd;
atomic_t next_cmd_tid;
bool irq_inactive;
u32 caps;
unsigned int quirks;
unsigned int DAT_entries;
unsigned int DAT_entry_size;
void *DAT_data;
struct dat_words *DAT;
unsigned int DCT_entries;
unsigned int DCT_entry_size;
u8 version_major;
u8 version_minor;
u8 revision;
u8 dyn_addr;
u32 vendor_mipi_id;
u32 vendor_version_id;
u32 vendor_product_id;
void *vendor_data;
};
/*
* Structure to represent a master initiated transfer.
* The rnw, data and data_len fields must be initialized before calling any
@ -108,7 +115,6 @@ static inline void hci_free_xfer(struct hci_xfer *xfer, unsigned int n)
kfree(xfer);
}
/* This abstracts PIO vs DMA operations */
struct hci_io_ops {
bool (*irq_handler)(struct i3c_hci *hci);
@ -121,25 +127,25 @@ struct hci_io_ops {
struct i3c_ibi_slot *slot);
int (*init)(struct i3c_hci *hci);
void (*cleanup)(struct i3c_hci *hci);
void (*suspend)(struct i3c_hci *hci);
void (*resume)(struct i3c_hci *hci);
};
extern const struct hci_io_ops mipi_i3c_hci_pio;
extern const struct hci_io_ops mipi_i3c_hci_dma;
/* Our per device master private data */
struct i3c_hci_dev_data {
int dat_idx;
void *ibi_data;
};
/* list of quirks */
#define HCI_QUIRK_RAW_CCC BIT(1) /* CCC framing must be explicit */
#define HCI_QUIRK_PIO_MODE BIT(2) /* Set PIO mode for AMD platforms */
#define HCI_QUIRK_OD_PP_TIMING BIT(3) /* Set OD and PP timings for AMD platforms */
#define HCI_QUIRK_RESP_BUF_THLD BIT(4) /* Set resp buf thld to 0 for AMD platforms */
#define HCI_QUIRK_RPM_ALLOWED BIT(5) /* Runtime PM allowed */
/* global functions */
void mipi_i3c_hci_resume(struct i3c_hci *hci);
@ -147,5 +153,6 @@ void mipi_i3c_hci_pio_reset(struct i3c_hci *hci);
void mipi_i3c_hci_dct_index_reset(struct i3c_hci *hci);
void amd_set_od_pp_timing(struct i3c_hci *hci);
void amd_set_resp_buf_thld(struct i3c_hci *hci);
void i3c_hci_sync_irq_inactive(struct i3c_hci *hci);
#endif

View file

@ -12,14 +12,23 @@
#include <linux/idr.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_data/mipi-i3c-hci.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
#include <linux/pm_runtime.h>
/*
* There can up to 15 instances, but implementations have at most 2 at this
* time.
*/
#define INST_MAX 2
struct mipi_i3c_hci_pci {
struct pci_dev *pci;
struct platform_device *pdev;
void __iomem *base;
const struct mipi_i3c_hci_pci_info *info;
void *private;
};
@ -27,12 +36,13 @@ struct mipi_i3c_hci_pci {
struct mipi_i3c_hci_pci_info {
int (*init)(struct mipi_i3c_hci_pci *hci);
void (*exit)(struct mipi_i3c_hci_pci *hci);
const char *name;
int id[INST_MAX];
u32 instance_offset[INST_MAX];
int instance_count;
};
static DEFINE_IDA(mipi_i3c_hci_pci_ida);
#define INTEL_PRIV_OFFSET 0x2b0
#define INTEL_PRIV_SIZE 0x28
#define INTEL_RESETS 0x04
#define INTEL_RESETS_RESET BIT(0)
#define INTEL_RESETS_RESET_DONE BIT(1)
@ -143,19 +153,12 @@ static void intel_reset(void __iomem *priv)
writel(INTEL_RESETS_RESET, priv + INTEL_RESETS);
}
static void __iomem *intel_priv(struct pci_dev *pci)
{
resource_size_t base = pci_resource_start(pci, 0);
return devm_ioremap(&pci->dev, base + INTEL_PRIV_OFFSET, INTEL_PRIV_SIZE);
}
static int intel_i3c_init(struct mipi_i3c_hci_pci *hci)
{
struct intel_host *host = devm_kzalloc(&hci->pci->dev, sizeof(*host), GFP_KERNEL);
void __iomem *priv = intel_priv(hci->pci);
void __iomem *priv = hci->base + INTEL_PRIV_OFFSET;
if (!host || !priv)
if (!host)
return -ENOMEM;
dma_set_mask_and_coherent(&hci->pci->dev, DMA_BIT_MASK(64));
@ -179,17 +182,89 @@ static void intel_i3c_exit(struct mipi_i3c_hci_pci *hci)
intel_ltr_hide(&hci->pci->dev);
}
static const struct mipi_i3c_hci_pci_info intel_info = {
static const struct mipi_i3c_hci_pci_info intel_mi_1_info = {
.init = intel_i3c_init,
.exit = intel_i3c_exit,
.name = "intel-lpss-i3c",
.id = {0, 1},
.instance_offset = {0, 0x400},
.instance_count = 2,
};
static const struct mipi_i3c_hci_pci_info intel_mi_2_info = {
.init = intel_i3c_init,
.exit = intel_i3c_exit,
.name = "intel-lpss-i3c",
.id = {2, 3},
.instance_offset = {0, 0x400},
.instance_count = 2,
};
static const struct mipi_i3c_hci_pci_info intel_si_2_info = {
.init = intel_i3c_init,
.exit = intel_i3c_exit,
.name = "intel-lpss-i3c",
.id = {2},
.instance_offset = {0},
.instance_count = 1,
};
static void mipi_i3c_hci_pci_rpm_allow(struct device *dev)
{
pm_runtime_put(dev);
pm_runtime_allow(dev);
}
static void mipi_i3c_hci_pci_rpm_forbid(struct device *dev)
{
pm_runtime_forbid(dev);
pm_runtime_get_sync(dev);
}
struct mipi_i3c_hci_pci_cell_data {
struct mipi_i3c_hci_platform_data pdata;
struct resource res;
};
static void mipi_i3c_hci_pci_setup_cell(struct mipi_i3c_hci_pci *hci, int idx,
struct mipi_i3c_hci_pci_cell_data *data,
struct mfd_cell *cell)
{
data->pdata.base_regs = hci->base + hci->info->instance_offset[idx];
data->res = DEFINE_RES_IRQ(0);
cell->name = hci->info->name;
cell->id = hci->info->id[idx];
cell->platform_data = &data->pdata;
cell->pdata_size = sizeof(data->pdata);
cell->num_resources = 1;
cell->resources = &data->res;
}
#define mipi_i3c_hci_pci_alloc(h, x) kcalloc((h)->info->instance_count, sizeof(*(x)), GFP_KERNEL)
static int mipi_i3c_hci_pci_add_instances(struct mipi_i3c_hci_pci *hci)
{
struct mipi_i3c_hci_pci_cell_data *data __free(kfree) = mipi_i3c_hci_pci_alloc(hci, data);
struct mfd_cell *cells __free(kfree) = mipi_i3c_hci_pci_alloc(hci, cells);
int irq = pci_irq_vector(hci->pci, 0);
int nr = hci->info->instance_count;
if (!cells || !data)
return -ENOMEM;
for (int i = 0; i < nr; i++)
mipi_i3c_hci_pci_setup_cell(hci, i, data + i, cells + i);
return mfd_add_devices(&hci->pci->dev, 0, cells, nr, NULL, irq, NULL);
}
static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,
const struct pci_device_id *id)
{
struct mipi_i3c_hci_pci *hci;
struct resource res[2];
int dev_id, ret;
int ret;
hci = devm_kzalloc(&pci->dev, sizeof(*hci), GFP_KERNEL);
if (!hci)
@ -203,81 +278,65 @@ static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,
pci_set_master(pci);
memset(&res, 0, sizeof(res));
hci->base = pcim_iomap_region(pci, 0, pci_name(pci));
if (IS_ERR(hci->base))
return PTR_ERR(hci->base);
res[0].flags = IORESOURCE_MEM;
res[0].start = pci_resource_start(pci, 0);
res[0].end = pci_resource_end(pci, 0);
res[1].flags = IORESOURCE_IRQ;
res[1].start = pci->irq;
res[1].end = pci->irq;
dev_id = ida_alloc(&mipi_i3c_hci_pci_ida, GFP_KERNEL);
if (dev_id < 0)
return dev_id;
hci->pdev = platform_device_alloc("mipi-i3c-hci", dev_id);
if (!hci->pdev)
return -ENOMEM;
hci->pdev->dev.parent = &pci->dev;
device_set_node(&hci->pdev->dev, dev_fwnode(&pci->dev));
ret = platform_device_add_resources(hci->pdev, res, ARRAY_SIZE(res));
if (ret)
goto err;
ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_ALL_TYPES);
if (ret < 0)
return ret;
hci->info = (const struct mipi_i3c_hci_pci_info *)id->driver_data;
if (hci->info && hci->info->init) {
ret = hci->info->init(hci);
if (ret)
goto err;
}
ret = platform_device_add(hci->pdev);
ret = hci->info->init ? hci->info->init(hci) : 0;
if (ret)
return ret;
ret = mipi_i3c_hci_pci_add_instances(hci);
if (ret)
goto err_exit;
pci_set_drvdata(pci, hci);
mipi_i3c_hci_pci_rpm_allow(&pci->dev);
return 0;
err_exit:
if (hci->info && hci->info->exit)
if (hci->info->exit)
hci->info->exit(hci);
err:
platform_device_put(hci->pdev);
ida_free(&mipi_i3c_hci_pci_ida, dev_id);
return ret;
}
static void mipi_i3c_hci_pci_remove(struct pci_dev *pci)
{
struct mipi_i3c_hci_pci *hci = pci_get_drvdata(pci);
struct platform_device *pdev = hci->pdev;
int dev_id = pdev->id;
if (hci->info && hci->info->exit)
if (hci->info->exit)
hci->info->exit(hci);
platform_device_unregister(pdev);
ida_free(&mipi_i3c_hci_pci_ida, dev_id);
mipi_i3c_hci_pci_rpm_forbid(&pci->dev);
mfd_remove_devices(&pci->dev);
}
/* PM ops must exist for PCI to put a device to a low power state */
static const struct dev_pm_ops mipi_i3c_hci_pci_pm_ops = {
};
static const struct pci_device_id mipi_i3c_hci_pci_devices[] = {
/* Wildcat Lake-U */
{ PCI_VDEVICE(INTEL, 0x4d7c), (kernel_ulong_t)&intel_info},
{ PCI_VDEVICE(INTEL, 0x4d6f), (kernel_ulong_t)&intel_info},
{ PCI_VDEVICE(INTEL, 0x4d7c), (kernel_ulong_t)&intel_mi_1_info},
{ PCI_VDEVICE(INTEL, 0x4d6f), (kernel_ulong_t)&intel_si_2_info},
/* Panther Lake-H */
{ PCI_VDEVICE(INTEL, 0xe37c), (kernel_ulong_t)&intel_info},
{ PCI_VDEVICE(INTEL, 0xe36f), (kernel_ulong_t)&intel_info},
{ PCI_VDEVICE(INTEL, 0xe37c), (kernel_ulong_t)&intel_mi_1_info},
{ PCI_VDEVICE(INTEL, 0xe36f), (kernel_ulong_t)&intel_si_2_info},
/* Panther Lake-P */
{ PCI_VDEVICE(INTEL, 0xe47c), (kernel_ulong_t)&intel_info},
{ PCI_VDEVICE(INTEL, 0xe46f), (kernel_ulong_t)&intel_info},
{ PCI_VDEVICE(INTEL, 0xe47c), (kernel_ulong_t)&intel_mi_1_info},
{ PCI_VDEVICE(INTEL, 0xe46f), (kernel_ulong_t)&intel_si_2_info},
/* Nova Lake-S */
{ PCI_VDEVICE(INTEL, 0x6e2c), (kernel_ulong_t)&intel_info},
{ PCI_VDEVICE(INTEL, 0x6e2d), (kernel_ulong_t)&intel_info},
{ PCI_VDEVICE(INTEL, 0x6e2c), (kernel_ulong_t)&intel_mi_1_info},
{ PCI_VDEVICE(INTEL, 0x6e2d), (kernel_ulong_t)&intel_mi_2_info},
{ },
};
MODULE_DEVICE_TABLE(pci, mipi_i3c_hci_pci_devices);
@ -287,6 +346,9 @@ static struct pci_driver mipi_i3c_hci_pci_driver = {
.id_table = mipi_i3c_hci_pci_devices,
.probe = mipi_i3c_hci_pci_probe,
.remove = mipi_i3c_hci_pci_remove,
.driver = {
.pm = pm_ptr(&mipi_i3c_hci_pci_pm_ops)
},
};
module_pci_driver(mipi_i3c_hci_pci_driver);

View file

@ -15,7 +15,6 @@
#include "cmd.h"
#include "ibi.h"
/*
* PIO Access Area
*/
@ -136,27 +135,14 @@ struct hci_pio_data {
u32 enabled_irqs;
};
static int hci_pio_init(struct i3c_hci *hci)
static void __hci_pio_init(struct i3c_hci *hci, u32 *size_val_ptr)
{
struct hci_pio_data *pio;
u32 val, size_val, rx_thresh, tx_thresh, ibi_val;
pio = kzalloc(sizeof(*pio), GFP_KERNEL);
if (!pio)
return -ENOMEM;
hci->io_data = pio;
spin_lock_init(&pio->lock);
struct hci_pio_data *pio = hci->io_data;
size_val = pio_reg_read(QUEUE_SIZE);
dev_info(&hci->master.dev, "CMD/RESP FIFO = %ld entries\n",
FIELD_GET(CR_QUEUE_SIZE, size_val));
dev_info(&hci->master.dev, "IBI FIFO = %ld bytes\n",
4 * FIELD_GET(IBI_STATUS_SIZE, size_val));
dev_info(&hci->master.dev, "RX data FIFO = %d bytes\n",
4 * (2 << FIELD_GET(RX_DATA_BUFFER_SIZE, size_val)));
dev_info(&hci->master.dev, "TX data FIFO = %d bytes\n",
4 * (2 << FIELD_GET(TX_DATA_BUFFER_SIZE, size_val)));
if (size_val_ptr)
*size_val_ptr = size_val;
/*
* Let's initialize data thresholds to half of the actual FIFO size.
@ -202,6 +188,42 @@ static int hci_pio_init(struct i3c_hci *hci)
/* Always accept error interrupts (will be activated on first xfer) */
pio->enabled_irqs = STAT_ALL_ERRORS;
}
static void hci_pio_suspend(struct i3c_hci *hci)
{
pio_reg_write(INTR_SIGNAL_ENABLE, 0);
i3c_hci_sync_irq_inactive(hci);
}
static void hci_pio_resume(struct i3c_hci *hci)
{
__hci_pio_init(hci, NULL);
}
static int hci_pio_init(struct i3c_hci *hci)
{
struct hci_pio_data *pio;
u32 size_val;
pio = devm_kzalloc(hci->master.dev.parent, sizeof(*pio), GFP_KERNEL);
if (!pio)
return -ENOMEM;
hci->io_data = pio;
spin_lock_init(&pio->lock);
__hci_pio_init(hci, &size_val);
dev_dbg(&hci->master.dev, "CMD/RESP FIFO = %ld entries\n",
FIELD_GET(CR_QUEUE_SIZE, size_val));
dev_dbg(&hci->master.dev, "IBI FIFO = %ld bytes\n",
4 * FIELD_GET(IBI_STATUS_SIZE, size_val));
dev_dbg(&hci->master.dev, "RX data FIFO = %d bytes\n",
4 * (2 << FIELD_GET(RX_DATA_BUFFER_SIZE, size_val)));
dev_dbg(&hci->master.dev, "TX data FIFO = %d bytes\n",
4 * (2 << FIELD_GET(TX_DATA_BUFFER_SIZE, size_val)));
return 0;
}
@ -212,6 +234,8 @@ static void hci_pio_cleanup(struct i3c_hci *hci)
pio_reg_write(INTR_SIGNAL_ENABLE, 0x0);
i3c_hci_sync_irq_inactive(hci);
if (pio) {
dev_dbg(&hci->master.dev, "status = %#x/%#x",
pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
@ -219,8 +243,6 @@ static void hci_pio_cleanup(struct i3c_hci *hci)
BUG_ON(pio->curr_rx);
BUG_ON(pio->curr_tx);
BUG_ON(pio->curr_resp);
kfree(pio);
hci->io_data = NULL;
}
}
@ -1049,4 +1071,6 @@ const struct hci_io_ops mipi_i3c_hci_pio = {
.request_ibi = hci_pio_request_ibi,
.free_ibi = hci_pio_free_ibi,
.recycle_ibi_slot = hci_pio_recycle_ibi_slot,
.suspend = hci_pio_suspend,
.resume = hci_pio_resume,
};

View file

@ -198,6 +198,8 @@
#define RENESAS_I3C_MAX_DEVS 8
#define I2C_INIT_MSG -1
#define RENESAS_I3C_TCLK_IDX 1
enum i3c_internal_state {
I3C_INTERNAL_STATE_DISABLED,
I3C_INTERNAL_STATE_CONTROLLER_IDLE,
@ -254,12 +256,19 @@ struct renesas_i3c {
enum i3c_internal_state internal_state;
u16 maxdevs;
u32 free_pos;
u32 dyn_addr;
u32 i2c_STDBR;
u32 i3c_STDBR;
unsigned long rate;
u8 addrs[RENESAS_I3C_MAX_DEVS];
struct renesas_i3c_xferqueue xferqueue;
void __iomem *regs;
struct clk *tclk;
u32 *DATBASn;
struct clk_bulk_data *clks;
struct reset_control *presetn;
struct reset_control *tresetn;
u8 num_clks;
u8 refclk_div;
};
struct renesas_i3c_i2c_dev_data {
@ -272,10 +281,6 @@ struct renesas_i3c_irq_desc {
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);
@ -477,92 +482,16 @@ static int renesas_i3c_reset(struct renesas_i3c *i3c)
0, 1000, false, i3c->regs, RSTCTL);
}
static int renesas_i3c_bus_init(struct i3c_master_controller *m)
static void renesas_i3c_hw_init(struct renesas_i3c *i3c)
{
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));
u32 val;
/* Disable Slave Mode */
renesas_writel(i3c->regs, SVCTL, 0);
/* Initialize Queue/Buffer threshold */
renesas_writel(i3c->regs, NQTHCTL, NQTHCTL_IBIDSSZ(6) |
NQTHCTL_CMDQTH(1));
NQTHCTL_CMDQTH(1));
/* The only supported configuration is two entries*/
renesas_writel(i3c->regs, NTBTHCTL0, 0);
@ -586,25 +515,113 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
renesas_set_bit(i3c->regs, BCTL, BCTL_HJACKCTL);
renesas_writel(i3c->regs, IBINCTL, IBINCTL_NRHJCTL | IBINCTL_NRMRCTL |
IBINCTL_NRSIRCTL);
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);
val = DIV_ROUND_UP(I3C_BUS_TBUF_MIXED_FM_MIN_NS,
NSEC_PER_SEC / i3c->rate);
renesas_writel(i3c->regs, BFRECDT, BFRECDT_FRECYC(val));
val = DIV_ROUND_UP(I3C_BUS_TAVAL_MIN_NS, NSEC_PER_SEC / rate);
val = DIV_ROUND_UP(I3C_BUS_TAVAL_MIN_NS,
NSEC_PER_SEC / i3c->rate);
renesas_writel(i3c->regs, BAVLCDT, BAVLCDT_AVLCYC(val));
val = DIV_ROUND_UP(I3C_BUS_TIDLE_MIN_NS, NSEC_PER_SEC / rate);
val = DIV_ROUND_UP(I3C_BUS_TIDLE_MIN_NS,
NSEC_PER_SEC / i3c->rate);
renesas_writel(i3c->regs, BIDLCDT, BIDLCDT_IDLCYC(val));
}
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;
u32 double_SBR;
int cks, pp_high_ticks, pp_low_ticks, i3c_total_ticks;
int od_high_ticks, od_low_ticks, i2c_total_ticks;
int ret;
i3c->rate = clk_get_rate(i3c->clks[RENESAS_I3C_TCLK_IDX].clk);
if (!i3c->rate)
return -EINVAL;
ret = renesas_i3c_reset(i3c);
if (ret)
return ret;
i2c_total_ticks = DIV_ROUND_UP(i3c->rate, bus->scl_rate.i2c);
i3c_total_ticks = DIV_ROUND_UP(i3c->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 / i3c->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;
i3c->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 / i3c->rate) + 1;
od_high_ticks -= t.scl_rise_ns / (NSEC_PER_SEC / i3c->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));
i3c->refclk_div = cks;
/* I3C hw init*/
renesas_i3c_hw_init(i3c);
ret = i3c_master_get_free_addr(m, 0);
if (ret < 0)
return ret;
i3c->dyn_addr = ret;
renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYAD(ret) | MSDVAD_MDYADV);
memset(&info, 0, sizeof(info));
@ -1301,14 +1318,8 @@ static const struct renesas_i3c_irq_desc renesas_i3c_irqs[] = {
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;
@ -1317,28 +1328,21 @@ static int renesas_i3c_probe(struct platform_device *pdev)
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);
ret = devm_clk_bulk_get_all_enabled(&pdev->dev, &i3c->clks);
if (ret <= RENESAS_I3C_TCLK_IDX)
return dev_err_probe(&pdev->dev, ret < 0 ? ret : -EINVAL,
"Failed to get clocks (need > %d, got %d)\n",
RENESAS_I3C_TCLK_IDX, ret);
i3c->num_clks = ret;
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),
i3c->tresetn = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "tresetn");
if (IS_ERR(i3c->tresetn))
return dev_err_probe(&pdev->dev, PTR_ERR(i3c->tresetn),
"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),
i3c->presetn = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "presetn");
if (IS_ERR(i3c->presetn))
return dev_err_probe(&pdev->dev, PTR_ERR(i3c->presetn),
"Error: missing presetn ctrl\n");
spin_lock_init(&i3c->xferqueue.lock);
@ -1364,6 +1368,12 @@ static int renesas_i3c_probe(struct platform_device *pdev)
i3c->maxdevs = RENESAS_I3C_MAX_DEVS;
i3c->free_pos = GENMASK(i3c->maxdevs - 1, 0);
/* Allocate dynamic Device Address Table backup. */
i3c->DATBASn = devm_kzalloc(&pdev->dev, sizeof(u32) * i3c->maxdevs,
GFP_KERNEL);
if (!i3c->DATBASn)
return -ENOMEM;
return i3c_master_register(&i3c->base, &pdev->dev, &renesas_i3c_ops, false);
}
@ -1374,16 +1384,86 @@ static void renesas_i3c_remove(struct platform_device *pdev)
i3c_master_unregister(&i3c->base);
}
static const struct renesas_i3c_config empty_i3c_config = {
};
static int renesas_i3c_suspend_noirq(struct device *dev)
{
struct renesas_i3c *i3c = dev_get_drvdata(dev);
int i, ret;
static const struct renesas_i3c_config r9a09g047_i3c_config = {
.has_pclkrw = 1,
i2c_mark_adapter_suspended(&i3c->base.i2c);
/* Store Device Address Table values. */
for (i = 0; i < i3c->maxdevs; i++)
i3c->DATBASn[i] = renesas_readl(i3c->regs, DATBAS(i));
ret = reset_control_assert(i3c->presetn);
if (ret)
goto err_mark_resumed;
ret = reset_control_assert(i3c->tresetn);
if (ret)
goto err_presetn;
clk_bulk_disable(i3c->num_clks, i3c->clks);
return 0;
err_presetn:
reset_control_deassert(i3c->presetn);
err_mark_resumed:
i2c_mark_adapter_resumed(&i3c->base.i2c);
return ret;
}
static int renesas_i3c_resume_noirq(struct device *dev)
{
struct renesas_i3c *i3c = dev_get_drvdata(dev);
int i, ret;
ret = reset_control_deassert(i3c->presetn);
if (ret)
return ret;
ret = reset_control_deassert(i3c->tresetn);
if (ret)
goto err_presetn;
ret = clk_bulk_enable(i3c->num_clks, i3c->clks);
if (ret)
goto err_tresetn;
/* Re-store I3C registers value. */
renesas_writel(i3c->regs, REFCKCTL,
REFCKCTL_IREFCKS(i3c->refclk_div));
renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYADV |
MSDVAD_MDYAD(i3c->dyn_addr));
/* Restore Device Address Table values. */
for (i = 0; i < i3c->maxdevs; i++)
renesas_writel(i3c->regs, DATBAS(i), i3c->DATBASn[i]);
/* I3C hw init. */
renesas_i3c_hw_init(i3c);
i2c_mark_adapter_resumed(&i3c->base.i2c);
return 0;
err_tresetn:
reset_control_assert(i3c->tresetn);
err_presetn:
reset_control_assert(i3c->presetn);
return ret;
}
static const struct dev_pm_ops renesas_i3c_pm_ops = {
NOIRQ_SYSTEM_SLEEP_PM_OPS(renesas_i3c_suspend_noirq,
renesas_i3c_resume_noirq)
};
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 },
{ .compatible = "renesas,r9a08g045-i3c" },
{ .compatible = "renesas,r9a09g047-i3c" },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, renesas_i3c_of_ids);
@ -1394,6 +1474,7 @@ static struct platform_driver renesas_i3c = {
.driver = {
.name = "renesas-i3c",
.of_match_table = renesas_i3c_of_ids,
.pm = pm_sleep_ptr(&renesas_i3c_pm_ops),
},
};
module_platform_driver(renesas_i3c);

View file

@ -533,8 +533,8 @@ static int svc_i3c_master_handle_ibi_won(struct svc_i3c_master *master, u32 msta
static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master)
{
struct svc_i3c_i2c_dev_data *data;
struct i3c_dev_desc *dev = NULL;
unsigned int ibitype, ibiaddr;
struct i3c_dev_desc *dev;
u32 status, val;
int ret;
@ -627,7 +627,7 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master)
* for the slave to interrupt again.
*/
if (svc_i3c_master_error(master)) {
if (master->ibi.tbq_slot) {
if (master->ibi.tbq_slot && dev) {
data = i3c_dev_get_master_data(dev);
i3c_generic_ibi_recycle_slot(data->ibi_pool,
master->ibi.tbq_slot);

View file

@ -25,7 +25,7 @@
* @I3C_ERROR_M2: M2 error
*
* These are the standard error codes as defined by the I3C specification.
* When -EIO is returned by the i3c_device_do_priv_xfers() or
* When -EIO is returned by the i3c_device_do_i3c_xfers() or
* i3c_device_send_hdr_cmds() one can check the error code in
* &struct_i3c_xfer.err or &struct i3c_hdr_cmd.err to get a better idea of
* what went wrong.
@ -79,9 +79,6 @@ struct i3c_xfer {
enum i3c_error_code err;
};
/* keep back compatible */
#define i3c_priv_xfer i3c_xfer
/**
* enum i3c_dcr - I3C DCR values
* @I3C_DCR_GENERIC_DEVICE: generic I3C device
@ -308,16 +305,24 @@ static __always_inline void i3c_i2c_driver_unregister(struct i3c_driver *i3cdrv,
i3c_i2c_driver_unregister, \
__i2cdrv)
#if IS_ENABLED(CONFIG_I3C)
int i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers,
int nxfers, enum i3c_xfer_mode mode);
static inline int i3c_device_do_priv_xfers(struct i3c_device *dev,
struct i3c_xfer *xfers,
int nxfers)
u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev);
#else
static inline int
i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers,
int nxfers, enum i3c_xfer_mode mode)
{
return i3c_device_do_xfers(dev, xfers, nxfers, I3C_SDR);
return -EOPNOTSUPP;
}
static inline u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev)
{
return 0;
}
#endif
int i3c_device_do_setdasa(struct i3c_device *dev);
void i3c_device_get_info(const struct i3c_device *dev, struct i3c_device_info *info);
@ -358,6 +363,5 @@ int i3c_device_request_ibi(struct i3c_device *dev,
void i3c_device_free_ibi(struct i3c_device *dev);
int i3c_device_enable_ibi(struct i3c_device *dev);
int i3c_device_disable_ibi(struct i3c_device *dev);
u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev);
#endif /* I3C_DEV_H */

View file

@ -462,6 +462,8 @@ struct i3c_bus {
* @enable_hotjoin: enable hot join event detect.
* @disable_hotjoin: disable hot join event detect.
* @set_speed: adjust I3C open drain mode timing.
* @set_dev_nack_retry: configure device NACK retry count for the master
* controller.
*/
struct i3c_master_controller_ops {
int (*bus_init)(struct i3c_master_controller *master);
@ -491,6 +493,8 @@ struct i3c_master_controller_ops {
int (*enable_hotjoin)(struct i3c_master_controller *master);
int (*disable_hotjoin)(struct i3c_master_controller *master);
int (*set_speed)(struct i3c_master_controller *master, enum i3c_open_drain_speed speed);
int (*set_dev_nack_retry)(struct i3c_master_controller *master,
unsigned long dev_nack_retry_cnt);
};
/**
@ -505,6 +509,8 @@ struct i3c_master_controller_ops {
* @secondary: true if the master is a secondary master
* @init_done: true when the bus initialization is done
* @hotjoin: true if the master support hotjoin
* @rpm_allowed: true if Runtime PM allowed
* @rpm_ibi_allowed: true if IBI and Hot-Join allowed while runtime suspended
* @boardinfo.i3c: list of I3C boardinfo objects
* @boardinfo.i2c: list of I2C boardinfo objects
* @boardinfo: board-level information attached to devices connected on the bus
@ -514,6 +520,7 @@ struct i3c_master_controller_ops {
* in a thread context. Typical examples are Hot Join processing which
* requires taking the bus lock in maintenance, which in turn, can only
* be done from a sleep-able context
* @dev_nack_retry_count: retry count when slave device nack
*
* A &struct i3c_master_controller has to be registered to the I3C subsystem
* through i3c_master_register(). None of &struct i3c_master_controller fields
@ -528,12 +535,15 @@ struct i3c_master_controller {
unsigned int secondary : 1;
unsigned int init_done : 1;
unsigned int hotjoin: 1;
unsigned int rpm_allowed: 1;
unsigned int rpm_ibi_allowed: 1;
struct {
struct list_head i3c;
struct list_head i2c;
} boardinfo;
struct i3c_bus bus;
struct workqueue_struct *wq;
unsigned int dev_nack_retry_count;
};
/**
@ -595,6 +605,7 @@ int i3c_master_get_free_addr(struct i3c_master_controller *master,
int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
u8 addr);
int i3c_master_do_daa(struct i3c_master_controller *master);
int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa);
struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *ptr,
size_t len, bool force_bounce,
enum dma_data_direction dir);

View file

@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef INCLUDE_PLATFORM_DATA_MIPI_I3C_HCI_H
#define INCLUDE_PLATFORM_DATA_MIPI_I3C_HCI_H
#include <linux/compiler_types.h>
/**
* struct mipi_i3c_hci_platform_data - Platform-dependent data for mipi_i3c_hci
* @base_regs: Register set base address (to support multi-bus instances)
*/
struct mipi_i3c_hci_platform_data {
void __iomem *base_regs;
};
#endif