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