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

Quelle  hisi-spmi-controller.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0

#include <linux/delay.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/spmi.h>

/*
 * SPMI register addr
 */

#define SPMI_CHANNEL_OFFSET    0x0300
#define SPMI_SLAVE_OFFSET    0x20

#define SPMI_APB_SPMI_CMD_BASE_ADDR   0x0100

#define SPMI_APB_SPMI_WDATA0_BASE_ADDR   0x0104
#define SPMI_APB_SPMI_WDATA1_BASE_ADDR   0x0108
#define SPMI_APB_SPMI_WDATA2_BASE_ADDR   0x010c
#define SPMI_APB_SPMI_WDATA3_BASE_ADDR   0x0110

#define SPMI_APB_SPMI_STATUS_BASE_ADDR   0x0200

#define SPMI_APB_SPMI_RDATA0_BASE_ADDR   0x0204
#define SPMI_APB_SPMI_RDATA1_BASE_ADDR   0x0208
#define SPMI_APB_SPMI_RDATA2_BASE_ADDR   0x020c
#define SPMI_APB_SPMI_RDATA3_BASE_ADDR   0x0210

#define SPMI_PER_DATAREG_BYTE    4
/*
 * SPMI cmd register
 */

#define SPMI_APB_SPMI_CMD_EN    BIT(31)
#define SPMI_APB_SPMI_CMD_TYPE_OFFSET   24
#define SPMI_APB_SPMI_CMD_LENGTH_OFFSET   20
#define SPMI_APB_SPMI_CMD_SLAVEID_OFFSET  16
#define SPMI_APB_SPMI_CMD_ADDR_OFFSET   0

/* Command Opcodes */

enum spmi_controller_cmd_op_code {
 SPMI_CMD_REG_ZERO_WRITE = 0,
 SPMI_CMD_REG_WRITE = 1,
 SPMI_CMD_REG_READ = 2,
 SPMI_CMD_EXT_REG_WRITE = 3,
 SPMI_CMD_EXT_REG_READ = 4,
 SPMI_CMD_EXT_REG_WRITE_L = 5,
 SPMI_CMD_EXT_REG_READ_L = 6,
 SPMI_CMD_REG_RESET = 7,
 SPMI_CMD_REG_SLEEP = 8,
 SPMI_CMD_REG_SHUTDOWN = 9,
 SPMI_CMD_REG_WAKEUP = 10,
};

/*
 * SPMI status register
 */

#define SPMI_APB_TRANS_DONE   BIT(0)
#define SPMI_APB_TRANS_FAIL   BIT(2)

/* Command register fields */
#define SPMI_CONTROLLER_CMD_MAX_BYTE_COUNT 16

/* Maximum number of support PMIC peripherals */
#define SPMI_CONTROLLER_TIMEOUT_US  1000
#define SPMI_CONTROLLER_MAX_TRANS_BYTES  16

struct spmi_controller_dev {
 struct spmi_controller *controller;
 struct device  *dev;
 void __iomem  *base;
 spinlock_t  lock;
 u32   channel;
};

static int spmi_controller_wait_for_done(struct device *dev,
      struct spmi_controller_dev *ctrl_dev,
      void __iomem *base, u8 sid, u16 addr)
{
 u32 timeout = SPMI_CONTROLLER_TIMEOUT_US;
 u32 status, offset;

 offset  = SPMI_APB_SPMI_STATUS_BASE_ADDR;
 offset += SPMI_CHANNEL_OFFSET * ctrl_dev->channel + SPMI_SLAVE_OFFSET * sid;

 do {
  status = readl(base + offset);

  if (status & SPMI_APB_TRANS_DONE) {
   if (status & SPMI_APB_TRANS_FAIL) {
    dev_err(dev, "%s: transaction failed (0x%x)\n",
     __func__, status);
    return -EIO;
   }
   dev_dbg(dev, "%s: status 0x%x\n", __func__, status);
   return 0;
  }
  udelay(1);
 } while (timeout--);

 dev_err(dev, "%s: timeout, status 0x%x\n", __func__, status);
 return -ETIMEDOUT;
}

static int spmi_read_cmd(struct spmi_controller *ctrl,
    u8 opc, u8 slave_id, u16 slave_addr, u8 *__buf, size_t bc)
{
 struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev);
 u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel;
 unsigned long flags;
 u8 *buf = __buf;
 u32 cmd, data;
 int rc;
 u8 op_code, i;

 if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) {
  dev_err(&ctrl->dev,
   "spmi_controller supports 1..%d bytes per trans, but:%zu requested\n",
   SPMI_CONTROLLER_MAX_TRANS_BYTES, bc);
  return  -EINVAL;
 }

 switch (opc) {
 case SPMI_CMD_READ:
  op_code = SPMI_CMD_REG_READ;
  break;
 case SPMI_CMD_EXT_READ:
  op_code = SPMI_CMD_EXT_REG_READ;
  break;
 case SPMI_CMD_EXT_READL:
  op_code = SPMI_CMD_EXT_REG_READ_L;
  break;
 default:
  dev_err(&ctrl->dev, "invalid read cmd 0x%x\n", opc);
  return -EINVAL;
 }

 cmd = SPMI_APB_SPMI_CMD_EN |
      (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) |
      ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) |
      ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) |  /* slvid */
      ((slave_addr & 0xffff)  << SPMI_APB_SPMI_CMD_ADDR_OFFSET); /* slave_addr */

 spin_lock_irqsave(&spmi_controller->lock, flags);

 writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR);

 rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller,
        spmi_controller->base, slave_id, slave_addr);
 if (rc)
  goto done;

 for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) {
  data = readl(spmi_controller->base + chnl_ofst +
        SPMI_SLAVE_OFFSET * slave_id +
        SPMI_APB_SPMI_RDATA0_BASE_ADDR +
        i * SPMI_PER_DATAREG_BYTE);
  data = be32_to_cpu((__be32 __force)data);
  if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) {
   memcpy(buf, &data, sizeof(data));
   buf += sizeof(data);
  } else {
   memcpy(buf, &data, bc % SPMI_PER_DATAREG_BYTE);
   buf += (bc % SPMI_PER_DATAREG_BYTE);
  }
 }

done:
 spin_unlock_irqrestore(&spmi_controller->lock, flags);
 if (rc)
  dev_err(&ctrl->dev,
   "spmi read wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n",
   opc, slave_id, slave_addr, bc + 1);
 else
  dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, read value: %*ph\n",
   __func__, slave_id, slave_addr, (int)bc, __buf);

 return rc;
}

static int spmi_write_cmd(struct spmi_controller *ctrl,
     u8 opc, u8 slave_id, u16 slave_addr, const u8 *__buf, size_t bc)
{
 struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev);
 u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel;
 const u8 *buf = __buf;
 unsigned long flags;
 u32 cmd, data;
 int rc;
 u8 op_code, i;

 if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) {
  dev_err(&ctrl->dev,
   "spmi_controller supports 1..%d bytes per trans, but:%zu requested\n",
   SPMI_CONTROLLER_MAX_TRANS_BYTES, bc);
  return  -EINVAL;
 }

 switch (opc) {
 case SPMI_CMD_WRITE:
  op_code = SPMI_CMD_REG_WRITE;
  break;
 case SPMI_CMD_EXT_WRITE:
  op_code = SPMI_CMD_EXT_REG_WRITE;
  break;
 case SPMI_CMD_EXT_WRITEL:
  op_code = SPMI_CMD_EXT_REG_WRITE_L;
  break;
 default:
  dev_err(&ctrl->dev, "invalid write cmd 0x%x\n", opc);
  return -EINVAL;
 }

 cmd = SPMI_APB_SPMI_CMD_EN |
       (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) |
       ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) |
       ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) |
       ((slave_addr & 0xffff)  << SPMI_APB_SPMI_CMD_ADDR_OFFSET);

 /* Write data to FIFOs */
 spin_lock_irqsave(&spmi_controller->lock, flags);

 for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) {
  data = 0;
  if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) {
   memcpy(&data, buf, sizeof(data));
   buf += sizeof(data);
  } else {
   memcpy(&data, buf, bc % SPMI_PER_DATAREG_BYTE);
   buf += (bc % SPMI_PER_DATAREG_BYTE);
  }

  writel((u32 __force)cpu_to_be32(data),
         spmi_controller->base + chnl_ofst +
         SPMI_APB_SPMI_WDATA0_BASE_ADDR +
         SPMI_PER_DATAREG_BYTE * i);
 }

 /* Start the transaction */
 writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR);

 rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller,
        spmi_controller->base, slave_id,
        slave_addr);
 spin_unlock_irqrestore(&spmi_controller->lock, flags);

 if (rc)
  dev_err(&ctrl->dev, "spmi write wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n",
   opc, slave_id, slave_addr, bc);
 else
  dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, wrote value: %*ph\n",
   __func__, slave_id, slave_addr, (int)bc, __buf);

 return rc;
}

static int spmi_controller_probe(struct platform_device *pdev)
{
 struct spmi_controller_dev *spmi_controller;
 struct spmi_controller *ctrl;
 struct resource *iores;
 int ret;

 ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*spmi_controller));
 if (IS_ERR(ctrl)) {
  dev_err(&pdev->dev, "can not allocate spmi_controller data\n");
  return PTR_ERR(ctrl);
 }
 spmi_controller = spmi_controller_get_drvdata(ctrl);
 spmi_controller->controller = ctrl;

 iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 if (!iores) {
  dev_err(&pdev->dev, "can not get resource!\n");
  return -EINVAL;
 }

 spmi_controller->base = devm_ioremap(&pdev->dev, iores->start,
          resource_size(iores));
 if (!spmi_controller->base) {
  dev_err(&pdev->dev, "can not remap base addr!\n");
  return -EADDRNOTAVAIL;
 }

 ret = of_property_read_u32(pdev->dev.of_node, "hisilicon,spmi-channel",
       &spmi_controller->channel);
 if (ret) {
  dev_err(&pdev->dev, "can not get channel\n");
  return -ENODEV;
 }

 platform_set_drvdata(pdev, spmi_controller);
 dev_set_drvdata(&ctrl->dev, spmi_controller);

 spin_lock_init(&spmi_controller->lock);

 /* Callbacks */
 ctrl->read_cmd = spmi_read_cmd;
 ctrl->write_cmd = spmi_write_cmd;

 ret = devm_spmi_controller_add(&pdev->dev, ctrl);
 if (ret) {
  dev_err(&pdev->dev, "spmi_controller_add failed with error %d!\n", ret);
  return ret;
 }

 return 0;
}

static const struct of_device_id spmi_controller_match_table[] = {
 {
  .compatible = "hisilicon,kirin970-spmi-controller",
 },
 {}
};
MODULE_DEVICE_TABLE(of, spmi_controller_match_table);

static struct platform_driver spmi_controller_driver = {
 .probe  = spmi_controller_probe,
 .driver  = {
  .name = "hisi_spmi_controller",
  .of_match_table = spmi_controller_match_table,
 },
};

static int __init spmi_controller_init(void)
{
 return platform_driver_register(&spmi_controller_driver);
}
postcore_initcall(spmi_controller_init);

static void __exit spmi_controller_exit(void)
{
 platform_driver_unregister(&spmi_controller_driver);
}
module_exit(spmi_controller_exit);

MODULE_DESCRIPTION("Hisilicon 3670 SPMI Controller driver");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("1.0");
MODULE_ALIAS("platform:spmi_controller");

Messung V0.5
C=90 H=89 G=89

¤ Dauer der Verarbeitung: 0.5 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.