Merge branch 'i2c/for-mergewindow' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux

Pull i2c updates from Wolfram Sang:

 - tracepoints when Linux acts as an I2C client

 - added support for AMD PSP

 - whole subsystem now uses generic_handle_irq_safe()

 - piix4 driver gained MMIO access enabling so far missed controllers
   with AMD chipsets

 - a bulk of device driver updates, refactorization, and fixes.

* 'i2c/for-mergewindow' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (61 commits)
  i2c: mux: demux-pinctrl: do not deactivate a master that is not active
  i2c: meson: Fix wrong speed use from probe
  i2c: add tracepoints for I2C slave events
  i2c: designware: Remove code duplication
  i2c: cros-ec-tunnel: Fix syntax errors in comments
  MAINTAINERS: adjust XLP9XX I2C DRIVER after removing the devicetree binding
  i2c: designware: Mark dw_i2c_plat_{suspend,resume}() as __maybe_unused
  i2c: mediatek: Add i2c compatible for Mediatek MT8168
  dt-bindings: i2c: update bindings for MT8168 SoC
  i2c: mt65xx: Simplify with clk-bulk
  i2c: i801: Drop two outdated comments
  i2c: xiic: Make bus names unique
  i2c: i801: Add support for the Process Call command
  i2c: i801: Drop useless masking in i801_access
  i2c: tegra: Add SMBus block read function
  i2c: designware: Use the i2c_mark_adapter_suspended/resumed() helpers
  i2c: designware: Lock the adapter while setting the suspended flag
  i2c: mediatek: remove redundant null check
  i2c: mediatek: modify bus speed calculation formula
  i2c: designware: Fix improper usage of readl
  ...
This commit is contained in:
Linus Torvalds 2022-03-26 12:46:08 -07:00
commit 5627ecb837
50 changed files with 1545 additions and 363 deletions

View File

@ -12,8 +12,10 @@ Required properties:
"mediatek,mt7622-i2c": for MediaTek MT7622
"mediatek,mt7623-i2c", "mediatek,mt6577-i2c": for MediaTek MT7623
"mediatek,mt7629-i2c", "mediatek,mt2712-i2c": for MediaTek MT7629
"mediatek,mt8168-i2c": for MediaTek MT8168
"mediatek,mt8173-i2c": for MediaTek MT8173
"mediatek,mt8183-i2c": for MediaTek MT8183
"mediatek,mt8186-i2c": for MediaTek MT8186
"mediatek,mt8192-i2c": for MediaTek MT8192
"mediatek,mt8195-i2c", "mediatek,mt8192-i2c": for MediaTek MT8195
"mediatek,mt8516-i2c", "mediatek,mt2712-i2c": for MediaTek MT8516

View File

@ -10,6 +10,7 @@ PROPERTIES:
"qcom,msm8996-cci"
"qcom,sdm845-cci"
"qcom,sm8250-cci"
"qcom,sm8450-cci"
- reg
Usage: required
@ -43,7 +44,8 @@ PROPERTIES:
SUBNODES:
The CCI provides I2C masters for one (msm8916) or two i2c busses (msm8996,
sdm845 and sm8250), described as subdevices named "i2c-bus@0" and "i2c-bus@1".
sdm845, sm8250 and sm8450), described as subdevices named "i2c-bus@0" and
"i2c-bus@1".
PROPERTIES:

View File

@ -0,0 +1,56 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/i2c/microchip,corei2c.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip MPFS I2C Controller Device Tree Bindings
maintainers:
- Daire McNamara <daire.mcnamara@microchip.com>
allOf:
- $ref: /schemas/i2c/i2c-controller.yaml#
properties:
compatible:
oneOf:
- items:
- const: microchip,mpfs-i2c # Microchip PolarFire SoC compatible SoCs
- const: microchip,corei2c-rtl-v7 # Microchip Fabric based i2c IP core
- const: microchip,corei2c-rtl-v7 # Microchip Fabric based i2c IP core
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
clock-frequency:
description: |
Desired I2C bus clock frequency in Hz. As only Standard and Fast
modes are supported, possible values are 100000 and 400000.
enum: [100000, 400000]
required:
- compatible
- reg
- interrupts
- clocks
unevaluatedProperties: false
examples:
- |
i2c@2010a000 {
compatible = "microchip,mpfs-i2c", "microchip,corei2c-rtl-v7";
reg = <0x2010a000 0x1000>;
clocks = <&clkcfg 15>;
interrupt-parent = <&plic>;
interrupts = <58>;
clock-frequency = <100000>;
};
...

View File

@ -49,6 +49,11 @@ properties:
- renesas,i2c-r8a779a0 # R-Car V3U
- const: renesas,rcar-gen3-i2c # R-Car Gen3 and RZ/G2
- items:
- enum:
- renesas,i2c-r8a779f0 # R-Car S4-8
- const: renesas,rcar-gen4-i2c # R-Car Gen4
reg:
maxItems: 1
@ -132,6 +137,7 @@ allOf:
enum:
- renesas,rcar-gen2-i2c
- renesas,rcar-gen3-i2c
- renesas,rcar-gen4-i2c
then:
required:
- resets

View File

@ -142,6 +142,45 @@ In robust cases the client unfortunately needs to call
acpi_dma_request_slave_chan_by_index() directly and therefore choose the
specific FixedDMA resource by its index.
Named Interrupts
================
Drivers enumerated via ACPI can have names to interrupts in the ACPI table
which can be used to get the IRQ number in the driver.
The interrupt name can be listed in _DSD as 'interrupt-names'. The names
should be listed as an array of strings which will map to the Interrupt()
resource in the ACPI table corresponding to its index.
The table below shows an example of its usage::
Device (DEV0) {
...
Name (_CRS, ResourceTemplate() {
...
Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive) {
0x20,
0x24
}
})
Name (_DSD, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package () {"interrupt-names",
Package (2) {"default", "alert"}},
}
...
})
}
The interrupt name 'default' will correspond to 0x20 in Interrupt()
resource and 'alert' to 0x24. Note that only the Interrupt() resource
is mapped and not GpioInt() or similar.
The driver can call the function - fwnode_irq_get_byname() with the fwnode
and interrupt name as arguments to get the corresponding IRQ number.
SPI serial bus support
======================

View File

@ -45,6 +45,7 @@ Supported adapters:
* Intel Jasper Lake (SOC)
* Intel Emmitsburg (PCH)
* Intel Alder Lake (PCH)
* Intel Raptor Lake (PCH)
Datasheets: Publicly available at the Intel website

View File

@ -18877,6 +18877,7 @@ SYNOPSYS DESIGNWARE I2C DRIVER
M: Jarkko Nikula <jarkko.nikula@linux.intel.com>
R: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
R: Mika Westerberg <mika.westerberg@linux.intel.com>
R: Jan Dabros <jsd@semihalf.com>
L: linux-i2c@vger.kernel.org
S: Maintained
F: drivers/i2c/busses/i2c-designware-*
@ -21520,7 +21521,6 @@ M: George Cherian <gcherian@marvell.com>
L: linux-i2c@vger.kernel.org
S: Supported
W: http://www.marvell.com
F: Documentation/devicetree/bindings/i2c/i2c-xlp9xx.txt
F: drivers/i2c/busses/i2c-xlp9xx.c
XRA1403 GPIO EXPANDER

View File

@ -232,12 +232,13 @@ static const struct acpi_device_id acpi_apd_device_ids[] = {
/* Generic apd devices */
#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE
{ "AMD0010", APD_ADDR(cz_i2c_desc) },
{ "AMDI0010", APD_ADDR(wt_i2c_desc) },
{ "AMD0020", APD_ADDR(cz_uart_desc) },
{ "AMDI0020", APD_ADDR(cz_uart_desc) },
{ "AMDI0022", APD_ADDR(cz_uart_desc) },
{ "AMD0030", },
{ "AMD0040", APD_ADDR(fch_misc_desc)},
{ "AMDI0010", APD_ADDR(wt_i2c_desc) },
{ "AMDI0019", APD_ADDR(wt_i2c_desc) },
{ "AMDI0020", APD_ADDR(cz_uart_desc) },
{ "AMDI0022", APD_ADDR(cz_uart_desc) },
{ "HYGO0010", APD_ADDR(wt_i2c_desc) },
#endif
#ifdef CONFIG_ARM64

View File

@ -935,6 +935,35 @@ void __iomem *fwnode_iomap(struct fwnode_handle *fwnode, int index)
}
EXPORT_SYMBOL(fwnode_iomap);
/**
* fwnode_irq_get_byname - Get IRQ from a fwnode using its name
* @fwnode: Pointer to the firmware node
* @name: IRQ name
*
* Description:
* Find a match to the string @name in the 'interrupt-names' string array
* in _DSD for ACPI, or of_node for Device Tree. Then get the Linux IRQ
* number of the IRQ resource corresponding to the index of the matched
* string.
*
* Return:
* Linux IRQ number on success, or negative errno otherwise.
*/
int fwnode_irq_get_byname(const struct fwnode_handle *fwnode, const char *name)
{
int index;
if (!name)
return -EINVAL;
index = fwnode_property_match_string(fwnode, "interrupt-names", name);
if (index < 0)
return index;
return fwnode_irq_get(fwnode, index);
}
EXPORT_SYMBOL(fwnode_irq_get_byname);
/**
* fwnode_graph_get_next_endpoint - Get next endpoint firmware node
* @fwnode: Pointer to the parent firmware node

View File

@ -9,6 +9,13 @@ menu "I2C Hardware Bus support"
comment "PC SMBus host controller drivers"
depends on PCI
config I2C_CCGX_UCSI
tristate
help
A common module to provide an API to instantiate UCSI device
for Cypress CCGx Type-C controller. Individual bus drivers
need to select this one on demand.
config I2C_ALI1535
tristate "ALI 1535"
depends on PCI
@ -148,6 +155,7 @@ config I2C_I801
Jasper Lake (SOC)
Emmitsburg (PCH)
Alder Lake (PCH)
Raptor Lake (PCH)
This driver can also be built as a module. If so, the module
will be called i2c-i801.
@ -245,6 +253,7 @@ config I2C_NFORCE2_S4985
config I2C_NVIDIA_GPU
tristate "NVIDIA GPU I2C controller"
depends on PCI
select I2C_CCGX_UCSI
help
If you say yes to this option, support will be included for the
NVIDIA GPU I2C controller which is used to communicate with the GPU's
@ -477,8 +486,8 @@ config I2C_BCM_KONA
config I2C_BRCMSTB
tristate "BRCM Settop/DSL I2C controller"
depends on ARCH_BCM2835 || ARCH_BRCMSTB || BMIPS_GENERIC || \
ARCH_BCM_63XX || COMPILE_TEST
depends on ARCH_BCM2835 || ARCH_BCM4908 || ARCH_BCM_63XX || \
ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
default y
help
If you say yes to this option, support will be included for the
@ -553,6 +562,17 @@ config I2C_DESIGNWARE_PLATFORM
This driver can also be built as a module. If so, the module
will be called i2c-designware-platform.
config I2C_DESIGNWARE_AMDPSP
bool "AMD PSP I2C semaphore support"
depends on X86_MSR
depends on ACPI
depends on I2C_DESIGNWARE_PLATFORM
help
This driver enables managed host access to the selected I2C bus shared
between AMD CPU and AMD PSP.
You should say Y if running on an AMD system equipped with the PSP.
config I2C_DESIGNWARE_BAYTRAIL
bool "Intel Baytrail I2C semaphore support"
depends on ACPI
@ -570,6 +590,7 @@ config I2C_DESIGNWARE_PCI
tristate "Synopsys DesignWare PCI"
depends on PCI
select I2C_DESIGNWARE_CORE
select I2C_CCGX_UCSI
help
If you say yes to this option, support will be included for the
Synopsys DesignWare I2C adapter. Only master mode is supported.

View File

@ -6,6 +6,9 @@
# ACPI drivers
obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o
# Auxiliary I2C/SMBus modules
obj-$(CONFIG_I2C_CCGX_UCSI) += i2c-ccgx-ucsi.o
# PC SMBus host controller drivers
obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o
obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o
@ -54,6 +57,7 @@ i2c-designware-core-y += i2c-designware-master.o
i2c-designware-core-$(CONFIG_I2C_DESIGNWARE_SLAVE) += i2c-designware-slave.o
obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
i2c-designware-platform-y := i2c-designware-platdrv.o
i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_AMDPSP) += i2c-designware-amdpsp.o
i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_BAYTRAIL) += i2c-designware-baytrail.o
obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
i2c-designware-pci-y := i2c-designware-pcidrv.o

View File

@ -308,11 +308,8 @@ static int amd_mp2_pci_init(struct amd_mp2_dev *privdata,
pci_set_master(pci_dev);
rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(64));
if (rc) {
rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32));
if (rc)
goto err_dma_mask;
}
if (rc)
goto err_dma_mask;
/* Set up intx irq */
writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);

View File

@ -454,18 +454,20 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
ret = clk_prepare_enable(i2c_dev->bus_clk);
if (ret) {
dev_err(&pdev->dev, "Couldn't prepare clock");
return ret;
goto err_put_exclusive_rate;
}
i2c_dev->irq = platform_get_irq(pdev, 0);
if (i2c_dev->irq < 0)
return i2c_dev->irq;
if (i2c_dev->irq < 0) {
ret = i2c_dev->irq;
goto err_disable_unprepare_clk;
}
ret = request_irq(i2c_dev->irq, bcm2835_i2c_isr, IRQF_SHARED,
dev_name(&pdev->dev), i2c_dev);
if (ret) {
dev_err(&pdev->dev, "Could not request IRQ\n");
return -ENODEV;
goto err_disable_unprepare_clk;
}
adap = &i2c_dev->adapter;
@ -489,7 +491,16 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
ret = i2c_add_adapter(adap);
if (ret)
free_irq(i2c_dev->irq, i2c_dev);
goto err_free_irq;
return 0;
err_free_irq:
free_irq(i2c_dev->irq, i2c_dev);
err_disable_unprepare_clk:
clk_disable_unprepare(i2c_dev->bus_clk);
err_put_exclusive_rate:
clk_rate_exclusive_put(i2c_dev->bus_clk);
return ret;
}

View File

@ -0,0 +1,30 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Instantiate UCSI device for Cypress CCGx Type-C controller.
* Derived from i2c-designware-pcidrv.c and i2c-nvidia-gpu.c.
*/
#include <linux/i2c.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/string.h>
#include "i2c-ccgx-ucsi.h"
struct software_node;
struct i2c_client *i2c_new_ccgx_ucsi(struct i2c_adapter *adapter, int irq,
const struct software_node *swnode)
{
struct i2c_board_info info = {};
strscpy(info.type, "ccgx-ucsi", sizeof(info.type));
info.addr = 0x08;
info.irq = irq;
info.swnode = swnode;
return i2c_new_client_device(adapter, &info);
}
EXPORT_SYMBOL_GPL(i2c_new_ccgx_ucsi);
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __I2C_CCGX_UCSI_H_
#define __I2C_CCGX_UCSI_H_
struct i2c_adapter;
struct i2c_client;
struct software_node;
struct i2c_client *i2c_new_ccgx_ucsi(struct i2c_adapter *adapter, int irq,
const struct software_node *swnode);
#endif /* __I2C_CCGX_UCSI_H_ */

View File

@ -100,15 +100,8 @@ static irqreturn_t cht_wc_i2c_adap_thread_handler(int id, void *data)
* interrupt handler as well, so running the client irq handler from
* this thread will cause things to lock up.
*/
if (reg & CHT_WC_EXTCHGRIRQ_CLIENT_IRQ) {
/*
* generic_handle_irq expects local IRQs to be disabled
* as normally it is called from interrupt context.
*/
local_irq_disable();
generic_handle_irq(adap->client_irq);
local_irq_enable();
}
if (reg & CHT_WC_EXTCHGRIRQ_CLIENT_IRQ)
generic_handle_irq_safe(adap->client_irq);
return IRQ_HANDLED;
}

View File

@ -107,7 +107,7 @@ static int ec_i2c_construct_message(u8 *buf, const struct i2c_msg i2c_msgs[],
/**
* ec_i2c_count_response - Count bytes needed for ec_i2c_parse_response
*
* @i2c_msgs: The i2c messages to to fill up.
* @i2c_msgs: The i2c messages to fill up.
* @num: The number of i2c messages expected.
*
* Returns the number of response bytes expeced.
@ -131,7 +131,7 @@ static int ec_i2c_count_response(struct i2c_msg i2c_msgs[], int num)
* We'll take the EC's response and copy it back into msgs.
*
* @buf: The buffer to parse.
* @i2c_msgs: The i2c messages to to fill up.
* @i2c_msgs: The i2c messages to fill up.
* @num: The number of i2c messages; will be modified to include the actual
* number received.
*

View File

@ -0,0 +1,388 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/i2c.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/psp-sev.h>
#include <linux/types.h>
#include <asm/msr.h>
#include "i2c-designware-core.h"
#define MSR_AMD_PSP_ADDR 0xc00110a2
#define PSP_MBOX_OFFSET 0x10570
#define PSP_CMD_TIMEOUT_US (500 * USEC_PER_MSEC)
#define PSP_I2C_REQ_BUS_CMD 0x64
#define PSP_I2C_REQ_RETRY_CNT 10
#define PSP_I2C_REQ_RETRY_DELAY_US (50 * USEC_PER_MSEC)
#define PSP_I2C_REQ_STS_OK 0x0
#define PSP_I2C_REQ_STS_BUS_BUSY 0x1
#define PSP_I2C_REQ_STS_INV_PARAM 0x3
#define PSP_MBOX_FIELDS_STS GENMASK(15, 0)
#define PSP_MBOX_FIELDS_CMD GENMASK(23, 16)
#define PSP_MBOX_FIELDS_RESERVED GENMASK(29, 24)
#define PSP_MBOX_FIELDS_RECOVERY BIT(30)
#define PSP_MBOX_FIELDS_READY BIT(31)
struct psp_req_buffer_hdr {
u32 total_size;
u32 status;
};
enum psp_i2c_req_type {
PSP_I2C_REQ_ACQUIRE,
PSP_I2C_REQ_RELEASE,
PSP_I2C_REQ_MAX
};
struct psp_i2c_req {
struct psp_req_buffer_hdr hdr;
enum psp_i2c_req_type type;
};
struct psp_mbox {
u32 cmd_fields;
u64 i2c_req_addr;
} __packed;
static DEFINE_MUTEX(psp_i2c_access_mutex);
static unsigned long psp_i2c_sem_acquired;
static void __iomem *mbox_iomem;
static u32 psp_i2c_access_count;
static bool psp_i2c_mbox_fail;
static struct device *psp_i2c_dev;
/*
* Implementation of PSP-x86 i2c-arbitration mailbox introduced for AMD Cezanne
* family of SoCs.
*/
static int psp_get_mbox_addr(unsigned long *mbox_addr)
{
unsigned long long psp_mmio;
if (rdmsrl_safe(MSR_AMD_PSP_ADDR, &psp_mmio))
return -EIO;
*mbox_addr = (unsigned long)(psp_mmio + PSP_MBOX_OFFSET);
return 0;
}
static int psp_mbox_probe(void)
{
unsigned long mbox_addr;
int ret;
ret = psp_get_mbox_addr(&mbox_addr);
if (ret)
return ret;
mbox_iomem = ioremap(mbox_addr, sizeof(struct psp_mbox));
if (!mbox_iomem)
return -ENOMEM;
return 0;
}
/* Recovery field should be equal 0 to start sending commands */
static int psp_check_mbox_recovery(struct psp_mbox __iomem *mbox)
{
u32 tmp;
tmp = readl(&mbox->cmd_fields);
return FIELD_GET(PSP_MBOX_FIELDS_RECOVERY, tmp);
}
static int psp_wait_cmd(struct psp_mbox __iomem *mbox)
{
u32 tmp, expected;
/* Expect mbox_cmd to be cleared and ready bit to be set by PSP */
expected = FIELD_PREP(PSP_MBOX_FIELDS_READY, 1);
/*
* Check for readiness of PSP mailbox in a tight loop in order to
* process further as soon as command was consumed.
*/
return readl_poll_timeout(&mbox->cmd_fields, tmp, (tmp == expected),
0, PSP_CMD_TIMEOUT_US);
}
/* Status equal to 0 means that PSP succeed processing command */
static u32 psp_check_mbox_sts(struct psp_mbox __iomem *mbox)
{
u32 cmd_reg;
cmd_reg = readl(&mbox->cmd_fields);
return FIELD_GET(PSP_MBOX_FIELDS_STS, cmd_reg);
}
static int psp_send_cmd(struct psp_i2c_req *req)
{
struct psp_mbox __iomem *mbox = mbox_iomem;
phys_addr_t req_addr;
u32 cmd_reg;
if (psp_check_mbox_recovery(mbox))
return -EIO;
if (psp_wait_cmd(mbox))
return -EBUSY;
/*
* Fill mailbox with address of command-response buffer, which will be
* used for sending i2c requests as well as reading status returned by
* PSP. Use physical address of buffer, since PSP will map this region.
*/
req_addr = __psp_pa((void *)req);
writeq(req_addr, &mbox->i2c_req_addr);
/* Write command register to trigger processing */
cmd_reg = FIELD_PREP(PSP_MBOX_FIELDS_CMD, PSP_I2C_REQ_BUS_CMD);
writel(cmd_reg, &mbox->cmd_fields);
if (psp_wait_cmd(mbox))
return -ETIMEDOUT;
if (psp_check_mbox_sts(mbox))
return -EIO;
return 0;
}
/* Helper to verify status returned by PSP */
static int check_i2c_req_sts(struct psp_i2c_req *req)
{
u32 status;
/* Status field in command-response buffer is updated by PSP */
status = READ_ONCE(req->hdr.status);
switch (status) {
case PSP_I2C_REQ_STS_OK:
return 0;
case PSP_I2C_REQ_STS_BUS_BUSY:
return -EBUSY;
case PSP_I2C_REQ_STS_INV_PARAM:
default:
return -EIO;
}
}
static int psp_send_check_i2c_req(struct psp_i2c_req *req)
{
/*
* Errors in x86-PSP i2c-arbitration protocol may occur at two levels:
* 1. mailbox communication - PSP is not operational or some IO errors
* with basic communication had happened;
* 2. i2c-requests - PSP refuses to grant i2c arbitration to x86 for too
* long.
* In order to distinguish between these two in error handling code, all
* errors on the first level (returned by psp_send_cmd) are shadowed by
* -EIO.
*/
if (psp_send_cmd(req))
return -EIO;
return check_i2c_req_sts(req);
}
static int psp_send_i2c_req(enum psp_i2c_req_type i2c_req_type)
{
struct psp_i2c_req *req;
unsigned long start;
int status, ret;
/* Allocate command-response buffer */
req = kzalloc(sizeof(*req), GFP_KERNEL);
if (!req)
return -ENOMEM;
req->hdr.total_size = sizeof(*req);
req->type = i2c_req_type;
start = jiffies;
ret = read_poll_timeout(psp_send_check_i2c_req, status,
(status != -EBUSY),
PSP_I2C_REQ_RETRY_DELAY_US,
PSP_I2C_REQ_RETRY_CNT * PSP_I2C_REQ_RETRY_DELAY_US,
0, req);
if (ret) {
dev_err(psp_i2c_dev, "Timed out waiting for PSP to %s I2C bus\n",
(i2c_req_type == PSP_I2C_REQ_ACQUIRE) ?
"release" : "acquire");
goto cleanup;
}
ret = status;
if (ret) {
dev_err(psp_i2c_dev, "PSP communication error\n");
goto cleanup;
}
dev_dbg(psp_i2c_dev, "Request accepted by PSP after %ums\n",
jiffies_to_msecs(jiffies - start));
cleanup:
if (ret) {
dev_err(psp_i2c_dev, "Assume i2c bus is for exclusive host usage\n");
psp_i2c_mbox_fail = true;
}
kfree(req);
return ret;
}
static int psp_acquire_i2c_bus(void)
{
int status;
mutex_lock(&psp_i2c_access_mutex);
/* Return early if mailbox malfunctioned */
if (psp_i2c_mbox_fail)
goto cleanup;
/*
* Simply increment usage counter and return if PSP semaphore was
* already taken by kernel.
*/
if (psp_i2c_access_count) {
psp_i2c_access_count++;
goto cleanup;
}
status = psp_send_i2c_req(PSP_I2C_REQ_ACQUIRE);
if (status)
goto cleanup;
psp_i2c_sem_acquired = jiffies;
psp_i2c_access_count++;
/*
* In case of errors with PSP arbitrator psp_i2c_mbox_fail variable is
* set above. As a consequence consecutive calls to acquire will bypass
* communication with PSP. At any case i2c bus is granted to the caller,
* thus always return success.
*/
cleanup:
mutex_unlock(&psp_i2c_access_mutex);
return 0;
}
static void psp_release_i2c_bus(void)
{
int status;
mutex_lock(&psp_i2c_access_mutex);
/* Return early if mailbox was malfunctional */
if (psp_i2c_mbox_fail)
goto cleanup;
/*
* If we are last owner of PSP semaphore, need to release aribtration
* via mailbox.
*/
psp_i2c_access_count--;
if (psp_i2c_access_count)
goto cleanup;
/* Send a release command to PSP */
status = psp_send_i2c_req(PSP_I2C_REQ_RELEASE);
if (status)
goto cleanup;
dev_dbg(psp_i2c_dev, "PSP semaphore held for %ums\n",
jiffies_to_msecs(jiffies - psp_i2c_sem_acquired));
cleanup:
mutex_unlock(&psp_i2c_access_mutex);
}
/*
* Locking methods are based on the default implementation from
* drivers/i2c/i2c-core-base.c, but with psp acquire and release operations
* added. With this in place we can ensure that i2c clients on the bus shared
* with psp are able to lock HW access to the bus for arbitrary number of
* operations - that is e.g. write-wait-read.
*/
static void i2c_adapter_dw_psp_lock_bus(struct i2c_adapter *adapter,
unsigned int flags)
{
psp_acquire_i2c_bus();
rt_mutex_lock_nested(&adapter->bus_lock, i2c_adapter_depth(adapter));
}
static int i2c_adapter_dw_psp_trylock_bus(struct i2c_adapter *adapter,
unsigned int flags)
{
int ret;
ret = rt_mutex_trylock(&adapter->bus_lock);
if (ret)
return ret;
psp_acquire_i2c_bus();
return ret;
}
static void i2c_adapter_dw_psp_unlock_bus(struct i2c_adapter *adapter,
unsigned int flags)
{
psp_release_i2c_bus();
rt_mutex_unlock(&adapter->bus_lock);
}
static const struct i2c_lock_operations i2c_dw_psp_lock_ops = {
.lock_bus = i2c_adapter_dw_psp_lock_bus,
.trylock_bus = i2c_adapter_dw_psp_trylock_bus,
.unlock_bus = i2c_adapter_dw_psp_unlock_bus,
};
int i2c_dw_amdpsp_probe_lock_support(struct dw_i2c_dev *dev)
{
int ret;
if (!dev)
return -ENODEV;
if (!(dev->flags & ARBITRATION_SEMAPHORE))
return -ENODEV;
/* Allow to bind only one instance of a driver */
if (psp_i2c_dev)
return -EEXIST;
psp_i2c_dev = dev->dev;
ret = psp_mbox_probe();
if (ret)
return ret;
dev_info(psp_i2c_dev, "I2C bus managed by AMD PSP\n");
/*
* Install global locking callbacks for adapter as well as internal i2c
* controller locks.
*/
dev->adapter.lock_ops = &i2c_dw_psp_lock_ops;
dev->acquire_lock = psp_acquire_i2c_bus;
dev->release_lock = psp_release_i2c_bus;
return 0;
}
/* Unmap area used as a mailbox with PSP */
void i2c_dw_amdpsp_remove_lock_support(struct dw_i2c_dev *dev)
{
iounmap(mbox_iomem);
}

View File

@ -12,25 +12,25 @@
#include "i2c-designware-core.h"
int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev)
int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev)
{
acpi_status status;
unsigned long long shared_host = 0;
acpi_handle handle;
if (!dev || !dev->dev)
return 0;
if (!dev)
return -ENODEV;
handle = ACPI_HANDLE(dev->dev);
if (!handle)
return 0;
return -ENODEV;
status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host);
if (ACPI_FAILURE(status))
return 0;
return -ENODEV;
if (!shared_host)
return 0;
return -ENODEV;
if (!iosf_mbi_available())
return -EPROBE_DEFER;

View File

@ -578,7 +578,12 @@ int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev)
* Try to detect the FIFO depth if not set by interface driver,
* the depth could be from 2 to 256 from HW spec.
*/
ret = i2c_dw_acquire_lock(dev);
if (ret)
return ret;
ret = regmap_read(dev->map, DW_IC_COMP_PARAM_1, &param);
i2c_dw_release_lock(dev);
if (ret)
return ret;
@ -607,6 +612,11 @@ u32 i2c_dw_func(struct i2c_adapter *adap)
void i2c_dw_disable(struct dw_i2c_dev *dev)
{
u32 dummy;
int ret;
ret = i2c_dw_acquire_lock(dev);
if (ret)
return;
/* Disable controller */
__i2c_dw_disable(dev);
@ -614,6 +624,8 @@ void i2c_dw_disable(struct dw_i2c_dev *dev)
/* Disable all interrupts */
regmap_write(dev->map, DW_IC_INTR_MASK, 0);
regmap_read(dev->map, DW_IC_CLR_INTR, &dummy);
i2c_dw_release_lock(dev);
}
void i2c_dw_disable_int(struct dw_i2c_dev *dev)

View File

@ -227,6 +227,8 @@ struct reset_control;
* @hs_lcnt: high speed LCNT value
* @acquire_lock: function to acquire a hardware lock on the bus
* @release_lock: function to release a hardware lock on the bus
* @semaphore_idx: Index of table with semaphore type attached to the bus. It's
* -1 if there is no semaphore.
* @shared_with_punit: true if this bus is shared with the SoCs PUNIT
* @disable: function to disable the controller
* @disable_int: function to disable all interrupts
@ -234,7 +236,6 @@ struct reset_control;
* @set_sda_hold_time: callback to retrieve IP specific SDA hold timing
* @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE
* @rinfo: I²C GPIO recovery information
* @suspended: set to true if the controller is suspended
*
* HCNT and LCNT parameters can be used if the platform knows more accurate
* values than the one computed based only on the input clock frequency.
@ -285,6 +286,7 @@ struct dw_i2c_dev {
u16 hs_lcnt;
int (*acquire_lock)(void);
void (*release_lock)(void);
int semaphore_idx;
bool shared_with_punit;
void (*disable)(struct dw_i2c_dev *dev);
void (*disable_int)(struct dw_i2c_dev *dev);
@ -292,11 +294,11 @@ struct dw_i2c_dev {
int (*set_sda_hold_time)(struct dw_i2c_dev *dev);
int mode;
struct i2c_bus_recovery_info rinfo;
bool suspended;
};
#define ACCESS_INTR_MASK BIT(0)
#define ACCESS_NO_IRQ_SUSPEND BIT(1)
#define ARBITRATION_SEMAPHORE BIT(2)
#define MODEL_MSCC_OCELOT BIT(8)
#define MODEL_BAIKAL_BT1 BIT(9)
@ -310,6 +312,11 @@ struct dw_i2c_dev {
#define AMD_UCSI_INTR_REG 0x474
#define AMD_UCSI_INTR_EN 0xd
struct i2c_dw_semaphore_callbacks {
int (*probe)(struct dw_i2c_dev *dev);
void (*remove)(struct dw_i2c_dev *dev);
};
int i2c_dw_init_regmap(struct dw_i2c_dev *dev);
u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset);
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset);
@ -370,9 +377,12 @@ static inline void i2c_dw_configure(struct dw_i2c_dev *dev)
}
#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL)
extern int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev);
#else
static inline int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) { return 0; }
int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev);
#endif
#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_AMDPSP)
int i2c_dw_amdpsp_probe_lock_support(struct dw_i2c_dev *dev);
void i2c_dw_amdpsp_remove_lock_support(struct dw_i2c_dev *dev);
#endif
int i2c_dw_validate_speed(struct dw_i2c_dev *dev);

View File

@ -567,11 +567,6 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
goto done_nolock;
}
if (dev_WARN_ONCE(dev->dev, dev->suspended, "Transfer while suspended\n")) {
ret = -ESHUTDOWN;
goto done_nolock;
}
reinit_completion(&dev->cmd_complete);
dev->msgs = msgs;
dev->msgs_num = num;
@ -905,7 +900,13 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev)
irq_flags = IRQF_SHARED | IRQF_COND_SUSPEND;
}
ret = i2c_dw_acquire_lock(dev);
if (ret)
return ret;
i2c_dw_disable_int(dev);
i2c_dw_release_lock(dev);
ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, irq_flags,
dev_name(dev->dev), dev);
if (ret) {

View File

@ -24,6 +24,7 @@
#include <linux/slab.h>
#include "i2c-designware-core.h"
#include "i2c-ccgx-ucsi.h"
#define DRIVER_NAME "i2c-designware-pci"
#define AMD_CLK_RATE_HZ 100000
@ -125,26 +126,6 @@ static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
return -ENODEV;
}
/*
* TODO find a better way how to deduplicate instantiation
* of USB PD slave device from nVidia GPU driver.
*/
static int navi_amd_register_client(struct dw_i2c_dev *dev)
{
struct i2c_board_info info;
memset(&info, 0, sizeof(struct i2c_board_info));
strscpy(info.type, "ccgx-ucsi", I2C_NAME_SIZE);
info.addr = 0x08;
info.irq = dev->irq;
dev->slave = i2c_new_client_device(&dev->adapter, &info);
if (IS_ERR(dev->slave))
return PTR_ERR(dev->slave);
return 0;
}
static int navi_amd_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
{
struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev);
@ -213,14 +194,28 @@ static struct dw_pci_controller dw_pci_controllers[] = {
},
};
static int __maybe_unused i2c_dw_pci_runtime_suspend(struct device *dev)
{
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
i_dev->disable(i_dev);
return 0;
}
static int __maybe_unused i2c_dw_pci_suspend(struct device *dev)
{
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
i_dev->suspended = true;
i_dev->disable(i_dev);
i2c_mark_adapter_suspended(&i_dev->adapter);
return 0;
return i2c_dw_pci_runtime_suspend(dev);
}
static int __maybe_unused i2c_dw_pci_runtime_resume(struct device *dev)
{
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
return i_dev->init(i_dev);
}
static int __maybe_unused i2c_dw_pci_resume(struct device *dev)
@ -228,14 +223,17 @@ static int __maybe_unused i2c_dw_pci_resume(struct device *dev)
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
int ret;
ret = i_dev->init(i_dev);
i_dev->suspended = false;
ret = i2c_dw_pci_runtime_resume(dev);
i2c_mark_adapter_resumed(&i_dev->adapter);
return ret;
}
static UNIVERSAL_DEV_PM_OPS(i2c_dw_pm_ops, i2c_dw_pci_suspend,
i2c_dw_pci_resume, NULL);
static const struct dev_pm_ops i2c_dw_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(i2c_dw_pci_suspend, i2c_dw_pci_resume)
SET_RUNTIME_PM_OPS(i2c_dw_pci_runtime_suspend, i2c_dw_pci_runtime_resume, NULL)
};
static int i2c_dw_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
@ -325,11 +323,10 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
}
if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) {
r = navi_amd_register_client(dev);
if (r) {
dev_err(dev->dev, "register client failed with %d\n", r);
return r;
}
dev->slave = i2c_new_ccgx_ucsi(&dev->adapter, dev->irq, NULL);
if (IS_ERR(dev->slave))
return dev_err_probe(dev->dev, PTR_ERR(dev->slave),
"register UCSI failed\n");
}
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);

View File

@ -50,6 +50,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = {
{ "808622C1", ACCESS_NO_IRQ_SUSPEND },
{ "AMD0010", ACCESS_INTR_MASK },
{ "AMDI0010", ACCESS_INTR_MASK },
{ "AMDI0019", ACCESS_INTR_MASK | ARBITRATION_SEMAPHORE },
{ "AMDI0510", 0 },
{ "APMC0D0F", 0 },
{ "HISI02A1", 0 },
@ -204,6 +205,63 @@ static const struct dmi_system_id dw_i2c_hwmon_class_dmi[] = {
{ } /* terminate list */
};
static const struct i2c_dw_semaphore_callbacks i2c_dw_semaphore_cb_table[] = {
#ifdef CONFIG_I2C_DESIGNWARE_BAYTRAIL
{
.probe = i2c_dw_baytrail_probe_lock_support,
},
#endif
#ifdef CONFIG_I2C_DESIGNWARE_AMDPSP
{
.probe = i2c_dw_amdpsp_probe_lock_support,
.remove = i2c_dw_amdpsp_remove_lock_support,
},
#endif
{}
};
static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev)
{
const struct i2c_dw_semaphore_callbacks *ptr;
int i = 0;
int ret;
ptr = i2c_dw_semaphore_cb_table;
dev->semaphore_idx = -1;
while (ptr->probe) {
ret = ptr->probe(dev);
if (ret) {
/*
* If there is no semaphore device attached to this
* controller, we shouldn't abort general i2c_controller
* probe.
*/
if (ret != -ENODEV)
return ret;
i++;
ptr++;
continue;
}
dev->semaphore_idx = i;
break;
}
return 0;
}
static void i2c_dw_remove_lock_support(struct dw_i2c_dev *dev)
{
if (dev->semaphore_idx < 0)
return;
if (i2c_dw_semaphore_cb_table[dev->semaphore_idx].remove)
i2c_dw_semaphore_cb_table[dev->semaphore_idx].remove(dev);
}
static int dw_i2c_plat_probe(struct platform_device *pdev)
{
struct i2c_adapter *adap;
@ -334,6 +392,8 @@ static int dw_i2c_plat_remove(struct platform_device *pdev)
pm_runtime_put_sync(&pdev->dev);
dw_i2c_plat_pm_cleanup(dev);
i2c_dw_remove_lock_support(dev);
reset_control_assert(dev->rst);
return 0;
@ -368,12 +428,10 @@ static void dw_i2c_plat_complete(struct device *dev)
#endif
#ifdef CONFIG_PM
static int dw_i2c_plat_suspend(struct device *dev)
static int dw_i2c_plat_runtime_suspend(struct device *dev)
{
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
i_dev->suspended = true;
if (i_dev->shared_with_punit)
return 0;
@ -383,7 +441,16 @@ static int dw_i2c_plat_suspend(struct device *dev)
return 0;
}
static int dw_i2c_plat_resume(struct device *dev)
static int __maybe_unused dw_i2c_plat_suspend(struct device *dev)
{
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
i2c_mark_adapter_suspended(&i_dev->adapter);
return dw_i2c_plat_runtime_suspend(dev);
}
static int dw_i2c_plat_runtime_resume(struct device *dev)
{
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
@ -391,7 +458,16 @@ static int dw_i2c_plat_resume(struct device *dev)
i2c_dw_prepare_clk(i_dev, true);
i_dev->init(i_dev);
i_dev->suspended = false;
return 0;
}
static int __maybe_unused dw_i2c_plat_resume(struct device *dev)
{
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
dw_i2c_plat_runtime_resume(dev);
i2c_mark_adapter_resumed(&i_dev->adapter);
return 0;
}
@ -400,7 +476,7 @@ static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
.prepare = dw_i2c_plat_prepare,
.complete = dw_i2c_plat_complete,
SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume)
SET_RUNTIME_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume, NULL)
SET_RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend, dw_i2c_plat_runtime_resume, NULL)
};
#define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)

View File

@ -75,6 +75,7 @@
* Alder Lake-S (PCH) 0x7aa3 32 hard yes yes yes
* Alder Lake-P (PCH) 0x51a3 32 hard yes yes yes
* Alder Lake-M (PCH) 0x54a3 32 hard yes yes yes
* Raptor Lake-S (PCH) 0x7a23 32 hard yes yes yes
*
* Features supported by this driver:
* Software PEC no
@ -165,7 +166,7 @@
#define I801_BYTE 0x04
#define I801_BYTE_DATA 0x08
#define I801_WORD_DATA 0x0C
#define I801_PROC_CALL 0x10 /* unimplemented */
#define I801_PROC_CALL 0x10
#define I801_BLOCK_DATA 0x14
#define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */
#define I801_BLOCK_PROC_CALL 0x1C
@ -228,6 +229,7 @@
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS 0x51a3
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS 0x54a3
#define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4
#define PCI_DEVICE_ID_INTEL_RAPTOR_LAKE_S_SMBUS 0x7a23
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS 0x7aa3
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS 0x8ca2
@ -362,9 +364,6 @@ static int i801_check_post(struct i801_priv *priv, int status)
/*
* If the SMBus is still busy, we give up
* Note: This timeout condition only happens when using polling
* transactions. For interrupt operation, NAK/timeout is indicated by
* DEV_ERR.
*/
if (unlikely(status < 0)) {
dev_err(&priv->pci_dev->dev, "Transaction timeout\n");
@ -473,8 +472,6 @@ static int i801_transaction(struct i801_priv *priv, int xact)
return i801_check_post(priv, result ? priv->status : -ETIMEDOUT);
}
/* the current contents of SMBHSTCNT can be overwritten, since PEC,
* SMBSCMD are passed in xact */
outb_p(xact | SMBHSTCNT_START, SMBHSTCNT(priv));
status = i801_wait_intr(priv);
@ -790,7 +787,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
{
int hwpec;
int block = 0;
int ret = 0, xact = 0;
int ret, xact;
struct i801_priv *priv = i2c_get_adapdata(adap);
mutex_lock(&priv->acpi_lock);
@ -836,6 +833,14 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
}
xact = I801_WORD_DATA;
break;
case I2C_SMBUS_PROC_CALL:
outb_p((addr & 0x7f) << 1, SMBHSTADD(priv));
outb_p(command, SMBHSTCMD(priv));
outb_p(data->word & 0xff, SMBHSTDAT0(priv));
outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
xact = I801_PROC_CALL;
read_write = I2C_SMBUS_READ;
break;
case I2C_SMBUS_BLOCK_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD(priv));
@ -902,12 +907,13 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
goto out;
switch (xact & 0x7f) {
switch (xact) {
case I801_BYTE: /* Result put in SMBHSTDAT0 */
case I801_BYTE_DATA:
data->byte = inb_p(SMBHSTDAT0(priv));
break;
case I801_WORD_DATA:
case I801_PROC_CALL:
data->word = inb_p(SMBHSTDAT0(priv)) +
(inb_p(SMBHSTDAT1(priv)) << 8);
break;
@ -933,6 +939,7 @@ static u32 i801_func(struct i2c_adapter *adapter)
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_PROC_CALL |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
((priv->features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) |
((priv->features & FEATURE_BLOCK_PROC) ?
@ -1041,6 +1048,7 @@ static const struct pci_device_id i801_ids[] = {
{ PCI_DEVICE_DATA(INTEL, ALDER_LAKE_S_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
{ PCI_DEVICE_DATA(INTEL, ALDER_LAKE_P_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
{ PCI_DEVICE_DATA(INTEL, ALDER_LAKE_M_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
{ PCI_DEVICE_DATA(INTEL, RAPTOR_LAKE_S_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
{ 0, }
};

View File

@ -465,18 +465,18 @@ static int meson_i2c_probe(struct platform_device *pdev)
*/
meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0);
ret = i2c_add_adapter(&i2c->adap);
if (ret < 0) {
clk_disable_unprepare(i2c->clk);
return ret;
}
/* Disable filtering */
meson_i2c_set_mask(i2c, REG_SLAVE_ADDR,
REG_SLV_SDA_FILTER | REG_SLV_SCL_FILTER, 0);
meson_i2c_set_clk_div(i2c, timings.bus_freq_hz);
ret = i2c_add_adapter(&i2c->adap);
if (ret < 0) {
clk_disable_unprepare(i2c->clk);
return ret;
}
return 0;
}

View File

@ -67,11 +67,12 @@
#define MAX_SAMPLE_CNT_DIV 8
#define MAX_STEP_CNT_DIV 64
#define MAX_CLOCK_DIV 256
#define MAX_CLOCK_DIV_8BITS 256
#define MAX_CLOCK_DIV_5BITS 32
#define MAX_HS_STEP_CNT_DIV 8
#define I2C_STANDARD_MODE_BUFFER (1000 / 2)
#define I2C_FAST_MODE_BUFFER (300 / 2)
#define I2C_FAST_MODE_PLUS_BUFFER (20 / 2)
#define I2C_STANDARD_MODE_BUFFER (1000 / 3)
#define I2C_FAST_MODE_BUFFER (300 / 3)
#define I2C_FAST_MODE_PLUS_BUFFER (20 / 3)
#define I2C_CONTROL_RS (0x1 << 1)
#define I2C_CONTROL_DMA_EN (0x1 << 2)
@ -85,6 +86,27 @@
#define I2C_DRV_NAME "i2c-mt65xx"
/**
* enum i2c_mt65xx_clks - Clocks enumeration for MT65XX I2C
*
* @I2C_MT65XX_CLK_MAIN: main clock for i2c bus
* @I2C_MT65XX_CLK_DMA: DMA clock for i2c via DMA
* @I2C_MT65XX_CLK_PMIC: PMIC clock for i2c from PMIC
* @I2C_MT65XX_CLK_ARB: Arbitrator clock for i2c
* @I2C_MT65XX_CLK_MAX: Number of supported clocks
*/
enum i2c_mt65xx_clks {
I2C_MT65XX_CLK_MAIN = 0,
I2C_MT65XX_CLK_DMA,
I2C_MT65XX_CLK_PMIC,
I2C_MT65XX_CLK_ARB,
I2C_MT65XX_CLK_MAX
};
static const char * const i2c_mt65xx_clk_ids[I2C_MT65XX_CLK_MAX] = {
"main", "dma", "pmic", "arb"
};
enum DMA_REGS_OFFSET {
OFFSET_INT_FLAG = 0x0,
OFFSET_INT_EN = 0x04,
@ -243,10 +265,7 @@ struct mtk_i2c {
/* set in i2c probe */
void __iomem *base; /* i2c base addr */
void __iomem *pdmabase; /* dma base address*/
struct clk *clk_main; /* main clock for i2c bus */
struct clk *clk_dma; /* DMA clock for i2c via DMA */
struct clk *clk_pmic; /* PMIC clock for i2c from PMIC */
struct clk *clk_arb; /* Arbitrator clock for i2c */
struct clk_bulk_data clocks[I2C_MT65XX_CLK_MAX]; /* clocks for i2c */
bool have_pmic; /* can use i2c pins from PMIC */
bool use_push_pull; /* IO config push-pull mode */
@ -370,6 +389,19 @@ static const struct mtk_i2c_compatible mt7622_compat = {
.max_dma_support = 32,
};
static const struct mtk_i2c_compatible mt8168_compat = {
.regs = mt_i2c_regs_v1,
.pmic_i2c = 0,
.dcm = 1,
.auto_restart = 1,
.aux_len_reg = 1,
.timing_adjust = 1,
.dma_sync = 1,
.ltiming_adjust = 0,
.apdma_sync = 0,
.max_dma_support = 33,
};
static const struct mtk_i2c_compatible mt8173_compat = {
.regs = mt_i2c_regs_v1,
.pmic_i2c = 0,
@ -397,6 +429,19 @@ static const struct mtk_i2c_compatible mt8183_compat = {
.max_dma_support = 33,
};
static const struct mtk_i2c_compatible mt8186_compat = {
.regs = mt_i2c_regs_v2,
.pmic_i2c = 0,
.dcm = 0,
.auto_restart = 1,
.aux_len_reg = 1,
.timing_adjust = 1,
.dma_sync = 0,
.ltiming_adjust = 1,
.apdma_sync = 0,
.max_dma_support = 36,
};
static const struct mtk_i2c_compatible mt8192_compat = {
.quirks = &mt8183_i2c_quirks,
.regs = mt_i2c_regs_v2,
@ -416,8 +461,10 @@ static const struct of_device_id mtk_i2c_of_match[] = {
{ .compatible = "mediatek,mt6577-i2c", .data = &mt6577_compat },
{ .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat },
{ .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat },
{ .compatible = "mediatek,mt8168-i2c", .data = &mt8168_compat },
{ .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat },
{ .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat },
{ .compatible = "mediatek,mt8186-i2c", .data = &mt8186_compat },
{ .compatible = "mediatek,mt8192-i2c", .data = &mt8192_compat },
{}
};
@ -434,55 +481,6 @@ static void mtk_i2c_writew(struct mtk_i2c *i2c, u16 val,
writew(val, i2c->base + i2c->dev_comp->regs[reg]);
}
static int mtk_i2c_clock_enable(struct mtk_i2c *i2c)
{
int ret;
ret = clk_prepare_enable(i2c->clk_dma);
if (ret)
return ret;
ret = clk_prepare_enable(i2c->clk_main);
if (ret)
goto err_main;
if (i2c->have_pmic) {
ret = clk_prepare_enable(i2c->clk_pmic);
if (ret)
goto err_pmic;
}
if (i2c->clk_arb) {
ret = clk_prepare_enable(i2c->clk_arb);
if (ret)
goto err_arb;
}
return 0;
err_arb:
if (i2c->have_pmic)
clk_disable_unprepare(i2c->clk_pmic);
err_pmic:
clk_disable_unprepare(i2c->clk_main);
err_main:
clk_disable_unprepare(i2c->clk_dma);
return ret;
}
static void mtk_i2c_clock_disable(struct mtk_i2c *i2c)
{
if (i2c->clk_arb)
clk_disable_unprepare(i2c->clk_arb);
if (i2c->have_pmic)
clk_disable_unprepare(i2c->clk_pmic);
clk_disable_unprepare(i2c->clk_main);
clk_disable_unprepare(i2c->clk_dma);
}
static void mtk_i2c_init_hw(struct mtk_i2c *i2c)
{
u16 control_reg;
@ -590,6 +588,31 @@ static int mtk_i2c_max_step_cnt(unsigned int target_speed)
return MAX_STEP_CNT_DIV;
}
static int mtk_i2c_get_clk_div_restri(struct mtk_i2c *i2c,
unsigned int sample_cnt)
{
int clk_div_restri = 0;
if (i2c->dev_comp->ltiming_adjust == 0)
return 0;
if (sample_cnt == 1) {
if (i2c->ac_timing.inter_clk_div == 0)
clk_div_restri = 0;
else
clk_div_restri = 1;
} else {
if (i2c->ac_timing.inter_clk_div == 0)
clk_div_restri = -1;
else if (i2c->ac_timing.inter_clk_div == 1)
clk_div_restri = 0;
else
clk_div_restri = 1;
}
return clk_div_restri;
}
/*
* Check and Calculate i2c ac-timing
*
@ -718,6 +741,7 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src,
unsigned int best_mul;
unsigned int cnt_mul;
int ret = -EINVAL;
int clk_div_restri = 0;
if (target_speed > I2C_MAX_HIGH_SPEED_MODE_FREQ)
target_speed = I2C_MAX_HIGH_SPEED_MODE_FREQ;
@ -735,7 +759,8 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src,
* optimizing for sample_cnt * step_cnt being minimal
*/
for (sample_cnt = 1; sample_cnt <= MAX_SAMPLE_CNT_DIV; sample_cnt++) {
step_cnt = DIV_ROUND_UP(opt_div, sample_cnt);
clk_div_restri = mtk_i2c_get_clk_div_restri(i2c, sample_cnt);
step_cnt = DIV_ROUND_UP(opt_div + clk_div_restri, sample_cnt);
cnt_mul = step_cnt * sample_cnt;
if (step_cnt > max_step_cnt)
continue;
@ -749,7 +774,7 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src,
best_mul = cnt_mul;
base_sample_cnt = sample_cnt;
base_step_cnt = step_cnt;
if (best_mul == opt_div)
if (best_mul == (opt_div + clk_div_restri))
break;
}
}
@ -760,7 +785,8 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src,
sample_cnt = base_sample_cnt;
step_cnt = base_step_cnt;
if ((clk_src / (2 * sample_cnt * step_cnt)) > target_speed) {
if ((clk_src / (2 * (sample_cnt * step_cnt - clk_div_restri))) >
target_speed) {
/* In this case, hardware can't support such
* low i2c_bus_freq
*/
@ -789,13 +815,16 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
target_speed = i2c->speed_hz;
parent_clk /= i2c->clk_src_div;
if (i2c->dev_comp->timing_adjust)
max_clk_div = MAX_CLOCK_DIV;
if (i2c->dev_comp->timing_adjust && i2c->dev_comp->ltiming_adjust)
max_clk_div = MAX_CLOCK_DIV_5BITS;
else if (i2c->dev_comp->timing_adjust)
max_clk_div = MAX_CLOCK_DIV_8BITS;
else
max_clk_div = 1;
for (clk_div = 1; clk_div <= max_clk_div; clk_div++) {
clk_src = parent_clk / clk_div;
i2c->ac_timing.inter_clk_div = clk_div - 1;
if (target_speed > I2C_MAX_FAST_MODE_PLUS_FREQ) {
/* Set master code speed register */
@ -842,7 +871,6 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
break;
}
i2c->ac_timing.inter_clk_div = clk_div - 1;
return 0;
}
@ -1149,7 +1177,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
int left_num = num;
struct mtk_i2c *i2c = i2c_get_adapdata(adap);
ret = mtk_i2c_clock_enable(i2c);
ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks);
if (ret)
return ret;
@ -1203,7 +1231,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
ret = num;
err_exit:
mtk_i2c_clock_disable(i2c);
clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks);
return ret;
}
@ -1281,9 +1309,8 @@ static int mtk_i2c_probe(struct platform_device *pdev)
{
int ret = 0;
struct mtk_i2c *i2c;
struct clk *clk;
struct resource *res;
int irq;
int i, irq, speed_clk;
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
@ -1329,35 +1356,42 @@ static int mtk_i2c_probe(struct platform_device *pdev)
if (i2c->have_pmic && !i2c->dev_comp->pmic_i2c)
return -EINVAL;
i2c->clk_main = devm_clk_get(&pdev->dev, "main");
if (IS_ERR(i2c->clk_main)) {
/* Fill in clk-bulk IDs */
for (i = 0; i < I2C_MT65XX_CLK_MAX; i++)
i2c->clocks[i].id = i2c_mt65xx_clk_ids[i];
/* Get clocks one by one, some may be optional */
i2c->clocks[I2C_MT65XX_CLK_MAIN].clk = devm_clk_get(&pdev->dev, "main");
if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_MAIN].clk)) {
dev_err(&pdev->dev, "cannot get main clock\n");
return PTR_ERR(i2c->clk_main);
return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_MAIN].clk);
}
i2c->clk_dma = devm_clk_get(&pdev->dev, "dma");
if (IS_ERR(i2c->clk_dma)) {
i2c->clocks[I2C_MT65XX_CLK_DMA].clk = devm_clk_get(&pdev->dev, "dma");
if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_DMA].clk)) {
dev_err(&pdev->dev, "cannot get dma clock\n");
return PTR_ERR(i2c->clk_dma);
return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_DMA].clk);
}
i2c->clk_arb = devm_clk_get(&pdev->dev, "arb");
if (IS_ERR(i2c->clk_arb))
i2c->clk_arb = NULL;
i2c->clocks[I2C_MT65XX_CLK_ARB].clk = devm_clk_get_optional(&pdev->dev, "arb");
if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_ARB].clk))
return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_ARB].clk);
clk = i2c->clk_main;
if (i2c->have_pmic) {
i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic");
if (IS_ERR(i2c->clk_pmic)) {
i2c->clocks[I2C_MT65XX_CLK_PMIC].clk = devm_clk_get(&pdev->dev, "pmic");
if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_PMIC].clk)) {
dev_err(&pdev->dev, "cannot get pmic clock\n");
return PTR_ERR(i2c->clk_pmic);
return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_PMIC].clk);
}
clk = i2c->clk_pmic;
speed_clk = I2C_MT65XX_CLK_PMIC;
} else {
i2c->clocks[I2C_MT65XX_CLK_PMIC].clk = NULL;
speed_clk = I2C_MT65XX_CLK_MAIN;
}
strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name));
ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk));
ret = mtk_i2c_set_speed(i2c, clk_get_rate(i2c->clocks[speed_clk].clk));
if (ret) {
dev_err(&pdev->dev, "Failed to set the speed.\n");
return -EINVAL;
@ -1372,13 +1406,13 @@ static int mtk_i2c_probe(struct platform_device *pdev)
}
}
ret = mtk_i2c_clock_enable(i2c);
ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks);
if (ret) {
dev_err(&pdev->dev, "clock enable failed!\n");
return ret;
}
mtk_i2c_init_hw(i2c);
mtk_i2c_clock_disable(i2c);
clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks);
ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq,
IRQF_NO_SUSPEND | IRQF_TRIGGER_NONE,
@ -1423,7 +1457,7 @@ static int mtk_i2c_resume_noirq(struct device *dev)
int ret;
struct mtk_i2c *i2c = dev_get_drvdata(dev);
ret = mtk_i2c_clock_enable(i2c);
ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks);
if (ret) {
dev_err(dev, "clock enable failed!\n");
return ret;
@ -1431,7 +1465,7 @@ static int mtk_i2c_resume_noirq(struct device *dev)
mtk_i2c_init_hw(i2c);
mtk_i2c_clock_disable(i2c);
clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks);
i2c_mark_adapter_resumed(&i2c->adap);

View File

@ -781,7 +781,7 @@ static void npcm_i2c_set_fifo(struct npcm_i2c *bus, int nread, int nwrite)
/*
* if we are about to read the first byte in blk rd mode,
* don't NACK it. If slave returns zero size HW can't NACK
* it immidiattly, it will read extra byte and then NACK.
* it immediately, it will read extra byte and then NACK.
*/
if (bus->rd_ind == 0 && bus->read_block_use) {
/* set fifo to read one byte, no last: */
@ -981,7 +981,7 @@ static void npcm_i2c_slave_xmit(struct npcm_i2c *bus, u16 nwrite,
/*
* npcm_i2c_slave_wr_buf_sync:
* currently slave IF only supports single byte operations.
* in order to utilyze the npcm HW FIFO, the driver will ask for 16 bytes
* in order to utilize the npcm HW FIFO, the driver will ask for 16 bytes
* at a time, pack them in buffer, and then transmit them all together
* to the FIFO and onward to the bus.
* NACK on read will be once reached to bus->adap->quirks->max_read_len.
@ -1175,7 +1175,7 @@ static irqreturn_t npcm_i2c_int_slave_handler(struct npcm_i2c *bus)
/*
* the i2c module can response to 10 own SA.
* check which one was addressed by the master.
* repond to the first one.
* respond to the first one.
*/
addr = ((i2ccst3 & 0x07) << 7) |
(i2ccst2 & 0x7F);
@ -1753,8 +1753,8 @@ static void npcm_i2c_recovery_init(struct i2c_adapter *_adap)
/*
* npcm i2c HW allows direct reading of SCL and SDA.
* However, it does not support setting SCL and SDA directly.
* The recovery function can togle SCL when SDA is low (but not set)
* Getter functions used internally, and can be used externaly.
* The recovery function can toggle SCL when SDA is low (but not set)
* Getter functions used internally, and can be used externally.
*/
rinfo->get_scl = npcm_i2c_get_SCL;
rinfo->get_sda = npcm_i2c_get_SDA;
@ -1768,10 +1768,10 @@ static void npcm_i2c_recovery_init(struct i2c_adapter *_adap)
/*
* npcm_i2c_init_clk: init HW timing parameters.
* NPCM7XX i2c module timing parameters are depenent on module core clk (APB)
* NPCM7XX i2c module timing parameters are dependent on module core clk (APB)
* and bus frequency.
* 100kHz bus requires tSCL = 4 * SCLFRQ * tCLK. LT and HT are simetric.
* 400kHz bus requires assymetric HT and LT. A different equation is recomended
* 100kHz bus requires tSCL = 4 * SCLFRQ * tCLK. LT and HT are symmetric.
* 400kHz bus requires asymmetric HT and LT. A different equation is recommended
* by the HW designer, given core clock range (equations in comments below).
*
*/

View File

@ -17,6 +17,8 @@
#include <asm/unaligned.h>
#include "i2c-ccgx-ucsi.h"
/* I2C definitions */
#define I2C_MST_CNTL 0x00
#define I2C_MST_CNTL_GEN_START BIT(0)
@ -266,54 +268,32 @@ static const struct software_node ccgx_node = {
.properties = ccgx_props,
};
static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq)
{
i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev,
sizeof(*i2cd->gpu_ccgx_ucsi),
GFP_KERNEL);
if (!i2cd->gpu_ccgx_ucsi)
return -ENOMEM;
strlcpy(i2cd->gpu_ccgx_ucsi->type, "ccgx-ucsi",
sizeof(i2cd->gpu_ccgx_ucsi->type));
i2cd->gpu_ccgx_ucsi->addr = 0x8;
i2cd->gpu_ccgx_ucsi->irq = irq;
i2cd->gpu_ccgx_ucsi->swnode = &ccgx_node;
i2cd->ccgx_client = i2c_new_client_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi);
return PTR_ERR_OR_ZERO(i2cd->ccgx_client);
}
static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct device *dev = &pdev->dev;
struct gpu_i2c_dev *i2cd;
int status;
i2cd = devm_kzalloc(&pdev->dev, sizeof(*i2cd), GFP_KERNEL);
i2cd = devm_kzalloc(dev, sizeof(*i2cd), GFP_KERNEL);
if (!i2cd)
return -ENOMEM;
i2cd->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, i2cd);
i2cd->dev = dev;
dev_set_drvdata(dev, i2cd);
status = pcim_enable_device(pdev);
if (status < 0) {
dev_err(&pdev->dev, "pcim_enable_device failed %d\n", status);
return status;
}
if (status < 0)
return dev_err_probe(dev, status, "pcim_enable_device failed\n");
pci_set_master(pdev);
i2cd->regs = pcim_iomap(pdev, 0, 0);
if (!i2cd->regs) {
dev_err(&pdev->dev, "pcim_iomap failed\n");
return -ENOMEM;
}
if (!i2cd->regs)
return dev_err_probe(dev, -ENOMEM, "pcim_iomap failed\n");
status = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
if (status < 0) {
dev_err(&pdev->dev, "pci_alloc_irq_vectors err %d\n", status);
return status;
}
if (status < 0)
return dev_err_probe(dev, status, "pci_alloc_irq_vectors err\n");
gpu_enable_i2c_bus(i2cd);
@ -323,21 +303,21 @@ static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id)
sizeof(i2cd->adapter.name));
i2cd->adapter.algo = &gpu_i2c_algorithm;
i2cd->adapter.quirks = &gpu_i2c_quirks;
i2cd->adapter.dev.parent = &pdev->dev;
i2cd->adapter.dev.parent = dev;
status = i2c_add_adapter(&i2cd->adapter);
if (status < 0)
goto free_irq_vectors;
status = gpu_populate_client(i2cd, pdev->irq);
if (status < 0) {
dev_err(&pdev->dev, "gpu_populate_client failed %d\n", status);
i2cd->ccgx_client = i2c_new_ccgx_ucsi(&i2cd->adapter, pdev->irq, &ccgx_node);
if (IS_ERR(i2cd->ccgx_client)) {
status = dev_err_probe(dev, PTR_ERR(i2cd->ccgx_client), "register UCSI failed\n");
goto del_adapter;
}
pm_runtime_set_autosuspend_delay(&pdev->dev, 3000);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
pm_runtime_allow(&pdev->dev);
pm_runtime_set_autosuspend_delay(dev, 3000);
pm_runtime_use_autosuspend(dev);
pm_runtime_put_autosuspend(dev);
pm_runtime_allow(dev);
return 0;
@ -350,7 +330,7 @@ free_irq_vectors:
static void gpu_i2c_remove(struct pci_dev *pdev)
{
struct gpu_i2c_dev *i2cd = dev_get_drvdata(&pdev->dev);
struct gpu_i2c_dev *i2cd = pci_get_drvdata(pdev);
pm_runtime_get_noresume(i2cd->dev);
i2c_del_adapter(&i2cd->adapter);

View File

@ -333,7 +333,6 @@ int pasemi_i2c_common_probe(struct pasemi_smbus *smbus)
smbus->adapter.owner = THIS_MODULE;
snprintf(smbus->adapter.name, sizeof(smbus->adapter.name),
"PA Semi SMBus adapter (%s)", dev_name(smbus->dev));
smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
smbus->adapter.algo = &smbus_algorithm;
smbus->adapter.algo_data = smbus;

View File

@ -56,6 +56,7 @@ static int pasemi_smb_pci_probe(struct pci_dev *dev,
if (!smbus->ioaddr)
return -EBUSY;
smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
error = pasemi_i2c_common_probe(smbus);
if (error)
return error;

View File

@ -77,6 +77,7 @@
/* SB800 constants */
#define SB800_PIIX4_SMB_IDX 0xcd6
#define SB800_PIIX4_SMB_MAP_SIZE 2
#define KERNCZ_IMC_IDX 0x3e
#define KERNCZ_IMC_DATA 0x3f
@ -97,6 +98,9 @@
#define SB800_PIIX4_PORT_IDX_MASK_KERNCZ 0x18
#define SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ 3
#define SB800_PIIX4_FCH_PM_ADDR 0xFED80300
#define SB800_PIIX4_FCH_PM_SIZE 8
/* insmod parameters */
/* If force is set to anything different from 0, we forcibly enable the
@ -155,6 +159,12 @@ static const char *piix4_main_port_names_sb800[PIIX4_MAX_ADAPTERS] = {
};
static const char *piix4_aux_port_name_sb800 = " port 1";
struct sb800_mmio_cfg {
void __iomem *addr;
struct resource *res;
bool use_mmio;
};
struct i2c_piix4_adapdata {
unsigned short smba;
@ -162,8 +172,75 @@ struct i2c_piix4_adapdata {
bool sb800_main;
bool notify_imc;
u8 port; /* Port number, shifted */
struct sb800_mmio_cfg mmio_cfg;
};
static int piix4_sb800_region_request(struct device *dev,
struct sb800_mmio_cfg *mmio_cfg)
{
if (mmio_cfg->use_mmio) {
struct resource *res;
void __iomem *addr;
res = request_mem_region_muxed(SB800_PIIX4_FCH_PM_ADDR,
SB800_PIIX4_FCH_PM_SIZE,
"sb800_piix4_smb");
if (!res) {
dev_err(dev,
"SMBus base address memory region 0x%x already in use.\n",
SB800_PIIX4_FCH_PM_ADDR);
return -EBUSY;
}
addr = ioremap(SB800_PIIX4_FCH_PM_ADDR,
SB800_PIIX4_FCH_PM_SIZE);
if (!addr) {
release_resource(res);
dev_err(dev, "SMBus base address mapping failed.\n");
return -ENOMEM;
}
mmio_cfg->res = res;
mmio_cfg->addr = addr;
return 0;
}
if (!request_muxed_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE,
"sb800_piix4_smb")) {
dev_err(dev,
"SMBus base address index region 0x%x already in use.\n",
SB800_PIIX4_SMB_IDX);
return -EBUSY;
}
return 0;
}
static void piix4_sb800_region_release(struct device *dev,
struct sb800_mmio_cfg *mmio_cfg)
{
if (mmio_cfg->use_mmio) {
iounmap(mmio_cfg->addr);
release_resource(mmio_cfg->res);
return;
}
release_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE);
}
static bool piix4_sb800_use_mmio(struct pci_dev *PIIX4_dev)
{
/*
* cd6h/cd7h port I/O accesses can be disabled on AMD processors
* w/ SMBus PCI revision ID 0x51 or greater. MMIO is supported on
* the same processors and is the recommended access method.
*/
return (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD &&
PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
PIIX4_dev->revision >= 0x51);
}
static int piix4_setup(struct pci_dev *PIIX4_dev,
const struct pci_device_id *id)
{
@ -263,12 +340,61 @@ static int piix4_setup(struct pci_dev *PIIX4_dev,
return piix4_smba;
}
static int piix4_setup_sb800_smba(struct pci_dev *PIIX4_dev,
u8 smb_en,
u8 aux,
u8 *smb_en_status,
unsigned short *piix4_smba)
{
struct sb800_mmio_cfg mmio_cfg;
u8 smba_en_lo;
u8 smba_en_hi;
int retval;
mmio_cfg.use_mmio = piix4_sb800_use_mmio(PIIX4_dev);
retval = piix4_sb800_region_request(&PIIX4_dev->dev, &mmio_cfg);
if (retval)
return retval;
if (mmio_cfg.use_mmio) {
smba_en_lo = ioread8(mmio_cfg.addr);
smba_en_hi = ioread8(mmio_cfg.addr + 1);
} else {
outb_p(smb_en, SB800_PIIX4_SMB_IDX);
smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX);
smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1);
}
piix4_sb800_region_release(&PIIX4_dev->dev, &mmio_cfg);
if (!smb_en) {
*smb_en_status = smba_en_lo & 0x10;
*piix4_smba = smba_en_hi << 8;
if (aux)
*piix4_smba |= 0x20;
} else {
*smb_en_status = smba_en_lo & 0x01;
*piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0;
}
if (!*smb_en_status) {
dev_err(&PIIX4_dev->dev,
"SMBus Host Controller not enabled!\n");
return -ENODEV;
}
return 0;
}
static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
const struct pci_device_id *id, u8 aux)
{
unsigned short piix4_smba;
u8 smba_en_lo, smba_en_hi, smb_en, smb_en_status, port_sel;
u8 smb_en, smb_en_status, port_sel;
u8 i2ccfg, i2ccfg_offset = 0x10;
struct sb800_mmio_cfg mmio_cfg;
int retval;
/* SB800 and later SMBus does not support forcing address */
if (force || force_addr) {
@ -290,35 +416,11 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
else
smb_en = (aux) ? 0x28 : 0x2c;
if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb")) {
dev_err(&PIIX4_dev->dev,
"SMB base address index region 0x%x already in use.\n",
SB800_PIIX4_SMB_IDX);
return -EBUSY;
}
retval = piix4_setup_sb800_smba(PIIX4_dev, smb_en, aux, &smb_en_status,
&piix4_smba);
outb_p(smb_en, SB800_PIIX4_SMB_IDX);
smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX);
smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1);
release_region(SB800_PIIX4_SMB_IDX, 2);
if (!smb_en) {
smb_en_status = smba_en_lo & 0x10;
piix4_smba = smba_en_hi << 8;
if (aux)
piix4_smba |= 0x20;
} else {
smb_en_status = smba_en_lo & 0x01;
piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0;
}
if (!smb_en_status) {
dev_err(&PIIX4_dev->dev,
"SMBus Host Controller not enabled!\n");
return -ENODEV;
}
if (retval)
return retval;
if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name))
return -ENODEV;
@ -371,10 +473,11 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT;
}
} else {
if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2,
"sb800_piix4_smb")) {
mmio_cfg.use_mmio = piix4_sb800_use_mmio(PIIX4_dev);
retval = piix4_sb800_region_request(&PIIX4_dev->dev, &mmio_cfg);
if (retval) {
release_region(piix4_smba, SMBIOSIZE);
return -EBUSY;
return retval;
}
outb_p(SB800_PIIX4_PORT_IDX_SEL, SB800_PIIX4_SMB_IDX);
@ -384,7 +487,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
SB800_PIIX4_PORT_IDX;
piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK;
piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT;
release_region(SB800_PIIX4_SMB_IDX, 2);
piix4_sb800_region_release(&PIIX4_dev->dev, &mmio_cfg);
}
dev_info(&PIIX4_dev->dev,
@ -662,6 +765,29 @@ static void piix4_imc_wakeup(void)
release_region(KERNCZ_IMC_IDX, 2);
}
static int piix4_sb800_port_sel(u8 port, struct sb800_mmio_cfg *mmio_cfg)
{
u8 smba_en_lo, val;
if (mmio_cfg->use_mmio) {
smba_en_lo = ioread8(mmio_cfg->addr + piix4_port_sel_sb800);
val = (smba_en_lo & ~piix4_port_mask_sb800) | port;
if (smba_en_lo != val)
iowrite8(val, mmio_cfg->addr + piix4_port_sel_sb800);
return (smba_en_lo & piix4_port_mask_sb800);
}
outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX);
smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
val = (smba_en_lo & ~piix4_port_mask_sb800) | port;
if (smba_en_lo != val)
outb_p(val, SB800_PIIX4_SMB_IDX + 1);
return (smba_en_lo & piix4_port_mask_sb800);
}
/*
* Handles access to multiple SMBus ports on the SB800.
* The port is selected by bits 2:1 of the smb_en register (0x2c).
@ -678,12 +804,12 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
unsigned short piix4_smba = adapdata->smba;
int retries = MAX_TIMEOUT;
int smbslvcnt;
u8 smba_en_lo;
u8 port;
u8 prev_port;
int retval;
if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb"))
return -EBUSY;
retval = piix4_sb800_region_request(&adap->dev, &adapdata->mmio_cfg);
if (retval)
return retval;
/* Request the SMBUS semaphore, avoid conflicts with the IMC */
smbslvcnt = inb_p(SMBSLVCNT);
@ -738,18 +864,12 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
}
}
outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX);
smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
port = adapdata->port;
if ((smba_en_lo & piix4_port_mask_sb800) != port)
outb_p((smba_en_lo & ~piix4_port_mask_sb800) | port,
SB800_PIIX4_SMB_IDX + 1);
prev_port = piix4_sb800_port_sel(adapdata->port, &adapdata->mmio_cfg);
retval = piix4_access(adap, addr, flags, read_write,
command, size, data);
outb_p(smba_en_lo, SB800_PIIX4_SMB_IDX + 1);
piix4_sb800_port_sel(prev_port, &adapdata->mmio_cfg);
/* Release the semaphore */
outb_p(smbslvcnt | 0x20, SMBSLVCNT);
@ -758,7 +878,7 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
piix4_imc_wakeup();
release:
release_region(SB800_PIIX4_SMB_IDX, 2);
piix4_sb800_region_release(&adap->dev, &adapdata->mmio_cfg);
return retval;
}
@ -836,6 +956,7 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
return -ENOMEM;
}
adapdata->mmio_cfg.use_mmio = piix4_sb800_use_mmio(dev);
adapdata->smba = smba;
adapdata->sb800_main = sb800_main;
adapdata->port = port << piix4_port_shift_sb800;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
// Copyright (c) 2017-20 Linaro Limited.
// Copyright (c) 2017-2022 Linaro Limited.
#include <linux/clk.h>
#include <linux/completion.h>
@ -776,6 +776,7 @@ static const struct of_device_id cci_dt_match[] = {
{ .compatible = "qcom,msm8996-cci", .data = &cci_v2_data},
{ .compatible = "qcom,sdm845-cci", .data = &cci_v2_data},
{ .compatible = "qcom,sm8250-cci", .data = &cci_v2_data},
{ .compatible = "qcom,sm8450-cci", .data = &cci_v2_data},
{}
};
MODULE_DEVICE_TABLE(of, cci_dt_match);

View File

@ -3,7 +3,9 @@
#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dma/qcom-gpi-dma.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
@ -48,6 +50,9 @@
#define LOW_COUNTER_SHFT 10
#define CYCLE_COUNTER_MSK GENMASK(9, 0)
#define I2C_PACK_TX BIT(0)
#define I2C_PACK_RX BIT(1)
enum geni_i2c_err_code {
GP_IRQ0,
NACK,
@ -89,6 +94,9 @@ struct geni_i2c_dev {
void *dma_buf;
size_t xfer_len;
dma_addr_t dma_addr;
struct dma_chan *tx_c;
struct dma_chan *rx_c;
bool gpi_mode;
};
struct geni_i2c_err_log {
@ -456,12 +464,207 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
return gi2c->err;
}
static void i2c_gpi_cb_result(void *cb, const struct dmaengine_result *result)
{
struct geni_i2c_dev *gi2c = cb;
if (result->result != DMA_TRANS_NOERROR) {
dev_err(gi2c->se.dev, "DMA txn failed:%d\n", result->result);
gi2c->err = -EIO;
} else if (result->residue) {
dev_dbg(gi2c->se.dev, "DMA xfer has pending: %d\n", result->residue);
}
complete(&gi2c->done);
}
static void geni_i2c_gpi_unmap(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
void *tx_buf, dma_addr_t tx_addr,
void *rx_buf, dma_addr_t rx_addr)
{
if (tx_buf) {
dma_unmap_single(gi2c->se.dev->parent, tx_addr, msg->len, DMA_TO_DEVICE);
i2c_put_dma_safe_msg_buf(tx_buf, msg, false);
}
if (rx_buf) {
dma_unmap_single(gi2c->se.dev->parent, rx_addr, msg->len, DMA_FROM_DEVICE);
i2c_put_dma_safe_msg_buf(rx_buf, msg, false);
}
}
static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
struct dma_slave_config *config, dma_addr_t *dma_addr_p,
void **buf, unsigned int op, struct dma_chan *dma_chan)
{
struct gpi_i2c_config *peripheral;
unsigned int flags;
void *dma_buf;
dma_addr_t addr;
enum dma_data_direction map_dirn;
enum dma_transfer_direction dma_dirn;
struct dma_async_tx_descriptor *desc;
int ret;
peripheral = config->peripheral_config;
dma_buf = i2c_get_dma_safe_msg_buf(msg, 1);
if (!dma_buf)
return -ENOMEM;
if (op == I2C_WRITE)
map_dirn = DMA_TO_DEVICE;
else
map_dirn = DMA_FROM_DEVICE;
addr = dma_map_single(gi2c->se.dev->parent, dma_buf, msg->len, map_dirn);
if (dma_mapping_error(gi2c->se.dev->parent, addr)) {
i2c_put_dma_safe_msg_buf(dma_buf, msg, false);
return -ENOMEM;
}
/* set the length as message for rx txn */
peripheral->rx_len = msg->len;
peripheral->op = op;
ret = dmaengine_slave_config(dma_chan, config);
if (ret) {
dev_err(gi2c->se.dev, "dma config error: %d for op:%d\n", ret, op);
goto err_config;
}
peripheral->set_config = 0;
peripheral->multi_msg = true;
flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
if (op == I2C_WRITE)
dma_dirn = DMA_MEM_TO_DEV;
else
dma_dirn = DMA_DEV_TO_MEM;
desc = dmaengine_prep_slave_single(dma_chan, addr, msg->len, dma_dirn, flags);
if (!desc) {
dev_err(gi2c->se.dev, "prep_slave_sg failed\n");
ret = -EIO;
goto err_config;
}
desc->callback_result = i2c_gpi_cb_result;
desc->callback_param = gi2c;
dmaengine_submit(desc);
*dma_addr_p = addr;
return 0;
err_config:
dma_unmap_single(gi2c->se.dev->parent, addr, msg->len, map_dirn);
i2c_put_dma_safe_msg_buf(dma_buf, msg, false);
return ret;
}
static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], int num)
{
struct dma_slave_config config = {};
struct gpi_i2c_config peripheral = {};
int i, ret = 0, timeout;
dma_addr_t tx_addr, rx_addr;
void *tx_buf = NULL, *rx_buf = NULL;
const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
config.peripheral_config = &peripheral;
config.peripheral_size = sizeof(peripheral);
peripheral.pack_enable = I2C_PACK_TX | I2C_PACK_RX;
peripheral.cycle_count = itr->t_cycle_cnt;
peripheral.high_count = itr->t_high_cnt;
peripheral.low_count = itr->t_low_cnt;
peripheral.clk_div = itr->clk_div;
peripheral.set_config = 1;
peripheral.multi_msg = false;
for (i = 0; i < num; i++) {
gi2c->cur = &msgs[i];
gi2c->err = 0;
dev_dbg(gi2c->se.dev, "msg[%d].len:%d\n", i, gi2c->cur->len);
peripheral.stretch = 0;
if (i < num - 1)
peripheral.stretch = 1;
peripheral.addr = msgs[i].addr;
if (msgs[i].flags & I2C_M_RD) {
ret = geni_i2c_gpi(gi2c, &msgs[i], &config,
&rx_addr, &rx_buf, I2C_READ, gi2c->rx_c);
if (ret)
goto err;
}
ret = geni_i2c_gpi(gi2c, &msgs[i], &config,
&tx_addr, &tx_buf, I2C_WRITE, gi2c->tx_c);
if (ret)
goto err;
if (msgs[i].flags & I2C_M_RD)
dma_async_issue_pending(gi2c->rx_c);
dma_async_issue_pending(gi2c->tx_c);
timeout = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
if (!timeout) {
dev_err(gi2c->se.dev, "I2C timeout gpi flags:%d addr:0x%x\n",
gi2c->cur->flags, gi2c->cur->addr);
gi2c->err = -ETIMEDOUT;
goto err;
}
if (gi2c->err) {
ret = gi2c->err;
goto err;
}
geni_i2c_gpi_unmap(gi2c, &msgs[i], tx_buf, tx_addr, rx_buf, rx_addr);
}
return num;
err:
dev_err(gi2c->se.dev, "GPI transfer failed: %d\n", ret);
dmaengine_terminate_sync(gi2c->rx_c);
dmaengine_terminate_sync(gi2c->tx_c);
geni_i2c_gpi_unmap(gi2c, &msgs[i], tx_buf, tx_addr, rx_buf, rx_addr);
return ret;
}
static int geni_i2c_fifo_xfer(struct geni_i2c_dev *gi2c,
struct i2c_msg msgs[], int num)
{
int i, ret = 0;
for (i = 0; i < num; i++) {
u32 m_param = i < (num - 1) ? STOP_STRETCH : 0;
m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK);
gi2c->cur = &msgs[i];
if (msgs[i].flags & I2C_M_RD)
ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param);
else
ret = geni_i2c_tx_one_msg(gi2c, &msgs[i], m_param);
if (ret)
return ret;
}
return num;
}
static int geni_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg msgs[],
int num)
{
struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
int i, ret;
int ret;
gi2c->err = 0;
reinit_completion(&gi2c->done);
@ -475,28 +678,17 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
}
qcom_geni_i2c_conf(gi2c);
for (i = 0; i < num; i++) {
u32 m_param = i < (num - 1) ? STOP_STRETCH : 0;
m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK);
gi2c->cur = &msgs[i];
if (msgs[i].flags & I2C_M_RD)
ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param);
else
ret = geni_i2c_tx_one_msg(gi2c, &msgs[i], m_param);
if (ret)
break;
}
if (ret == 0)
ret = num;
if (gi2c->gpi_mode)
ret = geni_i2c_gpi_xfer(gi2c, msgs, num);
else
ret = geni_i2c_fifo_xfer(gi2c, msgs, num);
pm_runtime_mark_last_busy(gi2c->se.dev);
pm_runtime_put_autosuspend(gi2c->se.dev);
gi2c->cur = NULL;
gi2c->err = 0;
return ret;
return num;
}
static u32 geni_i2c_func(struct i2c_adapter *adap)
@ -517,11 +709,50 @@ static const struct acpi_device_id geni_i2c_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, geni_i2c_acpi_match);
#endif
static void release_gpi_dma(struct geni_i2c_dev *gi2c)
{
if (gi2c->rx_c)
dma_release_channel(gi2c->rx_c);
if (gi2c->tx_c)
dma_release_channel(gi2c->tx_c);
}
static int setup_gpi_dma(struct geni_i2c_dev *gi2c)
{
int ret;
geni_se_select_mode(&gi2c->se, GENI_GPI_DMA);
gi2c->tx_c = dma_request_chan(gi2c->se.dev, "tx");
if (IS_ERR(gi2c->tx_c)) {
ret = dev_err_probe(gi2c->se.dev, PTR_ERR(gi2c->tx_c),
"Failed to get tx DMA ch\n");
if (ret < 0)
goto err_tx;
}
gi2c->rx_c = dma_request_chan(gi2c->se.dev, "rx");
if (IS_ERR(gi2c->rx_c)) {
ret = dev_err_probe(gi2c->se.dev, PTR_ERR(gi2c->rx_c),
"Failed to get rx DMA ch\n");
if (ret < 0)
goto err_rx;
}
dev_dbg(gi2c->se.dev, "Grabbed GPI dma channels\n");
return 0;
err_rx:
dma_release_channel(gi2c->tx_c);
err_tx:
return ret;
}
static int geni_i2c_probe(struct platform_device *pdev)
{
struct geni_i2c_dev *gi2c;
struct resource *res;
u32 proto, tx_depth;
u32 proto, tx_depth, fifo_disable;
int ret;
struct device *dev = &pdev->dev;
@ -601,27 +832,43 @@ static int geni_i2c_probe(struct platform_device *pdev)
return ret;
}
proto = geni_se_read_proto(&gi2c->se);
tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
if (proto != GENI_SE_I2C) {
dev_err(dev, "Invalid proto %d\n", proto);
geni_se_resources_off(&gi2c->se);
return -ENXIO;
}
gi2c->tx_wm = tx_depth - 1;
geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth);
geni_se_config_packing(&gi2c->se, BITS_PER_BYTE, PACKING_BYTES_PW,
true, true, true);
fifo_disable = readl_relaxed(gi2c->se.base + GENI_IF_DISABLE_RO) & FIFO_IF_DISABLE;
if (fifo_disable) {
/* FIFO is disabled, so we can only use GPI DMA */
gi2c->gpi_mode = true;
ret = setup_gpi_dma(gi2c);
if (ret) {
dev_err(dev, "Failed to setup GPI DMA mode:%d ret\n", ret);
return ret;
}
dev_dbg(dev, "Using GPI DMA mode for I2C\n");
} else {
gi2c->gpi_mode = false;
tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
gi2c->tx_wm = tx_depth - 1;
geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth);
geni_se_config_packing(&gi2c->se, BITS_PER_BYTE,
PACKING_BYTES_PW, true, true, true);
dev_dbg(dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
}
ret = geni_se_resources_off(&gi2c->se);
if (ret) {
dev_err(dev, "Error turning off resources %d\n", ret);
return ret;
goto err_dma;
}
ret = geni_icc_disable(&gi2c->se);
if (ret)
return ret;
dev_dbg(dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
goto err_dma;
gi2c->suspended = 1;
pm_runtime_set_suspended(gi2c->se.dev);
@ -633,12 +880,16 @@ static int geni_i2c_probe(struct platform_device *pdev)
if (ret) {
dev_err(dev, "Error adding i2c adapter %d\n", ret);
pm_runtime_disable(gi2c->se.dev);
return ret;
goto err_dma;
}
dev_dbg(dev, "Geni-I2C adaptor successfully added\n");
return 0;
err_dma:
release_gpi_dma(gi2c);
return ret;
}
static int geni_i2c_remove(struct platform_device *pdev)
@ -646,6 +897,7 @@ static int geni_i2c_remove(struct platform_device *pdev)
struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);
i2c_del_adapter(&gi2c->adap);
release_gpi_dma(gi2c);
pm_runtime_disable(gi2c->se.dev);
return 0;
}

View File

@ -1008,6 +1008,7 @@ static const struct of_device_id rcar_i2c_dt_ids[] = {
{ .compatible = "renesas,rcar-gen1-i2c", .data = (void *)I2C_RCAR_GEN1 },
{ .compatible = "renesas,rcar-gen2-i2c", .data = (void *)I2C_RCAR_GEN2 },
{ .compatible = "renesas,rcar-gen3-i2c", .data = (void *)I2C_RCAR_GEN3 },
{ .compatible = "renesas,rcar-gen4-i2c", .data = (void *)I2C_RCAR_GEN3 },
{},
};
MODULE_DEVICE_TABLE(of, rcar_i2c_dt_ids);

View File

@ -88,11 +88,6 @@
#define RIIC_INIT_MSG -1
enum riic_type {
RIIC_RZ_A,
RIIC_RZ_G2L,
};
struct riic_dev {
void __iomem *base;
u8 *buf;
@ -396,6 +391,11 @@ static struct riic_irq_desc riic_irqs[] = {
{ .res_num = 5, .isr = riic_tend_isr, .name = "riic-nack" },
};
static void riic_reset_control_assert(void *data)
{
reset_control_assert(data);
}
static int riic_i2c_probe(struct platform_device *pdev)
{
struct riic_dev *riic;
@ -404,7 +404,6 @@ static int riic_i2c_probe(struct platform_device *pdev)
struct i2c_timings i2c_t;
struct reset_control *rstc;
int i, ret;
enum riic_type type;
riic = devm_kzalloc(&pdev->dev, sizeof(*riic), GFP_KERNEL);
if (!riic)
@ -421,16 +420,18 @@ static int riic_i2c_probe(struct platform_device *pdev)
return PTR_ERR(riic->clk);
}
type = (enum riic_type)of_device_get_match_data(&pdev->dev);
if (type == RIIC_RZ_G2L) {
rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
if (IS_ERR(rstc)) {
dev_err(&pdev->dev, "Error: missing reset ctrl\n");
return PTR_ERR(rstc);
}
rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
if (IS_ERR(rstc))
return dev_err_probe(&pdev->dev, PTR_ERR(rstc),
"Error: missing reset ctrl\n");
reset_control_deassert(rstc);
}
ret = reset_control_deassert(rstc);
if (ret)
return ret;
ret = devm_add_action_or_reset(&pdev->dev, riic_reset_control_assert, rstc);
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(riic_irqs); i++) {
ret = platform_get_irq(pdev, riic_irqs[i].res_num);
@ -492,8 +493,7 @@ static int riic_i2c_remove(struct platform_device *pdev)
}
static const struct of_device_id riic_i2c_dt_ids[] = {
{ .compatible = "renesas,riic-r9a07g044", .data = (void *)RIIC_RZ_G2L },
{ .compatible = "renesas,riic-rz", .data = (void *)RIIC_RZ_A },
{ .compatible = "renesas,riic-rz", },
{ /* Sentinel */ },
};

View File

@ -1233,6 +1233,11 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
return err;
i2c_dev->msg_buf = msg->buf;
/* The condition true implies smbus block read and len is already read */
if (msg->flags & I2C_M_RECV_LEN && end_state != MSG_END_CONTINUE)
i2c_dev->msg_buf = msg->buf + 1;
i2c_dev->msg_buf_remaining = msg->len;
i2c_dev->msg_err = I2C_ERR_NONE;
i2c_dev->msg_read = !!(msg->flags & I2C_M_RD);
@ -1389,6 +1394,15 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
else
end_type = MSG_END_REPEAT_START;
}
/* If M_RECV_LEN use ContinueXfer to read the first byte */
if (msgs[i].flags & I2C_M_RECV_LEN) {
ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], MSG_END_CONTINUE);
if (ret)
break;
/* Set the read byte as msg len */
msgs[i].len = msgs[i].buf[0];
dev_dbg(i2c_dev->dev, "reading %d bytes\n", msgs[i].len);
}
ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], end_type);
if (ret)
break;
@ -1416,10 +1430,10 @@ static u32 tegra_i2c_func(struct i2c_adapter *adap)
{
struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
u32 ret = I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
if (i2c_dev->hw->has_continue_xfer_support)
ret |= I2C_FUNC_NOSTART;
ret |= I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_READ_BLOCK_DATA;
return ret;
}

View File

@ -734,7 +734,6 @@ static const struct i2c_adapter_quirks xiic_quirks = {
static const struct i2c_adapter xiic_adapter = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
.class = I2C_CLASS_DEPRECATED,
.algo = &xiic_algorithm,
.quirks = &xiic_quirks,
@ -771,6 +770,8 @@ static int xiic_i2c_probe(struct platform_device *pdev)
i2c_set_adapdata(&i2c->adap, i2c);
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.dev.of_node = pdev->dev.of_node;
snprintf(i2c->adap.name, sizeof(i2c->adap.name),
DRIVER_NAME " %s", pdev->name);
mutex_init(&i2c->lock);

View File

@ -236,7 +236,8 @@ static int i2c_acpi_get_info(struct acpi_device *adev,
struct acpi_device *adapter_adev;
/* The adapter must be present */
if (acpi_bus_get_device(lookup.adapter_handle, &adapter_adev))
adapter_adev = acpi_fetch_acpi_dev(lookup.adapter_handle);
if (!adapter_adev)
return -ENODEV;
if (acpi_bus_get_status(adapter_adev) ||
!adapter_adev->status.present)
@ -275,13 +276,10 @@ static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level,
void *data, void **return_value)
{
struct i2c_adapter *adapter = data;
struct acpi_device *adev;
struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
struct i2c_board_info info;
if (acpi_bus_get_device(handle, &adev))
return AE_OK;
if (i2c_acpi_get_info(adev, &info, adapter, NULL))
if (!adev || i2c_acpi_get_info(adev, &info, adapter, NULL))
return AE_OK;
i2c_acpi_register_device(adapter, adev, &info);
@ -341,12 +339,9 @@ static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level,
void *data, void **return_value)
{
struct i2c_acpi_lookup *lookup = data;
struct acpi_device *adev;
struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
if (acpi_bus_get_device(handle, &adev))
return AE_OK;
if (i2c_acpi_do_lookup(adev, lookup))
if (!adev || i2c_acpi_do_lookup(adev, lookup))
return AE_OK;
if (lookup->search_handle != lookup->adapter_handle)

View File

@ -1424,7 +1424,7 @@ int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr)
if (irq <= 0)
return -ENXIO;
generic_handle_irq(irq);
generic_handle_irq_safe(irq);
return 0;
}
@ -1479,7 +1479,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
goto out_list;
}
res = of_i2c_setup_smbus_alert(adap);
res = i2c_setup_smbus_alert(adap);
if (res)
goto out_reg;

View File

@ -14,6 +14,9 @@
#include "i2c-core.h"
#define CREATE_TRACE_POINTS
#include <trace/events/i2c_slave.h>
int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb)
{
int ret;
@ -79,6 +82,18 @@ int i2c_slave_unregister(struct i2c_client *client)
}
EXPORT_SYMBOL_GPL(i2c_slave_unregister);
int i2c_slave_event(struct i2c_client *client,
enum i2c_slave_event event, u8 *val)
{
int ret = client->slave_cb(client, event, val);
if (trace_i2c_slave_enabled())
trace_i2c_slave(client, event, val, ret);
return ret;
}
EXPORT_SYMBOL_GPL(i2c_slave_event);
/**
* i2c_detect_slave_mode - detect operation mode
* @dev: The device owning the bus

View File

@ -14,6 +14,7 @@
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/i2c-smbus.h>
#include <linux/property.h>
#include <linux/slab.h>
#include "i2c-core.h"
@ -701,13 +702,17 @@ struct i2c_client *i2c_new_smbus_alert_device(struct i2c_adapter *adapter,
}
EXPORT_SYMBOL_GPL(i2c_new_smbus_alert_device);
#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF)
int of_i2c_setup_smbus_alert(struct i2c_adapter *adapter)
#if IS_ENABLED(CONFIG_I2C_SMBUS)
int i2c_setup_smbus_alert(struct i2c_adapter *adapter)
{
struct device *parent = adapter->dev.parent;
int irq;
irq = of_property_match_string(adapter->dev.of_node, "interrupt-names",
"smbus_alert");
/* Adapter instantiated without parent, skip the SMBus alert setup */
if (!parent)
return 0;
irq = device_property_match_string(parent, "interrupt-names", "smbus_alert");
if (irq == -EINVAL || irq == -ENODATA)
return 0;
else if (irq < 0)
@ -715,5 +720,4 @@ int of_i2c_setup_smbus_alert(struct i2c_adapter *adapter)
return PTR_ERR_OR_ZERO(i2c_new_smbus_alert_device(adapter, NULL));
}
EXPORT_SYMBOL_GPL(of_i2c_setup_smbus_alert);
#endif

View File

@ -86,3 +86,12 @@ void of_i2c_register_devices(struct i2c_adapter *adap);
static inline void of_i2c_register_devices(struct i2c_adapter *adap) { }
#endif
extern struct notifier_block i2c_of_notifier;
#if IS_ENABLED(CONFIG_I2C_SMBUS)
int i2c_setup_smbus_alert(struct i2c_adapter *adap);
#else
static inline int i2c_setup_smbus_alert(struct i2c_adapter *adap)
{
return 0;
}
#endif

View File

@ -13,7 +13,7 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
@ -128,7 +128,8 @@ static int smbalert_probe(struct i2c_client *ara,
if (setup) {
irq = setup->irq;
} else {
irq = of_irq_get_byname(adapter->dev.of_node, "smbus_alert");
irq = fwnode_irq_get_byname(dev_fwnode(adapter->dev.parent),
"smbus_alert");
if (irq <= 0)
return irq;
}

View File

@ -261,7 +261,7 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev)
err = device_create_file(&pdev->dev, &dev_attr_available_masters);
if (err)
goto err_rollback;
goto err_rollback_activation;
err = device_create_file(&pdev->dev, &dev_attr_current_master);
if (err)
@ -271,8 +271,9 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev)
err_rollback_available:
device_remove_file(&pdev->dev, &dev_attr_available_masters);
err_rollback:
err_rollback_activation:
i2c_demux_deactivate_master(priv);
err_rollback:
for (j = 0; j < i; j++) {
of_node_put(priv->chan[j].parent_np);
of_changeset_destroy(&priv->chan[j].chgset);

View File

@ -30,14 +30,6 @@ struct i2c_client *i2c_new_smbus_alert_device(struct i2c_adapter *adapter,
struct i2c_smbus_alert_setup *setup);
int i2c_handle_smbus_alert(struct i2c_client *ara);
#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF)
int of_i2c_setup_smbus_alert(struct i2c_adapter *adap);
#else
static inline int of_i2c_setup_smbus_alert(struct i2c_adapter *adap)
{
return 0;
}
#endif
#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_I2C_SLAVE)
struct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter);
void i2c_free_slave_host_notify_device(struct i2c_client *client);

View File

@ -392,12 +392,8 @@ enum i2c_slave_event {
int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb);
int i2c_slave_unregister(struct i2c_client *client);
bool i2c_detect_slave_mode(struct device *dev);
static inline int i2c_slave_event(struct i2c_client *client,
enum i2c_slave_event event, u8 *val)
{
return client->slave_cb(client, event, val);
}
int i2c_slave_event(struct i2c_client *client,
enum i2c_slave_event event, u8 *val);
#else
static inline bool i2c_detect_slave_mode(struct device *dev) { return false; }
#endif

View File

@ -262,6 +262,8 @@ resource_union(struct resource *r1, struct resource *r2, struct resource *r)
#define request_muxed_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name), IORESOURCE_MUXED)
#define __request_mem_region(start,n,name, excl) __request_region(&iomem_resource, (start), (n), (name), excl)
#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name), 0)
#define request_mem_region_muxed(start, n, name) \
__request_region(&iomem_resource, (start), (n), (name), IORESOURCE_MUXED)
#define request_mem_region_exclusive(start,n,name) \
__request_region(&iomem_resource, (start), (n), (name), IORESOURCE_EXCLUSIVE)
#define rename_region(region, newname) do { (region)->name = (newname); } while (0)

View File

@ -121,6 +121,7 @@ struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode);
void fwnode_handle_put(struct fwnode_handle *fwnode);
int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index);
int fwnode_irq_get_byname(const struct fwnode_handle *fwnode, const char *name);
void __iomem *fwnode_iomap(struct fwnode_handle *fwnode, int index);

View File

@ -0,0 +1,67 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* I2C slave tracepoints
*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM i2c_slave
#if !defined(_TRACE_I2C_SLAVE_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_I2C_SLAVE_H
#include <linux/i2c.h>
#include <linux/tracepoint.h>
TRACE_DEFINE_ENUM(I2C_SLAVE_READ_REQUESTED);
TRACE_DEFINE_ENUM(I2C_SLAVE_WRITE_REQUESTED);
TRACE_DEFINE_ENUM(I2C_SLAVE_READ_PROCESSED);
TRACE_DEFINE_ENUM(I2C_SLAVE_WRITE_RECEIVED);
TRACE_DEFINE_ENUM(I2C_SLAVE_STOP);
#define show_event_type(type) \
__print_symbolic(type, \
{ I2C_SLAVE_READ_REQUESTED, "RD_REQ" }, \
{ I2C_SLAVE_WRITE_REQUESTED, "WR_REQ" }, \
{ I2C_SLAVE_READ_PROCESSED, "RD_PRO" }, \
{ I2C_SLAVE_WRITE_RECEIVED, "WR_RCV" }, \
{ I2C_SLAVE_STOP, " STOP" })
TRACE_EVENT(i2c_slave,
TP_PROTO(const struct i2c_client *client, enum i2c_slave_event event,
__u8 *val, int cb_ret),
TP_ARGS(client, event, val, cb_ret),
TP_STRUCT__entry(
__field(int, adapter_nr )
__field(int, ret )
__field(__u16, addr )
__field(__u16, len )
__field(enum i2c_slave_event, event )
__array(__u8, buf, 1) ),
TP_fast_assign(
__entry->adapter_nr = client->adapter->nr;
__entry->addr = client->addr;
__entry->event = event;
__entry->ret = cb_ret;
switch (event) {
case I2C_SLAVE_READ_REQUESTED:
case I2C_SLAVE_READ_PROCESSED:
case I2C_SLAVE_WRITE_RECEIVED:
__entry->len = 1;
memcpy(__entry->buf, val, __entry->len);
break;
default:
__entry->len = 0;
break;
}
),
TP_printk("i2c-%d a=%03x ret=%d %s [%*phD]",
__entry->adapter_nr, __entry->addr, __entry->ret,
show_event_type(__entry->event), __entry->len, __entry->buf
));
#endif /* _TRACE_I2C_SLAVE_H */
/* This part must be outside protection */
#include <trace/define_trace.h>