uboot/arch/riscv/lib/andes_plic.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2019, Rick Chen <rick@andestech.com>
   4 *
   5 * U-Boot syscon driver for Andes's Platform Level Interrupt Controller (PLIC).
   6 * The PLIC block holds memory-mapped claim and pending registers
   7 * associated with software interrupt.
   8 */
   9
  10#include <common.h>
  11#include <dm.h>
  12#include <asm/global_data.h>
  13#include <dm/device-internal.h>
  14#include <dm/lists.h>
  15#include <dm/uclass-internal.h>
  16#include <regmap.h>
  17#include <syscon.h>
  18#include <asm/io.h>
  19#include <asm/syscon.h>
  20#include <cpu.h>
  21#include <linux/err.h>
  22
  23/* pending register */
  24#define PENDING_REG(base, hart) ((ulong)(base) + 0x1000 + ((hart) / 4) * 4)
  25/* enable register */
  26#define ENABLE_REG(base, hart)  ((ulong)(base) + 0x2000 + (hart) * 0x80)
  27/* claim register */
  28#define CLAIM_REG(base, hart)   ((ulong)(base) + 0x200004 + (hart) * 0x1000)
  29
  30#define ENABLE_HART_IPI         (0x80808080)
  31#define SEND_IPI_TO_HART(hart)  (0x80 >> (hart))
  32
  33DECLARE_GLOBAL_DATA_PTR;
  34
  35static int enable_ipi(int hart)
  36{
  37        unsigned int en;
  38
  39        en = ENABLE_HART_IPI >> hart;
  40        writel(en, (void __iomem *)ENABLE_REG(gd->arch.plic, hart));
  41
  42        return 0;
  43}
  44
  45int riscv_init_ipi(void)
  46{
  47        int ret;
  48        long *base = syscon_get_first_range(RISCV_SYSCON_PLIC);
  49        ofnode node;
  50        struct udevice *dev;
  51        u32 reg;
  52
  53        if (IS_ERR(base))
  54                return PTR_ERR(base);
  55        gd->arch.plic = base;
  56
  57        ret = uclass_find_first_device(UCLASS_CPU, &dev);
  58        if (ret)
  59                return ret;
  60        else if (!dev)
  61                return -ENODEV;
  62
  63        ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) {
  64                const char *device_type;
  65
  66                device_type = ofnode_read_string(node, "device_type");
  67                if (!device_type)
  68                        continue;
  69
  70                if (strcmp(device_type, "cpu"))
  71                        continue;
  72
  73                /* skip if hart is marked as not available */
  74                if (!ofnode_is_available(node))
  75                        continue;
  76
  77                /* read hart ID of CPU */
  78                ret = ofnode_read_u32(node, "reg", &reg);
  79                if (ret == 0)
  80                        enable_ipi(reg);
  81        }
  82
  83        return 0;
  84}
  85
  86int riscv_send_ipi(int hart)
  87{
  88        unsigned int ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
  89
  90        writel(ipi, (void __iomem *)PENDING_REG(gd->arch.plic,
  91                                gd->arch.boot_hart));
  92
  93        return 0;
  94}
  95
  96int riscv_clear_ipi(int hart)
  97{
  98        u32 source_id;
  99
 100        source_id = readl((void __iomem *)CLAIM_REG(gd->arch.plic, hart));
 101        writel(source_id, (void __iomem *)CLAIM_REG(gd->arch.plic, hart));
 102
 103        return 0;
 104}
 105
 106int riscv_get_ipi(int hart, int *pending)
 107{
 108        unsigned int ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
 109
 110        *pending = readl((void __iomem *)PENDING_REG(gd->arch.plic,
 111                                                     gd->arch.boot_hart));
 112        *pending = !!(*pending & ipi);
 113
 114        return 0;
 115}
 116
 117static const struct udevice_id andes_plic_ids[] = {
 118        { .compatible = "riscv,plic1", .data = RISCV_SYSCON_PLIC },
 119        { }
 120};
 121
 122U_BOOT_DRIVER(andes_plic) = {
 123        .name           = "andes_plic",
 124        .id             = UCLASS_SYSCON,
 125        .of_match       = andes_plic_ids,
 126        .flags          = DM_FLAG_PRE_RELOC,
 127};
 128