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 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 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 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 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 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 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