xref: /qemu/hw/misc/exynos4210_clk.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
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