pinctrl: imx: scu: Align imx sc msg structs to 4
[platform/kernel/linux-rpi.git] / drivers / soundwire / cadence_master.c
index 60e8bde..502ed4e 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <linux/delay.h>
 #include <linux/device.h>
+#include <linux/debugfs.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/module.h>
 #include "bus.h"
 #include "cadence_master.h"
 
+static int interrupt_mask;
+module_param_named(cnds_mcp_int_mask, interrupt_mask, int, 0444);
+MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask");
+
 #define CDNS_MCP_CONFIG                                0x0
 
 #define CDNS_MCP_CONFIG_MCMD_RETRY             GENMASK(27, 24)
@@ -47,6 +52,8 @@
 #define CDNS_MCP_SSPSTAT                       0xC
 #define CDNS_MCP_FRAME_SHAPE                   0x10
 #define CDNS_MCP_FRAME_SHAPE_INIT              0x14
+#define CDNS_MCP_FRAME_SHAPE_COL_MASK          GENMASK(2, 0)
+#define CDNS_MCP_FRAME_SHAPE_ROW_OFFSET                3
 
 #define CDNS_MCP_CONFIG_UPDATE                 0x18
 #define CDNS_MCP_CONFIG_UPDATE_BIT             BIT(0)
@@ -56,6 +63,7 @@
 #define CDNS_MCP_SSP_CTRL1                     0x28
 #define CDNS_MCP_CLK_CTRL0                     0x30
 #define CDNS_MCP_CLK_CTRL1                     0x38
+#define CDNS_MCP_CLK_MCLKD_MASK                GENMASK(7, 0)
 
 #define CDNS_MCP_STAT                          0x40
 
 #define CDNS_MCP_INT_DPINT                     BIT(11)
 #define CDNS_MCP_INT_CTRL_CLASH                        BIT(10)
 #define CDNS_MCP_INT_DATA_CLASH                        BIT(9)
+#define CDNS_MCP_INT_PARITY                    BIT(8)
 #define CDNS_MCP_INT_CMD_ERR                   BIT(7)
+#define CDNS_MCP_INT_RX_NE                     BIT(3)
 #define CDNS_MCP_INT_RX_WL                     BIT(2)
 #define CDNS_MCP_INT_TXE                       BIT(1)
+#define CDNS_MCP_INT_TXF                       BIT(0)
 
 #define CDNS_MCP_INTSET                                0x4C
 
 #define CDNS_PDI_CONFIG_PORT                   GENMASK(4, 0)
 
 /* Driver defaults */
-
-#define CDNS_DEFAULT_CLK_DIVIDER               0
-#define CDNS_DEFAULT_FRAME_SHAPE               0x30
 #define CDNS_DEFAULT_SSP_INTERVAL              0x18
 #define CDNS_TX_TIMEOUT                                2000
 
@@ -224,6 +232,112 @@ static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value)
 }
 
 /*
+ * debugfs
+ */
+#ifdef CONFIG_DEBUG_FS
+
+#define RD_BUF (2 * PAGE_SIZE)
+
+static ssize_t cdns_sprintf(struct sdw_cdns *cdns,
+                           char *buf, size_t pos, unsigned int reg)
+{
+       return scnprintf(buf + pos, RD_BUF - pos,
+                        "%4x\t%8x\n", reg, cdns_readl(cdns, reg));
+}
+
+static int cdns_reg_show(struct seq_file *s, void *data)
+{
+       struct sdw_cdns *cdns = s->private;
+       char *buf;
+       ssize_t ret;
+       int num_ports;
+       int i, j;
+
+       buf = kzalloc(RD_BUF, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = scnprintf(buf, RD_BUF, "Register  Value\n");
+       ret += scnprintf(buf + ret, RD_BUF - ret, "\nMCP Registers\n");
+       /* 8 MCP registers */
+       for (i = CDNS_MCP_CONFIG; i <= CDNS_MCP_PHYCTRL; i += sizeof(u32))
+               ret += cdns_sprintf(cdns, buf, ret, i);
+
+       ret += scnprintf(buf + ret, RD_BUF - ret,
+                        "\nStatus & Intr Registers\n");
+       /* 13 Status & Intr registers (offsets 0x70 and 0x74 not defined) */
+       for (i = CDNS_MCP_STAT; i <=  CDNS_MCP_FIFOSTAT; i += sizeof(u32))
+               ret += cdns_sprintf(cdns, buf, ret, i);
+
+       ret += scnprintf(buf + ret, RD_BUF - ret,
+                        "\nSSP & Clk ctrl Registers\n");
+       ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL0);
+       ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL1);
+       ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL0);
+       ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL1);
+
+       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;
+
+       for (i = 0; i < num_ports; i++) {
+               ret += scnprintf(buf + ret, RD_BUF - ret,
+                                "\nDP-%d\n", i);
+               for (j = CDNS_DPN_B0_CONFIG(i);
+                    j < CDNS_DPN_B0_ASYNC_CTRL(i); j += sizeof(u32))
+                       ret += cdns_sprintf(cdns, buf, ret, j);
+       }
+
+       ret += scnprintf(buf + ret, RD_BUF - ret,
+                        "\nDPn B1 Registers\n");
+       for (i = 0; i < num_ports; i++) {
+               ret += scnprintf(buf + ret, RD_BUF - ret,
+                                "\nDP-%d\n", i);
+
+               for (j = CDNS_DPN_B1_CONFIG(i);
+                    j < CDNS_DPN_B1_ASYNC_CTRL(i); j += sizeof(u32))
+                       ret += cdns_sprintf(cdns, buf, ret, j);
+       }
+
+       ret += scnprintf(buf + ret, RD_BUF - ret,
+                        "\nDPn Control Registers\n");
+       for (i = 0; i < num_ports; i++)
+               ret += cdns_sprintf(cdns, buf, ret,
+                               CDNS_PORTCTRL + i * CDNS_PORT_OFFSET);
+
+       ret += scnprintf(buf + ret, RD_BUF - ret,
+                        "\nPDIn Config Registers\n");
+
+       /* number of PDI and ports is interchangeable */
+       for (i = 0; i < num_ports; i++)
+               ret += cdns_sprintf(cdns, buf, ret, CDNS_PDI_CONFIG(i));
+
+       seq_printf(s, "%s", buf);
+       kfree(buf);
+
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(cdns_reg);
+
+/**
+ * sdw_cdns_debugfs_init() - Cadence debugfs init
+ * @cdns: Cadence instance
+ * @root: debugfs root
+ */
+void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root)
+{
+       debugfs_create_file("cdns-registers", 0400, root, cdns, &cdns_reg_fops);
+}
+EXPORT_SYMBOL_GPL(sdw_cdns_debugfs_init);
+
+#endif /* CONFIG_DEBUG_FS */
+
+/*
  * IO Calls
  */
 static enum sdw_command_response
@@ -575,10 +689,14 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
                }
        }
 
+       if (int_status & CDNS_MCP_INT_PARITY) {
+               /* Parity error detected by Master */
+               dev_err_ratelimited(cdns->dev, "Parity error\n");
+       }
+
        if (int_status & CDNS_MCP_INT_CTRL_CLASH) {
                /* Slave is driving bit slot during control word */
                dev_err_ratelimited(cdns->dev, "Bus clash for control word\n");
-               int_status |= CDNS_MCP_INT_CTRL_CLASH;
        }
 
        if (int_status & CDNS_MCP_INT_DATA_CLASH) {
@@ -587,7 +705,6 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
                 * ownership of data bits or Slave gone bonkers
                 */
                dev_err_ratelimited(cdns->dev, "Bus clash for data word\n");
-               int_status |= CDNS_MCP_INT_DATA_CLASH;
        }
 
        if (int_status & CDNS_MCP_INT_SLAVE_MASK) {
@@ -644,10 +761,26 @@ static int _cdns_enable_interrupt(struct sdw_cdns *cdns)
        cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1,
                    CDNS_MCP_SLAVE_INTMASK1_MASK);
 
-       mask = CDNS_MCP_INT_SLAVE_RSVD | CDNS_MCP_INT_SLAVE_ALERT |
-               CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH |
-               CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH |
-               CDNS_MCP_INT_RX_WL | CDNS_MCP_INT_IRQ | CDNS_MCP_INT_DPINT;
+       /* enable detection of all slave state changes */
+       mask = CDNS_MCP_INT_SLAVE_MASK;
+
+       /* enable detection of bus issues */
+       mask |= CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH |
+               CDNS_MCP_INT_PARITY;
+
+       /* no detection of port interrupts for now */
+
+       /* enable detection of RX fifo level */
+       mask |= CDNS_MCP_INT_RX_WL;
+
+       /*
+        * CDNS_MCP_INT_IRQ needs to be set otherwise all previous
+        * settings are irrelevant
+        */
+       mask |= CDNS_MCP_INT_IRQ;
+
+       if (interrupt_mask) /* parameter override */
+               mask = interrupt_mask;
 
        cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
 
@@ -788,13 +921,30 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
 }
 EXPORT_SYMBOL(sdw_cdns_pdi_init);
 
+static u32 cdns_set_initial_frame_shape(int n_rows, int n_cols)
+{
+       u32 val;
+       int c;
+       int r;
+
+       r = sdw_find_row_index(n_rows);
+       c = sdw_find_col_index(n_cols) & CDNS_MCP_FRAME_SHAPE_COL_MASK;
+
+       val = (r << CDNS_MCP_FRAME_SHAPE_ROW_OFFSET) | c;
+
+       return val;
+}
+
 /**
  * sdw_cdns_init() - Cadence initialization
  * @cdns: Cadence instance
  */
 int sdw_cdns_init(struct sdw_cdns *cdns)
 {
+       struct sdw_bus *bus = &cdns->bus;
+       struct sdw_master_prop *prop = &bus->prop;
        u32 val;
+       int divider;
        int ret;
 
        /* Exit clock stop */
@@ -806,12 +956,20 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
        }
 
        /* Set clock divider */
-       val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0);
-       val |= CDNS_DEFAULT_CLK_DIVIDER;
-       cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val);
+       divider = (prop->mclk_freq / prop->max_clk_freq) - 1;
 
-       /* Set the default frame shape */
-       cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, CDNS_DEFAULT_FRAME_SHAPE);
+       cdns_updatel(cdns, CDNS_MCP_CLK_CTRL0,
+                    CDNS_MCP_CLK_MCLKD_MASK, divider);
+       cdns_updatel(cdns, CDNS_MCP_CLK_CTRL1,
+                    CDNS_MCP_CLK_MCLKD_MASK, divider);
+
+       /*
+        * Frame shape changes after initialization have to be done
+        * with the bank switch mechanism
+        */
+       val = cdns_set_initial_frame_shape(prop->default_row,
+                                          prop->default_col);
+       cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, val);
 
        /* Set SSP interval to default value */
        cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL);
@@ -851,8 +1009,9 @@ EXPORT_SYMBOL(sdw_cdns_init);
 
 int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params)
 {
+       struct sdw_master_prop *prop = &bus->prop;
        struct sdw_cdns *cdns = bus_to_cdns(bus);
-       int mcp_clkctrl_off, mcp_clkctrl;
+       int mcp_clkctrl_off;
        int divider;
 
        if (!params->curr_dr_freq) {
@@ -860,16 +1019,16 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params)
                return -EINVAL;
        }
 
-       divider = (params->max_dr_freq / params->curr_dr_freq) - 1;
+       divider = prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR /
+               params->curr_dr_freq;
+       divider--; /* divider is 1/(N+1) */
 
        if (params->next_bank)
                mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1;
        else
                mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0;
 
-       mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off);
-       mcp_clkctrl |= divider;
-       cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl);
+       cdns_updatel(cdns, mcp_clkctrl_off, CDNS_MCP_CLK_MCLKD_MASK, divider);
 
        return 0;
 }
@@ -1170,19 +1329,5 @@ int sdw_cdns_alloc_stream(struct sdw_cdns *cdns,
 }
 EXPORT_SYMBOL(sdw_cdns_alloc_stream);
 
-void sdw_cdns_shutdown(struct snd_pcm_substream *substream,
-                      struct snd_soc_dai *dai)
-{
-       struct sdw_cdns_dma_data *dma;
-
-       dma = snd_soc_dai_get_dma_data(dai, substream);
-       if (!dma)
-               return;
-
-       snd_soc_dai_set_dma_data(dai, substream, NULL);
-       kfree(dma);
-}
-EXPORT_SYMBOL(sdw_cdns_shutdown);
-
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_DESCRIPTION("Cadence Soundwire Library");