mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-27 14:14:24 +08:00
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:
commit
5627ecb837
@ -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
|
||||
|
@ -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:
|
||||
|
||||
|
56
Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml
Normal file
56
Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml
Normal 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>;
|
||||
};
|
||||
...
|
@ -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
|
||||
|
@ -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
|
||||
======================
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
30
drivers/i2c/busses/i2c-ccgx-ucsi.c
Normal file
30
drivers/i2c/busses/i2c-ccgx-ucsi.c
Normal 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");
|
11
drivers/i2c/busses/i2c-ccgx-ucsi.h
Normal file
11
drivers/i2c/busses/i2c-ccgx-ucsi.h
Normal 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_ */
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
388
drivers/i2c/busses/i2c-designware-amdpsp.c
Normal file
388
drivers/i2c/busses/i2c-designware-amdpsp.c
Normal 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);
|
||||
}
|
@ -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;
|
||||
|
@ -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, ¶m);
|
||||
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)
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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, }
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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).
|
||||
*
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 */ },
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
67
include/trace/events/i2c_slave.h
Normal file
67
include/trace/events/i2c_slave.h
Normal 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>
|
Loading…
Reference in New Issue
Block a user