149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini * ARM dummy L210, L220, PL310 cache controller.
349ab747fSPaolo Bonzini *
449ab747fSPaolo Bonzini * Copyright (c) 2010-2012 Calxeda
549ab747fSPaolo Bonzini *
649ab747fSPaolo Bonzini * This program is free software; you can redistribute it and/or modify it
749ab747fSPaolo Bonzini * under the terms and conditions of the GNU General Public License,
849ab747fSPaolo Bonzini * version 2 or any later version, as published by the Free Software
949ab747fSPaolo Bonzini * Foundation.
1049ab747fSPaolo Bonzini *
1149ab747fSPaolo Bonzini * This program is distributed in the hope it will be useful, but WITHOUT
1249ab747fSPaolo Bonzini * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1349ab747fSPaolo Bonzini * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
1449ab747fSPaolo Bonzini * more details.
1549ab747fSPaolo Bonzini *
1649ab747fSPaolo Bonzini * You should have received a copy of the GNU General Public License along with
1749ab747fSPaolo Bonzini * this program. If not, see <http://www.gnu.org/licenses/>.
1849ab747fSPaolo Bonzini *
1949ab747fSPaolo Bonzini */
2049ab747fSPaolo Bonzini
210d1c9782SPeter Maydell #include "qemu/osdep.h"
22a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
2349ab747fSPaolo Bonzini #include "hw/sysbus.h"
24d6454270SMarkus Armbruster #include "migration/vmstate.h"
2503dd024fSPaolo Bonzini #include "qemu/log.h"
260b8fa32fSMarkus Armbruster #include "qemu/module.h"
27db1015e9SEduardo Habkost #include "qom/object.h"
2849ab747fSPaolo Bonzini
2949ab747fSPaolo Bonzini /* L2C-310 r3p2 */
3049ab747fSPaolo Bonzini #define CACHE_ID 0x410000c8
3149ab747fSPaolo Bonzini
320e8982e9SAndreas Färber #define TYPE_ARM_L2X0 "l2x0"
338063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(L2x0State, ARM_L2X0)
340e8982e9SAndreas Färber
35db1015e9SEduardo Habkost struct L2x0State {
360e8982e9SAndreas Färber SysBusDevice parent_obj;
370e8982e9SAndreas Färber
3849ab747fSPaolo Bonzini MemoryRegion iomem;
3949ab747fSPaolo Bonzini uint32_t cache_type;
4049ab747fSPaolo Bonzini uint32_t ctrl;
4149ab747fSPaolo Bonzini uint32_t aux_ctrl;
4249ab747fSPaolo Bonzini uint32_t data_ctrl;
4349ab747fSPaolo Bonzini uint32_t tag_ctrl;
4449ab747fSPaolo Bonzini uint32_t filter_start;
4549ab747fSPaolo Bonzini uint32_t filter_end;
46db1015e9SEduardo Habkost };
4749ab747fSPaolo Bonzini
4849ab747fSPaolo Bonzini static const VMStateDescription vmstate_l2x0 = {
4949ab747fSPaolo Bonzini .name = "l2x0",
5049ab747fSPaolo Bonzini .version_id = 1,
5149ab747fSPaolo Bonzini .minimum_version_id = 1,
52e4ea952fSRichard Henderson .fields = (const VMStateField[]) {
53ae1953d0SAndreas Färber VMSTATE_UINT32(ctrl, L2x0State),
54ae1953d0SAndreas Färber VMSTATE_UINT32(aux_ctrl, L2x0State),
55ae1953d0SAndreas Färber VMSTATE_UINT32(data_ctrl, L2x0State),
56ae1953d0SAndreas Färber VMSTATE_UINT32(tag_ctrl, L2x0State),
57ae1953d0SAndreas Färber VMSTATE_UINT32(filter_start, L2x0State),
58ae1953d0SAndreas Färber VMSTATE_UINT32(filter_end, L2x0State),
5949ab747fSPaolo Bonzini VMSTATE_END_OF_LIST()
6049ab747fSPaolo Bonzini }
6149ab747fSPaolo Bonzini };
6249ab747fSPaolo Bonzini
6349ab747fSPaolo Bonzini
l2x0_priv_read(void * opaque,hwaddr offset,unsigned size)6449ab747fSPaolo Bonzini static uint64_t l2x0_priv_read(void *opaque, hwaddr offset,
6549ab747fSPaolo Bonzini unsigned size)
6649ab747fSPaolo Bonzini {
6749ab747fSPaolo Bonzini uint32_t cache_data;
68ae1953d0SAndreas Färber L2x0State *s = (L2x0State *)opaque;
6949ab747fSPaolo Bonzini offset &= 0xfff;
7049ab747fSPaolo Bonzini if (offset >= 0x730 && offset < 0x800) {
7149ab747fSPaolo Bonzini return 0; /* cache ops complete */
7249ab747fSPaolo Bonzini }
7349ab747fSPaolo Bonzini switch (offset) {
7449ab747fSPaolo Bonzini case 0:
7549ab747fSPaolo Bonzini return CACHE_ID;
7649ab747fSPaolo Bonzini case 0x4:
7749ab747fSPaolo Bonzini /* aux_ctrl values affect cache_type values */
7849ab747fSPaolo Bonzini cache_data = (s->aux_ctrl & (7 << 17)) >> 15;
7949ab747fSPaolo Bonzini cache_data |= (s->aux_ctrl & (1 << 16)) >> 16;
8049ab747fSPaolo Bonzini return s->cache_type |= (cache_data << 18) | (cache_data << 6);
8149ab747fSPaolo Bonzini case 0x100:
8249ab747fSPaolo Bonzini return s->ctrl;
8349ab747fSPaolo Bonzini case 0x104:
8449ab747fSPaolo Bonzini return s->aux_ctrl;
8549ab747fSPaolo Bonzini case 0x108:
8649ab747fSPaolo Bonzini return s->tag_ctrl;
8749ab747fSPaolo Bonzini case 0x10C:
8849ab747fSPaolo Bonzini return s->data_ctrl;
8949ab747fSPaolo Bonzini case 0xC00:
9049ab747fSPaolo Bonzini return s->filter_start;
9149ab747fSPaolo Bonzini case 0xC04:
9249ab747fSPaolo Bonzini return s->filter_end;
9349ab747fSPaolo Bonzini case 0xF40:
9449ab747fSPaolo Bonzini return 0;
9549ab747fSPaolo Bonzini case 0xF60:
9649ab747fSPaolo Bonzini return 0;
9749ab747fSPaolo Bonzini case 0xF80:
9849ab747fSPaolo Bonzini return 0;
9949ab747fSPaolo Bonzini default:
10049ab747fSPaolo Bonzini qemu_log_mask(LOG_GUEST_ERROR,
10149ab747fSPaolo Bonzini "l2x0_priv_read: Bad offset %x\n", (int)offset);
10249ab747fSPaolo Bonzini break;
10349ab747fSPaolo Bonzini }
10449ab747fSPaolo Bonzini return 0;
10549ab747fSPaolo Bonzini }
10649ab747fSPaolo Bonzini
l2x0_priv_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)10749ab747fSPaolo Bonzini static void l2x0_priv_write(void *opaque, hwaddr offset,
10849ab747fSPaolo Bonzini uint64_t value, unsigned size)
10949ab747fSPaolo Bonzini {
110ae1953d0SAndreas Färber L2x0State *s = (L2x0State *)opaque;
11149ab747fSPaolo Bonzini offset &= 0xfff;
11249ab747fSPaolo Bonzini if (offset >= 0x730 && offset < 0x800) {
11349ab747fSPaolo Bonzini /* ignore */
11449ab747fSPaolo Bonzini return;
11549ab747fSPaolo Bonzini }
11649ab747fSPaolo Bonzini switch (offset) {
11749ab747fSPaolo Bonzini case 0x100:
11849ab747fSPaolo Bonzini s->ctrl = value & 1;
11949ab747fSPaolo Bonzini break;
12049ab747fSPaolo Bonzini case 0x104:
12149ab747fSPaolo Bonzini s->aux_ctrl = value;
12249ab747fSPaolo Bonzini break;
12349ab747fSPaolo Bonzini case 0x108:
12449ab747fSPaolo Bonzini s->tag_ctrl = value;
12549ab747fSPaolo Bonzini break;
12649ab747fSPaolo Bonzini case 0x10C:
12749ab747fSPaolo Bonzini s->data_ctrl = value;
12849ab747fSPaolo Bonzini break;
12949ab747fSPaolo Bonzini case 0xC00:
13049ab747fSPaolo Bonzini s->filter_start = value;
13149ab747fSPaolo Bonzini break;
13249ab747fSPaolo Bonzini case 0xC04:
13349ab747fSPaolo Bonzini s->filter_end = value;
13449ab747fSPaolo Bonzini break;
13549ab747fSPaolo Bonzini case 0xF40:
13649ab747fSPaolo Bonzini return;
13749ab747fSPaolo Bonzini case 0xF60:
13849ab747fSPaolo Bonzini return;
13949ab747fSPaolo Bonzini case 0xF80:
14049ab747fSPaolo Bonzini return;
14149ab747fSPaolo Bonzini default:
14249ab747fSPaolo Bonzini qemu_log_mask(LOG_GUEST_ERROR,
14349ab747fSPaolo Bonzini "l2x0_priv_write: Bad offset %x\n", (int)offset);
14449ab747fSPaolo Bonzini break;
14549ab747fSPaolo Bonzini }
14649ab747fSPaolo Bonzini }
14749ab747fSPaolo Bonzini
l2x0_priv_reset(DeviceState * dev)14849ab747fSPaolo Bonzini static void l2x0_priv_reset(DeviceState *dev)
14949ab747fSPaolo Bonzini {
1500e8982e9SAndreas Färber L2x0State *s = ARM_L2X0(dev);
15149ab747fSPaolo Bonzini
15249ab747fSPaolo Bonzini s->ctrl = 0;
15349ab747fSPaolo Bonzini s->aux_ctrl = 0x02020000;
15449ab747fSPaolo Bonzini s->tag_ctrl = 0;
15549ab747fSPaolo Bonzini s->data_ctrl = 0;
15649ab747fSPaolo Bonzini s->filter_start = 0;
15749ab747fSPaolo Bonzini s->filter_end = 0;
15849ab747fSPaolo Bonzini }
15949ab747fSPaolo Bonzini
16049ab747fSPaolo Bonzini static const MemoryRegionOps l2x0_mem_ops = {
16149ab747fSPaolo Bonzini .read = l2x0_priv_read,
16249ab747fSPaolo Bonzini .write = l2x0_priv_write,
16349ab747fSPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN,
16449ab747fSPaolo Bonzini };
16549ab747fSPaolo Bonzini
l2x0_priv_init(Object * obj)166da8060bfSxiaoqiang zhao static void l2x0_priv_init(Object *obj)
16749ab747fSPaolo Bonzini {
168da8060bfSxiaoqiang zhao L2x0State *s = ARM_L2X0(obj);
169da8060bfSxiaoqiang zhao SysBusDevice *dev = SYS_BUS_DEVICE(obj);
17049ab747fSPaolo Bonzini
171da8060bfSxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &l2x0_mem_ops, s,
1723c161542SPaolo Bonzini "l2x0_cc", 0x1000);
17349ab747fSPaolo Bonzini sysbus_init_mmio(dev, &s->iomem);
17449ab747fSPaolo Bonzini }
17549ab747fSPaolo Bonzini
17649ab747fSPaolo Bonzini static Property l2x0_properties[] = {
177ae1953d0SAndreas Färber DEFINE_PROP_UINT32("cache-type", L2x0State, cache_type, 0x1c100100),
17849ab747fSPaolo Bonzini DEFINE_PROP_END_OF_LIST(),
17949ab747fSPaolo Bonzini };
18049ab747fSPaolo Bonzini
l2x0_class_init(ObjectClass * klass,void * data)18149ab747fSPaolo Bonzini static void l2x0_class_init(ObjectClass *klass, void *data)
18249ab747fSPaolo Bonzini {
18349ab747fSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass);
18449ab747fSPaolo Bonzini
18549ab747fSPaolo Bonzini dc->vmsd = &vmstate_l2x0;
1864f67d30bSMarc-André Lureau device_class_set_props(dc, l2x0_properties);
187*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, l2x0_priv_reset);
18849ab747fSPaolo Bonzini }
18949ab747fSPaolo Bonzini
19049ab747fSPaolo Bonzini static const TypeInfo l2x0_info = {
1910e8982e9SAndreas Färber .name = TYPE_ARM_L2X0,
19249ab747fSPaolo Bonzini .parent = TYPE_SYS_BUS_DEVICE,
193ae1953d0SAndreas Färber .instance_size = sizeof(L2x0State),
194da8060bfSxiaoqiang zhao .instance_init = l2x0_priv_init,
19549ab747fSPaolo Bonzini .class_init = l2x0_class_init,
19649ab747fSPaolo Bonzini };
19749ab747fSPaolo Bonzini
l2x0_register_types(void)19849ab747fSPaolo Bonzini static void l2x0_register_types(void)
19949ab747fSPaolo Bonzini {
20049ab747fSPaolo Bonzini type_register_static(&l2x0_info);
20149ab747fSPaolo Bonzini }
20249ab747fSPaolo Bonzini
20349ab747fSPaolo Bonzini type_init(l2x0_register_types)
204