/*
 * Copyright (C) 2014-2015 Broadcom Corporation
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation version 2.
 *
 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
 * kind, whether express or implied; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
#ifndef __CYGNUS_SSP_H__
#define __CYGNUS_SSP_H__

#define CYGNUS_TDM_DAI_MAX_SLOTS 16

#define CYGNUS_MAX_PLAYBACK_PORTS 4
#define CYGNUS_MAX_CAPTURE_PORTS 3
#define CYGNUS_MAX_I2S_PORTS 3
#define CYGNUS_MAX_PORTS  CYGNUS_MAX_PLAYBACK_PORTS
#define CYGNUS_AUIDO_MAX_NUM_CLKS 3

#define CYGNUS_SSP_FRAMEBITS_DIV 1

#define CYGNUS_SSPMODE_I2S 0
#define CYGNUS_SSPMODE_TDM 1
#define CYGNUS_SSPMODE_UNKNOWN -1

#define CYGNUS_SSP_CLKSRC_PLL      0

/* Max string length of our dt property names */
#define PROP_LEN_MAX 40

struct ringbuf_regs {
	unsigned rdaddr;
	unsigned wraddr;
	unsigned baseaddr;
	unsigned endaddr;
	unsigned fmark;   /* freemark for play, fullmark for caputure */
	unsigned period_bytes;
	unsigned buf_size;
};

#define RINGBUF_REG_PLAYBACK(num) ((struct ringbuf_regs) { \
	.rdaddr = SRC_RBUF_ ##num## _RDADDR_OFFSET, \
	.wraddr = SRC_RBUF_ ##num## _WRADDR_OFFSET, \
	.baseaddr = SRC_RBUF_ ##num## _BASEADDR_OFFSET, \
	.endaddr = SRC_RBUF_ ##num## _ENDADDR_OFFSET, \
	.fmark = SRC_RBUF_ ##num## _FREE_MARK_OFFSET, \
	.period_bytes = 0, \
	.buf_size = 0, \
})

#define RINGBUF_REG_CAPTURE(num) ((struct ringbuf_regs)  { \
	.rdaddr = DST_RBUF_ ##num## _RDADDR_OFFSET, \
	.wraddr = DST_RBUF_ ##num## _WRADDR_OFFSET, \
	.baseaddr = DST_RBUF_ ##num## _BASEADDR_OFFSET, \
	.endaddr = DST_RBUF_ ##num## _ENDADDR_OFFSET, \
	.fmark = DST_RBUF_ ##num## _FULL_MARK_OFFSET, \
	.period_bytes = 0, \
	.buf_size = 0, \
})

enum cygnus_audio_port_type {
	PORT_TDM,
	PORT_SPDIF,
};

struct cygnus_ssp_regs {
	u32 i2s_stream_cfg;
	u32 i2s_cfg;
	u32 i2s_cap_stream_cfg;
	u32 i2s_cap_cfg;
	u32 i2s_mclk_cfg;

	u32 bf_destch_ctrl;
	u32 bf_destch_cfg;
	u32 bf_sourcech_ctrl;
	u32 bf_sourcech_cfg;
	u32 bf_sourcech_grp;
};

struct cygnus_track_clk {
	bool cap_en;
	bool play_en;
	bool cap_clk_en;
	bool play_clk_en;
};

struct cygnus_aio_port {
	int portnum;
	int mode;
	bool is_slave;
	int streams_on;   /* will be 0 if both capture and play are off */
	int fsync_width;
	int port_type;

	u32 mclk;
	u32 lrclk;
	u32 bit_per_frame;
	u32 pll_clk_num;

	struct cygnus_audio *cygaud;
	struct cygnus_ssp_regs regs;

	struct ringbuf_regs play_rb_regs;
	struct ringbuf_regs capture_rb_regs;

	struct snd_pcm_substream *play_stream;
	struct snd_pcm_substream *capture_stream;

	struct cygnus_track_clk clk_trace;
};


struct cygnus_audio {
	struct cygnus_aio_port  portinfo[CYGNUS_MAX_PORTS];

	int irq_num;
	void __iomem *audio;
	struct device *dev;
	void __iomem *i2s_in;

	struct clk *audio_clk[CYGNUS_AUIDO_MAX_NUM_CLKS];
	int active_ports;
	unsigned long vco_rate;
};

extern int cygnus_ssp_get_mode(struct snd_soc_dai *cpu_dai);
extern int cygnus_ssp_add_pll_tweak_controls(struct snd_soc_pcm_runtime *rtd);
extern int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai,
						int len);
extern int cygnus_soc_platform_register(struct device *dev,
					struct cygnus_audio *cygaud);
extern int cygnus_soc_platform_unregister(struct device *dev);
extern int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai,
	int len);
#endif
option><option value='7'>7</option><option value='8'>8</option><option value='9'>9</option><option value='10'>10</option><option value='15'>15</option><option value='20'>20</option><option value='25'>25</option><option value='30'>30</option><option value='35'>35</option><option value='40'>40</option></select></td></tr><tr><td class='label'>space:</td><td class='ctrl'><select name='ignorews' onchange='this.form.submit();'><option value='0' selected='selected'>include</option><option value='1'>ignore</option></select></td></tr><tr><td class='label'>mode:</td><td class='ctrl'><select name='dt' onchange='this.form.submit();'><option value='0' selected='selected'>unified</option><option value='1'>ssdiff</option><option value='2'>stat only</option></select></td></tr><tr><td/><td class='ctrl'><noscript><input type='submit' value='reload'/></noscript></td></tr></table></form></div><table summary='commit info' class='commit-info'>
<tr><th>author</th><td>Jean-Nicolas Graux &lt;jean-nicolas.graux@st.com&gt;</td><td class='right'>2017-02-07 12:12:41 +0100</td></tr>
<tr><th>committer</th><td>Ulf Hansson &lt;ulf.hansson@linaro.org&gt;</td><td class='right'>2017-02-08 12:22:27 +0100</td></tr>
<tr><th>commit</th><td colspan='2' class='oid'><a href='/cgit.cgi/linux/net-next.git/commit/drivers/usb/atm?id=5cad24d835772f9f709971a8d6fcf12afe53b2a7'>5cad24d835772f9f709971a8d6fcf12afe53b2a7</a> (<a href='/cgit.cgi/linux/net-next.git/patch/drivers/usb/atm?id=5cad24d835772f9f709971a8d6fcf12afe53b2a7'>patch</a>)</td></tr>
<tr><th>tree</th><td colspan='2' class='oid'><a href='/cgit.cgi/linux/net-next.git/tree/?id=5cad24d835772f9f709971a8d6fcf12afe53b2a7'>0c1a8bc700c3c219c45ca05dbc5ce3df6be89813</a> /<a href='/cgit.cgi/linux/net-next.git/tree/drivers/usb/atm?id=5cad24d835772f9f709971a8d6fcf12afe53b2a7'>drivers/usb/atm</a></td></tr>
<tr><th>parent</th><td colspan='2' class='oid'><a href='/cgit.cgi/linux/net-next.git/commit/drivers/usb/atm?id=d5adbfcd5f7bcc6fa58a41c5c5ada0e5c826ce2c'>d5adbfcd5f7bcc6fa58a41c5c5ada0e5c826ce2c</a> (<a href='/cgit.cgi/linux/net-next.git/diff/drivers/usb/atm?id=5cad24d835772f9f709971a8d6fcf12afe53b2a7&amp;id2=d5adbfcd5f7bcc6fa58a41c5c5ada0e5c826ce2c'>diff</a>)</td></tr></table>
<div class='commit-subject'>mmc: mmci: avoid clearing ST Micro busy end interrupt mistakenly</div><div class='commit-msg'>This fixes a race condition that may occur whenever ST micro busy end
interrupt is raised just after being unmasked but before leaving mmci
interrupt context.

A dead-lock has been found if connecting mmci ST Micro variant whose amba
id is 0x10480180 to some new eMMC that supports internal caches.  Whenever
mmci driver enables cache control by programming eMMC's EXT_CSD register,
block driver may request to flush the eMMC internal caches causing mmci
driver to send a MMC_SWITCH command to the card with FLUSH_CACHE operation.
And because busy end interrupt may be mistakenly cleared while not yet
processed, this mmc request may never complete.  As a result, mmcqd task
may be stuck forever.

Here is an instance caught by lockup detector which shows that mmcqd task
was hung while waiting for mmc_flush_cache command to complete:

..
[  240.251595] INFO: task mmcqd/1:52 blocked for more than 120 seconds.
[  240.257973]       Not tainted 4.1.13-00510-g9d91424 #2
[  240.263109] "echo 0 &gt; /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[  240.270955] mmcqd/1         D c047504c     0    52      2 0x00000000
[  240.277359] [&lt;c047504c&gt;] (__schedule) from [&lt;c04754a0&gt;] (schedule+0x40/0x98)
[  240.284418] [&lt;c04754a0&gt;] (schedule) from [&lt;c0477d40&gt;] (schedule_timeout+0x148/0x188)
[  240.292191] [&lt;c0477d40&gt;] (schedule_timeout) from [&lt;c0476040&gt;] (wait_for_common+0xa4/0x170)
[  240.300491] [&lt;c0476040&gt;] (wait_for_common) from [&lt;c02efc1c&gt;] (mmc_wait_for_req_done+0x4c/0x13c)
[  240.309224] [&lt;c02efc1c&gt;] (mmc_wait_for_req_done) from [&lt;c02efd90&gt;] (mmc_wait_for_cmd+0x64/0x84)
[  240.317953] [&lt;c02efd90&gt;] (mmc_wait_for_cmd) from [&lt;c02f5b14&gt;] (__mmc_switch+0xa4/0x2a8)
[  240.325964] [&lt;c02f5b14&gt;] (__mmc_switch) from [&lt;c02f5d40&gt;] (mmc_switch+0x28/0x30)
[  240.333389] [&lt;c02f5d40&gt;] (mmc_switch) from [&lt;c02f0984&gt;] (mmc_flush_cache+0x54/0x80)
[  240.341073] [&lt;c02f0984&gt;] (mmc_flush_cache) from [&lt;c02ff0c4&gt;] (mmc_blk_issue_rq+0x114/0x4e8)
[  240.349459] [&lt;c02ff0c4&gt;] (mmc_blk_issue_rq) from [&lt;c03008d4&gt;] (mmc_queue_thread+0xc0/0x180)
[  240.357844] [&lt;c03008d4&gt;] (mmc_queue_thread) from [&lt;c003cf90&gt;] (kthread+0xdc/0xf4)
[  240.365339] [&lt;c003cf90&gt;] (kthread) from [&lt;c0010068&gt;] (ret_from_fork+0x14/0x2c)
..
..
[  240.664311] INFO: task partprobe:564 blocked for more than 120 seconds.
[  240.670943]       Not tainted 4.1.13-00510-g9d91424 #2
[  240.676078] "echo 0 &gt; /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[  240.683922] partprobe       D c047504c     0   564    486 0x00000000
[  240.690318] [&lt;c047504c&gt;] (__schedule) from [&lt;c04754a0&gt;] (schedule+0x40/0x98)
[  240.697396] [&lt;c04754a0&gt;] (schedule) from [&lt;c0477d40&gt;] (schedule_timeout+0x148/0x188)
[  240.705149] [&lt;c0477d40&gt;] (schedule_timeout) from [&lt;c0476040&gt;] (wait_for_common+0xa4/0x170)
[  240.713446] [&lt;c0476040&gt;] (wait_for_common) from [&lt;c01f3300&gt;] (submit_bio_wait+0x58/0x64)
[  240.721571] [&lt;c01f3300&gt;] (submit_bio_wait) from [&lt;c01fbbd8&gt;] (blkdev_issue_flush+0x60/0x88)
[  240.729957] [&lt;c01fbbd8&gt;] (blkdev_issue_flush) from [&lt;c010ff84&gt;] (blkdev_fsync+0x34/0x44)
[  240.738083] [&lt;c010ff84&gt;] (blkdev_fsync) from [&lt;c0109594&gt;] (do_fsync+0x3c/0x64)
[  240.745319] [&lt;c0109594&gt;] (do_fsync) from [&lt;c000ffc0&gt;] (ret_fast_syscall+0x0/0x3c)
..

Here is the detailed sequence showing when this issue may happen:

1) At probe time, mmci device is initialized and card busy detection based
on DAT[0] monitoring is enabled.

2) Later during run time, since card reported to support internal caches, a
MMCI_SWITCH command is sent to eMMC device with FLUSH_CACHE operation. On
receiving this command, eMMC may enter busy state (for a relatively short
time in the case of the dead-lock).

3) Then mmci interrupt is raised and mmci_irq() is called:

MMCISTATUS register is read and is equal to 0x01000440. So the following
status bits are set:
- MCI_CMDRESPEND (= 6)
- MCI_DATABLOCKEND (= 10)
- MCI_ST_CARDBUSY (= 24)

Since MMCIMASK0 register is 0x3FF, status variable is set to 0x00000040 and
BIT MCI_CMDRESPEND is cleared by writing MMCICLEAR register.

Then mmci_cmd_irq() is called. Considering the following conditions:
- host-&gt;busy_status is 0,
- this is a "busy response",
- reading again MMCISTATUS register gives 0x1000400,
MMCIMASK0 is updated to unmask MCI_ST_BUSYEND bit.

Thus, MMCIMASK0 is set to 0x010003FF and host-&gt;busy_status is set to wait
for busy end completion.

Back again in status loop of mmci_irq(), we quickly go through
mmci_data_irq() as there are no data in that case.  And we finally go
through following test at the end of while(status) loop:

/*
 * Don't poll for busy completion in irq context.
 */
if (host-&gt;variant-&gt;busy_detect &amp;&amp; host-&gt;busy_status)
	status &amp;= ~host-&gt;variant-&gt;busy_detect_flag;

Because status variable is not yet null (is equal to 0x40), we do not leave
interrupt context yet but we loop again into while(status) loop. So we run
across following steps:

a) MMCISTATUS register is read again and this time is equal to 0x01000400.
So that following bits are set:
- MCI_DATABLOCKEND (= 10)
- MCI_ST_CARDBUSY (= 24)

Since MMCIMASK0 register is equal to 0x010003FF:

b) status variable is set to 0x01000000.
c) MCI_ST_CARDBUSY bit is cleared by writing MMCICLEAR register.

Then, mmci_cmd_irq() is called one more time. Since host-&gt;busy_status is
set and that MCI_ST_CARDBUSY is set in status variable, we just return from
this function.

Back again in mmci_irq(), status variable is set to 0 and we finally leave
the while(status) loop. As a result we leave interrupt context, waiting for
busy end interrupt event.

Now, consider that busy end completion is raised IN BETWEEN steps 3.a) and
3.c). In such a case, we may mistakenly clear busy end interrupt at step
3.c) while it has not yet been processed. This will result in mmc command
to wait forever for a busy end completion that will never happen.

To fix the problem, this patch implements the following changes:

Considering that the mmci seems to be triggering the IRQ on both edges
while monitoring DAT0 for busy completion and that same status bit is used
to monitor start and end of busy detection, special care must be taken to
make sure that both start and end interrupts are always cleared one after
the other.

1) Clearing of card busy bit is moved in mmc_cmd_irq() function where
unmasking of busy end bit is effectively handled.
2) Just before unmasking busy end event, busy start event is cleared by
writing card busy bit in MMCICLEAR register.
3) Finally, once we are no more busy with a command, busy end event is
cleared writing again card busy bit in MMCICLEAR register.

This patch has been tested with the ST Accordo5 machine, not yet supported
upstream but relies on the mmci driver.

Signed-off-by: Sarang Mairal &lt;sarang.mairal@garmin.com&gt;
Signed-off-by: Jean-Nicolas Graux &lt;jean-nicolas.graux@st.com&gt;
Reviewed-by: Linus Walleij &lt;linus.walleij@linaro.org&gt;
Tested-by: Ulf Hansson &lt;ulf.hansson@linaro.org&gt;
Signed-off-by: Ulf Hansson &lt;ulf.hansson@linaro.org&gt;
</div><div class='diffstat-header'><a href='/cgit.cgi/linux/net-next.git/diff/?id=5cad24d835772f9f709971a8d6fcf12afe53b2a7'>Diffstat</a> (limited to 'drivers/usb/atm')</div><table summary='diffstat' class='diffstat'>