Message ID | 20210813084536.182381-1-yoong.siang.song@intel.com |
---|---|
State | New |
Headers | show |
Series | [net-next,1/1] net: phy: marvell10g: Add WAKE_PHY support to WOL event | expand |
On Fri, 13 Aug 2021 16:45:36 +0800 Song Yoong Siang <yoong.siang.song@intel.com> wrote: > Add Wake-on-PHY feature support by enabling the Link Status Changed > interrupt. > > Signed-off-by: Song Yoong Siang <yoong.siang.song@intel.com> Hi Song, I presume this is also tested. The code look ok. Reviewed-by: Marek Behún <kabel@kernel.org>
On Fri, Aug 13, 2021 at 04:45:36PM +0800, Song Yoong Siang wrote: > Add Wake-on-PHY feature support by enabling the Link Status Changed > interrupt. > > Signed-off-by: Song Yoong Siang <yoong.siang.song@intel.com> > --- > drivers/net/phy/marvell10g.c | 33 ++++++++++++++++++++++++++++++++- > 1 file changed, 32 insertions(+), 1 deletion(-) > > diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c > index 0b7cae118ad7..d46761c225f0 100644 > --- a/drivers/net/phy/marvell10g.c > +++ b/drivers/net/phy/marvell10g.c > @@ -76,6 +76,11 @@ enum { > MV_PCS_CSSR1_SPD2_2500 = 0x0004, > MV_PCS_CSSR1_SPD2_10000 = 0x0000, > > + /* Copper Specific Interrupt registers */ > + MV_PCS_INTR_ENABLE = 0x8010, > + MV_PCS_INTR_ENABLE_LSC = BIT(10), > + MV_PCS_INTR_STS = 0x8011, > + > /* Temperature read register (88E2110 only) */ > MV_PCS_TEMP = 0x8042, > > @@ -1036,7 +1041,7 @@ static void mv3110_get_wol(struct phy_device *phydev, > { > int ret; > > - wol->supported = WAKE_MAGIC; > + wol->supported = WAKE_MAGIC | WAKE_PHY; > wol->wolopts = 0; > > ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_WOL_CTRL); > @@ -1045,6 +1050,13 @@ static void mv3110_get_wol(struct phy_device *phydev, > > if (ret & MV_V2_WOL_CTRL_MAGIC_PKT_EN) > wol->wolopts |= WAKE_MAGIC; > + > + ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_INTR_ENABLE); > + if (ret < 0) > + return; > + > + if (ret & MV_PCS_INTR_ENABLE_LSC) > + wol->wolopts |= WAKE_PHY; > } > > static int mv3110_set_wol(struct phy_device *phydev, > @@ -1099,6 +1111,25 @@ static int mv3110_set_wol(struct phy_device *phydev, > return ret; > } > > + if (wol->wolopts & WAKE_PHY) { > + /* Enable the link status changed interrupt */ > + ret = phy_set_bits_mmd(phydev, MDIO_MMD_PCS, > + MV_PCS_INTR_ENABLE, > + MV_PCS_INTR_ENABLE_LSC); > + if (ret < 0) > + return ret; > + > + /* Clear the interrupt status register */ > + ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_INTR_STS); > + } else { > + /* Disable the link status changed interrupt */ > + ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, > + MV_PCS_INTR_ENABLE, > + MV_PCS_INTR_ENABLE_LSC); > + if (ret < 0) > + return ret; > + } > + How does this work if the driver has no interrupt support? What is the hardware setup this has been tested with? What if we later want to add interrupt support to this driver to support detecting changes in link state - isn't using this bit in the interrupt enable register going to confict with that? -- RMK's Patch system: https://www.armlinux.org.uk/developer/patches/ FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!
> How does this work if the driver has no interrupt support? What is > the hardware setup this has been tested with? Hi Russell We already know from previous patches that the Intel hardware is broken, and does not actually deliver the interrupt which caused the wake up. So i assume this just continues on with the same broken hardware, but they have a different PHY connected. > What if we later want to add interrupt support to this driver to > support detecting changes in link state - isn't using this bit > in the interrupt enable register going to confict with that? Agreed. If the interrupt register is being used, i think we need this patchset to add proper interrupt support. Can you recommend a board they can buy off the shelf with the interrupt wired up? Or maybe Intel can find a hardware engineer to add a patch wire to link the interrupt output to a SoC pin that can do interrupts. Andrew
On Sat, Aug 14, 2021 at 08:04:55PM +0200, Andrew Lunn wrote: > Agreed. If the interrupt register is being used, i think we need this > patchset to add proper interrupt support. Can you recommend a board > they can buy off the shelf with the interrupt wired up? Or maybe Intel > can find a hardware engineer to add a patch wire to link the interrupt > output to a SoC pin that can do interrupts. The only board I'm aware of with the 88x3310 interrupt wired is the Macchiatobin double-shot. :) I forget why I didn't implement interrupt support though - I probably need to revisit that. Sure enough, looking at the code I was tinkering with, adding interrupt support would certainly conflict with this patch. -- RMK's Patch system: https://www.armlinux.org.uk/developer/patches/ FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!
> > How does this work if the driver has no interrupt support? What is the > > hardware setup this has been tested with? > > Hi Russell > > We already know from previous patches that the Intel hardware is broken, > and does not actually deliver the interrupt which caused the wake up. So i > assume this just continues on with the same broken hardware, but they have > a different PHY connected. Hi Russell & Andrew, This is tested on Intel Elkhart Lake (EHL) board. We are using polling mode. Both WoL interrupt and link change interrupt are the same pin which is routed to PMC. PMC will wake up the system when there is WoL event. Regards Siang > > > What if we later want to add interrupt support to this driver to > > support detecting changes in link state - isn't using this bit in the > > interrupt enable register going to confict with that? > > Agreed. If the interrupt register is being used, i think we need this patchset to > add proper interrupt support. Can you recommend a board they can buy off > the shelf with the interrupt wired up? Or maybe Intel can find a hardware > engineer to add a patch wire to link the interrupt output to a SoC pin that can > do interrupts. > > Andrew
> > Agreed. If the interrupt register is being used, i think we need this > > patchset to add proper interrupt support. Can you recommend a board > > they can buy off the shelf with the interrupt wired up? Or maybe Intel > > can find a hardware engineer to add a patch wire to link the interrupt > > output to a SoC pin that can do interrupts. > > The only board I'm aware of with the 88x3310 interrupt wired is the > Macchiatobin double-shot. :) > > I forget why I didn't implement interrupt support though - I probably need to > revisit that. Sure enough, looking at the code I was tinkering with, adding > interrupt support would certainly conflict with this patch. Hi Russell, For EHL board, both WoL interrupt and link change interrupt are the same pin. Based on your knowledge, is this common across other platforms? Can we take set wol function as one of the ways to control the interrupts? Regards, Siang > > -- > RMK's Patch system: https://www.armlinux.org.uk/developer/patches/ > FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!
On Mon, Aug 16, 2021 at 03:19:34AM +0000, Song, Yoong Siang wrote: > > > How does this work if the driver has no interrupt support? What is the > > > hardware setup this has been tested with? > > > > Hi Russell > > > > We already know from previous patches that the Intel hardware is broken, > > and does not actually deliver the interrupt which caused the wake up. So i > > assume this just continues on with the same broken hardware, but they have > > a different PHY connected. > > Hi Russell & Andrew, > > This is tested on Intel Elkhart Lake (EHL) board. We are using polling mode. > Both WoL interrupt and link change interrupt are the same pin which is > routed to PMC. PMC will wake up the system when there is WoL event. Is the PMC also an interrupt controller? Andrew
On Mon, Aug 16, 2021 at 03:52:06AM +0000, Song, Yoong Siang wrote: > > > Agreed. If the interrupt register is being used, i think we need this > > > patchset to add proper interrupt support. Can you recommend a board > > > they can buy off the shelf with the interrupt wired up? Or maybe Intel > > > can find a hardware engineer to add a patch wire to link the interrupt > > > output to a SoC pin that can do interrupts. > > > > The only board I'm aware of with the 88x3310 interrupt wired is the > > Macchiatobin double-shot. :) > > > > I forget why I didn't implement interrupt support though - I probably need to > > revisit that. Sure enough, looking at the code I was tinkering with, adding > > interrupt support would certainly conflict with this patch. > > Hi Russell, > > For EHL board, both WoL interrupt and link change interrupt are the same pin. > Based on your knowledge, is this common across other platforms? Other PHYs? Yes. WoL is just another interrupt, and any interrupt can wake the system, so longer as the interrupt controller can actually wake the system. > Can we take set wol function as one of the ways to control the > interrupts? WOl does not control the interrupt, it is an interrupt source. And you need to service it as an interrupt. So long as your PMC is also an interrupt controller, it should all work. Andrew
> On Mon, Aug 16, 2021 at 03:52:06AM +0000, Song, Yoong Siang wrote: > > > > Agreed. If the interrupt register is being used, i think we need > > > > this patchset to add proper interrupt support. Can you recommend a > > > > board they can buy off the shelf with the interrupt wired up? Or > > > > maybe Intel can find a hardware engineer to add a patch wire to > > > > link the interrupt output to a SoC pin that can do interrupts. > > > > > > The only board I'm aware of with the 88x3310 interrupt wired is the > > > Macchiatobin double-shot. :) > > > > > > I forget why I didn't implement interrupt support though - I > > > probably need to revisit that. Sure enough, looking at the code I > > > was tinkering with, adding interrupt support would certainly conflict with > this patch. > > > > Hi Russell, > > > > For EHL board, both WoL interrupt and link change interrupt are the same > pin. > > Based on your knowledge, is this common across other platforms? > > Other PHYs? Yes. WoL is just another interrupt, and any interrupt can wake > the system, so longer as the interrupt controller can actually wake the > system. > > > Can we take set wol function as one of the ways to control the > > interrupts? > > WOl does not control the interrupt, it is an interrupt source. And you need to > service it as an interrupt. So long as your PMC is also an interrupt controller, > it should all work. > > Andrew Sorry, I should not use the word "control". Actually what I am trying to said was "can we take set_wol() as one of the ways to enable/disable link change interrupt?". PMC is not an interrupt controller. I guess the confusion here is due to I am using polling mode. Let me ask the question differently. What is the conflict that will happen when interrupt support is added? I can help to add config_intr() and handle_interrupt() callback support If they will help to solve the conflict. Regards Siang
On Mon, Aug 16, 2021 at 05:40:18AM +0000, Song, Yoong Siang wrote: > > On Mon, Aug 16, 2021 at 03:52:06AM +0000, Song, Yoong Siang wrote: > > > > > Agreed. If the interrupt register is being used, i think we need > > > > > this patchset to add proper interrupt support. Can you recommend a > > > > > board they can buy off the shelf with the interrupt wired up? Or > > > > > maybe Intel can find a hardware engineer to add a patch wire to > > > > > link the interrupt output to a SoC pin that can do interrupts. > > > > > > > > The only board I'm aware of with the 88x3310 interrupt wired is the > > > > Macchiatobin double-shot. :) > > > > > > > > I forget why I didn't implement interrupt support though - I > > > > probably need to revisit that. Sure enough, looking at the code I > > > > was tinkering with, adding interrupt support would certainly conflict with > > this patch. > > > > > > Hi Russell, > > > > > > For EHL board, both WoL interrupt and link change interrupt are the same > > pin. > > > Based on your knowledge, is this common across other platforms? > > > > Other PHYs? Yes. WoL is just another interrupt, and any interrupt can wake > > the system, so longer as the interrupt controller can actually wake the > > system. > > > > > Can we take set wol function as one of the ways to control the > > > interrupts? > > > > WOl does not control the interrupt, it is an interrupt source. And you need to > > service it as an interrupt. So long as your PMC is also an interrupt controller, > > it should all work. > > > > Andrew > > Sorry, I should not use the word "control". Actually what I am trying to said was > "can we take set_wol() as one of the ways to enable/disable link change interrupt?". > PMC is not an interrupt controller. I guess the confusion here is due to I am > using polling mode. Let me ask the question differently. > > What is the conflict that will happen when interrupt support is added? > I can help to add config_intr() and handle_interrupt() callback support > If they will help to solve the conflict. The conflict is - when interrupt support is added, the link change interrupt will be enabled all the time the PHY is in use. This will have the effect with your patch of making the PHY appear to have WoL enabled, even when it hasn't been configured through a set_wol call. Essentially, your proposal for WoL on link-change fundamentally conflicts with proper interrupt support. -- RMK's Patch system: https://www.armlinux.org.uk/developer/patches/ FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!
> On Mon, Aug 16, 2021 at 05:40:18AM +0000, Song, Yoong Siang wrote: > > > On Mon, Aug 16, 2021 at 03:52:06AM +0000, Song, Yoong Siang wrote: > > > > > > Agreed. If the interrupt register is being used, i think we > > > > > > need this patchset to add proper interrupt support. Can you > > > > > > recommend a board they can buy off the shelf with the > > > > > > interrupt wired up? Or maybe Intel can find a hardware > > > > > > engineer to add a patch wire to link the interrupt output to a SoC pin > that can do interrupts. > > > > > > > > > > The only board I'm aware of with the 88x3310 interrupt wired is > > > > > the Macchiatobin double-shot. :) > > > > > > > > > > I forget why I didn't implement interrupt support though - I > > > > > probably need to revisit that. Sure enough, looking at the code > > > > > I was tinkering with, adding interrupt support would certainly > > > > > conflict with > > > this patch. > > > > > > > > Hi Russell, > > > > > > > > For EHL board, both WoL interrupt and link change interrupt are > > > > the same > > > pin. > > > > Based on your knowledge, is this common across other platforms? > > > > > > Other PHYs? Yes. WoL is just another interrupt, and any interrupt > > > can wake the system, so longer as the interrupt controller can > > > actually wake the system. > > > > > > > Can we take set wol function as one of the ways to control the > > > > interrupts? > > > > > > WOl does not control the interrupt, it is an interrupt source. And > > > you need to service it as an interrupt. So long as your PMC is also > > > an interrupt controller, it should all work. > > > > > > Andrew > > > > Sorry, I should not use the word "control". Actually what I am trying > > to said was "can we take set_wol() as one of the ways to enable/disable > link change interrupt?". > > PMC is not an interrupt controller. I guess the confusion here is due > > to I am using polling mode. Let me ask the question differently. > > > > What is the conflict that will happen when interrupt support is added? > > I can help to add config_intr() and handle_interrupt() callback > > support If they will help to solve the conflict. > > The conflict is - when interrupt support is added, the link change interrupt > will be enabled all the time the PHY is in use. This will have the effect with > your patch of making the PHY appear to have WoL enabled, even when it > hasn't been configured through a set_wol call. > > Essentially, your proposal for WoL on link-change fundamentally conflicts > with proper interrupt support. > Thanks for your explanation. I understand your concern better now. In the case of WoL hasn't been enabled through a set_wol call, the PHY will be suspended, so we no need worry the link change interrupt will create an undesired WoL event. In the case of set_wol is called to disable WAKE_PHY event, we can keep the link change interrupt enable, so that it won't affect the interrupt support. Since any interrupts can wake the system, as long as we handle the future going-to-implement interrupt support properly, things should work fine. Any other thoughts? > -- > RMK's Patch system: https://www.armlinux.org.uk/developer/patches/ > FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!
On Mon, Aug 16, 2021 at 08:03:59AM +0000, Song, Yoong Siang wrote: > Thanks for your explanation. I understand your concern better now. > > In the case of WoL hasn't been enabled through a set_wol call, the PHY will > be suspended, so we no need worry the link change interrupt will create > an undesired WoL event. > > In the case of set_wol is called to disable WAKE_PHY event, we can keep > the link change interrupt enable, so that it won't affect the interrupt > support. I think you're missing the point. In your get_wol method for this PHY: + ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_INTR_ENABLE); + if (ret < 0) + return; + + if (ret & MV_PCS_INTR_ENABLE_LSC) + wol->wolopts |= WAKE_PHY; If the link change interrupt is enabled because we want to use interrupt support, the above code has the effect of reporting to userspace that WoL is enabled, even when nothing has requested WoL to be enabled. This also has the effect of preventing the PHY being suspended (see phy_suspend()) and in effect means that WoL is enabled, even though set_wol() was not called. -- RMK's Patch system: https://www.armlinux.org.uk/developer/patches/ FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!
> On Mon, Aug 16, 2021 at 08:03:59AM +0000, Song, Yoong Siang wrote: > > Thanks for your explanation. I understand your concern better now. > > > > In the case of WoL hasn't been enabled through a set_wol call, the PHY > > will be suspended, so we no need worry the link change interrupt will > > create an undesired WoL event. > > > > In the case of set_wol is called to disable WAKE_PHY event, we can > > keep the link change interrupt enable, so that it won't affect the > > interrupt support. > > I think you're missing the point. In your get_wol method for this > PHY: > > + ret = phy_read_mmd(phydev, MDIO_MMD_PCS, > MV_PCS_INTR_ENABLE); > + if (ret < 0) > + return; > + > + if (ret & MV_PCS_INTR_ENABLE_LSC) > + wol->wolopts |= WAKE_PHY; > > If the link change interrupt is enabled because we want to use interrupt > support, the above code has the effect of reporting to userspace that WoL is > enabled, even when nothing has requested WoL to be enabled. > > This also has the effect of preventing the PHY being suspended (see > phy_suspend()) and in effect means that WoL is enabled, even though > set_wol() was not called. > Yes, you are right. I missed the effect of get_wol. Is it needed in future to implement link change interrupt in phy driver? Cause I dint see much phy driver implement link change interrupt. > -- > RMK's Patch system: https://www.armlinux.org.uk/developer/patches/ > FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!
On Mon, 16 Aug 2021 08:56:36 +0000 "Song, Yoong Siang" <yoong.siang.song@intel.com> wrote: > Yes, you are right. I missed the effect of get_wol. > Is it needed in future to implement link change interrupt in phy > driver? Cause I dint see much phy driver implement link change > interrupt. If there is a board that has interrupt pin wired correctly from the PHY and the interrupt controller is safe to use (i.e. it is not a PCA953x which cannot handle interrupt storms correctly), then I think the PHY driver should use the interrupt, instead of polling. Marek
> > Yes, you are right. I missed the effect of get_wol. > > Is it needed in future to implement link change interrupt in phy > > driver? Cause I dint see much phy driver implement link change > > interrupt. > > If there is a board that has interrupt pin wired correctly from the PHY and the > interrupt controller is safe to use (i.e. it is not a PCA953x which cannot > handle interrupt storms correctly), then I think the PHY driver should use the > interrupt, instead of polling. > > Marek Any suggestion to avoid the conflict of "WoL on link change" mentioned by Russell? Is it make sense to create a new member called wolopts under struct phy_device to track the WoL status and return the correct status in get_wol callback? Regards Siang
On Mon, Aug 16, 2021 at 03:02:03PM +0000, Song, Yoong Siang wrote: > > > Yes, you are right. I missed the effect of get_wol. > > > Is it needed in future to implement link change interrupt in phy > > > driver? Cause I dint see much phy driver implement link change > > > interrupt. > > > > If there is a board that has interrupt pin wired correctly from the PHY and the > > interrupt controller is safe to use (i.e. it is not a PCA953x which cannot > > handle interrupt storms correctly), then I think the PHY driver should use the > > interrupt, instead of polling. > > > > Marek > > Any suggestion to avoid the conflict of "WoL on link change" mentioned by Russell? > Is it make sense to create a new member called wolopts under struct phy_device > to track the WoL status and return the correct status in get_wol callback? I really think you need to look at your PMC and see if you can make it an interrupt controller. You only need level interrupts, not edge. So the microcontroller in the PMC could just poll the GPIO. There appears to be a simple IPC between the host and PMC, so just extend it with a couple of registers, interrupt state, interrupt mask, and make use of the existing interrupt between the host and PMC. Andrew
> > > > Yes, you are right. I missed the effect of get_wol. > > > > Is it needed in future to implement link change interrupt in phy > > > > driver? Cause I dint see much phy driver implement link change > > > > interrupt. > > > > > > If there is a board that has interrupt pin wired correctly from the > > > PHY and the interrupt controller is safe to use (i.e. it is not a > > > PCA953x which cannot handle interrupt storms correctly), then I > > > think the PHY driver should use the interrupt, instead of polling. > > > > > > Marek > > > > Any suggestion to avoid the conflict of "WoL on link change" mentioned by > Russell? > > Is it make sense to create a new member called wolopts under struct > > phy_device to track the WoL status and return the correct status in get_wol > callback? > > I really think you need to look at your PMC and see if you can make it an > interrupt controller. You only need level interrupts, not edge. So the > microcontroller in the PMC could just poll the GPIO. There appears to be a > simple IPC between the host and PMC, so just extend it with a couple of > registers, interrupt state, interrupt mask, and make use of the existing > interrupt between the host and PMC. > > Andrew Thanks for your suggestion. Currently, PMC is designed for platform-wide power management and not meant to control any device specific registers. Seem like it is not possible to make PMC an interrupt controller, but I will continue to discuss more with my team. Regards Siang
diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 0b7cae118ad7..d46761c225f0 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -76,6 +76,11 @@ enum { MV_PCS_CSSR1_SPD2_2500 = 0x0004, MV_PCS_CSSR1_SPD2_10000 = 0x0000, + /* Copper Specific Interrupt registers */ + MV_PCS_INTR_ENABLE = 0x8010, + MV_PCS_INTR_ENABLE_LSC = BIT(10), + MV_PCS_INTR_STS = 0x8011, + /* Temperature read register (88E2110 only) */ MV_PCS_TEMP = 0x8042, @@ -1036,7 +1041,7 @@ static void mv3110_get_wol(struct phy_device *phydev, { int ret; - wol->supported = WAKE_MAGIC; + wol->supported = WAKE_MAGIC | WAKE_PHY; wol->wolopts = 0; ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_WOL_CTRL); @@ -1045,6 +1050,13 @@ static void mv3110_get_wol(struct phy_device *phydev, if (ret & MV_V2_WOL_CTRL_MAGIC_PKT_EN) wol->wolopts |= WAKE_MAGIC; + + ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_INTR_ENABLE); + if (ret < 0) + return; + + if (ret & MV_PCS_INTR_ENABLE_LSC) + wol->wolopts |= WAKE_PHY; } static int mv3110_set_wol(struct phy_device *phydev, @@ -1099,6 +1111,25 @@ static int mv3110_set_wol(struct phy_device *phydev, return ret; } + if (wol->wolopts & WAKE_PHY) { + /* Enable the link status changed interrupt */ + ret = phy_set_bits_mmd(phydev, MDIO_MMD_PCS, + MV_PCS_INTR_ENABLE, + MV_PCS_INTR_ENABLE_LSC); + if (ret < 0) + return ret; + + /* Clear the interrupt status register */ + ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_INTR_STS); + } else { + /* Disable the link status changed interrupt */ + ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, + MV_PCS_INTR_ENABLE, + MV_PCS_INTR_ENABLE_LSC); + if (ret < 0) + return ret; + } + /* Reset the clear WOL status bit as it does not self-clear */ return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_WOL_CTRL,
Add Wake-on-PHY feature support by enabling the Link Status Changed interrupt. Signed-off-by: Song Yoong Siang <yoong.siang.song@intel.com> --- drivers/net/phy/marvell10g.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-)