xref: /openbmc/qemu/hw/ssi/npcm_pspi.c (revision d328fef93ae757a0dd65ed786a4086e27952eef3)
1  /*
2   * Nuvoton NPCM Peripheral SPI Module (PSPI)
3   *
4   * Copyright 2023 Google LLC
5   *
6   * This program is free software; you can redistribute it and/or modify it
7   * under the terms of the GNU General Public License as published by the
8   * Free Software Foundation; either version 2 of the License, or
9   * (at your option) any later version.
10   *
11   * This program is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14   * for more details.
15   */
16  
17  #include "qemu/osdep.h"
18  
19  #include "hw/irq.h"
20  #include "hw/registerfields.h"
21  #include "hw/ssi/npcm_pspi.h"
22  #include "migration/vmstate.h"
23  #include "qapi/error.h"
24  #include "qemu/error-report.h"
25  #include "qemu/log.h"
26  #include "qemu/module.h"
27  #include "qemu/units.h"
28  
29  #include "trace.h"
30  
31  REG16(PSPI_DATA, 0x0)
32  REG16(PSPI_CTL1, 0x2)
33      FIELD(PSPI_CTL1, SPIEN, 0,  1)
34      FIELD(PSPI_CTL1, MOD,   2,  1)
35      FIELD(PSPI_CTL1, EIR,   5,  1)
36      FIELD(PSPI_CTL1, EIW,   6,  1)
37      FIELD(PSPI_CTL1, SCM,   7,  1)
38      FIELD(PSPI_CTL1, SCIDL, 8,  1)
39      FIELD(PSPI_CTL1, SCDV,  9,  7)
40  REG16(PSPI_STAT, 0x4)
41      FIELD(PSPI_STAT, BSY,  0,  1)
42      FIELD(PSPI_STAT, RBF,  1,  1)
43  
npcm_pspi_update_irq(NPCMPSPIState * s)44  static void npcm_pspi_update_irq(NPCMPSPIState *s)
45  {
46      int level = 0;
47  
48      /* Only fire IRQ when the module is enabled. */
49      if (FIELD_EX16(s->regs[R_PSPI_CTL1], PSPI_CTL1, SPIEN)) {
50          /* Update interrupt as BSY is cleared. */
51          if ((!FIELD_EX16(s->regs[R_PSPI_STAT], PSPI_STAT, BSY)) &&
52              FIELD_EX16(s->regs[R_PSPI_CTL1], PSPI_CTL1, EIW)) {
53              level = 1;
54          }
55  
56          /* Update interrupt as RBF is set. */
57          if (FIELD_EX16(s->regs[R_PSPI_STAT], PSPI_STAT, RBF) &&
58              FIELD_EX16(s->regs[R_PSPI_CTL1], PSPI_CTL1, EIR)) {
59              level = 1;
60          }
61      }
62      qemu_set_irq(s->irq, level);
63  }
64  
npcm_pspi_read_data(NPCMPSPIState * s)65  static uint16_t npcm_pspi_read_data(NPCMPSPIState *s)
66  {
67      uint16_t value = s->regs[R_PSPI_DATA];
68  
69      /* Clear stat bits as the value are read out. */
70      s->regs[R_PSPI_STAT] = 0;
71  
72      return value;
73  }
74  
npcm_pspi_write_data(NPCMPSPIState * s,uint16_t data)75  static void npcm_pspi_write_data(NPCMPSPIState *s, uint16_t data)
76  {
77      uint16_t value = 0;
78  
79      if (FIELD_EX16(s->regs[R_PSPI_CTL1], PSPI_CTL1, MOD)) {
80          value = ssi_transfer(s->spi, extract16(data, 8, 8)) << 8;
81      }
82      value |= ssi_transfer(s->spi, extract16(data, 0, 8));
83      s->regs[R_PSPI_DATA] = value;
84  
85      /* Mark data as available */
86      s->regs[R_PSPI_STAT] = R_PSPI_STAT_BSY_MASK | R_PSPI_STAT_RBF_MASK;
87  }
88  
89  /* Control register read handler. */
npcm_pspi_ctrl_read(void * opaque,hwaddr addr,unsigned int size)90  static uint64_t npcm_pspi_ctrl_read(void *opaque, hwaddr addr,
91                                      unsigned int size)
92  {
93      NPCMPSPIState *s = opaque;
94      uint16_t value;
95  
96      switch (addr) {
97      case A_PSPI_DATA:
98          value = npcm_pspi_read_data(s);
99          break;
100  
101      case A_PSPI_CTL1:
102          value = s->regs[R_PSPI_CTL1];
103          break;
104  
105      case A_PSPI_STAT:
106          value = s->regs[R_PSPI_STAT];
107          break;
108  
109      default:
110          qemu_log_mask(LOG_GUEST_ERROR,
111                        "%s: write to invalid offset 0x%" PRIx64 "\n",
112                        DEVICE(s)->canonical_path, addr);
113          return 0;
114      }
115      trace_npcm_pspi_ctrl_read(DEVICE(s)->canonical_path, addr, value);
116      npcm_pspi_update_irq(s);
117  
118      return value;
119  }
120  
121  /* Control register write handler. */
npcm_pspi_ctrl_write(void * opaque,hwaddr addr,uint64_t v,unsigned int size)122  static void npcm_pspi_ctrl_write(void *opaque, hwaddr addr, uint64_t v,
123                                   unsigned int size)
124  {
125      NPCMPSPIState *s = opaque;
126      uint16_t value = v;
127  
128      trace_npcm_pspi_ctrl_write(DEVICE(s)->canonical_path, addr, value);
129  
130      switch (addr) {
131      case A_PSPI_DATA:
132          npcm_pspi_write_data(s, value);
133          break;
134  
135      case A_PSPI_CTL1:
136          s->regs[R_PSPI_CTL1] = value;
137          break;
138  
139      case A_PSPI_STAT:
140          qemu_log_mask(LOG_GUEST_ERROR,
141                        "%s: write to read-only register PSPI_STAT: 0x%08"
142                        PRIx64 "\n", DEVICE(s)->canonical_path, v);
143          break;
144  
145      default:
146          qemu_log_mask(LOG_GUEST_ERROR,
147                        "%s: write to invalid offset 0x%" PRIx64 "\n",
148                        DEVICE(s)->canonical_path, addr);
149          return;
150      }
151      npcm_pspi_update_irq(s);
152  }
153  
154  static const MemoryRegionOps npcm_pspi_ctrl_ops = {
155      .read = npcm_pspi_ctrl_read,
156      .write = npcm_pspi_ctrl_write,
157      .endianness = DEVICE_LITTLE_ENDIAN,
158      .valid = {
159          .min_access_size = 1,
160          .max_access_size = 2,
161          .unaligned = false,
162      },
163      .impl = {
164          .min_access_size = 2,
165          .max_access_size = 2,
166          .unaligned = false,
167      },
168  };
169  
npcm_pspi_enter_reset(Object * obj,ResetType type)170  static void npcm_pspi_enter_reset(Object *obj, ResetType type)
171  {
172      NPCMPSPIState *s = NPCM_PSPI(obj);
173  
174      trace_npcm_pspi_enter_reset(DEVICE(obj)->canonical_path, type);
175      memset(s->regs, 0, sizeof(s->regs));
176  }
177  
npcm_pspi_realize(DeviceState * dev,Error ** errp)178  static void npcm_pspi_realize(DeviceState *dev, Error **errp)
179  {
180      NPCMPSPIState *s = NPCM_PSPI(dev);
181      SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
182      Object *obj = OBJECT(dev);
183  
184      s->spi = ssi_create_bus(dev, "pspi");
185      memory_region_init_io(&s->mmio, obj, &npcm_pspi_ctrl_ops, s,
186                            "mmio", 4 * KiB);
187      sysbus_init_mmio(sbd, &s->mmio);
188      sysbus_init_irq(sbd, &s->irq);
189  }
190  
191  static const VMStateDescription vmstate_npcm_pspi = {
192      .name = "npcm-pspi",
193      .version_id = 0,
194      .minimum_version_id = 0,
195      .fields = (const VMStateField[]) {
196          VMSTATE_UINT16_ARRAY(regs, NPCMPSPIState, NPCM_PSPI_NR_REGS),
197          VMSTATE_END_OF_LIST(),
198      },
199  };
200  
201  
npcm_pspi_class_init(ObjectClass * klass,void * data)202  static void npcm_pspi_class_init(ObjectClass *klass, void *data)
203  {
204      ResettableClass *rc = RESETTABLE_CLASS(klass);
205      DeviceClass *dc = DEVICE_CLASS(klass);
206  
207      dc->desc = "NPCM Peripheral SPI Module";
208      dc->realize = npcm_pspi_realize;
209      dc->vmsd = &vmstate_npcm_pspi;
210      rc->phases.enter = npcm_pspi_enter_reset;
211  }
212  
213  static const TypeInfo npcm_pspi_types[] = {
214      {
215          .name = TYPE_NPCM_PSPI,
216          .parent = TYPE_SYS_BUS_DEVICE,
217          .instance_size = sizeof(NPCMPSPIState),
218          .class_init = npcm_pspi_class_init,
219      },
220  };
221  DEFINE_TYPES(npcm_pspi_types);
222