1f7d420d创建于 4月21日历史提交
/****************************************************************************
 * drivers/pci/pci_ecam.c
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <errno.h>

#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/lib/math32.h>
#include <nuttx/pci/pci.h>
#include <nuttx/pci/pci_ecam.h>
#include <nuttx/nuttx.h>

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#define readb(a)     (*(FAR volatile uint8_t *)(a))
#define writeb(v,a)  (*(FAR volatile uint8_t *)(a) = (v))
#define readw(a)     (*(FAR volatile uint16_t *)(a))
#define writew(v,a)  (*(FAR volatile uint16_t *)(a) = (v))
#define readl(a)     (*(FAR volatile uint32_t *)(a))
#define writel(v,a)  (*(FAR volatile uint32_t *)(a) = (v))

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

static int pci_ecam_read_config(FAR struct pci_bus_s *bus,
                                uint32_t devfn, int where, int size,
                                FAR uint32_t *val);

static int pci_ecam_write_config(FAR struct pci_bus_s *bus,
                                 uint32_t devfn, int where, int size,
                                 uint32_t val);

static int pci_ecam_read_io(FAR struct pci_bus_s *bus, uintptr_t addr,
                            int size, FAR uint32_t *val);

static int pci_ecam_write_io(FAR struct pci_bus_s *bus, uintptr_t addr,
                             int size, uint32_t val);

static int pci_ecam_get_irq(FAR struct pci_bus_s *bus, uint32_t devfn,
                            uint8_t line, uint8_t pin);

static uintptr_t pci_ecam_map(FAR struct pci_bus_s *bus, uintptr_t start,
                              uintptr_t end);
#ifdef CONFIG_PCI_MSIX
static int pci_ecam_alloc_irq(FAR struct pci_bus_s *bus, uint32_t devfn,
                              FAR int *irq, int num);

static void pci_ecam_release_irq(FAR struct pci_bus_s *bus, FAR int *irq,
                                 int num);

static int pci_ecam_connect_irq(FAR struct pci_bus_s *bus, FAR int *irq,
                                int num, FAR uintptr_t *mar,
                                FAR uint32_t *mdr);
#endif

/****************************************************************************
 * Private Types
 ****************************************************************************/

struct pci_ecam_s
{
  struct pci_controller_s ctrl;
  struct pci_resource_s cfg;
};

/****************************************************************************
 * Private Data
 ****************************************************************************/

static const struct pci_ops_s g_pci_ecam_ops =
{
  .read        = pci_ecam_read_config,
  .write       = pci_ecam_write_config,
  .read_io     = pci_ecam_read_io,
  .write_io    = pci_ecam_write_io,
  .get_irq     = pci_ecam_get_irq,
  .map         = pci_ecam_map,
#ifdef CONFIG_PCI_MSIX
  .alloc_irq   = pci_ecam_alloc_irq,
  .release_irq = pci_ecam_release_irq,
  .connect_irq = pci_ecam_connect_irq,
#endif
};

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: pci_ecam_from_controller
 *
 * Description:
 *   To get the ecam type from ctrl type.
 *
 * Input Parameters:
 *   ctrl - The pci controller
 *
 * Returned Value:
 *   Return the struct pci_ecam_pcie s address
 *
 ****************************************************************************/

static inline FAR struct pci_ecam_s *
pci_ecam_from_controller(FAR struct pci_controller_s *ctrl)
{
  return container_of(ctrl, struct pci_ecam_s, ctrl);
}

/****************************************************************************
 * Name: pci_ecam_conf_address
 *
 * Description:
 *   This function is used to get the specify reg address of a function
 *   configuration space.
 *
 * Input Parameters:
 *   bus   - PCI bus type private data
 *   devfn - Specify a BDF
 *   where - Which ID in configuration space
 *
 ****************************************************************************/

static FAR void *pci_ecam_conf_address(FAR const struct pci_bus_s *bus,
                                       uint32_t devfn, int where)
{
  FAR struct pci_ecam_s *ecam = pci_ecam_from_controller(bus->ctrl);
  FAR void *addr;

  addr = (FAR void *)(uintptr_t)ecam->cfg.start;
  addr += bus->number << 20;
  addr += PCI_SLOT(devfn) << 15;
  addr += PCI_FUNC(devfn) << 12;
  addr += where;

  return addr;
}

/****************************************************************************
 * Name: pci_ecam_addr_valid
 *
 * Description:
 *   To check the bus number whether or not valid.
 *
 * Input Parameters:
 *   bus   - The bus private data
 *   devfn - Get a specify dev by devfn
 *
 * Returned Value:
 *   True if success, false if failed
 *
 ****************************************************************************/

static bool pci_ecam_addr_valid(FAR const struct pci_bus_s *bus,
                                uint32_t devfn)
{
  FAR struct pci_ecam_s *ecam = pci_ecam_from_controller(bus->ctrl);
  int num_buses = div_round_up(pci_resource_size(&ecam->cfg), 1 << 20);

  return bus->number < num_buses;
}

/****************************************************************************
 * Name: pci_ecam_read_config
 *
 * Description:
 *   Read data from the speicfy register.
 *
 * Input Parameters:
 *   bus   - The bus on this to read reg data
 *   devfn - BDF
 *   where - Which cfg space ID
 *   size  - Data size
 *   val   - Return value to this var
 *
 * Returned Value:
 *   Return the specify enum result of operation
 *
 ****************************************************************************/

static int pci_ecam_read_config(FAR struct pci_bus_s *bus,
                                uint32_t devfn, int where, int size,
                                FAR uint32_t *val)
{
  FAR void *addr;

  if (!pci_ecam_addr_valid(bus, devfn))
    {
      return -ENODEV;
    }

  addr = pci_ecam_conf_address(bus, devfn, where);

  if (!IS_ALIGNED((uintptr_t)addr, size))
    {
      *val = 0;
      return -EINVAL;
    }

  if (size == 4)
    {
      *val = readl(addr);
    }
  else if (size == 2)
    {
      *val = readw(addr);
    }
  else if (size == 1)
    {
      *val = readb(addr);
    }
  else
    {
      *val = 0;
      return -EINVAL;
    }

  return OK;
}

/****************************************************************************
 * Name: pci_ecam_write_config
 *
 * Description:
 *   Write data into speicfy register.
 *
 * Input Parameters:
 *   bus   - The specify bus private data
 *   devfn - BDF
 *   where - Which ID in configuration space
 *   size  - Data Size
 *   val   - The value to be written
 *
 * Returned Value:
 *   Return the specify enum result of operation
 *
 ****************************************************************************/

static int pci_ecam_write_config(FAR struct pci_bus_s *bus,
                                 uint32_t devfn, int where, int size,
                                 uint32_t val)
{
  FAR void *addr;

  if (!pci_ecam_addr_valid(bus, devfn))
    {
      return -ENODEV;
    }

  addr = pci_ecam_conf_address(bus, devfn, where);

  if (!IS_ALIGNED((uintptr_t)addr, size))
    {
      return -EINVAL;
    }

  if (size == 4)
    {
      writel(val, addr);
    }
  else if (size == 2)
    {
      writew(val, addr);
    }
  else if (size == 1)
    {
      writeb(val, addr);
    }
  else
    {
      return -EINVAL;
    }

  return OK;
}

/****************************************************************************
 * Name: pci_ecam_read_io
 *
 * Description:
 *   Read data from the specific address.
 *
 * Input Parameters:
 *   bus  - The bus on this to read reg data
 *   addr - Data address
 *   size - Data size
 *   val  - Return value to this var
 *
 * Returned Value:
 *   Zero (OK) is returned on success.  A negated errno value is returned
 *   on failure.
 *
 ****************************************************************************/

static int pci_ecam_read_io(FAR struct pci_bus_s *bus, uintptr_t addr,
                            int size, FAR uint32_t *val)
{
  if (!IS_ALIGNED(addr, size))
    {
      *val = 0;
      return -EINVAL;
    }

  if (size == 4)
    {
      *val = readl(addr);
    }
  else if (size == 2)
    {
      *val = readw(addr);
    }
  else if (size == 1)
    {
      *val = readb(addr);
    }
  else
    {
      *val = 0;
      return -EINVAL;
    }

  return OK;
}

/****************************************************************************
 * Name: pci_ecam_read_io
 *
 * Description:
 *   Write data to the specific address.
 *
 * Input Parameters:
 *   bus  - The bus on this to read reg data
 *   addr - Data address
 *   size - Data size
 *   val  - Write this value to specific address
 *
 * Returned Value:
 *   Zero (OK) is returned on success.  A negated errno value is returned
 *   on failure.
 *
 ****************************************************************************/

static int pci_ecam_write_io(FAR struct pci_bus_s *bus, uintptr_t addr,
                             int size, uint32_t val)
{
  if (!IS_ALIGNED(addr, size))
    {
      return -EINVAL;
    }

  if (size == 4)
    {
      writel(val, addr);
    }
  else if (size == 2)
    {
      writew(val, addr);
    }
  else if (size == 1)
    {
      writeb(val, addr);
    }
  else
    {
      return -EINVAL;
    }

  return OK;
}

#ifdef CONFIG_PCI_MSIX
static int pci_ecam_alloc_irq(FAR struct pci_bus_s *bus, uint32_t devfn,
                              FAR int *irq, int num)
{
  return up_alloc_irq_msi(bus->ctrl->busno, devfn, irq, num);
}

static void pci_ecam_release_irq(FAR struct pci_bus_s *bus, FAR int *irq,
                                 int num)
{
  return up_release_irq_msi(irq, num);
}

static int pci_ecam_connect_irq(FAR struct pci_bus_s *bus, FAR int *irq,
                                int num, FAR uintptr_t *mar,
                                FAR uint32_t *mdr)
{
  return up_connect_irq(irq, num, mar, mdr);
}
#endif

/****************************************************************************
 * Name: pci_ecam_get_irq
 *
 * Description:
 *  Get interrupt number associated with a given INTx line.
 *
 * Input Parameters:
 *   bus   - Bus that PCI device resides
 *   devfn - The pci device and function number
 *   line  - Activated PCI legacy interrupt line
 *   pin   - Intx pin number
 *
 * Returned Value:
 *   Return interrupt number associated with a given INTx
 *
 ****************************************************************************/

static int pci_ecam_get_irq(FAR struct pci_bus_s *bus, uint32_t devfn,
                            uint8_t line, uint8_t pin)
{
  uint32_t sbdf = PCI_SBDF(bus->ctrl->domain, bus->ctrl->busno, devfn);
  return up_get_legacy_irq(sbdf, line, pin);
}

/****************************************************************************
 * Name: pci_ecam_map
 *
 * Description:
 *  Map pci addr to cpu addr.
 *
 * Input Parameters:
 *   bus   - Bus that PCI device resides
 *   start - The pci device start pci addr
 *   end   - The pci device end pci addr
 *
 * Returned Value:
 *   Return pci device cpu addr
 *
 ****************************************************************************/

static uintptr_t pci_ecam_map(FAR struct pci_bus_s *bus, uintptr_t start,
                              uintptr_t end)
{
  FAR struct pci_controller_s *ctrl = bus->ctrl;

  if (start >= ctrl->io.start && end < ctrl->io.end)
    {
      return start + ctrl->io.offset;
    }

  if (start >= ctrl->mem.start && end < ctrl->mem.end)
    {
      return start + ctrl->mem.offset;
    }

  if (start >= ctrl->mem_pref.start && end < ctrl->mem_pref.end)
    {
      return start + ctrl->mem_pref.offset;
    }

  return 0;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: pci_ecam_register
 *
 * Description:
 *   This function is used to register an ecam driver for pci.
 *
 * Input Parameters:
 *   domain   - PCI domain/segment id
 *   cfg      - Configuration space data
 *   io       - I/O space data
 *   mem      - No-prefetchable space data
 *   mem_pref - Prefetchable space data
 *
 * Returned Value:
 *   Return 0 if success, nageative if failed
 *
 ****************************************************************************/

int pci_ecam_register(uint16_t domain,
                      FAR const struct pci_resource_s *cfg,
                      FAR const struct pci_resource_s *io,
                      FAR const struct pci_resource_s *mem,
                      FAR const struct pci_resource_s *mem_pref)
{
  FAR struct pci_ecam_s *ecam;

  if (cfg == NULL || (io == NULL && mem == NULL && mem_pref == NULL))
    {
      return -EINVAL;
    }

  ecam = kmm_zalloc(sizeof(*ecam));
  if (ecam == NULL)
    {
      return -ENOMEM;
    }

  memcpy(&ecam->cfg, cfg, sizeof(*cfg));

  if (io != NULL)
    {
      memcpy(&ecam->ctrl.io, io, sizeof(*io));
    }

  if (mem != NULL)
    {
      memcpy(&ecam->ctrl.mem, mem, sizeof(*mem));
    }

  if (mem_pref != NULL)
    {
      memcpy(&ecam->ctrl.mem_pref, mem_pref, sizeof(*mem_pref));
    }

  ecam->ctrl.domain = domain;
  ecam->ctrl.ops = &g_pci_ecam_ops;
  return pci_register_controller(&ecam->ctrl);
}