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

Pull i2c updates from Wolfram Sang:

 - big refactoring of the PASEMI driver to support the Apple M1

 - huge improvements to the XIIC in terms of locking and SMP safety

 - refactoring and clean ups for the i801 driver

... and the usual bunch of small driver updates

* 'i2c/for-mergewindow' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (43 commits)
  i2c: amd-mp2-plat: ACPI: Use ACPI_COMPANION() directly
  i2c: i801: Add support for Intel Ice Lake PCH-N
  i2c: virtio: update the maintainer to Conghui
  i2c: xlr: Fix a resource leak in the error handling path of 'xlr_i2c_probe()'
  i2c: qup: move to use request_irq by IRQF_NO_AUTOEN flag
  i2c: qup: fix a trivial typo
  i2c: tegra: Ensure that device is suspended before driver is removed
  i2c: i801: Fix incorrect and needless software PEC disabling
  i2c: mediatek: Dump i2c/dma register when a timeout occurs
  i2c: mediatek: Reset the handshake signal between i2c and dma
  i2c: mlxcpld: Allow flexible polling time setting for I2C transactions
  i2c: pasemi: Set enable bit for Apple variant
  i2c: pasemi: Add Apple platform driver
  i2c: pasemi: Refactor _probe to use devm_*
  i2c: pasemi: Allow to configure bus frequency
  i2c: pasemi: Move common reset code to own function
  i2c: pasemi: Split pci driver to its own file
  i2c: pasemi: Split off common probing code
  i2c: pasemi: Remove usage of pci_dev
  i2c: pasemi: Use dev_name instead of port number
  ...
This commit is contained in:
Linus Torvalds
2021-11-08 11:46:10 -08:00
23 changed files with 558 additions and 261 deletions
@@ -97,6 +97,12 @@ properties:
- items:
- const: nxp,se97b
- const: atmel,24c02
- items:
- const: onnn,cat24c04
- const: atmel,24c04
- items:
- const: onnn,cat24c05
- const: atmel,24c04
- items:
- const: renesas,r1ex24002
- const: atmel,24c02
@@ -0,0 +1,61 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/i2c/apple,i2c.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Apple/PASemi I2C controller
maintainers:
- Sven Peter <sven@svenpeter.dev>
description: |
Apple SoCs such as the M1 come with a I2C controller based on the one found
in machines with P. A. Semi's PWRficient processors.
The bus is used to communicate with e.g. USB PD chips or the speaker
amp.
allOf:
- $ref: /schemas/i2c/i2c-controller.yaml#
properties:
compatible:
enum:
- apple,t8103-i2c
- apple,i2c
reg:
maxItems: 1
clocks:
items:
- description: I2C bus reference clock
interrupts:
maxItems: 1
clock-frequency:
description: |
Desired I2C bus clock frequency in Hz. If not specified, 100 kHz will be
used. This frequency is generated by dividing the reference clock.
Allowed values are between ref_clk/(16*4) and ref_clk/(16*255).
required:
- compatible
- reg
- clocks
- interrupts
unevaluatedProperties: false
examples:
- |
i2c@35010000 {
compatible = "apple,t8103-i2c";
reg = <0x35010000 0x4000>;
interrupt-parent = <&aic>;
interrupts = <0 627 4>;
clocks = <&ref_clk>;
#address-cells = <1>;
#size-cells = <0>;
};
+1 -1
View File
@@ -20189,7 +20189,7 @@ F: include/uapi/linux/virtio_snd.h
F: sound/virtio/*
VIRTIO I2C DRIVER
M: Jie Deng <jie.deng@intel.com>
M: Conghui Chen <conghui.chen@intel.com>
M: Viresh Kumar <viresh.kumar@linaro.org>
L: linux-i2c@vger.kernel.org
L: virtualization@lists.linux-foundation.org
+15 -1
View File
@@ -615,7 +615,10 @@ config I2C_EXYNOS5
depends on ARCH_EXYNOS || COMPILE_TEST
default y if ARCH_EXYNOS
help
High-speed I2C controller on Exynos5 and newer Samsung SoCs.
High-speed I2C controller on Samsung Exynos5 and newer Samsung SoCs:
Exynos5250, Exynos5260, Exynos5410, Exynos542x, Exynos5800,
Exynos5433 and Exynos7.
Choose Y here only if you build for such Samsung SoC.
config I2C_GPIO
tristate "GPIO-based bitbanging I2C"
@@ -856,6 +859,17 @@ config I2C_PASEMI
help
Supports the PA Semi PWRficient on-chip SMBus interfaces.
config I2C_APPLE
tristate "Apple SMBus platform driver"
depends on ARCH_APPLE || COMPILE_TEST
default ARCH_APPLE
help
Say Y here if you want to use the I2C controller present on Apple
Silicon chips such as the M1.
This driver can also be built as a module. If so, the module
will be called i2c-apple.
config I2C_PCA_PLATFORM
tristate "PCA9564/PCA9665 as platform device"
select I2C_ALGOPCA
+3
View File
@@ -84,7 +84,10 @@ obj-$(CONFIG_I2C_NPCM7XX) += i2c-npcm7xx.o
obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
obj-$(CONFIG_I2C_OWL) += i2c-owl.o
i2c-pasemi-objs := i2c-pasemi-core.o i2c-pasemi-pci.o
obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o
i2c-apple-objs := i2c-pasemi-core.o i2c-pasemi-platform.o
obj-$(CONFIG_I2C_APPLE) += i2c-apple.o
obj-$(CONFIG_I2C_PCA_PLATFORM) += i2c-pca-platform.o
obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
+2 -2
View File
@@ -307,9 +307,9 @@ static int amd_mp2_pci_init(struct amd_mp2_dev *privdata,
pci_set_master(pci_dev);
rc = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(64));
rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(64));
if (rc) {
rc = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32));
if (rc)
goto err_dma_mask;
}
+2 -3
View File
@@ -246,12 +246,11 @@ static int i2c_amd_probe(struct platform_device *pdev)
{
int ret;
struct amd_i2c_dev *i2c_dev;
acpi_handle handle = ACPI_HANDLE(&pdev->dev);
struct acpi_device *adev;
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
struct amd_mp2_dev *mp2_dev;
const char *uid;
if (acpi_bus_get_device(handle, &adev))
if (!adev)
return -ENODEV;
/* The ACPI namespace doesn't contain information about which MP2 PCI
+1 -1
View File
@@ -763,7 +763,7 @@ static int bcm_kona_i2c_probe(struct platform_device *pdev)
/* Map hardware registers */
dev->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(dev->base))
return -ENOMEM;
return PTR_ERR(dev->base);
/* Get and enable external clock */
dev->external_clk = devm_clk_get(dev->device, NULL);
+31 -52
View File
@@ -64,6 +64,7 @@
* Cannon Lake-LP (PCH) 0x9da3 32 hard yes yes yes
* Cedar Fork (PCH) 0x18df 32 hard yes yes yes
* Ice Lake-LP (PCH) 0x34a3 32 hard yes yes yes
* Ice Lake-N (PCH) 0x38a3 32 hard yes yes yes
* Comet Lake (PCH) 0x02a3 32 hard yes yes yes
* Comet Lake-H (PCH) 0x06a3 32 hard yes yes yes
* Elkhart Lake (PCH) 0x4b23 32 hard yes yes yes
@@ -218,6 +219,7 @@
#define PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS 0x23b0
#define PCI_DEVICE_ID_INTEL_GEMINILAKE_SMBUS 0x31d4
#define PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS 0x34a3
#define PCI_DEVICE_ID_INTEL_ICELAKE_N_SMBUS 0x38a3
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
#define PCI_DEVICE_ID_INTEL_TIGERLAKE_H_SMBUS 0x43a3
#define PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS 0x4b23
@@ -1042,6 +1044,7 @@ static const struct pci_device_id i801_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_H_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_LP_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICELAKE_N_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COMETLAKE_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COMETLAKE_H_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COMETLAKE_V_SMBUS) },
@@ -1192,7 +1195,7 @@ static acpi_status check_acpi_smo88xx_device(acpi_handle obj_handle,
kfree(info);
*((bool *)return_value) = true;
*return_value = NULL;
return AE_CTRL_TERMINATE;
smo88xx_not_found:
@@ -1202,11 +1205,9 @@ smo88xx_not_found:
static bool is_dell_system_with_lis3lv02d(void)
{
bool found;
const char *vendor;
void *err = ERR_PTR(-ENOENT);
vendor = dmi_get_system_info(DMI_SYS_VENDOR);
if (!vendor || strcmp(vendor, "Dell Inc."))
if (!dmi_match(DMI_SYS_VENDOR, "Dell Inc."))
return false;
/*
@@ -1217,11 +1218,9 @@ static bool is_dell_system_with_lis3lv02d(void)
* accelerometer but unfortunately ACPI does not provide any other
* information (like I2C address).
*/
found = false;
acpi_get_devices(NULL, check_acpi_smo88xx_device, NULL,
(void **)&found);
acpi_get_devices(NULL, check_acpi_smo88xx_device, NULL, &err);
return found;
return !IS_ERR(err);
}
/*
@@ -1395,7 +1394,7 @@ static const struct dmi_system_id mux_dmi_table[] = {
};
/* Setup multiplexing if needed */
static int i801_add_mux(struct i801_priv *priv)
static void i801_add_mux(struct i801_priv *priv)
{
struct device *dev = &priv->adapter.dev;
const struct i801_mux_config *mux_config;
@@ -1404,7 +1403,7 @@ static int i801_add_mux(struct i801_priv *priv)
int i;
if (!priv->mux_drvdata)
return 0;
return;
mux_config = priv->mux_drvdata;
/* Prepare the platform data */
@@ -1420,13 +1419,11 @@ static int i801_add_mux(struct i801_priv *priv)
struct_size(lookup, table, mux_config->n_gpios + 1),
GFP_KERNEL);
if (!lookup)
return -ENOMEM;
return;
lookup->dev_id = "i2c-mux-gpio";
for (i = 0; i < mux_config->n_gpios; i++) {
lookup->table[i] = (struct gpiod_lookup)
GPIO_LOOKUP(mux_config->gpio_chip,
mux_config->gpios[i], "mux", 0);
}
for (i = 0; i < mux_config->n_gpios; i++)
lookup->table[i] = GPIO_LOOKUP(mux_config->gpio_chip,
mux_config->gpios[i], "mux", 0);
gpiod_add_lookup_table(lookup);
priv->lookup = lookup;
@@ -1444,8 +1441,6 @@ static int i801_add_mux(struct i801_priv *priv)
gpiod_remove_lookup_table(lookup);
dev_err(dev, "Failed to register i2c-mux-gpio device\n");
}
return PTR_ERR_OR_ZERO(priv->mux_pdev);
}
static void i801_del_mux(struct i801_priv *priv)
@@ -1475,7 +1470,7 @@ static unsigned int i801_get_adapter_class(struct i801_priv *priv)
return class;
}
#else
static inline int i801_add_mux(struct i801_priv *priv) { return 0; }
static inline void i801_add_mux(struct i801_priv *priv) { }
static inline void i801_del_mux(struct i801_priv *priv) { }
static inline unsigned int i801_get_adapter_class(struct i801_priv *priv)
@@ -1493,7 +1488,6 @@ static struct platform_device *
i801_add_tco_spt(struct i801_priv *priv, struct pci_dev *pci_dev,
struct resource *tco_res)
{
static DEFINE_MUTEX(p2sb_mutex);
struct resource *res;
unsigned int devfn;
u64 base64_addr;
@@ -1506,7 +1500,7 @@ i801_add_tco_spt(struct i801_priv *priv, struct pci_dev *pci_dev,
* enumerated by the PCI subsystem, so we need to unhide/hide it
* to lookup the P2SB BAR.
*/
mutex_lock(&p2sb_mutex);
pci_lock_rescan_remove();
devfn = PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 1);
@@ -1524,7 +1518,7 @@ i801_add_tco_spt(struct i801_priv *priv, struct pci_dev *pci_dev,
/* Hide the P2SB device, if it was hidden before */
if (hidden)
pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, hidden);
mutex_unlock(&p2sb_mutex);
pci_unlock_rescan_remove();
res = &tco_res[1];
if (pci_dev->device == PCI_DEVICE_ID_INTEL_DNV_SMBUS)
@@ -1624,7 +1618,7 @@ i801_acpi_io_handler(u32 function, acpi_physical_address address, u32 bits,
* BIOS is accessing the host controller so prevent it from
* suspending automatically from now on.
*/
pm_runtime_set_autosuspend_delay(&pdev->dev, -1);
pm_runtime_get_sync(&pdev->dev);
}
if ((function & ACPI_IO_MASK) == ACPI_READ)
@@ -1639,31 +1633,22 @@ i801_acpi_io_handler(u32 function, acpi_physical_address address, u32 bits,
static int i801_acpi_probe(struct i801_priv *priv)
{
struct acpi_device *adev;
acpi_handle ah = ACPI_HANDLE(&priv->pci_dev->dev);
acpi_status status;
adev = ACPI_COMPANION(&priv->pci_dev->dev);
if (adev) {
status = acpi_install_address_space_handler(adev->handle,
ACPI_ADR_SPACE_SYSTEM_IO, i801_acpi_io_handler,
NULL, priv);
if (ACPI_SUCCESS(status))
return 0;
}
status = acpi_install_address_space_handler(ah, ACPI_ADR_SPACE_SYSTEM_IO,
i801_acpi_io_handler, NULL, priv);
if (ACPI_SUCCESS(status))
return 0;
return acpi_check_resource_conflict(&priv->pci_dev->resource[SMBBAR]);
}
static void i801_acpi_remove(struct i801_priv *priv)
{
struct acpi_device *adev;
acpi_handle ah = ACPI_HANDLE(&priv->pci_dev->dev);
adev = ACPI_COMPANION(&priv->pci_dev->dev);
if (!adev)
return;
acpi_remove_address_space_handler(adev->handle,
ACPI_ADR_SPACE_SYSTEM_IO, i801_acpi_io_handler);
acpi_remove_address_space_handler(ah, ACPI_ADR_SPACE_SYSTEM_IO, i801_acpi_io_handler);
}
#else
static inline int i801_acpi_probe(struct i801_priv *priv) { return 0; }
@@ -1675,7 +1660,6 @@ static void i801_setup_hstcfg(struct i801_priv *priv)
unsigned char hstcfg = priv->original_hstcfg;
hstcfg &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */
hstcfg &= ~SMBHSTCNT_PEC_EN; /* Disable software PEC */
hstcfg |= SMBHSTCFG_HST_EN;
pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hstcfg);
}
@@ -1720,6 +1704,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
case PCI_DEVICE_ID_INTEL_CANNONLAKE_LP_SMBUS:
case PCI_DEVICE_ID_INTEL_CDF_SMBUS:
case PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS:
case PCI_DEVICE_ID_INTEL_ICELAKE_N_SMBUS:
case PCI_DEVICE_ID_INTEL_COMETLAKE_SMBUS:
case PCI_DEVICE_ID_INTEL_COMETLAKE_H_SMBUS:
case PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS:
@@ -1831,19 +1816,12 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
priv->features &= ~FEATURE_IRQ;
if (priv->features & FEATURE_IRQ) {
u16 pcictl, pcists;
u16 pcists;
/* Complain if an interrupt is already pending */
pci_read_config_word(priv->pci_dev, PCI_STATUS, &pcists);
if (pcists & PCI_STATUS_INTERRUPT)
dev_warn(&dev->dev, "An interrupt is pending!\n");
/* Check if interrupts have been disabled */
pci_read_config_word(priv->pci_dev, PCI_COMMAND, &pcictl);
if (pcictl & PCI_COMMAND_INTX_DISABLE) {
dev_info(&dev->dev, "Interrupts are disabled\n");
priv->features &= ~FEATURE_IRQ;
}
}
if (priv->features & FEATURE_IRQ) {
@@ -1891,9 +1869,6 @@ static void i801_remove(struct pci_dev *dev)
{
struct i801_priv *priv = pci_get_drvdata(dev);
pm_runtime_forbid(&dev->dev);
pm_runtime_get_noresume(&dev->dev);
i801_disable_host_notify(priv);
i801_del_mux(priv);
i2c_del_adapter(&priv->adapter);
@@ -1902,6 +1877,10 @@ static void i801_remove(struct pci_dev *dev)
platform_device_unregister(priv->tco_pdev);
/* if acpi_reserved is set then usage_count is incremented already */
if (!priv->acpi_reserved)
pm_runtime_get_noresume(&dev->dev);
/*
* do not call pci_disable_device(dev) since it can cause hard hangs on
* some systems during power-off (eg. Fujitsu-Siemens Lifebook E8010)
+5 -7
View File
@@ -918,13 +918,11 @@ ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return -ENODEV;
}
if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) ||
(pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0)) {
if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) ||
(pci_set_consistent_dma_mask(pdev,
DMA_BIT_MASK(32)) != 0)) {
dev_err(&pdev->dev, "pci_set_dma_mask fail %p\n",
pdev);
err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (err) {
err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (err) {
dev_err(&pdev->dev, "dma_set_mask fail\n");
return -ENODEV;
}
}
+2 -1
View File
@@ -283,7 +283,8 @@ static const struct i2c_algorithm kempld_i2c_algorithm = {
static const struct i2c_adapter kempld_i2c_adapter = {
.owner = THIS_MODULE,
.name = "i2c-kempld",
.class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.class = I2C_CLASS_HWMON | I2C_CLASS_SPD |
I2C_CLASS_DEPRECATED,
.algo = &kempld_i2c_algorithm,
};
+9 -5
View File
@@ -27,7 +27,7 @@
#define MLXCPLD_I2C_MAX_ADDR_LEN 4
#define MLXCPLD_I2C_RETR_NUM 2
#define MLXCPLD_I2C_XFER_TO 500000 /* usec */
#define MLXCPLD_I2C_POLL_TIME 400 /* usec */
#define MLXCPLD_I2C_POLL_TIME 200 /* usec */
/* LPC I2C registers */
#define MLXCPLD_LPCI2C_CPBLTY_REG 0x0
@@ -73,6 +73,7 @@ struct mlxcpld_i2c_priv {
struct mlxcpld_i2c_curr_xfer xfer;
struct device *dev;
bool smbus_block;
int polling_time;
};
static void mlxcpld_i2c_lpc_write_buf(u8 *data, u8 len, u32 addr)
@@ -267,8 +268,8 @@ static int mlxcpld_i2c_wait_for_free(struct mlxcpld_i2c_priv *priv)
do {
if (!mlxcpld_i2c_check_busy(priv))
break;
usleep_range(MLXCPLD_I2C_POLL_TIME / 2, MLXCPLD_I2C_POLL_TIME);
timeout += MLXCPLD_I2C_POLL_TIME;
usleep_range(priv->polling_time / 2, priv->polling_time);
timeout += priv->polling_time;
} while (timeout <= MLXCPLD_I2C_XFER_TO);
if (timeout > MLXCPLD_I2C_XFER_TO)
@@ -288,10 +289,10 @@ static int mlxcpld_i2c_wait_for_tc(struct mlxcpld_i2c_priv *priv)
u8 datalen, val;
do {
usleep_range(MLXCPLD_I2C_POLL_TIME / 2, MLXCPLD_I2C_POLL_TIME);
usleep_range(priv->polling_time / 2, priv->polling_time);
if (!mlxcpld_i2c_check_status(priv, &status))
break;
timeout += MLXCPLD_I2C_POLL_TIME;
timeout += priv->polling_time;
} while (status == 0 && timeout < MLXCPLD_I2C_XFER_TO);
switch (status) {
@@ -498,9 +499,11 @@ mlxcpld_i2c_set_frequency(struct mlxcpld_i2c_priv *priv,
switch ((regval & data->mask) >> data->bit) {
case MLXCPLD_I2C_FREQ_1000KHZ:
freq = MLXCPLD_I2C_FREQ_1000KHZ_SET;
priv->polling_time /= 4;
break;
case MLXCPLD_I2C_FREQ_400KHZ:
freq = MLXCPLD_I2C_FREQ_400KHZ_SET;
priv->polling_time /= 4;
break;
default:
return 0;
@@ -527,6 +530,7 @@ static int mlxcpld_i2c_probe(struct platform_device *pdev)
priv->dev = &pdev->dev;
priv->base_addr = MLXPLAT_CPLD_LPC_I2C_BASE_ADDR;
priv->polling_time = MLXCPLD_I2C_POLL_TIME;
/* Set I2C bus frequency if platform data provides this info. */
pdata = dev_get_platdata(&pdev->dev);
+81 -1
View File
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
@@ -49,6 +50,8 @@
#define I2C_RD_TRANAC_VALUE 0x0001
#define I2C_SCL_MIS_COMP_VALUE 0x0000
#define I2C_CHN_CLR_FLAG 0x0000
#define I2C_RELIABILITY 0x0010
#define I2C_DMAACK_ENABLE 0x0008
#define I2C_DMA_CON_TX 0x0000
#define I2C_DMA_CON_RX 0x0001
@@ -127,6 +130,7 @@ enum I2C_REGS_OFFSET {
OFFSET_HS,
OFFSET_SOFTRESET,
OFFSET_DCM_EN,
OFFSET_MULTI_DMA,
OFFSET_PATH_DIR,
OFFSET_DEBUGSTAT,
OFFSET_DEBUGCTRL,
@@ -194,8 +198,9 @@ static const u16 mt_i2c_regs_v2[] = {
[OFFSET_TRANSFER_LEN_AUX] = 0x44,
[OFFSET_CLOCK_DIV] = 0x48,
[OFFSET_SOFTRESET] = 0x50,
[OFFSET_MULTI_DMA] = 0x8c,
[OFFSET_SCL_MIS_COMP_POINT] = 0x90,
[OFFSET_DEBUGSTAT] = 0xe0,
[OFFSET_DEBUGSTAT] = 0xe4,
[OFFSET_DEBUGCTRL] = 0xe8,
[OFFSET_FIFO_STAT] = 0xf4,
[OFFSET_FIFO_THRESH] = 0xf8,
@@ -842,6 +847,57 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
return 0;
}
static void i2c_dump_register(struct mtk_i2c *i2c)
{
dev_dbg(i2c->dev, "SLAVE_ADDR: 0x%x, INTR_MASK: 0x%x\n",
mtk_i2c_readw(i2c, OFFSET_SLAVE_ADDR),
mtk_i2c_readw(i2c, OFFSET_INTR_MASK));
dev_dbg(i2c->dev, "INTR_STAT: 0x%x, CONTROL: 0x%x\n",
mtk_i2c_readw(i2c, OFFSET_INTR_STAT),
mtk_i2c_readw(i2c, OFFSET_CONTROL));
dev_dbg(i2c->dev, "TRANSFER_LEN: 0x%x, TRANSAC_LEN: 0x%x\n",
mtk_i2c_readw(i2c, OFFSET_TRANSFER_LEN),
mtk_i2c_readw(i2c, OFFSET_TRANSAC_LEN));
dev_dbg(i2c->dev, "DELAY_LEN: 0x%x, HTIMING: 0x%x\n",
mtk_i2c_readw(i2c, OFFSET_DELAY_LEN),
mtk_i2c_readw(i2c, OFFSET_TIMING));
dev_dbg(i2c->dev, "START: 0x%x, EXT_CONF: 0x%x\n",
mtk_i2c_readw(i2c, OFFSET_START),
mtk_i2c_readw(i2c, OFFSET_EXT_CONF));
dev_dbg(i2c->dev, "HS: 0x%x, IO_CONFIG: 0x%x\n",
mtk_i2c_readw(i2c, OFFSET_HS),
mtk_i2c_readw(i2c, OFFSET_IO_CONFIG));
dev_dbg(i2c->dev, "DCM_EN: 0x%x, TRANSFER_LEN_AUX: 0x%x\n",
mtk_i2c_readw(i2c, OFFSET_DCM_EN),
mtk_i2c_readw(i2c, OFFSET_TRANSFER_LEN_AUX));
dev_dbg(i2c->dev, "CLOCK_DIV: 0x%x, FIFO_STAT: 0x%x\n",
mtk_i2c_readw(i2c, OFFSET_CLOCK_DIV),
mtk_i2c_readw(i2c, OFFSET_FIFO_STAT));
dev_dbg(i2c->dev, "DEBUGCTRL : 0x%x, DEBUGSTAT: 0x%x\n",
mtk_i2c_readw(i2c, OFFSET_DEBUGCTRL),
mtk_i2c_readw(i2c, OFFSET_DEBUGSTAT));
if (i2c->dev_comp->regs == mt_i2c_regs_v2) {
dev_dbg(i2c->dev, "LTIMING: 0x%x, MULTI_DMA: 0x%x\n",
mtk_i2c_readw(i2c, OFFSET_LTIMING),
mtk_i2c_readw(i2c, OFFSET_MULTI_DMA));
}
dev_dbg(i2c->dev, "\nDMA_INT_FLAG: 0x%x, DMA_INT_EN: 0x%x\n",
readl(i2c->pdmabase + OFFSET_INT_FLAG),
readl(i2c->pdmabase + OFFSET_INT_EN));
dev_dbg(i2c->dev, "DMA_EN: 0x%x, DMA_CON: 0x%x\n",
readl(i2c->pdmabase + OFFSET_EN),
readl(i2c->pdmabase + OFFSET_CON));
dev_dbg(i2c->dev, "DMA_TX_MEM_ADDR: 0x%x, DMA_RX_MEM_ADDR: 0x%x\n",
readl(i2c->pdmabase + OFFSET_TX_MEM_ADDR),
readl(i2c->pdmabase + OFFSET_RX_MEM_ADDR));
dev_dbg(i2c->dev, "DMA_TX_LEN: 0x%x, DMA_RX_LEN: 0x%x\n",
readl(i2c->pdmabase + OFFSET_TX_LEN),
readl(i2c->pdmabase + OFFSET_RX_LEN));
dev_dbg(i2c->dev, "DMA_TX_4G_MODE: 0x%x, DMA_RX_4G_MODE: 0x%x",
readl(i2c->pdmabase + OFFSET_TX_4G_MODE),
readl(i2c->pdmabase + OFFSET_RX_4G_MODE));
}
static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
int num, int left_num)
{
@@ -851,6 +907,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
u16 restart_flag = 0;
u16 dma_sync = 0;
u32 reg_4g_mode;
u32 reg_dma_reset;
u8 *dma_rd_buf = NULL;
u8 *dma_wr_buf = NULL;
dma_addr_t rpaddr = 0;
@@ -864,6 +921,28 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
reinit_completion(&i2c->msg_complete);
if (i2c->dev_comp->apdma_sync &&
i2c->op != I2C_MASTER_WRRD && num > 1) {
mtk_i2c_writew(i2c, 0x00, OFFSET_DEBUGCTRL);
writel(I2C_DMA_HANDSHAKE_RST | I2C_DMA_WARM_RST,
i2c->pdmabase + OFFSET_RST);
ret = readw_poll_timeout(i2c->pdmabase + OFFSET_RST,
reg_dma_reset,
!(reg_dma_reset & I2C_DMA_WARM_RST),
0, 100);
if (ret) {
dev_err(i2c->dev, "DMA warm reset timeout\n");
return -ETIMEDOUT;
}
writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST);
mtk_i2c_writew(i2c, I2C_HANDSHAKE_RST, OFFSET_SOFTRESET);
mtk_i2c_writew(i2c, I2C_CHN_CLR_FLAG, OFFSET_SOFTRESET);
mtk_i2c_writew(i2c, I2C_RELIABILITY | I2C_DMAACK_ENABLE,
OFFSET_DEBUGCTRL);
}
control_reg = mtk_i2c_readw(i2c, OFFSET_CONTROL) &
~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS);
if ((i2c->speed_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) || (left_num >= 1))
@@ -1049,6 +1128,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
if (ret == 0) {
dev_dbg(i2c->dev, "addr: %x, transfer timeout\n", msgs->addr);
i2c_dump_register(i2c);
mtk_i2c_init_hw(i2c);
return -ETIMEDOUT;
}
@@ -15,20 +15,14 @@
#include <linux/slab.h>
#include <linux/io.h>
static struct pci_driver pasemi_smb_driver;
struct pasemi_smbus {
struct pci_dev *dev;
struct i2c_adapter adapter;
unsigned long base;
int size;
};
#include "i2c-pasemi-core.h"
/* Register offsets */
#define REG_MTXFIFO 0x00
#define REG_MRXFIFO 0x04
#define REG_SMSTA 0x14
#define REG_CTL 0x1c
#define REG_REV 0x28
/* Register defs */
#define MTXFIFO_READ 0x00000400
@@ -44,30 +38,36 @@ struct pasemi_smbus {
#define CTL_MRR 0x00000400
#define CTL_MTR 0x00000200
#define CTL_EN 0x00000800
#define CTL_CLK_M 0x000000ff
#define CLK_100K_DIV 84
#define CLK_400K_DIV 21
static inline void reg_write(struct pasemi_smbus *smbus, int reg, int val)
{
dev_dbg(&smbus->dev->dev, "smbus write reg %lx val %08x\n",
smbus->base + reg, val);
outl(val, smbus->base + reg);
dev_dbg(smbus->dev, "smbus write reg %x val %08x\n", reg, val);
iowrite32(val, smbus->ioaddr + reg);
}
static inline int reg_read(struct pasemi_smbus *smbus, int reg)
{
int ret;
ret = inl(smbus->base + reg);
dev_dbg(&smbus->dev->dev, "smbus read reg %lx val %08x\n",
smbus->base + reg, ret);
ret = ioread32(smbus->ioaddr + reg);
dev_dbg(smbus->dev, "smbus read reg %x val %08x\n", reg, ret);
return ret;
}
#define TXFIFO_WR(smbus, reg) reg_write((smbus), REG_MTXFIFO, (reg))
#define RXFIFO_RD(smbus) reg_read((smbus), REG_MRXFIFO)
static void pasemi_reset(struct pasemi_smbus *smbus)
{
u32 val = (CTL_MTR | CTL_MRR | (smbus->clk_div & CTL_CLK_M));
if (smbus->hw_rev >= 6)
val |= CTL_EN;
reg_write(smbus, REG_CTL, val);
}
static void pasemi_smb_clear(struct pasemi_smbus *smbus)
{
unsigned int status;
@@ -93,7 +93,7 @@ static int pasemi_smb_waitready(struct pasemi_smbus *smbus)
return -ENXIO;
if (timeout < 0) {
dev_warn(&smbus->dev->dev, "Timeout, status 0x%08x\n", status);
dev_warn(smbus->dev, "Timeout, status 0x%08x\n", status);
reg_write(smbus, REG_SMSTA, status);
return -ETIME;
}
@@ -142,8 +142,7 @@ static int pasemi_i2c_xfer_msg(struct i2c_adapter *adapter,
return 0;
reset_out:
reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR |
(CLK_100K_DIV & CTL_CLK_M)));
pasemi_reset(smbus);
return err;
}
@@ -309,8 +308,7 @@ static int pasemi_smb_xfer(struct i2c_adapter *adapter,
return 0;
reset_out:
reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR |
(CLK_100K_DIV & CTL_CLK_M)));
pasemi_reset(smbus);
return err;
}
@@ -328,82 +326,28 @@ static const struct i2c_algorithm smbus_algorithm = {
.functionality = pasemi_smb_func,
};
static int pasemi_smb_probe(struct pci_dev *dev,
const struct pci_device_id *id)
int pasemi_i2c_common_probe(struct pasemi_smbus *smbus)
{
struct pasemi_smbus *smbus;
int error;
if (!(pci_resource_flags(dev, 0) & IORESOURCE_IO))
return -ENODEV;
smbus = kzalloc(sizeof(struct pasemi_smbus), GFP_KERNEL);
if (!smbus)
return -ENOMEM;
smbus->dev = dev;
smbus->base = pci_resource_start(dev, 0);
smbus->size = pci_resource_len(dev, 0);
if (!request_region(smbus->base, smbus->size,
pasemi_smb_driver.name)) {
error = -EBUSY;
goto out_kfree;
}
smbus->adapter.owner = THIS_MODULE;
snprintf(smbus->adapter.name, sizeof(smbus->adapter.name),
"PA Semi SMBus adapter at 0x%lx", smbus->base);
"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;
/* set up the sysfs linkage to our parent device */
smbus->adapter.dev.parent = &dev->dev;
smbus->adapter.dev.parent = smbus->dev;
reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR |
(CLK_100K_DIV & CTL_CLK_M)));
if (smbus->hw_rev != PASEMI_HW_REV_PCI)
smbus->hw_rev = reg_read(smbus, REG_REV);
error = i2c_add_adapter(&smbus->adapter);
pasemi_reset(smbus);
error = devm_i2c_add_adapter(smbus->dev, &smbus->adapter);
if (error)
goto out_release_region;
pci_set_drvdata(dev, smbus);
return error;
return 0;
out_release_region:
release_region(smbus->base, smbus->size);
out_kfree:
kfree(smbus);
return error;
}
static void pasemi_smb_remove(struct pci_dev *dev)
{
struct pasemi_smbus *smbus = pci_get_drvdata(dev);
i2c_del_adapter(&smbus->adapter);
release_region(smbus->base, smbus->size);
kfree(smbus);
}
static const struct pci_device_id pasemi_smb_ids[] = {
{ PCI_DEVICE(0x1959, 0xa003) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, pasemi_smb_ids);
static struct pci_driver pasemi_smb_driver = {
.name = "i2c-pasemi",
.id_table = pasemi_smb_ids,
.probe = pasemi_smb_probe,
.remove = pasemi_smb_remove,
};
module_pci_driver(pasemi_smb_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>");
MODULE_DESCRIPTION("PA Semi PWRficient SMBus driver");
+21
View File
@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/atomic.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/i2c-smbus.h>
#include <linux/io.h>
#include <linux/kernel.h>
#define PASEMI_HW_REV_PCI -1
struct pasemi_smbus {
struct device *dev;
struct i2c_adapter adapter;
void __iomem *ioaddr;
unsigned int clk_div;
int hw_rev;
};
int pasemi_i2c_common_probe(struct pasemi_smbus *smbus);
+85
View File
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2006-2007 PA Semi, Inc
*
* SMBus host driver for PA Semi PWRficient
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/io.h>
#include "i2c-pasemi-core.h"
#define CLK_100K_DIV 84
#define CLK_400K_DIV 21
static struct pci_driver pasemi_smb_pci_driver;
static int pasemi_smb_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
struct pasemi_smbus *smbus;
unsigned long base;
int size;
int error;
if (!(pci_resource_flags(dev, 0) & IORESOURCE_IO))
return -ENODEV;
smbus = devm_kzalloc(&dev->dev, sizeof(*smbus), GFP_KERNEL);
if (!smbus)
return -ENOMEM;
smbus->dev = &dev->dev;
base = pci_resource_start(dev, 0);
size = pci_resource_len(dev, 0);
smbus->clk_div = CLK_100K_DIV;
/*
* The original PASemi PCI controllers don't have a register for
* their HW revision.
*/
smbus->hw_rev = PASEMI_HW_REV_PCI;
if (!devm_request_region(&dev->dev, base, size,
pasemi_smb_pci_driver.name))
return -EBUSY;
smbus->ioaddr = pcim_iomap(dev, 0, 0);
if (!smbus->ioaddr)
return -EBUSY;
error = pasemi_i2c_common_probe(smbus);
if (error)
return error;
pci_set_drvdata(dev, smbus);
return 0;
}
static const struct pci_device_id pasemi_smb_pci_ids[] = {
{ PCI_DEVICE(0x1959, 0xa003) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, pasemi_smb_pci_ids);
static struct pci_driver pasemi_smb_pci_driver = {
.name = "i2c-pasemi",
.id_table = pasemi_smb_pci_ids,
.probe = pasemi_smb_pci_probe,
};
module_pci_driver(pasemi_smb_pci_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Olof Johansson <olof@lixom.net>");
MODULE_DESCRIPTION("PA Semi PWRficient SMBus driver");
+122
View File
@@ -0,0 +1,122 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021 The Asahi Linux Contributors
*
* PA Semi PWRficient SMBus host driver for Apple SoCs
*/
#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include "i2c-pasemi-core.h"
struct pasemi_platform_i2c_data {
struct pasemi_smbus smbus;
struct clk *clk_ref;
};
static int
pasemi_platform_i2c_calc_clk_div(struct pasemi_platform_i2c_data *data,
u32 frequency)
{
unsigned long clk_rate = clk_get_rate(data->clk_ref);
if (!clk_rate)
return -EINVAL;
data->smbus.clk_div = DIV_ROUND_UP(clk_rate, 16 * frequency);
if (data->smbus.clk_div < 4)
return dev_err_probe(data->smbus.dev, -EINVAL,
"Bus frequency %d is too fast.\n",
frequency);
if (data->smbus.clk_div > 0xff)
return dev_err_probe(data->smbus.dev, -EINVAL,
"Bus frequency %d is too slow.\n",
frequency);
return 0;
}
static int pasemi_platform_i2c_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct pasemi_platform_i2c_data *data;
struct pasemi_smbus *smbus;
u32 frequency;
int error;
data = devm_kzalloc(dev, sizeof(struct pasemi_platform_i2c_data),
GFP_KERNEL);
if (!data)
return -ENOMEM;
smbus = &data->smbus;
smbus->dev = dev;
smbus->ioaddr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(smbus->ioaddr))
return PTR_ERR(smbus->ioaddr);
if (of_property_read_u32(dev->of_node, "clock-frequency", &frequency))
frequency = I2C_MAX_STANDARD_MODE_FREQ;
data->clk_ref = devm_clk_get(dev, NULL);
if (IS_ERR(data->clk_ref))
return PTR_ERR(data->clk_ref);
error = clk_prepare_enable(data->clk_ref);
if (error)
return error;
error = pasemi_platform_i2c_calc_clk_div(data, frequency);
if (error)
goto out_clk_disable;
smbus->adapter.dev.of_node = pdev->dev.of_node;
error = pasemi_i2c_common_probe(smbus);
if (error)
goto out_clk_disable;
platform_set_drvdata(pdev, data);
return 0;
out_clk_disable:
clk_disable_unprepare(data->clk_ref);
return error;
}
static int pasemi_platform_i2c_remove(struct platform_device *pdev)
{
struct pasemi_platform_i2c_data *data = platform_get_drvdata(pdev);
clk_disable_unprepare(data->clk_ref);
return 0;
}
static const struct of_device_id pasemi_platform_i2c_of_match[] = {
{ .compatible = "apple,t8103-i2c" },
{ .compatible = "apple,i2c" },
{},
};
MODULE_DEVICE_TABLE(of, pasemi_platform_i2c_of_match);
static struct platform_driver pasemi_platform_i2c_driver = {
.driver = {
.name = "i2c-apple",
.of_match_table = pasemi_platform_i2c_of_match,
},
.probe = pasemi_platform_i2c_probe,
.remove = pasemi_platform_i2c_remove,
};
module_platform_driver(pasemi_platform_i2c_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>");
MODULE_DESCRIPTION("Apple/PASemi SMBus platform driver");
-1
View File
@@ -1547,7 +1547,6 @@ static void __exit i2c_adap_pxa_exit(void)
}
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:pxa2xx-i2c");
subsys_initcall(i2c_adap_pxa_init);
module_exit(i2c_adap_pxa_exit);
+3 -3
View File
@@ -1290,7 +1290,7 @@ static void qup_i2c_write_rx_tags_v2(struct qup_i2c_dev *qup)
* 1. Check if tx_tags_sent is false i.e. the start of QUP block so write the
* tags to TX FIFO and set tx_tags_sent to true.
* 2. Check if send_last_word is true. It will be set when last few data bytes
* (less than 4 bytes) are reamining to be written in FIFO because of no FIFO
* (less than 4 bytes) are remaining to be written in FIFO because of no FIFO
* space. All this data bytes are available in tx_fifo_data so write this
* in FIFO.
* 3. Write the data to TX FIFO and check for cur_blk_len. If it is non zero
@@ -1797,12 +1797,12 @@ nodma:
goto fail;
ret = devm_request_irq(qup->dev, qup->irq, qup_i2c_interrupt,
IRQF_TRIGGER_HIGH, "i2c_qup", qup);
IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN,
"i2c_qup", qup);
if (ret) {
dev_err(qup->dev, "Request %d IRQ failed\n", qup->irq);
goto fail;
}
disable_irq(qup->irq);
hw_ver = readl(qup->base + QUP_HW_VERSION);
dev_dbg(qup->dev, "Revision %x\n", hw_ver);
+3 -3
View File
@@ -339,6 +339,9 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv)
priv->flags |= ID_LAST_MSG;
rcar_i2c_write(priv, ICMAR, i2c_8bit_addr_from_msg(priv->msg));
if (!priv->atomic_xfer)
rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
/*
* We don't have a test case but the HW engineers say that the write order
* of ICMSR and ICMCR depends on whether we issue START or REP_START. Since
@@ -354,9 +357,6 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv)
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
rcar_i2c_write(priv, ICMSR, 0);
}
if (!priv->atomic_xfer)
rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
}
static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv)

Some files were not shown because too many files have changed in this diff Show More