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