Skip to content

Commit

Permalink
Merge branch 'ipa-reg-versions'
Browse files Browse the repository at this point in the history
Alex Elder says:

====================
net: ipa: update registers for other versions

This series updates IPA and GSI register definitions to permit more
versions of IPA hardware to be supported.  Most of the updates are
informational, updating comments to indicate which IPA versions
support each register and field.  But some registers are new and
others are deprecated.  In a few cases register fields are laid
out differently, and in these cases the changes are a little more
substantive.

I won't claim the result is 100% correct, but it's close, and should
allow all IPA versions 3.x through 4.x to be supported by the driver.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Mar 26, 2021
2 parents ae8f586 + 2ad6f03 commit b01483a
Show file tree
Hide file tree
Showing 6 changed files with 364 additions and 123 deletions.
9 changes: 6 additions & 3 deletions drivers/net/ipa/gsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,7 @@ static void gsi_evt_ring_program(struct gsi *gsi, u32 evt_ring_id)
val |= u32_encode_bits(GSI_RING_ELEMENT_SIZE, EV_ELEMENT_SIZE_FMASK);
iowrite32(val, gsi->virt + GSI_EV_CH_E_CNTXT_0_OFFSET(evt_ring_id));

val = u32_encode_bits(size, EV_R_LENGTH_FMASK);
val = ev_r_length_encoded(gsi->version, size);
iowrite32(val, gsi->virt + GSI_EV_CH_E_CNTXT_1_OFFSET(evt_ring_id));

/* The context 2 and 3 registers store the low-order and
Expand Down Expand Up @@ -801,14 +801,14 @@ static void gsi_channel_program(struct gsi_channel *channel, bool doorbell)
channel->tre_ring.index = 0;

/* We program all channels as GPI type/protocol */
val = u32_encode_bits(GSI_CHANNEL_TYPE_GPI, CHTYPE_PROTOCOL_FMASK);
val = chtype_protocol_encoded(gsi->version, GSI_CHANNEL_TYPE_GPI);
if (channel->toward_ipa)
val |= CHTYPE_DIR_FMASK;
val |= u32_encode_bits(channel->evt_ring_id, ERINDEX_FMASK);
val |= u32_encode_bits(GSI_RING_ELEMENT_SIZE, ELEMENT_SIZE_FMASK);
iowrite32(val, gsi->virt + GSI_CH_C_CNTXT_0_OFFSET(channel_id));

val = u32_encode_bits(size, R_LENGTH_FMASK);
val = r_length_encoded(gsi->version, size);
iowrite32(val, gsi->virt + GSI_CH_C_CNTXT_1_OFFSET(channel_id));

/* The context 2 and 3 registers store the low-order and
Expand Down Expand Up @@ -842,6 +842,9 @@ static void gsi_channel_program(struct gsi_channel *channel, bool doorbell)
val |= u32_encode_bits(GSI_ESCAPE_BUF_ONLY,
PREFETCH_MODE_FMASK);
}
/* All channels set DB_IN_BYTES */
if (gsi->version >= IPA_VERSION_4_9)
val |= DB_IN_BYTES;

iowrite32(val, gsi->virt + GSI_CH_C_QOS_OFFSET(channel_id));

Expand Down
69 changes: 55 additions & 14 deletions drivers/net/ipa/gsi_reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@
(0x0000c01c + 0x1000 * (ee))

/* All other register offsets are relative to gsi->virt */

/** enum gsi_channel_type - CHTYPE_PROTOCOL field values in CH_C_CNTXT_0 */
enum gsi_channel_type {
GSI_CHANNEL_TYPE_MHI = 0x0,
GSI_CHANNEL_TYPE_XHCI = 0x1,
GSI_CHANNEL_TYPE_GPI = 0x2,
GSI_CHANNEL_TYPE_XDCI = 0x3,
GSI_CHANNEL_TYPE_WDI2 = 0x4,
GSI_CHANNEL_TYPE_GCI = 0x5,
GSI_CHANNEL_TYPE_WDI3 = 0x6,
GSI_CHANNEL_TYPE_MHIP = 0x7,
GSI_CHANNEL_TYPE_AQC = 0x8,
GSI_CHANNEL_TYPE_11AD = 0x9,
};

#define GSI_CH_C_CNTXT_0_OFFSET(ch) \
GSI_EE_N_CH_C_CNTXT_0_OFFSET((ch), GSI_EE_AP)
#define GSI_EE_N_CH_C_CNTXT_0_OFFSET(ch, ee) \
Expand All @@ -78,19 +93,35 @@
#define CHSTATE_FMASK GENMASK(23, 20)
#define ELEMENT_SIZE_FMASK GENMASK(31, 24)

/** enum gsi_channel_type - CHTYPE_PROTOCOL field values in CH_C_CNTXT_0 */
enum gsi_channel_type {
GSI_CHANNEL_TYPE_MHI = 0x0,
GSI_CHANNEL_TYPE_XHCI = 0x1,
GSI_CHANNEL_TYPE_GPI = 0x2,
GSI_CHANNEL_TYPE_XDCI = 0x3,
};
/* Encoded value for CH_C_CNTXT_0 register channel protocol fields */
static inline u32
chtype_protocol_encoded(enum ipa_version version, enum gsi_channel_type type)
{
u32 val;

val = u32_encode_bits(type, CHTYPE_PROTOCOL_FMASK);
if (version < IPA_VERSION_4_5)
return val;

/* Encode upper bit(s) as well */
type >>= hweight32(CHTYPE_PROTOCOL_FMASK);
val |= u32_encode_bits(type, CHTYPE_PROTOCOL_MSB_FMASK);

return val;
}

#define GSI_CH_C_CNTXT_1_OFFSET(ch) \
GSI_EE_N_CH_C_CNTXT_1_OFFSET((ch), GSI_EE_AP)
#define GSI_EE_N_CH_C_CNTXT_1_OFFSET(ch, ee) \
(0x0001c004 + 0x4000 * (ee) + 0x80 * (ch))
#define R_LENGTH_FMASK GENMASK(15, 0)

/* Encoded value for CH_C_CNTXT_1 register R_LENGTH field */
static inline u32 r_length_encoded(enum ipa_version version, u32 length)
{
if (version < IPA_VERSION_4_9)
return u32_encode_bits(length, GENMASK(15, 0));
return u32_encode_bits(length, GENMASK(19, 0));
}

#define GSI_CH_C_CNTXT_2_OFFSET(ch) \
GSI_EE_N_CH_C_CNTXT_2_OFFSET((ch), GSI_EE_AP)
Expand All @@ -114,6 +145,9 @@ enum gsi_channel_type {
/* The next two fields are present for IPA v4.5 and above */
#define PREFETCH_MODE_FMASK GENMASK(13, 10)
#define EMPTY_LVL_THRSHOLD_FMASK GENMASK(23, 16)
/* The next field is present for IPA v4.9 and above */
#define DB_IN_BYTES GENMASK(24, 24)

/** enum gsi_prefetch_mode - PREFETCH_MODE field in CH_C_QOS */
enum gsi_prefetch_mode {
GSI_USE_PREFETCH_BUFS = 0x0,
Expand Down Expand Up @@ -146,19 +180,25 @@ enum gsi_prefetch_mode {
GSI_EE_N_EV_CH_E_CNTXT_0_OFFSET((ev), GSI_EE_AP)
#define GSI_EE_N_EV_CH_E_CNTXT_0_OFFSET(ev, ee) \
(0x0001d000 + 0x4000 * (ee) + 0x80 * (ev))
/* enum gsi_channel_type defines EV_CHTYPE field values in EV_CH_E_CNTXT_0 */
#define EV_CHTYPE_FMASK GENMASK(3, 0)
#define EV_EE_FMASK GENMASK(7, 4)
#define EV_EVCHID_FMASK GENMASK(15, 8)
#define EV_INTYPE_FMASK GENMASK(16, 16)
#define EV_CHSTATE_FMASK GENMASK(23, 20)
#define EV_ELEMENT_SIZE_FMASK GENMASK(31, 24)
/* enum gsi_channel_type defines EV_CHTYPE field values in EV_CH_E_CNTXT_0 */

#define GSI_EV_CH_E_CNTXT_1_OFFSET(ev) \
GSI_EE_N_EV_CH_E_CNTXT_1_OFFSET((ev), GSI_EE_AP)
#define GSI_EE_N_EV_CH_E_CNTXT_1_OFFSET(ev, ee) \
(0x0001d004 + 0x4000 * (ee) + 0x80 * (ev))
#define EV_R_LENGTH_FMASK GENMASK(15, 0)
/* Encoded value for EV_CH_C_CNTXT_1 register EV_R_LENGTH field */
static inline u32 ev_r_length_encoded(enum ipa_version version, u32 length)
{
if (version < IPA_VERSION_4_9)
return u32_encode_bits(length, GENMASK(15, 0));
return u32_encode_bits(length, GENMASK(19, 0));
}

#define GSI_EV_CH_E_CNTXT_2_OFFSET(ev) \
GSI_EE_N_EV_CH_E_CNTXT_2_OFFSET((ev), GSI_EE_AP)
Expand Down Expand Up @@ -248,6 +288,7 @@ enum gsi_ch_cmd_opcode {
GSI_CH_STOP = 0x2,
GSI_CH_RESET = 0x9,
GSI_CH_DE_ALLOC = 0xa,
GSI_CH_DB_STOP = 0xb,
};

#define GSI_EV_CH_CMD_OFFSET \
Expand Down Expand Up @@ -278,6 +319,7 @@ enum gsi_generic_cmd_opcode {
GSI_GENERIC_ALLOCATE_CHANNEL = 0x2,
};

/* The next register is present for IPA v3.5.1 and above */
#define GSI_GSI_HW_PARAM_2_OFFSET \
GSI_EE_N_GSI_HW_PARAM_2_OFFSET(GSI_EE_AP)
#define GSI_EE_N_GSI_HW_PARAM_2_OFFSET(ee) \
Expand All @@ -300,7 +342,7 @@ enum gsi_generic_cmd_opcode {
enum gsi_iram_size {
IRAM_SIZE_ONE_KB = 0x0,
IRAM_SIZE_TWO_KB = 0x1,
/* The next two values are available for IPA v4.0 and above */
/* The next two values are available for IPA v4.0 and above */
IRAM_SIZE_TWO_N_HALF_KB = 0x2,
IRAM_SIZE_THREE_KB = 0x3,
/* The next two values are available for IPA v4.5 and above */
Expand Down Expand Up @@ -424,6 +466,8 @@ enum gsi_general_id {
GSI_EE_N_ERROR_LOG_OFFSET(GSI_EE_AP)
#define GSI_EE_N_ERROR_LOG_OFFSET(ee) \
(0x0001f200 + 0x4000 * (ee))

/* Fields below are present for IPA v3.5.1 and above */
#define ERR_ARG3_FMASK GENMASK(3, 0)
#define ERR_ARG2_FMASK GENMASK(7, 4)
#define ERR_ARG1_FMASK GENMASK(11, 8)
Expand Down Expand Up @@ -474,7 +518,4 @@ enum gsi_generic_ee_result {
GENERIC_EE_NO_RESOURCES = 0x7,
};

#define USB_MAX_PACKET_FMASK GENMASK(15, 15) /* 0: HS; 1: SS */
#define MHI_BASE_CHANNEL_FMASK GENMASK(31, 24)

#endif /* _GSI_REG_H_ */
54 changes: 41 additions & 13 deletions drivers/net/ipa/ipa_interrupt.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,14 @@ static void ipa_interrupt_process(struct ipa_interrupt *interrupt, u32 irq_id)
bool uc_irq = ipa_interrupt_uc(interrupt, irq_id);
struct ipa *ipa = interrupt->ipa;
u32 mask = BIT(irq_id);
u32 offset;

/* For microcontroller interrupts, clear the interrupt right away,
* "to avoid clearing unhandled interrupts."
*/
offset = ipa_reg_irq_clr_offset(ipa->version);
if (uc_irq)
iowrite32(mask, ipa->reg_virt + IPA_REG_IRQ_CLR_OFFSET);
iowrite32(mask, ipa->reg_virt + offset);

if (irq_id < IPA_IRQ_COUNT && interrupt->handler[irq_id])
interrupt->handler[irq_id](interrupt->ipa, irq_id);
Expand All @@ -69,21 +71,23 @@ static void ipa_interrupt_process(struct ipa_interrupt *interrupt, u32 irq_id)
* so defer clearing until after the handler has been called.
*/
if (!uc_irq)
iowrite32(mask, ipa->reg_virt + IPA_REG_IRQ_CLR_OFFSET);
iowrite32(mask, ipa->reg_virt + offset);
}

/* Process all IPA interrupt types that have been signaled */
static void ipa_interrupt_process_all(struct ipa_interrupt *interrupt)
{
struct ipa *ipa = interrupt->ipa;
u32 enabled = interrupt->enabled;
u32 offset;
u32 mask;

/* The status register indicates which conditions are present,
* including conditions whose interrupt is not enabled. Handle
* only the enabled ones.
*/
mask = ioread32(ipa->reg_virt + IPA_REG_IRQ_STTS_OFFSET);
offset = ipa_reg_irq_stts_offset(ipa->version);
mask = ioread32(ipa->reg_virt + offset);
while ((mask &= enabled)) {
do {
u32 irq_id = __ffs(mask);
Expand All @@ -92,7 +96,7 @@ static void ipa_interrupt_process_all(struct ipa_interrupt *interrupt)

ipa_interrupt_process(interrupt, irq_id);
} while (mask);
mask = ioread32(ipa->reg_virt + IPA_REG_IRQ_STTS_OFFSET);
mask = ioread32(ipa->reg_virt + offset);
}
}

Expand All @@ -115,14 +119,17 @@ static irqreturn_t ipa_isr(int irq, void *dev_id)
{
struct ipa_interrupt *interrupt = dev_id;
struct ipa *ipa = interrupt->ipa;
u32 offset;
u32 mask;

mask = ioread32(ipa->reg_virt + IPA_REG_IRQ_STTS_OFFSET);
offset = ipa_reg_irq_stts_offset(ipa->version);
mask = ioread32(ipa->reg_virt + offset);
if (mask & interrupt->enabled)
return IRQ_WAKE_THREAD;

/* Nothing in the mask was supposed to cause an interrupt */
iowrite32(mask, ipa->reg_virt + IPA_REG_IRQ_CLR_OFFSET);
offset = ipa_reg_irq_clr_offset(ipa->version);
iowrite32(mask, ipa->reg_virt + offset);

dev_err(&ipa->pdev->dev, "%s: unexpected interrupt, mask 0x%08x\n",
__func__, mask);
Expand All @@ -136,15 +143,22 @@ static void ipa_interrupt_suspend_control(struct ipa_interrupt *interrupt,
{
struct ipa *ipa = interrupt->ipa;
u32 mask = BIT(endpoint_id);
u32 offset;
u32 val;

/* assert(mask & ipa->available); */
val = ioread32(ipa->reg_virt + IPA_REG_IRQ_SUSPEND_EN_OFFSET);

/* IPA version 3.0 does not support TX_SUSPEND interrupt control */
if (ipa->version == IPA_VERSION_3_0)
return;

offset = ipa_reg_irq_suspend_en_offset(ipa->version);
val = ioread32(ipa->reg_virt + offset);
if (enable)
val |= mask;
else
val &= ~mask;
iowrite32(val, ipa->reg_virt + IPA_REG_IRQ_SUSPEND_EN_OFFSET);
iowrite32(val, ipa->reg_virt + offset);
}

/* Enable TX_SUSPEND for an endpoint */
Expand All @@ -165,10 +179,18 @@ ipa_interrupt_suspend_disable(struct ipa_interrupt *interrupt, u32 endpoint_id)
void ipa_interrupt_suspend_clear_all(struct ipa_interrupt *interrupt)
{
struct ipa *ipa = interrupt->ipa;
u32 offset;
u32 val;

val = ioread32(ipa->reg_virt + IPA_REG_IRQ_SUSPEND_INFO_OFFSET);
iowrite32(val, ipa->reg_virt + IPA_REG_IRQ_SUSPEND_CLR_OFFSET);
offset = ipa_reg_irq_suspend_info_offset(ipa->version);
val = ioread32(ipa->reg_virt + offset);

/* SUSPEND interrupt status isn't cleared on IPA version 3.0 */
if (ipa->version == IPA_VERSION_3_0)
return;

offset = ipa_reg_irq_suspend_clr_offset(ipa->version);
iowrite32(val, ipa->reg_virt + offset);
}

/* Simulate arrival of an IPA TX_SUSPEND interrupt */
Expand All @@ -182,25 +204,29 @@ void ipa_interrupt_add(struct ipa_interrupt *interrupt,
enum ipa_irq_id ipa_irq, ipa_irq_handler_t handler)
{
struct ipa *ipa = interrupt->ipa;
u32 offset;

/* assert(ipa_irq < IPA_IRQ_COUNT); */
interrupt->handler[ipa_irq] = handler;

/* Update the IPA interrupt mask to enable it */
interrupt->enabled |= BIT(ipa_irq);
iowrite32(interrupt->enabled, ipa->reg_virt + IPA_REG_IRQ_EN_OFFSET);
offset = ipa_reg_irq_en_offset(ipa->version);
iowrite32(interrupt->enabled, ipa->reg_virt + offset);
}

/* Remove the handler for an IPA interrupt type */
void
ipa_interrupt_remove(struct ipa_interrupt *interrupt, enum ipa_irq_id ipa_irq)
{
struct ipa *ipa = interrupt->ipa;
u32 offset;

/* assert(ipa_irq < IPA_IRQ_COUNT); */
/* Update the IPA interrupt mask to disable it */
interrupt->enabled &= ~BIT(ipa_irq);
iowrite32(interrupt->enabled, ipa->reg_virt + IPA_REG_IRQ_EN_OFFSET);
offset = ipa_reg_irq_en_offset(ipa->version);
iowrite32(interrupt->enabled, ipa->reg_virt + offset);

interrupt->handler[ipa_irq] = NULL;
}
Expand All @@ -211,6 +237,7 @@ struct ipa_interrupt *ipa_interrupt_setup(struct ipa *ipa)
struct device *dev = &ipa->pdev->dev;
struct ipa_interrupt *interrupt;
unsigned int irq;
u32 offset;
int ret;

ret = platform_get_irq_byname(ipa->pdev, "ipa");
Expand All @@ -228,7 +255,8 @@ struct ipa_interrupt *ipa_interrupt_setup(struct ipa *ipa)
interrupt->irq = irq;

/* Start with all IPA interrupts disabled */
iowrite32(0, ipa->reg_virt + IPA_REG_IRQ_EN_OFFSET);
offset = ipa_reg_irq_en_offset(ipa->version);
iowrite32(0, ipa->reg_virt + offset);

ret = request_threaded_irq(irq, ipa_isr, ipa_isr_thread, IRQF_ONESHOT,
"ipa", interrupt);
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/ipa/ipa_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ static void ipa_teardown(struct ipa *ipa)
gsi_teardown(&ipa->gsi);
}

/* Configure QMB Core Master Port selection */
/* Configure bus access behavior for IPA components */
static void ipa_hardware_config_comp(struct ipa *ipa)
{
u32 val;
Expand Down
Loading

0 comments on commit b01483a

Please sign in to comment.