| // SPDX-License-Identifier: MIT | |
| /* | |
| * Copyright 2012 Red Hat Inc. | |
| * | |
| * Permission is hereby granted, free of charge, to any person obtaining a | |
| * copy of this software and associated documentation files (the | |
| * "Software"), to deal in the Software without restriction, including | |
| * without limitation the rights to use, copy, modify, merge, publish, | |
| * distribute, sub license, and/or sell copies of the Software, and to | |
| * permit persons to whom the Software is furnished to do so, subject to | |
| * the following conditions: | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | |
| * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, | |
| * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
| * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
| * USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| * | |
| * The above copyright notice and this permission notice (including the | |
| * next paragraph) shall be included in all copies or substantial portions | |
| * of the Software. | |
| */ | |
| /* | |
| * Authors: Dave Airlie <airlied@redhat.com> | |
| */ | |
| #include <linux/delay.h> | |
| #include <linux/pci.h> | |
| #include <drm/drm_drv.h> | |
| #include <drm/drm_print.h> | |
| #include "ast_drv.h" | |
| #include "ast_post.h" | |
| /* | |
| * POST | |
| */ | |
| /* | |
| * AST2500 DRAM settings modules | |
| */ | |
| #define REGTBL_NUM 17 | |
| #define REGIDX_010 0 | |
| #define REGIDX_014 1 | |
| #define REGIDX_018 2 | |
| #define REGIDX_020 3 | |
| #define REGIDX_024 4 | |
| #define REGIDX_02C 5 | |
| #define REGIDX_030 6 | |
| #define REGIDX_214 7 | |
| #define REGIDX_2E0 8 | |
| #define REGIDX_2E4 9 | |
| #define REGIDX_2E8 10 | |
| #define REGIDX_2EC 11 | |
| #define REGIDX_2F0 12 | |
| #define REGIDX_2F4 13 | |
| #define REGIDX_2F8 14 | |
| #define REGIDX_RFC 15 | |
| #define REGIDX_PLL 16 | |
| static const u32 ast2500_ddr3_1600_timing_table[REGTBL_NUM] = { | |
| 0x64604D38, /* 0x010 */ | |
| 0x29690599, /* 0x014 */ | |
| 0x00000300, /* 0x018 */ | |
| 0x00000000, /* 0x020 */ | |
| 0x00000000, /* 0x024 */ | |
| 0x02181E70, /* 0x02C */ | |
| 0x00000040, /* 0x030 */ | |
| 0x00000024, /* 0x214 */ | |
| 0x02001300, /* 0x2E0 */ | |
| 0x0E0000A0, /* 0x2E4 */ | |
| 0x000E001B, /* 0x2E8 */ | |
| 0x35B8C105, /* 0x2EC */ | |
| 0x08090408, /* 0x2F0 */ | |
| 0x9B000800, /* 0x2F4 */ | |
| 0x0E400A00, /* 0x2F8 */ | |
| 0x9971452F, /* tRFC */ | |
| 0x000071C1 /* PLL */ | |
| }; | |
| static const u32 ast2500_ddr4_1600_timing_table[REGTBL_NUM] = { | |
| 0x63604E37, /* 0x010 */ | |
| 0xE97AFA99, /* 0x014 */ | |
| 0x00019000, /* 0x018 */ | |
| 0x08000000, /* 0x020 */ | |
| 0x00000400, /* 0x024 */ | |
| 0x00000410, /* 0x02C */ | |
| 0x00000101, /* 0x030 */ | |
| 0x00000024, /* 0x214 */ | |
| 0x03002900, /* 0x2E0 */ | |
| 0x0E0000A0, /* 0x2E4 */ | |
| 0x000E001C, /* 0x2E8 */ | |
| 0x35B8C106, /* 0x2EC */ | |
| 0x08080607, /* 0x2F0 */ | |
| 0x9B000900, /* 0x2F4 */ | |
| 0x0E400A00, /* 0x2F8 */ | |
| 0x99714545, /* tRFC */ | |
| 0x000071C1 /* PLL */ | |
| }; | |
| #define TIMEOUT 5000000 | |
| void ast_2500_patch_ahb(void __iomem *regs) | |
| { | |
| u32 data; | |
| /* Clear bus lock condition */ | |
| __ast_moutdwm(regs, AST_REG_AHBC00, AST_REG_AHBC00_PROTECT_KEY); | |
| __ast_moutdwm(regs, AST_REG_AHBC84, 0x00010000); | |
| __ast_moutdwm(regs, AST_REG_AHBC88, 0x00000000); | |
| __ast_moutdwm(regs, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY); | |
| data = __ast_mindwm(regs, AST_REG_SCU070); | |
| if (data & 0x08000000) { /* check fast reset */ | |
| /* | |
| * If "Fast restet" is enabled for ARM-ICE debugger, | |
| * then WDT needs to enable, that | |
| * WDT04 is WDT#1 Reload reg. | |
| * WDT08 is WDT#1 counter restart reg to avoid system deadlock | |
| * WDT0C is WDT#1 control reg | |
| * [6:5]:= 01:Full chip | |
| * [4]:= 1:1MHz clock source | |
| * [1]:= 1:WDT will be cleeared and disabled after timeout occurs | |
| * [0]:= 1:WDT enable | |
| */ | |
| __ast_moutdwm(regs, AST_REG_WDT04(0), 0x00000010); | |
| __ast_moutdwm(regs, AST_REG_WDT08(0), 0x00004755); | |
| __ast_moutdwm(regs, AST_REG_WDT0C(0), 0x00000033); | |
| udelay(1000); | |
| } | |
| do { | |
| __ast_moutdwm(regs, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY); | |
| data = __ast_mindwm(regs, AST_REG_SCU000); | |
| } while (data != 1); | |
| __ast_moutdwm(regs, AST_REG_SCU07C, 0x08000000); /* clear fast reset */ | |
| } | |
| static bool mmc_test_single_2500(struct ast_device *ast, u32 datagen) | |
| { | |
| return mmc_test(ast, datagen, 0x85); | |
| } | |
| static bool cbr_test_2500(struct ast_device *ast) | |
| { | |
| ast_moutdwm(ast, AST_REG_MCR74, 0x0000FFFF); | |
| ast_moutdwm(ast, AST_REG_MCR7C, 0xFF00FF00); | |
| if (!mmc_test_burst(ast, 0)) | |
| return false; | |
| if (!mmc_test_single_2500(ast, 0)) | |
| return false; | |
| return true; | |
| } | |
| static bool ddr_test_2500(struct ast_device *ast) | |
| { | |
| ast_moutdwm(ast, AST_REG_MCR74, 0x0000FFFF); | |
| ast_moutdwm(ast, AST_REG_MCR7C, 0xFF00FF00); | |
| if (!mmc_test_burst(ast, 0)) | |
| return false; | |
| if (!mmc_test_burst(ast, 1)) | |
| return false; | |
| if (!mmc_test_burst(ast, 2)) | |
| return false; | |
| if (!mmc_test_burst(ast, 3)) | |
| return false; | |
| if (!mmc_test_single_2500(ast, 0)) | |
| return false; | |
| return true; | |
| } | |
| static void ddr_init_common_2500(struct ast_device *ast) | |
| { | |
| ast_moutdwm(ast, AST_REG_MCR34, 0x00020080); | |
| ast_moutdwm(ast, AST_REG_MCR08, 0x2003000F); | |
| ast_moutdwm(ast, AST_REG_MCR38, 0x00000FFF); | |
| ast_moutdwm(ast, AST_REG_MCR40, 0x88448844); | |
| ast_moutdwm(ast, AST_REG_MCR44, 0x24422288); | |
| ast_moutdwm(ast, AST_REG_MCR48, 0x22222222); | |
| ast_moutdwm(ast, AST_REG_MCR4C, 0x22222222); | |
| ast_moutdwm(ast, AST_REG_MCR50, 0x80000000); | |
| ast_moutdwm(ast, AST_REG_MCR208, 0x00000000); | |
| ast_moutdwm(ast, AST_REG_MCR218, 0x00000000); | |
| ast_moutdwm(ast, AST_REG_MCR220, 0x00000000); | |
| ast_moutdwm(ast, AST_REG_MCR228, 0x00000000); | |
| ast_moutdwm(ast, AST_REG_MCR230, 0x00000000); | |
| ast_moutdwm(ast, AST_REG_MCR2A8, 0x00000000); | |
| ast_moutdwm(ast, AST_REG_MCR2B0, 0x00000000); | |
| ast_moutdwm(ast, AST_REG_MCR240, 0x86000000); | |
| ast_moutdwm(ast, AST_REG_MCR244, 0x00008600); | |
| ast_moutdwm(ast, AST_REG_MCR248, 0x80000000); | |
| ast_moutdwm(ast, AST_REG_MCR24C, 0x80808080); | |
| } | |
| static void ddr_phy_init_2500(struct ast_device *ast) | |
| { | |
| u32 data, pass, timecnt; | |
| pass = 0; | |
| ast_moutdwm(ast, AST_REG_MCR60, 0x00000005); | |
| while (!pass) { | |
| for (timecnt = 0; timecnt < TIMEOUT; timecnt++) { | |
| data = ast_mindwm(ast, AST_REG_MCR60) & 0x1; | |
| if (!data) | |
| break; | |
| } | |
| if (timecnt != TIMEOUT) { | |
| data = ast_mindwm(ast, AST_REG_MCR300) & 0x000A0000; | |
| if (!data) | |
| pass = 1; | |
| } | |
| if (!pass) { | |
| ast_moutdwm(ast, AST_REG_MCR60, 0x00000000); | |
| udelay(10); /* delay 10 us */ | |
| ast_moutdwm(ast, AST_REG_MCR60, 0x00000005); | |
| } | |
| } | |
| ast_moutdwm(ast, AST_REG_MCR60, 0x00000006); | |
| } | |
| /* | |
| * TODO: Review and fix the comments. The function below only detects | |
| * up to 1 GiB of SDRAM. | |
| * | |
| * Check DRAM Size | |
| * 1Gb : 0x80000000 ~ 0x87FFFFFF | |
| * 2Gb : 0x80000000 ~ 0x8FFFFFFF | |
| * 4Gb : 0x80000000 ~ 0x9FFFFFFF | |
| * 8Gb : 0x80000000 ~ 0xBFFFFFFF | |
| */ | |
| static void check_dram_size_2500(struct ast_device *ast, u32 tRFC) | |
| { | |
| u32 reg_04, reg_14; | |
| reg_04 = ast_mindwm(ast, AST_REG_MCR04) & 0xfffffffc; | |
| reg_14 = ast_mindwm(ast, AST_REG_MCR14) & 0xffffff00; | |
| ast_moutdwm(ast, AST_SDRAM(0x20100000), 0x41424344); | |
| ast_moutdwm(ast, AST_SDRAM(0x10100000), 0x35363738); | |
| ast_moutdwm(ast, AST_SDRAM(0x08100000), 0x292A2B2C); | |
| ast_moutdwm(ast, AST_SDRAM(0x00100000), 0x1D1E1F10); | |
| /* Check 8Gbit */ | |
| if (ast_mindwm(ast, AST_SDRAM(0x20100000)) == 0x41424344) { | |
| reg_04 |= 0x03; | |
| reg_14 |= (tRFC >> 24) & 0xFF; | |
| /* Check 4Gbit */ | |
| } else if (ast_mindwm(ast, AST_SDRAM(0x10100000)) == 0x35363738) { | |
| reg_04 |= 0x02; | |
| reg_14 |= (tRFC >> 16) & 0xFF; | |
| /* Check 2Gbit */ | |
| } else if (ast_mindwm(ast, AST_SDRAM(0x08100000)) == 0x292A2B2C) { | |
| reg_04 |= 0x01; | |
| reg_14 |= (tRFC >> 8) & 0xFF; | |
| } else { | |
| reg_14 |= tRFC & 0xFF; | |
| } | |
| ast_moutdwm(ast, AST_REG_MCR04, reg_04); | |
| ast_moutdwm(ast, AST_REG_MCR14, reg_14); | |
| } | |
| static void enable_cache_2500(struct ast_device *ast) | |
| { | |
| u32 reg_04, data; | |
| reg_04 = ast_mindwm(ast, AST_REG_MCR04); | |
| ast_moutdwm(ast, AST_REG_MCR04, reg_04 | 0x1000); | |
| do | |
| data = ast_mindwm(ast, AST_REG_MCR04); | |
| while (!(data & 0x80000)); | |
| ast_moutdwm(ast, AST_REG_MCR04, reg_04 | 0x400); | |
| } | |
| static void set_mpll_2500(struct ast_device *ast) | |
| { | |
| u32 mcr, data, param; | |
| /* Reset MMC */ | |
| ast_moutdwm(ast, AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY); | |
| ast_moutdwm(ast, AST_REG_MCR34, 0x00020080); | |
| for (mcr = AST_REG_MCR04; mcr <= AST_REG_MCR8C; mcr += 4) | |
| ast_moutdwm(ast, mcr, 0x00000000); | |
| ast_moutdwm(ast, AST_REG_MCR34, 0x00020000); | |
| ast_moutdwm(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY); | |
| data = ast_mindwm(ast, AST_REG_SCU070) & 0x00800000; | |
| if (data) { | |
| /* CLKIN = 25MHz */ | |
| param = 0x930023E0; | |
| ast_moutdwm(ast, AST_REG_SCU160, 0x00011320); | |
| } else { | |
| /* CLKIN = 24MHz */ | |
| param = 0x93002400; | |
| } | |
| ast_moutdwm(ast, AST_REG_SCU020, param); | |
| udelay(100); | |
| } | |
| static void reset_mmc_2500(struct ast_device *ast) | |
| { | |
| ast_moutdwm(ast, AST_REG_WDT1C(1), 0x00000004); | |
| ast_moutdwm(ast, AST_REG_WDT04(1), 0x00000001); | |
| ast_moutdwm(ast, AST_REG_WDT08(1), 0x00004755); | |
| ast_moutdwm(ast, AST_REG_WDT0C(1), 0x00000013); | |
| mdelay(100); | |
| ast_moutdwm(ast, AST_REG_WDT14(1), 0x00000077); | |
| ast_moutdwm(ast, AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY); | |
| } | |
| static void ddr3_init_2500(struct ast_device *ast, const u32 *ddr_table) | |
| { | |
| ast_moutdwm(ast, AST_REG_MCR04, 0x00000303); | |
| ast_moutdwm(ast, AST_REG_MCR10, ddr_table[REGIDX_010]); | |
| ast_moutdwm(ast, AST_REG_MCR14, ddr_table[REGIDX_014]); | |
| ast_moutdwm(ast, AST_REG_MCR18, ddr_table[REGIDX_018]); | |
| ast_moutdwm(ast, AST_REG_MCR20, ddr_table[REGIDX_020]); /* MODEREG4/6 */ | |
| ast_moutdwm(ast, AST_REG_MCR24, ddr_table[REGIDX_024]); /* MODEREG5 */ | |
| ast_moutdwm(ast, AST_REG_MCR2C, ddr_table[REGIDX_02C] | 0x100); /* MODEREG0/2 */ | |
| ast_moutdwm(ast, AST_REG_MCR30, ddr_table[REGIDX_030]); /* MODEREG1/3 */ | |
| /* DDR PHY Setting */ | |
| ast_moutdwm(ast, AST_REG_MCR200, 0x02492AAE); | |
| ast_moutdwm(ast, AST_REG_MCR204, 0x00001001); | |
| ast_moutdwm(ast, AST_REG_MCR20C, 0x55E00B0B); | |
| ast_moutdwm(ast, AST_REG_MCR210, 0x20000000); | |
| ast_moutdwm(ast, AST_REG_MCR214, ddr_table[REGIDX_214]); | |
| ast_moutdwm(ast, AST_REG_MCR2E0, ddr_table[REGIDX_2E0]); | |
| ast_moutdwm(ast, AST_REG_MCR2E4, ddr_table[REGIDX_2E4]); | |
| ast_moutdwm(ast, AST_REG_MCR2E8, ddr_table[REGIDX_2E8]); | |
| ast_moutdwm(ast, AST_REG_MCR2EC, ddr_table[REGIDX_2EC]); | |
| ast_moutdwm(ast, AST_REG_MCR2F0, ddr_table[REGIDX_2F0]); | |
| ast_moutdwm(ast, AST_REG_MCR2F4, ddr_table[REGIDX_2F4]); | |
| ast_moutdwm(ast, AST_REG_MCR2F8, ddr_table[REGIDX_2F8]); | |
| ast_moutdwm(ast, AST_REG_MCR290, 0x00100008); | |
| ast_moutdwm(ast, AST_REG_MCR2C0, 0x00000006); | |
| /* Controller Setting */ | |
| ast_moutdwm(ast, AST_REG_MCR34, 0x00020091); | |
| /* Wait DDR PHY init done */ | |
| ddr_phy_init_2500(ast); | |
| ast_moutdwm(ast, AST_REG_MCR120, ddr_table[REGIDX_PLL]); | |
| ast_moutdwm(ast, AST_REG_MCR0C, 0x42AA5C81); | |
| ast_moutdwm(ast, AST_REG_MCR34, 0x0001AF93); | |
| check_dram_size_2500(ast, ddr_table[REGIDX_RFC]); | |
| enable_cache_2500(ast); | |
| ast_moutdwm(ast, AST_REG_MCR1C, 0x00000008); | |
| ast_moutdwm(ast, AST_REG_MCR38, 0xFFFFFF00); | |
| } | |
| static void ddr4_init_2500(struct ast_device *ast, const u32 *ddr_table) | |
| { | |
| u32 data, data2, pass, retrycnt; | |
| u32 ddr_vref, phy_vref; | |
| u32 min_ddr_vref = 0, min_phy_vref = 0; | |
| u32 max_ddr_vref = 0, max_phy_vref = 0; | |
| ast_moutdwm(ast, AST_REG_MCR04, 0x00000313); | |
| ast_moutdwm(ast, AST_REG_MCR10, ddr_table[REGIDX_010]); | |
| ast_moutdwm(ast, AST_REG_MCR14, ddr_table[REGIDX_014]); | |
| ast_moutdwm(ast, AST_REG_MCR18, ddr_table[REGIDX_018]); | |
| ast_moutdwm(ast, AST_REG_MCR20, ddr_table[REGIDX_020]); /* MODEREG4/6 */ | |
| ast_moutdwm(ast, AST_REG_MCR24, ddr_table[REGIDX_024]); /* MODEREG5 */ | |
| ast_moutdwm(ast, AST_REG_MCR2C, ddr_table[REGIDX_02C] | 0x100); /* MODEREG0/2 */ | |
| ast_moutdwm(ast, AST_REG_MCR30, ddr_table[REGIDX_030]); /* MODEREG1/3 */ | |
| /* DDR PHY Setting */ | |
| ast_moutdwm(ast, AST_REG_MCR200, 0x42492AAE); | |
| ast_moutdwm(ast, AST_REG_MCR204, 0x09002000); | |
| ast_moutdwm(ast, AST_REG_MCR20C, 0x55E00B0B); | |
| ast_moutdwm(ast, AST_REG_MCR210, 0x20000000); | |
| ast_moutdwm(ast, AST_REG_MCR214, ddr_table[REGIDX_214]); | |
| ast_moutdwm(ast, AST_REG_MCR2E0, ddr_table[REGIDX_2E0]); | |
| ast_moutdwm(ast, AST_REG_MCR2E4, ddr_table[REGIDX_2E4]); | |
| ast_moutdwm(ast, AST_REG_MCR2E8, ddr_table[REGIDX_2E8]); | |
| ast_moutdwm(ast, AST_REG_MCR2EC, ddr_table[REGIDX_2EC]); | |
| ast_moutdwm(ast, AST_REG_MCR2F0, ddr_table[REGIDX_2F0]); | |
| ast_moutdwm(ast, AST_REG_MCR2F4, ddr_table[REGIDX_2F4]); | |
| ast_moutdwm(ast, AST_REG_MCR2F8, ddr_table[REGIDX_2F8]); | |
| ast_moutdwm(ast, AST_REG_MCR290, 0x00100008); | |
| ast_moutdwm(ast, AST_REG_MCR2C4, 0x3C183C3C); | |
| ast_moutdwm(ast, AST_REG_MCR2C8, 0x00631E0E); | |
| /* Controller Setting */ | |
| ast_moutdwm(ast, AST_REG_MCR34, 0x0001A991); | |
| /* Train PHY Vref first */ | |
| pass = 0; | |
| for (retrycnt = 0; retrycnt < 4 && pass == 0; retrycnt++) { | |
| max_phy_vref = 0x0; | |
| pass = 0; | |
| ast_moutdwm(ast, AST_REG_MCR2C0, 0x00001C06); | |
| for (phy_vref = 0x40; phy_vref < 0x80; phy_vref++) { | |
| ast_moutdwm(ast, AST_REG_MCR0C, 0x00000000); | |
| ast_moutdwm(ast, AST_REG_MCR60, 0x00000000); | |
| ast_moutdwm(ast, AST_REG_MCR2CC, phy_vref | (phy_vref << 8)); | |
| /* Fire DFI Init */ | |
| ddr_phy_init_2500(ast); | |
| ast_moutdwm(ast, AST_REG_MCR0C, 0x00005C01); | |
| if (cbr_test_2500(ast)) { | |
| pass++; | |
| data = ast_mindwm(ast, AST_REG_MCR3D0); | |
| data2 = data >> 8; | |
| data = data & 0xff; | |
| if (data > data2) | |
| data = data2; | |
| if (max_phy_vref < data) { | |
| max_phy_vref = data; | |
| min_phy_vref = phy_vref; | |
| } | |
| } else if (pass > 0) { | |
| break; | |
| } | |
| } | |
| } | |
| ast_moutdwm(ast, AST_REG_MCR2CC, min_phy_vref | (min_phy_vref << 8)); | |
| /* Train DDR Vref next */ | |
| pass = 0; | |
| for (retrycnt = 0; retrycnt < 4 && pass == 0; retrycnt++) { | |
| min_ddr_vref = 0xFF; | |
| max_ddr_vref = 0x0; | |
| pass = 0; | |
| for (ddr_vref = 0x00; ddr_vref < 0x40; ddr_vref++) { | |
| ast_moutdwm(ast, AST_REG_MCR0C, 0x00000000); | |
| ast_moutdwm(ast, AST_REG_MCR60, 0x00000000); | |
| ast_moutdwm(ast, AST_REG_MCR2C0, 0x00000006 | (ddr_vref << 8)); | |
| /* Fire DFI Init */ | |
| ddr_phy_init_2500(ast); | |
| ast_moutdwm(ast, AST_REG_MCR0C, 0x00005C01); | |
| if (cbr_test_2500(ast)) { | |
| pass++; | |
| if (min_ddr_vref > ddr_vref) | |
| min_ddr_vref = ddr_vref; | |
| if (max_ddr_vref < ddr_vref) | |
| max_ddr_vref = ddr_vref; | |
| } else if (pass != 0) { | |
| break; | |
| } | |
| } | |
| } | |
| ast_moutdwm(ast, AST_REG_MCR0C, 0x00000000); | |
| ast_moutdwm(ast, AST_REG_MCR60, 0x00000000); | |
| ddr_vref = (min_ddr_vref + max_ddr_vref + 1) >> 1; | |
| ast_moutdwm(ast, AST_REG_MCR2C0, 0x00000006 | (ddr_vref << 8)); | |
| /* Wait DDR PHY init done */ | |
| ddr_phy_init_2500(ast); | |
| ast_moutdwm(ast, AST_REG_MCR120, ddr_table[REGIDX_PLL]); | |
| ast_moutdwm(ast, AST_REG_MCR0C, 0x42AA5C81); | |
| ast_moutdwm(ast, AST_REG_MCR34, 0x0001AF93); | |
| check_dram_size_2500(ast, ddr_table[REGIDX_RFC]); | |
| enable_cache_2500(ast); | |
| ast_moutdwm(ast, AST_REG_MCR1C, 0x00000008); | |
| ast_moutdwm(ast, AST_REG_MCR38, 0xFFFFFF00); | |
| } | |
| static bool ast_dram_init_2500(struct ast_device *ast) | |
| { | |
| u32 data; | |
| u32 max_tries = 5; | |
| do { | |
| if (max_tries-- == 0) | |
| return false; | |
| set_mpll_2500(ast); | |
| reset_mmc_2500(ast); | |
| ddr_init_common_2500(ast); | |
| data = ast_mindwm(ast, AST_REG_SCU070); | |
| if (data & 0x01000000) | |
| ddr4_init_2500(ast, ast2500_ddr4_1600_timing_table); | |
| else | |
| ddr3_init_2500(ast, ast2500_ddr3_1600_timing_table); | |
| } while (!ddr_test_2500(ast)); | |
| ast_moutdwm(ast, AST_REG_SCU040, ast_mindwm(ast, AST_REG_SCU040) | 0x41); | |
| /* Patch code */ | |
| data = ast_mindwm(ast, AST_REG_SCU00C) & 0xF9FFFFFF; | |
| ast_moutdwm(ast, AST_REG_SCU00C, data | 0x10000000); | |
| return true; | |
| } | |
| static void ast_post_chip_2500(struct ast_device *ast) | |
| { | |
| struct drm_device *dev = &ast->base; | |
| u32 temp; | |
| u8 reg; | |
| reg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff); | |
| if ((reg & AST_IO_VGACRD0_VRAM_INIT_STATUS_MASK) == 0) {/* vga only */ | |
| u32 scu008; | |
| /* Clear bus lock condition */ | |
| ast_2500_patch_ahb(ast->regs); | |
| /* Disable watchdog */ | |
| ast_moutdwm(ast, AST_REG_WDT2C(0), 0x00000000); | |
| ast_moutdwm(ast, AST_REG_WDT0C(1), 0x00000000); | |
| /* | |
| * Reset USB port to patch USB unknown device issue | |
| * SCU90 is Multi-function Pin Control #5 | |
| * [29]:= 1:Enable USB2.0 Host port#1 (that the mutually shared USB2.0 Hub | |
| * port). | |
| * SCU94 is Multi-function Pin Control #6 | |
| * [14:13]:= 1x:USB2.0 Host2 controller | |
| * SCU70 is Hardware Strap reg | |
| * [23]:= 1:CLKIN is 25MHz and USBCK1 = 24/48 MHz (determined by | |
| * [18]: 0(24)/1(48) MHz) | |
| * SCU7C is Write clear reg to SCU70 | |
| * [23]:= write 1 and then SCU70[23] will be clear as 0b. | |
| */ | |
| ast_moutdwm(ast, AST_REG_SCU090, 0x20000000); | |
| ast_moutdwm(ast, AST_REG_SCU094, 0x00004000); | |
| if (ast_mindwm(ast, AST_REG_SCU070) & 0x00800000) { | |
| ast_moutdwm(ast, AST_REG_SCU07C, 0x00800000); | |
| mdelay(100); | |
| ast_moutdwm(ast, AST_REG_SCU070, 0x00800000); | |
| } | |
| /* Modify eSPI reset pin */ | |
| temp = ast_mindwm(ast, AST_REG_SCU070); | |
| if (temp & 0x02000000) | |
| ast_moutdwm(ast, AST_REG_SCU07C, 0x00004000); | |
| /* Slow down CPU/AHB CLK in VGA only mode */ | |
| scu008 = ast_mindwm(ast, AST_REG_SCU008); | |
| scu008 |= 0x00000073; | |
| ast_moutdwm(ast, AST_REG_SCU008, scu008); | |
| if (!ast_dram_init_2500(ast)) | |
| drm_err(dev, "DRAM init failed !\n"); | |
| temp = ast_mindwm(ast, AST_REG_SCU040); | |
| ast_moutdwm(ast, AST_REG_SCU040, temp | 0x40); | |
| } | |
| /* wait ready */ | |
| do { | |
| reg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff); | |
| } while ((reg & 0x40) == 0); | |
| } | |
| int ast_2500_post(struct ast_device *ast) | |
| { | |
| ast_2300_set_def_ext_reg(ast); | |
| if (ast->config_mode == ast_use_p2a) { | |
| ast_post_chip_2500(ast); | |
| } else { | |
| if (ast->tx_chip == AST_TX_SIL164) { | |
| /* Enable DVO */ | |
| ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x80); | |
| } | |
| } | |
| return 0; | |
| } | |
| /* | |
| * Mode setting | |
| */ | |
| const struct ast_vbios_dclk_info ast_2500_dclk_table[] = { | |
| {0x2c, 0xe7, 0x03}, /* 00: VCLK25_175 */ | |
| {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */ | |
| {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */ | |
| {0x76, 0x63, 0x01}, /* 03: VCLK36 */ | |
| {0xee, 0x67, 0x01}, /* 04: VCLK40 */ | |
| {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */ | |
| {0xc6, 0x64, 0x01}, /* 06: VCLK50 */ | |
| {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */ | |
| {0x80, 0x64, 0x00}, /* 08: VCLK65 */ | |
| {0x7b, 0x63, 0x00}, /* 09: VCLK75 */ | |
| {0x67, 0x62, 0x00}, /* 0a: VCLK78_75 */ | |
| {0x7c, 0x62, 0x00}, /* 0b: VCLK94_5 */ | |
| {0x8e, 0x62, 0x00}, /* 0c: VCLK108 */ | |
| {0x85, 0x24, 0x00}, /* 0d: VCLK135 */ | |
| {0x67, 0x22, 0x00}, /* 0e: VCLK157_5 */ | |
| {0x6a, 0x22, 0x00}, /* 0f: VCLK162 */ | |
| {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */ | |
| {0x68, 0x6f, 0x80}, /* 11: VCLK83.5 */ | |
| {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */ | |
| {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */ | |
| {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */ | |
| {0x47, 0x6c, 0x80}, /* 15: VCLK71 */ | |
| {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */ | |
| {0x58, 0x01, 0x42}, /* 17: VCLK119 */ | |
| {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */ | |
| {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */ | |
| {0x44, 0x20, 0x43}, /* 1a: VCLK118_25 */ | |
| }; | |
| /* | |
| * Device initialization | |
| */ | |
| static void ast_2500_detect_widescreen(struct ast_device *ast) | |
| { | |
| if (__ast_2100_detect_wsxga_p(ast) || ast->chip == AST2510) { | |
| ast->support_wsxga_p = true; | |
| ast->support_fullhd = true; | |
| } | |
| if (__ast_2100_detect_wuxga(ast)) | |
| ast->support_wuxga = true; | |
| } | |
| static const struct ast_device_quirks ast_2500_device_quirks = { | |
| .crtc_mem_req_threshold_low = 96, | |
| .crtc_mem_req_threshold_high = 120, | |
| .crtc_hsync_precatch_needed = true, | |
| }; | |
| struct drm_device *ast_2500_device_create(struct pci_dev *pdev, | |
| const struct drm_driver *drv, | |
| enum ast_chip chip, | |
| enum ast_config_mode config_mode, | |
| void __iomem *regs, | |
| void __iomem *ioregs, | |
| bool need_post) | |
| { | |
| struct drm_device *dev; | |
| struct ast_device *ast; | |
| int ret; | |
| ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); | |
| if (IS_ERR(ast)) | |
| return ERR_CAST(ast); | |
| dev = &ast->base; | |
| ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2500_device_quirks); | |
| ast->dclk_table = ast_2500_dclk_table; | |
| ast_2300_detect_tx_chip(ast); | |
| if (need_post) { | |
| ret = ast_post_gpu(ast); | |
| if (ret) | |
| return ERR_PTR(ret); | |
| } | |
| ret = ast_mm_init(ast); | |
| if (ret) | |
| return ERR_PTR(ret); | |
| /* map reserved buffer */ | |
| ast->dp501_fw_buf = NULL; | |
| if (ast->vram_size < pci_resource_len(pdev, 0)) { | |
| ast->dp501_fw_buf = pci_iomap_range(pdev, 0, ast->vram_size, 0); | |
| if (!ast->dp501_fw_buf) | |
| drm_info(dev, "failed to map reserved buffer!\n"); | |
| } | |
| ast_2500_detect_widescreen(ast); | |
| ret = ast_mode_config_init(ast); | |
| if (ret) | |
| return ERR_PTR(ret); | |
| return dev; | |
| } |