Re: [PATCH 5/8] mailbox: tegra-hsp: Add support for shared mailboxes
On 08/05/18 12:44, Mikko Perttunen wrote: The Tegra HSP block supports 'shared mailboxes' that are simple 32-bit registers consisting of a FULL bit in MSB position and 31 bits of data. The hardware can be configured to trigger interrupts when a mailbox is empty or full. Add support for these shared mailboxes to the HSP driver. The initial use for the mailboxes is the Tegra Combined UART. For this purpose, we use interrupts to receive data, and spinning to wait for the transmit mailbox to be emptied to minimize unnecessary overhead. Signed-off-by: Mikko Perttunen --- drivers/mailbox/tegra-hsp.c | 216 +++- 1 file changed, 193 insertions(+), 23 deletions(-) diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c index 16eb970f2c9f..77bc8ed7ef15 100644 --- a/drivers/mailbox/tegra-hsp.c +++ b/drivers/mailbox/tegra-hsp.c @@ -21,6 +21,11 @@ #include +#include "mailbox.h" + +#define HSP_INT0_IE0x100 +#define HSP_INT_IR 0x304 + #define HSP_INT_DIMENSIONING 0x380 #define HSP_nSM_SHIFT 0 #define HSP_nSS_SHIFT 4 @@ -34,6 +39,8 @@ #define HSP_DB_RAW0x8 #define HSP_DB_PENDING0xc +#define HSP_SM_SHRD_MBOX 0x0 + #define HSP_DB_CCPLEX 1 #define HSP_DB_BPMP 3 #define HSP_DB_MAX7 @@ -68,6 +75,18 @@ struct tegra_hsp_db_map { unsigned int index; }; +struct tegra_hsp_mailbox { + struct tegra_hsp_channel channel; + unsigned int index; + bool sending; +}; + +static inline struct tegra_hsp_mailbox * +channel_to_mailbox(struct tegra_hsp_channel *channel) +{ + return container_of(channel, struct tegra_hsp_mailbox, channel); +} + struct tegra_hsp_soc { const struct tegra_hsp_db_map *map; }; @@ -77,6 +96,7 @@ struct tegra_hsp { struct mbox_controller mbox; void __iomem *regs; unsigned int doorbell_irq; + unsigned int shared_irq; unsigned int num_sm; unsigned int num_as; unsigned int num_ss; @@ -85,6 +105,7 @@ struct tegra_hsp { spinlock_t lock; struct list_head doorbells; + struct tegra_hsp_mailbox *mailboxes; }; static inline struct tegra_hsp * @@ -189,6 +210,35 @@ static irqreturn_t tegra_hsp_doorbell_irq(int irq, void *data) return IRQ_HANDLED; } +static irqreturn_t tegra_hsp_shared_irq(int irq, void *data) +{ + struct tegra_hsp_mailbox *mb; + struct tegra_hsp *hsp = data; + unsigned long bit, mask; + u32 value; + + mask = tegra_hsp_readl(hsp, HSP_INT_IR); + /* Only interested in FULL interrupts */ + mask &= 0xff << 8; Maybe add some definitions for the above. Should we qualify 'mask' against the HSP_INT_IE register as well? + + for_each_set_bit(bit, &mask, 16) { + unsigned int mb_i = bit % 8; If you right-shifted the mask above, you could avoid this modulo. + + mb = &hsp->mailboxes[mb_i]; + + if (!mb->sending) { + value = tegra_hsp_channel_readl(&mb->channel, + HSP_SM_SHRD_MBOX); + value &= ~BIT(31); Similarly a definition for bit 31 may add some clarity. + mbox_chan_received_data(mb->channel.chan, &value); + tegra_hsp_channel_writel(&mb->channel, value, +HSP_SM_SHRD_MBOX); + } + } + + return IRQ_HANDLED; +} + static struct tegra_hsp_channel * tegra_hsp_doorbell_create(struct tegra_hsp *hsp, const char *name, unsigned int master, unsigned int index) @@ -277,15 +327,58 @@ static void tegra_hsp_doorbell_shutdown(struct tegra_hsp_doorbell *db) spin_unlock_irqrestore(&hsp->lock, flags); } +static int tegra_hsp_mailbox_startup(struct tegra_hsp_mailbox *mb) +{ + struct tegra_hsp *hsp = mb->channel.hsp; + u32 value; + + mb->channel.chan->txdone_method = TXDONE_BY_BLOCK; + + /* Route FULL interrupt to external IRQ 0 */ + value = tegra_hsp_readl(hsp, HSP_INT0_IE); + value |= BIT(mb->index + 8); + tegra_hsp_writel(hsp, value, HSP_INT0_IE); + + return 0; +} + +static int tegra_hsp_mailbox_shutdown(struct tegra_hsp_mailbox *mb) +{ + struct tegra_hsp *hsp = mb->channel.hsp; + u32 value; + + value = tegra_hsp_readl(hsp, HSP_INT0_IE); + value &= ~BIT(mb->index + 8); + tegra_hsp_writel(hsp, value, HSP_INT0_IE); + + return 0; +} + static int tegra_hsp_send_data(struct mbox_chan *chan, void *data) { struct tegra_hsp_channel *channel = chan->con_priv; - struct tegra_hsp_doorbell *db; + struct tegra_hsp_mailbox *mailbox; + uint32_t value; switch (channel->type) { case TEGRA_HSP_MBOX_TYPE_DB: - db = channel_to_doorbell(channel); -
[PATCH 5/8] mailbox: tegra-hsp: Add support for shared mailboxes
The Tegra HSP block supports 'shared mailboxes' that are simple 32-bit registers consisting of a FULL bit in MSB position and 31 bits of data. The hardware can be configured to trigger interrupts when a mailbox is empty or full. Add support for these shared mailboxes to the HSP driver. The initial use for the mailboxes is the Tegra Combined UART. For this purpose, we use interrupts to receive data, and spinning to wait for the transmit mailbox to be emptied to minimize unnecessary overhead. Signed-off-by: Mikko Perttunen --- drivers/mailbox/tegra-hsp.c | 216 +++- 1 file changed, 193 insertions(+), 23 deletions(-) diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c index 16eb970f2c9f..77bc8ed7ef15 100644 --- a/drivers/mailbox/tegra-hsp.c +++ b/drivers/mailbox/tegra-hsp.c @@ -21,6 +21,11 @@ #include +#include "mailbox.h" + +#define HSP_INT0_IE0x100 +#define HSP_INT_IR 0x304 + #define HSP_INT_DIMENSIONING 0x380 #define HSP_nSM_SHIFT 0 #define HSP_nSS_SHIFT 4 @@ -34,6 +39,8 @@ #define HSP_DB_RAW 0x8 #define HSP_DB_PENDING 0xc +#define HSP_SM_SHRD_MBOX 0x0 + #define HSP_DB_CCPLEX 1 #define HSP_DB_BPMP3 #define HSP_DB_MAX 7 @@ -68,6 +75,18 @@ struct tegra_hsp_db_map { unsigned int index; }; +struct tegra_hsp_mailbox { + struct tegra_hsp_channel channel; + unsigned int index; + bool sending; +}; + +static inline struct tegra_hsp_mailbox * +channel_to_mailbox(struct tegra_hsp_channel *channel) +{ + return container_of(channel, struct tegra_hsp_mailbox, channel); +} + struct tegra_hsp_soc { const struct tegra_hsp_db_map *map; }; @@ -77,6 +96,7 @@ struct tegra_hsp { struct mbox_controller mbox; void __iomem *regs; unsigned int doorbell_irq; + unsigned int shared_irq; unsigned int num_sm; unsigned int num_as; unsigned int num_ss; @@ -85,6 +105,7 @@ struct tegra_hsp { spinlock_t lock; struct list_head doorbells; + struct tegra_hsp_mailbox *mailboxes; }; static inline struct tegra_hsp * @@ -189,6 +210,35 @@ static irqreturn_t tegra_hsp_doorbell_irq(int irq, void *data) return IRQ_HANDLED; } +static irqreturn_t tegra_hsp_shared_irq(int irq, void *data) +{ + struct tegra_hsp_mailbox *mb; + struct tegra_hsp *hsp = data; + unsigned long bit, mask; + u32 value; + + mask = tegra_hsp_readl(hsp, HSP_INT_IR); + /* Only interested in FULL interrupts */ + mask &= 0xff << 8; + + for_each_set_bit(bit, &mask, 16) { + unsigned int mb_i = bit % 8; + + mb = &hsp->mailboxes[mb_i]; + + if (!mb->sending) { + value = tegra_hsp_channel_readl(&mb->channel, + HSP_SM_SHRD_MBOX); + value &= ~BIT(31); + mbox_chan_received_data(mb->channel.chan, &value); + tegra_hsp_channel_writel(&mb->channel, value, +HSP_SM_SHRD_MBOX); + } + } + + return IRQ_HANDLED; +} + static struct tegra_hsp_channel * tegra_hsp_doorbell_create(struct tegra_hsp *hsp, const char *name, unsigned int master, unsigned int index) @@ -277,15 +327,58 @@ static void tegra_hsp_doorbell_shutdown(struct tegra_hsp_doorbell *db) spin_unlock_irqrestore(&hsp->lock, flags); } +static int tegra_hsp_mailbox_startup(struct tegra_hsp_mailbox *mb) +{ + struct tegra_hsp *hsp = mb->channel.hsp; + u32 value; + + mb->channel.chan->txdone_method = TXDONE_BY_BLOCK; + + /* Route FULL interrupt to external IRQ 0 */ + value = tegra_hsp_readl(hsp, HSP_INT0_IE); + value |= BIT(mb->index + 8); + tegra_hsp_writel(hsp, value, HSP_INT0_IE); + + return 0; +} + +static int tegra_hsp_mailbox_shutdown(struct tegra_hsp_mailbox *mb) +{ + struct tegra_hsp *hsp = mb->channel.hsp; + u32 value; + + value = tegra_hsp_readl(hsp, HSP_INT0_IE); + value &= ~BIT(mb->index + 8); + tegra_hsp_writel(hsp, value, HSP_INT0_IE); + + return 0; +} + static int tegra_hsp_send_data(struct mbox_chan *chan, void *data) { struct tegra_hsp_channel *channel = chan->con_priv; - struct tegra_hsp_doorbell *db; + struct tegra_hsp_mailbox *mailbox; + uint32_t value; switch (channel->type) { case TEGRA_HSP_MBOX_TYPE_DB: - db = channel_to_doorbell(channel); - tegra_hsp_channel_writel(&db->channel, 1, HSP_DB_TRIGGER); + tegra_hsp_channel_writel(channel, 1, HSP_DB_TRIGGER); + return 0; + case TEGRA_HSP_MBOX_TYPE_SM: + mailbox = channel_to_mailbox(channel); + mailbox->sending = true; + +