return 0;
 }
 
+static int mlxsw_sp_port_module_map(struct mlxsw_sp *mlxsw_sp, u8 local_port,
+                                   u8 module, u8 width, u8 lane)
+{
+       char pmlp_pl[MLXSW_REG_PMLP_LEN];
+       int i;
+
+       mlxsw_reg_pmlp_pack(pmlp_pl, local_port);
+       mlxsw_reg_pmlp_width_set(pmlp_pl, width);
+       for (i = 0; i < width; i++) {
+               mlxsw_reg_pmlp_module_set(pmlp_pl, i, module);
+               mlxsw_reg_pmlp_tx_lane_set(pmlp_pl, i, lane + i);  /* Rx & Tx */
+       }
+
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl);
+}
+
 static int mlxsw_sp_port_module_unmap(struct mlxsw_sp *mlxsw_sp, u8 local_port)
 {
        char pmlp_pl[MLXSW_REG_PMLP_LEN];
        return ptys_proto;
 }
 
+static u32 mlxsw_sp_to_ptys_upper_speed(u32 upper_speed)
+{
+       u32 ptys_proto = 0;
+       int i;
+
+       for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) {
+               if (mlxsw_sp_port_link_mode[i].speed <= upper_speed)
+                       ptys_proto |= mlxsw_sp_port_link_mode[i].mask;
+       }
+       return ptys_proto;
+}
+
 static int mlxsw_sp_port_set_settings(struct net_device *dev,
                                      struct ethtool_cmd *cmd)
 {
        .set_settings           = mlxsw_sp_port_set_settings,
 };
 
-static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
+static int
+mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port, u8 width)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       u32 upper_speed = MLXSW_SP_PORT_BASE_SPEED * width;
+       char ptys_pl[MLXSW_REG_PTYS_LEN];
+       u32 eth_proto_admin;
+
+       eth_proto_admin = mlxsw_sp_to_ptys_upper_speed(upper_speed);
+       mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sp_port->local_port,
+                           eth_proto_admin);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
+}
+
+static int __mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
+                                 bool split, u8 module, u8 width)
 {
        struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
        struct mlxsw_sp_port *mlxsw_sp_port;
        mlxsw_sp_port->dev = dev;
        mlxsw_sp_port->mlxsw_sp = mlxsw_sp;
        mlxsw_sp_port->local_port = local_port;
+       mlxsw_sp_port->split = split;
        bytes = DIV_ROUND_UP(VLAN_N_VID, BITS_PER_BYTE);
        mlxsw_sp_port->active_vlans = kzalloc(bytes, GFP_KERNEL);
        if (!mlxsw_sp_port->active_vlans) {
        dev->hard_header_len += MLXSW_TXHDR_LEN;
 
        devlink_port = &mlxsw_sp_port->devlink_port;
+       if (mlxsw_sp_port->split)
+               devlink_port_split_set(devlink_port, module);
        err = devlink_port_register(devlink, devlink_port, local_port);
        if (err) {
                dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to register devlink port\n",
                goto err_port_swid_set;
        }
 
+       err = mlxsw_sp_port_speed_by_width_set(mlxsw_sp_port, width);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to enable speeds\n",
+                       mlxsw_sp_port->local_port);
+               goto err_port_speed_by_width_set;
+       }
+
        err = mlxsw_sp_port_mtu_set(mlxsw_sp_port, ETH_DATA_LEN);
        if (err) {
                dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set MTU\n",
 err_port_buffers_init:
 err_port_admin_status_set:
 err_port_mtu_set:
+err_port_speed_by_width_set:
 err_port_swid_set:
 err_port_system_port_mapping_set:
        devlink_port_unregister(&mlxsw_sp_port->devlink_port);
        return err;
 }
 
+static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
+                               bool split, u8 module, u8 width, u8 lane)
+{
+       int err;
+
+       err = mlxsw_sp_port_module_map(mlxsw_sp, local_port, module, width,
+                                      lane);
+       if (err)
+               return err;
+
+       err = __mlxsw_sp_port_create(mlxsw_sp, local_port, split, module,
+                                    width);
+       if (err)
+               goto err_port_create;
+
+       return 0;
+
+err_port_create:
+       mlxsw_sp_port_module_unmap(mlxsw_sp, local_port);
+       return err;
+}
+
 static void mlxsw_sp_port_vports_fini(struct mlxsw_sp_port *mlxsw_sp_port)
 {
        struct net_device *dev = mlxsw_sp_port->dev;
                if (!width)
                        continue;
                mlxsw_sp->port_to_module[i] = module;
-               err = mlxsw_sp_port_create(mlxsw_sp, i);
+               err = __mlxsw_sp_port_create(mlxsw_sp, i, false, module, width);
                if (err)
                        goto err_port_create;
        }
        return err;
 }
 
+static u8 mlxsw_sp_cluster_base_port_get(u8 local_port)
+{
+       u8 offset = (local_port - 1) % MLXSW_SP_PORTS_PER_CLUSTER_MAX;
+
+       return local_port - offset;
+}
+
+static int mlxsw_sp_port_split(void *priv, u8 local_port, unsigned int count)
+{
+       struct mlxsw_sp *mlxsw_sp = priv;
+       struct mlxsw_sp_port *mlxsw_sp_port;
+       u8 width = MLXSW_PORT_MODULE_MAX_WIDTH / count;
+       u8 module, cur_width, base_port;
+       int i;
+       int err;
+
+       mlxsw_sp_port = mlxsw_sp->ports[local_port];
+       if (!mlxsw_sp_port) {
+               dev_err(mlxsw_sp->bus_info->dev, "Port number \"%d\" does not exist\n",
+                       local_port);
+               return -EINVAL;
+       }
+
+       if (count != 2 && count != 4) {
+               netdev_err(mlxsw_sp_port->dev, "Port can only be split into 2 or 4 ports\n");
+               return -EINVAL;
+       }
+
+       err = mlxsw_sp_port_module_info_get(mlxsw_sp, local_port, &module,
+                                           &cur_width);
+       if (err) {
+               netdev_err(mlxsw_sp_port->dev, "Failed to get port's width\n");
+               return err;
+       }
+
+       if (cur_width != MLXSW_PORT_MODULE_MAX_WIDTH) {
+               netdev_err(mlxsw_sp_port->dev, "Port cannot be split further\n");
+               return -EINVAL;
+       }
+
+       /* Make sure we have enough slave (even) ports for the split. */
+       if (count == 2) {
+               base_port = local_port;
+               if (mlxsw_sp->ports[base_port + 1]) {
+                       netdev_err(mlxsw_sp_port->dev, "Invalid split configuration\n");
+                       return -EINVAL;
+               }
+       } else {
+               base_port = mlxsw_sp_cluster_base_port_get(local_port);
+               if (mlxsw_sp->ports[base_port + 1] ||
+                   mlxsw_sp->ports[base_port + 3]) {
+                       netdev_err(mlxsw_sp_port->dev, "Invalid split configuration\n");
+                       return -EINVAL;
+               }
+       }
+
+       for (i = 0; i < count; i++)
+               mlxsw_sp_port_remove(mlxsw_sp, base_port + i);
+
+       for (i = 0; i < count; i++) {
+               err = mlxsw_sp_port_create(mlxsw_sp, base_port + i, true,
+                                          module, width, i * width);
+               if (err) {
+                       dev_err(mlxsw_sp->bus_info->dev, "Failed to create split port\n");
+                       goto err_port_create;
+               }
+       }
+
+       return 0;
+
+err_port_create:
+       for (i--; i >= 0; i--)
+               mlxsw_sp_port_remove(mlxsw_sp, base_port + i);
+       for (i = 0; i < count / 2; i++) {
+               module = mlxsw_sp->port_to_module[base_port + i * 2];
+               mlxsw_sp_port_create(mlxsw_sp, base_port + i * 2, false,
+                                    module, MLXSW_PORT_MODULE_MAX_WIDTH, 0);
+       }
+       return err;
+}
+
+static int mlxsw_sp_port_unsplit(void *priv, u8 local_port)
+{
+       struct mlxsw_sp *mlxsw_sp = priv;
+       struct mlxsw_sp_port *mlxsw_sp_port;
+       u8 module, cur_width, base_port;
+       unsigned int count;
+       int i;
+       int err;
+
+       mlxsw_sp_port = mlxsw_sp->ports[local_port];
+       if (!mlxsw_sp_port) {
+               dev_err(mlxsw_sp->bus_info->dev, "Port number \"%d\" does not exist\n",
+                       local_port);
+               return -EINVAL;
+       }
+
+       if (!mlxsw_sp_port->split) {
+               netdev_err(mlxsw_sp_port->dev, "Port wasn't split\n");
+               return -EINVAL;
+       }
+
+       err = mlxsw_sp_port_module_info_get(mlxsw_sp, local_port, &module,
+                                           &cur_width);
+       if (err) {
+               netdev_err(mlxsw_sp_port->dev, "Failed to get port's width\n");
+               return err;
+       }
+       count = cur_width == 1 ? 4 : 2;
+
+       base_port = mlxsw_sp_cluster_base_port_get(local_port);
+
+       /* Determine which ports to remove. */
+       if (count == 2 && local_port >= base_port + 2)
+               base_port = base_port + 2;
+
+       for (i = 0; i < count; i++)
+               mlxsw_sp_port_remove(mlxsw_sp, base_port + i);
+
+       for (i = 0; i < count / 2; i++) {
+               module = mlxsw_sp->port_to_module[base_port + i * 2];
+               err = mlxsw_sp_port_create(mlxsw_sp, base_port + i * 2, false,
+                                          module, MLXSW_PORT_MODULE_MAX_WIDTH,
+                                          0);
+               if (err)
+                       dev_err(mlxsw_sp->bus_info->dev, "Failed to reinstantiate port\n");
+       }
+
+       return 0;
+}
+
 static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg,
                                     char *pude_pl, void *priv)
 {
        .priv_size              = sizeof(struct mlxsw_sp),
        .init                   = mlxsw_sp_init,
        .fini                   = mlxsw_sp_fini,
+       .port_split             = mlxsw_sp_port_split,
+       .port_unsplit           = mlxsw_sp_port_unsplit,
        .txhdr_construct        = mlxsw_sp_txhdr_construct,
        .txhdr_len              = MLXSW_TXHDR_LEN,
        .profile                = &mlxsw_sp_config_profile,