Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Documentation/devicetree/bindings/i2c/qcom,i2c-geni-qcom.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ properties:
power-domains:
maxItems: 1

qcom,qup-multi-owner:
type: boolean
description:
Indicates that the QUP-based controller is shared with one or more
other system processors and must not be assumed to have exclusive
ownership by the operating system.

reg:
maxItems: 1

Expand Down
44 changes: 43 additions & 1 deletion drivers/dma/qcom/gpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2020, Linaro Limited
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/

#include <dt-bindings/dma/qcom-gpi.h>
Expand Down Expand Up @@ -67,6 +68,14 @@
#define TRE_DMA_LEN GENMASK(23, 0)
#define TRE_DMA_IMMEDIATE_LEN GENMASK(3, 0)

/* Lock TRE */
#define TRE_LOCK BIT(0)
#define TRE_MINOR_TYPE GENMASK(19, 16)
#define TRE_MAJOR_TYPE GENMASK(23, 20)

/* Unlock TRE */
#define TRE_UNLOCK BIT(8)

/* Register offsets from gpi-top */
#define GPII_n_CH_k_CNTXT_0_OFFS(n, k) (0x20000 + (0x4000 * (n)) + (0x80 * (k)))
#define GPII_n_CH_k_CNTXT_0_EL_SIZE GENMASK(31, 24)
Expand Down Expand Up @@ -518,7 +527,7 @@ struct gpii {
bool ieob_set;
};

#define MAX_TRE 3
#define MAX_TRE 5

struct gpi_desc {
struct virt_dma_desc vd;
Expand Down Expand Up @@ -1625,12 +1634,27 @@ static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,
unsigned long flags)
{
struct gpi_i2c_config *i2c = chan->config;
enum gpi_lock_action lock_action = i2c->lock_action;
struct device *dev = chan->gpii->gpi_dev->dev;
unsigned int tre_idx = 0;
dma_addr_t address;
struct gpi_tre *tre;
unsigned int i;

/* Optional lock TRE before transfer */
if (lock_action == GPI_LOCK_ACQUIRE) {
tre = &desc->tre[tre_idx];
tre_idx++;

tre->dword[0] = 0;
tre->dword[1] = 0;
tre->dword[2] = 0;
tre->dword[3] = u32_encode_bits(1, TRE_LOCK);
tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOB);
tre->dword[3] |= u32_encode_bits(0, TRE_MINOR_TYPE);
tre->dword[3] |= u32_encode_bits(3, TRE_MAJOR_TYPE);
}

/* first create config tre if applicable */
if (i2c->set_config) {
tre = &desc->tre[tre_idx];
Expand Down Expand Up @@ -1690,6 +1714,24 @@ static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,

if (!(flags & DMA_PREP_INTERRUPT))
tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_BEI);

/* If multi-owner and this is the release boundary, chain it */
if (i2c->lock_action == GPI_LOCK_RELEASE)
tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN);
}

/* Optional unlock TRE after transfer */
if (lock_action == GPI_LOCK_RELEASE && i2c->op != I2C_READ) {
tre = &desc->tre[tre_idx];
tre_idx++;

tre->dword[0] = 0;
tre->dword[1] = 0;
tre->dword[2] = 0;
tre->dword[3] = u32_encode_bits(1, TRE_UNLOCK);
tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOB);
tre->dword[3] |= u32_encode_bits(1, TRE_MINOR_TYPE);
tre->dword[3] |= u32_encode_bits(3, TRE_MAJOR_TYPE);
}

for (i = 0; i < tre_idx; i++)
Expand Down
27 changes: 26 additions & 1 deletion drivers/i2c/busses/i2c-qcom-geni.c
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,14 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i
if (i < num - 1)
peripheral.stretch = 1;

peripheral.lock_action = GPI_LOCK_NONE;
if (gi2c->se.multi_owner) {
if (i == 0)
peripheral.lock_action = GPI_LOCK_ACQUIRE;
else if (i == num - 1)
peripheral.lock_action = GPI_LOCK_RELEASE;
}

peripheral.addr = msgs[i].addr;
if (i > 0 && (!(msgs[i].flags & I2C_M_RD)))
peripheral.multi_msg = false;
Expand Down Expand Up @@ -1014,6 +1022,17 @@ static int geni_i2c_probe(struct platform_device *pdev)
gi2c->clk_freq_out = I2C_MAX_STANDARD_MODE_FREQ;
}

if (of_property_read_bool(pdev->dev.of_node, "qcom,qup-multi-owner")) {
/*
* Multi-owner controller configuration: the controller may be
* used by another system processor. Mark the SE as shared so
* common GENI resource handling can avoid pin state changes
* that would disrupt the other user.
*/
gi2c->se.multi_owner = true;
dev_dbg(&pdev->dev, "I2C controller is shared with another system processor\n");
}

if (has_acpi_companion(dev))
ACPI_COMPANION_SET(&gi2c->adap.dev, ACPI_COMPANION(dev));

Expand Down Expand Up @@ -1089,7 +1108,9 @@ static int geni_i2c_probe(struct platform_device *pdev)
}

if (fifo_disable) {
/* FIFO is disabled, so we can only use GPI DMA */
/* FIFO is disabled, so we can only use GPI DMA.
* SE can be shared in GSI mode between subsystems, each SS owns a GPII.
*/
gi2c->gpi_mode = true;
ret = setup_gpi_dma(gi2c);
if (ret)
Expand All @@ -1098,6 +1119,10 @@ static int geni_i2c_probe(struct platform_device *pdev)
dev_dbg(dev, "Using GPI DMA mode for I2C\n");
} else {
gi2c->gpi_mode = false;

if (gi2c->se.multi_owner)
dev_err_probe(dev, -EINVAL, "I2C sharing not supported in non GSI mode\n");

tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);

/* I2C Master Hub Serial Elements doesn't have the HW_PARAM_0 register */
Expand Down
15 changes: 11 additions & 4 deletions drivers/soc/qcom/qcom-geni-se.c
Original file line number Diff line number Diff line change
Expand Up @@ -597,10 +597,17 @@ int geni_se_resources_off(struct geni_se *se)

if (has_acpi_companion(se->dev))
return 0;

ret = pinctrl_pm_select_sleep_state(se->dev);
if (ret)
return ret;
/*
* Select the "sleep" pinctrl state only when the serial engine is
* exclusively owned by this system processor. For shared controller
* configurations, another system processor may still be using the pins,
* and switching them to "sleep" can disrupt ongoing transfers.
*/
if (!se->multi_owner) {
ret = pinctrl_pm_select_sleep_state(se->dev);
if (ret)
return ret;
}

geni_se_clks_off(se);
return 0;
Expand Down
18 changes: 18 additions & 0 deletions include/linux/dma/qcom-gpi-dma.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2020, Linaro Limited
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/

#ifndef QCOM_GPI_DMA_H
Expand Down Expand Up @@ -51,6 +52,21 @@ enum i2c_op {
I2C_READ,
};

/**
* enum gpi_lock_action - request lock/unlock TRE sequencing
* @GPI_LOCK_NONE: No lock/unlock TRE requested for this transfer
* @GPI_LOCK_ACQUIRE: Emit a lock TRE before the transfer
* @GPI_LOCK_RELEASE: Emit an unlock TRE after the transfer
*
* Used by protocol drivers for multi-owner controller setups (e.g. when
* DeviceTree indicates the controller is shared via qcom,qup-multi-owner).
*/
enum gpi_lock_action {
GPI_LOCK_NONE = 0,
GPI_LOCK_ACQUIRE,
GPI_LOCK_RELEASE,
};

/**
* struct gpi_i2c_config - i2c config for peripheral
*
Expand All @@ -65,6 +81,7 @@ enum i2c_op {
* @rx_len: receive length for buffer
* @op: i2c cmd
* @muli-msg: is part of multi i2c r-w msgs
* @lock_action: request lock/unlock TRE sequencing for this transfer
*/
struct gpi_i2c_config {
u8 set_config;
Expand All @@ -78,6 +95,7 @@ struct gpi_i2c_config {
u32 rx_len;
enum i2c_op op;
bool multi_msg;
enum gpi_lock_action lock_action;
};

#endif /* QCOM_GPI_DMA_H */
2 changes: 2 additions & 0 deletions include/linux/soc/qcom/geni-se.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ struct geni_icc_path {
* @num_clk_levels: Number of valid clock levels in clk_perf_tbl
* @clk_perf_tbl: Table of clock frequency input to serial engine clock
* @icc_paths: Array of ICC paths for SE
* @multi_owner: True if SE is shared between multiprocessors.
*/
struct geni_se {
void __iomem *base;
Expand All @@ -72,6 +73,7 @@ struct geni_se {
unsigned int num_clk_levels;
unsigned long *clk_perf_tbl;
struct geni_icc_path icc_paths[3];
bool multi_owner;
};

/* Common SE registers */
Expand Down