xref: /OK3568_Linux_fs/kernel/drivers/video/fbdev/vermilion/cr_pll.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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