1f7018c21STomi Valkeinen /* 2f7018c21STomi Valkeinen * Copyright 1998-2008 VIA Technologies, Inc. All Rights Reserved. 3f7018c21STomi Valkeinen * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved. 4f7018c21STomi Valkeinen * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> 5f7018c21STomi Valkeinen * 6f7018c21STomi Valkeinen * This program is free software; you can redistribute it and/or 7f7018c21STomi Valkeinen * modify it under the terms of the GNU General Public 8f7018c21STomi Valkeinen * License as published by the Free Software Foundation; 9f7018c21STomi Valkeinen * either version 2, or (at your option) any later version. 10f7018c21STomi Valkeinen * 11f7018c21STomi Valkeinen * This program is distributed in the hope that it will be useful, 12f7018c21STomi Valkeinen * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even 13f7018c21STomi Valkeinen * the implied warranty of MERCHANTABILITY or FITNESS FOR 14f7018c21STomi Valkeinen * A PARTICULAR PURPOSE.See the GNU General Public License 15f7018c21STomi Valkeinen * for more details. 16f7018c21STomi Valkeinen * 17f7018c21STomi Valkeinen * You should have received a copy of the GNU General Public License 18f7018c21STomi Valkeinen * along with this program; if not, write to the Free Software 19f7018c21STomi Valkeinen * Foundation, Inc., 20f7018c21STomi Valkeinen * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21f7018c21STomi Valkeinen */ 22f7018c21STomi Valkeinen /* 23f7018c21STomi Valkeinen * clock and PLL management functions 24f7018c21STomi Valkeinen */ 25f7018c21STomi Valkeinen 26f7018c21STomi Valkeinen #include <linux/kernel.h> 27f7018c21STomi Valkeinen #include <linux/via-core.h> 28598041f3SBartlomiej Zolnierkiewicz 29f7018c21STomi Valkeinen #include "via_clock.h" 30f7018c21STomi Valkeinen #include "global.h" 31f7018c21STomi Valkeinen #include "debug.h" 32f7018c21STomi Valkeinen 33dc4d5214SLad, Prabhakar static const char *via_slap = "Please slap VIA Technologies to motivate them " 34f7018c21STomi Valkeinen "releasing full documentation for your platform!\n"; 35f7018c21STomi Valkeinen 36f7018c21STomi Valkeinen static inline u32 cle266_encode_pll(struct via_pll_config pll) 37f7018c21STomi Valkeinen { 38f7018c21STomi Valkeinen return (pll.multiplier << 8) 39f7018c21STomi Valkeinen | (pll.rshift << 6) 40f7018c21STomi Valkeinen | pll.divisor; 41f7018c21STomi Valkeinen } 42f7018c21STomi Valkeinen 43f7018c21STomi Valkeinen static inline u32 k800_encode_pll(struct via_pll_config pll) 44f7018c21STomi Valkeinen { 45f7018c21STomi Valkeinen return ((pll.divisor - 2) << 16) 46f7018c21STomi Valkeinen | (pll.rshift << 10) 47f7018c21STomi Valkeinen | (pll.multiplier - 2); 48f7018c21STomi Valkeinen } 49f7018c21STomi Valkeinen 50f7018c21STomi Valkeinen static inline u32 vx855_encode_pll(struct via_pll_config pll) 51f7018c21STomi Valkeinen { 52f7018c21STomi Valkeinen return (pll.divisor << 16) 53f7018c21STomi Valkeinen | (pll.rshift << 10) 54f7018c21STomi Valkeinen | pll.multiplier; 55f7018c21STomi Valkeinen } 56f7018c21STomi Valkeinen 57f7018c21STomi Valkeinen static inline void cle266_set_primary_pll_encoded(u32 data) 58f7018c21STomi Valkeinen { 59f7018c21STomi Valkeinen via_write_reg_mask(VIASR, 0x40, 0x02, 0x02); /* enable reset */ 60f7018c21STomi Valkeinen via_write_reg(VIASR, 0x46, data & 0xFF); 61f7018c21STomi Valkeinen via_write_reg(VIASR, 0x47, (data >> 8) & 0xFF); 62f7018c21STomi Valkeinen via_write_reg_mask(VIASR, 0x40, 0x00, 0x02); /* disable reset */ 63f7018c21STomi Valkeinen } 64f7018c21STomi Valkeinen 65f7018c21STomi Valkeinen static inline void k800_set_primary_pll_encoded(u32 data) 66f7018c21STomi Valkeinen { 67f7018c21STomi Valkeinen via_write_reg_mask(VIASR, 0x40, 0x02, 0x02); /* enable reset */ 68f7018c21STomi Valkeinen via_write_reg(VIASR, 0x44, data & 0xFF); 69f7018c21STomi Valkeinen via_write_reg(VIASR, 0x45, (data >> 8) & 0xFF); 70f7018c21STomi Valkeinen via_write_reg(VIASR, 0x46, (data >> 16) & 0xFF); 71f7018c21STomi Valkeinen via_write_reg_mask(VIASR, 0x40, 0x00, 0x02); /* disable reset */ 72f7018c21STomi Valkeinen } 73f7018c21STomi Valkeinen 74f7018c21STomi Valkeinen static inline void cle266_set_secondary_pll_encoded(u32 data) 75f7018c21STomi Valkeinen { 76f7018c21STomi Valkeinen via_write_reg_mask(VIASR, 0x40, 0x04, 0x04); /* enable reset */ 77f7018c21STomi Valkeinen via_write_reg(VIASR, 0x44, data & 0xFF); 78f7018c21STomi Valkeinen via_write_reg(VIASR, 0x45, (data >> 8) & 0xFF); 79f7018c21STomi Valkeinen via_write_reg_mask(VIASR, 0x40, 0x00, 0x04); /* disable reset */ 80f7018c21STomi Valkeinen } 81f7018c21STomi Valkeinen 82f7018c21STomi Valkeinen static inline void k800_set_secondary_pll_encoded(u32 data) 83f7018c21STomi Valkeinen { 84f7018c21STomi Valkeinen via_write_reg_mask(VIASR, 0x40, 0x04, 0x04); /* enable reset */ 85f7018c21STomi Valkeinen via_write_reg(VIASR, 0x4A, data & 0xFF); 86f7018c21STomi Valkeinen via_write_reg(VIASR, 0x4B, (data >> 8) & 0xFF); 87f7018c21STomi Valkeinen via_write_reg(VIASR, 0x4C, (data >> 16) & 0xFF); 88f7018c21STomi Valkeinen via_write_reg_mask(VIASR, 0x40, 0x00, 0x04); /* disable reset */ 89f7018c21STomi Valkeinen } 90f7018c21STomi Valkeinen 91f7018c21STomi Valkeinen static inline void set_engine_pll_encoded(u32 data) 92f7018c21STomi Valkeinen { 93f7018c21STomi Valkeinen via_write_reg_mask(VIASR, 0x40, 0x01, 0x01); /* enable reset */ 94f7018c21STomi Valkeinen via_write_reg(VIASR, 0x47, data & 0xFF); 95f7018c21STomi Valkeinen via_write_reg(VIASR, 0x48, (data >> 8) & 0xFF); 96f7018c21STomi Valkeinen via_write_reg(VIASR, 0x49, (data >> 16) & 0xFF); 97f7018c21STomi Valkeinen via_write_reg_mask(VIASR, 0x40, 0x00, 0x01); /* disable reset */ 98f7018c21STomi Valkeinen } 99f7018c21STomi Valkeinen 100f7018c21STomi Valkeinen static void cle266_set_primary_pll(struct via_pll_config config) 101f7018c21STomi Valkeinen { 102f7018c21STomi Valkeinen cle266_set_primary_pll_encoded(cle266_encode_pll(config)); 103f7018c21STomi Valkeinen } 104f7018c21STomi Valkeinen 105f7018c21STomi Valkeinen static void k800_set_primary_pll(struct via_pll_config config) 106f7018c21STomi Valkeinen { 107f7018c21STomi Valkeinen k800_set_primary_pll_encoded(k800_encode_pll(config)); 108f7018c21STomi Valkeinen } 109f7018c21STomi Valkeinen 110f7018c21STomi Valkeinen static void vx855_set_primary_pll(struct via_pll_config config) 111f7018c21STomi Valkeinen { 112f7018c21STomi Valkeinen k800_set_primary_pll_encoded(vx855_encode_pll(config)); 113f7018c21STomi Valkeinen } 114f7018c21STomi Valkeinen 115f7018c21STomi Valkeinen static void cle266_set_secondary_pll(struct via_pll_config config) 116f7018c21STomi Valkeinen { 117f7018c21STomi Valkeinen cle266_set_secondary_pll_encoded(cle266_encode_pll(config)); 118f7018c21STomi Valkeinen } 119f7018c21STomi Valkeinen 120f7018c21STomi Valkeinen static void k800_set_secondary_pll(struct via_pll_config config) 121f7018c21STomi Valkeinen { 122f7018c21STomi Valkeinen k800_set_secondary_pll_encoded(k800_encode_pll(config)); 123f7018c21STomi Valkeinen } 124f7018c21STomi Valkeinen 125f7018c21STomi Valkeinen static void vx855_set_secondary_pll(struct via_pll_config config) 126f7018c21STomi Valkeinen { 127f7018c21STomi Valkeinen k800_set_secondary_pll_encoded(vx855_encode_pll(config)); 128f7018c21STomi Valkeinen } 129f7018c21STomi Valkeinen 130f7018c21STomi Valkeinen static void k800_set_engine_pll(struct via_pll_config config) 131f7018c21STomi Valkeinen { 132f7018c21STomi Valkeinen set_engine_pll_encoded(k800_encode_pll(config)); 133f7018c21STomi Valkeinen } 134f7018c21STomi Valkeinen 135f7018c21STomi Valkeinen static void vx855_set_engine_pll(struct via_pll_config config) 136f7018c21STomi Valkeinen { 137f7018c21STomi Valkeinen set_engine_pll_encoded(vx855_encode_pll(config)); 138f7018c21STomi Valkeinen } 139f7018c21STomi Valkeinen 140f7018c21STomi Valkeinen static void set_primary_pll_state(u8 state) 141f7018c21STomi Valkeinen { 142f7018c21STomi Valkeinen u8 value; 143f7018c21STomi Valkeinen 144f7018c21STomi Valkeinen switch (state) { 145f7018c21STomi Valkeinen case VIA_STATE_ON: 146f7018c21STomi Valkeinen value = 0x20; 147f7018c21STomi Valkeinen break; 148f7018c21STomi Valkeinen case VIA_STATE_OFF: 149f7018c21STomi Valkeinen value = 0x00; 150f7018c21STomi Valkeinen break; 151f7018c21STomi Valkeinen default: 152f7018c21STomi Valkeinen return; 153f7018c21STomi Valkeinen } 154f7018c21STomi Valkeinen 155f7018c21STomi Valkeinen via_write_reg_mask(VIASR, 0x2D, value, 0x30); 156f7018c21STomi Valkeinen } 157f7018c21STomi Valkeinen 158f7018c21STomi Valkeinen static void set_secondary_pll_state(u8 state) 159f7018c21STomi Valkeinen { 160f7018c21STomi Valkeinen u8 value; 161f7018c21STomi Valkeinen 162f7018c21STomi Valkeinen switch (state) { 163f7018c21STomi Valkeinen case VIA_STATE_ON: 164f7018c21STomi Valkeinen value = 0x08; 165f7018c21STomi Valkeinen break; 166f7018c21STomi Valkeinen case VIA_STATE_OFF: 167f7018c21STomi Valkeinen value = 0x00; 168f7018c21STomi Valkeinen break; 169f7018c21STomi Valkeinen default: 170f7018c21STomi Valkeinen return; 171f7018c21STomi Valkeinen } 172f7018c21STomi Valkeinen 173f7018c21STomi Valkeinen via_write_reg_mask(VIASR, 0x2D, value, 0x0C); 174f7018c21STomi Valkeinen } 175f7018c21STomi Valkeinen 176f7018c21STomi Valkeinen static void set_engine_pll_state(u8 state) 177f7018c21STomi Valkeinen { 178f7018c21STomi Valkeinen u8 value; 179f7018c21STomi Valkeinen 180f7018c21STomi Valkeinen switch (state) { 181f7018c21STomi Valkeinen case VIA_STATE_ON: 182f7018c21STomi Valkeinen value = 0x02; 183f7018c21STomi Valkeinen break; 184f7018c21STomi Valkeinen case VIA_STATE_OFF: 185f7018c21STomi Valkeinen value = 0x00; 186f7018c21STomi Valkeinen break; 187f7018c21STomi Valkeinen default: 188f7018c21STomi Valkeinen return; 189f7018c21STomi Valkeinen } 190f7018c21STomi Valkeinen 191f7018c21STomi Valkeinen via_write_reg_mask(VIASR, 0x2D, value, 0x03); 192f7018c21STomi Valkeinen } 193f7018c21STomi Valkeinen 194f7018c21STomi Valkeinen static void set_primary_clock_state(u8 state) 195f7018c21STomi Valkeinen { 196f7018c21STomi Valkeinen u8 value; 197f7018c21STomi Valkeinen 198f7018c21STomi Valkeinen switch (state) { 199f7018c21STomi Valkeinen case VIA_STATE_ON: 200f7018c21STomi Valkeinen value = 0x20; 201f7018c21STomi Valkeinen break; 202f7018c21STomi Valkeinen case VIA_STATE_OFF: 203f7018c21STomi Valkeinen value = 0x00; 204f7018c21STomi Valkeinen break; 205f7018c21STomi Valkeinen default: 206f7018c21STomi Valkeinen return; 207f7018c21STomi Valkeinen } 208f7018c21STomi Valkeinen 209f7018c21STomi Valkeinen via_write_reg_mask(VIASR, 0x1B, value, 0x30); 210f7018c21STomi Valkeinen } 211f7018c21STomi Valkeinen 212f7018c21STomi Valkeinen static void set_secondary_clock_state(u8 state) 213f7018c21STomi Valkeinen { 214f7018c21STomi Valkeinen u8 value; 215f7018c21STomi Valkeinen 216f7018c21STomi Valkeinen switch (state) { 217f7018c21STomi Valkeinen case VIA_STATE_ON: 218f7018c21STomi Valkeinen value = 0x80; 219f7018c21STomi Valkeinen break; 220f7018c21STomi Valkeinen case VIA_STATE_OFF: 221f7018c21STomi Valkeinen value = 0x00; 222f7018c21STomi Valkeinen break; 223f7018c21STomi Valkeinen default: 224f7018c21STomi Valkeinen return; 225f7018c21STomi Valkeinen } 226f7018c21STomi Valkeinen 227f7018c21STomi Valkeinen via_write_reg_mask(VIASR, 0x1B, value, 0xC0); 228f7018c21STomi Valkeinen } 229f7018c21STomi Valkeinen 230f7018c21STomi Valkeinen static inline u8 set_clock_source_common(enum via_clksrc source, bool use_pll) 231f7018c21STomi Valkeinen { 232f7018c21STomi Valkeinen u8 data = 0; 233f7018c21STomi Valkeinen 234f7018c21STomi Valkeinen switch (source) { 235f7018c21STomi Valkeinen case VIA_CLKSRC_X1: 236f7018c21STomi Valkeinen data = 0x00; 237f7018c21STomi Valkeinen break; 238f7018c21STomi Valkeinen case VIA_CLKSRC_TVX1: 239f7018c21STomi Valkeinen data = 0x02; 240f7018c21STomi Valkeinen break; 241f7018c21STomi Valkeinen case VIA_CLKSRC_TVPLL: 242f7018c21STomi Valkeinen data = 0x04; /* 0x06 should be the same */ 243f7018c21STomi Valkeinen break; 244f7018c21STomi Valkeinen case VIA_CLKSRC_DVP1TVCLKR: 245f7018c21STomi Valkeinen data = 0x0A; 246f7018c21STomi Valkeinen break; 247f7018c21STomi Valkeinen case VIA_CLKSRC_CAP0: 248f7018c21STomi Valkeinen data = 0xC; 249f7018c21STomi Valkeinen break; 250f7018c21STomi Valkeinen case VIA_CLKSRC_CAP1: 251f7018c21STomi Valkeinen data = 0x0E; 252f7018c21STomi Valkeinen break; 253f7018c21STomi Valkeinen } 254f7018c21STomi Valkeinen 255f7018c21STomi Valkeinen if (!use_pll) 256f7018c21STomi Valkeinen data |= 1; 257f7018c21STomi Valkeinen 258f7018c21STomi Valkeinen return data; 259f7018c21STomi Valkeinen } 260f7018c21STomi Valkeinen 261f7018c21STomi Valkeinen static void set_primary_clock_source(enum via_clksrc source, bool use_pll) 262f7018c21STomi Valkeinen { 263f7018c21STomi Valkeinen u8 data = set_clock_source_common(source, use_pll) << 4; 264f7018c21STomi Valkeinen via_write_reg_mask(VIACR, 0x6C, data, 0xF0); 265f7018c21STomi Valkeinen } 266f7018c21STomi Valkeinen 267f7018c21STomi Valkeinen static void set_secondary_clock_source(enum via_clksrc source, bool use_pll) 268f7018c21STomi Valkeinen { 269f7018c21STomi Valkeinen u8 data = set_clock_source_common(source, use_pll); 270f7018c21STomi Valkeinen via_write_reg_mask(VIACR, 0x6C, data, 0x0F); 271f7018c21STomi Valkeinen } 272f7018c21STomi Valkeinen 273f7018c21STomi Valkeinen static void dummy_set_clock_state(u8 state) 274f7018c21STomi Valkeinen { 275f7018c21STomi Valkeinen printk(KERN_INFO "Using undocumented set clock state.\n%s", via_slap); 276f7018c21STomi Valkeinen } 277f7018c21STomi Valkeinen 278f7018c21STomi Valkeinen static void dummy_set_clock_source(enum via_clksrc source, bool use_pll) 279f7018c21STomi Valkeinen { 280f7018c21STomi Valkeinen printk(KERN_INFO "Using undocumented set clock source.\n%s", via_slap); 281f7018c21STomi Valkeinen } 282f7018c21STomi Valkeinen 283f7018c21STomi Valkeinen static void dummy_set_pll_state(u8 state) 284f7018c21STomi Valkeinen { 285f7018c21STomi Valkeinen printk(KERN_INFO "Using undocumented set PLL state.\n%s", via_slap); 286f7018c21STomi Valkeinen } 287f7018c21STomi Valkeinen 288f7018c21STomi Valkeinen static void dummy_set_pll(struct via_pll_config config) 289f7018c21STomi Valkeinen { 290f7018c21STomi Valkeinen printk(KERN_INFO "Using undocumented set PLL.\n%s", via_slap); 291f7018c21STomi Valkeinen } 292f7018c21STomi Valkeinen 293f7018c21STomi Valkeinen static void noop_set_clock_state(u8 state) 294f7018c21STomi Valkeinen { 295f7018c21STomi Valkeinen } 296f7018c21STomi Valkeinen 297f7018c21STomi Valkeinen void via_clock_init(struct via_clock *clock, int gfx_chip) 298f7018c21STomi Valkeinen { 299f7018c21STomi Valkeinen switch (gfx_chip) { 300f7018c21STomi Valkeinen case UNICHROME_CLE266: 301f7018c21STomi Valkeinen case UNICHROME_K400: 302f7018c21STomi Valkeinen clock->set_primary_clock_state = dummy_set_clock_state; 303f7018c21STomi Valkeinen clock->set_primary_clock_source = dummy_set_clock_source; 304f7018c21STomi Valkeinen clock->set_primary_pll_state = dummy_set_pll_state; 305f7018c21STomi Valkeinen clock->set_primary_pll = cle266_set_primary_pll; 306f7018c21STomi Valkeinen 307f7018c21STomi Valkeinen clock->set_secondary_clock_state = dummy_set_clock_state; 308f7018c21STomi Valkeinen clock->set_secondary_clock_source = dummy_set_clock_source; 309f7018c21STomi Valkeinen clock->set_secondary_pll_state = dummy_set_pll_state; 310f7018c21STomi Valkeinen clock->set_secondary_pll = cle266_set_secondary_pll; 311f7018c21STomi Valkeinen 312f7018c21STomi Valkeinen clock->set_engine_pll_state = dummy_set_pll_state; 313f7018c21STomi Valkeinen clock->set_engine_pll = dummy_set_pll; 314f7018c21STomi Valkeinen break; 315f7018c21STomi Valkeinen case UNICHROME_K800: 316f7018c21STomi Valkeinen case UNICHROME_PM800: 317f7018c21STomi Valkeinen case UNICHROME_CN700: 318f7018c21STomi Valkeinen case UNICHROME_CX700: 319f7018c21STomi Valkeinen case UNICHROME_CN750: 320f7018c21STomi Valkeinen case UNICHROME_K8M890: 321f7018c21STomi Valkeinen case UNICHROME_P4M890: 322f7018c21STomi Valkeinen case UNICHROME_P4M900: 323f7018c21STomi Valkeinen case UNICHROME_VX800: 324f7018c21STomi Valkeinen clock->set_primary_clock_state = set_primary_clock_state; 325f7018c21STomi Valkeinen clock->set_primary_clock_source = set_primary_clock_source; 326f7018c21STomi Valkeinen clock->set_primary_pll_state = set_primary_pll_state; 327f7018c21STomi Valkeinen clock->set_primary_pll = k800_set_primary_pll; 328f7018c21STomi Valkeinen 329f7018c21STomi Valkeinen clock->set_secondary_clock_state = set_secondary_clock_state; 330f7018c21STomi Valkeinen clock->set_secondary_clock_source = set_secondary_clock_source; 331f7018c21STomi Valkeinen clock->set_secondary_pll_state = set_secondary_pll_state; 332f7018c21STomi Valkeinen clock->set_secondary_pll = k800_set_secondary_pll; 333f7018c21STomi Valkeinen 334f7018c21STomi Valkeinen clock->set_engine_pll_state = set_engine_pll_state; 335f7018c21STomi Valkeinen clock->set_engine_pll = k800_set_engine_pll; 336f7018c21STomi Valkeinen break; 337f7018c21STomi Valkeinen case UNICHROME_VX855: 338f7018c21STomi Valkeinen case UNICHROME_VX900: 339f7018c21STomi Valkeinen clock->set_primary_clock_state = set_primary_clock_state; 340f7018c21STomi Valkeinen clock->set_primary_clock_source = set_primary_clock_source; 341f7018c21STomi Valkeinen clock->set_primary_pll_state = set_primary_pll_state; 342f7018c21STomi Valkeinen clock->set_primary_pll = vx855_set_primary_pll; 343f7018c21STomi Valkeinen 344f7018c21STomi Valkeinen clock->set_secondary_clock_state = set_secondary_clock_state; 345f7018c21STomi Valkeinen clock->set_secondary_clock_source = set_secondary_clock_source; 346f7018c21STomi Valkeinen clock->set_secondary_pll_state = set_secondary_pll_state; 347f7018c21STomi Valkeinen clock->set_secondary_pll = vx855_set_secondary_pll; 348f7018c21STomi Valkeinen 349f7018c21STomi Valkeinen clock->set_engine_pll_state = set_engine_pll_state; 350f7018c21STomi Valkeinen clock->set_engine_pll = vx855_set_engine_pll; 351f7018c21STomi Valkeinen break; 352f7018c21STomi Valkeinen 353f7018c21STomi Valkeinen } 354f7018c21STomi Valkeinen 355f7018c21STomi Valkeinen if (machine_is_olpc()) { 356f7018c21STomi Valkeinen /* The OLPC XO-1.5 cannot suspend/resume reliably if the 357f7018c21STomi Valkeinen * IGA1/IGA2 clocks are set as on or off (memory rot 358f7018c21STomi Valkeinen * occasionally happens during suspend under such 359f7018c21STomi Valkeinen * configurations). 360f7018c21STomi Valkeinen * 361f7018c21STomi Valkeinen * The only known stable scenario is to leave this bits as-is, 362f7018c21STomi Valkeinen * which in their default states are documented to enable the 363f7018c21STomi Valkeinen * clock only when it is needed. 364f7018c21STomi Valkeinen */ 365f7018c21STomi Valkeinen clock->set_primary_clock_state = noop_set_clock_state; 366f7018c21STomi Valkeinen clock->set_secondary_clock_state = noop_set_clock_state; 367f7018c21STomi Valkeinen } 368f7018c21STomi Valkeinen } 369