mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-01-11 17:10:13 +00:00
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() ...
This commit is contained in:
commit
c2f2b01b74
@ -14,7 +14,11 @@ allOf:
|
|||||||
|
|
||||||
properties:
|
properties:
|
||||||
compatible:
|
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:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|||||||
@ -11,7 +11,7 @@ static int regmap_i3c_write(void *context, const void *data, size_t count)
|
|||||||
{
|
{
|
||||||
struct device *dev = context;
|
struct device *dev = context;
|
||||||
struct i3c_device *i3c = dev_to_i3cdev(dev);
|
struct i3c_device *i3c = dev_to_i3cdev(dev);
|
||||||
struct i3c_priv_xfer xfers[] = {
|
struct i3c_xfer xfers[] = {
|
||||||
{
|
{
|
||||||
.rnw = false,
|
.rnw = false,
|
||||||
.len = count,
|
.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,
|
static int regmap_i3c_read(void *context,
|
||||||
@ -28,7 +28,7 @@ static int regmap_i3c_read(void *context,
|
|||||||
{
|
{
|
||||||
struct device *dev = context;
|
struct device *dev = context;
|
||||||
struct i3c_device *i3c = dev_to_i3cdev(dev);
|
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].rnw = false;
|
||||||
xfers[0].len = reg_size;
|
xfers[0].len = reg_size;
|
||||||
@ -38,7 +38,7 @@ static int regmap_i3c_read(void *context,
|
|||||||
xfers[1].len = val_size;
|
xfers[1].len = val_size;
|
||||||
xfers[1].data.in = val;
|
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 = {
|
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 i3c_device *i3cdev = context;
|
||||||
struct lm75_data *data = i3cdev_get_drvdata(i3cdev);
|
struct lm75_data *data = i3cdev_get_drvdata(i3cdev);
|
||||||
struct i3c_priv_xfer xfers[] = {
|
struct i3c_xfer xfers[] = {
|
||||||
{
|
{
|
||||||
.rnw = false,
|
.rnw = false,
|
||||||
.len = 1,
|
.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)
|
if (reg == LM75_REG_CONF && !data->params->config_reg_16bits)
|
||||||
xfers[1].len--;
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
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 i3c_device *i3cdev = context;
|
||||||
struct lm75_data *data = i3cdev_get_drvdata(i3cdev);
|
struct lm75_data *data = i3cdev_get_drvdata(i3cdev);
|
||||||
struct i3c_priv_xfer xfers[] = {
|
struct i3c_xfer xfers[] = {
|
||||||
{
|
{
|
||||||
.rnw = false,
|
.rnw = false,
|
||||||
.len = 3,
|
.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;
|
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 = {
|
static const struct regmap_bus lm75_i3c_regmap_bus = {
|
||||||
|
|||||||
@ -15,12 +15,12 @@
|
|||||||
#include "internals.h"
|
#include "internals.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* i3c_device_do_priv_xfers() - do I3C SDR private transfers directed to a
|
* i3c_device_do_xfers() - do I3C transfers directed to a specific device
|
||||||
* specific device
|
|
||||||
*
|
*
|
||||||
* @dev: device with which the transfers should be done
|
* @dev: device with which the transfers should be done
|
||||||
* @xfers: array of transfers
|
* @xfers: array of transfers
|
||||||
* @nxfers: number of transfers
|
* @nxfers: number of transfers
|
||||||
|
* @mode: transfer mode
|
||||||
*
|
*
|
||||||
* Initiate one or several private SDR transfers with @dev.
|
* 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:
|
* 'xfers' some time later. See I3C spec ver 1.1.1 09-Jun-2021. Section:
|
||||||
* 5.1.2.2.3.
|
* 5.1.2.2.3.
|
||||||
*/
|
*/
|
||||||
int i3c_device_do_priv_xfers(struct i3c_device *dev,
|
int i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers,
|
||||||
struct i3c_priv_xfer *xfers,
|
int nxfers, enum i3c_xfer_mode mode)
|
||||||
int nxfers)
|
|
||||||
{
|
{
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
|
||||||
@ -48,12 +47,12 @@ int i3c_device_do_priv_xfers(struct i3c_device *dev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
i3c_bus_normaluse_lock(dev->bus);
|
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);
|
i3c_bus_normaluse_unlock(dev->bus);
|
||||||
|
|
||||||
return ret;
|
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
|
* 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);
|
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
|
* 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);
|
void i3c_bus_normaluse_unlock(struct i3c_bus *bus);
|
||||||
|
|
||||||
int i3c_dev_setdasa_locked(struct i3c_dev_desc *dev);
|
int i3c_dev_setdasa_locked(struct i3c_dev_desc *dev);
|
||||||
int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
|
int i3c_dev_do_xfers_locked(struct i3c_dev_desc *dev,
|
||||||
struct i3c_priv_xfer *xfers,
|
struct i3c_xfer *xfers,
|
||||||
int nxfers);
|
int nxfers, enum i3c_xfer_mode mode);
|
||||||
int i3c_dev_disable_ibi_locked(struct i3c_dev_desc *dev);
|
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_enable_ibi_locked(struct i3c_dev_desc *dev);
|
||||||
int i3c_dev_request_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)
|
if (driver->remove)
|
||||||
driver->remove(i3cdev);
|
driver->remove(i3cdev);
|
||||||
|
|
||||||
i3c_device_free_ibi(i3cdev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct bus_type i3c_bus_type = {
|
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)
|
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)
|
!ops->send_ccc_cmd || !ops->do_daa || !ops->i2c_xfers)
|
||||||
return -EINVAL;
|
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 &&
|
if (ops->request_ibi &&
|
||||||
(!ops->enable_ibi || !ops->disable_ibi || !ops->free_ibi ||
|
(!ops->enable_ibi || !ops->disable_ibi || !ops->free_ibi ||
|
||||||
!ops->recycle_ibi_slot))
|
!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.i2c);
|
||||||
INIT_LIST_HEAD(&master->boardinfo.i3c);
|
INIT_LIST_HEAD(&master->boardinfo.i3c);
|
||||||
|
|
||||||
ret = i3c_bus_init(i3cbus, master->dev.of_node);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
device_initialize(&master->dev);
|
device_initialize(&master->dev);
|
||||||
dev_set_name(&master->dev, "i3c-%d", i3cbus->id);
|
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.coherent_dma_mask = parent->coherent_dma_mask;
|
||||||
master->dev.dma_parms = parent->dma_parms;
|
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);
|
ret = of_populate_i3c_bus(master);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_put_dev;
|
goto err_put_dev;
|
||||||
@ -2925,7 +2927,7 @@ int i3c_master_register(struct i3c_master_controller *master,
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto err_put_dev;
|
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) {
|
if (!master->wq) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto err_put_dev;
|
goto err_put_dev;
|
||||||
@ -3014,9 +3016,8 @@ int i3c_dev_setdasa_locked(struct i3c_dev_desc *dev)
|
|||||||
dev->boardinfo->init_dyn_addr);
|
dev->boardinfo->init_dyn_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
|
int i3c_dev_do_xfers_locked(struct i3c_dev_desc *dev, struct i3c_xfer *xfers,
|
||||||
struct i3c_priv_xfer *xfers,
|
int nxfers, enum i3c_xfer_mode mode)
|
||||||
int nxfers)
|
|
||||||
{
|
{
|
||||||
struct i3c_master_controller *master;
|
struct i3c_master_controller *master;
|
||||||
|
|
||||||
@ -3027,9 +3028,15 @@ int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
|
|||||||
if (!master || !xfers)
|
if (!master || !xfers)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!master->ops->priv_xfers)
|
if (mode != I3C_SDR && !(master->this->info.hdr_cap & BIT(mode)))
|
||||||
return -EOPNOTSUPP;
|
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);
|
return master->ops->priv_xfers(dev, xfers, nxfers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -228,6 +228,7 @@
|
|||||||
|
|
||||||
/* List of quirks */
|
/* List of quirks */
|
||||||
#define AMD_I3C_OD_PP_TIMING BIT(1)
|
#define AMD_I3C_OD_PP_TIMING BIT(1)
|
||||||
|
#define DW_I3C_DISABLE_RUNTIME_PM_QUIRK BIT(2)
|
||||||
|
|
||||||
struct dw_i3c_cmd {
|
struct dw_i3c_cmd {
|
||||||
u32 cmd_lo;
|
u32 cmd_lo;
|
||||||
@ -252,6 +253,10 @@ struct dw_i3c_i2c_dev_data {
|
|||||||
struct i3c_generic_ibi_pool *ibi_pool;
|
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,
|
static bool dw_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m,
|
||||||
const struct i3c_ccc_cmd *cmd)
|
const struct i3c_ccc_cmd *cmd)
|
||||||
{
|
{
|
||||||
@ -1535,6 +1540,8 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
|
|||||||
struct platform_device *pdev)
|
struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret, irq;
|
int ret, irq;
|
||||||
|
const struct dw_i3c_drvdata *drvdata;
|
||||||
|
unsigned long quirks = 0;
|
||||||
|
|
||||||
if (!master->platform_ops)
|
if (!master->platform_ops)
|
||||||
master->platform_ops = &dw_i3c_platform_ops_default;
|
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->maxdevs = ret >> 16;
|
||||||
master->free_pos = GENMASK(master->maxdevs - 1, 0);
|
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);
|
INIT_WORK(&master->hj_work, dw_i3c_hj_work);
|
||||||
ret = i3c_master_register(&master->base, &pdev->dev,
|
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);
|
cancel_work_sync(&master->hj_work);
|
||||||
i3c_master_unregister(&master->base);
|
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_disable(master->dev);
|
||||||
pm_runtime_set_suspended(master->dev);
|
pm_runtime_set_suspended(master->dev);
|
||||||
pm_runtime_dont_use_autosuspend(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);
|
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[] = {
|
static const struct of_device_id dw_i3c_master_of_match[] = {
|
||||||
{ .compatible = "snps,dw-i3c-master-1.00a", },
|
{ .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);
|
MODULE_DEVICE_TABLE(of, dw_i3c_master_of_match);
|
||||||
|
|||||||
@ -7,61 +7,196 @@
|
|||||||
* Author: Jarkko Nikula <jarkko.nikula@linux.intel.com>
|
* Author: Jarkko Nikula <jarkko.nikula@linux.intel.com>
|
||||||
*/
|
*/
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/bitfield.h>
|
||||||
|
#include <linux/debugfs.h>
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_qos.h>
|
||||||
|
|
||||||
struct mipi_i3c_hci_pci_info {
|
struct mipi_i3c_hci_pci {
|
||||||
int (*init)(struct pci_dev *pci);
|
struct pci_dev *pci;
|
||||||
|
struct platform_device *pdev;
|
||||||
|
const struct mipi_i3c_hci_pci_info *info;
|
||||||
|
void *private;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define INTEL_PRIV_OFFSET 0x2b0
|
struct mipi_i3c_hci_pci_info {
|
||||||
#define INTEL_PRIV_SIZE 0x28
|
int (*init)(struct mipi_i3c_hci_pci *hci);
|
||||||
#define INTEL_PRIV_RESETS 0x04
|
void (*exit)(struct mipi_i3c_hci_pci *hci);
|
||||||
#define INTEL_PRIV_RESETS_RESET BIT(0)
|
};
|
||||||
#define INTEL_PRIV_RESETS_RESET_DONE BIT(1)
|
|
||||||
|
|
||||||
static DEFINE_IDA(mipi_i3c_hci_pci_ida);
|
static DEFINE_IDA(mipi_i3c_hci_pci_ida);
|
||||||
|
|
||||||
static int mipi_i3c_hci_pci_intel_init(struct pci_dev *pci)
|
#define INTEL_PRIV_OFFSET 0x2b0
|
||||||
{
|
#define INTEL_PRIV_SIZE 0x28
|
||||||
unsigned long timeout;
|
#define INTEL_RESETS 0x04
|
||||||
void __iomem *priv;
|
#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,
|
#define INTEL_ACTIVELTR 0x0c
|
||||||
pci_resource_start(pci, 0) + INTEL_PRIV_OFFSET,
|
#define INTEL_IDLELTR 0x10
|
||||||
INTEL_PRIV_SIZE);
|
|
||||||
if (!priv)
|
#define INTEL_LTR_REQ BIT(15)
|
||||||
return -ENOMEM;
|
#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 */
|
/* Assert reset, wait for completion and release reset */
|
||||||
writel(0, priv + INTEL_PRIV_RESETS);
|
writel(0, priv + INTEL_RESETS);
|
||||||
timeout = jiffies + msecs_to_jiffies(10);
|
readl_poll_timeout(priv + INTEL_RESETS, reg,
|
||||||
while (!(readl(priv + INTEL_PRIV_RESETS) &
|
reg & INTEL_RESETS_RESET_DONE, 0,
|
||||||
INTEL_PRIV_RESETS_RESET_DONE)) {
|
INTEL_RESETS_TIMEOUT_US);
|
||||||
if (time_after(jiffies, timeout))
|
writel(INTEL_RESETS_RESET, priv + INTEL_RESETS);
|
||||||
break;
|
}
|
||||||
cpu_relax();
|
|
||||||
}
|
static void __iomem *intel_priv(struct pci_dev *pci)
|
||||||
writel(INTEL_PRIV_RESETS_RESET, priv + INTEL_PRIV_RESETS);
|
{
|
||||||
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct mipi_i3c_hci_pci_info intel_info = {
|
static void intel_i3c_exit(struct mipi_i3c_hci_pci *hci)
|
||||||
.init = mipi_i3c_hci_pci_intel_init,
|
{
|
||||||
|
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,
|
static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,
|
||||||
const struct pci_device_id *id)
|
const struct pci_device_id *id)
|
||||||
{
|
{
|
||||||
struct mipi_i3c_hci_pci_info *info;
|
struct mipi_i3c_hci_pci *hci;
|
||||||
struct platform_device *pdev;
|
|
||||||
struct resource res[2];
|
struct resource res[2];
|
||||||
int dev_id, ret;
|
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);
|
ret = pcim_enable_device(pci);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@ -82,43 +217,50 @@ static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,
|
|||||||
if (dev_id < 0)
|
if (dev_id < 0)
|
||||||
return dev_id;
|
return dev_id;
|
||||||
|
|
||||||
pdev = platform_device_alloc("mipi-i3c-hci", dev_id);
|
hci->pdev = platform_device_alloc("mipi-i3c-hci", dev_id);
|
||||||
if (!pdev)
|
if (!hci->pdev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
pdev->dev.parent = &pci->dev;
|
hci->pdev->dev.parent = &pci->dev;
|
||||||
device_set_node(&pdev->dev, dev_fwnode(&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)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
info = (struct mipi_i3c_hci_pci_info *)id->driver_data;
|
hci->info = (const struct mipi_i3c_hci_pci_info *)id->driver_data;
|
||||||
if (info && info->init) {
|
if (hci->info && hci->info->init) {
|
||||||
ret = info->init(pci);
|
ret = hci->info->init(hci);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = platform_device_add(pdev);
|
ret = platform_device_add(hci->pdev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err_exit;
|
||||||
|
|
||||||
pci_set_drvdata(pci, pdev);
|
pci_set_drvdata(pci, hci);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_exit:
|
||||||
|
if (hci->info && hci->info->exit)
|
||||||
|
hci->info->exit(hci);
|
||||||
err:
|
err:
|
||||||
platform_device_put(pdev);
|
platform_device_put(hci->pdev);
|
||||||
ida_free(&mipi_i3c_hci_pci_ida, dev_id);
|
ida_free(&mipi_i3c_hci_pci_ida, dev_id);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mipi_i3c_hci_pci_remove(struct pci_dev *pci)
|
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;
|
int dev_id = pdev->id;
|
||||||
|
|
||||||
|
if (hci->info && hci->info->exit)
|
||||||
|
hci->info->exit(hci);
|
||||||
|
|
||||||
platform_device_unregister(pdev);
|
platform_device_unregister(pdev);
|
||||||
ida_free(&mipi_i3c_hci_pci_ida, dev_id);
|
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 */
|
/* Panther Lake-P */
|
||||||
{ PCI_VDEVICE(INTEL, 0xe47c), (kernel_ulong_t)&intel_info},
|
{ PCI_VDEVICE(INTEL, 0xe47c), (kernel_ulong_t)&intel_info},
|
||||||
{ PCI_VDEVICE(INTEL, 0xe46f), (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);
|
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_NONE 0
|
||||||
#define SVC_I3C_MCTRL_REQUEST_START_ADDR 1
|
#define SVC_I3C_MCTRL_REQUEST_START_ADDR 1
|
||||||
#define SVC_I3C_MCTRL_REQUEST_STOP 2
|
#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_IBI_ACKNACK 3
|
||||||
#define SVC_I3C_MCTRL_REQUEST_PROC_DAA 4
|
#define SVC_I3C_MCTRL_REQUEST_PROC_DAA 4
|
||||||
#define SVC_I3C_MCTRL_REQUEST_AUTO_IBI 7
|
#define SVC_I3C_MCTRL_REQUEST_AUTO_IBI 7
|
||||||
#define SVC_I3C_MCTRL_TYPE_I3C 0
|
#define SVC_I3C_MCTRL_TYPE_I3C 0
|
||||||
#define SVC_I3C_MCTRL_TYPE_I2C BIT(4)
|
#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_AUTO 0
|
||||||
#define SVC_I3C_MCTRL_IBIRESP_ACK_WITHOUT_BYTE 0
|
#define SVC_I3C_MCTRL_IBIRESP_ACK_WITHOUT_BYTE 0
|
||||||
#define SVC_I3C_MCTRL_IBIRESP_ACK_WITH_BYTE BIT(7)
|
#define SVC_I3C_MCTRL_IBIRESP_ACK_WITH_BYTE BIT(7)
|
||||||
@ -95,6 +97,7 @@
|
|||||||
#define SVC_I3C_MINTMASKED 0x098
|
#define SVC_I3C_MINTMASKED 0x098
|
||||||
#define SVC_I3C_MERRWARN 0x09C
|
#define SVC_I3C_MERRWARN 0x09C
|
||||||
#define SVC_I3C_MERRWARN_NACK BIT(2)
|
#define SVC_I3C_MERRWARN_NACK BIT(2)
|
||||||
|
#define SVC_I3C_MERRWARN_CRC BIT(10)
|
||||||
#define SVC_I3C_MERRWARN_TIMEOUT BIT(20)
|
#define SVC_I3C_MERRWARN_TIMEOUT BIT(20)
|
||||||
#define SVC_I3C_MDMACTRL 0x0A0
|
#define SVC_I3C_MDMACTRL 0x0A0
|
||||||
#define SVC_I3C_MDATACTRL 0x0AC
|
#define SVC_I3C_MDATACTRL 0x0AC
|
||||||
@ -165,12 +168,16 @@
|
|||||||
|
|
||||||
struct svc_i3c_cmd {
|
struct svc_i3c_cmd {
|
||||||
u8 addr;
|
u8 addr;
|
||||||
bool rnw;
|
union {
|
||||||
|
bool rnw;
|
||||||
|
u8 cmd;
|
||||||
|
u32 rnw_cmd;
|
||||||
|
};
|
||||||
u8 *in;
|
u8 *in;
|
||||||
const void *out;
|
const void *out;
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
unsigned int actual_len;
|
unsigned int actual_len;
|
||||||
struct i3c_priv_xfer *xfer;
|
struct i3c_xfer *xfer;
|
||||||
bool continued;
|
bool continued;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -383,6 +390,36 @@ svc_i3c_master_dev_from_addr(struct svc_i3c_master *master,
|
|||||||
return master->descs[i];
|
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)
|
static void svc_i3c_master_emit_stop(struct svc_i3c_master *master)
|
||||||
{
|
{
|
||||||
writel(SVC_I3C_MCTRL_REQUEST_STOP, master->regs + SVC_I3C_MCTRL);
|
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;
|
int ret, val;
|
||||||
u8 *buf;
|
u8 *buf;
|
||||||
|
|
||||||
slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
|
/*
|
||||||
if (!slot)
|
* Wait for transfer to complete before returning. Otherwise, the EmitStop
|
||||||
return -ENOSPC;
|
* request might be sent when the transfer is not complete.
|
||||||
|
*/
|
||||||
slot->len = 0;
|
|
||||||
buf = slot->data;
|
|
||||||
|
|
||||||
ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
|
ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
|
||||||
SVC_I3C_MSTATUS_COMPLETE(val), 0, 1000);
|
SVC_I3C_MSTATUS_COMPLETE(val), 0, 1000);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(master->dev, "Timeout when polling for COMPLETE\n");
|
dev_err(master->dev, "Timeout when polling for COMPLETE\n");
|
||||||
i3c_generic_ibi_recycle_slot(data->ibi_pool, slot);
|
|
||||||
return ret;
|
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)) &&
|
while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS)) &&
|
||||||
slot->len < SVC_I3C_FIFO_SIZE) {
|
slot->len < SVC_I3C_FIFO_SIZE) {
|
||||||
mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
|
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.
|
* cycle, leading to missed client IBI handlers.
|
||||||
*
|
*
|
||||||
* A typical scenario is when IBIWON occurs and bus arbitration is lost
|
* 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.
|
* 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.dyn_addr = ret;
|
||||||
|
|
||||||
|
info.hdr_cap = I3C_CCC_HDR_MODE(I3C_HDR_DDR);
|
||||||
|
|
||||||
writel(SVC_MDYNADDR_VALID | SVC_MDYNADDR_ADDR(info.dyn_addr),
|
writel(SVC_MDYNADDR_VALID | SVC_MDYNADDR_ADDR(info.dyn_addr),
|
||||||
master->regs + SVC_I3C_MDYNADDR);
|
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,
|
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,
|
u8 *in, const u8 *out, unsigned int xfer_len,
|
||||||
unsigned int *actual_len, bool continued, bool repeat_start)
|
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;
|
int retry = repeat_start ? 1 : 2;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
int ret;
|
int ret;
|
||||||
@ -1304,6 +1350,16 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
|||||||
/* clean SVC_I3C_MINT_IBIWON w1c bits */
|
/* clean SVC_I3C_MINT_IBIWON w1c bits */
|
||||||
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
|
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--) {
|
while (retry--) {
|
||||||
writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
|
writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
|
||||||
@ -1397,7 +1453,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
|||||||
|
|
||||||
if (rnw)
|
if (rnw)
|
||||||
ret = svc_i3c_master_read(master, in, xfer_len);
|
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);
|
ret = svc_i3c_master_write(master, out, xfer_len);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto emit_stop;
|
goto emit_stop;
|
||||||
@ -1410,10 +1466,19 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto emit_stop;
|
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);
|
writel(SVC_I3C_MINT_COMPLETE, master->regs + SVC_I3C_MSTATUS);
|
||||||
|
|
||||||
if (!continued) {
|
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. */
|
/* Wait idle if stop is sent. */
|
||||||
readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
|
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;
|
return 0;
|
||||||
|
|
||||||
emit_stop:
|
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_clear_merrwarn(master);
|
||||||
svc_i3c_master_flush_fifo(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);
|
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)
|
static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
|
||||||
{
|
{
|
||||||
struct svc_i3c_xfer *xfer = master->xferqueue.cur;
|
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++) {
|
for (i = 0; i < xfer->ncmds; i++) {
|
||||||
struct svc_i3c_cmd *cmd = &xfer->cmds[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->addr, cmd->in, cmd->out,
|
||||||
cmd->len, &cmd->actual_len,
|
cmd->len, &cmd->actual_len,
|
||||||
cmd->continued, i > 0);
|
cmd->continued, i > 0);
|
||||||
@ -1659,9 +1733,8 @@ static int svc_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
|
static int svc_i3c_master_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *xfers,
|
||||||
struct i3c_priv_xfer *xfers,
|
int nxfers, enum i3c_xfer_mode mode)
|
||||||
int nxfers)
|
|
||||||
{
|
{
|
||||||
struct i3c_master_controller *m = i3c_dev_get_master(dev);
|
struct i3c_master_controller *m = i3c_dev_get_master(dev);
|
||||||
struct svc_i3c_master *master = to_svc_i3c_master(m);
|
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;
|
struct svc_i3c_xfer *xfer;
|
||||||
int ret, i;
|
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);
|
xfer = svc_i3c_master_alloc_xfer(master, nxfers);
|
||||||
if (!xfer)
|
if (!xfer)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
xfer->type = SVC_I3C_MCTRL_TYPE_I3C;
|
xfer->type = i3c_mode_to_svc_type(mode);
|
||||||
|
|
||||||
for (i = 0; i < nxfers; i++) {
|
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];
|
struct svc_i3c_cmd *cmd = &xfer->cmds[i];
|
||||||
|
|
||||||
cmd->xfer = &xfers[i];
|
cmd->xfer = &xfers[i];
|
||||||
cmd->addr = master->addrs[data->index];
|
cmd->addr = master->addrs[data->index];
|
||||||
cmd->rnw = xfers[i].rnw;
|
cmd->rnw_cmd = rnw_cmd;
|
||||||
cmd->in = xfers[i].rnw ? xfers[i].data.in : NULL;
|
cmd->in = rnw ? xfers[i].data.in : NULL;
|
||||||
cmd->out = xfers[i].rnw ? NULL : xfers[i].data.out;
|
cmd->out = rnw ? NULL : xfers[i].data.out;
|
||||||
cmd->len = xfers[i].len;
|
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;
|
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,
|
.do_daa = svc_i3c_master_do_daa,
|
||||||
.supports_ccc_cmd = svc_i3c_master_supports_ccc_cmd,
|
.supports_ccc_cmd = svc_i3c_master_supports_ccc_cmd,
|
||||||
.send_ccc_cmd = svc_i3c_master_send_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,
|
.i2c_xfers = svc_i3c_master_i2c_xfers,
|
||||||
.request_ibi = svc_i3c_master_request_ibi,
|
.request_ibi = svc_i3c_master_request_ibi,
|
||||||
.free_ibi = svc_i3c_master_free_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)
|
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 net_device_stats *stats = &mi->mbus->ndev->stats;
|
||||||
struct mctp_i3c_internal_hdr *ihdr = NULL;
|
struct mctp_i3c_internal_hdr *ihdr = NULL;
|
||||||
struct sk_buff *skb = 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. */
|
/* Make sure netif_rx() is read in the same order as i3c. */
|
||||||
mutex_lock(&mi->lock);
|
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)
|
if (rc < 0)
|
||||||
goto err;
|
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)
|
static void mctp_i3c_xmit(struct mctp_i3c_bus *mbus, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct net_device_stats *stats = &mbus->ndev->stats;
|
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_internal_hdr *ihdr = NULL;
|
||||||
struct mctp_i3c_device *mi = NULL;
|
struct mctp_i3c_device *mi = NULL;
|
||||||
unsigned int data_len;
|
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;
|
data[data_len] = pec;
|
||||||
|
|
||||||
xfer.data.out = data;
|
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) {
|
if (rc == 0) {
|
||||||
stats->tx_bytes += data_len;
|
stats->tx_bytes += data_len;
|
||||||
stats->tx_packets++;
|
stats->tx_packets++;
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
* These are the standard error codes as defined by the I3C specification.
|
* 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_priv_xfers() or
|
||||||
* i3c_device_send_hdr_cmds() one can check the error code in
|
* 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.
|
* 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_DDR: DDR mode
|
||||||
* @I3C_HDR_TSP: TSP mode
|
* @I3C_HDR_TSP: TSP mode
|
||||||
* @I3C_HDR_TSL: TSL mode
|
* @I3C_HDR_TSL: TSL mode
|
||||||
|
* @I3C_SDR: SDR mode (NOT HDR mode)
|
||||||
*/
|
*/
|
||||||
enum i3c_hdr_mode {
|
enum i3c_xfer_mode {
|
||||||
I3C_HDR_DDR,
|
/* The below 3 value (I3C_HDR*) must match GETCAP1 Byte bit position */
|
||||||
I3C_HDR_TSP,
|
I3C_HDR_DDR = 0,
|
||||||
I3C_HDR_TSL,
|
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
|
* @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
|
* @len: transfer length in bytes of the transfer
|
||||||
* @actual_len: actual length in bytes are transferred by the controller
|
* @actual_len: actual length in bytes are transferred by the controller
|
||||||
* @data: input/output buffer
|
* @data: input/output buffer
|
||||||
@ -60,8 +65,11 @@ enum i3c_hdr_mode {
|
|||||||
* @data.out: output buffer. Must point to a DMA-able buffer
|
* @data.out: output buffer. Must point to a DMA-able buffer
|
||||||
* @err: I3C error code
|
* @err: I3C error code
|
||||||
*/
|
*/
|
||||||
struct i3c_priv_xfer {
|
struct i3c_xfer {
|
||||||
u8 rnw;
|
union {
|
||||||
|
u8 rnw;
|
||||||
|
u8 cmd;
|
||||||
|
};
|
||||||
u16 len;
|
u16 len;
|
||||||
u16 actual_len;
|
u16 actual_len;
|
||||||
union {
|
union {
|
||||||
@ -71,6 +79,9 @@ struct i3c_priv_xfer {
|
|||||||
enum i3c_error_code err;
|
enum i3c_error_code err;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* keep back compatible */
|
||||||
|
#define i3c_priv_xfer i3c_xfer
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enum i3c_dcr - I3C DCR values
|
* enum i3c_dcr - I3C DCR values
|
||||||
* @I3C_DCR_GENERIC_DEVICE: generic I3C device
|
* @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, \
|
i3c_i2c_driver_unregister, \
|
||||||
__i2cdrv)
|
__i2cdrv)
|
||||||
|
|
||||||
int i3c_device_do_priv_xfers(struct i3c_device *dev,
|
int i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers,
|
||||||
struct i3c_priv_xfer *xfers,
|
int nxfers, enum i3c_xfer_mode mode);
|
||||||
int nxfers);
|
|
||||||
|
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);
|
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);
|
void i3c_device_free_ibi(struct i3c_device *dev);
|
||||||
int i3c_device_enable_ibi(struct i3c_device *dev);
|
int i3c_device_enable_ibi(struct i3c_device *dev);
|
||||||
int i3c_device_disable_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 */
|
#endif /* I3C_DEV_H */
|
||||||
|
|||||||
@ -418,7 +418,11 @@ struct i3c_bus {
|
|||||||
* @send_ccc_cmd: send a CCC command
|
* @send_ccc_cmd: send a CCC command
|
||||||
* This method is mandatory.
|
* This method is mandatory.
|
||||||
* @priv_xfers: do one or several private I3C SDR transfers
|
* @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.
|
* @attach_i2c_dev: called every time an I2C device is attached to the bus.
|
||||||
* This is a good place to attach master controller specific
|
* This is a good place to attach master controller specific
|
||||||
* data to I2C devices.
|
* data to I2C devices.
|
||||||
@ -474,9 +478,13 @@ struct i3c_master_controller_ops {
|
|||||||
const struct i3c_ccc_cmd *cmd);
|
const struct i3c_ccc_cmd *cmd);
|
||||||
int (*send_ccc_cmd)(struct i3c_master_controller *master,
|
int (*send_ccc_cmd)(struct i3c_master_controller *master,
|
||||||
struct i3c_ccc_cmd *cmd);
|
struct i3c_ccc_cmd *cmd);
|
||||||
|
/* Deprecated, please use i3c_xfers() */
|
||||||
int (*priv_xfers)(struct i3c_dev_desc *dev,
|
int (*priv_xfers)(struct i3c_dev_desc *dev,
|
||||||
struct i3c_priv_xfer *xfers,
|
struct i3c_priv_xfer *xfers,
|
||||||
int nxfers);
|
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);
|
int (*attach_i2c_dev)(struct i2c_dev_desc *dev);
|
||||||
void (*detach_i2c_dev)(struct i2c_dev_desc *dev);
|
void (*detach_i2c_dev)(struct i2c_dev_desc *dev);
|
||||||
int (*i2c_xfers)(struct i2c_dev_desc *dev,
|
int (*i2c_xfers)(struct i2c_dev_desc *dev,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user