diff mbox series

[v2,21/21] ipmi: kcs_bmc_aspeed: Optionally apply status address

Message ID 20210319062752.145730-21-andrew@aj.id.au (mailing list archive)
State New, archived
Headers show
Series ipmi: Allow raw access to KCS devices | expand

Commit Message

Andrew Jeffery March 19, 2021, 6:27 a.m. UTC
Some Aspeed KCS devices can derive the status register address from the
address of the data register. As such, the address of the status
register can be implicit in the configuration if desired. On the other
hand, sometimes address schemes might be requested that are incompatible
with the default addressing scheme. Allow these requests where possible
if the devicetree specifies the status register address.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/char/ipmi/kcs_bmc_aspeed.c | 113 +++++++++++++++++++++--------
 1 file changed, 81 insertions(+), 32 deletions(-)

Comments

Zev Weiss April 1, 2021, 6:18 p.m. UTC | #1
On Fri, Mar 19, 2021 at 01:27:52AM CDT, Andrew Jeffery wrote:
>Some Aspeed KCS devices can derive the status register address from the
>address of the data register. As such, the address of the status
>register can be implicit in the configuration if desired. On the other
>hand, sometimes address schemes might be requested that are incompatible
>with the default addressing scheme. Allow these requests where possible
>if the devicetree specifies the status register address.
>
>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>---
> drivers/char/ipmi/kcs_bmc_aspeed.c | 113 +++++++++++++++++++++--------
> 1 file changed, 81 insertions(+), 32 deletions(-)
>
>diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
>index 7334b1f51dcc..98789b837690 100644
>--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
>+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
>@@ -83,6 +83,8 @@
> #define LPC_STR2             0x040
> #define LPC_STR3             0x044
> #define LPC_HICRB            0x100
>+#define     LPC_HICRB_EN16LADR2      BIT(5)
>+#define     LPC_HICRB_EN16LADR1      BIT(4)
> #define     LPC_HICRB_IBFIE4         BIT(1)
> #define     LPC_HICRB_LPC4E          BIT(0)
> #define LPC_HICRC            0x104
>@@ -96,6 +98,11 @@
> #define LPC_IDR4             0x114
> #define LPC_ODR4             0x118
> #define LPC_STR4             0x11C
>+#define LPC_LSADR12	     0x120
>+#define     LPC_LSADR12_LSADR2_MASK  GENMASK(31, 16)
>+#define     LPC_LSADR12_LSADR2_SHIFT 16
>+#define     LPC_LSADR12_LSADR1_MASK  GENMASK(15, 0)
>+#define     LPC_LSADR12_LSADR1_SHIFT 0
>
> #define OBE_POLL_PERIOD	     (HZ / 2)
>
>@@ -123,7 +130,7 @@ struct aspeed_kcs_bmc {
>
> struct aspeed_kcs_of_ops {
> 	int (*get_channel)(struct platform_device *pdev);
>-	int (*get_io_address)(struct platform_device *pdev);
>+	int (*get_io_address)(struct platform_device *pdev, u32 addrs[2]);
> };
>
> static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct kcs_bmc_device *kcs_bmc)
>@@ -217,38 +224,64 @@ static void aspeed_kcs_updateb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask,
>  *     C. KCS4
>  *        D / C : CA4h / CA5h
>  */
>-static void aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u16 addr)
>+static int aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u32 addrs[2], int nr_addrs)
> {
> 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>
>-	switch (kcs_bmc->channel) {
>+	if (WARN_ON(nr_addrs < 1 || nr_addrs > 2))
>+		return -EINVAL;
>+
>+	switch (priv->kcs_bmc.channel) {
> 	case 1:
>-		regmap_update_bits(priv->map, LPC_HICR4,
>-				LPC_HICR4_LADR12AS, 0);
>-		regmap_write(priv->map, LPC_LADR12H, addr >> 8);
>-		regmap_write(priv->map, LPC_LADR12L, addr & 0xFF);
>+		regmap_update_bits(priv->map, LPC_HICR4, LPC_HICR4_LADR12AS, 0);
>+		regmap_write(priv->map, LPC_LADR12H, addrs[0] >> 8);
>+		regmap_write(priv->map, LPC_LADR12L, addrs[0] & 0xFF);
>+		if (nr_addrs == 2) {
>+			regmap_update_bits(priv->map, LPC_LSADR12, LPC_LSADR12_LSADR1_MASK,
>+					   addrs[1] << LPC_LSADR12_LSADR1_SHIFT);
>+
>+			regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_EN16LADR1,
>+					   LPC_HICRB_EN16LADR1);
>+		}
> 		break;
>
> 	case 2:
>-		regmap_update_bits(priv->map, LPC_HICR4,
>-				LPC_HICR4_LADR12AS, LPC_HICR4_LADR12AS);
>-		regmap_write(priv->map, LPC_LADR12H, addr >> 8);
>-		regmap_write(priv->map, LPC_LADR12L, addr & 0xFF);
>+		regmap_update_bits(priv->map, LPC_HICR4, LPC_HICR4_LADR12AS, LPC_HICR4_LADR12AS);
>+		regmap_write(priv->map, LPC_LADR12H, addrs[0] >> 8);
>+		regmap_write(priv->map, LPC_LADR12L, addrs[0] & 0xFF);
>+		if (nr_addrs == 2) {
>+			regmap_update_bits(priv->map, LPC_LSADR12, LPC_LSADR12_LSADR2_MASK,
>+					   addrs[1] << LPC_LSADR12_LSADR2_SHIFT);
>+
>+			regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_EN16LADR2,
>+					   LPC_HICRB_EN16LADR2);
>+		}
> 		break;
>
> 	case 3:
>-		regmap_write(priv->map, LPC_LADR3H, addr >> 8);
>-		regmap_write(priv->map, LPC_LADR3L, addr & 0xFF);
>+		if (nr_addrs == 2) {
>+			dev_err(priv->kcs_bmc.dev,
>+				"Channel 3 only supports inferred status IO address\n");
>+			return -EINVAL;
>+		}
>+
>+		regmap_write(priv->map, LPC_LADR3H, addrs[0] >> 8);
>+		regmap_write(priv->map, LPC_LADR3L, addrs[0] & 0xFF);
> 		break;
>
> 	case 4:
>-		regmap_write(priv->map, LPC_LADR4, ((addr + 1) << 16) |
>-			addr);
>+		if (nr_addrs == 1)
>+			regmap_write(priv->map, LPC_LADR4, ((addrs[0] + 1) << 16) | addrs[0]);
>+		else
>+			regmap_write(priv->map, LPC_LADR4, (addrs[1] << 16) | addrs[0]);
>+
> 		break;
>
> 	default:
>-		break;
>+		return -EINVAL;
> 	}
>+
>+	return 0;
> }
>
> static inline int aspeed_kcs_map_serirq_type(u32 dt_type)
>@@ -462,18 +495,18 @@ static int aspeed_kcs_of_v1_get_channel(struct platform_device *pdev)
> 	return channel;
> }
>
>-static int aspeed_kcs_of_v1_get_io_address(struct platform_device *pdev)
>+static int
>+aspeed_kcs_of_v1_get_io_address(struct platform_device *pdev, u32 addrs[2])
> {
>-	u32 slave;
> 	int rc;
>
>-	rc = of_property_read_u32(pdev->dev.of_node, "kcs_addr", &slave);
>-	if (rc || slave > 0xffff) {
>+	rc = of_property_read_u32(pdev->dev.of_node, "kcs_addr", addrs);
>+	if (rc || addrs[0] > 0xffff) {
> 		dev_err(&pdev->dev, "no valid 'kcs_addr' configured\n");
> 		return -EINVAL;
> 	}
>
>-	return slave;
>+	return 1;
> }
>
> static int aspeed_kcs_of_v2_get_channel(struct platform_device *pdev)
>@@ -509,16 +542,27 @@ static int aspeed_kcs_of_v2_get_channel(struct platform_device *pdev)
> 	return -EINVAL;
> }
>
>-static int aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev)
>+static int
>+aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev, u32 addrs[2])
> {
>-	uint32_t slave;
> 	int rc;
>
>-	rc = of_property_read_u32(pdev->dev.of_node, "aspeed,lpc-io-reg", &slave);
>-	if (rc || slave > 0xffff)
>+	rc = of_property_read_variable_u32_array(pdev->dev.of_node,
>+						 "aspeed,lpc-io-reg",
>+						 addrs, 1, 2);
>+	if (rc < 0)
>+		return rc;
>+
>+	if (WARN_ON(rc == 0))
>+		return -EINVAL;

Is this check necessary?  It looks like
of_property_read_variable_u32_array() should fail in that case given
sz_min==1, so this seems like it should be impossible to trigger.

>+
>+	if (addrs[0] > 0xffff)
>+		return -EINVAL;
>+
>+	if (rc == 2 && addrs[1] > 0xffff)
> 		return -EINVAL;
>
>-	return slave;
>+	return rc;
> }
>
> static int aspeed_kcs_probe(struct platform_device *pdev)
>@@ -527,9 +571,11 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> 	struct kcs_bmc_device *kcs_bmc;
> 	struct aspeed_kcs_bmc *priv;
> 	struct device_node *np;
>-	int rc, channel, addr;
> 	bool have_upstream_irq;
> 	u32 upstream_irq[2];
>+	int rc, channel;
>+	int nr_addrs;
>+	u32 addrs[2];
>
> 	np = pdev->dev.of_node->parent;
> 	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
>@@ -547,9 +593,9 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> 	if (channel < 0)
> 		return channel;
>
>-	addr = ops->get_io_address(pdev);
>-	if (addr < 0)
>-		return addr;
>+	nr_addrs = ops->get_io_address(pdev, addrs);
>+	if (nr_addrs < 0)
>+		return nr_addrs;
>
> 	np = pdev->dev.of_node;
> 	rc = of_property_read_u32_array(np, "aspeed,lpc-interrupts", upstream_irq, 2);
>@@ -578,7 +624,9 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> 	priv->obe.remove = false;
> 	timer_setup(&priv->obe.timer, aspeed_kcs_check_obe, 0);
>
>-	aspeed_kcs_set_address(kcs_bmc, addr);
>+	rc = aspeed_kcs_set_address(kcs_bmc, addrs, nr_addrs);
>+	if (rc)
>+		return rc;
>
> 	/* Host to BMC IRQ */
> 	rc = aspeed_kcs_config_downstream_irq(kcs_bmc, pdev);
>@@ -600,7 +648,8 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> 	if (rc < 0)
> 		return rc;
>
>-	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n", kcs_bmc->channel, addr);
>+	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n",
>+			kcs_bmc->channel, addrs[0]);
>
> 	return 0;
> }
>-- 
>2.27.0
>
ChiaWei Wang April 6, 2021, 6:09 a.m. UTC | #2
Reviewed-by: Chia-Wei Wang <chiawei_wang@aspeedtech.com>

> -----Original Message-----
> From: Andrew Jeffery <andrew@aj.id.au>
> Sent: Friday, March 19, 2021 2:28 PM
> To: openipmi-developer@lists.sourceforge.net; openbmc@lists.ozlabs.org;
> minyard@acm.org
> Subject: [PATCH v2 21/21] ipmi: kcs_bmc_aspeed: Optionally apply status
> address
> 
> Some Aspeed KCS devices can derive the status register address from the
> address of the data register. As such, the address of the status register can be
> implicit in the configuration if desired. On the other hand, sometimes address
> schemes might be requested that are incompatible with the default addressing
> scheme. Allow these requests where possible if the devicetree specifies the
> status register address.
> 
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> ---
>  drivers/char/ipmi/kcs_bmc_aspeed.c | 113 +++++++++++++++++++++--------
>  1 file changed, 81 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c
> b/drivers/char/ipmi/kcs_bmc_aspeed.c
> index 7334b1f51dcc..98789b837690 100644
> --- a/drivers/char/ipmi/kcs_bmc_aspeed.c
> +++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
> @@ -83,6 +83,8 @@
>  #define LPC_STR2             0x040
>  #define LPC_STR3             0x044
>  #define LPC_HICRB            0x100
> +#define     LPC_HICRB_EN16LADR2      BIT(5)
> +#define     LPC_HICRB_EN16LADR1      BIT(4)
>  #define     LPC_HICRB_IBFIE4         BIT(1)
>  #define     LPC_HICRB_LPC4E          BIT(0)
>  #define LPC_HICRC            0x104
> @@ -96,6 +98,11 @@
>  #define LPC_IDR4             0x114
>  #define LPC_ODR4             0x118
>  #define LPC_STR4             0x11C
> +#define LPC_LSADR12	     0x120
> +#define     LPC_LSADR12_LSADR2_MASK  GENMASK(31, 16)
> +#define     LPC_LSADR12_LSADR2_SHIFT 16
> +#define     LPC_LSADR12_LSADR1_MASK  GENMASK(15, 0)
> +#define     LPC_LSADR12_LSADR1_SHIFT 0
> 
>  #define OBE_POLL_PERIOD	     (HZ / 2)
> 
> @@ -123,7 +130,7 @@ struct aspeed_kcs_bmc {
> 
>  struct aspeed_kcs_of_ops {
>  	int (*get_channel)(struct platform_device *pdev);
> -	int (*get_io_address)(struct platform_device *pdev);
> +	int (*get_io_address)(struct platform_device *pdev, u32 addrs[2]);
>  };
> 
>  static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct
> kcs_bmc_device *kcs_bmc) @@ -217,38 +224,64 @@ static void
> aspeed_kcs_updateb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask,
>   *     C. KCS4
>   *        D / C : CA4h / CA5h
>   */
> -static void aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u16
> addr)
> +static int aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u32
> +addrs[2], int nr_addrs)
>  {
>  	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> 
> -	switch (kcs_bmc->channel) {
> +	if (WARN_ON(nr_addrs < 1 || nr_addrs > 2))
> +		return -EINVAL;
> +
> +	switch (priv->kcs_bmc.channel) {
>  	case 1:
> -		regmap_update_bits(priv->map, LPC_HICR4,
> -				LPC_HICR4_LADR12AS, 0);
> -		regmap_write(priv->map, LPC_LADR12H, addr >> 8);
> -		regmap_write(priv->map, LPC_LADR12L, addr & 0xFF);
> +		regmap_update_bits(priv->map, LPC_HICR4, LPC_HICR4_LADR12AS,
> 0);
> +		regmap_write(priv->map, LPC_LADR12H, addrs[0] >> 8);
> +		regmap_write(priv->map, LPC_LADR12L, addrs[0] & 0xFF);
> +		if (nr_addrs == 2) {
> +			regmap_update_bits(priv->map, LPC_LSADR12,
> LPC_LSADR12_LSADR1_MASK,
> +					   addrs[1] << LPC_LSADR12_LSADR1_SHIFT);
> +
> +			regmap_update_bits(priv->map, LPC_HICRB,
> LPC_HICRB_EN16LADR1,
> +					   LPC_HICRB_EN16LADR1);
> +		}
>  		break;
> 
>  	case 2:
> -		regmap_update_bits(priv->map, LPC_HICR4,
> -				LPC_HICR4_LADR12AS, LPC_HICR4_LADR12AS);
> -		regmap_write(priv->map, LPC_LADR12H, addr >> 8);
> -		regmap_write(priv->map, LPC_LADR12L, addr & 0xFF);
> +		regmap_update_bits(priv->map, LPC_HICR4, LPC_HICR4_LADR12AS,
> LPC_HICR4_LADR12AS);
> +		regmap_write(priv->map, LPC_LADR12H, addrs[0] >> 8);
> +		regmap_write(priv->map, LPC_LADR12L, addrs[0] & 0xFF);
> +		if (nr_addrs == 2) {
> +			regmap_update_bits(priv->map, LPC_LSADR12,
> LPC_LSADR12_LSADR2_MASK,
> +					   addrs[1] << LPC_LSADR12_LSADR2_SHIFT);
> +
> +			regmap_update_bits(priv->map, LPC_HICRB,
> LPC_HICRB_EN16LADR2,
> +					   LPC_HICRB_EN16LADR2);
> +		}
>  		break;
> 
>  	case 3:
> -		regmap_write(priv->map, LPC_LADR3H, addr >> 8);
> -		regmap_write(priv->map, LPC_LADR3L, addr & 0xFF);
> +		if (nr_addrs == 2) {
> +			dev_err(priv->kcs_bmc.dev,
> +				"Channel 3 only supports inferred status IO address\n");
> +			return -EINVAL;
> +		}
> +
> +		regmap_write(priv->map, LPC_LADR3H, addrs[0] >> 8);
> +		regmap_write(priv->map, LPC_LADR3L, addrs[0] & 0xFF);
>  		break;
> 
>  	case 4:
> -		regmap_write(priv->map, LPC_LADR4, ((addr + 1) << 16) |
> -			addr);
> +		if (nr_addrs == 1)
> +			regmap_write(priv->map, LPC_LADR4, ((addrs[0] + 1) << 16) |
> addrs[0]);
> +		else
> +			regmap_write(priv->map, LPC_LADR4, (addrs[1] << 16) |
> addrs[0]);
> +
>  		break;
> 
>  	default:
> -		break;
> +		return -EINVAL;
>  	}
> +
> +	return 0;
>  }
> 
>  static inline int aspeed_kcs_map_serirq_type(u32 dt_type) @@ -462,18
> +495,18 @@ static int aspeed_kcs_of_v1_get_channel(struct platform_device
> *pdev)
>  	return channel;
>  }
> 
> -static int aspeed_kcs_of_v1_get_io_address(struct platform_device *pdev)
> +static int
> +aspeed_kcs_of_v1_get_io_address(struct platform_device *pdev, u32
> +addrs[2])
>  {
> -	u32 slave;
>  	int rc;
> 
> -	rc = of_property_read_u32(pdev->dev.of_node, "kcs_addr", &slave);
> -	if (rc || slave > 0xffff) {
> +	rc = of_property_read_u32(pdev->dev.of_node, "kcs_addr", addrs);
> +	if (rc || addrs[0] > 0xffff) {
>  		dev_err(&pdev->dev, "no valid 'kcs_addr' configured\n");
>  		return -EINVAL;
>  	}
> 
> -	return slave;
> +	return 1;
>  }
> 
>  static int aspeed_kcs_of_v2_get_channel(struct platform_device *pdev) @@
> -509,16 +542,27 @@ static int aspeed_kcs_of_v2_get_channel(struct
> platform_device *pdev)
>  	return -EINVAL;
>  }
> 
> -static int aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev)
> +static int
> +aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev, u32
> +addrs[2])
>  {
> -	uint32_t slave;
>  	int rc;
> 
> -	rc = of_property_read_u32(pdev->dev.of_node, "aspeed,lpc-io-reg",
> &slave);
> -	if (rc || slave > 0xffff)
> +	rc = of_property_read_variable_u32_array(pdev->dev.of_node,
> +						 "aspeed,lpc-io-reg",
> +						 addrs, 1, 2);
> +	if (rc < 0)
> +		return rc;
> +
> +	if (WARN_ON(rc == 0))
> +		return -EINVAL;
> +
> +	if (addrs[0] > 0xffff)
> +		return -EINVAL;
> +
> +	if (rc == 2 && addrs[1] > 0xffff)
>  		return -EINVAL;
> 
> -	return slave;
> +	return rc;
>  }
> 
>  static int aspeed_kcs_probe(struct platform_device *pdev) @@ -527,9
> +571,11 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
>  	struct kcs_bmc_device *kcs_bmc;
>  	struct aspeed_kcs_bmc *priv;
>  	struct device_node *np;
> -	int rc, channel, addr;
>  	bool have_upstream_irq;
>  	u32 upstream_irq[2];
> +	int rc, channel;
> +	int nr_addrs;
> +	u32 addrs[2];
> 
>  	np = pdev->dev.of_node->parent;
>  	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") && @@ -547,9
> +593,9 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
>  	if (channel < 0)
>  		return channel;
> 
> -	addr = ops->get_io_address(pdev);
> -	if (addr < 0)
> -		return addr;
> +	nr_addrs = ops->get_io_address(pdev, addrs);
> +	if (nr_addrs < 0)
> +		return nr_addrs;
> 
>  	np = pdev->dev.of_node;
>  	rc = of_property_read_u32_array(np, "aspeed,lpc-interrupts",
> upstream_irq, 2); @@ -578,7 +624,9 @@ static int aspeed_kcs_probe(struct
> platform_device *pdev)
>  	priv->obe.remove = false;
>  	timer_setup(&priv->obe.timer, aspeed_kcs_check_obe, 0);
> 
> -	aspeed_kcs_set_address(kcs_bmc, addr);
> +	rc = aspeed_kcs_set_address(kcs_bmc, addrs, nr_addrs);
> +	if (rc)
> +		return rc;
> 
>  	/* Host to BMC IRQ */
>  	rc = aspeed_kcs_config_downstream_irq(kcs_bmc, pdev); @@ -600,7
> +648,8 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
>  	if (rc < 0)
>  		return rc;
> 
> -	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n",
> kcs_bmc->channel, addr);
> +	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n",
> +			kcs_bmc->channel, addrs[0]);
> 
>  	return 0;
>  }
> --
> 2.27.0
diff mbox series

Patch

diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
index 7334b1f51dcc..98789b837690 100644
--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -83,6 +83,8 @@ 
 #define LPC_STR2             0x040
 #define LPC_STR3             0x044
 #define LPC_HICRB            0x100
+#define     LPC_HICRB_EN16LADR2      BIT(5)
+#define     LPC_HICRB_EN16LADR1      BIT(4)
 #define     LPC_HICRB_IBFIE4         BIT(1)
 #define     LPC_HICRB_LPC4E          BIT(0)
 #define LPC_HICRC            0x104
@@ -96,6 +98,11 @@ 
 #define LPC_IDR4             0x114
 #define LPC_ODR4             0x118
 #define LPC_STR4             0x11C
+#define LPC_LSADR12	     0x120
+#define     LPC_LSADR12_LSADR2_MASK  GENMASK(31, 16)
+#define     LPC_LSADR12_LSADR2_SHIFT 16
+#define     LPC_LSADR12_LSADR1_MASK  GENMASK(15, 0)
+#define     LPC_LSADR12_LSADR1_SHIFT 0
 
 #define OBE_POLL_PERIOD	     (HZ / 2)
 
@@ -123,7 +130,7 @@  struct aspeed_kcs_bmc {
 
 struct aspeed_kcs_of_ops {
 	int (*get_channel)(struct platform_device *pdev);
-	int (*get_io_address)(struct platform_device *pdev);
+	int (*get_io_address)(struct platform_device *pdev, u32 addrs[2]);
 };
 
 static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct kcs_bmc_device *kcs_bmc)
@@ -217,38 +224,64 @@  static void aspeed_kcs_updateb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask,
  *     C. KCS4
  *        D / C : CA4h / CA5h
  */
-static void aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u16 addr)
+static int aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u32 addrs[2], int nr_addrs)
 {
 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
 
-	switch (kcs_bmc->channel) {
+	if (WARN_ON(nr_addrs < 1 || nr_addrs > 2))
+		return -EINVAL;
+
+	switch (priv->kcs_bmc.channel) {
 	case 1:
-		regmap_update_bits(priv->map, LPC_HICR4,
-				LPC_HICR4_LADR12AS, 0);
-		regmap_write(priv->map, LPC_LADR12H, addr >> 8);
-		regmap_write(priv->map, LPC_LADR12L, addr & 0xFF);
+		regmap_update_bits(priv->map, LPC_HICR4, LPC_HICR4_LADR12AS, 0);
+		regmap_write(priv->map, LPC_LADR12H, addrs[0] >> 8);
+		regmap_write(priv->map, LPC_LADR12L, addrs[0] & 0xFF);
+		if (nr_addrs == 2) {
+			regmap_update_bits(priv->map, LPC_LSADR12, LPC_LSADR12_LSADR1_MASK,
+					   addrs[1] << LPC_LSADR12_LSADR1_SHIFT);
+
+			regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_EN16LADR1,
+					   LPC_HICRB_EN16LADR1);
+		}
 		break;
 
 	case 2:
-		regmap_update_bits(priv->map, LPC_HICR4,
-				LPC_HICR4_LADR12AS, LPC_HICR4_LADR12AS);
-		regmap_write(priv->map, LPC_LADR12H, addr >> 8);
-		regmap_write(priv->map, LPC_LADR12L, addr & 0xFF);
+		regmap_update_bits(priv->map, LPC_HICR4, LPC_HICR4_LADR12AS, LPC_HICR4_LADR12AS);
+		regmap_write(priv->map, LPC_LADR12H, addrs[0] >> 8);
+		regmap_write(priv->map, LPC_LADR12L, addrs[0] & 0xFF);
+		if (nr_addrs == 2) {
+			regmap_update_bits(priv->map, LPC_LSADR12, LPC_LSADR12_LSADR2_MASK,
+					   addrs[1] << LPC_LSADR12_LSADR2_SHIFT);
+
+			regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_EN16LADR2,
+					   LPC_HICRB_EN16LADR2);
+		}
 		break;
 
 	case 3:
-		regmap_write(priv->map, LPC_LADR3H, addr >> 8);
-		regmap_write(priv->map, LPC_LADR3L, addr & 0xFF);
+		if (nr_addrs == 2) {
+			dev_err(priv->kcs_bmc.dev,
+				"Channel 3 only supports inferred status IO address\n");
+			return -EINVAL;
+		}
+
+		regmap_write(priv->map, LPC_LADR3H, addrs[0] >> 8);
+		regmap_write(priv->map, LPC_LADR3L, addrs[0] & 0xFF);
 		break;
 
 	case 4:
-		regmap_write(priv->map, LPC_LADR4, ((addr + 1) << 16) |
-			addr);
+		if (nr_addrs == 1)
+			regmap_write(priv->map, LPC_LADR4, ((addrs[0] + 1) << 16) | addrs[0]);
+		else
+			regmap_write(priv->map, LPC_LADR4, (addrs[1] << 16) | addrs[0]);
+
 		break;
 
 	default:
-		break;
+		return -EINVAL;
 	}
+
+	return 0;
 }
 
 static inline int aspeed_kcs_map_serirq_type(u32 dt_type)
@@ -462,18 +495,18 @@  static int aspeed_kcs_of_v1_get_channel(struct platform_device *pdev)
 	return channel;
 }
 
-static int aspeed_kcs_of_v1_get_io_address(struct platform_device *pdev)
+static int
+aspeed_kcs_of_v1_get_io_address(struct platform_device *pdev, u32 addrs[2])
 {
-	u32 slave;
 	int rc;
 
-	rc = of_property_read_u32(pdev->dev.of_node, "kcs_addr", &slave);
-	if (rc || slave > 0xffff) {
+	rc = of_property_read_u32(pdev->dev.of_node, "kcs_addr", addrs);
+	if (rc || addrs[0] > 0xffff) {
 		dev_err(&pdev->dev, "no valid 'kcs_addr' configured\n");
 		return -EINVAL;
 	}
 
-	return slave;
+	return 1;
 }
 
 static int aspeed_kcs_of_v2_get_channel(struct platform_device *pdev)
@@ -509,16 +542,27 @@  static int aspeed_kcs_of_v2_get_channel(struct platform_device *pdev)
 	return -EINVAL;
 }
 
-static int aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev)
+static int
+aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev, u32 addrs[2])
 {
-	uint32_t slave;
 	int rc;
 
-	rc = of_property_read_u32(pdev->dev.of_node, "aspeed,lpc-io-reg", &slave);
-	if (rc || slave > 0xffff)
+	rc = of_property_read_variable_u32_array(pdev->dev.of_node,
+						 "aspeed,lpc-io-reg",
+						 addrs, 1, 2);
+	if (rc < 0)
+		return rc;
+
+	if (WARN_ON(rc == 0))
+		return -EINVAL;
+
+	if (addrs[0] > 0xffff)
+		return -EINVAL;
+
+	if (rc == 2 && addrs[1] > 0xffff)
 		return -EINVAL;
 
-	return slave;
+	return rc;
 }
 
 static int aspeed_kcs_probe(struct platform_device *pdev)
@@ -527,9 +571,11 @@  static int aspeed_kcs_probe(struct platform_device *pdev)
 	struct kcs_bmc_device *kcs_bmc;
 	struct aspeed_kcs_bmc *priv;
 	struct device_node *np;
-	int rc, channel, addr;
 	bool have_upstream_irq;
 	u32 upstream_irq[2];
+	int rc, channel;
+	int nr_addrs;
+	u32 addrs[2];
 
 	np = pdev->dev.of_node->parent;
 	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
@@ -547,9 +593,9 @@  static int aspeed_kcs_probe(struct platform_device *pdev)
 	if (channel < 0)
 		return channel;
 
-	addr = ops->get_io_address(pdev);
-	if (addr < 0)
-		return addr;
+	nr_addrs = ops->get_io_address(pdev, addrs);
+	if (nr_addrs < 0)
+		return nr_addrs;
 
 	np = pdev->dev.of_node;
 	rc = of_property_read_u32_array(np, "aspeed,lpc-interrupts", upstream_irq, 2);
@@ -578,7 +624,9 @@  static int aspeed_kcs_probe(struct platform_device *pdev)
 	priv->obe.remove = false;
 	timer_setup(&priv->obe.timer, aspeed_kcs_check_obe, 0);
 
-	aspeed_kcs_set_address(kcs_bmc, addr);
+	rc = aspeed_kcs_set_address(kcs_bmc, addrs, nr_addrs);
+	if (rc)
+		return rc;
 
 	/* Host to BMC IRQ */
 	rc = aspeed_kcs_config_downstream_irq(kcs_bmc, pdev);
@@ -600,7 +648,8 @@  static int aspeed_kcs_probe(struct platform_device *pdev)
 	if (rc < 0)
 		return rc;
 
-	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n", kcs_bmc->channel, addr);
+	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n",
+			kcs_bmc->channel, addrs[0]);
 
 	return 0;
 }