mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
I3C for 6.12
Core: - allow adjusting first broadcast address speed Drivers: - cdns: few fixes - mipi-i3c-hci: Add AMD SoC I3C controller support and quirks, fix get_i3c_mode - svc: adjust rates, fix race condition -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmbx6h4ACgkQY6TcMGxw OjL1qw/9HTddkWARFkD7eS5/GuzBdsXlaUcAZZHgbu8AGLVAMJbmB8MPSYpkP1dj 1MDBXBgUwNjP8/Pbn3il3hvOcRje7SsvtHivYho+OA2mnLBxKIJUBfSPswiW9Y00 j2FtJCfBFms5+UC4fX2SywSgkEWp5a7RCOW2d69nwYqkoWlpSwIRRoVerFXKyCto iCCdos/KyvblD2tMT+fWtSHQRmPds9Fytl9RwHdCKRmlGi9qONuqJK2twlUnE164 bDk7C5JZuadX4l1pwOINlAOcpljEdANlSPq+5zmvGPqlSMMwFavhLEUPC2gJqOwQ bZXcWJs12vn28ZDeob0NDHIOJvLOrMDKWOtCQoxPMDfd00QO6+HI0vG5S7+yh+Q0 B/UDU+PwR48OncVys4P4kvu82NU9OeBqeg6QK1KLdKKQpw9K5zNca2qTlHX3ZYKd fQGu1G2K7FcZFi4WduKzUXbo7dIv69hi1uj4gbXii79LiOaZdtCAnYKn1qX7aeWM bwutMqLcug8vxqRxIR+23Ki7QLr55TPzj3YCGQsPPnmKAnkHJeEUiCtPJ5Ymytb8 GWzW0PVEdsFg1LB9Ei1K41/JICx62h/U0X2OdJnYx4JIij0e8GcoItYVsKsq+mG0 f7g2kBZDMDcAzaoHl7lgW5gBB8NnFmC0+74pqnjrBiSfzWkNFcM= =jSyb -----END PGP SIGNATURE----- Merge tag 'i3c/for-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux Pull i3c updates from Alexandre Belloni: "This adds support for the I3C HCI controller of the AMD SoC which as expected requires quirks. Also fixes for the other drivers, including rate selection fixes for svc. Core: - allow adjusting first broadcast address speed Drivers: - cdns: few fixes - mipi-i3c-hci: Add AMD SoC I3C controller support and quirks, fix get_i3c_mode - svc: adjust rates, fix race condition" * tag 'i3c/for-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux: i3c: master: svc: Fix use after free vulnerability in svc_i3c_master Driver Due to Race Condition i3c: master: cdns: Fix use after free vulnerability in cdns_i3c_master Driver Due to Race Condition i3c: master: svc: adjust SDR according to i3c spec i3c: master: svc: use slow speed for first broadcast address i3c: master: support to adjust first broadcast address speed i3c/master: cmd_v1: Fix the rule for getting i3c mode i3c: master: cdns: fix module autoloading i3c: mipi-i3c-hci: Add a quirk to set Response buffer threshold i3c: mipi-i3c-hci: Add a quirk to set timing parameters i3c: mipi-i3c-hci: Relocate helper macros to HCI header file i3c: mipi-i3c-hci: Add a quirk to set PIO mode i3c: mipi-i3c-hci: Read HC_CONTROL_PIO_MODE only after i3c hci v1.1 i3c: mipi-i3c-hci: Add AMDI5017 ACPI ID to the I3C Support List
This commit is contained in:
commit
cd3d647729
@ -1868,6 +1868,12 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
|
||||
goto err_bus_cleanup;
|
||||
}
|
||||
|
||||
if (master->ops->set_speed) {
|
||||
ret = master->ops->set_speed(master, I3C_OPEN_DRAIN_SLOW_SPEED);
|
||||
if (ret)
|
||||
goto err_bus_cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset all dynamic address that may have been assigned before
|
||||
* (assigned by the bootloader for example).
|
||||
@ -1876,6 +1882,12 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
|
||||
if (ret && ret != I3C_ERROR_M2)
|
||||
goto err_bus_cleanup;
|
||||
|
||||
if (master->ops->set_speed) {
|
||||
master->ops->set_speed(master, I3C_OPEN_DRAIN_NORMAL_SPEED);
|
||||
if (ret)
|
||||
goto err_bus_cleanup;
|
||||
}
|
||||
|
||||
/* Disable all slave events before starting DAA. */
|
||||
ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
|
||||
I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
|
||||
|
@ -1562,6 +1562,7 @@ static const struct of_device_id cdns_i3c_master_of_ids[] = {
|
||||
{ .compatible = "cdns,i3c-master", .data = &cdns_i3c_devdata },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cdns_i3c_master_of_ids);
|
||||
|
||||
static int cdns_i3c_master_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -1666,6 +1667,7 @@ static void cdns_i3c_master_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cdns_i3c_master *master = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_work_sync(&master->hj_work);
|
||||
i3c_master_unregister(&master->base);
|
||||
|
||||
clk_disable_unprepare(master->sysclk);
|
||||
|
@ -3,4 +3,5 @@
|
||||
obj-$(CONFIG_MIPI_I3C_HCI) += mipi-i3c-hci.o
|
||||
mipi-i3c-hci-y := core.o ext_caps.o pio.o dma.o \
|
||||
cmd_v1.o cmd_v2.o \
|
||||
dat_v1.o dct_v1.o
|
||||
dat_v1.o dct_v1.o \
|
||||
hci_quirks.o
|
||||
|
@ -123,17 +123,15 @@ static enum hci_cmd_mode get_i3c_mode(struct i3c_hci *hci)
|
||||
{
|
||||
struct i3c_bus *bus = i3c_master_get_bus(&hci->master);
|
||||
|
||||
if (bus->scl_rate.i3c >= 12500000)
|
||||
return MODE_I3C_SDR0;
|
||||
if (bus->scl_rate.i3c > 8000000)
|
||||
return MODE_I3C_SDR1;
|
||||
return MODE_I3C_SDR0;
|
||||
if (bus->scl_rate.i3c > 6000000)
|
||||
return MODE_I3C_SDR2;
|
||||
return MODE_I3C_SDR1;
|
||||
if (bus->scl_rate.i3c > 4000000)
|
||||
return MODE_I3C_SDR3;
|
||||
return MODE_I3C_SDR2;
|
||||
if (bus->scl_rate.i3c > 2000000)
|
||||
return MODE_I3C_SDR4;
|
||||
return MODE_I3C_Fm_FmP;
|
||||
return MODE_I3C_SDR3;
|
||||
return MODE_I3C_SDR4;
|
||||
}
|
||||
|
||||
static enum hci_cmd_mode get_i2c_mode(struct i3c_hci *hci)
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/i3c/master.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -27,11 +26,6 @@
|
||||
* Host Controller Capabilities and Operation Registers
|
||||
*/
|
||||
|
||||
#define reg_read(r) readl(hci->base_regs + (r))
|
||||
#define reg_write(r, v) writel(v, hci->base_regs + (r))
|
||||
#define reg_set(r, v) reg_write(r, reg_read(r) | (v))
|
||||
#define reg_clear(r, v) reg_write(r, reg_read(r) & ~(v))
|
||||
|
||||
#define HCI_VERSION 0x00 /* HCI Version (in BCD) */
|
||||
|
||||
#define HC_CONTROL 0x04
|
||||
@ -152,6 +146,10 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set RESP_BUF_THLD to 0(n) to get 1(n+1) response */
|
||||
if (hci->quirks & HCI_QUIRK_RESP_BUF_THLD)
|
||||
amd_set_resp_buf_thld(hci);
|
||||
|
||||
reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE);
|
||||
DBG("HC_CONTROL = %#x", reg_read(HC_CONTROL));
|
||||
|
||||
@ -630,8 +628,8 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
|
||||
|
||||
static int i3c_hci_init(struct i3c_hci *hci)
|
||||
{
|
||||
bool size_in_dwords, mode_selector;
|
||||
u32 regval, offset;
|
||||
bool size_in_dwords;
|
||||
int ret;
|
||||
|
||||
/* Validate HCI hardware version */
|
||||
@ -753,10 +751,17 @@ static int i3c_hci_init(struct i3c_hci *hci)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mode_selector = hci->version_major > 1 ||
|
||||
(hci->version_major == 1 && hci->version_minor > 0);
|
||||
|
||||
/* Quirk for HCI_QUIRK_PIO_MODE on AMD platforms */
|
||||
if (hci->quirks & HCI_QUIRK_PIO_MODE)
|
||||
hci->RHS_regs = NULL;
|
||||
|
||||
/* Try activating DMA operations first */
|
||||
if (hci->RHS_regs) {
|
||||
reg_clear(HC_CONTROL, HC_CONTROL_PIO_MODE);
|
||||
if (reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE) {
|
||||
if (mode_selector && (reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE)) {
|
||||
dev_err(&hci->master.dev, "PIO mode is stuck\n");
|
||||
ret = -EIO;
|
||||
} else {
|
||||
@ -768,7 +773,7 @@ static int i3c_hci_init(struct i3c_hci *hci)
|
||||
/* If no DMA, try PIO */
|
||||
if (!hci->io && hci->PIO_regs) {
|
||||
reg_set(HC_CONTROL, HC_CONTROL_PIO_MODE);
|
||||
if (!(reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE)) {
|
||||
if (mode_selector && !(reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE)) {
|
||||
dev_err(&hci->master.dev, "DMA mode is stuck\n");
|
||||
ret = -EIO;
|
||||
} else {
|
||||
@ -784,6 +789,10 @@ static int i3c_hci_init(struct i3c_hci *hci)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configure OD and PP timings for AMD platforms */
|
||||
if (hci->quirks & HCI_QUIRK_OD_PP_TIMING)
|
||||
amd_set_od_pp_timing(hci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -803,6 +812,8 @@ static int i3c_hci_probe(struct platform_device *pdev)
|
||||
/* temporary for dev_printk's, to be replaced in i3c_master_register */
|
||||
hci->master.dev.init_name = dev_name(&pdev->dev);
|
||||
|
||||
hci->quirks = (unsigned long)device_get_match_data(&pdev->dev);
|
||||
|
||||
ret = i3c_hci_init(hci);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -834,12 +845,19 @@ static const __maybe_unused struct of_device_id i3c_hci_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i3c_hci_of_match);
|
||||
|
||||
static const struct acpi_device_id i3c_hci_acpi_match[] = {
|
||||
{ "AMDI5017", HCI_QUIRK_PIO_MODE | HCI_QUIRK_OD_PP_TIMING | HCI_QUIRK_RESP_BUF_THLD },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, i3c_hci_acpi_match);
|
||||
|
||||
static struct platform_driver i3c_hci_driver = {
|
||||
.probe = i3c_hci_probe,
|
||||
.remove_new = i3c_hci_remove,
|
||||
.driver = {
|
||||
.name = "mipi-i3c-hci",
|
||||
.of_match_table = of_match_ptr(i3c_hci_of_match),
|
||||
.acpi_match_table = i3c_hci_acpi_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(i3c_hci_driver);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#ifndef HCI_H
|
||||
#define HCI_H
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
/* Handy logging macro to save on line length */
|
||||
#define DBG(x, ...) pr_devel("%s: " x "\n", __func__, ##__VA_ARGS__)
|
||||
@ -26,6 +27,10 @@
|
||||
#define W2_BIT_(x) BIT((x) - 64)
|
||||
#define W3_BIT_(x) BIT((x) - 96)
|
||||
|
||||
#define reg_read(r) readl(hci->base_regs + (r))
|
||||
#define reg_write(r, v) writel(v, hci->base_regs + (r))
|
||||
#define reg_set(r, v) reg_write(r, reg_read(r) | (v))
|
||||
#define reg_clear(r, v) reg_write(r, reg_read(r) & ~(v))
|
||||
|
||||
struct hci_cmd_ops;
|
||||
|
||||
@ -135,11 +140,16 @@ struct i3c_hci_dev_data {
|
||||
|
||||
/* list of quirks */
|
||||
#define HCI_QUIRK_RAW_CCC BIT(1) /* CCC framing must be explicit */
|
||||
#define HCI_QUIRK_PIO_MODE BIT(2) /* Set PIO mode for AMD platforms */
|
||||
#define HCI_QUIRK_OD_PP_TIMING BIT(3) /* Set OD and PP timings for AMD platforms */
|
||||
#define HCI_QUIRK_RESP_BUF_THLD BIT(4) /* Set resp buf thld to 0 for AMD platforms */
|
||||
|
||||
|
||||
/* global functions */
|
||||
void mipi_i3c_hci_resume(struct i3c_hci *hci);
|
||||
void mipi_i3c_hci_pio_reset(struct i3c_hci *hci);
|
||||
void mipi_i3c_hci_dct_index_reset(struct i3c_hci *hci);
|
||||
void amd_set_od_pp_timing(struct i3c_hci *hci);
|
||||
void amd_set_resp_buf_thld(struct i3c_hci *hci);
|
||||
|
||||
#endif
|
||||
|
44
drivers/i3c/master/mipi-i3c-hci/hci_quirks.c
Normal file
44
drivers/i3c/master/mipi-i3c-hci/hci_quirks.c
Normal file
@ -0,0 +1,44 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* I3C HCI Quirks
|
||||
*
|
||||
* Copyright 2024 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
|
||||
* Guruvendra Punugupati <Guruvendra.Punugupati@amd.com>
|
||||
*/
|
||||
|
||||
#include <linux/i3c/master.h>
|
||||
#include "hci.h"
|
||||
|
||||
/* Timing registers */
|
||||
#define HCI_SCL_I3C_OD_TIMING 0x214
|
||||
#define HCI_SCL_I3C_PP_TIMING 0x218
|
||||
#define HCI_SDA_HOLD_SWITCH_DLY_TIMING 0x230
|
||||
|
||||
/* Timing values to configure 9MHz frequency */
|
||||
#define AMD_SCL_I3C_OD_TIMING 0x00cf00cf
|
||||
#define AMD_SCL_I3C_PP_TIMING 0x00160016
|
||||
|
||||
#define QUEUE_THLD_CTRL 0xD0
|
||||
|
||||
void amd_set_od_pp_timing(struct i3c_hci *hci)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
reg_write(HCI_SCL_I3C_OD_TIMING, AMD_SCL_I3C_OD_TIMING);
|
||||
reg_write(HCI_SCL_I3C_PP_TIMING, AMD_SCL_I3C_PP_TIMING);
|
||||
data = reg_read(HCI_SDA_HOLD_SWITCH_DLY_TIMING);
|
||||
/* Configure maximum TX hold time */
|
||||
data |= W0_MASK(18, 16);
|
||||
reg_write(HCI_SDA_HOLD_SWITCH_DLY_TIMING, data);
|
||||
}
|
||||
|
||||
void amd_set_resp_buf_thld(struct i3c_hci *hci)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
data = reg_read(QUEUE_THLD_CTRL);
|
||||
data = data & ~W0_MASK(15, 8);
|
||||
reg_write(QUEUE_THLD_CTRL, data);
|
||||
}
|
@ -127,6 +127,8 @@
|
||||
|
||||
/* This parameter depends on the implementation and may be tuned */
|
||||
#define SVC_I3C_FIFO_SIZE 16
|
||||
#define SVC_I3C_PPBAUD_MAX 15
|
||||
#define SVC_I3C_QUICK_I2C_CLK 4170000
|
||||
|
||||
#define SVC_I3C_EVENT_IBI BIT(0)
|
||||
#define SVC_I3C_EVENT_HOTJOIN BIT(1)
|
||||
@ -182,6 +184,7 @@ struct svc_i3c_regs_save {
|
||||
* @ibi.lock: IBI lock
|
||||
* @lock: Transfer lock, protect between IBI work thread and callbacks from master
|
||||
* @enabled_events: Bit masks for enable events (IBI, HotJoin).
|
||||
* @mctrl_config: Configuration value in SVC_I3C_MCTRL for setting speed back.
|
||||
*/
|
||||
struct svc_i3c_master {
|
||||
struct i3c_master_controller base;
|
||||
@ -212,6 +215,7 @@ struct svc_i3c_master {
|
||||
} ibi;
|
||||
struct mutex lock;
|
||||
int enabled_events;
|
||||
u32 mctrl_config;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -529,12 +533,61 @@ static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int svc_i3c_master_set_speed(struct i3c_master_controller *m,
|
||||
enum i3c_open_drain_speed speed)
|
||||
{
|
||||
struct svc_i3c_master *master = to_svc_i3c_master(m);
|
||||
struct i3c_bus *bus = i3c_master_get_bus(&master->base);
|
||||
u32 ppbaud, odbaud, odhpp, mconfig;
|
||||
unsigned long fclk_rate;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(master->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (speed) {
|
||||
case I3C_OPEN_DRAIN_SLOW_SPEED:
|
||||
fclk_rate = clk_get_rate(master->fclk);
|
||||
if (!fclk_rate) {
|
||||
ret = -EINVAL;
|
||||
goto rpm_out;
|
||||
}
|
||||
/*
|
||||
* Set 50% duty-cycle I2C speed to I3C OPEN-DRAIN mode, so the first
|
||||
* broadcast address is visible to all I2C/I3C devices on the I3C bus.
|
||||
* I3C device working as a I2C device will turn off its 50ns Spike
|
||||
* Filter to change to I3C mode.
|
||||
*/
|
||||
mconfig = master->mctrl_config;
|
||||
ppbaud = FIELD_GET(GENMASK(11, 8), mconfig);
|
||||
odhpp = 0;
|
||||
odbaud = DIV_ROUND_UP(fclk_rate, bus->scl_rate.i2c * (2 + 2 * ppbaud)) - 1;
|
||||
mconfig &= ~GENMASK(24, 16);
|
||||
mconfig |= SVC_I3C_MCONFIG_ODBAUD(odbaud) | SVC_I3C_MCONFIG_ODHPP(odhpp);
|
||||
writel(mconfig, master->regs + SVC_I3C_MCONFIG);
|
||||
break;
|
||||
case I3C_OPEN_DRAIN_NORMAL_SPEED:
|
||||
writel(master->mctrl_config, master->regs + SVC_I3C_MCONFIG);
|
||||
break;
|
||||
}
|
||||
|
||||
rpm_out:
|
||||
pm_runtime_mark_last_busy(master->dev);
|
||||
pm_runtime_put_autosuspend(master->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
|
||||
{
|
||||
struct svc_i3c_master *master = to_svc_i3c_master(m);
|
||||
struct i3c_bus *bus = i3c_master_get_bus(m);
|
||||
struct i3c_device_info info = {};
|
||||
unsigned long fclk_rate, fclk_period_ns;
|
||||
unsigned long i2c_period_ns, i2c_scl_rate, i3c_scl_rate;
|
||||
unsigned int high_period_ns, od_low_period_ns;
|
||||
u32 ppbaud, pplow, odhpp, odbaud, odstop, i2cbaud, reg;
|
||||
int ret;
|
||||
@ -555,12 +608,15 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
|
||||
}
|
||||
|
||||
fclk_period_ns = DIV_ROUND_UP(1000000000, fclk_rate);
|
||||
i2c_period_ns = DIV_ROUND_UP(1000000000, bus->scl_rate.i2c);
|
||||
i2c_scl_rate = bus->scl_rate.i2c;
|
||||
i3c_scl_rate = bus->scl_rate.i3c;
|
||||
|
||||
/*
|
||||
* Using I3C Push-Pull mode, target is 12.5MHz/80ns period.
|
||||
* Simplest configuration is using a 50% duty-cycle of 40ns.
|
||||
*/
|
||||
ppbaud = DIV_ROUND_UP(40, fclk_period_ns) - 1;
|
||||
ppbaud = DIV_ROUND_UP(fclk_rate / 2, i3c_scl_rate) - 1;
|
||||
pplow = 0;
|
||||
|
||||
/*
|
||||
@ -570,7 +626,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
|
||||
*/
|
||||
odhpp = 1;
|
||||
high_period_ns = (ppbaud + 1) * fclk_period_ns;
|
||||
odbaud = DIV_ROUND_UP(240 - high_period_ns, high_period_ns) - 1;
|
||||
odbaud = DIV_ROUND_UP(fclk_rate, SVC_I3C_QUICK_I2C_CLK * (1 + ppbaud)) - 2;
|
||||
od_low_period_ns = (odbaud + 1) * high_period_ns;
|
||||
|
||||
switch (bus->mode) {
|
||||
@ -579,20 +635,27 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
|
||||
odstop = 0;
|
||||
break;
|
||||
case I3C_BUS_MODE_MIXED_FAST:
|
||||
case I3C_BUS_MODE_MIXED_LIMITED:
|
||||
/*
|
||||
* Using I2C Fm+ mode, target is 1MHz/1000ns, the difference
|
||||
* between the high and low period does not really matter.
|
||||
*/
|
||||
i2cbaud = DIV_ROUND_UP(1000, od_low_period_ns) - 2;
|
||||
i2cbaud = DIV_ROUND_UP(i2c_period_ns, od_low_period_ns) - 2;
|
||||
odstop = 1;
|
||||
break;
|
||||
case I3C_BUS_MODE_MIXED_LIMITED:
|
||||
case I3C_BUS_MODE_MIXED_SLOW:
|
||||
/*
|
||||
* Using I2C Fm mode, target is 0.4MHz/2500ns, with the same
|
||||
* constraints as the FM+ mode.
|
||||
*/
|
||||
i2cbaud = DIV_ROUND_UP(2500, od_low_period_ns) - 2;
|
||||
/* I3C PP + I3C OP + I2C OP both use i2c clk rate */
|
||||
if (ppbaud > SVC_I3C_PPBAUD_MAX) {
|
||||
ppbaud = SVC_I3C_PPBAUD_MAX;
|
||||
pplow = DIV_ROUND_UP(fclk_rate, i3c_scl_rate) - (2 + 2 * ppbaud);
|
||||
}
|
||||
|
||||
high_period_ns = (ppbaud + 1) * fclk_period_ns;
|
||||
odhpp = 0;
|
||||
odbaud = DIV_ROUND_UP(fclk_rate, i2c_scl_rate * (2 + 2 * ppbaud)) - 1;
|
||||
|
||||
od_low_period_ns = (odbaud + 1) * high_period_ns;
|
||||
i2cbaud = DIV_ROUND_UP(i2c_period_ns, od_low_period_ns) - 2;
|
||||
odstop = 1;
|
||||
break;
|
||||
default:
|
||||
@ -611,6 +674,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
|
||||
SVC_I3C_MCONFIG_I2CBAUD(i2cbaud);
|
||||
writel(reg, master->regs + SVC_I3C_MCONFIG);
|
||||
|
||||
master->mctrl_config = reg;
|
||||
/* Master core's registration */
|
||||
ret = i3c_master_get_free_addr(m, 0);
|
||||
if (ret < 0)
|
||||
@ -1645,6 +1709,7 @@ static const struct i3c_master_controller_ops svc_i3c_master_ops = {
|
||||
.disable_ibi = svc_i3c_master_disable_ibi,
|
||||
.enable_hotjoin = svc_i3c_master_enable_hotjoin,
|
||||
.disable_hotjoin = svc_i3c_master_disable_hotjoin,
|
||||
.set_speed = svc_i3c_master_set_speed,
|
||||
};
|
||||
|
||||
static int svc_i3c_master_prepare_clks(struct svc_i3c_master *master)
|
||||
@ -1775,6 +1840,7 @@ static void svc_i3c_master_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct svc_i3c_master *master = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_work_sync(&master->hj_work);
|
||||
i3c_master_unregister(&master->base);
|
||||
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
|
@ -277,6 +277,20 @@ enum i3c_bus_mode {
|
||||
I3C_BUS_MODE_MIXED_SLOW,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum i3c_open_drain_speed - I3C open-drain speed
|
||||
* @I3C_OPEN_DRAIN_SLOW_SPEED: Slow open-drain speed for sending the first
|
||||
* broadcast address. The first broadcast address at this speed
|
||||
* will be visible to all devices on the I3C bus. I3C devices
|
||||
* working in I2C mode will turn off their spike filter when
|
||||
* switching into I3C mode.
|
||||
* @I3C_OPEN_DRAIN_NORMAL_SPEED: Normal open-drain speed in I3C bus mode.
|
||||
*/
|
||||
enum i3c_open_drain_speed {
|
||||
I3C_OPEN_DRAIN_SLOW_SPEED,
|
||||
I3C_OPEN_DRAIN_NORMAL_SPEED,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum i3c_addr_slot_status - I3C address slot status
|
||||
* @I3C_ADDR_SLOT_FREE: address is free
|
||||
@ -436,6 +450,7 @@ struct i3c_bus {
|
||||
* NULL.
|
||||
* @enable_hotjoin: enable hot join event detect.
|
||||
* @disable_hotjoin: disable hot join event detect.
|
||||
* @set_speed: adjust I3C open drain mode timing.
|
||||
*/
|
||||
struct i3c_master_controller_ops {
|
||||
int (*bus_init)(struct i3c_master_controller *master);
|
||||
@ -464,6 +479,7 @@ struct i3c_master_controller_ops {
|
||||
struct i3c_ibi_slot *slot);
|
||||
int (*enable_hotjoin)(struct i3c_master_controller *master);
|
||||
int (*disable_hotjoin)(struct i3c_master_controller *master);
|
||||
int (*set_speed)(struct i3c_master_controller *master, enum i3c_open_drain_speed speed);
|
||||
};
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user