From: Albert Lee <albertcc@tw.ibm.com>
To: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
Cc: Linux IDE <linux-ide@vger.kernel.org>
Subject: [PATCH ide-2.6 3/3] pdc202xx_new: PLL initialization support
Date: Fri, 04 Mar 2005 16:24:10 +0800 [thread overview]
Message-ID: <42281B2A.8060005@tw.ibm.com> (raw)
Hi Bart,
Attached please find the patch 3/3 against the ide-2.6 tree for your review. Thanks.
Changes:
3/3: Add PLL initialization code
Albert
Signed-off-by: Albert Lee <albertcc@tw.ibm.com>
-----------------------------------------------------------------------------------------------------------
diff -Nru libata-dev-2.6-3/drivers/ide/pci/pdc202xx_new.c libata-dev-2.6-pll/drivers/ide/pci/pdc202xx_new.c
--- libata-dev-2.6-3/drivers/ide/pci/pdc202xx_new.c 2005-03-04 15:22:38.000000000 +0800
+++ libata-dev-2.6-pll/drivers/ide/pci/pdc202xx_new.c 2005-03-04 15:48:09.000000000 +0800
@@ -32,6 +32,9 @@
#include <asm/io.h>
#include <asm/irq.h>
+#define DRV_NAME "pdc202xx_new"
+#define DRV_VERSION "0.50"
+
#ifdef CONFIG_PPC_PMAC
#include <asm/prom.h>
#include <asm/pci-bridge.h>
@@ -46,6 +49,11 @@
#define PDPRINTK(fmt, args...)
#endif
+enum {
+ PDC_100_MHZ = 100000000,
+ PDC_133_MHZ = 133333333,
+};
+
const static char *pdc_quirk_drives[] = {
"QUANTUM FIREBALLlct08 08",
"QUANTUM FIREBALLP KA6.4",
@@ -124,11 +132,11 @@
PDPRINTK("Set index reg%X[%X] \n", index, value);
}
-static u8 pdcnew_ratemask(ide_drive_t *drive)
+static u8 pdcnew_chip_clock(struct pci_dev *pdev)
{
u8 mode;
- switch(HWIF(drive)->pci_dev->device) {
+ switch(pdev->device) {
case PCI_DEVICE_ID_PROMISE_20277:
case PCI_DEVICE_ID_PROMISE_20276:
case PCI_DEVICE_ID_PROMISE_20275:
@@ -144,6 +152,13 @@
return 0;
}
+ return mode;
+}
+
+static u8 pdcnew_ratemask(ide_drive_t *drive)
+{
+ u8 mode = pdcnew_chip_clock(HWIF(drive)->pci_dev);
+
if (!eighty_ninty_three(drive))
mode = min(mode, (u8)1);
@@ -218,7 +233,7 @@
break;
default:
- printk(KERN_ERR ": Unknown speed %d ignored\n", speed);
+ printk(KERN_ERR DRV_NAME ": Unknown speed %d ignored\n", speed);
;
}
@@ -333,16 +348,233 @@
/*
* Deleted this because it is redundant from the caller.
*/
- printk(KERN_WARNING "PDC202XX: %s channel reset.\n",
+ printk(KERN_WARNING DRV_NAME ": %s channel reset.\n",
HWIF(drive)->channel ? "Secondary" : "Primary");
}
+/**
+ * adjust_pll - Adjust the PLL input clock in Hz.
+ *
+ * @pdc_controller: controller specific information
+ * @probe_ent: For the port address
+ * @pll_clock: The input of PLL in HZ
+ */
+static void pdc_adjust_pll(unsigned long dma_base, long pll_clock, long pout_required)
+{
+ unsigned long port1_dma_base = dma_base + 8;
+
+ u8 pll_ctl0, pll_ctl1;
+ long pll_clock_khz = pll_clock / 1000;
+ long ratio = pout_required / pll_clock_khz;
+ int F, R;
+
+
+ /* Sanity check */
+ if (unlikely(pll_clock_khz < 5000L || pll_clock_khz > 70000L)) {
+ printk(KERN_ERR DRV_NAME ": Invalid PLL input clock %ldkHz, give up!\n", pll_clock_khz);
+ return;
+ }
+
+#ifdef PDC_DEBUG
+ PDPRINTK("pout_required is %ld\n", pout_required);
+
+ /* Show the current clock value of PLL control register
+ * (maybe already configured by the firmware)
+ */
+ outb(0x02, port1_dma_base + 0x01);
+ pll_ctl0 = inb(port1_dma_base + 0x03);
+ outb(0x03, port1_dma_base + 0x01);
+ pll_ctl1 = inb(port1_dma_base + 0x03);
+
+ PDPRINTK("pll_ctl[%X][%X]\n", pll_ctl0, pll_ctl1);
+#endif
+
+ /*
+ * Calculate the ratio of F, R and OD
+ * POUT = (F + 2) / (( R + 2) * NO)
+ */
+ if (ratio < 8600L) { // 8.6x
+ /* Using NO = 0x01, R = 0x0D */
+ R = 0x0d;
+ } else if (ratio < 12900L) { // 12.9x
+ /* Using NO = 0x01, R = 0x08 */
+ R = 0x08;
+ } else if (ratio < 16100L) { // 16.1x
+ /* Using NO = 0x01, R = 0x06 */
+ R = 0x06;
+ } else if (ratio < 64000L) { // 64x
+ R = 0x00;
+ } else {
+ /* Invalid ratio */
+ printk(KERN_ERR DRV_NAME ": Invalid ratio %ld, give up!\n", ratio);
+ return;
+ }
+
+ F = (ratio * (R+2)) / 1000 - 2;
+
+ if (unlikely(F < 0 || F > 127)) {
+ /* Invalid F */
+ printk(KERN_ERR DRV_NAME ": F[%d] invalid!\n", F);
+ return;
+ }
+
+ PDPRINTK("F[%d] R[%d] ratio*1000[%ld]\n", F, R, ratio);
+
+ pll_ctl0 = (u8) F;
+ pll_ctl1 = (u8) R;
+
+ PDPRINTK("Writing pll_ctl[%X][%X]\n", pll_ctl0, pll_ctl1);
+
+ outb(0x02, port1_dma_base + 0x01);
+ outb(pll_ctl0, port1_dma_base + 0x03);
+ outb(0x03, port1_dma_base + 0x01);
+ outb(pll_ctl1, port1_dma_base + 0x03);
+
+ /* Wait the PLL circuit to be stable */
+ mdelay(30);
+
+#ifdef PDC_DEBUG
+ /*
+ * Show the current clock value of PLL control register
+ * (maybe configured by the firmware)
+ */
+ outb(0x02, port1_dma_base + 0x01);
+ pll_ctl0 = inb(port1_dma_base + 0x03);
+ outb(0x03, port1_dma_base + 0x01);
+ pll_ctl1 = inb(port1_dma_base + 0x03);
+
+ PDPRINTK("pll_ctl[%X][%X]\n", pll_ctl0, pll_ctl1);
+#endif
+
+ return;
+}
+
+/**
+ * detect_pll_input_clock - Detect the PLL input clock in Hz.
+ * @dma_base: for the port address
+ * Ex. 16949000 on 33MHz PCI bus for pdc20275.
+ * Half of the PCI clock.
+ */
+static long pdc_detect_pll_input_clock(unsigned long dma_base)
+{
+ unsigned long port0_dma_base = dma_base;
+ unsigned long port1_dma_base = dma_base + 8;
+
+ u8 scr1;
+ unsigned long ctr0;
+ unsigned long ctr1;
+ unsigned long ctr2 = 0;
+ unsigned long ctr3 = 0;
+
+ unsigned long start_count, end_count;
+ long pll_clock;
+
+ /* Read current counter value */
+ outb(0x20, port0_dma_base + 0x01);
+ ctr0 = inb(port0_dma_base + 0x03);
+ outb(0x21, port0_dma_base + 0x01);
+ ctr1 = inb(port0_dma_base + 0x03);
+
+ outb(0x20, port1_dma_base + 0x01);
+ ctr2 = inb(port1_dma_base + 0x03);
+ outb(0x21, port1_dma_base + 0x01);
+ ctr3 = inb(port1_dma_base + 0x03);
+
+ start_count = (ctr3 << 23 ) | (ctr2 << 15) | (ctr1 << 8) | ctr0;
+
+ PDPRINTK("ctr0[%lX] ctr1[%lX] ctr2 [%lX] ctr3 [%lX]\n", ctr0, ctr1, ctr2, ctr3);
+
+ /* Start the test mode */
+ outb(0x01, port0_dma_base + 0x01);
+ scr1 = inb(port0_dma_base + 0x03);
+ PDPRINTK("scr1[%X]\n", scr1);
+ outb(scr1 | 0x40, port0_dma_base + 0x03);
+
+ /* Let the counter run for 1000 us. */
+ udelay(1000);
+
+ /* Read the counter values again */
+ outb(0x20, port0_dma_base + 0x01);
+ ctr0 = inb(port0_dma_base + 0x03);
+ outb(0x21, port0_dma_base + 0x01);
+ ctr1 = inb(port0_dma_base + 0x03);
+
+ outb(0x20, port1_dma_base + 0x01);
+ ctr2 = inb(port1_dma_base + 0x03);
+ outb(0x21, port1_dma_base + 0x01);
+ ctr3 = inb(port1_dma_base + 0x03);
+
+ end_count = (ctr3 << 23 ) | (ctr2 << 15) | (ctr1 << 8) | ctr0;
+
+ PDPRINTK("ctr0[%lX] ctr1[%lX] ctr2 [%lX] ctr3 [%lX]\n", ctr0, ctr1, ctr2, ctr3);
+
+ /* Stop the test mode */
+ outb(0x01, port0_dma_base + 0x01);
+ scr1 = inb(port0_dma_base + 0x03);
+ PDPRINTK("scr1[%X]\n", scr1);
+ outb(scr1 & 0xBF, port0_dma_base + 0x03);
+
+ /* calculate the input clock in Hz */
+ pll_clock = (long) ((start_count - end_count) * 1000);
+
+ PDPRINTK("start[%lu] end[%lu] \n", start_count, end_count);
+ PDPRINTK("PLL input clock[%ld]Hz\n", pll_clock);
+
+ return pll_clock;
+}
+
+/**
+ * pdc_hardware_init - Initialize the hardware.
+ * @pdev: instance of pci_dev found
+ * @pdc_controller: controller specific information
+ * @pe: for the port address
+ */
+static int pdc_hardware_init(struct pci_dev *pdev)
+{
+ unsigned long dma_base;
+ u8 mode;
+ long pout_required = 0;
+ long pll_clock;
+
+ /* Get dma_base */
+ dma_base = pci_resource_start(pdev, 4);
+ if (!dma_base) {
+ printk(KERN_ERR DRV_NAME ": dma_base is invalid\n");
+ return -1;
+ }
+
+ /* Calculate pout_required */
+ mode = pdcnew_chip_clock(pdev);
+
+ if (mode == 4)
+ pout_required = PDC_133_MHZ;
+ else if (mode == 3)
+ pout_required = PDC_100_MHZ;
+
+ /*
+ * Detect PLL input clock rate.
+ * On some system, where PCI bus is running at non-standard clock rate.
+ * Ex. 25MHz or 40MHz, we have to adjust the cycle_time.
+ * The pdc20275 controller employs PLL circuit to help correct timing registers setting.
+ */
+ pll_clock = pdc_detect_pll_input_clock(dma_base);
+
+ if(pll_clock < 0) /* counter overflow? Try again. */
+ pll_clock = pdc_detect_pll_input_clock(dma_base);
+
+ PDPRINTK("PLL input clock %ld kHz\n", pll_clock/1000);
+
+ /* Adjust PLL control register */
+ pdc_adjust_pll(dma_base, pll_clock, pout_required);
+
+ return 0;
+}
+
#ifdef CONFIG_PPC_PMAC
static void __devinit apple_kiwi_init(struct pci_dev *pdev)
{
struct device_node *np = pci_device_to_OF_node(pdev);
unsigned int class_rev = 0;
- void __iomem *mmio;
u8 conf;
if (np == NULL || !device_is_compatible(np, "kiwi-root"))
@@ -356,22 +588,7 @@
pci_read_config_byte(pdev, 0x40, &conf);
pci_write_config_byte(pdev, 0x40, conf | 0x01);
}
- mmio = ioremap(pci_resource_start(pdev, 5),
- pci_resource_len(pdev, 5));
-
- /* Setup some PLL stuffs */
- switch (pdev->device) {
- case PCI_DEVICE_ID_PROMISE_20270:
- writew(0x0d2b, mmio + 0x1202);
- mdelay(30);
- break;
- case PCI_DEVICE_ID_PROMISE_20271:
- writew(0x0826, mmio + 0x1202);
- mdelay(30);
- break;
- }
- iounmap(mmio);
}
#endif /* CONFIG_PPC_PMAC */
@@ -387,6 +604,7 @@
#ifdef CONFIG_PPC_PMAC
apple_kiwi_init(dev);
#endif
+ pdc_hardware_init(dev);
return dev->irq;
}
reply other threads:[~2005-03-04 8:24 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=42281B2A.8060005@tw.ibm.com \
--to=albertcc@tw.ibm.com \
--cc=bzolnier@gmail.com \
--cc=linux-ide@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).