Merge tag 'usb-serial-5.15-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / soundwire / cadence_master.c
index a9bd56f..4fcc3ba 100644 (file)
@@ -450,6 +450,40 @@ static int cdns_parity_error_injection(void *data, u64 value)
 DEFINE_DEBUGFS_ATTRIBUTE(cdns_parity_error_fops, NULL,
                         cdns_parity_error_injection, "%llu\n");
 
+static int cdns_set_pdi_loopback_source(void *data, u64 value)
+{
+       struct sdw_cdns *cdns = data;
+       unsigned int pdi_out_num = cdns->pcm.num_bd + cdns->pcm.num_out;
+
+       if (value > pdi_out_num)
+               return -EINVAL;
+
+       /* Userspace changed the hardware state behind the kernel's back */
+       add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+
+       cdns->pdi_loopback_source = value;
+
+       return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(cdns_pdi_loopback_source_fops, NULL, cdns_set_pdi_loopback_source, "%llu\n");
+
+static int cdns_set_pdi_loopback_target(void *data, u64 value)
+{
+       struct sdw_cdns *cdns = data;
+       unsigned int pdi_in_num = cdns->pcm.num_bd + cdns->pcm.num_in;
+
+       if (value > pdi_in_num)
+               return -EINVAL;
+
+       /* Userspace changed the hardware state behind the kernel's back */
+       add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+
+       cdns->pdi_loopback_target = value;
+
+       return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(cdns_pdi_loopback_target_fops, NULL, cdns_set_pdi_loopback_target, "%llu\n");
+
 /**
  * sdw_cdns_debugfs_init() - Cadence debugfs init
  * @cdns: Cadence instance
@@ -464,6 +498,16 @@ void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root)
 
        debugfs_create_file("cdns-parity-error-injection", 0200, root, cdns,
                            &cdns_parity_error_fops);
+
+       cdns->pdi_loopback_source = -1;
+       cdns->pdi_loopback_target = -1;
+
+       debugfs_create_file("cdns-pdi-loopback-source", 0200, root, cdns,
+                           &cdns_pdi_loopback_source_fops);
+
+       debugfs_create_file("cdns-pdi-loopback-target", 0200, root, cdns,
+                           &cdns_pdi_loopback_target_fops);
+
 }
 EXPORT_SYMBOL_GPL(sdw_cdns_debugfs_init);
 
@@ -988,10 +1032,7 @@ EXPORT_SYMBOL(sdw_cdns_check_self_clearing_bits);
  */
 int sdw_cdns_exit_reset(struct sdw_cdns *cdns)
 {
-       /* program maximum length reset to be safe */
-       cdns_updatel(cdns, CDNS_MCP_CONTROL,
-                    CDNS_MCP_CONTROL_RST_DELAY,
-                    CDNS_MCP_CONTROL_RST_DELAY);
+       /* keep reset delay unchanged to 4096 cycles */
 
        /* use hardware generated reset */
        cdns_updatel(cdns, CDNS_MCP_CONTROL,
@@ -1330,20 +1371,37 @@ static int cdns_port_params(struct sdw_bus *bus,
                            struct sdw_port_params *p_params, unsigned int bank)
 {
        struct sdw_cdns *cdns = bus_to_cdns(bus);
-       int dpn_config = 0, dpn_config_off;
+       int dpn_config_off_source;
+       int dpn_config_off_target;
+       int target_num = p_params->num;
+       int source_num = p_params->num;
+       bool override = false;
+       int dpn_config;
+
+       if (target_num == cdns->pdi_loopback_target &&
+           cdns->pdi_loopback_source != -1) {
+               source_num = cdns->pdi_loopback_source;
+               override = true;
+       }
 
-       if (bank)
-               dpn_config_off = CDNS_DPN_B1_CONFIG(p_params->num);
-       else
-               dpn_config_off = CDNS_DPN_B0_CONFIG(p_params->num);
+       if (bank) {
+               dpn_config_off_source = CDNS_DPN_B1_CONFIG(source_num);
+               dpn_config_off_target = CDNS_DPN_B1_CONFIG(target_num);
+       } else {
+               dpn_config_off_source = CDNS_DPN_B0_CONFIG(source_num);
+               dpn_config_off_target = CDNS_DPN_B0_CONFIG(target_num);
+       }
 
-       dpn_config = cdns_readl(cdns, dpn_config_off);
+       dpn_config = cdns_readl(cdns, dpn_config_off_source);
 
-       u32p_replace_bits(&dpn_config, (p_params->bps - 1), CDNS_DPN_CONFIG_WL);
-       u32p_replace_bits(&dpn_config, p_params->flow_mode, CDNS_DPN_CONFIG_PORT_FLOW);
-       u32p_replace_bits(&dpn_config, p_params->data_mode, CDNS_DPN_CONFIG_PORT_DAT);
+       /* use port params if there is no loopback, otherwise use source as is */
+       if (!override) {
+               u32p_replace_bits(&dpn_config, p_params->bps - 1, CDNS_DPN_CONFIG_WL);
+               u32p_replace_bits(&dpn_config, p_params->flow_mode, CDNS_DPN_CONFIG_PORT_FLOW);
+               u32p_replace_bits(&dpn_config, p_params->data_mode, CDNS_DPN_CONFIG_PORT_DAT);
+       }
 
-       cdns_writel(cdns, dpn_config_off, dpn_config);
+       cdns_writel(cdns, dpn_config_off_target, dpn_config);
 
        return 0;
 }
@@ -1353,11 +1411,27 @@ static int cdns_transport_params(struct sdw_bus *bus,
                                 enum sdw_reg_bank bank)
 {
        struct sdw_cdns *cdns = bus_to_cdns(bus);
-       int dpn_offsetctrl = 0, dpn_offsetctrl_off;
-       int dpn_config = 0, dpn_config_off;
-       int dpn_hctrl = 0, dpn_hctrl_off;
-       int num = t_params->port_num;
-       int dpn_samplectrl_off;
+       int dpn_config;
+       int dpn_config_off_source;
+       int dpn_config_off_target;
+       int dpn_hctrl;
+       int dpn_hctrl_off_source;
+       int dpn_hctrl_off_target;
+       int dpn_offsetctrl;
+       int dpn_offsetctrl_off_source;
+       int dpn_offsetctrl_off_target;
+       int dpn_samplectrl;
+       int dpn_samplectrl_off_source;
+       int dpn_samplectrl_off_target;
+       int source_num = t_params->port_num;
+       int target_num = t_params->port_num;
+       bool override = false;
+
+       if (target_num == cdns->pdi_loopback_target &&
+           cdns->pdi_loopback_source != -1) {
+               source_num = cdns->pdi_loopback_source;
+               override = true;
+       }
 
        /*
         * Note: Only full data port is supported on the Master side for
@@ -1365,32 +1439,59 @@ static int cdns_transport_params(struct sdw_bus *bus,
         */
 
        if (bank) {
-               dpn_config_off = CDNS_DPN_B1_CONFIG(num);
-               dpn_samplectrl_off = CDNS_DPN_B1_SAMPLE_CTRL(num);
-               dpn_hctrl_off = CDNS_DPN_B1_HCTRL(num);
-               dpn_offsetctrl_off = CDNS_DPN_B1_OFFSET_CTRL(num);
+               dpn_config_off_source = CDNS_DPN_B1_CONFIG(source_num);
+               dpn_hctrl_off_source = CDNS_DPN_B1_HCTRL(source_num);
+               dpn_offsetctrl_off_source = CDNS_DPN_B1_OFFSET_CTRL(source_num);
+               dpn_samplectrl_off_source = CDNS_DPN_B1_SAMPLE_CTRL(source_num);
+
+               dpn_config_off_target = CDNS_DPN_B1_CONFIG(target_num);
+               dpn_hctrl_off_target = CDNS_DPN_B1_HCTRL(target_num);
+               dpn_offsetctrl_off_target = CDNS_DPN_B1_OFFSET_CTRL(target_num);
+               dpn_samplectrl_off_target = CDNS_DPN_B1_SAMPLE_CTRL(target_num);
+
        } else {
-               dpn_config_off = CDNS_DPN_B0_CONFIG(num);
-               dpn_samplectrl_off = CDNS_DPN_B0_SAMPLE_CTRL(num);
-               dpn_hctrl_off = CDNS_DPN_B0_HCTRL(num);
-               dpn_offsetctrl_off = CDNS_DPN_B0_OFFSET_CTRL(num);
+               dpn_config_off_source = CDNS_DPN_B0_CONFIG(source_num);
+               dpn_hctrl_off_source = CDNS_DPN_B0_HCTRL(source_num);
+               dpn_offsetctrl_off_source = CDNS_DPN_B0_OFFSET_CTRL(source_num);
+               dpn_samplectrl_off_source = CDNS_DPN_B0_SAMPLE_CTRL(source_num);
+
+               dpn_config_off_target = CDNS_DPN_B0_CONFIG(target_num);
+               dpn_hctrl_off_target = CDNS_DPN_B0_HCTRL(target_num);
+               dpn_offsetctrl_off_target = CDNS_DPN_B0_OFFSET_CTRL(target_num);
+               dpn_samplectrl_off_target = CDNS_DPN_B0_SAMPLE_CTRL(target_num);
        }
 
-       dpn_config = cdns_readl(cdns, dpn_config_off);
-       u32p_replace_bits(&dpn_config, t_params->blk_grp_ctrl, CDNS_DPN_CONFIG_BGC);
-       u32p_replace_bits(&dpn_config, t_params->blk_pkg_mode, CDNS_DPN_CONFIG_BPM);
-       cdns_writel(cdns, dpn_config_off, dpn_config);
+       dpn_config = cdns_readl(cdns, dpn_config_off_source);
+       if (!override) {
+               u32p_replace_bits(&dpn_config, t_params->blk_grp_ctrl, CDNS_DPN_CONFIG_BGC);
+               u32p_replace_bits(&dpn_config, t_params->blk_pkg_mode, CDNS_DPN_CONFIG_BPM);
+       }
+       cdns_writel(cdns, dpn_config_off_target, dpn_config);
 
-       u32p_replace_bits(&dpn_offsetctrl, t_params->offset1, CDNS_DPN_OFFSET_CTRL_1);
-       u32p_replace_bits(&dpn_offsetctrl, t_params->offset2, CDNS_DPN_OFFSET_CTRL_2);
-       cdns_writel(cdns, dpn_offsetctrl_off,  dpn_offsetctrl);
+       if (!override) {
+               dpn_offsetctrl = 0;
+               u32p_replace_bits(&dpn_offsetctrl, t_params->offset1, CDNS_DPN_OFFSET_CTRL_1);
+               u32p_replace_bits(&dpn_offsetctrl, t_params->offset2, CDNS_DPN_OFFSET_CTRL_2);
+       } else {
+               dpn_offsetctrl = cdns_readl(cdns, dpn_offsetctrl_off_source);
+       }
+       cdns_writel(cdns, dpn_offsetctrl_off_target,  dpn_offsetctrl);
 
-       u32p_replace_bits(&dpn_hctrl, t_params->hstart, CDNS_DPN_HCTRL_HSTART);
-       u32p_replace_bits(&dpn_hctrl, t_params->hstop, CDNS_DPN_HCTRL_HSTOP);
-       u32p_replace_bits(&dpn_hctrl, t_params->lane_ctrl, CDNS_DPN_HCTRL_LCTRL);
+       if (!override) {
+               dpn_hctrl = 0;
+               u32p_replace_bits(&dpn_hctrl, t_params->hstart, CDNS_DPN_HCTRL_HSTART);
+               u32p_replace_bits(&dpn_hctrl, t_params->hstop, CDNS_DPN_HCTRL_HSTOP);
+               u32p_replace_bits(&dpn_hctrl, t_params->lane_ctrl, CDNS_DPN_HCTRL_LCTRL);
+       } else {
+               dpn_hctrl = cdns_readl(cdns, dpn_hctrl_off_source);
+       }
+       cdns_writel(cdns, dpn_hctrl_off_target, dpn_hctrl);
 
-       cdns_writel(cdns, dpn_hctrl_off, dpn_hctrl);
-       cdns_writel(cdns, dpn_samplectrl_off, (t_params->sample_interval - 1));
+       if (!override)
+               dpn_samplectrl = t_params->sample_interval - 1;
+       else
+               dpn_samplectrl = cdns_readl(cdns, dpn_samplectrl_off_source);
+       cdns_writel(cdns, dpn_samplectrl_off_target, dpn_samplectrl);
 
        return 0;
 }