1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) Intel Corp. 2007.
4*4882a593Smuzhiyun * All Rights Reserved.
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
7*4882a593Smuzhiyun * develop this driver.
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * This file is part of the Carillo Ranch video subsystem driver.
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * Authors:
12*4882a593Smuzhiyun * Thomas Hellstrom <thomas-at-tungstengraphics-dot-com>
13*4882a593Smuzhiyun * Alan Hourihane <alanh-at-tungstengraphics-dot-com>
14*4882a593Smuzhiyun */
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include <linux/module.h>
17*4882a593Smuzhiyun #include <linux/kernel.h>
18*4882a593Smuzhiyun #include <linux/pci.h>
19*4882a593Smuzhiyun #include <linux/errno.h>
20*4882a593Smuzhiyun #include <linux/fb.h>
21*4882a593Smuzhiyun #include "vermilion.h"
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun /* The PLL Clock register sits on Host bridge */
24*4882a593Smuzhiyun #define CRVML_DEVICE_MCH 0x5001
25*4882a593Smuzhiyun #define CRVML_REG_MCHBAR 0x44
26*4882a593Smuzhiyun #define CRVML_REG_MCHEN 0x54
27*4882a593Smuzhiyun #define CRVML_MCHEN_BIT (1 << 28)
28*4882a593Smuzhiyun #define CRVML_MCHMAP_SIZE 4096
29*4882a593Smuzhiyun #define CRVML_REG_CLOCK 0xc3c
30*4882a593Smuzhiyun #define CRVML_CLOCK_SHIFT 8
31*4882a593Smuzhiyun #define CRVML_CLOCK_MASK 0x00000f00
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun static struct pci_dev *mch_dev;
34*4882a593Smuzhiyun static u32 mch_bar;
35*4882a593Smuzhiyun static void __iomem *mch_regs_base;
36*4882a593Smuzhiyun static u32 saved_clock;
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun static const unsigned crvml_clocks[] = {
39*4882a593Smuzhiyun 6750,
40*4882a593Smuzhiyun 13500,
41*4882a593Smuzhiyun 27000,
42*4882a593Smuzhiyun 29700,
43*4882a593Smuzhiyun 37125,
44*4882a593Smuzhiyun 54000,
45*4882a593Smuzhiyun 59400,
46*4882a593Smuzhiyun 74250,
47*4882a593Smuzhiyun 120000
48*4882a593Smuzhiyun /*
49*4882a593Smuzhiyun * There are more clocks, but they are disabled on the CR board.
50*4882a593Smuzhiyun */
51*4882a593Smuzhiyun };
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun static const u32 crvml_clock_bits[] = {
54*4882a593Smuzhiyun 0x0a,
55*4882a593Smuzhiyun 0x09,
56*4882a593Smuzhiyun 0x08,
57*4882a593Smuzhiyun 0x07,
58*4882a593Smuzhiyun 0x06,
59*4882a593Smuzhiyun 0x05,
60*4882a593Smuzhiyun 0x04,
61*4882a593Smuzhiyun 0x03,
62*4882a593Smuzhiyun 0x0b
63*4882a593Smuzhiyun };
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun static const unsigned crvml_num_clocks = ARRAY_SIZE(crvml_clocks);
66*4882a593Smuzhiyun
crvml_sys_restore(struct vml_sys * sys)67*4882a593Smuzhiyun static int crvml_sys_restore(struct vml_sys *sys)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun void __iomem *clock_reg = mch_regs_base + CRVML_REG_CLOCK;
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun iowrite32(saved_clock, clock_reg);
72*4882a593Smuzhiyun ioread32(clock_reg);
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun return 0;
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun
crvml_sys_save(struct vml_sys * sys)77*4882a593Smuzhiyun static int crvml_sys_save(struct vml_sys *sys)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun void __iomem *clock_reg = mch_regs_base + CRVML_REG_CLOCK;
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun saved_clock = ioread32(clock_reg);
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun return 0;
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun
crvml_nearest_index(const struct vml_sys * sys,int clock)86*4882a593Smuzhiyun static int crvml_nearest_index(const struct vml_sys *sys, int clock)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun int i;
89*4882a593Smuzhiyun int cur_index = 0;
90*4882a593Smuzhiyun int cur_diff;
91*4882a593Smuzhiyun int diff;
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun cur_diff = clock - crvml_clocks[0];
94*4882a593Smuzhiyun cur_diff = (cur_diff < 0) ? -cur_diff : cur_diff;
95*4882a593Smuzhiyun for (i = 1; i < crvml_num_clocks; ++i) {
96*4882a593Smuzhiyun diff = clock - crvml_clocks[i];
97*4882a593Smuzhiyun diff = (diff < 0) ? -diff : diff;
98*4882a593Smuzhiyun if (diff < cur_diff) {
99*4882a593Smuzhiyun cur_index = i;
100*4882a593Smuzhiyun cur_diff = diff;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun return cur_index;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
crvml_nearest_clock(const struct vml_sys * sys,int clock)106*4882a593Smuzhiyun static int crvml_nearest_clock(const struct vml_sys *sys, int clock)
107*4882a593Smuzhiyun {
108*4882a593Smuzhiyun return crvml_clocks[crvml_nearest_index(sys, clock)];
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun
crvml_set_clock(struct vml_sys * sys,int clock)111*4882a593Smuzhiyun static int crvml_set_clock(struct vml_sys *sys, int clock)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun void __iomem *clock_reg = mch_regs_base + CRVML_REG_CLOCK;
114*4882a593Smuzhiyun int index;
115*4882a593Smuzhiyun u32 clock_val;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun index = crvml_nearest_index(sys, clock);
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun if (crvml_clocks[index] != clock)
120*4882a593Smuzhiyun return -EINVAL;
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun clock_val = ioread32(clock_reg) & ~CRVML_CLOCK_MASK;
123*4882a593Smuzhiyun clock_val = crvml_clock_bits[index] << CRVML_CLOCK_SHIFT;
124*4882a593Smuzhiyun iowrite32(clock_val, clock_reg);
125*4882a593Smuzhiyun ioread32(clock_reg);
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun return 0;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun static struct vml_sys cr_pll_ops = {
131*4882a593Smuzhiyun .name = "Carillo Ranch",
132*4882a593Smuzhiyun .save = crvml_sys_save,
133*4882a593Smuzhiyun .restore = crvml_sys_restore,
134*4882a593Smuzhiyun .set_clock = crvml_set_clock,
135*4882a593Smuzhiyun .nearest_clock = crvml_nearest_clock,
136*4882a593Smuzhiyun };
137*4882a593Smuzhiyun
cr_pll_init(void)138*4882a593Smuzhiyun static int __init cr_pll_init(void)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun int err;
141*4882a593Smuzhiyun u32 dev_en;
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun mch_dev = pci_get_device(PCI_VENDOR_ID_INTEL,
144*4882a593Smuzhiyun CRVML_DEVICE_MCH, NULL);
145*4882a593Smuzhiyun if (!mch_dev) {
146*4882a593Smuzhiyun printk(KERN_ERR
147*4882a593Smuzhiyun "Could not find Carillo Ranch MCH device.\n");
148*4882a593Smuzhiyun return -ENODEV;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun pci_read_config_dword(mch_dev, CRVML_REG_MCHEN, &dev_en);
152*4882a593Smuzhiyun if (!(dev_en & CRVML_MCHEN_BIT)) {
153*4882a593Smuzhiyun printk(KERN_ERR
154*4882a593Smuzhiyun "Carillo Ranch MCH device was not enabled.\n");
155*4882a593Smuzhiyun pci_dev_put(mch_dev);
156*4882a593Smuzhiyun return -ENODEV;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun pci_read_config_dword(mch_dev, CRVML_REG_MCHBAR,
160*4882a593Smuzhiyun &mch_bar);
161*4882a593Smuzhiyun mch_regs_base =
162*4882a593Smuzhiyun ioremap(mch_bar, CRVML_MCHMAP_SIZE);
163*4882a593Smuzhiyun if (!mch_regs_base) {
164*4882a593Smuzhiyun printk(KERN_ERR
165*4882a593Smuzhiyun "Carillo Ranch MCH device was not enabled.\n");
166*4882a593Smuzhiyun pci_dev_put(mch_dev);
167*4882a593Smuzhiyun return -ENODEV;
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun err = vmlfb_register_subsys(&cr_pll_ops);
171*4882a593Smuzhiyun if (err) {
172*4882a593Smuzhiyun printk(KERN_ERR
173*4882a593Smuzhiyun "Carillo Ranch failed to initialize vml_sys.\n");
174*4882a593Smuzhiyun iounmap(mch_regs_base);
175*4882a593Smuzhiyun pci_dev_put(mch_dev);
176*4882a593Smuzhiyun return err;
177*4882a593Smuzhiyun }
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun return 0;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun
cr_pll_exit(void)182*4882a593Smuzhiyun static void __exit cr_pll_exit(void)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun vmlfb_unregister_subsys(&cr_pll_ops);
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun iounmap(mch_regs_base);
187*4882a593Smuzhiyun pci_dev_put(mch_dev);
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun module_init(cr_pll_init);
191*4882a593Smuzhiyun module_exit(cr_pll_exit);
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun MODULE_AUTHOR("Tungsten Graphics Inc.");
194*4882a593Smuzhiyun MODULE_DESCRIPTION("Carillo Ranch PLL Driver");
195*4882a593Smuzhiyun MODULE_LICENSE("GPL");
196