Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/i2c/busses/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 10 kB image not shown  

Quelle  i2c-amd-asf-plat.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * AMD Alert Standard Format Platform Driver
 *
 * Copyright (c) 2024, Advanced Micro Devices, Inc.
 * All Rights Reserved.
 *
 * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
 *     Sanket Goswami <Sanket.Goswami@amd.com>
 */


#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/devm-helpers.h>
#include <linux/errno.h>
#include <linux/gfp_types.h>
#include <linux/i2c.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#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 ASFINDEX (0x07 + piix4_smba)
#define ASFLISADDR (0x09 + piix4_smba)
#define ASFSTA  (0x0A + piix4_smba)
#define ASFSLVSTA (0x0D + piix4_smba)
#define ASFDATARWPTR (0x11 + piix4_smba)
#define ASFSETDATARDPTR (0x12 + piix4_smba)
#define ASFDATABNKSEL (0x13 + piix4_smba)
#define ASFSLVEN (0x15 + piix4_smba)

#define ASF_BLOCK_MAX_BYTES 72
#define ASF_ERROR_STATUS GENMASK(3, 1)

struct amd_asf_dev {
 struct i2c_adapter adap;
 void __iomem *eoi_base;
 struct i2c_client *target;
 struct delayed_work work_buf;
 struct sb800_mmio_cfg mmio_cfg;
 struct resource *port_addr;
};

static void amd_asf_process_target(struct work_struct *work)
{
 struct amd_asf_dev *dev = container_of(work, struct amd_asf_dev, work_buf.work);
 unsigned short piix4_smba = dev->port_addr->start;
 u8 data[ASF_BLOCK_MAX_BYTES];
 u8 bank, reg, cmd;
 u8 len = 0, idx, val;

 /* Read target status register */
 reg = inb_p(ASFSLVSTA);

 /* Check if no error bits are set in target status register */
 if (reg & ASF_ERROR_STATUS) {
  /* Set bank as full */
  cmd = 1;
  reg |= GENMASK(3, 2);
  outb_p(reg, ASFDATABNKSEL);
 } else {
  /* Read data bank */
  reg = inb_p(ASFDATABNKSEL);
  bank = (reg & BIT(3)) ? 1 : 0;

  /* Set read data bank */
  if (bank) {
   reg |= BIT(4);
   reg &= ~BIT(3);
  } else {
   reg &= ~BIT(4);
   reg &= ~BIT(2);
  }

  /* Read command register */
  outb_p(reg, ASFDATABNKSEL);
  cmd = inb_p(ASFINDEX);
  len = inb_p(ASFDATARWPTR);
  for (idx = 0; idx < len; idx++)
   data[idx] = inb_p(ASFINDEX);

  /* Clear data bank status */
  if (bank) {
   reg |= BIT(3);
   outb_p(reg, ASFDATABNKSEL);
  } else {
   reg |= BIT(2);
   outb_p(reg, ASFDATABNKSEL);
  }
 }

 outb_p(0, ASFSETDATARDPTR);
 if (cmd & BIT(0))
  return;

 /*
 * Although i2c_slave_event() returns an appropriate error code, we
 * don't check it here because we're operating in the workqueue context.
 */

 i2c_slave_event(dev->target, I2C_SLAVE_WRITE_REQUESTED, &val);
 for (idx = 0; idx < len; idx++) {
  val = data[idx];
  i2c_slave_event(dev->target, I2C_SLAVE_WRITE_RECEIVED, &val);
 }
 i2c_slave_event(dev->target, I2C_SLAVE_STOP, &val);
}

static void amd_asf_update_ioport_target(unsigned short piix4_smba, u8 bit,
      unsigned long offset, bool set)
{
 unsigned long reg;

 reg = inb_p(offset);
 __assign_bit(bit, ®, set);
 outb_p(reg, offset);
}

static void amd_asf_update_mmio_target(struct amd_asf_dev *dev, u8 bit, bool set)
{
 unsigned long reg;

 reg = ioread32(dev->mmio_cfg.addr);
 __assign_bit(bit, ®, set);
 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_ioport_target(piix4_smba, ASF_SLV_LISTN, ASFLISADDR, true);
 /* Enable target and set the clock */
 amd_asf_update_mmio_target(dev, ASF_MSTR_EN, false);
 amd_asf_update_mmio_target(dev, ASF_CLK_EN, true);
 /* Enable target interrupt */
 amd_asf_update_ioport_target(piix4_smba, ASF_SLV_INTR, ASFSLVEN, true);
 amd_asf_update_ioport_target(piix4_smba, ASF_SLV_RST, ASFSLVEN, false);
 /* Enable PEC and PEC append */
 amd_asf_update_ioport_target(piix4_smba, ASF_DATA_EN, SMBHSTCNT, true);
 amd_asf_update_ioport_target(piix4_smba, ASF_PEC_SP, SMBHSTCNT, true);
}

static int 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_ioport_target(piix4_smba, ASF_DATA_EN, SMBHSTCNT, true);
 amd_asf_update_ioport_target(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_warn(&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_ioport_target(piix4_smba, ASF_SLV_RST, ASFSLVEN, true);
 amd_asf_update_ioport_target(piix4_smba, ASF_SLV_LISTN, ASFLISADDR, false);
 /* Clear ASF target status */
 outb_p(0, ASFSLVSTA);

 /* Enable ASF SMBus controller function */
 amd_asf_update_mmio_target(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) | I2C_M_RD;
 outb_p(reg, ASFLISADDR);

 amd_asf_setup_target(dev);
 dev->target = target;
 amd_asf_update_ioport_target(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_ioport_target(piix4_smba, ASF_SLV_INTR, ASFSLVEN, false);
 amd_asf_update_ioport_target(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_WRITE_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |
        I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_PEC | I2C_FUNC_SLAVE;
}

static const struct i2c_algorithm amd_asf_smbus_algorithm = {
 .xfer = amd_asf_xfer,
 .reg_target = amd_asf_reg_target,
 .unreg_target = amd_asf_unreg_target,
 .functionality = amd_asf_func,
};

static irqreturn_t amd_asf_irq_handler(int irq, void *ptr)
{
 struct amd_asf_dev *dev = ptr;
 unsigned short piix4_smba = dev->port_addr->start;
 u8 target_int = inb_p(ASFSTA);

 if (target_int & BIT(6)) {
  /* Target Interrupt */
  outb_p(target_int | BIT(6), ASFSTA);
  schedule_delayed_work(&dev->work_buf, HZ);
 } else {
  /* Controller Interrupt */
  amd_asf_update_ioport_target(piix4_smba, ASF_SLV_INTR, SMBHSTSTS, true);
 }

 iowrite32(irq, dev->eoi_base);
 return IRQ_HANDLED;
}

static int amd_asf_probe(struct platform_device *pdev)
{
 struct device *dev = &pdev->dev;
 struct amd_asf_dev *asf_dev;
 struct resource *eoi_addr;
 int ret, irq;

 asf_dev = devm_kzalloc(dev, sizeof(*asf_dev), GFP_KERNEL);
 if (!asf_dev)
  return dev_err_probe(dev, -ENOMEM, "Failed to allocate memory\n");

 asf_dev->mmio_cfg.use_mmio = true;
 asf_dev->port_addr = platform_get_resource(pdev, IORESOURCE_IO, 0);
 if (!asf_dev->port_addr)
  return dev_err_probe(dev, -EINVAL, "missing IO resources\n");

 /*
 * The resource obtained via ACPI might not belong to the ASF device address space. Instead,
 * it could be within other IP blocks of the ASIC, which are crucial for generating
 * subsequent interrupts. Therefore, we avoid using devm_platform_ioremap_resource() and
 * use platform_get_resource() and devm_ioremap() separately to prevent any address space
 * conflicts.
 */

 eoi_addr = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 if (!eoi_addr)
  return dev_err_probe(dev, -EINVAL, "missing MEM resources\n");

 asf_dev->eoi_base = devm_ioremap(dev, eoi_addr->start, resource_size(eoi_addr));
 if (!asf_dev->eoi_base)
  return dev_err_probe(dev, -EBUSY, "failed mapping IO region\n");

 ret = devm_delayed_work_autocancel(dev, &asf_dev->work_buf, amd_asf_process_target);
 if (ret)
  return dev_err_probe(dev, ret, "failed to create work queue\n");

 irq = platform_get_irq(pdev, 0);
 if (irq < 0)
  return dev_err_probe(dev, irq, "missing IRQ resources\n");

 ret = devm_request_irq(dev, irq, amd_asf_irq_handler, IRQF_SHARED, "amd_asf", asf_dev);
 if (ret)
  return dev_err_probe(dev, ret, "Unable to request irq: %d for use\n", irq);

 asf_dev->adap.owner = THIS_MODULE;
 asf_dev->adap.algo = &amd_asf_smbus_algorithm;
 asf_dev->adap.dev.parent = dev;

 i2c_set_adapdata(&asf_dev->adap, asf_dev);
 snprintf(asf_dev->adap.name, sizeof(asf_dev->adap.name), "AMD ASF adapter");

 return devm_i2c_add_adapter(dev, &asf_dev->adap);
}

static const struct acpi_device_id amd_asf_acpi_ids[] = {
 { "AMDI001A" },
 { }
};
MODULE_DEVICE_TABLE(acpi, amd_asf_acpi_ids);

static struct platform_driver amd_asf_driver = {
 .driver = {
  .name = "i2c-amd-asf",
  .acpi_match_table = amd_asf_acpi_ids,
 },
 .probe = amd_asf_probe,
};
module_platform_driver(amd_asf_driver);

MODULE_IMPORT_NS("PIIX4_SMBUS");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("AMD Alert Standard Format Driver");

Messung V0.5
C=95 H=95 G=94

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.