aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrent Casavant <bcasavan@sgi.com>2005-06-21 20:16:01 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-06-21 21:46:32 -0400
commitd4c477ca5448f19afaaf6c0cfd655009ea9e614d (patch)
tree75571ad144ff904afbd39b1b24766461255396ac
parente5d310b349b2cbcc0dab31139c92201f332695bb (diff)
[PATCH] ioc4: PCI bus speed detection
Several hardware features of SGI's IOC4 I/O controller chip require timing-related driver calculations dependent upon the PCI bus speed. This patch enables the core IOC4 driver code to detect the actual bus speed and store a value that can later be used by the IOC4 subdrivers as needed. Signed-off-by: Brent Casavant <bcasavan@sgi.com> Acked-by: Pat Gefre <pfg@sgi.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--drivers/serial/ioc4_serial.c31
-rw-r--r--drivers/sn/Kconfig2
-rw-r--r--drivers/sn/ioc4.c128
-rw-r--r--include/linux/ioc4.h37
4 files changed, 177 insertions, 21 deletions
diff --git a/drivers/serial/ioc4_serial.c b/drivers/serial/ioc4_serial.c
index da5f10eb4845..793c3a7cbe47 100644
--- a/drivers/serial/ioc4_serial.c
+++ b/drivers/serial/ioc4_serial.c
@@ -299,7 +299,6 @@ struct ioc4_serial {
299} ioc4_serial; 299} ioc4_serial;
300 300
301/* UART clock speed */ 301/* UART clock speed */
302#define IOC4_SER_XIN_CLK IOC4_SER_XIN_CLK_66
303#define IOC4_SER_XIN_CLK_66 66666667 302#define IOC4_SER_XIN_CLK_66 66666667
304#define IOC4_SER_XIN_CLK_33 33333333 303#define IOC4_SER_XIN_CLK_33 33333333
305 304
@@ -1012,21 +1011,20 @@ static irqreturn_t ioc4_intr(int irq, void *arg, struct pt_regs *regs)
1012 * ioc4_attach_local - Device initialization. 1011 * ioc4_attach_local - Device initialization.
1013 * Called at *_attach() time for each 1012 * Called at *_attach() time for each
1014 * IOC4 with serial ports in the system. 1013 * IOC4 with serial ports in the system.
1015 * @control: ioc4_control ptr 1014 * @idd: Master module data for this IOC4
1016 * @pdev: PCI handle for this device
1017 * @soft: soft struct for this device
1018 * @ioc4: ioc4 mem space
1019 */ 1015 */
1020static int inline ioc4_attach_local(struct pci_dev *pdev, 1016static int inline ioc4_attach_local(struct ioc4_driver_data *idd)
1021 struct ioc4_control *control,
1022 struct ioc4_soft *soft, void __iomem *ioc4_misc,
1023 void __iomem *ioc4_serial)
1024{ 1017{
1025 struct ioc4_port *port; 1018 struct ioc4_port *port;
1026 struct ioc4_port *ports[IOC4_NUM_SERIAL_PORTS]; 1019 struct ioc4_port *ports[IOC4_NUM_SERIAL_PORTS];
1027 int port_number; 1020 int port_number;
1028 uint16_t ioc4_revid_min = 62; 1021 uint16_t ioc4_revid_min = 62;
1029 uint16_t ioc4_revid; 1022 uint16_t ioc4_revid;
1023 struct pci_dev *pdev = idd->idd_pdev;
1024 struct ioc4_control* control = idd->idd_serial_data;
1025 struct ioc4_soft *soft = control->ic_soft;
1026 void __iomem *ioc4_misc = idd->idd_misc_regs;
1027 void __iomem *ioc4_serial = soft->is_ioc4_serial_addr;
1030 1028
1031 /* IOC4 firmware must be at least rev 62 */ 1029 /* IOC4 firmware must be at least rev 62 */
1032 pci_read_config_word(pdev, PCI_COMMAND_SPECIAL, &ioc4_revid); 1030 pci_read_config_word(pdev, PCI_COMMAND_SPECIAL, &ioc4_revid);
@@ -1063,7 +1061,15 @@ static int inline ioc4_attach_local(struct pci_dev *pdev,
1063 port->ip_ioc4_soft = soft; 1061 port->ip_ioc4_soft = soft;
1064 port->ip_pdev = pdev; 1062 port->ip_pdev = pdev;
1065 port->ip_ienb = 0; 1063 port->ip_ienb = 0;
1066 port->ip_pci_bus_speed = IOC4_SER_XIN_CLK; 1064 /* Use baud rate calculations based on detected PCI
1065 * bus speed. Simply test whether the PCI clock is
1066 * running closer to 66MHz or 33MHz.
1067 */
1068 if (idd->count_period/IOC4_EXTINT_COUNT_DIVISOR < 20) {
1069 port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_66;
1070 } else {
1071 port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_33;
1072 }
1067 port->ip_baud = 9600; 1073 port->ip_baud = 9600;
1068 port->ip_control = control; 1074 port->ip_control = control;
1069 port->ip_mem = ioc4_misc; 1075 port->ip_mem = ioc4_misc;
@@ -2733,9 +2739,8 @@ ioc4_serial_attach_one(struct ioc4_driver_data *idd)
2733 "%s : request_irq fails for IRQ 0x%x\n ", 2739 "%s : request_irq fails for IRQ 0x%x\n ",
2734 __FUNCTION__, idd->idd_pdev->irq); 2740 __FUNCTION__, idd->idd_pdev->irq);
2735 } 2741 }
2736 if ((ret = ioc4_attach_local(idd->idd_pdev, control, soft, 2742 ret = ioc4_attach_local(idd);
2737 soft->is_ioc4_misc_addr, 2743 if (ret)
2738 soft->is_ioc4_serial_addr)))
2739 goto out4; 2744 goto out4;
2740 2745
2741 /* register port with the serial core */ 2746 /* register port with the serial core */
diff --git a/drivers/sn/Kconfig b/drivers/sn/Kconfig
index 20f7515ab830..13b8d249da5c 100644
--- a/drivers/sn/Kconfig
+++ b/drivers/sn/Kconfig
@@ -6,7 +6,7 @@ menu "SN Devices"
6 6
7config SGI_IOC4 7config SGI_IOC4
8 tristate "SGI IOC4 Base IO support" 8 tristate "SGI IOC4 Base IO support"
9 depends on IA64_GENERIC || IA64_SGI_SN2 9 depends on (IA64_GENERIC || IA64_SGI_SN2) && MMTIMER
10 default m 10 default m
11 ---help--- 11 ---help---
12 This option enables basic support for the SGI IOC4-based Base IO 12 This option enables basic support for the SGI IOC4-based Base IO
diff --git a/drivers/sn/ioc4.c b/drivers/sn/ioc4.c
index 70862d72ea9d..ea75b3d0612b 100644
--- a/drivers/sn/ioc4.c
+++ b/drivers/sn/ioc4.c
@@ -29,7 +29,26 @@
29#include <linux/module.h> 29#include <linux/module.h>
30#include <linux/pci.h> 30#include <linux/pci.h>
31#include <linux/ioc4.h> 31#include <linux/ioc4.h>
32#include <linux/mmtimer.h>
33#include <linux/rtc.h>
32#include <linux/rwsem.h> 34#include <linux/rwsem.h>
35#include <asm/sn/addrs.h>
36#include <asm/sn/clksupport.h>
37#include <asm/sn/shub_mmr.h>
38
39/***************
40 * Definitions *
41 ***************/
42
43/* Tweakable values */
44
45/* PCI bus speed detection/calibration */
46#define IOC4_CALIBRATE_COUNT 63 /* Calibration cycle period */
47#define IOC4_CALIBRATE_CYCLES 256 /* Average over this many cycles */
48#define IOC4_CALIBRATE_DISCARD 2 /* Discard first few cycles */
49#define IOC4_CALIBRATE_LOW_MHZ 25 /* Lower bound on bus speed sanity */
50#define IOC4_CALIBRATE_HIGH_MHZ 75 /* Upper bound on bus speed sanity */
51#define IOC4_CALIBRATE_DEFAULT_MHZ 66 /* Assumed if sanity check fails */
33 52
34/************************ 53/************************
35 * Submodule management * 54 * Submodule management *
@@ -101,6 +120,112 @@ ioc4_unregister_submodule(struct ioc4_submodule *is)
101 * Device management * 120 * Device management *
102 *********************/ 121 *********************/
103 122
123#define IOC4_CALIBRATE_LOW_LIMIT \
124 (1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_LOW_MHZ)
125#define IOC4_CALIBRATE_HIGH_LIMIT \
126 (1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_HIGH_MHZ)
127#define IOC4_CALIBRATE_DEFAULT \
128 (1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_DEFAULT_MHZ)
129
130#define IOC4_CALIBRATE_END \
131 (IOC4_CALIBRATE_CYCLES + IOC4_CALIBRATE_DISCARD)
132
133#define IOC4_INT_OUT_MODE_TOGGLE 0x7 /* Toggle INT_OUT every COUNT+1 ticks */
134
135/* Determines external interrupt output clock period of the PCI bus an
136 * IOC4 is attached to. This value can be used to determine the PCI
137 * bus speed.
138 *
139 * IOC4 has a design feature that various internal timers are derived from
140 * the PCI bus clock. This causes IOC4 device drivers to need to take the
141 * bus speed into account when setting various register values (e.g. INT_OUT
142 * register COUNT field, UART divisors, etc). Since this information is
143 * needed by several subdrivers, it is determined by the main IOC4 driver,
144 * even though the following code utilizes external interrupt registers
145 * to perform the speed calculation.
146 */
147static void
148ioc4_clock_calibrate(struct ioc4_driver_data *idd)
149{
150 extern unsigned long sn_rtc_cycles_per_second;
151 union ioc4_int_out int_out;
152 union ioc4_gpcr gpcr;
153 unsigned int state, last_state = 1;
154 uint64_t start = 0, end, period;
155 unsigned int count = 0;
156
157 /* Enable output */
158 gpcr.raw = 0;
159 gpcr.fields.dir = IOC4_GPCR_DIR_0;
160 gpcr.fields.int_out_en = 1;
161 writel(gpcr.raw, &idd->idd_misc_regs->gpcr_s.raw);
162
163 /* Reset to power-on state */
164 writel(0, &idd->idd_misc_regs->int_out.raw);
165 mmiowb();
166
167 printk(KERN_INFO
168 "%s: Calibrating PCI bus speed "
169 "for pci_dev %s ... ", __FUNCTION__, pci_name(idd->idd_pdev));
170 /* Set up square wave */
171 int_out.raw = 0;
172 int_out.fields.count = IOC4_CALIBRATE_COUNT;
173 int_out.fields.mode = IOC4_INT_OUT_MODE_TOGGLE;
174 int_out.fields.diag = 0;
175 writel(int_out.raw, &idd->idd_misc_regs->int_out.raw);
176 mmiowb();
177
178 /* Check square wave period averaged over some number of cycles */
179 do {
180 int_out.raw = readl(&idd->idd_misc_regs->int_out.raw);
181 state = int_out.fields.int_out;
182 if (!last_state && state) {
183 count++;
184 if (count == IOC4_CALIBRATE_END) {
185 end = rtc_time();
186 break;
187 } else if (count == IOC4_CALIBRATE_DISCARD)
188 start = rtc_time();
189 }
190 last_state = state;
191 } while (1);
192
193 /* Calculation rearranged to preserve intermediate precision.
194 * Logically:
195 * 1. "end - start" gives us number of RTC cycles over all the
196 * square wave cycles measured.
197 * 2. Divide by number of square wave cycles to get number of
198 * RTC cycles per square wave cycle.
199 * 3. Divide by 2*(int_out.fields.count+1), which is the formula
200 * by which the IOC4 generates the square wave, to get the
201 * number of RTC cycles per IOC4 INT_OUT count.
202 * 4. Divide by sn_rtc_cycles_per_second to get seconds per
203 * count.
204 * 5. Multiply by 1E9 to get nanoseconds per count.
205 */
206 period = ((end - start) * 1000000000) /
207 (IOC4_CALIBRATE_CYCLES * 2 * (IOC4_CALIBRATE_COUNT + 1)
208 * sn_rtc_cycles_per_second);
209
210 /* Bounds check the result. */
211 if (period > IOC4_CALIBRATE_LOW_LIMIT ||
212 period < IOC4_CALIBRATE_HIGH_LIMIT) {
213 printk("failed. Assuming PCI clock ticks are %d ns.\n",
214 IOC4_CALIBRATE_DEFAULT / IOC4_EXTINT_COUNT_DIVISOR);
215 period = IOC4_CALIBRATE_DEFAULT;
216 } else {
217 printk("succeeded. PCI clock ticks are %ld ns.\n",
218 period / IOC4_EXTINT_COUNT_DIVISOR);
219 }
220
221 /* Remember results. We store the extint clock period rather
222 * than the PCI clock period so that greater precision is
223 * retained. Divide by IOC4_EXTINT_COUNT_DIVISOR to get
224 * PCI clock period.
225 */
226 idd->count_period = period;
227}
228
104/* Adds a new instance of an IOC4 card */ 229/* Adds a new instance of an IOC4 card */
105static int 230static int
106ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) 231ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
@@ -170,6 +295,9 @@ ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
170 pci_write_config_dword(idd->idd_pdev, PCI_COMMAND, 295 pci_write_config_dword(idd->idd_pdev, PCI_COMMAND,
171 pcmd | PCI_COMMAND_PARITY | PCI_COMMAND_SERR); 296 pcmd | PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
172 297
298 /* Determine PCI clock */
299 ioc4_clock_calibrate(idd);
300
173 /* Disable/clear all interrupts. Need to do this here lest 301 /* Disable/clear all interrupts. Need to do this here lest
174 * one submodule request the shared IOC4 IRQ, but interrupt 302 * one submodule request the shared IOC4 IRQ, but interrupt
175 * is generated by a different subdevice. 303 * is generated by a different subdevice.
diff --git a/include/linux/ioc4.h b/include/linux/ioc4.h
index 729bfa4c4ac5..3dd18b785ebd 100644
--- a/include/linux/ioc4.h
+++ b/include/linux/ioc4.h
@@ -11,6 +11,14 @@
11 11
12#include <linux/interrupt.h> 12#include <linux/interrupt.h>
13 13
14/***************
15 * Definitions *
16 ***************/
17
18/* Miscellaneous values inherent to hardware */
19
20#define IOC4_EXTINT_COUNT_DIVISOR 520 /* PCI clocks per COUNT tick */
21
14/*********************************** 22/***********************************
15 * Structures needed by subdrivers * 23 * Structures needed by subdrivers *
16 ***********************************/ 24 ***********************************/
@@ -119,19 +127,34 @@ struct ioc4_misc_regs {
119 } gppr[8]; /* Generic PIO pins */ 127 } gppr[8]; /* Generic PIO pins */
120}; 128};
121 129
122/* One of these per IOC4 130/* Masks for GPCR DIR pins */
123 * 131#define IOC4_GPCR_DIR_0 0x01 /* External interrupt output */
124 * The idd_serial_data field is present here, even though it's used 132#define IOC4_GPCR_DIR_1 0x02 /* External interrupt input */
125 * solely by the serial subdriver, because the main IOC4 module 133#define IOC4_GPCR_DIR_2 0x04
126 * properly owns pci_{get,set}_drvdata functionality. This field 134#define IOC4_GPCR_DIR_3 0x08 /* Keyboard/mouse presence */
127 * allows that subdriver to stash its own drvdata somewhere. 135#define IOC4_GPCR_DIR_4 0x10 /* Ser. port 0 xcvr select (0=232, 1=422) */
128 */ 136#define IOC4_GPCR_DIR_5 0x20 /* Ser. port 1 xcvr select (0=232, 1=422) */
137#define IOC4_GPCR_DIR_6 0x40 /* Ser. port 2 xcvr select (0=232, 1=422) */
138#define IOC4_GPCR_DIR_7 0x80 /* Ser. port 3 xcvr select (0=232, 1=422) */
139
140/* Masks for GPCR EDGE pins */
141#define IOC4_GPCR_EDGE_0 0x01
142#define IOC4_GPCR_EDGE_1 0x02 /* External interrupt input */
143#define IOC4_GPCR_EDGE_2 0x04
144#define IOC4_GPCR_EDGE_3 0x08
145#define IOC4_GPCR_EDGE_4 0x10
146#define IOC4_GPCR_EDGE_5 0x20
147#define IOC4_GPCR_EDGE_6 0x40
148#define IOC4_GPCR_EDGE_7 0x80
149
150/* One of these per IOC4 */
129struct ioc4_driver_data { 151struct ioc4_driver_data {
130 struct list_head idd_list; 152 struct list_head idd_list;
131 unsigned long idd_bar0; 153 unsigned long idd_bar0;
132 struct pci_dev *idd_pdev; 154 struct pci_dev *idd_pdev;
133 const struct pci_device_id *idd_pci_id; 155 const struct pci_device_id *idd_pci_id;
134 struct __iomem ioc4_misc_regs *idd_misc_regs; 156 struct __iomem ioc4_misc_regs *idd_misc_regs;
157 unsigned long count_period;
135 void *idd_serial_data; 158 void *idd_serial_data;
136}; 159};
137 160