11e0228fdSKrzysztof Kozlowski /*
21e0228fdSKrzysztof Kozlowski * Exynos4210 Clock Controller Emulation
31e0228fdSKrzysztof Kozlowski *
41e0228fdSKrzysztof Kozlowski * Copyright (c) 2017 Krzysztof Kozlowski <krzk@kernel.org>
51e0228fdSKrzysztof Kozlowski *
61e0228fdSKrzysztof Kozlowski * This program is free software; you can redistribute it and/or modify it
71e0228fdSKrzysztof Kozlowski * under the terms of the GNU General Public License as published by the
81e0228fdSKrzysztof Kozlowski * Free Software Foundation; either version 2 of the License, or
91e0228fdSKrzysztof Kozlowski * (at your option) any later version.
101e0228fdSKrzysztof Kozlowski *
111e0228fdSKrzysztof Kozlowski * This program is distributed in the hope that it will be useful, but WITHOUT
121e0228fdSKrzysztof Kozlowski * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
131e0228fdSKrzysztof Kozlowski * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
141e0228fdSKrzysztof Kozlowski * for more details.
151e0228fdSKrzysztof Kozlowski *
161e0228fdSKrzysztof Kozlowski * You should have received a copy of the GNU General Public License along
171e0228fdSKrzysztof Kozlowski * with this program; if not, see <http://www.gnu.org/licenses/>.
181e0228fdSKrzysztof Kozlowski */
191e0228fdSKrzysztof Kozlowski
201e0228fdSKrzysztof Kozlowski #include "qemu/osdep.h"
211e0228fdSKrzysztof Kozlowski #include "hw/sysbus.h"
22d6454270SMarkus Armbruster #include "migration/vmstate.h"
231e0228fdSKrzysztof Kozlowski #include "qemu/log.h"
240b8fa32fSMarkus Armbruster #include "qemu/module.h"
25db1015e9SEduardo Habkost #include "qom/object.h"
261e0228fdSKrzysztof Kozlowski
271e0228fdSKrzysztof Kozlowski #define TYPE_EXYNOS4210_CLK "exynos4210.clk"
288063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(Exynos4210ClkState, EXYNOS4210_CLK)
291e0228fdSKrzysztof Kozlowski
301e0228fdSKrzysztof Kozlowski #define CLK_PLL_LOCKED BIT(29)
311e0228fdSKrzysztof Kozlowski
321e0228fdSKrzysztof Kozlowski #define EXYNOS4210_CLK_REGS_MEM_SIZE 0x15104
331e0228fdSKrzysztof Kozlowski
341e0228fdSKrzysztof Kozlowski typedef struct Exynos4210Reg {
351e0228fdSKrzysztof Kozlowski const char *name; /* for debug only */
361e0228fdSKrzysztof Kozlowski uint32_t offset;
371e0228fdSKrzysztof Kozlowski uint32_t reset_value;
381e0228fdSKrzysztof Kozlowski } Exynos4210Reg;
391e0228fdSKrzysztof Kozlowski
401e0228fdSKrzysztof Kozlowski /* Clock controller register base: 0x10030000 */
411e0228fdSKrzysztof Kozlowski static const Exynos4210Reg exynos4210_clk_regs[] = {
421e0228fdSKrzysztof Kozlowski {"EPLL_LOCK", 0xc010, 0x00000fff},
431e0228fdSKrzysztof Kozlowski {"VPLL_LOCK", 0xc020, 0x00000fff},
441e0228fdSKrzysztof Kozlowski {"EPLL_CON0", 0xc110, 0x00300301 | CLK_PLL_LOCKED},
451e0228fdSKrzysztof Kozlowski {"EPLL_CON1", 0xc114, 0x00000000},
461e0228fdSKrzysztof Kozlowski {"VPLL_CON0", 0xc120, 0x00240201 | CLK_PLL_LOCKED},
471e0228fdSKrzysztof Kozlowski {"VPLL_CON1", 0xc124, 0x66010464},
481e0228fdSKrzysztof Kozlowski {"APLL_LOCK", 0x14000, 0x00000fff},
491e0228fdSKrzysztof Kozlowski {"MPLL_LOCK", 0x14004, 0x00000fff},
501e0228fdSKrzysztof Kozlowski {"APLL_CON0", 0x14100, 0x00c80601 | CLK_PLL_LOCKED},
511e0228fdSKrzysztof Kozlowski {"APLL_CON1", 0x14104, 0x0000001c},
521e0228fdSKrzysztof Kozlowski {"MPLL_CON0", 0x14108, 0x00c80601 | CLK_PLL_LOCKED},
531e0228fdSKrzysztof Kozlowski {"MPLL_CON1", 0x1410c, 0x0000001c},
541e0228fdSKrzysztof Kozlowski };
551e0228fdSKrzysztof Kozlowski
561e0228fdSKrzysztof Kozlowski #define EXYNOS4210_REGS_NUM ARRAY_SIZE(exynos4210_clk_regs)
571e0228fdSKrzysztof Kozlowski
58db1015e9SEduardo Habkost struct Exynos4210ClkState {
591e0228fdSKrzysztof Kozlowski SysBusDevice parent_obj;
601e0228fdSKrzysztof Kozlowski
611e0228fdSKrzysztof Kozlowski MemoryRegion iomem;
621e0228fdSKrzysztof Kozlowski uint32_t reg[EXYNOS4210_REGS_NUM];
63db1015e9SEduardo Habkost };
641e0228fdSKrzysztof Kozlowski
exynos4210_clk_read(void * opaque,hwaddr offset,unsigned size)651e0228fdSKrzysztof Kozlowski static uint64_t exynos4210_clk_read(void *opaque, hwaddr offset,
661e0228fdSKrzysztof Kozlowski unsigned size)
671e0228fdSKrzysztof Kozlowski {
681e0228fdSKrzysztof Kozlowski const Exynos4210ClkState *s = (Exynos4210ClkState *)opaque;
691e0228fdSKrzysztof Kozlowski const Exynos4210Reg *regs = exynos4210_clk_regs;
701e0228fdSKrzysztof Kozlowski unsigned int i;
711e0228fdSKrzysztof Kozlowski
721e0228fdSKrzysztof Kozlowski for (i = 0; i < EXYNOS4210_REGS_NUM; i++) {
731e0228fdSKrzysztof Kozlowski if (regs->offset == offset) {
741e0228fdSKrzysztof Kozlowski return s->reg[i];
751e0228fdSKrzysztof Kozlowski }
761e0228fdSKrzysztof Kozlowski regs++;
771e0228fdSKrzysztof Kozlowski }
781e0228fdSKrzysztof Kozlowski qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read offset 0x%04x\n",
791e0228fdSKrzysztof Kozlowski __func__, (uint32_t)offset);
801e0228fdSKrzysztof Kozlowski return 0;
811e0228fdSKrzysztof Kozlowski }
821e0228fdSKrzysztof Kozlowski
exynos4210_clk_write(void * opaque,hwaddr offset,uint64_t val,unsigned size)831e0228fdSKrzysztof Kozlowski static void exynos4210_clk_write(void *opaque, hwaddr offset,
841e0228fdSKrzysztof Kozlowski uint64_t val, unsigned size)
851e0228fdSKrzysztof Kozlowski {
861e0228fdSKrzysztof Kozlowski Exynos4210ClkState *s = (Exynos4210ClkState *)opaque;
871e0228fdSKrzysztof Kozlowski const Exynos4210Reg *regs = exynos4210_clk_regs;
881e0228fdSKrzysztof Kozlowski unsigned int i;
891e0228fdSKrzysztof Kozlowski
901e0228fdSKrzysztof Kozlowski for (i = 0; i < EXYNOS4210_REGS_NUM; i++) {
911e0228fdSKrzysztof Kozlowski if (regs->offset == offset) {
921e0228fdSKrzysztof Kozlowski s->reg[i] = val;
931e0228fdSKrzysztof Kozlowski return;
941e0228fdSKrzysztof Kozlowski }
951e0228fdSKrzysztof Kozlowski regs++;
961e0228fdSKrzysztof Kozlowski }
971e0228fdSKrzysztof Kozlowski qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write offset 0x%04x\n",
981e0228fdSKrzysztof Kozlowski __func__, (uint32_t)offset);
991e0228fdSKrzysztof Kozlowski }
1001e0228fdSKrzysztof Kozlowski
1011e0228fdSKrzysztof Kozlowski static const MemoryRegionOps exynos4210_clk_ops = {
1021e0228fdSKrzysztof Kozlowski .read = exynos4210_clk_read,
1031e0228fdSKrzysztof Kozlowski .write = exynos4210_clk_write,
1041e0228fdSKrzysztof Kozlowski .endianness = DEVICE_NATIVE_ENDIAN,
1051e0228fdSKrzysztof Kozlowski .valid = {
1061e0228fdSKrzysztof Kozlowski .min_access_size = 4,
1071e0228fdSKrzysztof Kozlowski .max_access_size = 4,
1081e0228fdSKrzysztof Kozlowski .unaligned = false
1091e0228fdSKrzysztof Kozlowski }
1101e0228fdSKrzysztof Kozlowski };
1111e0228fdSKrzysztof Kozlowski
exynos4210_clk_reset(DeviceState * dev)1121e0228fdSKrzysztof Kozlowski static void exynos4210_clk_reset(DeviceState *dev)
1131e0228fdSKrzysztof Kozlowski {
1141e0228fdSKrzysztof Kozlowski Exynos4210ClkState *s = EXYNOS4210_CLK(dev);
1151e0228fdSKrzysztof Kozlowski unsigned int i;
1161e0228fdSKrzysztof Kozlowski
1171e0228fdSKrzysztof Kozlowski /* Set default values for registers */
1181e0228fdSKrzysztof Kozlowski for (i = 0; i < EXYNOS4210_REGS_NUM; i++) {
1191e0228fdSKrzysztof Kozlowski s->reg[i] = exynos4210_clk_regs[i].reset_value;
1201e0228fdSKrzysztof Kozlowski }
1211e0228fdSKrzysztof Kozlowski }
1221e0228fdSKrzysztof Kozlowski
exynos4210_clk_init(Object * obj)1231e0228fdSKrzysztof Kozlowski static void exynos4210_clk_init(Object *obj)
1241e0228fdSKrzysztof Kozlowski {
1251e0228fdSKrzysztof Kozlowski Exynos4210ClkState *s = EXYNOS4210_CLK(obj);
1261e0228fdSKrzysztof Kozlowski SysBusDevice *dev = SYS_BUS_DEVICE(obj);
1271e0228fdSKrzysztof Kozlowski
1281e0228fdSKrzysztof Kozlowski /* memory mapping */
1291e0228fdSKrzysztof Kozlowski memory_region_init_io(&s->iomem, obj, &exynos4210_clk_ops, s,
1301e0228fdSKrzysztof Kozlowski TYPE_EXYNOS4210_CLK, EXYNOS4210_CLK_REGS_MEM_SIZE);
1311e0228fdSKrzysztof Kozlowski sysbus_init_mmio(dev, &s->iomem);
1321e0228fdSKrzysztof Kozlowski }
1331e0228fdSKrzysztof Kozlowski
1341e0228fdSKrzysztof Kozlowski static const VMStateDescription exynos4210_clk_vmstate = {
1351e0228fdSKrzysztof Kozlowski .name = TYPE_EXYNOS4210_CLK,
1361e0228fdSKrzysztof Kozlowski .version_id = 1,
1371e0228fdSKrzysztof Kozlowski .minimum_version_id = 1,
138e4ea952fSRichard Henderson .fields = (const VMStateField[]) {
1391e0228fdSKrzysztof Kozlowski VMSTATE_UINT32_ARRAY(reg, Exynos4210ClkState, EXYNOS4210_REGS_NUM),
1401e0228fdSKrzysztof Kozlowski VMSTATE_END_OF_LIST()
1411e0228fdSKrzysztof Kozlowski }
1421e0228fdSKrzysztof Kozlowski };
1431e0228fdSKrzysztof Kozlowski
exynos4210_clk_class_init(ObjectClass * klass,const void * data)144*12d1a768SPhilippe Mathieu-Daudé static void exynos4210_clk_class_init(ObjectClass *klass, const void *data)
1451e0228fdSKrzysztof Kozlowski {
1461e0228fdSKrzysztof Kozlowski DeviceClass *dc = DEVICE_CLASS(klass);
1471e0228fdSKrzysztof Kozlowski
148e3d08143SPeter Maydell device_class_set_legacy_reset(dc, exynos4210_clk_reset);
1491e0228fdSKrzysztof Kozlowski dc->vmsd = &exynos4210_clk_vmstate;
1501e0228fdSKrzysztof Kozlowski }
1511e0228fdSKrzysztof Kozlowski
1521e0228fdSKrzysztof Kozlowski static const TypeInfo exynos4210_clk_info = {
1531e0228fdSKrzysztof Kozlowski .name = TYPE_EXYNOS4210_CLK,
1541e0228fdSKrzysztof Kozlowski .parent = TYPE_SYS_BUS_DEVICE,
1551e0228fdSKrzysztof Kozlowski .instance_size = sizeof(Exynos4210ClkState),
1561e0228fdSKrzysztof Kozlowski .instance_init = exynos4210_clk_init,
1571e0228fdSKrzysztof Kozlowski .class_init = exynos4210_clk_class_init,
1581e0228fdSKrzysztof Kozlowski };
1591e0228fdSKrzysztof Kozlowski
exynos4210_clk_register(void)1601e0228fdSKrzysztof Kozlowski static void exynos4210_clk_register(void)
1611e0228fdSKrzysztof Kozlowski {
1621e0228fdSKrzysztof Kozlowski qemu_log_mask(LOG_GUEST_ERROR, "Clock init\n");
1631e0228fdSKrzysztof Kozlowski type_register_static(&exynos4210_clk_info);
1641e0228fdSKrzysztof Kozlowski }
1651e0228fdSKrzysztof Kozlowski
1661e0228fdSKrzysztof Kozlowski type_init(exynos4210_clk_register)
167