diff options
Diffstat (limited to 'drivers/video/fbdev/vermilion/cr_pll.c')
-rw-r--r-- | drivers/video/fbdev/vermilion/cr_pll.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/drivers/video/fbdev/vermilion/cr_pll.c b/drivers/video/fbdev/vermilion/cr_pll.c new file mode 100644 index 000000000..ba105c876 --- /dev/null +++ b/drivers/video/fbdev/vermilion/cr_pll.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) Intel Corp. 2007. + * All Rights Reserved. + * + * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to + * develop this driver. + * + * This file is part of the Carillo Ranch video subsystem driver. + * The Carillo Ranch video subsystem driver is free software; + * you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The Carillo Ranch video subsystem driver is distributed + * in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Thomas Hellstrom <thomas-at-tungstengraphics-dot-com> + * Alan Hourihane <alanh-at-tungstengraphics-dot-com> + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/errno.h> +#include <linux/fb.h> +#include "vermilion.h" + +/* The PLL Clock register sits on Host bridge */ +#define CRVML_DEVICE_MCH 0x5001 +#define CRVML_REG_MCHBAR 0x44 +#define CRVML_REG_MCHEN 0x54 +#define CRVML_MCHEN_BIT (1 << 28) +#define CRVML_MCHMAP_SIZE 4096 +#define CRVML_REG_CLOCK 0xc3c +#define CRVML_CLOCK_SHIFT 8 +#define CRVML_CLOCK_MASK 0x00000f00 + +static struct pci_dev *mch_dev; +static u32 mch_bar; +static void __iomem *mch_regs_base; +static u32 saved_clock; + +static const unsigned crvml_clocks[] = { + 6750, + 13500, + 27000, + 29700, + 37125, + 54000, + 59400, + 74250, + 120000 + /* + * There are more clocks, but they are disabled on the CR board. + */ +}; + +static const u32 crvml_clock_bits[] = { + 0x0a, + 0x09, + 0x08, + 0x07, + 0x06, + 0x05, + 0x04, + 0x03, + 0x0b +}; + +static const unsigned crvml_num_clocks = ARRAY_SIZE(crvml_clocks); + +static int crvml_sys_restore(struct vml_sys *sys) +{ + void __iomem *clock_reg = mch_regs_base + CRVML_REG_CLOCK; + + iowrite32(saved_clock, clock_reg); + ioread32(clock_reg); + + return 0; +} + +static int crvml_sys_save(struct vml_sys *sys) +{ + void __iomem *clock_reg = mch_regs_base + CRVML_REG_CLOCK; + + saved_clock = ioread32(clock_reg); + + return 0; +} + +static int crvml_nearest_index(const struct vml_sys *sys, int clock) +{ + int i; + int cur_index = 0; + int cur_diff; + int diff; + + cur_diff = clock - crvml_clocks[0]; + cur_diff = (cur_diff < 0) ? -cur_diff : cur_diff; + for (i = 1; i < crvml_num_clocks; ++i) { + diff = clock - crvml_clocks[i]; + diff = (diff < 0) ? -diff : diff; + if (diff < cur_diff) { + cur_index = i; + cur_diff = diff; + } + } + return cur_index; +} + +static int crvml_nearest_clock(const struct vml_sys *sys, int clock) +{ + return crvml_clocks[crvml_nearest_index(sys, clock)]; +} + +static int crvml_set_clock(struct vml_sys *sys, int clock) +{ + void __iomem *clock_reg = mch_regs_base + CRVML_REG_CLOCK; + int index; + u32 clock_val; + + index = crvml_nearest_index(sys, clock); + + if (crvml_clocks[index] != clock) + return -EINVAL; + + clock_val = ioread32(clock_reg) & ~CRVML_CLOCK_MASK; + clock_val = crvml_clock_bits[index] << CRVML_CLOCK_SHIFT; + iowrite32(clock_val, clock_reg); + ioread32(clock_reg); + + return 0; +} + +static struct vml_sys cr_pll_ops = { + .name = "Carillo Ranch", + .save = crvml_sys_save, + .restore = crvml_sys_restore, + .set_clock = crvml_set_clock, + .nearest_clock = crvml_nearest_clock, +}; + +static int __init cr_pll_init(void) +{ + int err; + u32 dev_en; + + mch_dev = pci_get_device(PCI_VENDOR_ID_INTEL, + CRVML_DEVICE_MCH, NULL); + if (!mch_dev) { + printk(KERN_ERR + "Could not find Carillo Ranch MCH device.\n"); + return -ENODEV; + } + + pci_read_config_dword(mch_dev, CRVML_REG_MCHEN, &dev_en); + if (!(dev_en & CRVML_MCHEN_BIT)) { + printk(KERN_ERR + "Carillo Ranch MCH device was not enabled.\n"); + pci_dev_put(mch_dev); + return -ENODEV; + } + + pci_read_config_dword(mch_dev, CRVML_REG_MCHBAR, + &mch_bar); + mch_regs_base = + ioremap_nocache(mch_bar, CRVML_MCHMAP_SIZE); + if (!mch_regs_base) { + printk(KERN_ERR + "Carillo Ranch MCH device was not enabled.\n"); + pci_dev_put(mch_dev); + return -ENODEV; + } + + err = vmlfb_register_subsys(&cr_pll_ops); + if (err) { + printk(KERN_ERR + "Carillo Ranch failed to initialize vml_sys.\n"); + iounmap(mch_regs_base); + pci_dev_put(mch_dev); + return err; + } + + return 0; +} + +static void __exit cr_pll_exit(void) +{ + vmlfb_unregister_subsys(&cr_pll_ops); + + iounmap(mch_regs_base); + pci_dev_put(mch_dev); +} + +module_init(cr_pll_init); +module_exit(cr_pll_exit); + +MODULE_AUTHOR("Tungsten Graphics Inc."); +MODULE_DESCRIPTION("Carillo Ranch PLL Driver"); +MODULE_LICENSE("GPL"); |