Skip to content

Commit 9b25419

Browse files
Shyam Sundar S KAndi Shyti
authored andcommitted
i2c: amd-asf: Add routine to handle the ASF slave process
Add support for handling ASF slave process events as described in the AMD ASF databook. This involves implementing the correct programming sequence to manage each ASF packet appropriately. Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> 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> Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
1 parent 78a78b3 commit 9b25419

1 file changed

Lines changed: 101 additions & 0 deletions

File tree

drivers/i2c/busses/i2c-amd-asf-plat.c

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <linux/bitops.h>
1313
#include <linux/device.h>
14+
#include <linux/devm-helpers.h>
1415
#include <linux/errno.h>
1516
#include <linux/gfp_types.h>
1617
#include <linux/i2c.h>
@@ -33,21 +34,90 @@
3334
#define ASF_CLK_EN 17
3435

3536
/* ASF address offsets */
37+
#define ASFINDEX (0x07 + piix4_smba)
3638
#define ASFLISADDR (0x09 + piix4_smba)
3739
#define ASFSTA (0x0A + piix4_smba)
3840
#define ASFSLVSTA (0x0D + piix4_smba)
41+
#define ASFDATARWPTR (0x11 + piix4_smba)
42+
#define ASFSETDATARDPTR (0x12 + piix4_smba)
3943
#define ASFDATABNKSEL (0x13 + piix4_smba)
4044
#define ASFSLVEN (0x15 + piix4_smba)
4145

4246
#define ASF_BLOCK_MAX_BYTES 72
47+
#define ASF_ERROR_STATUS GENMASK(3, 1)
4348

4449
struct amd_asf_dev {
4550
struct i2c_adapter adap;
4651
struct i2c_client *target;
52+
struct delayed_work work_buf;
4753
struct sb800_mmio_cfg mmio_cfg;
4854
struct resource *port_addr;
4955
};
5056

57+
static void amd_asf_process_target(struct work_struct *work)
58+
{
59+
struct amd_asf_dev *dev = container_of(work, struct amd_asf_dev, work_buf.work);
60+
unsigned short piix4_smba = dev->port_addr->start;
61+
u8 data[ASF_BLOCK_MAX_BYTES];
62+
u8 bank, reg, cmd;
63+
u8 len, idx, val;
64+
65+
/* Read target status register */
66+
reg = inb_p(ASFSLVSTA);
67+
68+
/* Check if no error bits are set in target status register */
69+
if (reg & ASF_ERROR_STATUS) {
70+
/* Set bank as full */
71+
cmd = 0;
72+
reg |= GENMASK(3, 2);
73+
outb_p(reg, ASFDATABNKSEL);
74+
} else {
75+
/* Read data bank */
76+
reg = inb_p(ASFDATABNKSEL);
77+
bank = (reg & BIT(3)) ? 1 : 0;
78+
79+
/* Set read data bank */
80+
if (bank) {
81+
reg |= BIT(4);
82+
reg &= ~BIT(3);
83+
} else {
84+
reg &= ~BIT(4);
85+
reg &= ~BIT(2);
86+
}
87+
88+
/* Read command register */
89+
outb_p(reg, ASFDATABNKSEL);
90+
cmd = inb_p(ASFINDEX);
91+
len = inb_p(ASFDATARWPTR);
92+
for (idx = 0; idx < len; idx++)
93+
data[idx] = inb_p(ASFINDEX);
94+
95+
/* Clear data bank status */
96+
if (bank) {
97+
reg |= BIT(3);
98+
outb_p(reg, ASFDATABNKSEL);
99+
} else {
100+
reg |= BIT(2);
101+
outb_p(reg, ASFDATABNKSEL);
102+
}
103+
}
104+
105+
outb_p(0, ASFSETDATARDPTR);
106+
if (cmd & BIT(0))
107+
return;
108+
109+
/*
110+
* Although i2c_slave_event() returns an appropriate error code, we
111+
* don't check it here because we're operating in the workqueue context.
112+
*/
113+
i2c_slave_event(dev->target, I2C_SLAVE_WRITE_REQUESTED, &val);
114+
for (idx = 0; idx < len; idx++) {
115+
val = data[idx];
116+
i2c_slave_event(dev->target, I2C_SLAVE_WRITE_RECEIVED, &val);
117+
}
118+
i2c_slave_event(dev->target, I2C_SLAVE_STOP, &val);
119+
}
120+
51121
static void amd_asf_update_ioport_target(unsigned short piix4_smba, u8 bit,
52122
unsigned long offset, bool set)
53123
{
@@ -207,10 +277,29 @@ static const struct i2c_algorithm amd_asf_smbus_algorithm = {
207277
.functionality = amd_asf_func,
208278
};
209279

280+
static irqreturn_t amd_asf_irq_handler(int irq, void *ptr)
281+
{
282+
struct amd_asf_dev *dev = ptr;
283+
unsigned short piix4_smba = dev->port_addr->start;
284+
u8 target_int = inb_p(ASFSTA);
285+
286+
if (target_int & BIT(6)) {
287+
/* Target Interrupt */
288+
outb_p(target_int | BIT(6), ASFSTA);
289+
schedule_delayed_work(&dev->work_buf, HZ);
290+
} else {
291+
/* Controller Interrupt */
292+
amd_asf_update_ioport_target(piix4_smba, ASF_SLV_INTR, SMBHSTSTS, true);
293+
}
294+
295+
return IRQ_HANDLED;
296+
}
297+
210298
static int amd_asf_probe(struct platform_device *pdev)
211299
{
212300
struct device *dev = &pdev->dev;
213301
struct amd_asf_dev *asf_dev;
302+
int ret, irq;
214303

215304
asf_dev = devm_kzalloc(dev, sizeof(*asf_dev), GFP_KERNEL);
216305
if (!asf_dev)
@@ -221,6 +310,18 @@ static int amd_asf_probe(struct platform_device *pdev)
221310
if (!asf_dev->port_addr)
222311
return dev_err_probe(dev, -EINVAL, "missing IO resources\n");
223312

313+
ret = devm_delayed_work_autocancel(dev, &asf_dev->work_buf, amd_asf_process_target);
314+
if (ret)
315+
return dev_err_probe(dev, ret, "failed to create work queue\n");
316+
317+
irq = platform_get_irq(pdev, 0);
318+
if (irq < 0)
319+
return dev_err_probe(dev, irq, "missing IRQ resources\n");
320+
321+
ret = devm_request_irq(dev, irq, amd_asf_irq_handler, IRQF_SHARED, "amd_asf", asf_dev);
322+
if (ret)
323+
return dev_err_probe(dev, ret, "Unable to request irq: %d for use\n", irq);
324+
224325
asf_dev->adap.owner = THIS_MODULE;
225326
asf_dev->adap.algo = &amd_asf_smbus_algorithm;
226327
asf_dev->adap.dev.parent = dev;

0 commit comments

Comments
 (0)