Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Brent Casavant and committed by
Linus Torvalds
d4c477ca e5d310b3

+177 -21
+18 -13
drivers/serial/ioc4_serial.c
··· 299 299 } ioc4_serial; 300 300 301 301 /* UART clock speed */ 302 - #define IOC4_SER_XIN_CLK IOC4_SER_XIN_CLK_66 303 302 #define IOC4_SER_XIN_CLK_66 66666667 304 303 #define IOC4_SER_XIN_CLK_33 33333333 305 304 ··· 1011 1012 * ioc4_attach_local - Device initialization. 1012 1013 * Called at *_attach() time for each 1013 1014 * IOC4 with serial ports in the system. 1014 - * @control: ioc4_control ptr 1015 - * @pdev: PCI handle for this device 1016 - * @soft: soft struct for this device 1017 - * @ioc4: ioc4 mem space 1015 + * @idd: Master module data for this IOC4 1018 1016 */ 1019 - static int inline ioc4_attach_local(struct pci_dev *pdev, 1020 - struct ioc4_control *control, 1021 - struct ioc4_soft *soft, void __iomem *ioc4_misc, 1022 - void __iomem *ioc4_serial) 1017 + static int inline ioc4_attach_local(struct ioc4_driver_data *idd) 1023 1018 { 1024 1019 struct ioc4_port *port; 1025 1020 struct ioc4_port *ports[IOC4_NUM_SERIAL_PORTS]; 1026 1021 int port_number; 1027 1022 uint16_t ioc4_revid_min = 62; 1028 1023 uint16_t ioc4_revid; 1024 + struct pci_dev *pdev = idd->idd_pdev; 1025 + struct ioc4_control* control = idd->idd_serial_data; 1026 + struct ioc4_soft *soft = control->ic_soft; 1027 + void __iomem *ioc4_misc = idd->idd_misc_regs; 1028 + void __iomem *ioc4_serial = soft->is_ioc4_serial_addr; 1029 1029 1030 1030 /* IOC4 firmware must be at least rev 62 */ 1031 1031 pci_read_config_word(pdev, PCI_COMMAND_SPECIAL, &ioc4_revid); ··· 1061 1063 port->ip_ioc4_soft = soft; 1062 1064 port->ip_pdev = pdev; 1063 1065 port->ip_ienb = 0; 1064 - port->ip_pci_bus_speed = IOC4_SER_XIN_CLK; 1066 + /* Use baud rate calculations based on detected PCI 1067 + * bus speed. Simply test whether the PCI clock is 1068 + * running closer to 66MHz or 33MHz. 1069 + */ 1070 + if (idd->count_period/IOC4_EXTINT_COUNT_DIVISOR < 20) { 1071 + port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_66; 1072 + } else { 1073 + port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_33; 1074 + } 1065 1075 port->ip_baud = 9600; 1066 1076 port->ip_control = control; 1067 1077 port->ip_mem = ioc4_misc; ··· 2739 2733 "%s : request_irq fails for IRQ 0x%x\n ", 2740 2734 __FUNCTION__, idd->idd_pdev->irq); 2741 2735 } 2742 - if ((ret = ioc4_attach_local(idd->idd_pdev, control, soft, 2743 - soft->is_ioc4_misc_addr, 2744 - soft->is_ioc4_serial_addr))) 2736 + ret = ioc4_attach_local(idd); 2737 + if (ret) 2745 2738 goto out4; 2746 2739 2747 2740 /* register port with the serial core */
+1 -1
drivers/sn/Kconfig
··· 6 6 7 7 config SGI_IOC4 8 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 10 default m 11 11 ---help--- 12 12 This option enables basic support for the SGI IOC4-based Base IO
+128
drivers/sn/ioc4.c
··· 29 29 #include <linux/module.h> 30 30 #include <linux/pci.h> 31 31 #include <linux/ioc4.h> 32 + #include <linux/mmtimer.h> 33 + #include <linux/rtc.h> 32 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 54 * Submodule management * ··· 120 101 * Device management * 121 102 *********************/ 122 103 104 + #define IOC4_CALIBRATE_LOW_LIMIT \ 105 + (1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_LOW_MHZ) 106 + #define IOC4_CALIBRATE_HIGH_LIMIT \ 107 + (1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_HIGH_MHZ) 108 + #define IOC4_CALIBRATE_DEFAULT \ 109 + (1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_DEFAULT_MHZ) 110 + 111 + #define IOC4_CALIBRATE_END \ 112 + (IOC4_CALIBRATE_CYCLES + IOC4_CALIBRATE_DISCARD) 113 + 114 + #define IOC4_INT_OUT_MODE_TOGGLE 0x7 /* Toggle INT_OUT every COUNT+1 ticks */ 115 + 116 + /* Determines external interrupt output clock period of the PCI bus an 117 + * IOC4 is attached to. This value can be used to determine the PCI 118 + * bus speed. 119 + * 120 + * IOC4 has a design feature that various internal timers are derived from 121 + * the PCI bus clock. This causes IOC4 device drivers to need to take the 122 + * bus speed into account when setting various register values (e.g. INT_OUT 123 + * register COUNT field, UART divisors, etc). Since this information is 124 + * needed by several subdrivers, it is determined by the main IOC4 driver, 125 + * even though the following code utilizes external interrupt registers 126 + * to perform the speed calculation. 127 + */ 128 + static void 129 + ioc4_clock_calibrate(struct ioc4_driver_data *idd) 130 + { 131 + extern unsigned long sn_rtc_cycles_per_second; 132 + union ioc4_int_out int_out; 133 + union ioc4_gpcr gpcr; 134 + unsigned int state, last_state = 1; 135 + uint64_t start = 0, end, period; 136 + unsigned int count = 0; 137 + 138 + /* Enable output */ 139 + gpcr.raw = 0; 140 + gpcr.fields.dir = IOC4_GPCR_DIR_0; 141 + gpcr.fields.int_out_en = 1; 142 + writel(gpcr.raw, &idd->idd_misc_regs->gpcr_s.raw); 143 + 144 + /* Reset to power-on state */ 145 + writel(0, &idd->idd_misc_regs->int_out.raw); 146 + mmiowb(); 147 + 148 + printk(KERN_INFO 149 + "%s: Calibrating PCI bus speed " 150 + "for pci_dev %s ... ", __FUNCTION__, pci_name(idd->idd_pdev)); 151 + /* Set up square wave */ 152 + int_out.raw = 0; 153 + int_out.fields.count = IOC4_CALIBRATE_COUNT; 154 + int_out.fields.mode = IOC4_INT_OUT_MODE_TOGGLE; 155 + int_out.fields.diag = 0; 156 + writel(int_out.raw, &idd->idd_misc_regs->int_out.raw); 157 + mmiowb(); 158 + 159 + /* Check square wave period averaged over some number of cycles */ 160 + do { 161 + int_out.raw = readl(&idd->idd_misc_regs->int_out.raw); 162 + state = int_out.fields.int_out; 163 + if (!last_state && state) { 164 + count++; 165 + if (count == IOC4_CALIBRATE_END) { 166 + end = rtc_time(); 167 + break; 168 + } else if (count == IOC4_CALIBRATE_DISCARD) 169 + start = rtc_time(); 170 + } 171 + last_state = state; 172 + } while (1); 173 + 174 + /* Calculation rearranged to preserve intermediate precision. 175 + * Logically: 176 + * 1. "end - start" gives us number of RTC cycles over all the 177 + * square wave cycles measured. 178 + * 2. Divide by number of square wave cycles to get number of 179 + * RTC cycles per square wave cycle. 180 + * 3. Divide by 2*(int_out.fields.count+1), which is the formula 181 + * by which the IOC4 generates the square wave, to get the 182 + * number of RTC cycles per IOC4 INT_OUT count. 183 + * 4. Divide by sn_rtc_cycles_per_second to get seconds per 184 + * count. 185 + * 5. Multiply by 1E9 to get nanoseconds per count. 186 + */ 187 + period = ((end - start) * 1000000000) / 188 + (IOC4_CALIBRATE_CYCLES * 2 * (IOC4_CALIBRATE_COUNT + 1) 189 + * sn_rtc_cycles_per_second); 190 + 191 + /* Bounds check the result. */ 192 + if (period > IOC4_CALIBRATE_LOW_LIMIT || 193 + period < IOC4_CALIBRATE_HIGH_LIMIT) { 194 + printk("failed. Assuming PCI clock ticks are %d ns.\n", 195 + IOC4_CALIBRATE_DEFAULT / IOC4_EXTINT_COUNT_DIVISOR); 196 + period = IOC4_CALIBRATE_DEFAULT; 197 + } else { 198 + printk("succeeded. PCI clock ticks are %ld ns.\n", 199 + period / IOC4_EXTINT_COUNT_DIVISOR); 200 + } 201 + 202 + /* Remember results. We store the extint clock period rather 203 + * than the PCI clock period so that greater precision is 204 + * retained. Divide by IOC4_EXTINT_COUNT_DIVISOR to get 205 + * PCI clock period. 206 + */ 207 + idd->count_period = period; 208 + } 209 + 123 210 /* Adds a new instance of an IOC4 card */ 124 211 static int 125 212 ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) ··· 294 169 pci_read_config_dword(idd->idd_pdev, PCI_COMMAND, &pcmd); 295 170 pci_write_config_dword(idd->idd_pdev, PCI_COMMAND, 296 171 pcmd | PCI_COMMAND_PARITY | PCI_COMMAND_SERR); 172 + 173 + /* Determine PCI clock */ 174 + ioc4_clock_calibrate(idd); 297 175 298 176 /* Disable/clear all interrupts. Need to do this here lest 299 177 * one submodule request the shared IOC4 IRQ, but interrupt
+30 -7
include/linux/ioc4.h
··· 11 11 12 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 23 * Structures needed by subdrivers * 16 24 ***********************************/ ··· 127 119 } gppr[8]; /* Generic PIO pins */ 128 120 }; 129 121 130 - /* One of these per IOC4 131 - * 132 - * The idd_serial_data field is present here, even though it's used 133 - * solely by the serial subdriver, because the main IOC4 module 134 - * properly owns pci_{get,set}_drvdata functionality. This field 135 - * allows that subdriver to stash its own drvdata somewhere. 136 - */ 122 + /* Masks for GPCR DIR pins */ 123 + #define IOC4_GPCR_DIR_0 0x01 /* External interrupt output */ 124 + #define IOC4_GPCR_DIR_1 0x02 /* External interrupt input */ 125 + #define IOC4_GPCR_DIR_2 0x04 126 + #define IOC4_GPCR_DIR_3 0x08 /* Keyboard/mouse presence */ 127 + #define IOC4_GPCR_DIR_4 0x10 /* Ser. port 0 xcvr select (0=232, 1=422) */ 128 + #define IOC4_GPCR_DIR_5 0x20 /* Ser. port 1 xcvr select (0=232, 1=422) */ 129 + #define IOC4_GPCR_DIR_6 0x40 /* Ser. port 2 xcvr select (0=232, 1=422) */ 130 + #define IOC4_GPCR_DIR_7 0x80 /* Ser. port 3 xcvr select (0=232, 1=422) */ 131 + 132 + /* Masks for GPCR EDGE pins */ 133 + #define IOC4_GPCR_EDGE_0 0x01 134 + #define IOC4_GPCR_EDGE_1 0x02 /* External interrupt input */ 135 + #define IOC4_GPCR_EDGE_2 0x04 136 + #define IOC4_GPCR_EDGE_3 0x08 137 + #define IOC4_GPCR_EDGE_4 0x10 138 + #define IOC4_GPCR_EDGE_5 0x20 139 + #define IOC4_GPCR_EDGE_6 0x40 140 + #define IOC4_GPCR_EDGE_7 0x80 141 + 142 + /* One of these per IOC4 */ 137 143 struct ioc4_driver_data { 138 144 struct list_head idd_list; 139 145 unsigned long idd_bar0; 140 146 struct pci_dev *idd_pdev; 141 147 const struct pci_device_id *idd_pci_id; 142 148 struct __iomem ioc4_misc_regs *idd_misc_regs; 149 + unsigned long count_period; 143 150 void *idd_serial_data; 144 151 }; 145 152