162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 1998-2008 VIA Technologies, Inc. All Rights Reserved. 462306a36Sopenharmony_ci * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci/* 862306a36Sopenharmony_ci * clock and PLL management functions 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/via-core.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "via_clock.h" 1562306a36Sopenharmony_ci#include "global.h" 1662306a36Sopenharmony_ci#include "debug.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic const char *via_slap = "Please slap VIA Technologies to motivate them " 1962306a36Sopenharmony_ci "releasing full documentation for your platform!\n"; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic inline u32 cle266_encode_pll(struct via_pll_config pll) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci return (pll.multiplier << 8) 2462306a36Sopenharmony_ci | (pll.rshift << 6) 2562306a36Sopenharmony_ci | pll.divisor; 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic inline u32 k800_encode_pll(struct via_pll_config pll) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci return ((pll.divisor - 2) << 16) 3162306a36Sopenharmony_ci | (pll.rshift << 10) 3262306a36Sopenharmony_ci | (pll.multiplier - 2); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic inline u32 vx855_encode_pll(struct via_pll_config pll) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci return (pll.divisor << 16) 3862306a36Sopenharmony_ci | (pll.rshift << 10) 3962306a36Sopenharmony_ci | pll.multiplier; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic inline void cle266_set_primary_pll_encoded(u32 data) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci via_write_reg_mask(VIASR, 0x40, 0x02, 0x02); /* enable reset */ 4562306a36Sopenharmony_ci via_write_reg(VIASR, 0x46, data & 0xFF); 4662306a36Sopenharmony_ci via_write_reg(VIASR, 0x47, (data >> 8) & 0xFF); 4762306a36Sopenharmony_ci via_write_reg_mask(VIASR, 0x40, 0x00, 0x02); /* disable reset */ 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic inline void k800_set_primary_pll_encoded(u32 data) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci via_write_reg_mask(VIASR, 0x40, 0x02, 0x02); /* enable reset */ 5362306a36Sopenharmony_ci via_write_reg(VIASR, 0x44, data & 0xFF); 5462306a36Sopenharmony_ci via_write_reg(VIASR, 0x45, (data >> 8) & 0xFF); 5562306a36Sopenharmony_ci via_write_reg(VIASR, 0x46, (data >> 16) & 0xFF); 5662306a36Sopenharmony_ci via_write_reg_mask(VIASR, 0x40, 0x00, 0x02); /* disable reset */ 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic inline void cle266_set_secondary_pll_encoded(u32 data) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci via_write_reg_mask(VIASR, 0x40, 0x04, 0x04); /* enable reset */ 6262306a36Sopenharmony_ci via_write_reg(VIASR, 0x44, data & 0xFF); 6362306a36Sopenharmony_ci via_write_reg(VIASR, 0x45, (data >> 8) & 0xFF); 6462306a36Sopenharmony_ci via_write_reg_mask(VIASR, 0x40, 0x00, 0x04); /* disable reset */ 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic inline void k800_set_secondary_pll_encoded(u32 data) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci via_write_reg_mask(VIASR, 0x40, 0x04, 0x04); /* enable reset */ 7062306a36Sopenharmony_ci via_write_reg(VIASR, 0x4A, data & 0xFF); 7162306a36Sopenharmony_ci via_write_reg(VIASR, 0x4B, (data >> 8) & 0xFF); 7262306a36Sopenharmony_ci via_write_reg(VIASR, 0x4C, (data >> 16) & 0xFF); 7362306a36Sopenharmony_ci via_write_reg_mask(VIASR, 0x40, 0x00, 0x04); /* disable reset */ 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic inline void set_engine_pll_encoded(u32 data) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci via_write_reg_mask(VIASR, 0x40, 0x01, 0x01); /* enable reset */ 7962306a36Sopenharmony_ci via_write_reg(VIASR, 0x47, data & 0xFF); 8062306a36Sopenharmony_ci via_write_reg(VIASR, 0x48, (data >> 8) & 0xFF); 8162306a36Sopenharmony_ci via_write_reg(VIASR, 0x49, (data >> 16) & 0xFF); 8262306a36Sopenharmony_ci via_write_reg_mask(VIASR, 0x40, 0x00, 0x01); /* disable reset */ 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic void cle266_set_primary_pll(struct via_pll_config config) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci cle266_set_primary_pll_encoded(cle266_encode_pll(config)); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void k800_set_primary_pll(struct via_pll_config config) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci k800_set_primary_pll_encoded(k800_encode_pll(config)); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void vx855_set_primary_pll(struct via_pll_config config) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci k800_set_primary_pll_encoded(vx855_encode_pll(config)); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic void cle266_set_secondary_pll(struct via_pll_config config) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci cle266_set_secondary_pll_encoded(cle266_encode_pll(config)); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void k800_set_secondary_pll(struct via_pll_config config) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci k800_set_secondary_pll_encoded(k800_encode_pll(config)); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void vx855_set_secondary_pll(struct via_pll_config config) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci k800_set_secondary_pll_encoded(vx855_encode_pll(config)); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void k800_set_engine_pll(struct via_pll_config config) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci set_engine_pll_encoded(k800_encode_pll(config)); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void vx855_set_engine_pll(struct via_pll_config config) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci set_engine_pll_encoded(vx855_encode_pll(config)); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void set_primary_pll_state(u8 state) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci u8 value; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci switch (state) { 13062306a36Sopenharmony_ci case VIA_STATE_ON: 13162306a36Sopenharmony_ci value = 0x20; 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci case VIA_STATE_OFF: 13462306a36Sopenharmony_ci value = 0x00; 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci default: 13762306a36Sopenharmony_ci return; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci via_write_reg_mask(VIASR, 0x2D, value, 0x30); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void set_secondary_pll_state(u8 state) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci u8 value; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci switch (state) { 14862306a36Sopenharmony_ci case VIA_STATE_ON: 14962306a36Sopenharmony_ci value = 0x08; 15062306a36Sopenharmony_ci break; 15162306a36Sopenharmony_ci case VIA_STATE_OFF: 15262306a36Sopenharmony_ci value = 0x00; 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci default: 15562306a36Sopenharmony_ci return; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci via_write_reg_mask(VIASR, 0x2D, value, 0x0C); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void set_engine_pll_state(u8 state) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci u8 value; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci switch (state) { 16662306a36Sopenharmony_ci case VIA_STATE_ON: 16762306a36Sopenharmony_ci value = 0x02; 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci case VIA_STATE_OFF: 17062306a36Sopenharmony_ci value = 0x00; 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci default: 17362306a36Sopenharmony_ci return; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci via_write_reg_mask(VIASR, 0x2D, value, 0x03); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void set_primary_clock_state(u8 state) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci u8 value; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci switch (state) { 18462306a36Sopenharmony_ci case VIA_STATE_ON: 18562306a36Sopenharmony_ci value = 0x20; 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci case VIA_STATE_OFF: 18862306a36Sopenharmony_ci value = 0x00; 18962306a36Sopenharmony_ci break; 19062306a36Sopenharmony_ci default: 19162306a36Sopenharmony_ci return; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci via_write_reg_mask(VIASR, 0x1B, value, 0x30); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void set_secondary_clock_state(u8 state) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci u8 value; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci switch (state) { 20262306a36Sopenharmony_ci case VIA_STATE_ON: 20362306a36Sopenharmony_ci value = 0x80; 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci case VIA_STATE_OFF: 20662306a36Sopenharmony_ci value = 0x00; 20762306a36Sopenharmony_ci break; 20862306a36Sopenharmony_ci default: 20962306a36Sopenharmony_ci return; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci via_write_reg_mask(VIASR, 0x1B, value, 0xC0); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic inline u8 set_clock_source_common(enum via_clksrc source, bool use_pll) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci u8 data = 0; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci switch (source) { 22062306a36Sopenharmony_ci case VIA_CLKSRC_X1: 22162306a36Sopenharmony_ci data = 0x00; 22262306a36Sopenharmony_ci break; 22362306a36Sopenharmony_ci case VIA_CLKSRC_TVX1: 22462306a36Sopenharmony_ci data = 0x02; 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci case VIA_CLKSRC_TVPLL: 22762306a36Sopenharmony_ci data = 0x04; /* 0x06 should be the same */ 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci case VIA_CLKSRC_DVP1TVCLKR: 23062306a36Sopenharmony_ci data = 0x0A; 23162306a36Sopenharmony_ci break; 23262306a36Sopenharmony_ci case VIA_CLKSRC_CAP0: 23362306a36Sopenharmony_ci data = 0xC; 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci case VIA_CLKSRC_CAP1: 23662306a36Sopenharmony_ci data = 0x0E; 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (!use_pll) 24162306a36Sopenharmony_ci data |= 1; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return data; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void set_primary_clock_source(enum via_clksrc source, bool use_pll) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci u8 data = set_clock_source_common(source, use_pll) << 4; 24962306a36Sopenharmony_ci via_write_reg_mask(VIACR, 0x6C, data, 0xF0); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void set_secondary_clock_source(enum via_clksrc source, bool use_pll) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci u8 data = set_clock_source_common(source, use_pll); 25562306a36Sopenharmony_ci via_write_reg_mask(VIACR, 0x6C, data, 0x0F); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic void dummy_set_clock_state(u8 state) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci printk(KERN_INFO "Using undocumented set clock state.\n%s", via_slap); 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic void dummy_set_clock_source(enum via_clksrc source, bool use_pll) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci printk(KERN_INFO "Using undocumented set clock source.\n%s", via_slap); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic void dummy_set_pll_state(u8 state) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci printk(KERN_INFO "Using undocumented set PLL state.\n%s", via_slap); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic void dummy_set_pll(struct via_pll_config config) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci printk(KERN_INFO "Using undocumented set PLL.\n%s", via_slap); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic void noop_set_clock_state(u8 state) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_civoid via_clock_init(struct via_clock *clock, int gfx_chip) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci switch (gfx_chip) { 28562306a36Sopenharmony_ci case UNICHROME_CLE266: 28662306a36Sopenharmony_ci case UNICHROME_K400: 28762306a36Sopenharmony_ci clock->set_primary_clock_state = dummy_set_clock_state; 28862306a36Sopenharmony_ci clock->set_primary_clock_source = dummy_set_clock_source; 28962306a36Sopenharmony_ci clock->set_primary_pll_state = dummy_set_pll_state; 29062306a36Sopenharmony_ci clock->set_primary_pll = cle266_set_primary_pll; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci clock->set_secondary_clock_state = dummy_set_clock_state; 29362306a36Sopenharmony_ci clock->set_secondary_clock_source = dummy_set_clock_source; 29462306a36Sopenharmony_ci clock->set_secondary_pll_state = dummy_set_pll_state; 29562306a36Sopenharmony_ci clock->set_secondary_pll = cle266_set_secondary_pll; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci clock->set_engine_pll_state = dummy_set_pll_state; 29862306a36Sopenharmony_ci clock->set_engine_pll = dummy_set_pll; 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci case UNICHROME_K800: 30162306a36Sopenharmony_ci case UNICHROME_PM800: 30262306a36Sopenharmony_ci case UNICHROME_CN700: 30362306a36Sopenharmony_ci case UNICHROME_CX700: 30462306a36Sopenharmony_ci case UNICHROME_CN750: 30562306a36Sopenharmony_ci case UNICHROME_K8M890: 30662306a36Sopenharmony_ci case UNICHROME_P4M890: 30762306a36Sopenharmony_ci case UNICHROME_P4M900: 30862306a36Sopenharmony_ci case UNICHROME_VX800: 30962306a36Sopenharmony_ci clock->set_primary_clock_state = set_primary_clock_state; 31062306a36Sopenharmony_ci clock->set_primary_clock_source = set_primary_clock_source; 31162306a36Sopenharmony_ci clock->set_primary_pll_state = set_primary_pll_state; 31262306a36Sopenharmony_ci clock->set_primary_pll = k800_set_primary_pll; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci clock->set_secondary_clock_state = set_secondary_clock_state; 31562306a36Sopenharmony_ci clock->set_secondary_clock_source = set_secondary_clock_source; 31662306a36Sopenharmony_ci clock->set_secondary_pll_state = set_secondary_pll_state; 31762306a36Sopenharmony_ci clock->set_secondary_pll = k800_set_secondary_pll; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci clock->set_engine_pll_state = set_engine_pll_state; 32062306a36Sopenharmony_ci clock->set_engine_pll = k800_set_engine_pll; 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci case UNICHROME_VX855: 32362306a36Sopenharmony_ci case UNICHROME_VX900: 32462306a36Sopenharmony_ci clock->set_primary_clock_state = set_primary_clock_state; 32562306a36Sopenharmony_ci clock->set_primary_clock_source = set_primary_clock_source; 32662306a36Sopenharmony_ci clock->set_primary_pll_state = set_primary_pll_state; 32762306a36Sopenharmony_ci clock->set_primary_pll = vx855_set_primary_pll; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci clock->set_secondary_clock_state = set_secondary_clock_state; 33062306a36Sopenharmony_ci clock->set_secondary_clock_source = set_secondary_clock_source; 33162306a36Sopenharmony_ci clock->set_secondary_pll_state = set_secondary_pll_state; 33262306a36Sopenharmony_ci clock->set_secondary_pll = vx855_set_secondary_pll; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci clock->set_engine_pll_state = set_engine_pll_state; 33562306a36Sopenharmony_ci clock->set_engine_pll = vx855_set_engine_pll; 33662306a36Sopenharmony_ci break; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (machine_is_olpc()) { 34162306a36Sopenharmony_ci /* The OLPC XO-1.5 cannot suspend/resume reliably if the 34262306a36Sopenharmony_ci * IGA1/IGA2 clocks are set as on or off (memory rot 34362306a36Sopenharmony_ci * occasionally happens during suspend under such 34462306a36Sopenharmony_ci * configurations). 34562306a36Sopenharmony_ci * 34662306a36Sopenharmony_ci * The only known stable scenario is to leave this bits as-is, 34762306a36Sopenharmony_ci * which in their default states are documented to enable the 34862306a36Sopenharmony_ci * clock only when it is needed. 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci clock->set_primary_clock_state = noop_set_clock_state; 35162306a36Sopenharmony_ci clock->set_secondary_clock_state = noop_set_clock_state; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci} 354