From: Sebastien Jan on
Hi Carlos,

Please see my comments inlined.

On Friday 07 May 2010 17:18:32 Carlos Chinea wrote:
[strip]
> diff --git a/drivers/hsi/controllers/omap_ssi.c
[strip]
> +
> +/**
> + * struct omap_ssm_ctx - OMAP synchronous serial module (TX/RX) context
> + * @mode: Bit transmission mode
> + * @channels: Number of channels
> + * @framesize: Frame size in bits
> + * @timeout: RX frame timeout
> + * @divisot: TX divider

Typo: s/divisot/divisor

> + * @arb_mode: Arbitration mode for TX frame (Round robin, priority)
> + */
> +struct omap_ssm_ctx {
> + u32 mode;
> + u32 channels;
> + u32 frame_size;
> + union {
> + u32 timeout; /* Rx Only */
> + struct {
> + u32 arb_mode;
> + u32 divisor;
> + }; /* Tx only */
> + };
> +};
> +
> +/**
> + * struct omap_ssi_port - OMAP SSI port data
> + * @dev: device associated to the port (HSI port)
> + * @sst_dma: SSI transmitter physical base address
> + * @ssr_dma: SSI receiver physical base address
> + * @sst_base: SSI transmitter base address
> + * @ssr_base: SSI receiver base address

wk_lock description is missing here

> + * @lock: Spin lock to serialize access to the SSI port
> + * @channels: Current number of channels configured (1,2,4 or 8)
> + * @txqueue: TX message queues
> + * @rxqueue: RX message queues
> + * @brkqueue: Queue of incoming HWBREAK requests (FRAME mode)
> + * @irq: IRQ number
> + * @wake_irq: IRQ number for incoming wake line (-1 if none)
> + * @pio_tasklet: Bottom half for PIO transfers and events
> + * @wake_tasklet: Bottom half for incoming wake events
> + * @wkin_cken: Keep track of clock references due to the incoming wake
> line
> + * @wake_refcount: Reference count for output wake line

s/wake_refcount/wk_refcount

> + * @sys_mpu_enable: Context for the interrupt enable register for irq 0
> + * @sst: Context for the synchronous serial transmitter
> + * @ssr: Context for the synchronous serial receiver
> + */
> +struct omap_ssi_port {
> + struct device *dev;
> + dma_addr_t sst_dma;
> + dma_addr_t ssr_dma;
> + unsigned long sst_base;
> + unsigned long ssr_base;
> + spinlock_t wk_lock;
> + spinlock_t lock;
> + unsigned int channels;
> + struct list_head txqueue[SSI_MAX_CHANNELS];
> + struct list_head rxqueue[SSI_MAX_CHANNELS];
> + struct list_head brkqueue;
> + unsigned int irq;
> + int wake_irq;
> + struct tasklet_struct pio_tasklet;
> + struct tasklet_struct wake_tasklet;
> + unsigned int wkin_cken:1; /* Workaround */
> + int wk_refcount;
> + /* OMAP SSI port context */
> + u32 sys_mpu_enable; /* We use only one irq */
> + struct omap_ssm_ctx sst;
> + struct omap_ssm_ctx ssr;
> +};
> +

[strip]

> +
> +static void ssi_pio_complete(struct hsi_port *port, struct list_head
> *queue) +{
> + struct hsi_controller *ssi =
> to_hsi_controller(port->device.parent); + struct omap_ssi_controller
> *omap_ssi = hsi_controller_drvdata(ssi); + struct omap_ssi_port
> *omap_port = hsi_port_drvdata(port);
> + struct hsi_msg *msg;
> + u32 *buf;
> + u32 val;
> +
> + spin_lock(&omap_port->lock);
> + msg = list_first_entry(queue, struct hsi_msg, link);
> + if ((!msg->sgt.nents) || (!msg->sgt.sgl->length)) {
> + msg->actual_len = 0;
> + msg->status = HSI_STATUS_PENDING;
> + }
> + if (msg->status == HSI_STATUS_PROCEDING) {
> + buf = sg_virt(msg->sgt.sgl) + msg->actual_len;
> + if (msg->ttype == HSI_MSG_WRITE)
> + __raw_writel(*buf, omap_port->sst_base +
> +
> SSI_SST_BUFFER_CH_REG(msg->channel)); + else
> + *buf = __raw_readl(omap_port->ssr_base +
> +
> SSI_SSR_BUFFER_CH_REG(msg->channel)); +
> dev_dbg(&port->device, "ch %d ttype %d 0x%08x\n", msg->channel, +
> msg->ttype, *buf); +
> msg->actual_len += sizeof(*buf);
> + if (msg->actual_len >= msg->sgt.sgl->length)
> + msg->status = HSI_STATUS_COMPLETED;
> + /*
> + * Wait for the last written frame to be really sent before
> + * we call the complete callback
> + */
> + if ((msg->status == HSI_STATUS_PROCEDING) ||
> + ((msg->status == HSI_STATUS_COMPLETED) &&
> + (msg->ttype == HSI_MSG_WRITE)))
> + goto out;
> +
> + }
> + if (msg->status == HSI_STATUS_PROCEDING)
> + goto out;
> + /* Transfer completed at this point */
> + val = __raw_readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num,
> 0)); + if (msg->ttype == HSI_MSG_WRITE) {
> + val &= ~SSI_DATAACCEPT(msg->channel);
> + ssi_clk_disable(ssi); /* Release clocks for write transfer
> */ + } else {
> + val &= ~SSI_DATAAVAILABLE(msg->channel);
> + }
> + __raw_writel(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num,
> 0)); + list_del(&msg->link);
> + spin_unlock(&omap_port->lock);
> + msg->complete(msg);
> + spin_lock(&omap_port->lock);
> + ssi_start_transfer(queue);
> +out:
> + spin_unlock(&omap_port->lock);
> +}
> +
> +static void ssi_gdd_complete(struct hsi_controller *ssi, unsigned int lch)
> +{
> + struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
> + struct hsi_msg *msg = omap_ssi->gdd_trn[lch].msg;
> + struct hsi_port *port = to_hsi_port(msg->cl->device.parent);
> + unsigned int dir;
> + u32 csr;
> + u32 val;
> +
> + spin_lock(&omap_ssi->lock);
> +
> + val = __raw_readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
> + val &= ~SSI_GDD_LCH(lch);
> + __raw_writel(val, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
> +
> + if (msg->ttype == HSI_MSG_READ) {
> + dir = DMA_FROM_DEVICE;
> + val = SSI_DATAAVAILABLE(msg->channel);
> + ssi_clk_disable(ssi);
> + } else {
> + dir = DMA_TO_DEVICE;
> + val = SSI_DATAACCEPT(msg->channel);
> + /* Keep clocks reference for write pio event */
> + }
> + dma_unmap_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents, dir);
> + csr = __raw_readw(omap_ssi->gdd + SSI_GDD_CSR_REG(lch));
> + omap_ssi->gdd_trn[lch].msg = NULL; /* release GDD lch */
> + if (csr & SSI_CSR_TOUR) { /* Timeout error */
> + msg->status = HSI_STATUS_ERROR;
> + msg->actual_len = 0;
> + list_del(&msg->link); /* Dequeue msg */
> + spin_unlock(&omap_ssi->lock);
> + msg->complete(msg);
> + return;
> + }
> +
> + val |= __raw_readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num,
> 0)); + __raw_writel(val, omap_ssi->sys +
> SSI_MPU_ENABLE_REG(port->num, 0)); +
> + msg->status = HSI_STATUS_COMPLETED;
> + msg->actual_len = sg_dma_len(msg->sgt.sgl);
> + spin_unlock(&omap_ssi->lock);
> +}

Don't you need to check the queue related to this transfer at this point, to
start the potentially next queued transfer on the same channel? (calling
ssi_start_transfer(), like in ssi_pio_complete()?)

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Carlos Chinea on
On Fri, 2010-05-14 at 16:41 +0200, ext Sebastien Jan wrote:
> Hi Carlos,
>
> Please see my comments inlined.
>
> On Friday 07 May 2010 17:18:32 Carlos Chinea wrote:
> [strip]
> > diff --git a/drivers/hsi/controllers/omap_ssi.c
> [strip]
> > +
> > +/**
> > + * struct omap_ssm_ctx - OMAP synchronous serial module (TX/RX) context
> > + * @mode: Bit transmission mode
> > + * @channels: Number of channels
> > + * @framesize: Frame size in bits
> > + * @timeout: RX frame timeout
> > + * @divisot: TX divider
>
> Typo: s/divisot/divisor
>
> > + * @arb_mode: Arbitration mode for TX frame (Round robin, priority)
> > + */
> > +struct omap_ssm_ctx {
> > + u32 mode;
> > + u32 channels;
> > + u32 frame_size;
> > + union {
> > + u32 timeout; /* Rx Only */
> > + struct {
> > + u32 arb_mode;
> > + u32 divisor;
> > + }; /* Tx only */
> > + };
> > +};
> > +
> > +/**
> > + * struct omap_ssi_port - OMAP SSI port data
> > + * @dev: device associated to the port (HSI port)
> > + * @sst_dma: SSI transmitter physical base address
> > + * @ssr_dma: SSI receiver physical base address
> > + * @sst_base: SSI transmitter base address
> > + * @ssr_base: SSI receiver base address
>
> wk_lock description is missing here
>
> > + * @lock: Spin lock to serialize access to the SSI port
> > + * @channels: Current number of channels configured (1,2,4 or 8)
> > + * @txqueue: TX message queues
> > + * @rxqueue: RX message queues
> > + * @brkqueue: Queue of incoming HWBREAK requests (FRAME mode)
> > + * @irq: IRQ number
> > + * @wake_irq: IRQ number for incoming wake line (-1 if none)
> > + * @pio_tasklet: Bottom half for PIO transfers and events
> > + * @wake_tasklet: Bottom half for incoming wake events
> > + * @wkin_cken: Keep track of clock references due to the incoming wake
> > line
> > + * @wake_refcount: Reference count for output wake line
>
> s/wake_refcount/wk_refcount
>
> > + * @sys_mpu_enable: Context for the interrupt enable register for irq 0
> > + * @sst: Context for the synchronous serial transmitter
> > + * @ssr: Context for the synchronous serial receiver
> > + */
> > +struct omap_ssi_port {
> > + struct device *dev;
> > + dma_addr_t sst_dma;
> > + dma_addr_t ssr_dma;
> > + unsigned long sst_base;
> > + unsigned long ssr_base;
> > + spinlock_t wk_lock;
> > + spinlock_t lock;
> > + unsigned int channels;
> > + struct list_head txqueue[SSI_MAX_CHANNELS];
> > + struct list_head rxqueue[SSI_MAX_CHANNELS];
> > + struct list_head brkqueue;
> > + unsigned int irq;
> > + int wake_irq;
> > + struct tasklet_struct pio_tasklet;
> > + struct tasklet_struct wake_tasklet;
> > + unsigned int wkin_cken:1; /* Workaround */
> > + int wk_refcount;
> > + /* OMAP SSI port context */
> > + u32 sys_mpu_enable; /* We use only one irq */
> > + struct omap_ssm_ctx sst;
> > + struct omap_ssm_ctx ssr;
> > +};
> > +
>
> [strip]
>
> > +
> > +static void ssi_pio_complete(struct hsi_port *port, struct list_head
> > *queue) +{
> > + struct hsi_controller *ssi =
> > to_hsi_controller(port->device.parent); + struct omap_ssi_controller
> > *omap_ssi = hsi_controller_drvdata(ssi); + struct omap_ssi_port
> > *omap_port = hsi_port_drvdata(port);
> > + struct hsi_msg *msg;
> > + u32 *buf;
> > + u32 val;
> > +
> > + spin_lock(&omap_port->lock);
> > + msg = list_first_entry(queue, struct hsi_msg, link);
> > + if ((!msg->sgt.nents) || (!msg->sgt.sgl->length)) {
> > + msg->actual_len = 0;
> > + msg->status = HSI_STATUS_PENDING;
> > + }
> > + if (msg->status == HSI_STATUS_PROCEDING) {
> > + buf = sg_virt(msg->sgt.sgl) + msg->actual_len;
> > + if (msg->ttype == HSI_MSG_WRITE)
> > + __raw_writel(*buf, omap_port->sst_base +
> > +
> > SSI_SST_BUFFER_CH_REG(msg->channel)); + else
> > + *buf = __raw_readl(omap_port->ssr_base +
> > +
> > SSI_SSR_BUFFER_CH_REG(msg->channel)); +
> > dev_dbg(&port->device, "ch %d ttype %d 0x%08x\n", msg->channel, +
> > msg->ttype, *buf); +
> > msg->actual_len += sizeof(*buf);
> > + if (msg->actual_len >= msg->sgt.sgl->length)
> > + msg->status = HSI_STATUS_COMPLETED;
> > + /*
> > + * Wait for the last written frame to be really sent before
> > + * we call the complete callback
> > + */
> > + if ((msg->status == HSI_STATUS_PROCEDING) ||
> > + ((msg->status == HSI_STATUS_COMPLETED) &&
> > + (msg->ttype == HSI_MSG_WRITE)))
> > + goto out;
> > +
> > + }
> > + if (msg->status == HSI_STATUS_PROCEDING)
> > + goto out;
> > + /* Transfer completed at this point */
> > + val = __raw_readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num,
> > 0)); + if (msg->ttype == HSI_MSG_WRITE) {
> > + val &= ~SSI_DATAACCEPT(msg->channel);
> > + ssi_clk_disable(ssi); /* Release clocks for write transfer
> > */ + } else {
> > + val &= ~SSI_DATAAVAILABLE(msg->channel);
> > + }
> > + __raw_writel(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num,
> > 0)); + list_del(&msg->link);
> > + spin_unlock(&omap_port->lock);
> > + msg->complete(msg);
> > + spin_lock(&omap_port->lock);
> > + ssi_start_transfer(queue);
> > +out:
> > + spin_unlock(&omap_port->lock);
> > +}
> > +
> > +static void ssi_gdd_complete(struct hsi_controller *ssi, unsigned int lch)
> > +{
> > + struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
> > + struct hsi_msg *msg = omap_ssi->gdd_trn[lch].msg;
> > + struct hsi_port *port = to_hsi_port(msg->cl->device.parent);
> > + unsigned int dir;
> > + u32 csr;
> > + u32 val;
> > +
> > + spin_lock(&omap_ssi->lock);
> > +
> > + val = __raw_readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
> > + val &= ~SSI_GDD_LCH(lch);
> > + __raw_writel(val, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
> > +
> > + if (msg->ttype == HSI_MSG_READ) {
> > + dir = DMA_FROM_DEVICE;
> > + val = SSI_DATAAVAILABLE(msg->channel);
> > + ssi_clk_disable(ssi);
> > + } else {
> > + dir = DMA_TO_DEVICE;
> > + val = SSI_DATAACCEPT(msg->channel);
> > + /* Keep clocks reference for write pio event */
> > + }
> > + dma_unmap_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents, dir);
> > + csr = __raw_readw(omap_ssi->gdd + SSI_GDD_CSR_REG(lch));
> > + omap_ssi->gdd_trn[lch].msg = NULL; /* release GDD lch */
> > + if (csr & SSI_CSR_TOUR) { /* Timeout error */
> > + msg->status = HSI_STATUS_ERROR;
> > + msg->actual_len = 0;
> > + list_del(&msg->link); /* Dequeue msg */
> > + spin_unlock(&omap_ssi->lock);
> > + msg->complete(msg);
> > + return;
> > + }
> > +
> > + val |= __raw_readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num,
> > 0)); + __raw_writel(val, omap_ssi->sys +
> > SSI_MPU_ENABLE_REG(port->num, 0)); +
> > + msg->status = HSI_STATUS_COMPLETED;
> > + msg->actual_len = sg_dma_len(msg->sgt.sgl);
> > + spin_unlock(&omap_ssi->lock);
> > +}
>
> Don't you need to check the queue related to this transfer at this point, to
> start the potentially next queued transfer on the same channel? (calling
> ssi_start_transfer(), like in ssi_pio_complete()?)
>

No this is done in ssi_pio_complete(). Notice that we do not call the
complete callback at any point here. We just arm the pio interrupt for
that channel and transfer direction. AFAIK, this is the SW logic
expected by the OMAP SSI HW.

Br,
--
Carlos Chinea <carlos.chinea(a)nokia.com>

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Sebastien Jan on
On Tuesday 18 May 2010 11:07:20 Carlos Chinea wrote:
[cut]
> > > + val |= __raw_readl(omap_ssi->sys +
> > > SSI_MPU_ENABLE_REG(port->num, 0)); + __raw_writel(val,
> > > omap_ssi->sys +
> > > SSI_MPU_ENABLE_REG(port->num, 0)); +
> > > + msg->status = HSI_STATUS_COMPLETED;
> > > + msg->actual_len = sg_dma_len(msg->sgt.sgl);
> > > + spin_unlock(&omap_ssi->lock);
> > > +}
> >
> > Don't you need to check the queue related to this transfer at this point,
> > to start the potentially next queued transfer on the same channel?
> > (calling ssi_start_transfer(), like in ssi_pio_complete()?)
>
> No this is done in ssi_pio_complete(). Notice that we do not call the
> complete callback at any point here. We just arm the pio interrupt for
> that channel and transfer direction. AFAIK, this is the SW logic
> expected by the OMAP SSI HW.

Ok, though I would not expect the interrupt to fire in an Rx scenario as the
fifo would have already been emptied by the DMA for this transfer (unless you
rely on the next transfer initiated by the peer to make the Rx interrupt fire
on this channel?)?
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Carlos Chinea on
On Tue, 2010-05-18 at 16:05 +0200, ext Sebastien Jan wrote:
> On Tuesday 18 May 2010 11:07:20 Carlos Chinea wrote:
> [cut]
> > > > + val |= __raw_readl(omap_ssi->sys +
> > > > SSI_MPU_ENABLE_REG(port->num, 0)); + __raw_writel(val,
> > > > omap_ssi->sys +
> > > > SSI_MPU_ENABLE_REG(port->num, 0)); +
> > > > + msg->status = HSI_STATUS_COMPLETED;
> > > > + msg->actual_len = sg_dma_len(msg->sgt.sgl);
> > > > + spin_unlock(&omap_ssi->lock);
> > > > +}
> > >
> > > Don't you need to check the queue related to this transfer at this point,
> > > to start the potentially next queued transfer on the same channel?
> > > (calling ssi_start_transfer(), like in ssi_pio_complete()?)
> >
> > No this is done in ssi_pio_complete(). Notice that we do not call the
> > complete callback at any point here. We just arm the pio interrupt for
> > that channel and transfer direction. AFAIK, this is the SW logic
> > expected by the OMAP SSI HW.
>
> Ok, though I would not expect the interrupt to fire in an Rx scenario as the
> fifo would have already been emptied by the DMA for this transfer (unless you
> rely on the next transfer initiated by the peer to make the Rx interrupt fire
> on this channel?)?

No I do not rely on the next RX transfer. I rely on the fact that the
GDD(DMA) controller does not reset the RX status bit for that channel
when RX transfer is finished.

Br,
--
Carlos Chinea <carlos.chinea(a)nokia.com>

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/