mirror of
https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-10.git
synced 2026-09-09 00:07:04 +08:00
Merge: i3c: master: v6.19 sync and ACPI support
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-10/-/merge_requests/3302 JIRA: https://issues.redhat.com/browse/RHEL-143335 Update the i3c master driver with all features added up to upstream v6.19 and most fixes up to v7.2-rc7. ACPI support for i3c is taken from one patch series in linux-next. --- This MR includes a partial backport for only the hunks affecting i3c from a couple treewide patches upstream. All of these fixes are related to excluded hunks. Omitted-fix: fd1d6b9d13f3 (`xz: fix arm fdt compile error for kmalloc replacement`) Omitted-fix: 96a7b71c4438 (`ubd: Use pointer-to-pointers for io_thread_req arrays`) Omitted-fix: 795469820c63 (`kcsan: test: Adjust "expect" allocation type for kmalloc_obj`) Omitted-fix: 5548dd7fa845 (`tools/testing: fix testing/vma and testing/radix-tree build`) Omitted-fix: 405ca72dc589 (`landlock: Fix formatting`) Omitted-fix: 4c0134639694 (`KVM: PPC: e500: Fix build error due to using kmalloc_obj() with wrong type`) Omitted-fix: 2d2b5507e598 (`btrfs: replace kcalloc() calls to kzalloc_objs()`) Omitted-fix: 9f4ab0787e7b (`btrfs: do more kmalloc_obj()/kmalloc_objs() conversions`) Omitted-fix: 37f1f51fba1a (`btrfs: convert kmalloc_array to kmalloc_objs in btrfs_calc_avail_data_space()`) Omitted-fix: 4c6d43db2a4d (`net: dst_metadata: fix false-positive memcpy overflow in tun_dst_unclone`) Signed-off-by: Jennifer Berringer <jberring@redhat.com> Approved-by: Mark Langsdorf <mlangsdo@redhat.com> Approved-by: Mark Salter <msalter@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: CKI GitLab Kmaint Pipeline Bot <26919896-cki-kmaint-pipeline-bot@users.noreply.gitlab.com>
This commit is contained in:
@@ -161,3 +161,34 @@ Contact: linux-i3c@vger.kernel.org
|
|||||||
Description:
|
Description:
|
||||||
These directories are just symbolic links to
|
These directories are just symbolic links to
|
||||||
/sys/bus/i3c/devices/i3c-<bus-id>/<bus-id>-<device-pid>.
|
/sys/bus/i3c/devices/i3c-<bus-id>/<bus-id>-<device-pid>.
|
||||||
|
|
||||||
|
What: /sys/bus/i3c/devices/i3c-<bus-id>/<bus-id>-<device-pid>/dev_nack_retry_count
|
||||||
|
KernelVersion: 6.18
|
||||||
|
Contact: linux-i3c@vger.kernel.org
|
||||||
|
Description:
|
||||||
|
Expose the dev_nak_retry_count which controls the number of
|
||||||
|
automatic retries that will be performed by the controller when
|
||||||
|
the target device returns a NACK response. A value of 0 disables
|
||||||
|
the automatic retries. Exist only when I3C constroller supports
|
||||||
|
this retry on nack feature.
|
||||||
|
|
||||||
|
What: /sys/bus/i3c/devices/i3c-<bus-id>/do_daa
|
||||||
|
KernelVersion: 7.0
|
||||||
|
Contact: linux-i3c@vger.kernel.org
|
||||||
|
Description:
|
||||||
|
Write-only attribute that triggers a Dynamic Address Assignment
|
||||||
|
(DAA) procedure which discovers new I3C devices on the bus.
|
||||||
|
Writing a boolean true value (1, y, yes, true, on) to this
|
||||||
|
attribute causes the master controller to perform DAA, which
|
||||||
|
includes broadcasting an ENTDAA (Enter Dynamic Address Assignment)
|
||||||
|
Common Command Code (CCC) on the bus. Writing a false value
|
||||||
|
returns -EINVAL.
|
||||||
|
|
||||||
|
This is useful for discovering I3C devices that were not present
|
||||||
|
during initial bus initialization and are unable to issue
|
||||||
|
Hot-Join. Only devices without a currently assigned dynamic address
|
||||||
|
will respond to the ENTDAA broadcast and be assigned addresses.
|
||||||
|
|
||||||
|
Note that this mechanism is distinct from Hot-Join, since this is
|
||||||
|
controller-initiated discovery, while Hot-Join is device-initiated
|
||||||
|
method to provoke controller discovery procedure.
|
||||||
|
|||||||
@@ -31,10 +31,12 @@ properties:
|
|||||||
described in the device tree, which in turn means we have to describe
|
described in the device tree, which in turn means we have to describe
|
||||||
I3C devices.
|
I3C devices.
|
||||||
|
|
||||||
Another use case for describing an I3C device in the device tree is when
|
Other use-cases for describing an I3C device in the device tree are:
|
||||||
this I3C device has a static I2C address and we want to assign it a
|
- When the I3C device has a static I2C address and we want to assign
|
||||||
specific I3C dynamic address before the DAA takes place (so that other
|
it a specific I3C dynamic address before the DAA takes place (so
|
||||||
devices on the bus can't take this dynamic address).
|
that other devices on the bus can't take this dynamic address).
|
||||||
|
- When the I3C device requires SETAASA for its discovery and uses a
|
||||||
|
pre-defined static address.
|
||||||
|
|
||||||
"#size-cells":
|
"#size-cells":
|
||||||
const: 0
|
const: 0
|
||||||
@@ -145,7 +147,31 @@ patternProperties:
|
|||||||
Dynamic address to be assigned to this device. In case static address is
|
Dynamic address to be assigned to this device. In case static address is
|
||||||
present (first cell of the reg property != 0), this address is assigned
|
present (first cell of the reg property != 0), this address is assigned
|
||||||
through SETDASA. If static address is not present, this address is assigned
|
through SETDASA. If static address is not present, this address is assigned
|
||||||
through SETNEWDA after assigning a temporary address via ENTDAA.
|
through SETNEWDA after assigning a temporary address via ENTDAA. If
|
||||||
|
SETAASA is used, this property is not used, and the static address itself
|
||||||
|
becomes the dynamic address.
|
||||||
|
|
||||||
|
mipi-i3c-static-method:
|
||||||
|
$ref: /schemas/types.yaml#/definitions/uint32
|
||||||
|
minimum: 0x1
|
||||||
|
maximum: 0x7
|
||||||
|
default: 1
|
||||||
|
description: |
|
||||||
|
Bitmap describing which methods of Dynamic Address Assignment from a
|
||||||
|
static address are supported by this I3C Target. For each defined bit
|
||||||
|
position, a set bit indicates support for that method and a cleared
|
||||||
|
bit indicates lack of support.
|
||||||
|
|
||||||
|
Bit 0: SETDASA CCC (Direct)
|
||||||
|
Bit 1: SETAASA CCC (Broadcast)
|
||||||
|
Bit 2: Other CCC (vendor / standards extension)
|
||||||
|
All other bits are reserved.
|
||||||
|
|
||||||
|
This property follows the MIPI I3C specification. The primary use
|
||||||
|
of this property is to indicate support for SETAASA, i.e Bit 1, but
|
||||||
|
will allow other values mentioned in the specification so that it
|
||||||
|
mirrors the specification. SETDASA will remain as the default method
|
||||||
|
even if this property is not present.
|
||||||
|
|
||||||
required:
|
required:
|
||||||
- reg
|
- reg
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ struct acpi_rsconvert_info acpi_rs_convert_csi2_serial_bus[14] = {
|
|||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[17] = {
|
struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[18] = {
|
||||||
{ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS,
|
{ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS,
|
||||||
ACPI_RS_SIZE(struct acpi_resource_i2c_serialbus),
|
ACPI_RS_SIZE(struct acpi_resource_i2c_serialbus),
|
||||||
ACPI_RSC_TABLE_SIZE(acpi_rs_convert_i2c_serial_bus)},
|
ACPI_RSC_TABLE_SIZE(acpi_rs_convert_i2c_serial_bus)},
|
||||||
@@ -391,6 +391,11 @@ struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[17] = {
|
|||||||
AML_OFFSET(i2c_serial_bus.type_specific_flags),
|
AML_OFFSET(i2c_serial_bus.type_specific_flags),
|
||||||
0},
|
0},
|
||||||
|
|
||||||
|
/* Read LVR from Type Specific Flags, bits[15:8] */
|
||||||
|
{ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.i2c_serial_bus.lvr),
|
||||||
|
AML_OFFSET(i2c_serial_bus.type_specific_flags) + 1,
|
||||||
|
1},
|
||||||
|
|
||||||
{ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.i2c_serial_bus.connection_speed),
|
{ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.i2c_serial_bus.connection_speed),
|
||||||
AML_OFFSET(i2c_serial_bus.connection_speed),
|
AML_OFFSET(i2c_serial_bus.connection_speed),
|
||||||
1},
|
1},
|
||||||
|
|||||||
@@ -2201,12 +2201,15 @@ config SENSORS_INA3221
|
|||||||
|
|
||||||
config SENSORS_SPD5118
|
config SENSORS_SPD5118
|
||||||
tristate "SPD5118 Compliant Temperature Sensors"
|
tristate "SPD5118 Compliant Temperature Sensors"
|
||||||
depends on I2C
|
depends on I3C_OR_I2C
|
||||||
select REGMAP_I2C
|
select REGMAP_I2C
|
||||||
|
select REGMAP_I3C if I3C
|
||||||
help
|
help
|
||||||
If you say yes here you get support for SPD5118 (JEDEC JESD300)
|
If you say yes here you get support for SPD5118 (JEDEC JESD300)
|
||||||
compliant temperature sensors. Such sensors are found on DDR5 memory
|
compliant temperature sensors using I2C or I3C bus interface.
|
||||||
modules.
|
Such sensors are found on DDR5 memory modules.
|
||||||
|
|
||||||
|
This driver supports both I2C and I3C interfaces.
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called spd5118.
|
will be called spd5118.
|
||||||
|
|||||||
+51
-66
@@ -18,6 +18,7 @@
|
|||||||
#include <linux/bits.h>
|
#include <linux/bits.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/i3c/device.h>
|
||||||
#include <linux/hwmon.h>
|
#include <linux/hwmon.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
@@ -66,9 +67,6 @@ static const unsigned short normal_i2c[] = {
|
|||||||
#define SPD5118_EEPROM_BASE 0x80
|
#define SPD5118_EEPROM_BASE 0x80
|
||||||
#define SPD5118_EEPROM_SIZE (SPD5118_PAGE_SIZE * SPD5118_NUM_PAGES)
|
#define SPD5118_EEPROM_SIZE (SPD5118_PAGE_SIZE * SPD5118_NUM_PAGES)
|
||||||
|
|
||||||
#define PAGE_ADDR0(page) (((page) & BIT(0)) << 6)
|
|
||||||
#define PAGE_ADDR1_4(page) (((page) & GENMASK(4, 1)) >> 1)
|
|
||||||
|
|
||||||
/* Temperature unit in millicelsius */
|
/* Temperature unit in millicelsius */
|
||||||
#define SPD5118_TEMP_UNIT (MILLIDEGREE_PER_DEGREE / 4)
|
#define SPD5118_TEMP_UNIT (MILLIDEGREE_PER_DEGREE / 4)
|
||||||
/* Representable temperature range in millicelsius */
|
/* Representable temperature range in millicelsius */
|
||||||
@@ -78,7 +76,6 @@ static const unsigned short normal_i2c[] = {
|
|||||||
struct spd5118_data {
|
struct spd5118_data {
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
struct mutex nvmem_lock;
|
struct mutex nvmem_lock;
|
||||||
bool is_16bit;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* hwmon */
|
/* hwmon */
|
||||||
@@ -354,12 +351,7 @@ static ssize_t spd5118_nvmem_read_page(struct spd5118_data *data, char *buf,
|
|||||||
if (offset + count > SPD5118_PAGE_SIZE)
|
if (offset + count > SPD5118_PAGE_SIZE)
|
||||||
count = SPD5118_PAGE_SIZE - offset;
|
count = SPD5118_PAGE_SIZE - offset;
|
||||||
|
|
||||||
if (data->is_16bit) {
|
|
||||||
addr = SPD5118_EEPROM_BASE | PAGE_ADDR0(page) |
|
|
||||||
(PAGE_ADDR1_4(page) << 8);
|
|
||||||
} else {
|
|
||||||
addr = page * 0x100 + SPD5118_EEPROM_BASE;
|
addr = page * 0x100 + SPD5118_EEPROM_BASE;
|
||||||
}
|
|
||||||
err = regmap_bulk_read(regmap, addr + offset, buf, count);
|
err = regmap_bulk_read(regmap, addr + offset, buf, count);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
@@ -479,10 +471,22 @@ static const struct regmap_config spd5118_regmap8_config = {
|
|||||||
.num_ranges = ARRAY_SIZE(spd5118_i2c_regmap_range_cfg),
|
.num_ranges = ARRAY_SIZE(spd5118_i2c_regmap_range_cfg),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct regmap_config spd5118_regmap16_config = {
|
/*
|
||||||
|
* SPD5118 2-byte register address format (JESD300-5, Tables 7 & 20):
|
||||||
|
* Byte 1 (on wire first): MemReg | BlkAddr[0] | Address[5:0]
|
||||||
|
* Byte 2 (on wire second): 0000 | BlkAddr[4:1]
|
||||||
|
*
|
||||||
|
* The address byte (with MemReg and lower address bits) must be sent first,
|
||||||
|
* followed by the upper block address byte. With regmap 16-bit register
|
||||||
|
* format, this maps to little-endian: the low byte of the 16-bit value is
|
||||||
|
* transmitted first. No range config is needed since I3C does not use MR11
|
||||||
|
* page switching.
|
||||||
|
*/
|
||||||
|
static const struct regmap_config spd5118_regmap_i3c_config = {
|
||||||
.reg_bits = 16,
|
.reg_bits = 16,
|
||||||
.val_bits = 8,
|
.val_bits = 8,
|
||||||
.max_register = 0x7ff,
|
.max_register = 0x7ff,
|
||||||
|
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||||
.writeable_reg = spd5118_writeable_reg,
|
.writeable_reg = spd5118_writeable_reg,
|
||||||
.volatile_reg = spd5118_volatile_reg,
|
.volatile_reg = spd5118_volatile_reg,
|
||||||
.cache_type = REGCACHE_MAPLE,
|
.cache_type = REGCACHE_MAPLE,
|
||||||
@@ -525,8 +529,7 @@ static int spd5118_resume(struct device *dev)
|
|||||||
|
|
||||||
static DEFINE_SIMPLE_DEV_PM_OPS(spd5118_pm_ops, spd5118_suspend, spd5118_resume);
|
static DEFINE_SIMPLE_DEV_PM_OPS(spd5118_pm_ops, spd5118_suspend, spd5118_resume);
|
||||||
|
|
||||||
static int spd5118_common_probe(struct device *dev, struct regmap *regmap,
|
static int spd5118_common_probe(struct device *dev, struct regmap *regmap)
|
||||||
bool is_16bit)
|
|
||||||
{
|
{
|
||||||
unsigned int capability, revision, vendor, bank;
|
unsigned int capability, revision, vendor, bank;
|
||||||
struct spd5118_data *data;
|
struct spd5118_data *data;
|
||||||
@@ -543,8 +546,6 @@ static int spd5118_common_probe(struct device *dev, struct regmap *regmap,
|
|||||||
if (!(capability & SPD5118_CAP_TS_SUPPORT))
|
if (!(capability & SPD5118_CAP_TS_SUPPORT))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
data->is_16bit = is_16bit;
|
|
||||||
|
|
||||||
err = regmap_read(regmap, SPD5118_REG_REVISION, &revision);
|
err = regmap_read(regmap, SPD5118_REG_REVISION, &revision);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
@@ -686,69 +687,21 @@ static int spd5118_i2c_init(struct i2c_client *client)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 16-bit addressing note:
|
|
||||||
*
|
|
||||||
* If I2C_FUNC_I2C is not supported by an I2C adapter driver, regmap uses
|
|
||||||
* SMBus operations as alternative. To simulate a read operation with a 16-bit
|
|
||||||
* address, it writes the address using i2c_smbus_write_byte_data(), followed
|
|
||||||
* by one or more calls to i2c_smbus_read_byte() to read the data.
|
|
||||||
* Per spd5118 standard, a read operation after writing the address must start
|
|
||||||
* with <Sr> (Repeat Start). However, a SMBus read byte operation starts with
|
|
||||||
* <S> (Start). This resets the register address in the spd5118 chip. As result,
|
|
||||||
* i2c_smbus_read_byte() always returns data from register address 0x00.
|
|
||||||
*
|
|
||||||
* A working alternative to access chips with 16-bit register addresses in the
|
|
||||||
* absence of I2C_FUNC_I2C support is not known.
|
|
||||||
*
|
|
||||||
* For this reason, 16-bit addressing can only be supported with I2C if the
|
|
||||||
* adapter supports I2C_FUNC_I2C.
|
|
||||||
*
|
|
||||||
* For I2C, the addressing mode selected by the BIOS must not be changed.
|
|
||||||
* Experiments show that at least some PC BIOS versions will not change the
|
|
||||||
* addressing mode on a soft reboot and end up in setup, claiming that some
|
|
||||||
* configuration change happened. This will happen again after a power cycle,
|
|
||||||
* which does reset the addressing mode. To prevent this from happening,
|
|
||||||
* detect if 16-bit addressing is enabled and always use the currently
|
|
||||||
* configured addressing mode.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int spd5118_i2c_probe(struct i2c_client *client)
|
static int spd5118_i2c_probe(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
const struct regmap_config *config;
|
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
int err, mode;
|
int err;
|
||||||
bool is_16bit;
|
|
||||||
|
|
||||||
err = spd5118_i2c_init(client);
|
err = spd5118_i2c_init(client);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE);
|
regmap = devm_regmap_init_i2c(client, &spd5118_regmap8_config);
|
||||||
if (mode < 0)
|
|
||||||
return mode;
|
|
||||||
|
|
||||||
is_16bit = mode & SPD5118_LEGACY_MODE_ADDR;
|
|
||||||
if (is_16bit) {
|
|
||||||
/*
|
|
||||||
* See 16-bit addressing note above explaining why it is
|
|
||||||
* necessary to check for I2C_FUNC_I2C support here.
|
|
||||||
*/
|
|
||||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
|
||||||
dev_err(dev, "Adapter does not support 16-bit register addresses\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
config = &spd5118_regmap16_config;
|
|
||||||
} else {
|
|
||||||
config = &spd5118_regmap8_config;
|
|
||||||
}
|
|
||||||
|
|
||||||
regmap = devm_regmap_init_i2c(client, config);
|
|
||||||
if (IS_ERR(regmap))
|
if (IS_ERR(regmap))
|
||||||
return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");
|
return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");
|
||||||
|
|
||||||
return spd5118_common_probe(dev, regmap, is_16bit);
|
return spd5118_common_probe(dev, regmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_device_id spd5118_i2c_id[] = {
|
static const struct i2c_device_id spd5118_i2c_id[] = {
|
||||||
@@ -776,7 +729,39 @@ static struct i2c_driver spd5118_i2c_driver = {
|
|||||||
.address_list = IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? normal_i2c : NULL,
|
.address_list = IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? normal_i2c : NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_i2c_driver(spd5118_i2c_driver);
|
/* I3C */
|
||||||
|
|
||||||
|
static int spd5118_i3c_probe(struct i3c_device *i3cdev)
|
||||||
|
{
|
||||||
|
struct device *dev = i3cdev_to_dev(i3cdev);
|
||||||
|
struct regmap *regmap;
|
||||||
|
u8 regval[2];
|
||||||
|
int err;
|
||||||
|
|
||||||
|
regmap = devm_regmap_init_i3c(i3cdev, &spd5118_regmap_i3c_config);
|
||||||
|
if (IS_ERR(regmap))
|
||||||
|
return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");
|
||||||
|
|
||||||
|
err = regmap_bulk_read(regmap, SPD5118_REG_TYPE, regval, 2);
|
||||||
|
if (err)
|
||||||
|
return dev_err_probe(dev, err, "failed to read device type\n");
|
||||||
|
|
||||||
|
if (regval[0] != 0x51 || regval[1] != 0x18)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
return spd5118_common_probe(dev, regmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct i3c_driver spd5118_i3c_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "spd5118_i3c",
|
||||||
|
.of_match_table = spd5118_of_ids,
|
||||||
|
.pm = pm_sleep_ptr(&spd5118_pm_ops),
|
||||||
|
},
|
||||||
|
.probe = spd5118_i3c_probe,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_i3c_i2c_driver(spd5118_i3c_driver, &spd5118_i2c_driver);
|
||||||
|
|
||||||
MODULE_AUTHOR("René Rebe <rene@exactcode.de>");
|
MODULE_AUTHOR("René Rebe <rene@exactcode.de>");
|
||||||
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
|
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
|
||||||
|
|||||||
+63
-10
@@ -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.
|
||||||
*
|
*
|
||||||
@@ -32,9 +32,8 @@
|
|||||||
* driver needs to resend the 'xfers' some time later.
|
* driver needs to resend the 'xfers' some time later.
|
||||||
* See I3C spec ver 1.1.1 09-Jun-2021. Section: 5.1.2.2.3.
|
* 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,
|
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;
|
||||||
|
|
||||||
@@ -46,13 +45,19 @@ int i3c_device_do_priv_xfers(struct i3c_device *dev,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = i3c_bus_rpm_get(dev->bus);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
i3c_bus_rpm_put(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
|
||||||
@@ -66,10 +71,16 @@ int i3c_device_do_setdasa(struct i3c_device *dev)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ret = i3c_bus_rpm_get(dev->bus);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
i3c_bus_normaluse_lock(dev->bus);
|
i3c_bus_normaluse_lock(dev->bus);
|
||||||
ret = i3c_dev_setdasa_locked(dev->desc);
|
ret = i3c_dev_setdasa_locked(dev->desc);
|
||||||
i3c_bus_normaluse_unlock(dev->bus);
|
i3c_bus_normaluse_unlock(dev->bus);
|
||||||
|
|
||||||
|
i3c_bus_rpm_put(dev->bus);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i3c_device_do_setdasa);
|
EXPORT_SYMBOL_GPL(i3c_device_do_setdasa);
|
||||||
@@ -106,16 +117,27 @@ EXPORT_SYMBOL_GPL(i3c_device_get_info);
|
|||||||
*/
|
*/
|
||||||
int i3c_device_disable_ibi(struct i3c_device *dev)
|
int i3c_device_disable_ibi(struct i3c_device *dev)
|
||||||
{
|
{
|
||||||
int ret = -ENOENT;
|
int ret;
|
||||||
|
|
||||||
|
if (i3c_bus_rpm_ibi_allowed(dev->bus)) {
|
||||||
|
ret = i3c_bus_rpm_get(dev->bus);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
i3c_bus_normaluse_lock(dev->bus);
|
i3c_bus_normaluse_lock(dev->bus);
|
||||||
if (dev->desc) {
|
if (dev->desc) {
|
||||||
mutex_lock(&dev->desc->ibi_lock);
|
mutex_lock(&dev->desc->ibi_lock);
|
||||||
ret = i3c_dev_disable_ibi_locked(dev->desc);
|
ret = i3c_dev_disable_ibi_locked(dev->desc);
|
||||||
mutex_unlock(&dev->desc->ibi_lock);
|
mutex_unlock(&dev->desc->ibi_lock);
|
||||||
|
} else {
|
||||||
|
ret = -ENOENT;
|
||||||
}
|
}
|
||||||
i3c_bus_normaluse_unlock(dev->bus);
|
i3c_bus_normaluse_unlock(dev->bus);
|
||||||
|
|
||||||
|
if (!ret || i3c_bus_rpm_ibi_allowed(dev->bus))
|
||||||
|
i3c_bus_rpm_put(dev->bus);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i3c_device_disable_ibi);
|
EXPORT_SYMBOL_GPL(i3c_device_disable_ibi);
|
||||||
@@ -135,16 +157,25 @@ EXPORT_SYMBOL_GPL(i3c_device_disable_ibi);
|
|||||||
*/
|
*/
|
||||||
int i3c_device_enable_ibi(struct i3c_device *dev)
|
int i3c_device_enable_ibi(struct i3c_device *dev)
|
||||||
{
|
{
|
||||||
int ret = -ENOENT;
|
int ret;
|
||||||
|
|
||||||
|
ret = i3c_bus_rpm_get(dev->bus);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
i3c_bus_normaluse_lock(dev->bus);
|
i3c_bus_normaluse_lock(dev->bus);
|
||||||
if (dev->desc) {
|
if (dev->desc) {
|
||||||
mutex_lock(&dev->desc->ibi_lock);
|
mutex_lock(&dev->desc->ibi_lock);
|
||||||
ret = i3c_dev_enable_ibi_locked(dev->desc);
|
ret = i3c_dev_enable_ibi_locked(dev->desc);
|
||||||
mutex_unlock(&dev->desc->ibi_lock);
|
mutex_unlock(&dev->desc->ibi_lock);
|
||||||
|
} else {
|
||||||
|
ret = -ENOENT;
|
||||||
}
|
}
|
||||||
i3c_bus_normaluse_unlock(dev->bus);
|
i3c_bus_normaluse_unlock(dev->bus);
|
||||||
|
|
||||||
|
if (ret || i3c_bus_rpm_ibi_allowed(dev->bus))
|
||||||
|
i3c_bus_rpm_put(dev->bus);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i3c_device_enable_ibi);
|
EXPORT_SYMBOL_GPL(i3c_device_enable_ibi);
|
||||||
@@ -163,19 +194,27 @@ EXPORT_SYMBOL_GPL(i3c_device_enable_ibi);
|
|||||||
int i3c_device_request_ibi(struct i3c_device *dev,
|
int i3c_device_request_ibi(struct i3c_device *dev,
|
||||||
const struct i3c_ibi_setup *req)
|
const struct i3c_ibi_setup *req)
|
||||||
{
|
{
|
||||||
int ret = -ENOENT;
|
int ret;
|
||||||
|
|
||||||
if (!req->handler || !req->num_slots)
|
if (!req->handler || !req->num_slots)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = i3c_bus_rpm_get(dev->bus);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
i3c_bus_normaluse_lock(dev->bus);
|
i3c_bus_normaluse_lock(dev->bus);
|
||||||
if (dev->desc) {
|
if (dev->desc) {
|
||||||
mutex_lock(&dev->desc->ibi_lock);
|
mutex_lock(&dev->desc->ibi_lock);
|
||||||
ret = i3c_dev_request_ibi_locked(dev->desc, req);
|
ret = i3c_dev_request_ibi_locked(dev->desc, req);
|
||||||
mutex_unlock(&dev->desc->ibi_lock);
|
mutex_unlock(&dev->desc->ibi_lock);
|
||||||
|
} else {
|
||||||
|
ret = -ENOENT;
|
||||||
}
|
}
|
||||||
i3c_bus_normaluse_unlock(dev->bus);
|
i3c_bus_normaluse_unlock(dev->bus);
|
||||||
|
|
||||||
|
i3c_bus_rpm_put(dev->bus);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i3c_device_request_ibi);
|
EXPORT_SYMBOL_GPL(i3c_device_request_ibi);
|
||||||
@@ -259,6 +298,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
|
||||||
*
|
*
|
||||||
|
|||||||
+53
-3
@@ -9,17 +9,67 @@
|
|||||||
#define I3C_INTERNALS_H
|
#define I3C_INTERNALS_H
|
||||||
|
|
||||||
#include <linux/i3c/master.h>
|
#include <linux/i3c/master.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
|
||||||
|
int __must_check i3c_bus_rpm_get(struct i3c_bus *bus);
|
||||||
|
void i3c_bus_rpm_put(struct i3c_bus *bus);
|
||||||
|
bool i3c_bus_rpm_ibi_allowed(struct i3c_bus *bus);
|
||||||
|
|
||||||
void i3c_bus_normaluse_lock(struct i3c_bus *bus);
|
void i3c_bus_normaluse_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,
|
||||||
const struct i3c_ibi_setup *req);
|
const struct i3c_ibi_setup *req);
|
||||||
void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev);
|
void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i3c_writel_fifo - Write data buffer to 32bit FIFO
|
||||||
|
* @addr: FIFO Address to write to
|
||||||
|
* @buf: Pointer to the data bytes to write
|
||||||
|
* @nbytes: Number of bytes to write
|
||||||
|
*/
|
||||||
|
static inline void i3c_writel_fifo(void __iomem *addr, const void *buf,
|
||||||
|
int nbytes)
|
||||||
|
{
|
||||||
|
writesl(addr, buf, nbytes / 4);
|
||||||
|
if (nbytes & 3) {
|
||||||
|
u32 tmp = 0;
|
||||||
|
|
||||||
|
memcpy(&tmp, buf + (nbytes & ~3), nbytes & 3);
|
||||||
|
/*
|
||||||
|
* writesl() instead of writel() to keep FIFO
|
||||||
|
* byteorder on big-endian targets
|
||||||
|
*/
|
||||||
|
writesl(addr, &tmp, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i3c_readl_fifo - Read data buffer from 32bit FIFO
|
||||||
|
* @addr: FIFO Address to read from
|
||||||
|
* @buf: Pointer to the buffer to store read bytes
|
||||||
|
* @nbytes: Number of bytes to read
|
||||||
|
*/
|
||||||
|
static inline void i3c_readl_fifo(const void __iomem *addr, void *buf,
|
||||||
|
int nbytes)
|
||||||
|
{
|
||||||
|
readsl(addr, buf, nbytes / 4);
|
||||||
|
if (nbytes & 3) {
|
||||||
|
u32 tmp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* readsl() instead of readl() to keep FIFO
|
||||||
|
* byteorder on big-endian targets
|
||||||
|
*/
|
||||||
|
readsl(addr, &tmp, 1);
|
||||||
|
memcpy(buf + (nbytes & ~3), &tmp, nbytes & 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* I3C_INTERNAL_H */
|
#endif /* I3C_INTERNAL_H */
|
||||||
|
|||||||
+825
-151
File diff suppressed because it is too large
Load Diff
+176
-128
@@ -5,7 +5,9 @@
|
|||||||
* Author: Vitor Soares <vitor.soares@synopsys.com>
|
* Author: Vitor Soares <vitor.soares@synopsys.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/bitfield.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/cleanup.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
@@ -23,6 +25,7 @@
|
|||||||
#include <linux/reset.h>
|
#include <linux/reset.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#include "../internals.h"
|
||||||
#include "dw-i3c-master.h"
|
#include "dw-i3c-master.h"
|
||||||
|
|
||||||
#define DEVICE_CTRL 0x0
|
#define DEVICE_CTRL 0x0
|
||||||
@@ -203,12 +206,23 @@
|
|||||||
#define EXTENDED_CAPABILITY 0xe8
|
#define EXTENDED_CAPABILITY 0xe8
|
||||||
#define SLAVE_CONFIG 0xec
|
#define SLAVE_CONFIG 0xec
|
||||||
|
|
||||||
|
#define DYN_ADDR_LO_MASK GENMASK(4, 0)
|
||||||
|
#define DYN_ADDR_HI_MASK GENMASK(6, 5)
|
||||||
|
#define IBI_SIR_BIT_MOD 32 /* 32-bit vector */
|
||||||
|
|
||||||
|
#define DW_I3C_DEV_NACK_RETRY_CNT_MAX 0x3
|
||||||
|
#define DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK GENMASK(30, 29)
|
||||||
|
#define DEV_ADDR_TABLE_DYNAMIC_MASK GENMASK(23, 16)
|
||||||
|
#define DEV_ADDR_TABLE_STATIC_MASK GENMASK(6, 0)
|
||||||
#define DEV_ADDR_TABLE_IBI_MDB BIT(12)
|
#define DEV_ADDR_TABLE_IBI_MDB BIT(12)
|
||||||
#define DEV_ADDR_TABLE_SIR_REJECT BIT(13)
|
#define DEV_ADDR_TABLE_SIR_REJECT BIT(13)
|
||||||
|
#define DEV_ADDR_TABLE_DEV_NACK_RETRY_CNT(x) \
|
||||||
|
FIELD_PREP(DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK, (x))
|
||||||
#define DEV_ADDR_TABLE_LEGACY_I2C_DEV BIT(31)
|
#define DEV_ADDR_TABLE_LEGACY_I2C_DEV BIT(31)
|
||||||
#define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) (((x) << 16) & GENMASK(23, 16))
|
#define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) FIELD_PREP(DEV_ADDR_TABLE_DYNAMIC_MASK, x)
|
||||||
#define DEV_ADDR_TABLE_STATIC_ADDR(x) ((x) & GENMASK(6, 0))
|
#define DEV_ADDR_TABLE_STATIC_ADDR(x) FIELD_PREP(DEV_ADDR_TABLE_STATIC_MASK, x)
|
||||||
#define DEV_ADDR_TABLE_LOC(start, idx) ((start) + ((idx) << 2))
|
#define DEV_ADDR_TABLE_LOC(start, idx) ((start) + ((idx) << 2))
|
||||||
|
#define DEV_ADDR_TABLE_GET_DYNAMIC_ADDR(x) FIELD_GET(DEV_ADDR_TABLE_DYNAMIC_MASK, x)
|
||||||
|
|
||||||
#define I3C_BUS_SDR1_SCL_RATE 8000000
|
#define I3C_BUS_SDR1_SCL_RATE 8000000
|
||||||
#define I3C_BUS_SDR2_SCL_RATE 6000000
|
#define I3C_BUS_SDR2_SCL_RATE 6000000
|
||||||
@@ -228,6 +242,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)
|
#define DW_I3C_DISABLE_RUNTIME_PM_QUIRK BIT(2)
|
||||||
|
#define DW_I3C_ACPI_SKIP_CLK_RST BIT(3)
|
||||||
|
|
||||||
struct dw_i3c_cmd {
|
struct dw_i3c_cmd {
|
||||||
u32 cmd_lo;
|
u32 cmd_lo;
|
||||||
@@ -264,6 +279,14 @@ static u8 even_parity(u8 p)
|
|||||||
return (0x9669 >> p) & 1;
|
return (0x9669 >> p) & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline u32 get_ibi_sir_bit_index(u8 addr)
|
||||||
|
{
|
||||||
|
u32 lo = FIELD_GET(DYN_ADDR_LO_MASK, addr);
|
||||||
|
u32 hi = FIELD_GET(DYN_ADDR_HI_MASK, addr);
|
||||||
|
|
||||||
|
return (lo + hi) % IBI_SIR_BIT_MOD;
|
||||||
|
}
|
||||||
|
|
||||||
static bool dw_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m,
|
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)
|
||||||
{
|
{
|
||||||
@@ -295,6 +318,8 @@ static bool dw_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m,
|
|||||||
case I3C_CCC_GETSTATUS:
|
case I3C_CCC_GETSTATUS:
|
||||||
case I3C_CCC_GETMXDS:
|
case I3C_CCC_GETMXDS:
|
||||||
case I3C_CCC_GETHDRCAP:
|
case I3C_CCC_GETHDRCAP:
|
||||||
|
case I3C_CCC_SETAASA:
|
||||||
|
case I3C_CCC_VENDOR(0, true): /* SETHID */
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
@@ -349,37 +374,19 @@ static int dw_i3c_master_get_free_pos(struct dw_i3c_master *master)
|
|||||||
static void dw_i3c_master_wr_tx_fifo(struct dw_i3c_master *master,
|
static void dw_i3c_master_wr_tx_fifo(struct dw_i3c_master *master,
|
||||||
const u8 *bytes, int nbytes)
|
const u8 *bytes, int nbytes)
|
||||||
{
|
{
|
||||||
writesl(master->regs + RX_TX_DATA_PORT, bytes, nbytes / 4);
|
i3c_writel_fifo(master->regs + RX_TX_DATA_PORT, bytes, nbytes);
|
||||||
if (nbytes & 3) {
|
|
||||||
u32 tmp = 0;
|
|
||||||
|
|
||||||
memcpy(&tmp, bytes + (nbytes & ~3), nbytes & 3);
|
|
||||||
writesl(master->regs + RX_TX_DATA_PORT, &tmp, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dw_i3c_master_read_fifo(struct dw_i3c_master *master,
|
|
||||||
int reg, u8 *bytes, int nbytes)
|
|
||||||
{
|
|
||||||
readsl(master->regs + reg, bytes, nbytes / 4);
|
|
||||||
if (nbytes & 3) {
|
|
||||||
u32 tmp;
|
|
||||||
|
|
||||||
readsl(master->regs + reg, &tmp, 1);
|
|
||||||
memcpy(bytes + (nbytes & ~3), &tmp, nbytes & 3);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dw_i3c_master_read_rx_fifo(struct dw_i3c_master *master,
|
static void dw_i3c_master_read_rx_fifo(struct dw_i3c_master *master,
|
||||||
u8 *bytes, int nbytes)
|
u8 *bytes, int nbytes)
|
||||||
{
|
{
|
||||||
return dw_i3c_master_read_fifo(master, RX_TX_DATA_PORT, bytes, nbytes);
|
i3c_readl_fifo(master->regs + RX_TX_DATA_PORT, bytes, nbytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dw_i3c_master_read_ibi_fifo(struct dw_i3c_master *master,
|
static void dw_i3c_master_read_ibi_fifo(struct dw_i3c_master *master,
|
||||||
u8 *bytes, int nbytes)
|
u8 *bytes, int nbytes)
|
||||||
{
|
{
|
||||||
return dw_i3c_master_read_fifo(master, IBI_QUEUE_STATUS, bytes, nbytes);
|
i3c_readl_fifo(master->regs + IBI_QUEUE_STATUS, bytes, nbytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dw_i3c_xfer *
|
static struct dw_i3c_xfer *
|
||||||
@@ -387,7 +394,7 @@ dw_i3c_master_alloc_xfer(struct dw_i3c_master *master, unsigned int ncmds)
|
|||||||
{
|
{
|
||||||
struct dw_i3c_xfer *xfer;
|
struct dw_i3c_xfer *xfer;
|
||||||
|
|
||||||
xfer = kzalloc(struct_size(xfer, cmds, ncmds), GFP_KERNEL);
|
xfer = kzalloc_flex(*xfer, cmds, ncmds, GFP_KERNEL);
|
||||||
if (!xfer)
|
if (!xfer)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -398,11 +405,6 @@ dw_i3c_master_alloc_xfer(struct dw_i3c_master *master, unsigned int ncmds)
|
|||||||
return xfer;
|
return xfer;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dw_i3c_master_free_xfer(struct dw_i3c_xfer *xfer)
|
|
||||||
{
|
|
||||||
kfree(xfer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dw_i3c_master_start_xfer_locked(struct dw_i3c_master *master)
|
static void dw_i3c_master_start_xfer_locked(struct dw_i3c_master *master)
|
||||||
{
|
{
|
||||||
struct dw_i3c_xfer *xfer = master->xferqueue.cur;
|
struct dw_i3c_xfer *xfer = master->xferqueue.cur;
|
||||||
@@ -434,17 +436,14 @@ static void dw_i3c_master_start_xfer_locked(struct dw_i3c_master *master)
|
|||||||
static void dw_i3c_master_enqueue_xfer(struct dw_i3c_master *master,
|
static void dw_i3c_master_enqueue_xfer(struct dw_i3c_master *master,
|
||||||
struct dw_i3c_xfer *xfer)
|
struct dw_i3c_xfer *xfer)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
init_completion(&xfer->comp);
|
init_completion(&xfer->comp);
|
||||||
spin_lock_irqsave(&master->xferqueue.lock, flags);
|
guard(spinlock_irqsave)(&master->xferqueue.lock);
|
||||||
if (master->xferqueue.cur) {
|
if (master->xferqueue.cur) {
|
||||||
list_add_tail(&xfer->node, &master->xferqueue.list);
|
list_add_tail(&xfer->node, &master->xferqueue.list);
|
||||||
} else {
|
} else {
|
||||||
master->xferqueue.cur = xfer;
|
master->xferqueue.cur = xfer;
|
||||||
dw_i3c_master_start_xfer_locked(master);
|
dw_i3c_master_start_xfer_locked(master);
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&master->xferqueue.lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dw_i3c_master_dequeue_xfer_locked(struct dw_i3c_master *master,
|
static void dw_i3c_master_dequeue_xfer_locked(struct dw_i3c_master *master,
|
||||||
@@ -469,11 +468,8 @@ static void dw_i3c_master_dequeue_xfer_locked(struct dw_i3c_master *master,
|
|||||||
static void dw_i3c_master_dequeue_xfer(struct dw_i3c_master *master,
|
static void dw_i3c_master_dequeue_xfer(struct dw_i3c_master *master,
|
||||||
struct dw_i3c_xfer *xfer)
|
struct dw_i3c_xfer *xfer)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
guard(spinlock_irqsave)(&master->xferqueue.lock);
|
||||||
|
|
||||||
spin_lock_irqsave(&master->xferqueue.lock, flags);
|
|
||||||
dw_i3c_master_dequeue_xfer_locked(master, xfer);
|
dw_i3c_master_dequeue_xfer_locked(master, xfer);
|
||||||
spin_unlock_irqrestore(&master->xferqueue.lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dw_i3c_master_end_xfer_locked(struct dw_i3c_master *master, u32 isr)
|
static void dw_i3c_master_end_xfer_locked(struct dw_i3c_master *master, u32 isr)
|
||||||
@@ -569,13 +565,28 @@ static void dw_i3c_master_set_intr_regs(struct dw_i3c_master *master)
|
|||||||
writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT);
|
writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned long dw_i3c_master_get_core_rate(struct dw_i3c_master *master)
|
||||||
|
{
|
||||||
|
unsigned int core_rate_prop;
|
||||||
|
|
||||||
|
if (master->core_clk)
|
||||||
|
return clk_get_rate(master->core_clk);
|
||||||
|
|
||||||
|
if (device_property_read_u32(master->dev, "clock-frequency", &core_rate_prop)) {
|
||||||
|
dev_err(master->dev, "missing clock-frequency property\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return core_rate_prop;
|
||||||
|
}
|
||||||
|
|
||||||
static int dw_i3c_clk_cfg(struct dw_i3c_master *master)
|
static int dw_i3c_clk_cfg(struct dw_i3c_master *master)
|
||||||
{
|
{
|
||||||
unsigned long core_rate, core_period;
|
unsigned long core_rate, core_period;
|
||||||
u32 scl_timing;
|
u32 scl_timing;
|
||||||
u8 hcnt, lcnt;
|
u8 hcnt, lcnt;
|
||||||
|
|
||||||
core_rate = clk_get_rate(master->core_clk);
|
core_rate = dw_i3c_master_get_core_rate(master);
|
||||||
if (!core_rate)
|
if (!core_rate)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@@ -628,21 +639,21 @@ static int dw_i2c_clk_cfg(struct dw_i3c_master *master)
|
|||||||
u16 hcnt, lcnt;
|
u16 hcnt, lcnt;
|
||||||
u32 scl_timing;
|
u32 scl_timing;
|
||||||
|
|
||||||
core_rate = clk_get_rate(master->core_clk);
|
core_rate = dw_i3c_master_get_core_rate(master);
|
||||||
if (!core_rate)
|
if (!core_rate)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
core_period = DIV_ROUND_UP(1000000000, core_rate);
|
core_period = DIV_ROUND_UP(1000000000, core_rate);
|
||||||
|
|
||||||
lcnt = DIV_ROUND_UP(I3C_BUS_I2C_FMP_TLOW_MIN_NS, core_period);
|
lcnt = DIV_ROUND_UP(I3C_BUS_I2C_FMP_TLOW_MIN_NS, core_period);
|
||||||
hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_PLUS_SCL_RATE) - lcnt;
|
hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_PLUS_SCL_MAX_RATE) - lcnt;
|
||||||
scl_timing = SCL_I2C_FMP_TIMING_HCNT(hcnt) |
|
scl_timing = SCL_I2C_FMP_TIMING_HCNT(hcnt) |
|
||||||
SCL_I2C_FMP_TIMING_LCNT(lcnt);
|
SCL_I2C_FMP_TIMING_LCNT(lcnt);
|
||||||
writel(scl_timing, master->regs + SCL_I2C_FMP_TIMING);
|
writel(scl_timing, master->regs + SCL_I2C_FMP_TIMING);
|
||||||
master->i2c_fmp_timing = scl_timing;
|
master->i2c_fmp_timing = scl_timing;
|
||||||
|
|
||||||
lcnt = DIV_ROUND_UP(I3C_BUS_I2C_FM_TLOW_MIN_NS, core_period);
|
lcnt = DIV_ROUND_UP(I3C_BUS_I2C_FM_TLOW_MIN_NS, core_period);
|
||||||
hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_SCL_RATE) - lcnt;
|
hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_SCL_MAX_RATE) - lcnt;
|
||||||
scl_timing = SCL_I2C_FM_TIMING_HCNT(hcnt) |
|
scl_timing = SCL_I2C_FM_TIMING_HCNT(hcnt) |
|
||||||
SCL_I2C_FM_TIMING_LCNT(lcnt);
|
SCL_I2C_FM_TIMING_LCNT(lcnt);
|
||||||
writel(scl_timing, master->regs + SCL_I2C_FM_TIMING);
|
writel(scl_timing, master->regs + SCL_I2C_FM_TIMING);
|
||||||
@@ -712,7 +723,6 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
|
|||||||
dw_i3c_master_enable(master);
|
dw_i3c_master_enable(master);
|
||||||
|
|
||||||
rpm_out:
|
rpm_out:
|
||||||
pm_runtime_mark_last_busy(master->dev);
|
|
||||||
pm_runtime_put_autosuspend(master->dev);
|
pm_runtime_put_autosuspend(master->dev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -727,7 +737,6 @@ static void dw_i3c_master_bus_cleanup(struct i3c_master_controller *m)
|
|||||||
static int dw_i3c_ccc_set(struct dw_i3c_master *master,
|
static int dw_i3c_ccc_set(struct dw_i3c_master *master,
|
||||||
struct i3c_ccc_cmd *ccc)
|
struct i3c_ccc_cmd *ccc)
|
||||||
{
|
{
|
||||||
struct dw_i3c_xfer *xfer;
|
|
||||||
struct dw_i3c_cmd *cmd;
|
struct dw_i3c_cmd *cmd;
|
||||||
int ret, pos = 0;
|
int ret, pos = 0;
|
||||||
|
|
||||||
@@ -737,7 +746,7 @@ static int dw_i3c_ccc_set(struct dw_i3c_master *master,
|
|||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
xfer = dw_i3c_master_alloc_xfer(master, 1);
|
struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, 1);
|
||||||
if (!xfer)
|
if (!xfer)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@@ -762,14 +771,11 @@ static int dw_i3c_ccc_set(struct dw_i3c_master *master,
|
|||||||
if (xfer->cmds[0].error == RESPONSE_ERROR_IBA_NACK)
|
if (xfer->cmds[0].error == RESPONSE_ERROR_IBA_NACK)
|
||||||
ccc->err = I3C_ERROR_M2;
|
ccc->err = I3C_ERROR_M2;
|
||||||
|
|
||||||
dw_i3c_master_free_xfer(xfer);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc)
|
static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc)
|
||||||
{
|
{
|
||||||
struct dw_i3c_xfer *xfer;
|
|
||||||
struct dw_i3c_cmd *cmd;
|
struct dw_i3c_cmd *cmd;
|
||||||
int ret, pos;
|
int ret, pos;
|
||||||
|
|
||||||
@@ -777,7 +783,7 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc)
|
|||||||
if (pos < 0)
|
if (pos < 0)
|
||||||
return pos;
|
return pos;
|
||||||
|
|
||||||
xfer = dw_i3c_master_alloc_xfer(master, 1);
|
struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, 1);
|
||||||
if (!xfer)
|
if (!xfer)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@@ -802,7 +808,6 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc)
|
|||||||
ret = xfer->ret;
|
ret = xfer->ret;
|
||||||
if (xfer->cmds[0].error == RESPONSE_ERROR_IBA_NACK)
|
if (xfer->cmds[0].error == RESPONSE_ERROR_IBA_NACK)
|
||||||
ccc->err = I3C_ERROR_M2;
|
ccc->err = I3C_ERROR_M2;
|
||||||
dw_i3c_master_free_xfer(xfer);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -842,7 +847,6 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
|
|||||||
else
|
else
|
||||||
ret = dw_i3c_ccc_set(master, ccc);
|
ret = dw_i3c_ccc_set(master, ccc);
|
||||||
|
|
||||||
pm_runtime_mark_last_busy(master->dev);
|
|
||||||
pm_runtime_put_autosuspend(master->dev);
|
pm_runtime_put_autosuspend(master->dev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -850,12 +854,15 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
|
|||||||
static int dw_i3c_master_daa(struct i3c_master_controller *m)
|
static int dw_i3c_master_daa(struct i3c_master_controller *m)
|
||||||
{
|
{
|
||||||
struct dw_i3c_master *master = to_dw_i3c_master(m);
|
struct dw_i3c_master *master = to_dw_i3c_master(m);
|
||||||
struct dw_i3c_xfer *xfer;
|
|
||||||
struct dw_i3c_cmd *cmd;
|
struct dw_i3c_cmd *cmd;
|
||||||
u32 olddevs, newdevs;
|
u32 olddevs, newdevs;
|
||||||
u8 p, last_addr = 0;
|
u8 p, last_addr = 0;
|
||||||
int ret, pos;
|
int ret, pos;
|
||||||
|
|
||||||
|
struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, 1);
|
||||||
|
if (!xfer)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = pm_runtime_resume_and_get(master->dev);
|
ret = pm_runtime_resume_and_get(master->dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(master->dev,
|
dev_err(master->dev,
|
||||||
@@ -889,15 +896,8 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
|
|||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
xfer = dw_i3c_master_alloc_xfer(master, 1);
|
|
||||||
if (!xfer) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto rpm_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = dw_i3c_master_get_free_pos(master);
|
pos = dw_i3c_master_get_free_pos(master);
|
||||||
if (pos < 0) {
|
if (pos < 0) {
|
||||||
dw_i3c_master_free_xfer(xfer);
|
|
||||||
ret = pos;
|
ret = pos;
|
||||||
goto rpm_out;
|
goto rpm_out;
|
||||||
}
|
}
|
||||||
@@ -922,30 +922,26 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
|
|||||||
i3c_master_add_i3c_dev_locked(m, master->devs[pos].addr);
|
i3c_master_add_i3c_dev_locked(m, master->devs[pos].addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
dw_i3c_master_free_xfer(xfer);
|
|
||||||
|
|
||||||
rpm_out:
|
rpm_out:
|
||||||
pm_runtime_mark_last_busy(master->dev);
|
|
||||||
pm_runtime_put_autosuspend(master->dev);
|
pm_runtime_put_autosuspend(master->dev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
|
static int dw_i3c_master_i3c_xfers(struct i3c_dev_desc *dev,
|
||||||
struct i3c_priv_xfer *i3c_xfers,
|
struct i3c_xfer *i3c_xfers,
|
||||||
int i3c_nxfers)
|
int i3c_nxfers, enum i3c_xfer_mode mode)
|
||||||
{
|
{
|
||||||
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
|
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
|
||||||
struct i3c_master_controller *m = i3c_dev_get_master(dev);
|
struct i3c_master_controller *m = i3c_dev_get_master(dev);
|
||||||
struct dw_i3c_master *master = to_dw_i3c_master(m);
|
struct dw_i3c_master *master = to_dw_i3c_master(m);
|
||||||
unsigned int nrxwords = 0, ntxwords = 0;
|
unsigned int nrxwords = 0, ntxwords = 0;
|
||||||
struct dw_i3c_xfer *xfer;
|
|
||||||
int i, ret = 0;
|
int i, ret = 0;
|
||||||
|
|
||||||
if (!i3c_nxfers)
|
if (!i3c_nxfers)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (i3c_nxfers > master->caps.cmdfifodepth)
|
if (i3c_nxfers > master->caps.cmdfifodepth)
|
||||||
return -ENOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
for (i = 0; i < i3c_nxfers; i++) {
|
for (i = 0; i < i3c_nxfers; i++) {
|
||||||
if (i3c_xfers[i].rnw)
|
if (i3c_xfers[i].rnw)
|
||||||
@@ -956,9 +952,9 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
|
|||||||
|
|
||||||
if (ntxwords > master->caps.datafifodepth ||
|
if (ntxwords > master->caps.datafifodepth ||
|
||||||
nrxwords > master->caps.datafifodepth)
|
nrxwords > master->caps.datafifodepth)
|
||||||
return -ENOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
xfer = dw_i3c_master_alloc_xfer(master, i3c_nxfers);
|
struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, i3c_nxfers);
|
||||||
if (!xfer)
|
if (!xfer)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@@ -1009,9 +1005,7 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = xfer->ret;
|
ret = xfer->ret;
|
||||||
dw_i3c_master_free_xfer(xfer);
|
|
||||||
|
|
||||||
pm_runtime_mark_last_busy(master->dev);
|
|
||||||
pm_runtime_put_autosuspend(master->dev);
|
pm_runtime_put_autosuspend(master->dev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -1039,7 +1033,7 @@ static int dw_i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
|
|||||||
master->free_pos &= ~BIT(pos);
|
master->free_pos &= ~BIT(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
writel(DEV_ADDR_TABLE_DYNAMIC_ADDR(dev->info.dyn_addr),
|
writel(DEV_ADDR_TABLE_DYNAMIC_ADDR(dev->info.dyn_addr) | DEV_ADDR_TABLE_SIR_REJECT,
|
||||||
master->regs +
|
master->regs +
|
||||||
DEV_ADDR_TABLE_LOC(master->datstartaddr, data->index));
|
DEV_ADDR_TABLE_LOC(master->datstartaddr, data->index));
|
||||||
|
|
||||||
@@ -1059,7 +1053,7 @@ static int dw_i3c_master_attach_i3c_dev(struct i3c_dev_desc *dev)
|
|||||||
if (pos < 0)
|
if (pos < 0)
|
||||||
return pos;
|
return pos;
|
||||||
|
|
||||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
data = kzalloc_obj(*data);
|
||||||
if (!data)
|
if (!data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@@ -1068,7 +1062,7 @@ static int dw_i3c_master_attach_i3c_dev(struct i3c_dev_desc *dev)
|
|||||||
master->free_pos &= ~BIT(pos);
|
master->free_pos &= ~BIT(pos);
|
||||||
i3c_dev_set_master_data(dev, data);
|
i3c_dev_set_master_data(dev, data);
|
||||||
|
|
||||||
writel(DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr),
|
writel(DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr) | DEV_ADDR_TABLE_SIR_REJECT,
|
||||||
master->regs +
|
master->regs +
|
||||||
DEV_ADDR_TABLE_LOC(master->datstartaddr, data->index));
|
DEV_ADDR_TABLE_LOC(master->datstartaddr, data->index));
|
||||||
|
|
||||||
@@ -1092,21 +1086,20 @@ static void dw_i3c_master_detach_i3c_dev(struct i3c_dev_desc *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
|
static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
|
||||||
const struct i2c_msg *i2c_xfers,
|
struct i2c_msg *i2c_xfers,
|
||||||
int i2c_nxfers)
|
int i2c_nxfers)
|
||||||
{
|
{
|
||||||
struct dw_i3c_i2c_dev_data *data = i2c_dev_get_master_data(dev);
|
struct dw_i3c_i2c_dev_data *data = i2c_dev_get_master_data(dev);
|
||||||
struct i3c_master_controller *m = i2c_dev_get_master(dev);
|
struct i3c_master_controller *m = i2c_dev_get_master(dev);
|
||||||
struct dw_i3c_master *master = to_dw_i3c_master(m);
|
struct dw_i3c_master *master = to_dw_i3c_master(m);
|
||||||
unsigned int nrxwords = 0, ntxwords = 0;
|
unsigned int nrxwords = 0, ntxwords = 0;
|
||||||
struct dw_i3c_xfer *xfer;
|
|
||||||
int i, ret = 0;
|
int i, ret = 0;
|
||||||
|
|
||||||
if (!i2c_nxfers)
|
if (!i2c_nxfers)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (i2c_nxfers > master->caps.cmdfifodepth)
|
if (i2c_nxfers > master->caps.cmdfifodepth)
|
||||||
return -ENOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
for (i = 0; i < i2c_nxfers; i++) {
|
for (i = 0; i < i2c_nxfers; i++) {
|
||||||
if (i2c_xfers[i].flags & I2C_M_RD)
|
if (i2c_xfers[i].flags & I2C_M_RD)
|
||||||
@@ -1117,9 +1110,9 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
|
|||||||
|
|
||||||
if (ntxwords > master->caps.datafifodepth ||
|
if (ntxwords > master->caps.datafifodepth ||
|
||||||
nrxwords > master->caps.datafifodepth)
|
nrxwords > master->caps.datafifodepth)
|
||||||
return -ENOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
xfer = dw_i3c_master_alloc_xfer(master, i2c_nxfers);
|
struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, i2c_nxfers);
|
||||||
if (!xfer)
|
if (!xfer)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@@ -1155,13 +1148,11 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
dw_i3c_master_enqueue_xfer(master, xfer);
|
dw_i3c_master_enqueue_xfer(master, xfer);
|
||||||
if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT))
|
if (!wait_for_completion_timeout(&xfer->comp, m->i2c.timeout))
|
||||||
dw_i3c_master_dequeue_xfer(master, xfer);
|
dw_i3c_master_dequeue_xfer(master, xfer);
|
||||||
|
|
||||||
ret = xfer->ret;
|
ret = xfer->ret;
|
||||||
dw_i3c_master_free_xfer(xfer);
|
|
||||||
|
|
||||||
pm_runtime_mark_last_busy(master->dev);
|
|
||||||
pm_runtime_put_autosuspend(master->dev);
|
pm_runtime_put_autosuspend(master->dev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -1177,7 +1168,7 @@ static int dw_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev)
|
|||||||
if (pos < 0)
|
if (pos < 0)
|
||||||
return pos;
|
return pos;
|
||||||
|
|
||||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
data = kzalloc_obj(*data);
|
||||||
if (!data)
|
if (!data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@@ -1217,15 +1208,13 @@ static int dw_i3c_master_request_ibi(struct i3c_dev_desc *dev,
|
|||||||
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
|
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
|
||||||
struct i3c_master_controller *m = i3c_dev_get_master(dev);
|
struct i3c_master_controller *m = i3c_dev_get_master(dev);
|
||||||
struct dw_i3c_master *master = to_dw_i3c_master(m);
|
struct dw_i3c_master *master = to_dw_i3c_master(m);
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
data->ibi_pool = i3c_generic_ibi_alloc_pool(dev, req);
|
data->ibi_pool = i3c_generic_ibi_alloc_pool(dev, req);
|
||||||
if (IS_ERR(data->ibi_pool))
|
if (IS_ERR(data->ibi_pool))
|
||||||
return PTR_ERR(data->ibi_pool);
|
return PTR_ERR(data->ibi_pool);
|
||||||
|
|
||||||
spin_lock_irqsave(&master->devs_lock, flags);
|
guard(spinlock_irqsave)(&master->devs_lock);
|
||||||
master->devs[data->index].ibi_dev = dev;
|
master->devs[data->index].ibi_dev = dev;
|
||||||
spin_unlock_irqrestore(&master->devs_lock, flags);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1235,11 +1224,10 @@ static void dw_i3c_master_free_ibi(struct i3c_dev_desc *dev)
|
|||||||
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
|
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
|
||||||
struct i3c_master_controller *m = i3c_dev_get_master(dev);
|
struct i3c_master_controller *m = i3c_dev_get_master(dev);
|
||||||
struct dw_i3c_master *master = to_dw_i3c_master(m);
|
struct dw_i3c_master *master = to_dw_i3c_master(m);
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&master->devs_lock, flags);
|
scoped_guard(spinlock_irqsave, &master->devs_lock) {
|
||||||
master->devs[data->index].ibi_dev = NULL;
|
master->devs[data->index].ibi_dev = NULL;
|
||||||
spin_unlock_irqrestore(&master->devs_lock, flags);
|
}
|
||||||
|
|
||||||
i3c_generic_ibi_free_pool(data->ibi_pool);
|
i3c_generic_ibi_free_pool(data->ibi_pool);
|
||||||
data->ibi_pool = NULL;
|
data->ibi_pool = NULL;
|
||||||
@@ -1266,14 +1254,21 @@ static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master,
|
|||||||
struct i3c_dev_desc *dev,
|
struct i3c_dev_desc *dev,
|
||||||
u8 idx, bool enable)
|
u8 idx, bool enable)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
u32 dat_entry, reg;
|
u32 dat_entry, reg;
|
||||||
bool global;
|
bool global;
|
||||||
|
u8 dynamic_addr;
|
||||||
|
|
||||||
dat_entry = DEV_ADDR_TABLE_LOC(master->datstartaddr, idx);
|
dat_entry = DEV_ADDR_TABLE_LOC(master->datstartaddr, idx);
|
||||||
|
|
||||||
spin_lock_irqsave(&master->devs_lock, flags);
|
guard(spinlock_irqsave)(&master->devs_lock);
|
||||||
reg = readl(master->regs + dat_entry);
|
reg = readl(master->regs + dat_entry);
|
||||||
|
dynamic_addr = DEV_ADDR_TABLE_GET_DYNAMIC_ADDR(reg);
|
||||||
|
|
||||||
|
if (!dynamic_addr)
|
||||||
|
dev_warn(master->dev,
|
||||||
|
"<%s> unassigned slave device, dynamic addr:%x\n",
|
||||||
|
__func__, dynamic_addr);
|
||||||
|
|
||||||
if (enable) {
|
if (enable) {
|
||||||
reg &= ~DEV_ADDR_TABLE_SIR_REJECT;
|
reg &= ~DEV_ADDR_TABLE_SIR_REJECT;
|
||||||
if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
|
if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
|
||||||
@@ -1286,20 +1281,17 @@ static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master,
|
|||||||
|
|
||||||
if (enable) {
|
if (enable) {
|
||||||
global = (master->sir_rej_mask == IBI_REQ_REJECT_ALL);
|
global = (master->sir_rej_mask == IBI_REQ_REJECT_ALL);
|
||||||
master->sir_rej_mask &= ~BIT(idx);
|
master->sir_rej_mask &= ~BIT(get_ibi_sir_bit_index(dynamic_addr));
|
||||||
} else {
|
} else {
|
||||||
bool hj_rejected = !!(readl(master->regs + DEVICE_CTRL) & DEV_CTRL_HOT_JOIN_NACK);
|
bool hj_rejected = !!(readl(master->regs + DEVICE_CTRL) & DEV_CTRL_HOT_JOIN_NACK);
|
||||||
|
|
||||||
master->sir_rej_mask |= BIT(idx);
|
master->sir_rej_mask |= BIT(get_ibi_sir_bit_index(dynamic_addr));
|
||||||
global = (master->sir_rej_mask == IBI_REQ_REJECT_ALL) && hj_rejected;
|
global = (master->sir_rej_mask == IBI_REQ_REJECT_ALL) && hj_rejected;
|
||||||
}
|
}
|
||||||
writel(master->sir_rej_mask, master->regs + IBI_SIR_REQ_REJECT);
|
writel(master->sir_rej_mask, master->regs + IBI_SIR_REQ_REJECT);
|
||||||
|
|
||||||
if (global)
|
if (global)
|
||||||
dw_i3c_master_enable_sir_signal(master, enable);
|
dw_i3c_master_enable_sir_signal(master, enable);
|
||||||
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&master->devs_lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dw_i3c_master_enable_hotjoin(struct i3c_master_controller *m)
|
static int dw_i3c_master_enable_hotjoin(struct i3c_master_controller *m)
|
||||||
@@ -1329,7 +1321,6 @@ static int dw_i3c_master_disable_hotjoin(struct i3c_master_controller *m)
|
|||||||
writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
|
writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
|
||||||
master->regs + DEVICE_CTRL);
|
master->regs + DEVICE_CTRL);
|
||||||
|
|
||||||
pm_runtime_mark_last_busy(master->dev);
|
|
||||||
pm_runtime_put_autosuspend(master->dev);
|
pm_runtime_put_autosuspend(master->dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1355,7 +1346,6 @@ static int dw_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
|
|||||||
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dw_i3c_master_set_sir_enabled(master, dev, data->index, false);
|
dw_i3c_master_set_sir_enabled(master, dev, data->index, false);
|
||||||
pm_runtime_mark_last_busy(master->dev);
|
|
||||||
pm_runtime_put_autosuspend(master->dev);
|
pm_runtime_put_autosuspend(master->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1375,7 +1365,6 @@ static int dw_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
|
|||||||
|
|
||||||
dw_i3c_master_set_sir_enabled(master, dev, data->index, false);
|
dw_i3c_master_set_sir_enabled(master, dev, data->index, false);
|
||||||
|
|
||||||
pm_runtime_mark_last_busy(master->dev);
|
|
||||||
pm_runtime_put_autosuspend(master->dev);
|
pm_runtime_put_autosuspend(master->dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1403,7 +1392,6 @@ static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master,
|
|||||||
struct dw_i3c_i2c_dev_data *data;
|
struct dw_i3c_i2c_dev_data *data;
|
||||||
struct i3c_ibi_slot *slot;
|
struct i3c_ibi_slot *slot;
|
||||||
struct i3c_dev_desc *dev;
|
struct i3c_dev_desc *dev;
|
||||||
unsigned long flags;
|
|
||||||
u8 addr, len;
|
u8 addr, len;
|
||||||
int idx;
|
int idx;
|
||||||
|
|
||||||
@@ -1421,7 +1409,7 @@ static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master,
|
|||||||
* a new platform op to validate it.
|
* a new platform op to validate it.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
spin_lock_irqsave(&master->devs_lock, flags);
|
guard(spinlock_irqsave)(&master->devs_lock);
|
||||||
idx = dw_i3c_master_get_addr_pos(master, addr);
|
idx = dw_i3c_master_get_addr_pos(master, addr);
|
||||||
if (idx < 0) {
|
if (idx < 0) {
|
||||||
dev_dbg_ratelimited(&master->base.dev,
|
dev_dbg_ratelimited(&master->base.dev,
|
||||||
@@ -1457,14 +1445,10 @@ static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master,
|
|||||||
}
|
}
|
||||||
i3c_master_queue_ibi(dev, slot);
|
i3c_master_queue_ibi(dev, slot);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&master->devs_lock, flags);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
err_drain:
|
err_drain:
|
||||||
dw_i3c_master_drain_ibi_queue(master, len);
|
dw_i3c_master_drain_ibi_queue(master, len);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&master->devs_lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* "ibis": referring to In-Band Interrupts, and not
|
/* "ibis": referring to In-Band Interrupts, and not
|
||||||
@@ -1477,7 +1461,11 @@ static void dw_i3c_master_irq_handle_ibis(struct dw_i3c_master *master)
|
|||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
reg = readl(master->regs + QUEUE_STATUS_LEVEL);
|
reg = readl(master->regs + QUEUE_STATUS_LEVEL);
|
||||||
|
if (master->has_ibi_data)
|
||||||
n_ibis = QUEUE_STATUS_IBI_STATUS_CNT(reg);
|
n_ibis = QUEUE_STATUS_IBI_STATUS_CNT(reg);
|
||||||
|
else
|
||||||
|
n_ibis = QUEUE_STATUS_IBI_BUF_BLR(reg);
|
||||||
|
|
||||||
if (!n_ibis)
|
if (!n_ibis)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -1522,6 +1510,40 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id)
|
|||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dw_i3c_master_set_dev_nack_retry(struct i3c_master_controller *m,
|
||||||
|
unsigned int dev_nack_retry_cnt)
|
||||||
|
{
|
||||||
|
struct dw_i3c_master *master = to_dw_i3c_master(m);
|
||||||
|
u32 reg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (dev_nack_retry_cnt > DW_I3C_DEV_NACK_RETRY_CNT_MAX) {
|
||||||
|
dev_err(&master->base.dev,
|
||||||
|
"Value %u exceeds maximum %d\n",
|
||||||
|
dev_nack_retry_cnt, DW_I3C_DEV_NACK_RETRY_CNT_MAX);
|
||||||
|
return -ERANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update DAT entries for all currently attached devices.
|
||||||
|
* We directly iterate through the master's device array.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < master->maxdevs; i++) {
|
||||||
|
/* Skip free/empty slots */
|
||||||
|
if (master->free_pos & BIT(i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
reg = readl(master->regs +
|
||||||
|
DEV_ADDR_TABLE_LOC(master->datstartaddr, i));
|
||||||
|
reg &= ~DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK;
|
||||||
|
reg |= DEV_ADDR_TABLE_DEV_NACK_RETRY_CNT(dev_nack_retry_cnt);
|
||||||
|
writel(reg, master->regs +
|
||||||
|
DEV_ADDR_TABLE_LOC(master->datstartaddr, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
|
static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
|
||||||
.bus_init = dw_i3c_master_bus_init,
|
.bus_init = dw_i3c_master_bus_init,
|
||||||
.bus_cleanup = dw_i3c_master_bus_cleanup,
|
.bus_cleanup = dw_i3c_master_bus_cleanup,
|
||||||
@@ -1531,7 +1553,7 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
|
|||||||
.do_daa = dw_i3c_master_daa,
|
.do_daa = dw_i3c_master_daa,
|
||||||
.supports_ccc_cmd = dw_i3c_master_supports_ccc_cmd,
|
.supports_ccc_cmd = dw_i3c_master_supports_ccc_cmd,
|
||||||
.send_ccc_cmd = dw_i3c_master_send_ccc_cmd,
|
.send_ccc_cmd = dw_i3c_master_send_ccc_cmd,
|
||||||
.priv_xfers = dw_i3c_master_priv_xfers,
|
.i3c_xfers = dw_i3c_master_i3c_xfers,
|
||||||
.attach_i2c_dev = dw_i3c_master_attach_i2c_dev,
|
.attach_i2c_dev = dw_i3c_master_attach_i2c_dev,
|
||||||
.detach_i2c_dev = dw_i3c_master_detach_i2c_dev,
|
.detach_i2c_dev = dw_i3c_master_detach_i2c_dev,
|
||||||
.i2c_xfers = dw_i3c_master_i2c_xfers,
|
.i2c_xfers = dw_i3c_master_i2c_xfers,
|
||||||
@@ -1542,6 +1564,7 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
|
|||||||
.recycle_ibi_slot = dw_i3c_master_recycle_ibi_slot,
|
.recycle_ibi_slot = dw_i3c_master_recycle_ibi_slot,
|
||||||
.enable_hotjoin = dw_i3c_master_enable_hotjoin,
|
.enable_hotjoin = dw_i3c_master_enable_hotjoin,
|
||||||
.disable_hotjoin = dw_i3c_master_disable_hotjoin,
|
.disable_hotjoin = dw_i3c_master_disable_hotjoin,
|
||||||
|
.set_dev_nack_retry = dw_i3c_master_set_dev_nack_retry,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* default platform ops implementations */
|
/* default platform ops implementations */
|
||||||
@@ -1573,6 +1596,7 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
|
|||||||
struct platform_device *pdev)
|
struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret, irq;
|
int ret, irq;
|
||||||
|
u32 thld_ctrl;
|
||||||
const struct dw_i3c_drvdata *drvdata;
|
const struct dw_i3c_drvdata *drvdata;
|
||||||
unsigned long quirks = 0;
|
unsigned long quirks = 0;
|
||||||
|
|
||||||
@@ -1581,35 +1605,49 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
|
|||||||
|
|
||||||
master->dev = &pdev->dev;
|
master->dev = &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;
|
||||||
|
|
||||||
master->regs = devm_platform_ioremap_resource(pdev, 0);
|
master->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||||
if (IS_ERR(master->regs))
|
if (IS_ERR(master->regs))
|
||||||
return PTR_ERR(master->regs);
|
return PTR_ERR(master->regs);
|
||||||
|
|
||||||
master->core_clk = devm_clk_get_enabled(&pdev->dev, NULL);
|
master->core_clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
|
||||||
if (IS_ERR(master->core_clk))
|
if (IS_ERR(master->core_clk))
|
||||||
return PTR_ERR(master->core_clk);
|
return PTR_ERR(master->core_clk);
|
||||||
|
|
||||||
|
if (!master->core_clk && !(master->quirks & DW_I3C_ACPI_SKIP_CLK_RST)) {
|
||||||
|
dev_err(&pdev->dev, "missing core clock\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
master->pclk = devm_clk_get_optional_enabled(&pdev->dev, "pclk");
|
master->pclk = devm_clk_get_optional_enabled(&pdev->dev, "pclk");
|
||||||
if (IS_ERR(master->pclk))
|
if (IS_ERR(master->pclk))
|
||||||
return PTR_ERR(master->pclk);
|
return PTR_ERR(master->pclk);
|
||||||
|
|
||||||
master->core_rst = devm_reset_control_get_optional_exclusive(&pdev->dev,
|
master->core_rst = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev,
|
||||||
"core_rst");
|
"core_rst");
|
||||||
if (IS_ERR(master->core_rst))
|
if (IS_ERR(master->core_rst))
|
||||||
return PTR_ERR(master->core_rst);
|
return PTR_ERR(master->core_rst);
|
||||||
|
|
||||||
reset_control_deassert(master->core_rst);
|
|
||||||
|
|
||||||
spin_lock_init(&master->xferqueue.lock);
|
spin_lock_init(&master->xferqueue.lock);
|
||||||
INIT_LIST_HEAD(&master->xferqueue.list);
|
INIT_LIST_HEAD(&master->xferqueue.list);
|
||||||
|
|
||||||
|
spin_lock_init(&master->devs_lock);
|
||||||
|
|
||||||
writel(INTR_ALL, master->regs + INTR_STATUS);
|
writel(INTR_ALL, master->regs + INTR_STATUS);
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
ret = devm_request_irq(&pdev->dev, irq,
|
ret = devm_request_irq(&pdev->dev, irq,
|
||||||
dw_i3c_master_irq_handler, 0,
|
dw_i3c_master_irq_handler, 0,
|
||||||
dev_name(&pdev->dev), master);
|
dev_name(&pdev->dev), master);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_assert_rst;
|
return ret;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
|
|
||||||
@@ -1630,20 +1668,27 @@ 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);
|
||||||
|
|
||||||
if (has_acpi_companion(&pdev->dev)) {
|
/*
|
||||||
quirks = (unsigned long)device_get_match_data(&pdev->dev);
|
* Detect IBI data capability (IC_HAS_IBI_DATA): write a non-zero value
|
||||||
} else if (pdev->dev.of_node) {
|
* to IBI_DATA_THLD and read back. On controllers like Versalnet
|
||||||
drvdata = device_get_match_data(&pdev->dev);
|
* the field is hardwired to 0 and the write is ignored. Restore the
|
||||||
if (drvdata)
|
* original register value after detection.
|
||||||
quirks = drvdata->flags;
|
*/
|
||||||
}
|
thld_ctrl = readl(master->regs + QUEUE_THLD_CTRL);
|
||||||
master->quirks = quirks;
|
ret = thld_ctrl | QUEUE_THLD_CTRL_IBI_DATA(2);
|
||||||
|
writel(ret, master->regs + QUEUE_THLD_CTRL);
|
||||||
|
ret = readl(master->regs + QUEUE_THLD_CTRL);
|
||||||
|
if (ret & QUEUE_THLD_CTRL_IBI_DATA_MASK)
|
||||||
|
master->has_ibi_data = true;
|
||||||
|
writel(thld_ctrl, master->regs + QUEUE_THLD_CTRL);
|
||||||
|
|
||||||
/* Keep controller enabled by preventing runtime suspend */
|
/* Keep controller enabled by preventing runtime suspend */
|
||||||
if (master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)
|
if (master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)
|
||||||
pm_runtime_get_noresume(&pdev->dev);
|
pm_runtime_get_noresume(&pdev->dev);
|
||||||
|
|
||||||
INIT_WORK(&master->hj_work, dw_i3c_hj_work);
|
INIT_WORK(&master->hj_work, dw_i3c_hj_work);
|
||||||
|
|
||||||
|
device_set_of_node_from_dev(&master->base.i2c.dev, &pdev->dev);
|
||||||
ret = i3c_master_register(&master->base, &pdev->dev,
|
ret = i3c_master_register(&master->base, &pdev->dev,
|
||||||
&dw_mipi_i3c_ops, false);
|
&dw_mipi_i3c_ops, false);
|
||||||
if (ret)
|
if (ret)
|
||||||
@@ -1658,9 +1703,6 @@ err_disable_pm:
|
|||||||
pm_runtime_set_suspended(&pdev->dev);
|
pm_runtime_set_suspended(&pdev->dev);
|
||||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||||
|
|
||||||
err_assert_rst:
|
|
||||||
reset_control_assert(master->core_rst);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(dw_i3c_common_probe);
|
EXPORT_SYMBOL_GPL(dw_i3c_common_probe);
|
||||||
@@ -1711,11 +1753,16 @@ static void dw_i3c_master_restore_addrs(struct dw_i3c_master *master)
|
|||||||
if (master->free_pos & BIT(pos))
|
if (master->free_pos & BIT(pos))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (master->devs[pos].is_i2c_addr)
|
reg_val = readl(master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
|
||||||
reg_val = DEV_ADDR_TABLE_LEGACY_I2C_DEV |
|
|
||||||
|
if (master->devs[pos].is_i2c_addr) {
|
||||||
|
reg_val &= ~DEV_ADDR_TABLE_STATIC_MASK;
|
||||||
|
reg_val |= DEV_ADDR_TABLE_LEGACY_I2C_DEV |
|
||||||
DEV_ADDR_TABLE_STATIC_ADDR(master->devs[pos].addr);
|
DEV_ADDR_TABLE_STATIC_ADDR(master->devs[pos].addr);
|
||||||
else
|
} else {
|
||||||
reg_val = DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr);
|
reg_val &= ~DEV_ADDR_TABLE_DYNAMIC_MASK;
|
||||||
|
reg_val |= DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr);
|
||||||
|
}
|
||||||
|
|
||||||
writel(reg_val, master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
|
writel(reg_val, master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
|
||||||
}
|
}
|
||||||
@@ -1829,11 +1876,12 @@ static const struct of_device_id dw_i3c_master_of_match[] = {
|
|||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, dw_i3c_master_of_match);
|
MODULE_DEVICE_TABLE(of, dw_i3c_master_of_match);
|
||||||
|
|
||||||
static const struct acpi_device_id amd_i3c_device_match[] = {
|
static const struct acpi_device_id dw_i3c_master_acpi_match[] = {
|
||||||
{ "AMDI0015", AMD_I3C_OD_PP_TIMING },
|
{ "AMDI0015", AMD_I3C_OD_PP_TIMING },
|
||||||
|
{ "NVDA2018", DW_I3C_ACPI_SKIP_CLK_RST },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(acpi, amd_i3c_device_match);
|
MODULE_DEVICE_TABLE(acpi, dw_i3c_master_acpi_match);
|
||||||
|
|
||||||
static struct platform_driver dw_i3c_driver = {
|
static struct platform_driver dw_i3c_driver = {
|
||||||
.probe = dw_i3c_probe,
|
.probe = dw_i3c_probe,
|
||||||
@@ -1842,7 +1890,7 @@ static struct platform_driver dw_i3c_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = "dw-i3c-master",
|
.name = "dw-i3c-master",
|
||||||
.of_match_table = dw_i3c_master_of_match,
|
.of_match_table = dw_i3c_master_of_match,
|
||||||
.acpi_match_table = amd_i3c_device_match,
|
.acpi_match_table = dw_i3c_master_acpi_match,
|
||||||
.pm = &dw_i3c_pm_ops,
|
.pm = &dw_i3c_pm_ops,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ struct dw_i3c_master {
|
|||||||
u32 i2c_fm_timing;
|
u32 i2c_fm_timing;
|
||||||
u32 i2c_fmp_timing;
|
u32 i2c_fmp_timing;
|
||||||
u32 quirks;
|
u32 quirks;
|
||||||
|
bool has_ibi_data;
|
||||||
/*
|
/*
|
||||||
* Per-device hardware data, used to manage the device address table
|
* Per-device hardware data, used to manage the device address table
|
||||||
* (DAT)
|
* (DAT)
|
||||||
|
|||||||
@@ -23,6 +23,8 @@
|
|||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
|
#include "../internals.h"
|
||||||
|
|
||||||
#define DEV_ID 0x0
|
#define DEV_ID 0x0
|
||||||
#define DEV_ID_I3C_MASTER 0x5034
|
#define DEV_ID_I3C_MASTER 0x5034
|
||||||
|
|
||||||
@@ -427,25 +429,13 @@ to_cdns_i3c_master(struct i3c_master_controller *master)
|
|||||||
static void cdns_i3c_master_wr_to_tx_fifo(struct cdns_i3c_master *master,
|
static void cdns_i3c_master_wr_to_tx_fifo(struct cdns_i3c_master *master,
|
||||||
const u8 *bytes, int nbytes)
|
const u8 *bytes, int nbytes)
|
||||||
{
|
{
|
||||||
writesl(master->regs + TX_FIFO, bytes, nbytes / 4);
|
i3c_writel_fifo(master->regs + TX_FIFO, bytes, nbytes);
|
||||||
if (nbytes & 3) {
|
|
||||||
u32 tmp = 0;
|
|
||||||
|
|
||||||
memcpy(&tmp, bytes + (nbytes & ~3), nbytes & 3);
|
|
||||||
writesl(master->regs + TX_FIFO, &tmp, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cdns_i3c_master_rd_from_rx_fifo(struct cdns_i3c_master *master,
|
static void cdns_i3c_master_rd_from_rx_fifo(struct cdns_i3c_master *master,
|
||||||
u8 *bytes, int nbytes)
|
u8 *bytes, int nbytes)
|
||||||
{
|
{
|
||||||
readsl(master->regs + RX_FIFO, bytes, nbytes / 4);
|
i3c_readl_fifo(master->regs + RX_FIFO, bytes, nbytes);
|
||||||
if (nbytes & 3) {
|
|
||||||
u32 tmp;
|
|
||||||
|
|
||||||
readsl(master->regs + RX_FIFO, &tmp, 1);
|
|
||||||
memcpy(bytes + (nbytes & ~3), &tmp, nbytes & 3);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cdns_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m,
|
static bool cdns_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m,
|
||||||
@@ -509,7 +499,7 @@ cdns_i3c_master_alloc_xfer(struct cdns_i3c_master *master, unsigned int ncmds)
|
|||||||
{
|
{
|
||||||
struct cdns_i3c_xfer *xfer;
|
struct cdns_i3c_xfer *xfer;
|
||||||
|
|
||||||
xfer = kzalloc(struct_size(xfer, cmds, ncmds), GFP_KERNEL);
|
xfer = kzalloc_flex(*xfer, cmds, ncmds, GFP_KERNEL);
|
||||||
if (!xfer)
|
if (!xfer)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -731,9 +721,9 @@ static int cdns_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cdns_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
|
static int cdns_i3c_master_i3c_xfers(struct i3c_dev_desc *dev,
|
||||||
struct i3c_priv_xfer *xfers,
|
struct i3c_xfer *xfers,
|
||||||
int nxfers)
|
int nxfers, enum i3c_xfer_mode mode)
|
||||||
{
|
{
|
||||||
struct i3c_master_controller *m = i3c_dev_get_master(dev);
|
struct i3c_master_controller *m = i3c_dev_get_master(dev);
|
||||||
struct cdns_i3c_master *master = to_cdns_i3c_master(m);
|
struct cdns_i3c_master *master = to_cdns_i3c_master(m);
|
||||||
@@ -813,7 +803,7 @@ static int cdns_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int cdns_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
|
static int cdns_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
|
||||||
const struct i2c_msg *xfers, int nxfers)
|
struct i2c_msg *xfers, int nxfers)
|
||||||
{
|
{
|
||||||
struct i3c_master_controller *m = i2c_dev_get_master(dev);
|
struct i3c_master_controller *m = i2c_dev_get_master(dev);
|
||||||
struct cdns_i3c_master *master = to_cdns_i3c_master(m);
|
struct cdns_i3c_master *master = to_cdns_i3c_master(m);
|
||||||
@@ -952,7 +942,7 @@ static int cdns_i3c_master_attach_i3c_dev(struct i3c_dev_desc *dev)
|
|||||||
struct cdns_i3c_i2c_dev_data *data;
|
struct cdns_i3c_i2c_dev_data *data;
|
||||||
int slot;
|
int slot;
|
||||||
|
|
||||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
data = kzalloc_obj(*data);
|
||||||
if (!data)
|
if (!data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@@ -1003,7 +993,7 @@ static int cdns_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev)
|
|||||||
if (slot < 0)
|
if (slot < 0)
|
||||||
return slot;
|
return slot;
|
||||||
|
|
||||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
data = kzalloc_obj(*data);
|
||||||
if (!data)
|
if (!data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@@ -1331,12 +1321,7 @@ static void cdns_i3c_master_handle_ibi(struct cdns_i3c_master *master,
|
|||||||
buf = slot->data;
|
buf = slot->data;
|
||||||
|
|
||||||
nbytes = IBIR_XFER_BYTES(ibir);
|
nbytes = IBIR_XFER_BYTES(ibir);
|
||||||
readsl(master->regs + IBI_DATA_FIFO, buf, nbytes / 4);
|
i3c_readl_fifo(master->regs + IBI_DATA_FIFO, buf, nbytes);
|
||||||
if (nbytes % 3) {
|
|
||||||
u32 tmp = __raw_readl(master->regs + IBI_DATA_FIFO);
|
|
||||||
|
|
||||||
memcpy(buf + (nbytes & ~3), &tmp, nbytes & 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
slot->len = min_t(unsigned int, IBIR_XFER_BYTES(ibir),
|
slot->len = min_t(unsigned int, IBIR_XFER_BYTES(ibir),
|
||||||
dev->ibi->max_payload_len);
|
dev->ibi->max_payload_len);
|
||||||
@@ -1536,7 +1521,7 @@ static const struct i3c_master_controller_ops cdns_i3c_master_ops = {
|
|||||||
.detach_i2c_dev = cdns_i3c_master_detach_i2c_dev,
|
.detach_i2c_dev = cdns_i3c_master_detach_i2c_dev,
|
||||||
.supports_ccc_cmd = cdns_i3c_master_supports_ccc_cmd,
|
.supports_ccc_cmd = cdns_i3c_master_supports_ccc_cmd,
|
||||||
.send_ccc_cmd = cdns_i3c_master_send_ccc_cmd,
|
.send_ccc_cmd = cdns_i3c_master_send_ccc_cmd,
|
||||||
.priv_xfers = cdns_i3c_master_priv_xfers,
|
.i3c_xfers = cdns_i3c_master_i3c_xfers,
|
||||||
.i2c_xfers = cdns_i3c_master_i2c_xfers,
|
.i2c_xfers = cdns_i3c_master_i2c_xfers,
|
||||||
.enable_ibi = cdns_i3c_master_enable_ibi,
|
.enable_ibi = cdns_i3c_master_enable_ibi,
|
||||||
.disable_ibi = cdns_i3c_master_disable_ibi,
|
.disable_ibi = cdns_i3c_master_disable_ibi,
|
||||||
|
|||||||
@@ -274,37 +274,9 @@ static int i3c_hci_daa(struct i3c_master_controller *m)
|
|||||||
return hci->cmd->perform_daa(hci);
|
return hci->cmd->perform_daa(hci);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i3c_hci_alloc_safe_xfer_buf(struct i3c_hci *hci,
|
static int i3c_hci_i3c_xfers(struct i3c_dev_desc *dev,
|
||||||
struct hci_xfer *xfer)
|
struct i3c_xfer *i3c_xfers, int nxfers,
|
||||||
{
|
enum i3c_xfer_mode mode)
|
||||||
if (hci->io != &mipi_i3c_hci_dma ||
|
|
||||||
xfer->data == NULL || !is_vmalloc_addr(xfer->data))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (xfer->rnw)
|
|
||||||
xfer->bounce_buf = kzalloc(xfer->data_len, GFP_KERNEL);
|
|
||||||
else
|
|
||||||
xfer->bounce_buf = kmemdup(xfer->data,
|
|
||||||
xfer->data_len, GFP_KERNEL);
|
|
||||||
|
|
||||||
return xfer->bounce_buf == NULL ? -ENOMEM : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void i3c_hci_free_safe_xfer_buf(struct i3c_hci *hci,
|
|
||||||
struct hci_xfer *xfer)
|
|
||||||
{
|
|
||||||
if (hci->io != &mipi_i3c_hci_dma || xfer->bounce_buf == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (xfer->rnw)
|
|
||||||
memcpy(xfer->data, xfer->bounce_buf, xfer->data_len);
|
|
||||||
|
|
||||||
kfree(xfer->bounce_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev,
|
|
||||||
struct i3c_priv_xfer *i3c_xfers,
|
|
||||||
int nxfers)
|
|
||||||
{
|
{
|
||||||
struct i3c_master_controller *m = i3c_dev_get_master(dev);
|
struct i3c_master_controller *m = i3c_dev_get_master(dev);
|
||||||
struct i3c_hci *hci = to_i3c_hci(m);
|
struct i3c_hci *hci = to_i3c_hci(m);
|
||||||
@@ -335,9 +307,6 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev,
|
|||||||
}
|
}
|
||||||
hci->cmd->prep_i3c_xfer(hci, dev, &xfer[i]);
|
hci->cmd->prep_i3c_xfer(hci, dev, &xfer[i]);
|
||||||
xfer[i].cmd_desc[0] |= CMD_0_ROC;
|
xfer[i].cmd_desc[0] |= CMD_0_ROC;
|
||||||
ret = i3c_hci_alloc_safe_xfer_buf(hci, &xfer[i]);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
last = i - 1;
|
last = i - 1;
|
||||||
xfer[last].cmd_desc[0] |= CMD_0_TOC;
|
xfer[last].cmd_desc[0] |= CMD_0_TOC;
|
||||||
@@ -361,15 +330,12 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
for (i = 0; i < nxfers; i++)
|
|
||||||
i3c_hci_free_safe_xfer_buf(hci, &xfer[i]);
|
|
||||||
|
|
||||||
hci_free_xfer(xfer, nxfers);
|
hci_free_xfer(xfer, nxfers);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
|
static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
|
||||||
const struct i2c_msg *i2c_xfers, int nxfers)
|
struct i2c_msg *i2c_xfers, int nxfers)
|
||||||
{
|
{
|
||||||
struct i3c_master_controller *m = i2c_dev_get_master(dev);
|
struct i3c_master_controller *m = i2c_dev_get_master(dev);
|
||||||
struct i3c_hci *hci = to_i3c_hci(m);
|
struct i3c_hci *hci = to_i3c_hci(m);
|
||||||
@@ -384,14 +350,11 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
for (i = 0; i < nxfers; i++) {
|
for (i = 0; i < nxfers; i++) {
|
||||||
xfer[i].data = i2c_xfers[i].buf;
|
xfer[i].data = i2c_get_dma_safe_msg_buf(&i2c_xfers[i], 1);
|
||||||
xfer[i].data_len = i2c_xfers[i].len;
|
xfer[i].data_len = i2c_xfers[i].len;
|
||||||
xfer[i].rnw = i2c_xfers[i].flags & I2C_M_RD;
|
xfer[i].rnw = i2c_xfers[i].flags & I2C_M_RD;
|
||||||
hci->cmd->prep_i2c_xfer(hci, dev, &xfer[i]);
|
hci->cmd->prep_i2c_xfer(hci, dev, &xfer[i]);
|
||||||
xfer[i].cmd_desc[0] |= CMD_0_ROC;
|
xfer[i].cmd_desc[0] |= CMD_0_ROC;
|
||||||
ret = i3c_hci_alloc_safe_xfer_buf(hci, &xfer[i]);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
last = i - 1;
|
last = i - 1;
|
||||||
xfer[last].cmd_desc[0] |= CMD_0_TOC;
|
xfer[last].cmd_desc[0] |= CMD_0_TOC;
|
||||||
@@ -414,7 +377,8 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
for (i = 0; i < nxfers; i++)
|
for (i = 0; i < nxfers; i++)
|
||||||
i3c_hci_free_safe_xfer_buf(hci, &xfer[i]);
|
i2c_put_dma_safe_msg_buf(xfer[i].data, &i2c_xfers[i],
|
||||||
|
ret ? false : true);
|
||||||
|
|
||||||
hci_free_xfer(xfer, nxfers);
|
hci_free_xfer(xfer, nxfers);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -572,7 +536,7 @@ static const struct i3c_master_controller_ops i3c_hci_ops = {
|
|||||||
.bus_cleanup = i3c_hci_bus_cleanup,
|
.bus_cleanup = i3c_hci_bus_cleanup,
|
||||||
.do_daa = i3c_hci_daa,
|
.do_daa = i3c_hci_daa,
|
||||||
.send_ccc_cmd = i3c_hci_send_ccc_cmd,
|
.send_ccc_cmd = i3c_hci_send_ccc_cmd,
|
||||||
.priv_xfers = i3c_hci_priv_xfers,
|
.i3c_xfers = i3c_hci_i3c_xfers,
|
||||||
.i2c_xfers = i3c_hci_i2c_xfers,
|
.i2c_xfers = i3c_hci_i2c_xfers,
|
||||||
.attach_i3c_dev = i3c_hci_attach_i3c_dev,
|
.attach_i3c_dev = i3c_hci_attach_i3c_dev,
|
||||||
.reattach_i3c_dev = i3c_hci_reattach_i3c_dev,
|
.reattach_i3c_dev = i3c_hci_reattach_i3c_dev,
|
||||||
|
|||||||
@@ -349,9 +349,7 @@ static void hci_dma_unmap_xfer(struct i3c_hci *hci,
|
|||||||
xfer = xfer_list + i;
|
xfer = xfer_list + i;
|
||||||
if (!xfer->data)
|
if (!xfer->data)
|
||||||
continue;
|
continue;
|
||||||
dma_unmap_single(&hci->master.dev,
|
i3c_master_dma_unmap_single(xfer->dma);
|
||||||
xfer->data_dma, xfer->data_len,
|
|
||||||
xfer->rnw ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +360,6 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
|
|||||||
struct hci_rh_data *rh;
|
struct hci_rh_data *rh;
|
||||||
unsigned int i, ring, enqueue_ptr;
|
unsigned int i, ring, enqueue_ptr;
|
||||||
u32 op1_val, op2_val;
|
u32 op1_val, op2_val;
|
||||||
void *buf;
|
|
||||||
|
|
||||||
/* For now we only use ring 0 */
|
/* For now we only use ring 0 */
|
||||||
ring = 0;
|
ring = 0;
|
||||||
@@ -373,6 +370,8 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
|
|||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
struct hci_xfer *xfer = xfer_list + i;
|
struct hci_xfer *xfer = xfer_list + i;
|
||||||
u32 *ring_data = rh->xfer + rh->xfer_struct_sz * enqueue_ptr;
|
u32 *ring_data = rh->xfer + rh->xfer_struct_sz * enqueue_ptr;
|
||||||
|
enum dma_data_direction dir = xfer->rnw ? DMA_FROM_DEVICE :
|
||||||
|
DMA_TO_DEVICE;
|
||||||
|
|
||||||
/* store cmd descriptor */
|
/* store cmd descriptor */
|
||||||
*ring_data++ = xfer->cmd_desc[0];
|
*ring_data++ = xfer->cmd_desc[0];
|
||||||
@@ -391,21 +390,17 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
|
|||||||
|
|
||||||
/* 2nd and 3rd words of Data Buffer Descriptor Structure */
|
/* 2nd and 3rd words of Data Buffer Descriptor Structure */
|
||||||
if (xfer->data) {
|
if (xfer->data) {
|
||||||
buf = xfer->bounce_buf ? xfer->bounce_buf : xfer->data;
|
xfer->dma = i3c_master_dma_map_single(&hci->master.dev,
|
||||||
xfer->data_dma =
|
xfer->data,
|
||||||
dma_map_single(&hci->master.dev,
|
|
||||||
buf,
|
|
||||||
xfer->data_len,
|
xfer->data_len,
|
||||||
xfer->rnw ?
|
false,
|
||||||
DMA_FROM_DEVICE :
|
dir);
|
||||||
DMA_TO_DEVICE);
|
if (!xfer->dma) {
|
||||||
if (dma_mapping_error(&hci->master.dev,
|
|
||||||
xfer->data_dma)) {
|
|
||||||
hci_dma_unmap_xfer(hci, xfer_list, i);
|
hci_dma_unmap_xfer(hci, xfer_list, i);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
*ring_data++ = lower_32_bits(xfer->data_dma);
|
*ring_data++ = lower_32_bits(xfer->dma->addr);
|
||||||
*ring_data++ = upper_32_bits(xfer->data_dma);
|
*ring_data++ = upper_32_bits(xfer->dma->addr);
|
||||||
} else {
|
} else {
|
||||||
*ring_data++ = 0;
|
*ring_data++ = 0;
|
||||||
*ring_data++ = 0;
|
*ring_data++ = 0;
|
||||||
|
|||||||
@@ -94,8 +94,7 @@ struct hci_xfer {
|
|||||||
};
|
};
|
||||||
struct {
|
struct {
|
||||||
/* DMA specific */
|
/* DMA specific */
|
||||||
dma_addr_t data_dma;
|
struct i3c_dma *dma;
|
||||||
void *bounce_buf;
|
|
||||||
int ring_number;
|
int ring_number;
|
||||||
int ring_entry;
|
int ring_entry;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,11 +39,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)
|
||||||
@@ -93,6 +95,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
|
||||||
@@ -135,12 +138,16 @@
|
|||||||
|
|
||||||
struct svc_i3c_cmd {
|
struct svc_i3c_cmd {
|
||||||
u8 addr;
|
u8 addr;
|
||||||
|
union {
|
||||||
bool rnw;
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -337,6 +344,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);
|
||||||
@@ -426,7 +463,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
|
|||||||
* 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.
|
||||||
*/
|
*/
|
||||||
@@ -682,6 +719,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);
|
||||||
|
|
||||||
@@ -747,7 +786,7 @@ static int svc_i3c_master_attach_i3c_dev(struct i3c_dev_desc *dev)
|
|||||||
if (slot < 0)
|
if (slot < 0)
|
||||||
return slot;
|
return slot;
|
||||||
|
|
||||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
data = kzalloc_obj(*data);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
svc_i3c_master_release_slot(master, slot);
|
svc_i3c_master_release_slot(master, slot);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@@ -800,7 +839,7 @@ static int svc_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev)
|
|||||||
if (slot < 0)
|
if (slot < 0)
|
||||||
return slot;
|
return slot;
|
||||||
|
|
||||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
data = kzalloc_obj(*data);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
svc_i3c_master_release_slot(master, slot);
|
svc_i3c_master_release_slot(master, slot);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@@ -1066,7 +1105,7 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
|
|||||||
/* Configure IBI auto-rules */
|
/* Configure IBI auto-rules */
|
||||||
ret = svc_i3c_update_ibirules(master);
|
ret = svc_i3c_update_ibirules(master);
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_err(master->dev, "Cannot handle such a list of devices");
|
dev_err(master->dev, "Cannot handle such a list of devices\n");
|
||||||
|
|
||||||
rpm_out:
|
rpm_out:
|
||||||
pm_runtime_mark_last_busy(master->dev);
|
pm_runtime_mark_last_busy(master->dev);
|
||||||
@@ -1137,17 +1176,28 @@ 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)
|
unsigned int *actual_len, bool continued, bool repeat_start)
|
||||||
{
|
{
|
||||||
int retry = 2;
|
bool rnw = svc_cmd_is_read(rnw_cmd, xfer_type);
|
||||||
|
int retry = repeat_start ? 1 : 2;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* 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 |
|
||||||
@@ -1216,7 +1266,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;
|
||||||
@@ -1229,10 +1279,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) {
|
||||||
|
if (xfer_type != SVC_I3C_MCTRL_TYPE_DDR)
|
||||||
svc_i3c_master_emit_stop(master);
|
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,
|
||||||
@@ -1242,7 +1301,11 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
emit_stop:
|
emit_stop:
|
||||||
|
if (xfer_type != SVC_I3C_MCTRL_TYPE_DDR)
|
||||||
svc_i3c_master_emit_stop(master);
|
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);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -1253,7 +1316,7 @@ svc_i3c_master_alloc_xfer(struct svc_i3c_master *master, unsigned int ncmds)
|
|||||||
{
|
{
|
||||||
struct svc_i3c_xfer *xfer;
|
struct svc_i3c_xfer *xfer;
|
||||||
|
|
||||||
xfer = kzalloc(struct_size(xfer, cmds, ncmds), GFP_KERNEL);
|
xfer = kzalloc_flex(*xfer, cmds, ncmds, GFP_KERNEL);
|
||||||
if (!xfer)
|
if (!xfer)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -1288,6 +1351,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;
|
||||||
@@ -1302,10 +1370,10 @@ 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);
|
cmd->continued, i > 0);
|
||||||
/* cmd->xfer is NULL if I2C or CCC transfer */
|
/* cmd->xfer is NULL if I2C or CCC transfer */
|
||||||
if (cmd->xfer)
|
if (cmd->xfer)
|
||||||
cmd->xfer->actual_len = cmd->actual_len;
|
cmd->xfer->actual_len = cmd->actual_len;
|
||||||
@@ -1478,9 +1546,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);
|
||||||
@@ -1488,22 +1555,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1520,7 +1601,7 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
|
static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
|
||||||
const struct i2c_msg *xfers,
|
struct i2c_msg *xfers,
|
||||||
int nxfers)
|
int nxfers)
|
||||||
{
|
{
|
||||||
struct i3c_master_controller *m = i2c_dev_get_master(dev);
|
struct i3c_master_controller *m = i2c_dev_get_master(dev);
|
||||||
@@ -1700,7 +1781,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,
|
||||||
|
|||||||
@@ -423,6 +423,7 @@ struct acpi_resource_i2c_serialbus {
|
|||||||
ACPI_RESOURCE_SERIAL_COMMON u8 access_mode;
|
ACPI_RESOURCE_SERIAL_COMMON u8 access_mode;
|
||||||
u16 slave_address;
|
u16 slave_address;
|
||||||
u32 connection_speed;
|
u32 connection_speed;
|
||||||
|
u8 lvr;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Values for access_mode field above */
|
/* Values for access_mode field above */
|
||||||
|
|||||||
@@ -13,4 +13,8 @@
|
|||||||
#define I2C_NO_FILTER_HIGH_FREQUENCY (1 << 5)
|
#define I2C_NO_FILTER_HIGH_FREQUENCY (1 << 5)
|
||||||
#define I2C_NO_FILTER_LOW_FREQUENCY (2 << 5)
|
#define I2C_NO_FILTER_LOW_FREQUENCY (2 << 5)
|
||||||
|
|
||||||
|
#define I3C_ADDR_METHOD_SETDASA (1 << 0)
|
||||||
|
#define I3C_ADDR_METHOD_SETAASA (1 << 1)
|
||||||
|
#define I3C_ADDR_METHOD_VENDOR (1 << 2)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#define I3C_CCC_DEFSLVS I3C_CCC_ID(0x8, true)
|
#define I3C_CCC_DEFSLVS I3C_CCC_ID(0x8, true)
|
||||||
#define I3C_CCC_ENTTM I3C_CCC_ID(0xb, true)
|
#define I3C_CCC_ENTTM I3C_CCC_ID(0xb, true)
|
||||||
#define I3C_CCC_ENTHDR(x) I3C_CCC_ID(0x20 + (x), true)
|
#define I3C_CCC_ENTHDR(x) I3C_CCC_ID(0x20 + (x), true)
|
||||||
|
#define I3C_CCC_SETAASA I3C_CCC_ID(0x29, true)
|
||||||
|
|
||||||
/* Unicast-only commands */
|
/* Unicast-only commands */
|
||||||
#define I3C_CCC_SETDASA I3C_CCC_ID(0x7, false)
|
#define I3C_CCC_SETDASA I3C_CCC_ID(0x7, false)
|
||||||
|
|||||||
+29
-11
@@ -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 = 0x31,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 {
|
||||||
|
union {
|
||||||
u8 rnw;
|
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 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 */
|
||||||
|
|||||||
+80
-14
@@ -174,10 +174,19 @@ struct i3c_device_ibi_info {
|
|||||||
* assigned a dynamic address by the master. Will be used during
|
* assigned a dynamic address by the master. Will be used during
|
||||||
* bus initialization to assign it a specific dynamic address
|
* bus initialization to assign it a specific dynamic address
|
||||||
* before starting DAA (Dynamic Address Assignment)
|
* before starting DAA (Dynamic Address Assignment)
|
||||||
|
* @static_addr_method: Bitmap describing which methods of Dynamic Address
|
||||||
|
* Assignment from a Static Address are supported by this I3C Target.
|
||||||
|
* A value of 1 in a bit position indicates that the I3C target
|
||||||
|
* supports that method, and a value of 0 indicates that the I3C
|
||||||
|
* target does not support that method.
|
||||||
|
* Bit 0: SETDASA
|
||||||
|
* Bit 1: SETAASA
|
||||||
|
* All other bits are reserved.
|
||||||
* @pid: I3C Provisioned ID exposed by the device. This is a unique identifier
|
* @pid: I3C Provisioned ID exposed by the device. This is a unique identifier
|
||||||
* that may be used to attach boardinfo to i3c_dev_desc when the device
|
* that may be used to attach boardinfo to i3c_dev_desc when the device
|
||||||
* does not have a static address
|
* does not have a static address
|
||||||
* @of_node: optional DT node in case the device has been described in the DT
|
* @fwnode: Firmware node (DT or ACPI) in case the device has been
|
||||||
|
* described in firmware
|
||||||
*
|
*
|
||||||
* This structure is used to attach board-level information to an I3C device.
|
* This structure is used to attach board-level information to an I3C device.
|
||||||
* Not all I3C devices connected on the bus will have a boardinfo. It's only
|
* Not all I3C devices connected on the bus will have a boardinfo. It's only
|
||||||
@@ -188,8 +197,9 @@ struct i3c_dev_boardinfo {
|
|||||||
struct list_head node;
|
struct list_head node;
|
||||||
u8 init_dyn_addr;
|
u8 init_dyn_addr;
|
||||||
u8 static_addr;
|
u8 static_addr;
|
||||||
|
u8 static_addr_method;
|
||||||
u64 pid;
|
u64 pid;
|
||||||
struct device_node *of_node;
|
struct fwnode_handle *fwnode;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -249,10 +259,15 @@ struct i3c_device {
|
|||||||
*/
|
*/
|
||||||
#define I3C_BUS_MAX_DEVS 11
|
#define I3C_BUS_MAX_DEVS 11
|
||||||
|
|
||||||
#define I3C_BUS_MAX_I3C_SCL_RATE 12900000
|
/* Taken from the I3C Spec V1.1.1, chapter 6.2. "Timing specification" */
|
||||||
#define I3C_BUS_TYP_I3C_SCL_RATE 12500000
|
#define I3C_BUS_I2C_FM_PLUS_SCL_MAX_RATE 1000000
|
||||||
#define I3C_BUS_I2C_FM_PLUS_SCL_RATE 1000000
|
#define I3C_BUS_I2C_FM_SCL_MAX_RATE 400000
|
||||||
#define I3C_BUS_I2C_FM_SCL_RATE 400000
|
#define I3C_BUS_I3C_SCL_MAX_RATE 12900000
|
||||||
|
#define I3C_BUS_I3C_SCL_TYP_RATE 12500000
|
||||||
|
#define I3C_BUS_TAVAL_MIN_NS 1000
|
||||||
|
#define I3C_BUS_TBUF_MIXED_FM_MIN_NS 1300
|
||||||
|
#define I3C_BUS_THIGH_MIXED_MAX_NS 41
|
||||||
|
#define I3C_BUS_TIDLE_MIN_NS 200000
|
||||||
#define I3C_BUS_TLOW_OD_MIN_NS 200
|
#define I3C_BUS_TLOW_OD_MIN_NS 200
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -298,7 +313,9 @@ enum i3c_open_drain_speed {
|
|||||||
* @I3C_ADDR_SLOT_I2C_DEV: address is assigned to an I2C device
|
* @I3C_ADDR_SLOT_I2C_DEV: address is assigned to an I2C device
|
||||||
* @I3C_ADDR_SLOT_I3C_DEV: address is assigned to an I3C device
|
* @I3C_ADDR_SLOT_I3C_DEV: address is assigned to an I3C device
|
||||||
* @I3C_ADDR_SLOT_STATUS_MASK: address slot mask
|
* @I3C_ADDR_SLOT_STATUS_MASK: address slot mask
|
||||||
*
|
* @I3C_ADDR_SLOT_EXT_STATUS_MASK: address slot mask with extended information
|
||||||
|
* @I3C_ADDR_SLOT_EXT_DESIRED: the bitmask represents addresses that are preferred by some devices,
|
||||||
|
* such as the "assigned-address" property in a device tree source.
|
||||||
* On an I3C bus, addresses are assigned dynamically, and we need to know which
|
* On an I3C bus, addresses are assigned dynamically, and we need to know which
|
||||||
* addresses are free to use and which ones are already assigned.
|
* addresses are free to use and which ones are already assigned.
|
||||||
*
|
*
|
||||||
@@ -311,8 +328,12 @@ enum i3c_addr_slot_status {
|
|||||||
I3C_ADDR_SLOT_I2C_DEV,
|
I3C_ADDR_SLOT_I2C_DEV,
|
||||||
I3C_ADDR_SLOT_I3C_DEV,
|
I3C_ADDR_SLOT_I3C_DEV,
|
||||||
I3C_ADDR_SLOT_STATUS_MASK = 3,
|
I3C_ADDR_SLOT_STATUS_MASK = 3,
|
||||||
|
I3C_ADDR_SLOT_EXT_STATUS_MASK = 7,
|
||||||
|
I3C_ADDR_SLOT_EXT_DESIRED = BIT(2),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define I3C_ADDR_SLOT_STATUS_BITS 4
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct i3c_bus - I3C bus object
|
* struct i3c_bus - I3C bus object
|
||||||
* @cur_master: I3C master currently driving the bus. Since I3C is multi-master
|
* @cur_master: I3C master currently driving the bus. Since I3C is multi-master
|
||||||
@@ -354,7 +375,7 @@ enum i3c_addr_slot_status {
|
|||||||
struct i3c_bus {
|
struct i3c_bus {
|
||||||
struct i3c_dev_desc *cur_master;
|
struct i3c_dev_desc *cur_master;
|
||||||
int id;
|
int id;
|
||||||
unsigned long addrslots[((I2C_MAX_ADDR + 1) * 2) / BITS_PER_LONG];
|
unsigned long addrslots[((I2C_MAX_ADDR + 1) * I3C_ADDR_SLOT_STATUS_BITS) / BITS_PER_LONG];
|
||||||
enum i3c_bus_mode mode;
|
enum i3c_bus_mode mode;
|
||||||
struct {
|
struct {
|
||||||
unsigned long i3c;
|
unsigned long i3c;
|
||||||
@@ -406,7 +427,7 @@ struct i3c_bus {
|
|||||||
* all CCC commands are supported.
|
* all CCC commands are supported.
|
||||||
* @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
|
* @i3c_xfers: do one or several I3C SDR or HDR transfers.
|
||||||
* This method is mandatory.
|
* This method is mandatory.
|
||||||
* @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
|
||||||
@@ -451,6 +472,8 @@ struct i3c_bus {
|
|||||||
* @enable_hotjoin: enable hot join event detect.
|
* @enable_hotjoin: enable hot join event detect.
|
||||||
* @disable_hotjoin: disable hot join event detect.
|
* @disable_hotjoin: disable hot join event detect.
|
||||||
* @set_speed: adjust I3C open drain mode timing.
|
* @set_speed: adjust I3C open drain mode timing.
|
||||||
|
* @set_dev_nack_retry: configure device NACK retry count for the master
|
||||||
|
* controller.
|
||||||
*/
|
*/
|
||||||
struct i3c_master_controller_ops {
|
struct i3c_master_controller_ops {
|
||||||
int (*bus_init)(struct i3c_master_controller *master);
|
int (*bus_init)(struct i3c_master_controller *master);
|
||||||
@@ -463,13 +486,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);
|
||||||
int (*priv_xfers)(struct i3c_dev_desc *dev,
|
int (*i3c_xfers)(struct i3c_dev_desc *dev,
|
||||||
struct i3c_priv_xfer *xfers,
|
struct i3c_xfer *xfers,
|
||||||
int nxfers);
|
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,
|
||||||
const struct i2c_msg *xfers, int nxfers);
|
struct i2c_msg *xfers, int nxfers);
|
||||||
int (*request_ibi)(struct i3c_dev_desc *dev,
|
int (*request_ibi)(struct i3c_dev_desc *dev,
|
||||||
const struct i3c_ibi_setup *req);
|
const struct i3c_ibi_setup *req);
|
||||||
void (*free_ibi)(struct i3c_dev_desc *dev);
|
void (*free_ibi)(struct i3c_dev_desc *dev);
|
||||||
@@ -480,6 +503,8 @@ struct i3c_master_controller_ops {
|
|||||||
int (*enable_hotjoin)(struct i3c_master_controller *master);
|
int (*enable_hotjoin)(struct i3c_master_controller *master);
|
||||||
int (*disable_hotjoin)(struct i3c_master_controller *master);
|
int (*disable_hotjoin)(struct i3c_master_controller *master);
|
||||||
int (*set_speed)(struct i3c_master_controller *master, enum i3c_open_drain_speed speed);
|
int (*set_speed)(struct i3c_master_controller *master, enum i3c_open_drain_speed speed);
|
||||||
|
int (*set_dev_nack_retry)(struct i3c_master_controller *master,
|
||||||
|
unsigned int dev_nack_retry_cnt);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -494,15 +519,23 @@ struct i3c_master_controller_ops {
|
|||||||
* @secondary: true if the master is a secondary master
|
* @secondary: true if the master is a secondary master
|
||||||
* @init_done: true when the bus initialization is done
|
* @init_done: true when the bus initialization is done
|
||||||
* @hotjoin: true if the master support hotjoin
|
* @hotjoin: true if the master support hotjoin
|
||||||
|
* @rpm_allowed: true if Runtime PM allowed
|
||||||
|
* @rpm_ibi_allowed: true if IBI and Hot-Join allowed while runtime suspended
|
||||||
* @boardinfo.i3c: list of I3C boardinfo objects
|
* @boardinfo.i3c: list of I3C boardinfo objects
|
||||||
* @boardinfo.i2c: list of I2C boardinfo objects
|
* @boardinfo.i2c: list of I2C boardinfo objects
|
||||||
* @boardinfo: board-level information attached to devices connected on the bus
|
* @boardinfo: board-level information attached to devices connected on the bus
|
||||||
* @bus: I3C bus exposed by this master
|
* @bus: I3C bus exposed by this master
|
||||||
* @wq: workqueue which can be used by master
|
* @addr_method: Bitmap describing which methods of Address Assignment required
|
||||||
|
* to be run for discovering all the devices on the bus.
|
||||||
|
* Bit 0: SETDASA
|
||||||
|
* Bit 1: SETAASA
|
||||||
|
* All other bits are reserved.
|
||||||
|
* @wq: freezable workqueue which can be used by master
|
||||||
* drivers if they need to postpone operations that need to take place
|
* drivers if they need to postpone operations that need to take place
|
||||||
* in a thread context. Typical examples are Hot Join processing which
|
* in a thread context. Typical examples are Hot Join processing which
|
||||||
* requires taking the bus lock in maintenance, which in turn, can only
|
* requires taking the bus lock in maintenance, which in turn, can only
|
||||||
* be done from a sleep-able context
|
* be done from a sleep-able context
|
||||||
|
* @dev_nack_retry_count: retry count when slave device nack
|
||||||
*
|
*
|
||||||
* A &struct i3c_master_controller has to be registered to the I3C subsystem
|
* A &struct i3c_master_controller has to be registered to the I3C subsystem
|
||||||
* through i3c_master_register(). None of &struct i3c_master_controller fields
|
* through i3c_master_register(). None of &struct i3c_master_controller fields
|
||||||
@@ -517,12 +550,16 @@ struct i3c_master_controller {
|
|||||||
unsigned int secondary : 1;
|
unsigned int secondary : 1;
|
||||||
unsigned int init_done : 1;
|
unsigned int init_done : 1;
|
||||||
unsigned int hotjoin: 1;
|
unsigned int hotjoin: 1;
|
||||||
|
unsigned int rpm_allowed: 1;
|
||||||
|
unsigned int rpm_ibi_allowed: 1;
|
||||||
struct {
|
struct {
|
||||||
struct list_head i3c;
|
struct list_head i3c;
|
||||||
struct list_head i2c;
|
struct list_head i2c;
|
||||||
} boardinfo;
|
} boardinfo;
|
||||||
struct i3c_bus bus;
|
struct i3c_bus bus;
|
||||||
|
u8 addr_method;
|
||||||
struct workqueue_struct *wq;
|
struct workqueue_struct *wq;
|
||||||
|
unsigned int dev_nack_retry_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -547,6 +584,26 @@ struct i3c_master_controller {
|
|||||||
#define i3c_bus_for_each_i3cdev(bus, dev) \
|
#define i3c_bus_for_each_i3cdev(bus, dev) \
|
||||||
list_for_each_entry(dev, &(bus)->devs.i3c, common.node)
|
list_for_each_entry(dev, &(bus)->devs.i3c, common.node)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct i3c_dma - DMA transfer and mapping descriptor
|
||||||
|
* @dev: device object of a device doing DMA
|
||||||
|
* @buf: destination/source buffer for DMA
|
||||||
|
* @len: length of transfer
|
||||||
|
* @map_len: length of DMA mapping
|
||||||
|
* @addr: mapped DMA address for a Host Controller Driver
|
||||||
|
* @dir: DMA direction
|
||||||
|
* @bounce_buf: an allocated bounce buffer if transfer needs it or NULL
|
||||||
|
*/
|
||||||
|
struct i3c_dma {
|
||||||
|
struct device *dev;
|
||||||
|
void *buf;
|
||||||
|
size_t len;
|
||||||
|
size_t map_len;
|
||||||
|
dma_addr_t addr;
|
||||||
|
enum dma_data_direction dir;
|
||||||
|
void *bounce_buf;
|
||||||
|
};
|
||||||
|
|
||||||
int i3c_master_do_i2c_xfers(struct i3c_master_controller *master,
|
int i3c_master_do_i2c_xfers(struct i3c_master_controller *master,
|
||||||
const struct i2c_msg *xfers,
|
const struct i2c_msg *xfers,
|
||||||
int nxfers);
|
int nxfers);
|
||||||
@@ -564,7 +621,16 @@ int i3c_master_get_free_addr(struct i3c_master_controller *master,
|
|||||||
int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
|
int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
|
||||||
u8 addr);
|
u8 addr);
|
||||||
int i3c_master_do_daa(struct i3c_master_controller *master);
|
int i3c_master_do_daa(struct i3c_master_controller *master);
|
||||||
|
int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa);
|
||||||
|
struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *ptr,
|
||||||
|
size_t len, bool force_bounce,
|
||||||
|
enum dma_data_direction dir);
|
||||||
|
void i3c_master_dma_unmap_single(struct i3c_dma *dma_xfer);
|
||||||
|
DEFINE_FREE(i3c_master_dma_unmap_single, void *,
|
||||||
|
if (_T) i3c_master_dma_unmap_single(_T))
|
||||||
|
|
||||||
|
int i3c_master_reattach_i3c_dev_locked(struct i3c_dev_desc *dev,
|
||||||
|
u8 old_dyn_addr);
|
||||||
int i3c_master_set_info(struct i3c_master_controller *master,
|
int i3c_master_set_info(struct i3c_master_controller *master,
|
||||||
const struct i3c_device_info *info);
|
const struct i3c_device_info *info);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user