diff mbox series

[v5,5/8] i2c: amd-asf: Add i2c_algorithm operations to support AMD ASF with SMBus

Message ID 20240913121110.1611340-6-Shyam-sundar.S-k@amd.com
State Superseded
Headers show
Series Introduce initial AMD ASF Controller driver support | expand

Commit Message

Shyam Sundar S K Sept. 13, 2024, 12:11 p.m. UTC
Implement the i2c_algorithm operations to enable support for AMD ASF
(Alert Standard Format) with SMBus. This enhancement includes:

- Adding functionality to identify and select the supported ASF functions.
- Implementing mechanisms for registering and deregistering I2C slave
  devices.
- Providing support for data transfer operations over ASF.

Additionally, include a 'select' Kconfig entry as the current patch
utilizes reg_slave and unreg_slave callbacks, which are controlled by
IS_ENABLED(CONFIG_I2C_SLAVE).

Co-developed-by: Sanket Goswami <Sanket.Goswami@amd.com>
Signed-off-by: Sanket Goswami <Sanket.Goswami@amd.com>
Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 drivers/i2c/busses/Kconfig            |   1 +
 drivers/i2c/busses/i2c-amd-asf-plat.c | 185 ++++++++++++++++++++++++++
 2 files changed, 186 insertions(+)

Comments

Shyam Sundar S K Sept. 17, 2024, 6:17 p.m. UTC | #1
On 9/14/2024 00:38, Andy Shevchenko wrote:
> On Fri, Sep 13, 2024 at 05:41:07PM +0530, Shyam Sundar S K wrote:
>> Implement the i2c_algorithm operations to enable support for AMD ASF
>> (Alert Standard Format) with SMBus. This enhancement includes:
>>
>> - Adding functionality to identify and select the supported ASF functions.
>> - Implementing mechanisms for registering and deregistering I2C slave
>>   devices.
>> - Providing support for data transfer operations over ASF.
>>
>> Additionally, include a 'select' Kconfig entry as the current patch
>> utilizes reg_slave and unreg_slave callbacks, which are controlled by
>> IS_ENABLED(CONFIG_I2C_SLAVE).
> 
> ...
> 
>> +/* ASF address offsets */
>> +#define ASFLISADDR	(9 + piix4_smba)
>> +#define ASFSTA		(0xA + piix4_smba)
>> +#define ASFSLVSTA	(0xD + piix4_smba)
>> +#define ASFDATABNKSEL	(0x13 + piix4_smba)
>> +#define ASFSLVEN	(0x15 + piix4_smba)
> 
> 0x09
> 0x0A
> 0x0D
> 
> ...
> 
>> +static void amd_asf_update_bits(unsigned short piix4_smba, u8 bit,
>> +				unsigned long offset, bool set)
>> +{
>> +	unsigned long reg;
>> +
>> +	reg = inb_p(offset);
>> +	if (set)
>> +		set_bit(bit, &reg);
>> +	else
>> +		clear_bit(bit, &reg);
> 
> + bitops.h
> 
> The above is home made assign_bit().
> Moreover, why atomic version? Wouldn't __assign_bit() suffice?
> 

thanks! __assign_bit() would suffice.

>> +	outb_p(reg, offset);
>> +}
> 
> ...
> 
>> +static void amd_asf_update_bytes(struct amd_asf_dev *dev, u8 bit, bool set)
> 
> I didn't get the naming, the above using IO port with _bits, and this is MMIO
> with _bytes. Are you sure the naming schema is correct?
> 

Thinking to merge both the functions into one, something like this:

enum io_type {
    IO_PORT,
    MMIO
};

static void amd_asf_update_target(struct amd_asf_dev *dev, enum
io_type type, u8 bit, bool set)
{

...

}

>> +{
>> +	unsigned long reg;
>> +
>> +	reg = ioread32(dev->mmio_cfg.addr);
>> +	if (set)
>> +		set_bit(bit, &reg);
>> +	else
>> +		clear_bit(bit, &reg);
>> +	iowrite32(reg, dev->mmio_cfg.addr);
> 
> Ditto (bitops and related things).
> 
>> +}
> 
> ...
> 
>> +static int amd_asf_reg_target(struct i2c_client *target)
>> +{
>> +	struct amd_asf_dev *dev = i2c_get_adapdata(target->adapter);
>> +	unsigned short piix4_smba = dev->port_addr->start;
>> +	int ret;
>> +	u8 reg;
>> +
>> +	if (dev->target)
>> +		return -EBUSY;
>> +
>> +	ret = piix4_sb800_region_request(&target->dev, &dev->mmio_cfg);
>> +	if (ret)
>> +		return ret;
>> +
>> +	reg = (target->addr << 1) | BIT(0);
> 
> Is BIT(0) == I2C_M_RD in this case? If so, use the latter defined constant.
> 
>> +	outb_p(reg, ASFLISADDR);
>> +
>> +	amd_asf_setup_target(dev);
>> +	dev->target = target;
>> +	amd_asf_update_bits(piix4_smba, ASF_DATA_EN, ASFDATABNKSEL, false);
>> +	piix4_sb800_region_release(&target->dev, &dev->mmio_cfg);
>> +
>> +	return 0;
>> +}
>
Andy Shevchenko Sept. 18, 2024, 9:58 a.m. UTC | #2
On Tue, Sep 17, 2024 at 11:47:00PM +0530, Shyam Sundar S K wrote:
> On 9/14/2024 00:38, Andy Shevchenko wrote:
> > On Fri, Sep 13, 2024 at 05:41:07PM +0530, Shyam Sundar S K wrote:

...

> >> +static void amd_asf_update_bytes(struct amd_asf_dev *dev, u8 bit, bool set)
> > 
> > I didn't get the naming, the above using IO port with _bits, and this is MMIO
> > with _bytes. Are you sure the naming schema is correct?
> 
> Thinking to merge both the functions into one, something like this:
> 
> enum io_type {
>     IO_PORT,
>     MMIO
> };
> 
> static void amd_asf_update_target(struct amd_asf_dev *dev, enum
> io_type type, u8 bit, bool set)
> {
> 
> ...
> 
> }

I'm not talking about merging them (and merged variant seems less readable
to me), but about naming. I.o.w. it's not obvious what the difference _bits vs.
_bytes.

> >> +{
> >> +	unsigned long reg;
> >> +
> >> +	reg = ioread32(dev->mmio_cfg.addr);
> >> +	if (set)
> >> +		set_bit(bit, &reg);
> >> +	else
> >> +		clear_bit(bit, &reg);
> >> +	iowrite32(reg, dev->mmio_cfg.addr);
> > 
> > Ditto (bitops and related things).
> > 
> >> +}
Shyam Sundar S K Sept. 18, 2024, 10:24 a.m. UTC | #3
On 9/18/2024 15:28, Andy Shevchenko wrote:
> On Tue, Sep 17, 2024 at 11:47:00PM +0530, Shyam Sundar S K wrote:
>> On 9/14/2024 00:38, Andy Shevchenko wrote:
>>> On Fri, Sep 13, 2024 at 05:41:07PM +0530, Shyam Sundar S K wrote:
> 
> ...
> 
>>>> +static void amd_asf_update_bytes(struct amd_asf_dev *dev, u8 bit, bool set)
>>>
>>> I didn't get the naming, the above using IO port with _bits, and this is MMIO
>>> with _bytes. Are you sure the naming schema is correct?
>>
>> Thinking to merge both the functions into one, something like this:
>>
>> enum io_type {
>>     IO_PORT,
>>     MMIO
>> };
>>
>> static void amd_asf_update_target(struct amd_asf_dev *dev, enum
>> io_type type, u8 bit, bool set)
>> {
>>
>> ...
>>
>> }
> 
> I'm not talking about merging them (and merged variant seems less readable
> to me), but about naming. I.o.w. it's not obvious what the difference _bits vs.
> _bytes.

Alright. I will stop merging them. I'll rename them as follows:


/* updates target using memory-mapped I/O */
amd_asf_update_mmio_target(...)

/* updates target using I/O port access */
amd_asf_update_ioport_target(...)

Thanks,
Shyam

> 
>>>> +{
>>>> +	unsigned long reg;
>>>> +
>>>> +	reg = ioread32(dev->mmio_cfg.addr);
>>>> +	if (set)
>>>> +		set_bit(bit, &reg);
>>>> +	else
>>>> +		clear_bit(bit, &reg);
>>>> +	iowrite32(reg, dev->mmio_cfg.addr);
>>>
>>> Ditto (bitops and related things).
>>>
>>>> +}
>
diff mbox series

Patch

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 03afcdbff209..9353946882db 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -98,6 +98,7 @@  config I2C_AMD_MP2
 config I2C_AMD_ASF
 	tristate "AMD ASF I2C Controller Support"
 	depends on I2C_PIIX4
+	select I2C_SLAVE
 	help
 	  This option enables support for the AMD ASF (Alert Standard Format)
 	  I2C controller. The AMD ASF controller is an SMBus controller with
diff --git a/drivers/i2c/busses/i2c-amd-asf-plat.c b/drivers/i2c/busses/i2c-amd-asf-plat.c
index 3d80805a7a3e..aa85e10a3927 100644
--- a/drivers/i2c/busses/i2c-amd-asf-plat.c
+++ b/drivers/i2c/busses/i2c-amd-asf-plat.c
@@ -19,15 +19,199 @@ 
 #include <linux/sprintf.h>
 #include "i2c-piix4.h"
 
+/* ASF register bits */
+#define ASF_SLV_LISTN	0
+#define ASF_SLV_INTR	1
+#define ASF_SLV_RST	4
+#define ASF_PEC_SP	5
+#define ASF_DATA_EN	7
+#define ASF_MSTR_EN	16
+#define ASF_CLK_EN	17
+
+/* ASF address offsets */
+#define ASFLISADDR	(9 + piix4_smba)
+#define ASFSTA		(0xA + piix4_smba)
+#define ASFSLVSTA	(0xD + piix4_smba)
+#define ASFDATABNKSEL	(0x13 + piix4_smba)
+#define ASFSLVEN	(0x15 + piix4_smba)
+
+#define ASF_BLOCK_MAX_BYTES		72
+
 static const char *amd_asf_port_name = " port 1";
 
 struct amd_asf_dev {
 	struct i2c_adapter adap;
 	struct device *dev;
+	struct i2c_client *target;
 	struct sb800_mmio_cfg mmio_cfg;
 	struct resource *port_addr;
 };
 
+static void amd_asf_update_bits(unsigned short piix4_smba, u8 bit,
+				unsigned long offset, bool set)
+{
+	unsigned long reg;
+
+	reg = inb_p(offset);
+	if (set)
+		set_bit(bit, &reg);
+	else
+		clear_bit(bit, &reg);
+	outb_p(reg, offset);
+}
+
+static void amd_asf_update_bytes(struct amd_asf_dev *dev, u8 bit, bool set)
+{
+	unsigned long reg;
+
+	reg = ioread32(dev->mmio_cfg.addr);
+	if (set)
+		set_bit(bit, &reg);
+	else
+		clear_bit(bit, &reg);
+	iowrite32(reg, dev->mmio_cfg.addr);
+}
+
+static void amd_asf_setup_target(struct amd_asf_dev *dev)
+{
+	unsigned short piix4_smba = dev->port_addr->start;
+
+	/* Reset both host and target before setting up */
+	outb_p(0, SMBHSTSTS);
+	outb_p(0, ASFSLVSTA);
+	outb_p(0, ASFSTA);
+
+	/* Update target address */
+	amd_asf_update_bits(piix4_smba, ASF_SLV_LISTN, ASFLISADDR, true);
+	/* Enable target and set the clock */
+	amd_asf_update_bytes(dev, ASF_MSTR_EN, false);
+	amd_asf_update_bytes(dev, ASF_CLK_EN, true);
+	/* Enable target interrupt */
+	amd_asf_update_bits(piix4_smba, ASF_SLV_INTR, ASFSLVEN, true);
+	amd_asf_update_bits(piix4_smba, ASF_SLV_RST, ASFSLVEN, false);
+	/* Enable PEC and PEC append */
+	amd_asf_update_bits(piix4_smba, ASF_DATA_EN, SMBHSTCNT, true);
+	amd_asf_update_bits(piix4_smba, ASF_PEC_SP, SMBHSTCNT, true);
+}
+
+static s32 amd_asf_access(struct i2c_adapter *adap, u16 addr, u8 command, u8 *data)
+{
+	struct amd_asf_dev *dev = i2c_get_adapdata(adap);
+	unsigned short piix4_smba = dev->port_addr->start;
+	u8 i, len;
+
+	outb_p((addr << 1), SMBHSTADD);
+	outb_p(command, SMBHSTCMD);
+	len = data[0];
+	if (len == 0 || len > ASF_BLOCK_MAX_BYTES)
+		return -EINVAL;
+
+	outb_p(len, SMBHSTDAT0);
+	/* Reset SMBBLKDAT */
+	inb_p(SMBHSTCNT);
+	for (i = 1; i <= len; i++)
+		outb_p(data[i], SMBBLKDAT);
+
+	outb_p(PIIX4_BLOCK_DATA, SMBHSTCNT);
+	/* Enable PEC and PEC append */
+	amd_asf_update_bits(piix4_smba, ASF_DATA_EN, SMBHSTCNT, true);
+	amd_asf_update_bits(piix4_smba, ASF_PEC_SP, SMBHSTCNT, true);
+
+	return piix4_transaction(adap, piix4_smba);
+}
+
+static int amd_asf_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+	struct amd_asf_dev *dev = i2c_get_adapdata(adap);
+	unsigned short piix4_smba = dev->port_addr->start;
+	u8 asf_data[ASF_BLOCK_MAX_BYTES];
+	struct i2c_msg *dev_msgs = msgs;
+	u8 prev_port;
+	int ret;
+
+	if (msgs->flags & I2C_M_RD) {
+		dev_err(&adap->dev, "ASF: Read not supported\n");
+		return -EOPNOTSUPP;
+	}
+
+	/* Exclude the receive header and PEC */
+	if (msgs->len > ASF_BLOCK_MAX_BYTES - 3) {
+		dev_err(&adap->dev, "ASF: max message length exceeded\n");
+		return -EOPNOTSUPP;
+	}
+
+	asf_data[0] = dev_msgs->len;
+	memcpy(asf_data + 1, dev_msgs[0].buf, dev_msgs->len);
+
+	ret = piix4_sb800_region_request(&adap->dev, &dev->mmio_cfg);
+	if (ret)
+		return ret;
+
+	amd_asf_update_bits(piix4_smba, ASF_SLV_RST, ASFSLVEN, true);
+	amd_asf_update_bits(piix4_smba, ASF_SLV_LISTN, ASFLISADDR, false);
+	/* Clear ASF target status */
+	outb_p(0, ASFSLVSTA);
+
+	/* Enable ASF SMBus controller function */
+	amd_asf_update_bytes(dev, ASF_MSTR_EN, true);
+	prev_port = piix4_sb800_port_sel(0, &dev->mmio_cfg);
+	ret = amd_asf_access(adap, msgs->addr, msgs[0].buf[0], asf_data);
+	piix4_sb800_port_sel(prev_port, &dev->mmio_cfg);
+	amd_asf_setup_target(dev);
+	piix4_sb800_region_release(&adap->dev, &dev->mmio_cfg);
+	return ret;
+}
+
+static int amd_asf_reg_target(struct i2c_client *target)
+{
+	struct amd_asf_dev *dev = i2c_get_adapdata(target->adapter);
+	unsigned short piix4_smba = dev->port_addr->start;
+	int ret;
+	u8 reg;
+
+	if (dev->target)
+		return -EBUSY;
+
+	ret = piix4_sb800_region_request(&target->dev, &dev->mmio_cfg);
+	if (ret)
+		return ret;
+
+	reg = (target->addr << 1) | BIT(0);
+	outb_p(reg, ASFLISADDR);
+
+	amd_asf_setup_target(dev);
+	dev->target = target;
+	amd_asf_update_bits(piix4_smba, ASF_DATA_EN, ASFDATABNKSEL, false);
+	piix4_sb800_region_release(&target->dev, &dev->mmio_cfg);
+
+	return 0;
+}
+
+static int amd_asf_unreg_target(struct i2c_client *target)
+{
+	struct amd_asf_dev *dev = i2c_get_adapdata(target->adapter);
+	unsigned short piix4_smba = dev->port_addr->start;
+
+	amd_asf_update_bits(piix4_smba, ASF_SLV_INTR, ASFSLVEN, false);
+	amd_asf_update_bits(piix4_smba, ASF_SLV_RST, ASFSLVEN, true);
+	dev->target = NULL;
+
+	return 0;
+}
+
+static u32 amd_asf_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BYTE |
+		I2C_FUNC_SLAVE | I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | I2C_FUNC_SMBUS_PEC;
+}
+
+static const struct i2c_algorithm amd_asf_smbus_algorithm = {
+	.master_xfer = amd_asf_xfer,
+	.reg_target = amd_asf_reg_target,
+	.unreg_target = amd_asf_unreg_target,
+	.functionality = amd_asf_func,
+};
+
 static int amd_asf_probe(struct platform_device *pdev)
 {
 	struct amd_asf_dev *asf_dev;
@@ -45,6 +229,7 @@  static int amd_asf_probe(struct platform_device *pdev)
 		return dev_err_probe(&pdev->dev, -EINVAL, "missing IO resources\n");
 
 	asf_dev->adap.owner = THIS_MODULE;
+	asf_dev->adap.algo = &amd_asf_smbus_algorithm;
 	asf_dev->adap.dev.parent = &pdev->dev;
 
 	i2c_set_adapdata(&asf_dev->adap, asf_dev);