I3C for 6.19
Subsystem: - Add HDR transfer support Drivers: - dw: fix bus hang on Agilex5 - mipi-i3c-hci: Intel Nova Lake-S support, IOMMU support - svc: HDR support -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmk2MWUACgkQY6TcMGxw OjKkBQ/8C7lUVwuCThYHjD1tXamYiZ3iTE2TP3rcn6w6VdFrvZq0I/D3Z67ze0PR kRwC9VRgV7qrOgu7niubRxUNkAsLkVvJBGGf3QKGaozqhGaqnhxoOIGZGVehG0GR DuuR6eCkiIFyG1Z+8rBROkx2K84ne81SPsZ1/E9bB9GJXW1aALJ8PgAblu5VPecO 4YtqVRS8JOxTx5c0fnlpYo5xn3M0jMIhtAI9Z7AAGj0ZvoBaPplHjSM5L/YJCKss KlGpuNMGCS25PsnujDmELn4QStIbMmCOYA6hiRvVk3iDf4zcG4f0Rem+Tr9jstUe t8h8/s7maUs01i2rVBZtcanAY1iYbECiBYmRTHowDcJDj+6/BAz6cKNz7eDv7THl tcPz4RotIAJOPTof9u9va0wt+LcFh6Y6SXVFEP3RYcFrgyjECFjJ+PL0TKIiJOyo pRFgmyAU/qakmfcLEuWe95WJyl27d/pyg6WW4iuKDiVU8/um5M8RxlBCcBiLzmrM j3XOAobE4KSpI/2deBxNdIpDNQyGTxNAyDobPQPU2nrQA0k/w8UmksQF/ZO+/RzM 5ZNfqSMhHO4j6GBIbo0iD+E2hO8l/7awMpiQZyhTrTrmFcBlVdbGttxlk+YYFKa7 7O3VILhoZZZppKIlLike6eOWFm+CiQ11zIGfHj7mnpAy5zggr3g= =qQUB -----END PGP SIGNATURE----- Merge tag 'i3c/for-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux Pull i3c updates from Alexandre Belloni: "HDR support has finally been added. mipi-i3c-hci has been reworked and Intel Nova Lake-S support has been added. Subsystem: - Add HDR transfer support Drivers: - dw: fix bus hang on Agilex5 - mipi-i3c-hci: Intel Nova Lake-S support, IOMMU support - svc: HDR support" * tag 'i3c/for-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux: (28 commits) regmap: i3c: switch to use i3c_xfer from i3c_priv_xfer net: mctp i3c: switch to use i3c_xfer from i3c_priv_xfer hwmon: (lm75): switch to use i3c_xfer from i3c_priv_xfer i3c: document i3c_xfers i3c: fix I3C_SDR bit number i3c: master: svc: Add basic HDR mode support i3c: master: svc: Replace bool rnw with union for HDR support i3c: Switch to use new i3c_xfer from i3c_priv_xfer i3c: Add HDR API support i3c: master: add WQ_PERCPU to alloc_workqueue users i3c: master: Remove i3c_device_free_ibi from i3c_device_remove i3c: mipi-i3c-hci-pci: Set d3cold_delay to 0 for Intel controllers i3c: mipi-i3c-hci-pci: Add LTR support for Intel controllers i3c: mipi-i3c-hci-pci: Add exit callback i3c: mipi-i3c-hci-pci: Change callback parameter i3c: mipi-i3c-hci-pci: Allocate a structure for mipi_i3c_hci_pci device information i3c: mipi-i3c-hci-pci: Factor out intel_reset() i3c: mipi-i3c-hci-pci: Factor out private registers ioremapping i3c: mipi-i3c-hci-pci: Constify driver data i3c: mipi-i3c-hci-pci: Use readl_poll_timeout() ...pull/1354/merge
commit
c2f2b01b74
|
|
@ -14,7 +14,11 @@ allOf:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
const: snps,dw-i3c-master-1.00a
|
||||
oneOf:
|
||||
- const: snps,dw-i3c-master-1.00a
|
||||
- items:
|
||||
- const: altr,agilex5-dw-i3c-master
|
||||
- const: snps,dw-i3c-master-1.00a
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ static int regmap_i3c_write(void *context, const void *data, size_t count)
|
|||
{
|
||||
struct device *dev = context;
|
||||
struct i3c_device *i3c = dev_to_i3cdev(dev);
|
||||
struct i3c_priv_xfer xfers[] = {
|
||||
struct i3c_xfer xfers[] = {
|
||||
{
|
||||
.rnw = false,
|
||||
.len = count,
|
||||
|
|
@ -19,7 +19,7 @@ static int regmap_i3c_write(void *context, const void *data, size_t count)
|
|||
},
|
||||
};
|
||||
|
||||
return i3c_device_do_priv_xfers(i3c, xfers, ARRAY_SIZE(xfers));
|
||||
return i3c_device_do_xfers(i3c, xfers, ARRAY_SIZE(xfers), I3C_SDR);
|
||||
}
|
||||
|
||||
static int regmap_i3c_read(void *context,
|
||||
|
|
@ -28,7 +28,7 @@ static int regmap_i3c_read(void *context,
|
|||
{
|
||||
struct device *dev = context;
|
||||
struct i3c_device *i3c = dev_to_i3cdev(dev);
|
||||
struct i3c_priv_xfer xfers[2];
|
||||
struct i3c_xfer xfers[2];
|
||||
|
||||
xfers[0].rnw = false;
|
||||
xfers[0].len = reg_size;
|
||||
|
|
@ -38,7 +38,7 @@ static int regmap_i3c_read(void *context,
|
|||
xfers[1].len = val_size;
|
||||
xfers[1].data.in = val;
|
||||
|
||||
return i3c_device_do_priv_xfers(i3c, xfers, ARRAY_SIZE(xfers));
|
||||
return i3c_device_do_xfers(i3c, xfers, ARRAY_SIZE(xfers), I3C_SDR);
|
||||
}
|
||||
|
||||
static const struct regmap_bus regmap_i3c = {
|
||||
|
|
|
|||
|
|
@ -621,7 +621,7 @@ static int lm75_i3c_reg_read(void *context, unsigned int reg, unsigned int *val)
|
|||
{
|
||||
struct i3c_device *i3cdev = context;
|
||||
struct lm75_data *data = i3cdev_get_drvdata(i3cdev);
|
||||
struct i3c_priv_xfer xfers[] = {
|
||||
struct i3c_xfer xfers[] = {
|
||||
{
|
||||
.rnw = false,
|
||||
.len = 1,
|
||||
|
|
@ -640,7 +640,7 @@ static int lm75_i3c_reg_read(void *context, unsigned int reg, unsigned int *val)
|
|||
if (reg == LM75_REG_CONF && !data->params->config_reg_16bits)
|
||||
xfers[1].len--;
|
||||
|
||||
ret = i3c_device_do_priv_xfers(i3cdev, xfers, 2);
|
||||
ret = i3c_device_do_xfers(i3cdev, xfers, 2, I3C_SDR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -658,7 +658,7 @@ static int lm75_i3c_reg_write(void *context, unsigned int reg, unsigned int val)
|
|||
{
|
||||
struct i3c_device *i3cdev = context;
|
||||
struct lm75_data *data = i3cdev_get_drvdata(i3cdev);
|
||||
struct i3c_priv_xfer xfers[] = {
|
||||
struct i3c_xfer xfers[] = {
|
||||
{
|
||||
.rnw = false,
|
||||
.len = 3,
|
||||
|
|
@ -680,7 +680,7 @@ static int lm75_i3c_reg_write(void *context, unsigned int reg, unsigned int val)
|
|||
data->val_buf[2] = val & 0xff;
|
||||
}
|
||||
|
||||
return i3c_device_do_priv_xfers(i3cdev, xfers, 1);
|
||||
return i3c_device_do_xfers(i3cdev, xfers, 1, I3C_SDR);
|
||||
}
|
||||
|
||||
static const struct regmap_bus lm75_i3c_regmap_bus = {
|
||||
|
|
|
|||
|
|
@ -15,12 +15,12 @@
|
|||
#include "internals.h"
|
||||
|
||||
/**
|
||||
* i3c_device_do_priv_xfers() - do I3C SDR private transfers directed to a
|
||||
* specific device
|
||||
* i3c_device_do_xfers() - do I3C transfers directed to a specific device
|
||||
*
|
||||
* @dev: device with which the transfers should be done
|
||||
* @xfers: array of transfers
|
||||
* @nxfers: number of transfers
|
||||
* @mode: transfer mode
|
||||
*
|
||||
* Initiate one or several private SDR transfers with @dev.
|
||||
*
|
||||
|
|
@ -33,9 +33,8 @@
|
|||
* '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,
|
||||
int nxfers)
|
||||
int i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers,
|
||||
int nxfers, enum i3c_xfer_mode mode)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
|
|
@ -48,12 +47,12 @@ int i3c_device_do_priv_xfers(struct i3c_device *dev,
|
|||
}
|
||||
|
||||
i3c_bus_normaluse_lock(dev->bus);
|
||||
ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers);
|
||||
ret = i3c_dev_do_xfers_locked(dev->desc, xfers, nxfers, mode);
|
||||
i3c_bus_normaluse_unlock(dev->bus);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i3c_device_do_priv_xfers);
|
||||
EXPORT_SYMBOL_GPL(i3c_device_do_xfers);
|
||||
|
||||
/**
|
||||
* i3c_device_do_setdasa() - do I3C dynamic address assignement with
|
||||
|
|
@ -260,6 +259,20 @@ i3c_device_match_id(struct i3c_device *i3cdev,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(i3c_device_match_id);
|
||||
|
||||
/**
|
||||
* i3c_device_get_supported_xfer_mode - Returns the supported transfer mode by
|
||||
* connected master controller.
|
||||
* @dev: I3C device
|
||||
*
|
||||
* Return: a bit mask, which supported transfer mode, bit position is defined at
|
||||
* enum i3c_hdr_mode
|
||||
*/
|
||||
u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev)
|
||||
{
|
||||
return i3c_dev_get_master(dev->desc)->this->info.hdr_cap | BIT(I3C_SDR);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i3c_device_get_supported_xfer_mode);
|
||||
|
||||
/**
|
||||
* i3c_driver_register_with_owner() - register an I3C device driver
|
||||
*
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ void i3c_bus_normaluse_lock(struct i3c_bus *bus);
|
|||
void i3c_bus_normaluse_unlock(struct i3c_bus *bus);
|
||||
|
||||
int i3c_dev_setdasa_locked(struct i3c_dev_desc *dev);
|
||||
int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
|
||||
struct i3c_priv_xfer *xfers,
|
||||
int nxfers);
|
||||
int i3c_dev_do_xfers_locked(struct i3c_dev_desc *dev,
|
||||
struct i3c_xfer *xfers,
|
||||
int nxfers, enum i3c_xfer_mode mode);
|
||||
int i3c_dev_disable_ibi_locked(struct i3c_dev_desc *dev);
|
||||
int i3c_dev_enable_ibi_locked(struct i3c_dev_desc *dev);
|
||||
int i3c_dev_request_ibi_locked(struct i3c_dev_desc *dev,
|
||||
|
|
|
|||
|
|
@ -334,8 +334,6 @@ static void i3c_device_remove(struct device *dev)
|
|||
|
||||
if (driver->remove)
|
||||
driver->remove(i3cdev);
|
||||
|
||||
i3c_device_free_ibi(i3cdev);
|
||||
}
|
||||
|
||||
const struct bus_type i3c_bus_type = {
|
||||
|
|
@ -2821,10 +2819,14 @@ EXPORT_SYMBOL_GPL(i3c_generic_ibi_recycle_slot);
|
|||
|
||||
static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops)
|
||||
{
|
||||
if (!ops || !ops->bus_init || !ops->priv_xfers ||
|
||||
if (!ops || !ops->bus_init ||
|
||||
!ops->send_ccc_cmd || !ops->do_daa || !ops->i2c_xfers)
|
||||
return -EINVAL;
|
||||
|
||||
/* Must provide one of priv_xfers (SDR only) or i3c_xfers (all modes) */
|
||||
if (!ops->priv_xfers && !ops->i3c_xfers)
|
||||
return -EINVAL;
|
||||
|
||||
if (ops->request_ibi &&
|
||||
(!ops->enable_ibi || !ops->disable_ibi || !ops->free_ibi ||
|
||||
!ops->recycle_ibi_slot))
|
||||
|
|
@ -2883,10 +2885,6 @@ int i3c_master_register(struct i3c_master_controller *master,
|
|||
INIT_LIST_HEAD(&master->boardinfo.i2c);
|
||||
INIT_LIST_HEAD(&master->boardinfo.i3c);
|
||||
|
||||
ret = i3c_bus_init(i3cbus, master->dev.of_node);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
device_initialize(&master->dev);
|
||||
dev_set_name(&master->dev, "i3c-%d", i3cbus->id);
|
||||
|
||||
|
|
@ -2894,6 +2892,10 @@ int i3c_master_register(struct i3c_master_controller *master,
|
|||
master->dev.coherent_dma_mask = parent->coherent_dma_mask;
|
||||
master->dev.dma_parms = parent->dma_parms;
|
||||
|
||||
ret = i3c_bus_init(i3cbus, master->dev.of_node);
|
||||
if (ret)
|
||||
goto err_put_dev;
|
||||
|
||||
ret = of_populate_i3c_bus(master);
|
||||
if (ret)
|
||||
goto err_put_dev;
|
||||
|
|
@ -2925,7 +2927,7 @@ int i3c_master_register(struct i3c_master_controller *master,
|
|||
if (ret)
|
||||
goto err_put_dev;
|
||||
|
||||
master->wq = alloc_workqueue("%s", 0, 0, dev_name(parent));
|
||||
master->wq = alloc_workqueue("%s", WQ_PERCPU, 0, dev_name(parent));
|
||||
if (!master->wq) {
|
||||
ret = -ENOMEM;
|
||||
goto err_put_dev;
|
||||
|
|
@ -3014,9 +3016,8 @@ int i3c_dev_setdasa_locked(struct i3c_dev_desc *dev)
|
|||
dev->boardinfo->init_dyn_addr);
|
||||
}
|
||||
|
||||
int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
|
||||
struct i3c_priv_xfer *xfers,
|
||||
int nxfers)
|
||||
int i3c_dev_do_xfers_locked(struct i3c_dev_desc *dev, struct i3c_xfer *xfers,
|
||||
int nxfers, enum i3c_xfer_mode mode)
|
||||
{
|
||||
struct i3c_master_controller *master;
|
||||
|
||||
|
|
@ -3027,9 +3028,15 @@ int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
|
|||
if (!master || !xfers)
|
||||
return -EINVAL;
|
||||
|
||||
if (!master->ops->priv_xfers)
|
||||
if (mode != I3C_SDR && !(master->this->info.hdr_cap & BIT(mode)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (master->ops->i3c_xfers)
|
||||
return master->ops->i3c_xfers(dev, xfers, nxfers, mode);
|
||||
|
||||
if (mode != I3C_SDR)
|
||||
return -EINVAL;
|
||||
|
||||
return master->ops->priv_xfers(dev, xfers, nxfers);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -228,6 +228,7 @@
|
|||
|
||||
/* List of quirks */
|
||||
#define AMD_I3C_OD_PP_TIMING BIT(1)
|
||||
#define DW_I3C_DISABLE_RUNTIME_PM_QUIRK BIT(2)
|
||||
|
||||
struct dw_i3c_cmd {
|
||||
u32 cmd_lo;
|
||||
|
|
@ -252,6 +253,10 @@ struct dw_i3c_i2c_dev_data {
|
|||
struct i3c_generic_ibi_pool *ibi_pool;
|
||||
};
|
||||
|
||||
struct dw_i3c_drvdata {
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
static bool dw_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m,
|
||||
const struct i3c_ccc_cmd *cmd)
|
||||
{
|
||||
|
|
@ -1535,6 +1540,8 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
|
|||
struct platform_device *pdev)
|
||||
{
|
||||
int ret, irq;
|
||||
const struct dw_i3c_drvdata *drvdata;
|
||||
unsigned long quirks = 0;
|
||||
|
||||
if (!master->platform_ops)
|
||||
master->platform_ops = &dw_i3c_platform_ops_default;
|
||||
|
|
@ -1590,7 +1597,18 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
|
|||
master->maxdevs = ret >> 16;
|
||||
master->free_pos = GENMASK(master->maxdevs - 1, 0);
|
||||
|
||||
master->quirks = (unsigned long)device_get_match_data(&pdev->dev);
|
||||
if (has_acpi_companion(&pdev->dev)) {
|
||||
quirks = (unsigned long)device_get_match_data(&pdev->dev);
|
||||
} else if (pdev->dev.of_node) {
|
||||
drvdata = device_get_match_data(&pdev->dev);
|
||||
if (drvdata)
|
||||
quirks = drvdata->flags;
|
||||
}
|
||||
master->quirks = quirks;
|
||||
|
||||
/* Keep controller enabled by preventing runtime suspend */
|
||||
if (master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
|
||||
INIT_WORK(&master->hj_work, dw_i3c_hj_work);
|
||||
ret = i3c_master_register(&master->base, &pdev->dev,
|
||||
|
|
@ -1617,6 +1635,10 @@ void dw_i3c_common_remove(struct dw_i3c_master *master)
|
|||
cancel_work_sync(&master->hj_work);
|
||||
i3c_master_unregister(&master->base);
|
||||
|
||||
/* Balance pm_runtime_get_noresume() from probe() */
|
||||
if (master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)
|
||||
pm_runtime_put_noidle(master->dev);
|
||||
|
||||
pm_runtime_disable(master->dev);
|
||||
pm_runtime_set_suspended(master->dev);
|
||||
pm_runtime_dont_use_autosuspend(master->dev);
|
||||
|
|
@ -1759,8 +1781,15 @@ static void dw_i3c_shutdown(struct platform_device *pdev)
|
|||
pm_runtime_put_autosuspend(master->dev);
|
||||
}
|
||||
|
||||
static const struct dw_i3c_drvdata altr_agilex5_drvdata = {
|
||||
.flags = DW_I3C_DISABLE_RUNTIME_PM_QUIRK,
|
||||
};
|
||||
|
||||
static const struct of_device_id dw_i3c_master_of_match[] = {
|
||||
{ .compatible = "snps,dw-i3c-master-1.00a", },
|
||||
{ .compatible = "altr,agilex5-dw-i3c-master",
|
||||
.data = &altr_agilex5_drvdata,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_i3c_master_of_match);
|
||||
|
|
|
|||
|
|
@ -7,61 +7,196 @@
|
|||
* Author: Jarkko Nikula <jarkko.nikula@linux.intel.com>
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_qos.h>
|
||||
|
||||
struct mipi_i3c_hci_pci_info {
|
||||
int (*init)(struct pci_dev *pci);
|
||||
struct mipi_i3c_hci_pci {
|
||||
struct pci_dev *pci;
|
||||
struct platform_device *pdev;
|
||||
const struct mipi_i3c_hci_pci_info *info;
|
||||
void *private;
|
||||
};
|
||||
|
||||
#define INTEL_PRIV_OFFSET 0x2b0
|
||||
#define INTEL_PRIV_SIZE 0x28
|
||||
#define INTEL_PRIV_RESETS 0x04
|
||||
#define INTEL_PRIV_RESETS_RESET BIT(0)
|
||||
#define INTEL_PRIV_RESETS_RESET_DONE BIT(1)
|
||||
struct mipi_i3c_hci_pci_info {
|
||||
int (*init)(struct mipi_i3c_hci_pci *hci);
|
||||
void (*exit)(struct mipi_i3c_hci_pci *hci);
|
||||
};
|
||||
|
||||
static DEFINE_IDA(mipi_i3c_hci_pci_ida);
|
||||
|
||||
static int mipi_i3c_hci_pci_intel_init(struct pci_dev *pci)
|
||||
{
|
||||
unsigned long timeout;
|
||||
void __iomem *priv;
|
||||
#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)
|
||||
#define INTEL_RESETS_TIMEOUT_US (10 * USEC_PER_MSEC)
|
||||
|
||||
priv = devm_ioremap(&pci->dev,
|
||||
pci_resource_start(pci, 0) + INTEL_PRIV_OFFSET,
|
||||
INTEL_PRIV_SIZE);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
#define INTEL_ACTIVELTR 0x0c
|
||||
#define INTEL_IDLELTR 0x10
|
||||
|
||||
#define INTEL_LTR_REQ BIT(15)
|
||||
#define INTEL_LTR_SCALE_MASK GENMASK(11, 10)
|
||||
#define INTEL_LTR_SCALE_1US FIELD_PREP(INTEL_LTR_SCALE_MASK, 2)
|
||||
#define INTEL_LTR_SCALE_32US FIELD_PREP(INTEL_LTR_SCALE_MASK, 3)
|
||||
#define INTEL_LTR_VALUE_MASK GENMASK(9, 0)
|
||||
|
||||
struct intel_host {
|
||||
void __iomem *priv;
|
||||
u32 active_ltr;
|
||||
u32 idle_ltr;
|
||||
struct dentry *debugfs_root;
|
||||
};
|
||||
|
||||
static void intel_cache_ltr(struct intel_host *host)
|
||||
{
|
||||
host->active_ltr = readl(host->priv + INTEL_ACTIVELTR);
|
||||
host->idle_ltr = readl(host->priv + INTEL_IDLELTR);
|
||||
}
|
||||
|
||||
static void intel_ltr_set(struct device *dev, s32 val)
|
||||
{
|
||||
struct mipi_i3c_hci_pci *hci = dev_get_drvdata(dev);
|
||||
struct intel_host *host = hci->private;
|
||||
u32 ltr;
|
||||
|
||||
/*
|
||||
* Program latency tolerance (LTR) accordingly what has been asked
|
||||
* by the PM QoS layer or disable it in case we were passed
|
||||
* negative value or PM_QOS_LATENCY_ANY.
|
||||
*/
|
||||
ltr = readl(host->priv + INTEL_ACTIVELTR);
|
||||
|
||||
if (val == PM_QOS_LATENCY_ANY || val < 0) {
|
||||
ltr &= ~INTEL_LTR_REQ;
|
||||
} else {
|
||||
ltr |= INTEL_LTR_REQ;
|
||||
ltr &= ~INTEL_LTR_SCALE_MASK;
|
||||
ltr &= ~INTEL_LTR_VALUE_MASK;
|
||||
|
||||
if (val > INTEL_LTR_VALUE_MASK) {
|
||||
val >>= 5;
|
||||
if (val > INTEL_LTR_VALUE_MASK)
|
||||
val = INTEL_LTR_VALUE_MASK;
|
||||
ltr |= INTEL_LTR_SCALE_32US | val;
|
||||
} else {
|
||||
ltr |= INTEL_LTR_SCALE_1US | val;
|
||||
}
|
||||
}
|
||||
|
||||
if (ltr == host->active_ltr)
|
||||
return;
|
||||
|
||||
writel(ltr, host->priv + INTEL_ACTIVELTR);
|
||||
writel(ltr, host->priv + INTEL_IDLELTR);
|
||||
|
||||
/* Cache the values into intel_host structure */
|
||||
intel_cache_ltr(host);
|
||||
}
|
||||
|
||||
static void intel_ltr_expose(struct device *dev)
|
||||
{
|
||||
dev->power.set_latency_tolerance = intel_ltr_set;
|
||||
dev_pm_qos_expose_latency_tolerance(dev);
|
||||
}
|
||||
|
||||
static void intel_ltr_hide(struct device *dev)
|
||||
{
|
||||
dev_pm_qos_hide_latency_tolerance(dev);
|
||||
dev->power.set_latency_tolerance = NULL;
|
||||
}
|
||||
|
||||
static void intel_add_debugfs(struct mipi_i3c_hci_pci *hci)
|
||||
{
|
||||
struct dentry *dir = debugfs_create_dir(dev_name(&hci->pci->dev), NULL);
|
||||
struct intel_host *host = hci->private;
|
||||
|
||||
intel_cache_ltr(host);
|
||||
|
||||
host->debugfs_root = dir;
|
||||
debugfs_create_x32("active_ltr", 0444, dir, &host->active_ltr);
|
||||
debugfs_create_x32("idle_ltr", 0444, dir, &host->idle_ltr);
|
||||
}
|
||||
|
||||
static void intel_remove_debugfs(struct mipi_i3c_hci_pci *hci)
|
||||
{
|
||||
struct intel_host *host = hci->private;
|
||||
|
||||
debugfs_remove_recursive(host->debugfs_root);
|
||||
}
|
||||
|
||||
static void intel_reset(void __iomem *priv)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* Assert reset, wait for completion and release reset */
|
||||
writel(0, priv + INTEL_PRIV_RESETS);
|
||||
timeout = jiffies + msecs_to_jiffies(10);
|
||||
while (!(readl(priv + INTEL_PRIV_RESETS) &
|
||||
INTEL_PRIV_RESETS_RESET_DONE)) {
|
||||
if (time_after(jiffies, timeout))
|
||||
break;
|
||||
cpu_relax();
|
||||
}
|
||||
writel(INTEL_PRIV_RESETS_RESET, priv + INTEL_PRIV_RESETS);
|
||||
writel(0, priv + INTEL_RESETS);
|
||||
readl_poll_timeout(priv + INTEL_RESETS, reg,
|
||||
reg & INTEL_RESETS_RESET_DONE, 0,
|
||||
INTEL_RESETS_TIMEOUT_US);
|
||||
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);
|
||||
|
||||
if (!host || !priv)
|
||||
return -ENOMEM;
|
||||
|
||||
dma_set_mask_and_coherent(&hci->pci->dev, DMA_BIT_MASK(64));
|
||||
|
||||
hci->pci->d3cold_delay = 0;
|
||||
|
||||
hci->private = host;
|
||||
host->priv = priv;
|
||||
|
||||
intel_reset(priv);
|
||||
|
||||
intel_ltr_expose(&hci->pci->dev);
|
||||
intel_add_debugfs(hci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mipi_i3c_hci_pci_info intel_info = {
|
||||
.init = mipi_i3c_hci_pci_intel_init,
|
||||
static void intel_i3c_exit(struct mipi_i3c_hci_pci *hci)
|
||||
{
|
||||
intel_remove_debugfs(hci);
|
||||
intel_ltr_hide(&hci->pci->dev);
|
||||
}
|
||||
|
||||
static const struct mipi_i3c_hci_pci_info intel_info = {
|
||||
.init = intel_i3c_init,
|
||||
.exit = intel_i3c_exit,
|
||||
};
|
||||
|
||||
static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct mipi_i3c_hci_pci_info *info;
|
||||
struct platform_device *pdev;
|
||||
struct mipi_i3c_hci_pci *hci;
|
||||
struct resource res[2];
|
||||
int dev_id, ret;
|
||||
|
||||
hci = devm_kzalloc(&pci->dev, sizeof(*hci), GFP_KERNEL);
|
||||
if (!hci)
|
||||
return -ENOMEM;
|
||||
|
||||
hci->pci = pci;
|
||||
|
||||
ret = pcim_enable_device(pci);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -82,43 +217,50 @@ static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,
|
|||
if (dev_id < 0)
|
||||
return dev_id;
|
||||
|
||||
pdev = platform_device_alloc("mipi-i3c-hci", dev_id);
|
||||
if (!pdev)
|
||||
hci->pdev = platform_device_alloc("mipi-i3c-hci", dev_id);
|
||||
if (!hci->pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
pdev->dev.parent = &pci->dev;
|
||||
device_set_node(&pdev->dev, dev_fwnode(&pci->dev));
|
||||
hci->pdev->dev.parent = &pci->dev;
|
||||
device_set_node(&hci->pdev->dev, dev_fwnode(&pci->dev));
|
||||
|
||||
ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
|
||||
ret = platform_device_add_resources(hci->pdev, res, ARRAY_SIZE(res));
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
info = (struct mipi_i3c_hci_pci_info *)id->driver_data;
|
||||
if (info && info->init) {
|
||||
ret = info->init(pci);
|
||||
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(pdev);
|
||||
ret = platform_device_add(hci->pdev);
|
||||
if (ret)
|
||||
goto err;
|
||||
goto err_exit;
|
||||
|
||||
pci_set_drvdata(pci, pdev);
|
||||
pci_set_drvdata(pci, hci);
|
||||
|
||||
return 0;
|
||||
|
||||
err_exit:
|
||||
if (hci->info && hci->info->exit)
|
||||
hci->info->exit(hci);
|
||||
err:
|
||||
platform_device_put(pdev);
|
||||
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 platform_device *pdev = pci_get_drvdata(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)
|
||||
hci->info->exit(hci);
|
||||
|
||||
platform_device_unregister(pdev);
|
||||
ida_free(&mipi_i3c_hci_pci_ida, dev_id);
|
||||
}
|
||||
|
|
@ -133,6 +275,9 @@ static const struct pci_device_id mipi_i3c_hci_pci_devices[] = {
|
|||
/* Panther Lake-P */
|
||||
{ PCI_VDEVICE(INTEL, 0xe47c), (kernel_ulong_t)&intel_info},
|
||||
{ PCI_VDEVICE(INTEL, 0xe46f), (kernel_ulong_t)&intel_info},
|
||||
/* Nova Lake-S */
|
||||
{ PCI_VDEVICE(INTEL, 0x6e2c), (kernel_ulong_t)&intel_info},
|
||||
{ PCI_VDEVICE(INTEL, 0x6e2d), (kernel_ulong_t)&intel_info},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, mipi_i3c_hci_pci_devices);
|
||||
|
|
|
|||
|
|
@ -40,11 +40,13 @@
|
|||
#define SVC_I3C_MCTRL_REQUEST_NONE 0
|
||||
#define SVC_I3C_MCTRL_REQUEST_START_ADDR 1
|
||||
#define SVC_I3C_MCTRL_REQUEST_STOP 2
|
||||
#define SVC_I3C_MCTRL_REQUEST_FORCE_EXIT 6
|
||||
#define SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK 3
|
||||
#define SVC_I3C_MCTRL_REQUEST_PROC_DAA 4
|
||||
#define SVC_I3C_MCTRL_REQUEST_AUTO_IBI 7
|
||||
#define SVC_I3C_MCTRL_TYPE_I3C 0
|
||||
#define SVC_I3C_MCTRL_TYPE_I2C BIT(4)
|
||||
#define SVC_I3C_MCTRL_TYPE_DDR BIT(5)
|
||||
#define SVC_I3C_MCTRL_IBIRESP_AUTO 0
|
||||
#define SVC_I3C_MCTRL_IBIRESP_ACK_WITHOUT_BYTE 0
|
||||
#define SVC_I3C_MCTRL_IBIRESP_ACK_WITH_BYTE BIT(7)
|
||||
|
|
@ -95,6 +97,7 @@
|
|||
#define SVC_I3C_MINTMASKED 0x098
|
||||
#define SVC_I3C_MERRWARN 0x09C
|
||||
#define SVC_I3C_MERRWARN_NACK BIT(2)
|
||||
#define SVC_I3C_MERRWARN_CRC BIT(10)
|
||||
#define SVC_I3C_MERRWARN_TIMEOUT BIT(20)
|
||||
#define SVC_I3C_MDMACTRL 0x0A0
|
||||
#define SVC_I3C_MDATACTRL 0x0AC
|
||||
|
|
@ -165,12 +168,16 @@
|
|||
|
||||
struct svc_i3c_cmd {
|
||||
u8 addr;
|
||||
bool rnw;
|
||||
union {
|
||||
bool rnw;
|
||||
u8 cmd;
|
||||
u32 rnw_cmd;
|
||||
};
|
||||
u8 *in;
|
||||
const void *out;
|
||||
unsigned int len;
|
||||
unsigned int actual_len;
|
||||
struct i3c_priv_xfer *xfer;
|
||||
struct i3c_xfer *xfer;
|
||||
bool continued;
|
||||
};
|
||||
|
||||
|
|
@ -383,6 +390,36 @@ svc_i3c_master_dev_from_addr(struct svc_i3c_master *master,
|
|||
return master->descs[i];
|
||||
}
|
||||
|
||||
static bool svc_cmd_is_read(u32 rnw_cmd, u32 type)
|
||||
{
|
||||
return (type == SVC_I3C_MCTRL_TYPE_DDR) ? (rnw_cmd & 0x80) : rnw_cmd;
|
||||
}
|
||||
|
||||
static void svc_i3c_master_emit_force_exit(struct svc_i3c_master *master)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
writel(SVC_I3C_MCTRL_REQUEST_FORCE_EXIT, master->regs + SVC_I3C_MCTRL);
|
||||
|
||||
/*
|
||||
* Not need check error here because it is never happen at hardware.
|
||||
* IP just wait for few fclk cycle to complete DDR exit pattern. Even
|
||||
* though fclk stop, timeout happen here, the whole data actually
|
||||
* already finish transfer. The next command will be timeout because
|
||||
* wrong hardware state.
|
||||
*/
|
||||
readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, reg,
|
||||
SVC_I3C_MSTATUS_MCTRLDONE(reg), 0, 1000);
|
||||
|
||||
/*
|
||||
* This delay is necessary after the emission of a stop, otherwise eg.
|
||||
* repeating IBIs do not get detected. There is a note in the manual
|
||||
* about it, stating that the stop condition might not be settled
|
||||
* correctly if a start condition follows too rapidly.
|
||||
*/
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
static void svc_i3c_master_emit_stop(struct svc_i3c_master *master)
|
||||
{
|
||||
writel(SVC_I3C_MCTRL_REQUEST_STOP, master->regs + SVC_I3C_MCTRL);
|
||||
|
|
@ -406,21 +443,27 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
|
|||
int ret, val;
|
||||
u8 *buf;
|
||||
|
||||
slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
|
||||
if (!slot)
|
||||
return -ENOSPC;
|
||||
|
||||
slot->len = 0;
|
||||
buf = slot->data;
|
||||
|
||||
/*
|
||||
* Wait for transfer to complete before returning. Otherwise, the EmitStop
|
||||
* request might be sent when the transfer is not complete.
|
||||
*/
|
||||
ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
|
||||
SVC_I3C_MSTATUS_COMPLETE(val), 0, 1000);
|
||||
if (ret) {
|
||||
dev_err(master->dev, "Timeout when polling for COMPLETE\n");
|
||||
i3c_generic_ibi_recycle_slot(data->ibi_pool, slot);
|
||||
return ret;
|
||||
}
|
||||
|
||||
slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
|
||||
if (!slot) {
|
||||
dev_dbg(master->dev, "No free ibi slot, drop the data\n");
|
||||
writel(SVC_I3C_MDATACTRL_FLUSHRB, master->regs + SVC_I3C_MDATACTRL);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
slot->len = 0;
|
||||
buf = slot->data;
|
||||
|
||||
while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS)) &&
|
||||
slot->len < SVC_I3C_FIFO_SIZE) {
|
||||
mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
|
||||
|
|
@ -512,7 +555,7 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master)
|
|||
* cycle, leading to missed client IBI handlers.
|
||||
*
|
||||
* A typical scenario is when IBIWON occurs and bus arbitration is lost
|
||||
* at svc_i3c_master_priv_xfers().
|
||||
* at svc_i3c_master_i3c_xfers().
|
||||
*
|
||||
* Clear SVC_I3C_MINT_IBIWON before sending SVC_I3C_MCTRL_REQUEST_AUTO_IBI.
|
||||
*/
|
||||
|
|
@ -792,6 +835,8 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
|
|||
|
||||
info.dyn_addr = ret;
|
||||
|
||||
info.hdr_cap = I3C_CCC_HDR_MODE(I3C_HDR_DDR);
|
||||
|
||||
writel(SVC_MDYNADDR_VALID | SVC_MDYNADDR_ADDR(info.dyn_addr),
|
||||
master->regs + SVC_I3C_MDYNADDR);
|
||||
|
||||
|
|
@ -1293,10 +1338,11 @@ static int svc_i3c_master_write(struct svc_i3c_master *master,
|
|||
}
|
||||
|
||||
static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
||||
bool rnw, unsigned int xfer_type, u8 addr,
|
||||
u32 rnw_cmd, unsigned int xfer_type, u8 addr,
|
||||
u8 *in, const u8 *out, unsigned int xfer_len,
|
||||
unsigned int *actual_len, bool continued, bool repeat_start)
|
||||
{
|
||||
bool rnw = svc_cmd_is_read(rnw_cmd, xfer_type);
|
||||
int retry = repeat_start ? 1 : 2;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
|
@ -1304,6 +1350,16 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
|||
/* clean SVC_I3C_MINT_IBIWON w1c bits */
|
||||
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
|
||||
|
||||
if (xfer_type == SVC_I3C_MCTRL_TYPE_DDR) {
|
||||
/* DDR command need prefill into FIFO */
|
||||
writel(rnw_cmd, master->regs + SVC_I3C_MWDATAB);
|
||||
if (!rnw) {
|
||||
/* write data also need prefill into FIFO */
|
||||
ret = svc_i3c_master_write(master, out, xfer_len);
|
||||
if (ret)
|
||||
goto emit_stop;
|
||||
}
|
||||
}
|
||||
|
||||
while (retry--) {
|
||||
writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
|
||||
|
|
@ -1397,7 +1453,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
|||
|
||||
if (rnw)
|
||||
ret = svc_i3c_master_read(master, in, xfer_len);
|
||||
else
|
||||
else if (xfer_type != SVC_I3C_MCTRL_TYPE_DDR)
|
||||
ret = svc_i3c_master_write(master, out, xfer_len);
|
||||
if (ret < 0)
|
||||
goto emit_stop;
|
||||
|
|
@ -1410,10 +1466,19 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
|||
if (ret)
|
||||
goto emit_stop;
|
||||
|
||||
if (xfer_type == SVC_I3C_MCTRL_TYPE_DDR &&
|
||||
(readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_CRC)) {
|
||||
ret = -ENXIO;
|
||||
goto emit_stop;
|
||||
}
|
||||
|
||||
writel(SVC_I3C_MINT_COMPLETE, master->regs + SVC_I3C_MSTATUS);
|
||||
|
||||
if (!continued) {
|
||||
svc_i3c_master_emit_stop(master);
|
||||
if (xfer_type != SVC_I3C_MCTRL_TYPE_DDR)
|
||||
svc_i3c_master_emit_stop(master);
|
||||
else
|
||||
svc_i3c_master_emit_force_exit(master);
|
||||
|
||||
/* Wait idle if stop is sent. */
|
||||
readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
|
||||
|
|
@ -1423,7 +1488,11 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
|||
return 0;
|
||||
|
||||
emit_stop:
|
||||
svc_i3c_master_emit_stop(master);
|
||||
if (xfer_type != SVC_I3C_MCTRL_TYPE_DDR)
|
||||
svc_i3c_master_emit_stop(master);
|
||||
else
|
||||
svc_i3c_master_emit_force_exit(master);
|
||||
|
||||
svc_i3c_master_clear_merrwarn(master);
|
||||
svc_i3c_master_flush_fifo(master);
|
||||
|
||||
|
|
@ -1470,6 +1539,11 @@ static void svc_i3c_master_dequeue_xfer(struct svc_i3c_master *master,
|
|||
spin_unlock_irqrestore(&master->xferqueue.lock, flags);
|
||||
}
|
||||
|
||||
static int i3c_mode_to_svc_type(enum i3c_xfer_mode mode)
|
||||
{
|
||||
return (mode == I3C_SDR) ? SVC_I3C_MCTRL_TYPE_I3C : SVC_I3C_MCTRL_TYPE_DDR;
|
||||
}
|
||||
|
||||
static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
|
||||
{
|
||||
struct svc_i3c_xfer *xfer = master->xferqueue.cur;
|
||||
|
|
@ -1484,7 +1558,7 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
|
|||
for (i = 0; i < xfer->ncmds; i++) {
|
||||
struct svc_i3c_cmd *cmd = &xfer->cmds[i];
|
||||
|
||||
ret = svc_i3c_master_xfer(master, cmd->rnw, xfer->type,
|
||||
ret = svc_i3c_master_xfer(master, cmd->rnw_cmd, xfer->type,
|
||||
cmd->addr, cmd->in, cmd->out,
|
||||
cmd->len, &cmd->actual_len,
|
||||
cmd->continued, i > 0);
|
||||
|
|
@ -1659,9 +1733,8 @@ static int svc_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
|
||||
struct i3c_priv_xfer *xfers,
|
||||
int nxfers)
|
||||
static int svc_i3c_master_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *xfers,
|
||||
int nxfers, enum i3c_xfer_mode mode)
|
||||
{
|
||||
struct i3c_master_controller *m = i3c_dev_get_master(dev);
|
||||
struct svc_i3c_master *master = to_svc_i3c_master(m);
|
||||
|
|
@ -1669,22 +1742,36 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
|
|||
struct svc_i3c_xfer *xfer;
|
||||
int ret, i;
|
||||
|
||||
if (mode != I3C_SDR) {
|
||||
/*
|
||||
* Only support data size less than FIFO SIZE when using DDR
|
||||
* mode. First entry is cmd in FIFO, so actual available FIFO
|
||||
* for data is SVC_I3C_FIFO_SIZE - 2 since DDR only supports
|
||||
* even length.
|
||||
*/
|
||||
for (i = 0; i < nxfers; i++)
|
||||
if (xfers[i].len > SVC_I3C_FIFO_SIZE - 2)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
xfer = svc_i3c_master_alloc_xfer(master, nxfers);
|
||||
if (!xfer)
|
||||
return -ENOMEM;
|
||||
|
||||
xfer->type = SVC_I3C_MCTRL_TYPE_I3C;
|
||||
xfer->type = i3c_mode_to_svc_type(mode);
|
||||
|
||||
for (i = 0; i < nxfers; i++) {
|
||||
u32 rnw_cmd = (mode == I3C_SDR) ? xfers[i].rnw : xfers[i].cmd;
|
||||
bool rnw = svc_cmd_is_read(rnw_cmd, xfer->type);
|
||||
struct svc_i3c_cmd *cmd = &xfer->cmds[i];
|
||||
|
||||
cmd->xfer = &xfers[i];
|
||||
cmd->addr = master->addrs[data->index];
|
||||
cmd->rnw = xfers[i].rnw;
|
||||
cmd->in = xfers[i].rnw ? xfers[i].data.in : NULL;
|
||||
cmd->out = xfers[i].rnw ? NULL : xfers[i].data.out;
|
||||
cmd->rnw_cmd = rnw_cmd;
|
||||
cmd->in = rnw ? xfers[i].data.in : NULL;
|
||||
cmd->out = rnw ? NULL : xfers[i].data.out;
|
||||
cmd->len = xfers[i].len;
|
||||
cmd->actual_len = xfers[i].rnw ? xfers[i].len : 0;
|
||||
cmd->actual_len = rnw ? xfers[i].len : 0;
|
||||
cmd->continued = (i + 1) < nxfers;
|
||||
}
|
||||
|
||||
|
|
@ -1879,7 +1966,7 @@ static const struct i3c_master_controller_ops svc_i3c_master_ops = {
|
|||
.do_daa = svc_i3c_master_do_daa,
|
||||
.supports_ccc_cmd = svc_i3c_master_supports_ccc_cmd,
|
||||
.send_ccc_cmd = svc_i3c_master_send_ccc_cmd,
|
||||
.priv_xfers = svc_i3c_master_priv_xfers,
|
||||
.i3c_xfers = svc_i3c_master_i3c_xfers,
|
||||
.i2c_xfers = svc_i3c_master_i2c_xfers,
|
||||
.request_ibi = svc_i3c_master_request_ibi,
|
||||
.free_ibi = svc_i3c_master_free_ibi,
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ struct mctp_i3c_internal_hdr {
|
|||
|
||||
static int mctp_i3c_read(struct mctp_i3c_device *mi)
|
||||
{
|
||||
struct i3c_priv_xfer xfer = { .rnw = 1, .len = mi->mrl };
|
||||
struct i3c_xfer xfer = { .rnw = 1, .len = mi->mrl };
|
||||
struct net_device_stats *stats = &mi->mbus->ndev->stats;
|
||||
struct mctp_i3c_internal_hdr *ihdr = NULL;
|
||||
struct sk_buff *skb = NULL;
|
||||
|
|
@ -127,7 +127,7 @@ static int mctp_i3c_read(struct mctp_i3c_device *mi)
|
|||
|
||||
/* Make sure netif_rx() is read in the same order as i3c. */
|
||||
mutex_lock(&mi->lock);
|
||||
rc = i3c_device_do_priv_xfers(mi->i3c, &xfer, 1);
|
||||
rc = i3c_device_do_xfers(mi->i3c, &xfer, 1, I3C_SDR);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
|
||||
|
|
@ -360,7 +360,7 @@ mctp_i3c_lookup(struct mctp_i3c_bus *mbus, u64 pid)
|
|||
static void mctp_i3c_xmit(struct mctp_i3c_bus *mbus, struct sk_buff *skb)
|
||||
{
|
||||
struct net_device_stats *stats = &mbus->ndev->stats;
|
||||
struct i3c_priv_xfer xfer = { .rnw = false };
|
||||
struct i3c_xfer xfer = { .rnw = false };
|
||||
struct mctp_i3c_internal_hdr *ihdr = NULL;
|
||||
struct mctp_i3c_device *mi = NULL;
|
||||
unsigned int data_len;
|
||||
|
|
@ -409,7 +409,7 @@ static void mctp_i3c_xmit(struct mctp_i3c_bus *mbus, struct sk_buff *skb)
|
|||
data[data_len] = pec;
|
||||
|
||||
xfer.data.out = data;
|
||||
rc = i3c_device_do_priv_xfers(mi->i3c, &xfer, 1);
|
||||
rc = i3c_device_do_xfers(mi->i3c, &xfer, 1, I3C_SDR);
|
||||
if (rc == 0) {
|
||||
stats->tx_bytes += data_len;
|
||||
stats->tx_packets++;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
* These are the standard error codes as defined by the I3C specification.
|
||||
* When -EIO is returned by the i3c_device_do_priv_xfers() or
|
||||
* i3c_device_send_hdr_cmds() one can check the error code in
|
||||
* &struct_i3c_priv_xfer.err or &struct i3c_hdr_cmd.err to get a better idea of
|
||||
* &struct_i3c_xfer.err or &struct i3c_hdr_cmd.err to get a better idea of
|
||||
* what went wrong.
|
||||
*
|
||||
*/
|
||||
|
|
@ -39,20 +39,25 @@ enum i3c_error_code {
|
|||
};
|
||||
|
||||
/**
|
||||
* enum i3c_hdr_mode - HDR mode ids
|
||||
* enum i3c_xfer_mode - I3C xfer mode ids
|
||||
* @I3C_HDR_DDR: DDR mode
|
||||
* @I3C_HDR_TSP: TSP mode
|
||||
* @I3C_HDR_TSL: TSL mode
|
||||
* @I3C_SDR: SDR mode (NOT HDR mode)
|
||||
*/
|
||||
enum i3c_hdr_mode {
|
||||
I3C_HDR_DDR,
|
||||
I3C_HDR_TSP,
|
||||
I3C_HDR_TSL,
|
||||
enum i3c_xfer_mode {
|
||||
/* The below 3 value (I3C_HDR*) must match GETCAP1 Byte bit position */
|
||||
I3C_HDR_DDR = 0,
|
||||
I3C_HDR_TSP = 1,
|
||||
I3C_HDR_TSL = 2,
|
||||
/* Use for default SDR transfer mode */
|
||||
I3C_SDR = 31,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct i3c_priv_xfer - I3C SDR private transfer
|
||||
* struct i3c_xfer - I3C data transfer
|
||||
* @rnw: encodes the transfer direction. true for a read, false for a write
|
||||
* @cmd: Read/Write command in HDR mode, read: 0x80 - 0xff, write: 0x00 - 0x7f
|
||||
* @len: transfer length in bytes of the transfer
|
||||
* @actual_len: actual length in bytes are transferred by the controller
|
||||
* @data: input/output buffer
|
||||
|
|
@ -60,8 +65,11 @@ enum i3c_hdr_mode {
|
|||
* @data.out: output buffer. Must point to a DMA-able buffer
|
||||
* @err: I3C error code
|
||||
*/
|
||||
struct i3c_priv_xfer {
|
||||
u8 rnw;
|
||||
struct i3c_xfer {
|
||||
union {
|
||||
u8 rnw;
|
||||
u8 cmd;
|
||||
};
|
||||
u16 len;
|
||||
u16 actual_len;
|
||||
union {
|
||||
|
|
@ -71,6 +79,9 @@ struct i3c_priv_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
|
||||
|
|
@ -297,9 +308,15 @@ static __always_inline void i3c_i2c_driver_unregister(struct i3c_driver *i3cdrv,
|
|||
i3c_i2c_driver_unregister, \
|
||||
__i2cdrv)
|
||||
|
||||
int i3c_device_do_priv_xfers(struct i3c_device *dev,
|
||||
struct i3c_priv_xfer *xfers,
|
||||
int nxfers);
|
||||
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)
|
||||
{
|
||||
return i3c_device_do_xfers(dev, xfers, nxfers, I3C_SDR);
|
||||
}
|
||||
|
||||
int i3c_device_do_setdasa(struct i3c_device *dev);
|
||||
|
||||
|
|
@ -341,5 +358,6 @@ 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 */
|
||||
|
|
|
|||
|
|
@ -418,7 +418,11 @@ struct i3c_bus {
|
|||
* @send_ccc_cmd: send a CCC command
|
||||
* This method is mandatory.
|
||||
* @priv_xfers: do one or several private I3C SDR transfers
|
||||
* This method is mandatory.
|
||||
* This method is mandatory when i3c_xfers is not implemented. It
|
||||
* is deprecated.
|
||||
* @i3c_xfers: do one or several I3C SDR or HDR transfers
|
||||
* This method is mandatory when priv_xfers is not implemented but
|
||||
* should be implemented instead of priv_xfers.
|
||||
* @attach_i2c_dev: called every time an I2C device is attached to the bus.
|
||||
* This is a good place to attach master controller specific
|
||||
* data to I2C devices.
|
||||
|
|
@ -474,9 +478,13 @@ struct i3c_master_controller_ops {
|
|||
const struct i3c_ccc_cmd *cmd);
|
||||
int (*send_ccc_cmd)(struct i3c_master_controller *master,
|
||||
struct i3c_ccc_cmd *cmd);
|
||||
/* Deprecated, please use i3c_xfers() */
|
||||
int (*priv_xfers)(struct i3c_dev_desc *dev,
|
||||
struct i3c_priv_xfer *xfers,
|
||||
int nxfers);
|
||||
int (*i3c_xfers)(struct i3c_dev_desc *dev,
|
||||
struct i3c_xfer *xfers,
|
||||
int nxfers, enum i3c_xfer_mode mode);
|
||||
int (*attach_i2c_dev)(struct i2c_dev_desc *dev);
|
||||
void (*detach_i2c_dev)(struct i2c_dev_desc *dev);
|
||||
int (*i2c_xfers)(struct i2c_dev_desc *dev,
|
||||
|
|
|
|||
Loading…
Reference in New Issue