]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/soundwire/cadence_master.c
Merge tag 'iomap-5.5-merge-13' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
[linux.git] / drivers / soundwire / cadence_master.c
index 502ed4ec8f070a7c398f2b5fa959408608e8ac02..fed21e2b22774de380d464499ae349b20dbd690e 100644 (file)
@@ -183,9 +183,6 @@ MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask");
 #define CDNS_DEFAULT_SSP_INTERVAL              0x18
 #define CDNS_TX_TIMEOUT                                2000
 
-#define CDNS_PCM_PDI_OFFSET                    0x2
-#define CDNS_PDM_PDI_OFFSET                    0x6
-
 #define CDNS_SCP_RX_FIFOLEVEL                  0x2
 
 /*
@@ -231,6 +228,22 @@ static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value)
        return -EAGAIN;
 }
 
+/*
+ * all changes to the MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL
+ * need to be confirmed with a write to MCP_CONFIG_UPDATE
+ */
+static int cdns_update_config(struct sdw_cdns *cdns)
+{
+       int ret;
+
+       ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE,
+                            CDNS_MCP_CONFIG_UPDATE_BIT);
+       if (ret < 0)
+               dev_err(cdns->dev, "Config update timedout\n");
+
+       return ret;
+}
+
 /*
  * debugfs
  */
@@ -279,11 +292,7 @@ static int cdns_reg_show(struct seq_file *s, void *data)
        ret += scnprintf(buf + ret, RD_BUF - ret,
                         "\nDPn B0 Registers\n");
 
-       /*
-        * in sdw_cdns_pdi_init() we filter out the Bulk PDIs,
-        * so the indices need to be corrected again
-        */
-       num_ports = cdns->num_ports + CDNS_PCM_PDI_OFFSET;
+       num_ports = cdns->num_ports;
 
        for (i = 0; i < num_ports; i++) {
                ret += scnprintf(buf + ret, RD_BUF - ret,
@@ -324,6 +333,26 @@ static int cdns_reg_show(struct seq_file *s, void *data)
 }
 DEFINE_SHOW_ATTRIBUTE(cdns_reg);
 
+static int cdns_hw_reset(void *data, u64 value)
+{
+       struct sdw_cdns *cdns = data;
+       int ret;
+
+       if (value != 1)
+               return -EINVAL;
+
+       /* Userspace changed the hardware state behind the kernel's back */
+       add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+
+       ret = sdw_cdns_exit_reset(cdns);
+
+       dev_dbg(cdns->dev, "link hw_reset done: %d\n", ret);
+
+       return ret;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(cdns_hw_reset_fops, NULL, cdns_hw_reset, "%llu\n");
+
 /**
  * sdw_cdns_debugfs_init() - Cadence debugfs init
  * @cdns: Cadence instance
@@ -332,6 +361,9 @@ DEFINE_SHOW_ATTRIBUTE(cdns_reg);
 void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root)
 {
        debugfs_create_file("cdns-registers", 0400, root, cdns, &cdns_reg_fops);
+
+       debugfs_create_file("cdns-hw-reset", 0200, root, cdns,
+                           &cdns_hw_reset_fops);
 }
 EXPORT_SYMBOL_GPL(sdw_cdns_debugfs_init);
 
@@ -752,14 +784,48 @@ EXPORT_SYMBOL(sdw_cdns_thread);
 /*
  * init routines
  */
-static int _cdns_enable_interrupt(struct sdw_cdns *cdns)
+
+/**
+ * sdw_cdns_exit_reset() - Program reset parameters and start bus operations
+ * @cdns: Cadence instance
+ */
+int sdw_cdns_exit_reset(struct sdw_cdns *cdns)
 {
-       u32 mask;
+       /* program maximum length reset to be safe */
+       cdns_updatel(cdns, CDNS_MCP_CONTROL,
+                    CDNS_MCP_CONTROL_RST_DELAY,
+                    CDNS_MCP_CONTROL_RST_DELAY);
+
+       /* use hardware generated reset */
+       cdns_updatel(cdns, CDNS_MCP_CONTROL,
+                    CDNS_MCP_CONTROL_HW_RST,
+                    CDNS_MCP_CONTROL_HW_RST);
+
+       /* enable bus operations with clock and data */
+       cdns_updatel(cdns, CDNS_MCP_CONFIG,
+                    CDNS_MCP_CONFIG_OP,
+                    CDNS_MCP_CONFIG_OP_NORMAL);
+
+       /* commit changes */
+       return cdns_update_config(cdns);
+}
+EXPORT_SYMBOL(sdw_cdns_exit_reset);
 
-       cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0,
-                   CDNS_MCP_SLAVE_INTMASK0_MASK);
-       cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1,
-                   CDNS_MCP_SLAVE_INTMASK1_MASK);
+/**
+ * sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config
+ * @cdns: Cadence instance
+ */
+int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state)
+{
+       u32 slave_intmask0 = 0;
+       u32 slave_intmask1 = 0;
+       u32 mask = 0;
+
+       if (!state)
+               goto update_masks;
+
+       slave_intmask0 = CDNS_MCP_SLAVE_INTMASK0_MASK;
+       slave_intmask1 = CDNS_MCP_SLAVE_INTMASK1_MASK;
 
        /* enable detection of all slave state changes */
        mask = CDNS_MCP_INT_SLAVE_MASK;
@@ -782,26 +848,13 @@ static int _cdns_enable_interrupt(struct sdw_cdns *cdns)
        if (interrupt_mask) /* parameter override */
                mask = interrupt_mask;
 
+update_masks:
+       cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, slave_intmask0);
+       cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, slave_intmask1);
        cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
 
-       return 0;
-}
-
-/**
- * sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config
- * @cdns: Cadence instance
- */
-int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns)
-{
-       int ret;
-
-       _cdns_enable_interrupt(cdns);
-       ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE,
-                            CDNS_MCP_CONFIG_UPDATE_BIT);
-       if (ret < 0)
-               dev_err(cdns->dev, "Config update timedout\n");
-
-       return ret;
+       /* commit changes */
+       return cdns_update_config(cdns);
 }
 EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
 
@@ -821,7 +874,6 @@ static int cdns_allocate_pdi(struct sdw_cdns *cdns,
 
        for (i = 0; i < num; i++) {
                pdi[i].num = i + pdi_offset;
-               pdi[i].assigned = false;
        }
 
        *stream = pdi;
@@ -838,7 +890,8 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
                      struct sdw_cdns_stream_config config)
 {
        struct sdw_cdns_streams *stream;
-       int offset, i, ret;
+       int offset;
+       int ret;
 
        cdns->pcm.num_bd = config.pcm_bd;
        cdns->pcm.num_in = config.pcm_in;
@@ -850,11 +903,8 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
        /* Allocate PDIs for PCMs */
        stream = &cdns->pcm;
 
-       /* First two PDIs are reserved for bulk transfers */
-       if (stream->num_bd < CDNS_PCM_PDI_OFFSET)
-               return -EINVAL;
-       stream->num_bd -= CDNS_PCM_PDI_OFFSET;
-       offset = CDNS_PCM_PDI_OFFSET;
+       /* we allocate PDI0 and PDI1 which are used for Bulk */
+       offset = 0;
 
        ret = cdns_allocate_pdi(cdns, &stream->bd,
                                stream->num_bd, offset);
@@ -881,7 +931,6 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
 
        /* Allocate PDIs for PDMs */
        stream = &cdns->pdm;
-       offset = CDNS_PDM_PDI_OFFSET;
        ret = cdns_allocate_pdi(cdns, &stream->bd,
                                stream->num_bd, offset);
        if (ret)
@@ -898,6 +947,9 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
 
        ret = cdns_allocate_pdi(cdns, &stream->out,
                                stream->num_out, offset);
+
+       offset += stream->num_out;
+
        if (ret)
                return ret;
 
@@ -905,18 +957,6 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
        stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out;
        cdns->num_ports += stream->num_pdi;
 
-       cdns->ports = devm_kcalloc(cdns->dev, cdns->num_ports,
-                                  sizeof(*cdns->ports), GFP_KERNEL);
-       if (!cdns->ports) {
-               ret = -ENOMEM;
-               return ret;
-       }
-
-       for (i = 0; i < cdns->num_ports; i++) {
-               cdns->ports[i].assigned = false;
-               cdns->ports[i].num = i + 1; /* Port 0 reserved for bulk */
-       }
-
        return 0;
 }
 EXPORT_SYMBOL(sdw_cdns_pdi_init);
@@ -939,7 +979,7 @@ static u32 cdns_set_initial_frame_shape(int n_rows, int n_cols)
  * sdw_cdns_init() - Cadence initialization
  * @cdns: Cadence instance
  */
-int sdw_cdns_init(struct sdw_cdns *cdns)
+int sdw_cdns_init(struct sdw_cdns *cdns, bool clock_stop_exit)
 {
        struct sdw_bus *bus = &cdns->bus;
        struct sdw_master_prop *prop = &bus->prop;
@@ -947,12 +987,13 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
        int divider;
        int ret;
 
-       /* Exit clock stop */
-       ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL,
-                            CDNS_MCP_CONTROL_CLK_STOP_CLR);
-       if (ret < 0) {
-               dev_err(cdns->dev, "Couldn't exit from clock stop\n");
-               return ret;
+       if (clock_stop_exit) {
+               ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL,
+                                    CDNS_MCP_CONTROL_CLK_STOP_CLR);
+               if (ret < 0) {
+                       dev_err(cdns->dev, "Couldn't exit from clock stop\n");
+                       return ret;
+               }
        }
 
        /* Set clock divider */
@@ -975,6 +1016,10 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
        cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL);
        cdns_writel(cdns, CDNS_MCP_SSP_CTRL1, CDNS_DEFAULT_SSP_INTERVAL);
 
+       /* flush command FIFOs */
+       cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_RST,
+                    CDNS_MCP_CONTROL_CMD_RST);
+
        /* Set cmd accept mode */
        cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT,
                     CDNS_MCP_CONTROL_CMD_ACCEPT);
@@ -997,13 +1042,10 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
        /* Set cmd mode for Tx and Rx cmds */
        val &= ~CDNS_MCP_CONFIG_CMD;
 
-       /* Set operation to normal */
-       val &= ~CDNS_MCP_CONFIG_OP;
-       val |= CDNS_MCP_CONFIG_OP_NORMAL;
-
        cdns_writel(cdns, CDNS_MCP_CONFIG, val);
 
-       return 0;
+       /* commit changes */
+       return cdns_update_config(cdns);
 }
 EXPORT_SYMBOL(sdw_cdns_init);
 
@@ -1185,20 +1227,20 @@ EXPORT_SYMBOL(cdns_set_sdw_stream);
  * @num: Number of PDIs
  * @pdi: PDI instances
  *
- * Find and return a free PDI for a given PDI array
+ * Find a PDI for a given PDI array. The PDI num and dai_id are
+ * expected to match, return NULL otherwise.
  */
 static struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns,
+                                         unsigned int offset,
                                          unsigned int num,
-                                         struct sdw_cdns_pdi *pdi)
+                                         struct sdw_cdns_pdi *pdi,
+                                         int dai_id)
 {
        int i;
 
-       for (i = 0; i < num; i++) {
-               if (pdi[i].assigned)
-                       continue;
-               pdi[i].assigned = true;
-               return &pdi[i];
-       }
+       for (i = offset; i < offset + num; i++)
+               if (pdi[i].num == dai_id)
+                       return &pdi[i];
 
        return NULL;
 }
@@ -1207,13 +1249,11 @@ static struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns,
  * sdw_cdns_config_stream: Configure a stream
  *
  * @cdns: Cadence instance
- * @port: Cadence data port
  * @ch: Channel count
  * @dir: Data direction
  * @pdi: PDI to be used
  */
 void sdw_cdns_config_stream(struct sdw_cdns *cdns,
-                           struct sdw_cdns_port *port,
                            u32 ch, u32 dir, struct sdw_cdns_pdi *pdi)
 {
        u32 offset, val = 0;
@@ -1221,113 +1261,51 @@ void sdw_cdns_config_stream(struct sdw_cdns *cdns,
        if (dir == SDW_DATA_DIR_RX)
                val = CDNS_PORTCTRL_DIRN;
 
-       offset = CDNS_PORTCTRL + port->num * CDNS_PORT_OFFSET;
+       offset = CDNS_PORTCTRL + pdi->num * CDNS_PORT_OFFSET;
        cdns_updatel(cdns, offset, CDNS_PORTCTRL_DIRN, val);
 
-       val = port->num;
+       val = pdi->num;
        val |= ((1 << ch) - 1) << SDW_REG_SHIFT(CDNS_PDI_CONFIG_CHANNEL);
        cdns_writel(cdns, CDNS_PDI_CONFIG(pdi->num), val);
 }
 EXPORT_SYMBOL(sdw_cdns_config_stream);
 
 /**
- * cdns_get_num_pdi() - Get number of PDIs required
- *
- * @cdns: Cadence instance
- * @pdi: PDI to be used
- * @num: Number of PDIs
- * @ch_count: Channel count
- */
-static int cdns_get_num_pdi(struct sdw_cdns *cdns,
-                           struct sdw_cdns_pdi *pdi,
-                           unsigned int num, u32 ch_count)
-{
-       int i, pdis = 0;
-
-       for (i = 0; i < num; i++) {
-               if (pdi[i].assigned)
-                       continue;
-
-               if (pdi[i].ch_count < ch_count)
-                       ch_count -= pdi[i].ch_count;
-               else
-                       ch_count = 0;
-
-               pdis++;
-
-               if (!ch_count)
-                       break;
-       }
-
-       if (ch_count)
-               return 0;
-
-       return pdis;
-}
-
-/**
- * sdw_cdns_get_stream() - Get stream information
- *
- * @cdns: Cadence instance
- * @stream: Stream to be allocated
- * @ch: Channel count
- * @dir: Data direction
- */
-int sdw_cdns_get_stream(struct sdw_cdns *cdns,
-                       struct sdw_cdns_streams *stream,
-                       u32 ch, u32 dir)
-{
-       int pdis = 0;
-
-       if (dir == SDW_DATA_DIR_RX)
-               pdis = cdns_get_num_pdi(cdns, stream->in, stream->num_in, ch);
-       else
-               pdis = cdns_get_num_pdi(cdns, stream->out, stream->num_out, ch);
-
-       /* check if we found PDI, else find in bi-directional */
-       if (!pdis)
-               pdis = cdns_get_num_pdi(cdns, stream->bd, stream->num_bd, ch);
-
-       return pdis;
-}
-EXPORT_SYMBOL(sdw_cdns_get_stream);
-
-/**
- * sdw_cdns_alloc_stream() - Allocate a stream
+ * sdw_cdns_alloc_pdi() - Allocate a PDI
  *
  * @cdns: Cadence instance
  * @stream: Stream to be allocated
- * @port: Cadence data port
  * @ch: Channel count
  * @dir: Data direction
  */
-int sdw_cdns_alloc_stream(struct sdw_cdns *cdns,
-                         struct sdw_cdns_streams *stream,
-                         struct sdw_cdns_port *port, u32 ch, u32 dir)
+struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns,
+                                       struct sdw_cdns_streams *stream,
+                                       u32 ch, u32 dir, int dai_id)
 {
        struct sdw_cdns_pdi *pdi = NULL;
 
        if (dir == SDW_DATA_DIR_RX)
-               pdi = cdns_find_pdi(cdns, stream->num_in, stream->in);
+               pdi = cdns_find_pdi(cdns, 0, stream->num_in, stream->in,
+                                   dai_id);
        else
-               pdi = cdns_find_pdi(cdns, stream->num_out, stream->out);
+               pdi = cdns_find_pdi(cdns, 0, stream->num_out, stream->out,
+                                   dai_id);
 
        /* check if we found a PDI, else find in bi-directional */
        if (!pdi)
-               pdi = cdns_find_pdi(cdns, stream->num_bd, stream->bd);
-
-       if (!pdi)
-               return -EIO;
-
-       port->pdi = pdi;
-       pdi->l_ch_num = 0;
-       pdi->h_ch_num = ch - 1;
-       pdi->dir = dir;
-       pdi->ch_count = ch;
+               pdi = cdns_find_pdi(cdns, 2, stream->num_bd, stream->bd,
+                                   dai_id);
+
+       if (pdi) {
+               pdi->l_ch_num = 0;
+               pdi->h_ch_num = ch - 1;
+               pdi->dir = dir;
+               pdi->ch_count = ch;
+       }
 
-       return 0;
+       return pdi;
 }
-EXPORT_SYMBOL(sdw_cdns_alloc_stream);
+EXPORT_SYMBOL(sdw_cdns_alloc_pdi);
 
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_DESCRIPTION("Cadence Soundwire Library");