Thanks for the information!
In that case this code example might work.
I tried to come up with a similar model architecture, so I just used three linear layers and had to transpose the weight matrix since I used a “bottleneck” layer.
Also, I call x.detach() after the first linear layer to fake your behavior of the gradient loss in the input layer:
class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.fc1 = nn.Linear(10, 5)
        self.fc2 = nn.Linear(5, 5)
        self.fc3 = nn.Linear(5, 10)
        
    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = x.detach()  # Fake your use case so that fc1 cannot be updated
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

data = torch.randn(10, 10)
target = torch.randn(10, 10)
model = MyModel()
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=1e-3)

for epoch in range(10):
    optimizer.zero_grad()
    output = model(data)
    loss = criterion(output, target)
    loss.backward()
    # print(model.fc1.weight.grad) # Will be None
    optimizer.step()
    print('Epoch {}, loss {}'.format(epoch, loss.item()))
    
    # Copy weights from output layer to input layer
    with torch.no_grad():
        model.fc1.weight.copy_(model.fc3.weight.t())

Let me know, if this helps or if I misunderstood your use case.