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

Quelle  uio_fsl_elbc_gpcm.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/* uio_fsl_elbc_gpcm: UIO driver for eLBC/GPCM peripherals

   Copyright (C) 2014 Linutronix GmbH
     Author: John Ogness <john.ogness@linutronix.de>

   This driver provides UIO access to memory of a peripheral connected
   to the Freescale enhanced local bus controller (eLBC) interface
   using the general purpose chip-select mode (GPCM).

   Here is an example of the device tree entries:

localbus@ffe05000 {
ranges = <0x2 0x0 0x0 0xff810000 0x10000>;

dpm@2,0 {
compatible = "fsl,elbc-gpcm-uio";
reg = <0x2 0x0 0x10000>;
elbc-gpcm-br = <0xff810800>;
elbc-gpcm-or = <0xffff09f7>;
interrupt-parent = <&mpic>;
interrupts = <4 1>;
device_type = "netx5152";
uio_name = "netx_custom";
netx5152,init-win0-offset = <0x0>;
};
};

   Only the entries reg (to identify bank) and elbc-gpcm-* (initial BR/OR
   values) are required. The entries interrupt*, device_type, and uio_name
   are optional (as well as any type-specific options such as
   netx5152,init-win0-offset). As long as no interrupt handler is needed,
   this driver can be used without any type-specific implementation.

   The netx5152 type has been tested to work with the netX 51/52 hardware
   from Hilscher using the Hilscher userspace netX stack.

   The netx5152 type should serve as a model to add new type-specific
   devices as needed.
*/


#include <linux/module.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/uio_driver.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>

#include <asm/fsl_lbc.h>

#define MAX_BANKS 8

struct fsl_elbc_gpcm {
 struct device *dev;
 struct fsl_lbc_regs __iomem *lbc;
 u32 bank;
 const char *name;

 void (*init)(struct uio_info *info);
 void (*shutdown)(struct uio_info *info, bool init_err);
 irqreturn_t (*irq_handler)(int irq, struct uio_info *info);
};

static ssize_t reg_show(struct device *dev, struct device_attribute *attr,
   char *buf);
static ssize_t reg_store(struct device *dev, struct device_attribute *attr,
    const char *buf, size_t count);

static DEVICE_ATTR(reg_br, 0664, reg_show, reg_store);
static DEVICE_ATTR(reg_or, 0664, reg_show, reg_store);

static struct attribute *uio_fsl_elbc_gpcm_attrs[] = {
 &dev_attr_reg_br.attr,
 &dev_attr_reg_or.attr,
 NULL,
};
ATTRIBUTE_GROUPS(uio_fsl_elbc_gpcm);

static ssize_t reg_show(struct device *dev, struct device_attribute *attr,
   char *buf)
{
 struct uio_info *info = dev_get_drvdata(dev);
 struct fsl_elbc_gpcm *priv = info->priv;
 struct fsl_lbc_bank *bank = &priv->lbc->bank[priv->bank];

 if (attr == &dev_attr_reg_br) {
  return scnprintf(buf, PAGE_SIZE, "0x%08x\n",
     in_be32(&bank->br));

 } else if (attr == &dev_attr_reg_or) {
  return scnprintf(buf, PAGE_SIZE, "0x%08x\n",
     in_be32(&bank->or));
 }

 return 0;
}

static ssize_t reg_store(struct device *dev, struct device_attribute *attr,
    const char *buf, size_t count)
{
 struct uio_info *info = dev_get_drvdata(dev);
 struct fsl_elbc_gpcm *priv = info->priv;
 struct fsl_lbc_bank *bank = &priv->lbc->bank[priv->bank];
 unsigned long val;
 u32 reg_br_cur;
 u32 reg_or_cur;
 u32 reg_new;

 /* parse use input */
 if (kstrtoul(buf, 0, &val) != 0)
  return -EINVAL;
 reg_new = (u32)val;

 /* read current values */
 reg_br_cur = in_be32(&bank->br);
 reg_or_cur = in_be32(&bank->or);

 if (attr == &dev_attr_reg_br) {
  /* not allowed to change effective base address */
  if ((reg_br_cur & reg_or_cur & BR_BA) !=
      (reg_new & reg_or_cur & BR_BA)) {
   return -EINVAL;
  }

  /* not allowed to change mode */
  if ((reg_new & BR_MSEL) != BR_MS_GPCM)
   return -EINVAL;

  /* write new value (force valid) */
  out_be32(&bank->br, reg_new | BR_V);

 } else if (attr == &dev_attr_reg_or) {
  /* not allowed to change access mask */
  if ((reg_or_cur & OR_GPCM_AM) != (reg_new & OR_GPCM_AM))
   return -EINVAL;

  /* write new value */
  out_be32(&bank->or, reg_new);

 } else {
  return -EINVAL;
 }

 return count;
}

#ifdef CONFIG_UIO_FSL_ELBC_GPCM_NETX5152
#define DPM_HOST_WIN0_OFFSET 0xff00
#define DPM_HOST_INT_STAT0 0xe0
#define DPM_HOST_INT_EN0 0xf0
#define DPM_HOST_INT_MASK 0xe600ffff
#define DPM_HOST_INT_GLOBAL_EN 0x80000000

static irqreturn_t netx5152_irq_handler(int irq, struct uio_info *info)
{
 void __iomem *reg_int_en = info->mem[0].internal_addr +
     DPM_HOST_WIN0_OFFSET +
     DPM_HOST_INT_EN0;
 void __iomem *reg_int_stat = info->mem[0].internal_addr +
     DPM_HOST_WIN0_OFFSET +
     DPM_HOST_INT_STAT0;

 /* check if an interrupt is enabled and active */
 if ((ioread32(reg_int_en) & ioread32(reg_int_stat) &
      DPM_HOST_INT_MASK) == 0) {
  return IRQ_NONE;
 }

 /* disable interrupts */
 iowrite32(ioread32(reg_int_en) & ~DPM_HOST_INT_GLOBAL_EN, reg_int_en);

 return IRQ_HANDLED;
}

static void netx5152_init(struct uio_info *info)
{
 unsigned long win0_offset = DPM_HOST_WIN0_OFFSET;
 struct fsl_elbc_gpcm *priv = info->priv;
 const void *prop;

 /* get an optional initial win0 offset */
 prop = of_get_property(priv->dev->of_node,
          "netx5152,init-win0-offset", NULL);
 if (prop)
  win0_offset = of_read_ulong(prop, 1);

 /* disable interrupts */
 iowrite32(0, info->mem[0].internal_addr + win0_offset +
       DPM_HOST_INT_EN0);
}

static void netx5152_shutdown(struct uio_info *info, bool init_err)
{
 if (init_err)
  return;

 /* disable interrupts */
 iowrite32(0, info->mem[0].internal_addr + DPM_HOST_WIN0_OFFSET +
       DPM_HOST_INT_EN0);
}
#endif

static void setup_periph(struct fsl_elbc_gpcm *priv,
       const char *type)
{
#ifdef CONFIG_UIO_FSL_ELBC_GPCM_NETX5152
 if (strcmp(type, "netx5152") == 0) {
  priv->irq_handler = netx5152_irq_handler;
  priv->init = netx5152_init;
  priv->shutdown = netx5152_shutdown;
  priv->name = "netX 51/52";
  return;
 }
#endif
}

static int check_of_data(struct fsl_elbc_gpcm *priv,
       struct resource *res,
       u32 reg_br, u32 reg_or)
{
 /* check specified bank */
 if (priv->bank >= MAX_BANKS) {
  dev_err(priv->dev, "invalid bank\n");
  return -ENODEV;
 }

 /* check specified mode (BR_MS_GPCM is 0) */
 if ((reg_br & BR_MSEL) != BR_MS_GPCM) {
  dev_err(priv->dev, "unsupported mode\n");
  return -ENODEV;
 }

 /* check specified mask vs. resource size */
 if ((~(reg_or & OR_GPCM_AM) + 1) != resource_size(res)) {
  dev_err(priv->dev, "address mask / size mismatch\n");
  return -ENODEV;
 }

 /* check specified address */
 if ((reg_br & reg_or & BR_BA) != fsl_lbc_addr(res->start)) {
  dev_err(priv->dev, "base address mismatch\n");
  return -ENODEV;
 }

 return 0;
}

static int get_of_data(struct fsl_elbc_gpcm *priv, struct device_node *node,
         struct resource *res, u32 *reg_br,
         u32 *reg_or, unsigned int *irq, char **name)
{
 const char *dt_name;
 const char *type;
 int ret;

 /* get the memory resource */
 ret = of_address_to_resource(node, 0, res);
 if (ret) {
  dev_err(priv->dev, "failed to get resource\n");
  return ret;
 }

 /* get the bank number */
 ret = of_property_read_u32(node, "reg", &priv->bank);
 if (ret) {
  dev_err(priv->dev, "failed to get bank number\n");
  return ret;
 }

 /* get BR value to set */
 ret = of_property_read_u32(node, "elbc-gpcm-br", reg_br);
 if (ret) {
  dev_err(priv->dev, "missing elbc-gpcm-br value\n");
  return ret;
 }

 /* get OR value to set */
 ret = of_property_read_u32(node, "elbc-gpcm-or", reg_or);
 if (ret) {
  dev_err(priv->dev, "missing elbc-gpcm-or value\n");
  return ret;
 }

 /* get optional peripheral type */
 priv->name = "generic";
 if (of_property_read_string(node, "device_type", &type) == 0)
  setup_periph(priv, type);

 /* get optional irq value */
 *irq = irq_of_parse_and_map(node, 0);

 /* sanity check device tree data */
 ret = check_of_data(priv, res, *reg_br, *reg_or);
 if (ret)
  return ret;

 /* get optional uio name */
 if (of_property_read_string(node, "uio_name", &dt_name) != 0)
  dt_name = "eLBC_GPCM";
 *name = devm_kstrdup(priv->dev, dt_name, GFP_KERNEL);
 if (!*name)
  return -ENOMEM;

 return 0;
}

static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev)
{
 struct device_node *node = pdev->dev.of_node;
 struct fsl_elbc_gpcm *priv;
 struct uio_info *info;
 char *uio_name = NULL;
 struct resource res;
 unsigned int irq;
 u32 reg_br_cur;
 u32 reg_or_cur;
 u32 reg_br_new;
 u32 reg_or_new;
 int ret;

 if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
  return -ENODEV;

 /* allocate private data */
 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 if (!priv)
  return -ENOMEM;
 priv->dev = &pdev->dev;
 priv->lbc = fsl_lbc_ctrl_dev->regs;

 /* get device tree data */
 ret = get_of_data(priv, node, &res, ®_br_new, ®_or_new,
     &irq, &uio_name);
 if (ret)
  return ret;

 /* allocate UIO structure */
 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
 if (!info)
  return -ENOMEM;

 /* get current BR/OR values */
 reg_br_cur = in_be32(&priv->lbc->bank[priv->bank].br);
 reg_or_cur = in_be32(&priv->lbc->bank[priv->bank].or);

 /* if bank already configured, make sure it matches */
 if ((reg_br_cur & BR_V)) {
  if ((reg_br_cur & BR_MSEL) != BR_MS_GPCM ||
      (reg_br_cur & reg_or_cur & BR_BA)
       != fsl_lbc_addr(res.start)) {
   dev_err(priv->dev,
    "bank in use by another peripheral\n");
   return -ENODEV;
  }

  /* warn if behavior settings changing */
  if ((reg_br_cur & ~(BR_BA | BR_V)) !=
      (reg_br_new & ~(BR_BA | BR_V))) {
   dev_warn(priv->dev,
     "modifying BR settings: 0x%08x -> 0x%08x",
     reg_br_cur, reg_br_new);
  }
  if ((reg_or_cur & ~OR_GPCM_AM) != (reg_or_new & ~OR_GPCM_AM)) {
   dev_warn(priv->dev,
     "modifying OR settings: 0x%08x -> 0x%08x",
     reg_or_cur, reg_or_new);
  }
 }

 /* configure the bank (force base address and GPCM) */
 reg_br_new &= ~(BR_BA | BR_MSEL);
 reg_br_new |= fsl_lbc_addr(res.start) | BR_MS_GPCM | BR_V;
 out_be32(&priv->lbc->bank[priv->bank].or, reg_or_new);
 out_be32(&priv->lbc->bank[priv->bank].br, reg_br_new);

 /* map the memory resource */
 info->mem[0].internal_addr = ioremap(res.start, resource_size(&res));
 if (!info->mem[0].internal_addr) {
  dev_err(priv->dev, "failed to map chip region\n");
  return -ENODEV;
 }

 /* set all UIO data */
 info->mem[0].name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%pOFn", node);
 info->mem[0].addr = res.start;
 info->mem[0].size = resource_size(&res);
 info->mem[0].memtype = UIO_MEM_PHYS;
 info->priv = priv;
 info->name = uio_name;
 info->version = "0.0.1";
 if (irq) {
  if (priv->irq_handler) {
   info->irq = irq;
   info->irq_flags = IRQF_SHARED;
   info->handler = priv->irq_handler;
  } else {
   irq = 0;
   dev_warn(priv->dev, "ignoring irq, no handler\n");
  }
 }

 if (priv->init)
  priv->init(info);

 /* register UIO device */
 if (uio_register_device(priv->dev, info) != 0) {
  dev_err(priv->dev, "UIO registration failed\n");
  ret = -ENODEV;
  goto out_err2;
 }

 /* store private data */
 platform_set_drvdata(pdev, info);

 dev_info(priv->dev,
   "eLBC/GPCM device (%s) at 0x%llx, bank %d, irq=%d\n",
   priv->name, (unsigned long long)res.start, priv->bank,
   irq ? : -1);

 return 0;
out_err2:
 if (priv->shutdown)
  priv->shutdown(info, true);
 iounmap(info->mem[0].internal_addr);
 return ret;
}

static void uio_fsl_elbc_gpcm_remove(struct platform_device *pdev)
{
 struct uio_info *info = platform_get_drvdata(pdev);
 struct fsl_elbc_gpcm *priv = info->priv;

 platform_set_drvdata(pdev, NULL);
 uio_unregister_device(info);
 if (priv->shutdown)
  priv->shutdown(info, false);
 iounmap(info->mem[0].internal_addr);

}

static const struct of_device_id uio_fsl_elbc_gpcm_match[] = {
 { .compatible = "fsl,elbc-gpcm-uio", },
 {}
};
MODULE_DEVICE_TABLE(of, uio_fsl_elbc_gpcm_match);

static struct platform_driver uio_fsl_elbc_gpcm_driver = {
 .driver = {
  .name = "fsl,elbc-gpcm-uio",
  .of_match_table = uio_fsl_elbc_gpcm_match,
  .dev_groups = uio_fsl_elbc_gpcm_groups,
 },
 .probe = uio_fsl_elbc_gpcm_probe,
 .remove = uio_fsl_elbc_gpcm_remove,
};
module_platform_driver(uio_fsl_elbc_gpcm_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("John Ogness ");
MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller GPCM driver");

Messung V0.5
C=93 H=91 G=91

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