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:
Коммит
dab334c98b
|
@ -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>;
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
|
@ -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);
|
|
@ -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");
|
|
@ -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");
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1700,7 +1700,7 @@ static int tegra_i2c_init_hardware(struct tegra_i2c_dev *i2c_dev)
|
|||
else
|
||||
ret = tegra_i2c_init(i2c_dev);
|
||||
|
||||
pm_runtime_put(i2c_dev->dev);
|
||||
pm_runtime_put_sync(i2c_dev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1819,7 +1819,7 @@ static int tegra_i2c_remove(struct platform_device *pdev)
|
|||
struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
|
||||
|
||||
i2c_del_adapter(&i2c_dev->adapter);
|
||||
pm_runtime_disable(i2c_dev->dev);
|
||||
pm_runtime_force_suspend(i2c_dev->dev);
|
||||
|
||||
tegra_i2c_release_dma(i2c_dev);
|
||||
tegra_i2c_release_clocks(i2c_dev);
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/platform_data/i2c-xiic.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -48,7 +48,7 @@ enum xiic_endian {
|
|||
* struct xiic_i2c - Internal representation of the XIIC I2C bus
|
||||
* @dev: Pointer to device structure
|
||||
* @base: Memory base of the HW registers
|
||||
* @wait: Wait queue for callers
|
||||
* @completion: Completion for callers
|
||||
* @adap: Kernel adapter representation
|
||||
* @tx_msg: Messages from above to be sent
|
||||
* @lock: Mutual exclusion
|
||||
|
@ -64,7 +64,7 @@ enum xiic_endian {
|
|||
struct xiic_i2c {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
wait_queue_head_t wait;
|
||||
struct completion completion;
|
||||
struct i2c_adapter adap;
|
||||
struct i2c_msg *tx_msg;
|
||||
struct mutex lock;
|
||||
|
@ -160,6 +160,9 @@ struct xiic_i2c {
|
|||
#define XIIC_PM_TIMEOUT 1000 /* ms */
|
||||
/* timeout waiting for the controller to respond */
|
||||
#define XIIC_I2C_TIMEOUT (msecs_to_jiffies(1000))
|
||||
/* timeout waiting for the controller finish transfers */
|
||||
#define XIIC_XFER_TIMEOUT (msecs_to_jiffies(10000))
|
||||
|
||||
/*
|
||||
* The following constant is used for the device global interrupt enable
|
||||
* register, to enable all interrupts for the device, this is the only bit
|
||||
|
@ -170,7 +173,7 @@ struct xiic_i2c {
|
|||
#define xiic_tx_space(i2c) ((i2c)->tx_msg->len - (i2c)->tx_pos)
|
||||
#define xiic_rx_space(i2c) ((i2c)->rx_msg->len - (i2c)->rx_pos)
|
||||
|
||||
static int xiic_start_xfer(struct xiic_i2c *i2c);
|
||||
static int xiic_start_xfer(struct xiic_i2c *i2c, struct i2c_msg *msgs, int num);
|
||||
static void __xiic_start_xfer(struct xiic_i2c *i2c);
|
||||
|
||||
/*
|
||||
|
@ -367,7 +370,7 @@ static void xiic_wakeup(struct xiic_i2c *i2c, int code)
|
|||
i2c->rx_msg = NULL;
|
||||
i2c->nmsgs = 0;
|
||||
i2c->state = code;
|
||||
wake_up(&i2c->wait);
|
||||
complete(&i2c->completion);
|
||||
}
|
||||
|
||||
static irqreturn_t xiic_process(int irq, void *dev_id)
|
||||
|
@ -375,6 +378,9 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
|
|||
struct xiic_i2c *i2c = dev_id;
|
||||
u32 pend, isr, ier;
|
||||
u32 clr = 0;
|
||||
int xfer_more = 0;
|
||||
int wakeup_req = 0;
|
||||
int wakeup_code = 0;
|
||||
|
||||
/* Get the interrupt Status from the IPIF. There is no clearing of
|
||||
* interrupts in the IPIF. Interrupts must be cleared at the source.
|
||||
|
@ -411,10 +417,14 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
|
|||
*/
|
||||
xiic_reinit(i2c);
|
||||
|
||||
if (i2c->rx_msg)
|
||||
xiic_wakeup(i2c, STATE_ERROR);
|
||||
if (i2c->tx_msg)
|
||||
xiic_wakeup(i2c, STATE_ERROR);
|
||||
if (i2c->rx_msg) {
|
||||
wakeup_req = 1;
|
||||
wakeup_code = STATE_ERROR;
|
||||
}
|
||||
if (i2c->tx_msg) {
|
||||
wakeup_req = 1;
|
||||
wakeup_code = STATE_ERROR;
|
||||
}
|
||||
}
|
||||
if (pend & XIIC_INTR_RX_FULL_MASK) {
|
||||
/* Receive register/FIFO is full */
|
||||
|
@ -448,8 +458,7 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
|
|||
i2c->tx_msg++;
|
||||
dev_dbg(i2c->adap.dev.parent,
|
||||
"%s will start next...\n", __func__);
|
||||
|
||||
__xiic_start_xfer(i2c);
|
||||
xfer_more = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -463,11 +472,13 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
|
|||
if (!i2c->tx_msg)
|
||||
goto out;
|
||||
|
||||
if ((i2c->nmsgs == 1) && !i2c->rx_msg &&
|
||||
xiic_tx_space(i2c) == 0)
|
||||
xiic_wakeup(i2c, STATE_DONE);
|
||||
wakeup_req = 1;
|
||||
|
||||
if (i2c->nmsgs == 1 && !i2c->rx_msg &&
|
||||
xiic_tx_space(i2c) == 0)
|
||||
wakeup_code = STATE_DONE;
|
||||
else
|
||||
xiic_wakeup(i2c, STATE_ERROR);
|
||||
wakeup_code = STATE_ERROR;
|
||||
}
|
||||
if (pend & (XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK)) {
|
||||
/* Transmit register/FIFO is empty or ½ empty */
|
||||
|
@ -491,7 +502,7 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
|
|||
if (i2c->nmsgs > 1) {
|
||||
i2c->nmsgs--;
|
||||
i2c->tx_msg++;
|
||||
__xiic_start_xfer(i2c);
|
||||
xfer_more = 1;
|
||||
} else {
|
||||
xiic_irq_dis(i2c, XIIC_INTR_TX_HALF_MASK);
|
||||
|
||||
|
@ -509,6 +520,13 @@ out:
|
|||
dev_dbg(i2c->adap.dev.parent, "%s clr: 0x%x\n", __func__, clr);
|
||||
|
||||
xiic_setreg32(i2c, XIIC_IISR_OFFSET, clr);
|
||||
if (xfer_more)
|
||||
__xiic_start_xfer(i2c);
|
||||
if (wakeup_req)
|
||||
xiic_wakeup(i2c, wakeup_code);
|
||||
|
||||
WARN_ON(xfer_more && wakeup_req);
|
||||
|
||||
mutex_unlock(&i2c->lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -525,7 +543,7 @@ static int xiic_busy(struct xiic_i2c *i2c)
|
|||
int tries = 3;
|
||||
int err;
|
||||
|
||||
if (i2c->tx_msg)
|
||||
if (i2c->tx_msg || i2c->rx_msg)
|
||||
return -EBUSY;
|
||||
|
||||
/* In single master mode bus can only be busy, when in use by this
|
||||
|
@ -554,7 +572,6 @@ static void xiic_start_recv(struct xiic_i2c *i2c)
|
|||
{
|
||||
u8 rx_watermark;
|
||||
struct i2c_msg *msg = i2c->rx_msg = i2c->tx_msg;
|
||||
unsigned long flags;
|
||||
|
||||
/* Clear and enable Rx full interrupt. */
|
||||
xiic_irq_clr_en(i2c, XIIC_INTR_RX_FULL_MASK | XIIC_INTR_TX_ERROR_MASK);
|
||||
|
@ -570,7 +587,6 @@ static void xiic_start_recv(struct xiic_i2c *i2c)
|
|||
rx_watermark = IIC_RX_FIFO_DEPTH;
|
||||
xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, rx_watermark - 1);
|
||||
|
||||
local_irq_save(flags);
|
||||
if (!(msg->flags & I2C_M_NOSTART))
|
||||
/* write the address */
|
||||
xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET,
|
||||
|
@ -580,7 +596,6 @@ static void xiic_start_recv(struct xiic_i2c *i2c)
|
|||
|
||||
xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET,
|
||||
msg->len | ((i2c->nmsgs == 1) ? XIIC_TX_DYN_STOP_MASK : 0));
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (i2c->nmsgs == 1)
|
||||
/* very last, enable bus not busy as well */
|
||||
|
@ -594,8 +609,6 @@ static void xiic_start_send(struct xiic_i2c *i2c)
|
|||
{
|
||||
struct i2c_msg *msg = i2c->tx_msg;
|
||||
|
||||
xiic_irq_clr(i2c, XIIC_INTR_TX_ERROR_MASK);
|
||||
|
||||
dev_dbg(i2c->adap.dev.parent, "%s entry, msg: %p, len: %d",
|
||||
__func__, msg, msg->len);
|
||||
dev_dbg(i2c->adap.dev.parent, "%s entry, ISR: 0x%x, CR: 0x%x\n",
|
||||
|
@ -613,36 +626,17 @@ static void xiic_start_send(struct xiic_i2c *i2c)
|
|||
xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, data);
|
||||
}
|
||||
|
||||
xiic_fill_tx_fifo(i2c);
|
||||
|
||||
/* Clear any pending Tx empty, Tx Error and then enable them. */
|
||||
xiic_irq_clr_en(i2c, XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_ERROR_MASK |
|
||||
XIIC_INTR_BNB_MASK);
|
||||
}
|
||||
XIIC_INTR_BNB_MASK |
|
||||
((i2c->nmsgs > 1 || xiic_tx_space(i2c)) ?
|
||||
XIIC_INTR_TX_HALF_MASK : 0));
|
||||
|
||||
static irqreturn_t xiic_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct xiic_i2c *i2c = dev_id;
|
||||
u32 pend, isr, ier;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
/* Do not processes a devices interrupts if the device has no
|
||||
* interrupts pending
|
||||
*/
|
||||
|
||||
dev_dbg(i2c->adap.dev.parent, "%s entry\n", __func__);
|
||||
|
||||
isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET);
|
||||
ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET);
|
||||
pend = isr & ier;
|
||||
if (pend)
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
|
||||
return ret;
|
||||
xiic_fill_tx_fifo(i2c);
|
||||
}
|
||||
|
||||
static void __xiic_start_xfer(struct xiic_i2c *i2c)
|
||||
{
|
||||
int first = 1;
|
||||
int fifo_space = xiic_tx_fifo_space(i2c);
|
||||
dev_dbg(i2c->adap.dev.parent, "%s entry, msg: %p, fifos space: %d\n",
|
||||
__func__, i2c->tx_msg, fifo_space);
|
||||
|
@ -653,46 +647,34 @@ static void __xiic_start_xfer(struct xiic_i2c *i2c)
|
|||
i2c->rx_pos = 0;
|
||||
i2c->tx_pos = 0;
|
||||
i2c->state = STATE_START;
|
||||
while ((fifo_space >= 2) && (first || (i2c->nmsgs > 1))) {
|
||||
if (!first) {
|
||||
i2c->nmsgs--;
|
||||
i2c->tx_msg++;
|
||||
i2c->tx_pos = 0;
|
||||
} else
|
||||
first = 0;
|
||||
|
||||
if (i2c->tx_msg->flags & I2C_M_RD) {
|
||||
/* we dont date putting several reads in the FIFO */
|
||||
xiic_start_recv(i2c);
|
||||
return;
|
||||
} else {
|
||||
xiic_start_send(i2c);
|
||||
if (xiic_tx_space(i2c) != 0) {
|
||||
/* the message could not be completely sent */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fifo_space = xiic_tx_fifo_space(i2c);
|
||||
if (i2c->tx_msg->flags & I2C_M_RD) {
|
||||
/* we dont date putting several reads in the FIFO */
|
||||
xiic_start_recv(i2c);
|
||||
} else {
|
||||
xiic_start_send(i2c);
|
||||
}
|
||||
|
||||
/* there are more messages or the current one could not be completely
|
||||
* put into the FIFO, also enable the half empty interrupt
|
||||
*/
|
||||
if (i2c->nmsgs > 1 || xiic_tx_space(i2c))
|
||||
xiic_irq_clr_en(i2c, XIIC_INTR_TX_HALF_MASK);
|
||||
|
||||
}
|
||||
|
||||
static int xiic_start_xfer(struct xiic_i2c *i2c)
|
||||
static int xiic_start_xfer(struct xiic_i2c *i2c, struct i2c_msg *msgs, int num)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&i2c->lock);
|
||||
|
||||
ret = xiic_busy(i2c);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
i2c->tx_msg = msgs;
|
||||
i2c->rx_msg = NULL;
|
||||
i2c->nmsgs = num;
|
||||
init_completion(&i2c->completion);
|
||||
|
||||
ret = xiic_reinit(i2c);
|
||||
if (!ret)
|
||||
__xiic_start_xfer(i2c);
|
||||
|
||||
out:
|
||||
mutex_unlock(&i2c->lock);
|
||||
|
||||
return ret;
|
||||
|
@ -710,31 +692,27 @@ static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = xiic_busy(i2c);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
i2c->tx_msg = msgs;
|
||||
i2c->nmsgs = num;
|
||||
|
||||
err = xiic_start_xfer(i2c);
|
||||
err = xiic_start_xfer(i2c, msgs, num);
|
||||
if (err < 0) {
|
||||
dev_err(adap->dev.parent, "Error xiic_start_xfer\n");
|
||||
goto out;
|
||||
return err;
|
||||
}
|
||||
|
||||
if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) ||
|
||||
(i2c->state == STATE_DONE), HZ)) {
|
||||
err = (i2c->state == STATE_DONE) ? num : -EIO;
|
||||
goto out;
|
||||
} else {
|
||||
err = wait_for_completion_timeout(&i2c->completion, XIIC_XFER_TIMEOUT);
|
||||
mutex_lock(&i2c->lock);
|
||||
if (err == 0) { /* Timeout */
|
||||
i2c->tx_msg = NULL;
|
||||
i2c->rx_msg = NULL;
|
||||
i2c->nmsgs = 0;
|
||||
err = -ETIMEDOUT;
|
||||
goto out;
|
||||
} else if (err < 0) { /* Completion error */
|
||||
i2c->tx_msg = NULL;
|
||||
i2c->rx_msg = NULL;
|
||||
i2c->nmsgs = 0;
|
||||
} else {
|
||||
err = (i2c->state == STATE_DONE) ? num : -EIO;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&i2c->lock);
|
||||
pm_runtime_mark_last_busy(i2c->dev);
|
||||
pm_runtime_put_autosuspend(i2c->dev);
|
||||
return err;
|
||||
|
@ -795,7 +773,6 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
|||
i2c->adap.dev.of_node = pdev->dev.of_node;
|
||||
|
||||
mutex_init(&i2c->lock);
|
||||
init_waitqueue_head(&i2c->wait);
|
||||
|
||||
i2c->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(i2c->clk))
|
||||
|
@ -812,7 +789,7 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
|||
pm_runtime_use_autosuspend(i2c->dev);
|
||||
pm_runtime_set_active(i2c->dev);
|
||||
pm_runtime_enable(i2c->dev);
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, xiic_isr,
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
xiic_process, IRQF_ONESHOT,
|
||||
pdev->name, i2c);
|
||||
|
||||
|
|
|
@ -431,11 +431,15 @@ static int xlr_i2c_probe(struct platform_device *pdev)
|
|||
i2c_set_adapdata(&priv->adap, priv);
|
||||
ret = i2c_add_numbered_adapter(&priv->adap);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto err_unprepare_clk;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
dev_info(&priv->adap.dev, "Added I2C Bus.\n");
|
||||
return 0;
|
||||
|
||||
err_unprepare_clk:
|
||||
clk_unprepare(clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xlr_i2c_remove(struct platform_device *pdev)
|
||||
|
|
Загрузка…
Ссылка в новой задаче