| 1 | --- /dev/null |
| 2 | +++ b/arch/powerpc/platforms/83xx/rbppc.c |
| 3 | @@ -0,0 +1,316 @@ |
| 4 | +/* |
| 5 | + * Copyright (C) 2008-2009 Noah Fontes <nfontes@transtruct.org> |
| 6 | + * Copyright (C) 2009 Michael Guntsche <mike@it-loops.com> |
| 7 | + * Copyright (C) Mikrotik 2007 |
| 8 | + * |
| 9 | + * This program is free software; you can redistribute it and/or modify it |
| 10 | + * under the terms of the GNU General Public License as published by the |
| 11 | + * Free Software Foundation; either version 2 of the License, or (at your |
| 12 | + * option) any later version. |
| 13 | + */ |
| 14 | + |
| 15 | +#include <linux/delay.h> |
| 16 | +#include <linux/root_dev.h> |
| 17 | +#include <linux/initrd.h> |
| 18 | +#include <linux/interrupt.h> |
| 19 | +#include <linux/of_platform.h> |
| 20 | +#include <linux/of_device.h> |
| 21 | +#include <linux/of_platform.h> |
| 22 | +#include <asm/time.h> |
| 23 | +#include <asm/ipic.h> |
| 24 | +#include <asm/udbg.h> |
| 25 | +#include <asm/qe.h> |
| 26 | +#include <asm/qe_ic.h> |
| 27 | +#include <sysdev/fsl_soc.h> |
| 28 | +#include <sysdev/fsl_pci.h> |
| 29 | +#include "mpc83xx.h" |
| 30 | + |
| 31 | +#define SYSCTL 0x100 |
| 32 | +#define SICRL 0x014 |
| 33 | + |
| 34 | +#define GTCFR2 0x04 |
| 35 | +#define GTMDR4 0x22 |
| 36 | +#define GTRFR4 0x26 |
| 37 | +#define GTCNR4 0x2e |
| 38 | +#define GTVER4 0x36 |
| 39 | +#define GTPSR4 0x3e |
| 40 | + |
| 41 | +#define GTCFR_BCM 0x40 |
| 42 | +#define GTCFR_STP4 0x20 |
| 43 | +#define GTCFR_RST4 0x10 |
| 44 | +#define GTCFR_STP3 0x02 |
| 45 | +#define GTCFR_RST3 0x01 |
| 46 | + |
| 47 | +#define GTMDR_ORI 0x10 |
| 48 | +#define GTMDR_FRR 0x08 |
| 49 | +#define GTMDR_ICLK16 0x04 |
| 50 | + |
| 51 | +extern int par_io_data_set(u8 port, u8 pin, u8 val); |
| 52 | +extern int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain, |
| 53 | + int assignment, int has_irq); |
| 54 | + |
| 55 | +static unsigned timer_freq; |
| 56 | +static void *gtm; |
| 57 | + |
| 58 | +static int beeper_irq; |
| 59 | +static unsigned beeper_gpio_pin[2]; |
| 60 | + |
| 61 | +irqreturn_t rbppc_timer_irq(int irq, void *ptr) |
| 62 | +{ |
| 63 | + static int toggle = 0; |
| 64 | + |
| 65 | + par_io_data_set(beeper_gpio_pin[0], beeper_gpio_pin[1], toggle); |
| 66 | + toggle = !toggle; |
| 67 | + |
| 68 | + /* ack interrupt */ |
| 69 | + out_be16(gtm + GTVER4, 3); |
| 70 | + |
| 71 | + return IRQ_HANDLED; |
| 72 | +} |
| 73 | + |
| 74 | +void rbppc_beep(unsigned freq) |
| 75 | +{ |
| 76 | + unsigned gtmdr; |
| 77 | + |
| 78 | + if (freq > 5000) freq = 5000; |
| 79 | + |
| 80 | + if (!gtm) |
| 81 | + return; |
| 82 | + if (!freq) { |
| 83 | + out_8(gtm + GTCFR2, GTCFR_STP4 | GTCFR_STP3); |
| 84 | + return; |
| 85 | + } |
| 86 | + |
| 87 | + out_8(gtm + GTCFR2, GTCFR_RST4 | GTCFR_STP3); |
| 88 | + out_be16(gtm + GTPSR4, 255); |
| 89 | + gtmdr = GTMDR_FRR | GTMDR_ICLK16; |
| 90 | + if (beeper_irq != NO_IRQ) gtmdr |= GTMDR_ORI; |
| 91 | + out_be16(gtm + GTMDR4, gtmdr); |
| 92 | + out_be16(gtm + GTVER4, 3); |
| 93 | + |
| 94 | + out_be16(gtm + GTRFR4, timer_freq / 16 / 256 / freq / 2); |
| 95 | + out_be16(gtm + GTCNR4, 0); |
| 96 | +} |
| 97 | +EXPORT_SYMBOL(rbppc_beep); |
| 98 | + |
| 99 | +static void __init rbppc_setup_arch(void) |
| 100 | +{ |
| 101 | + struct device_node *np; |
| 102 | + |
| 103 | + np = of_find_node_by_type(NULL, "cpu"); |
| 104 | + if (np) { |
| 105 | + const unsigned *fp = of_get_property(np, "clock-frequency", NULL); |
| 106 | + loops_per_jiffy = fp ? *fp / HZ : 0; |
| 107 | + |
| 108 | + of_node_put(np); |
| 109 | + } |
| 110 | + |
| 111 | + np = of_find_node_by_name(NULL, "serial"); |
| 112 | + if (np) { |
| 113 | + timer_freq = |
| 114 | + *(unsigned *) of_get_property(np, "clock-frequency", NULL); |
| 115 | + of_node_put(np); |
| 116 | + } |
| 117 | + |
| 118 | +#ifdef CONFIG_PCI |
| 119 | + np = of_find_node_by_type(NULL, "pci"); |
| 120 | + if (np) { |
| 121 | + mpc83xx_add_bridge(np); |
| 122 | + } |
| 123 | +#endif |
| 124 | + |
| 125 | +#ifdef CONFIG_QUICC_ENGINE |
| 126 | + np = of_find_node_by_name(np, "par_io"); |
| 127 | + if (np) { |
| 128 | + qe_reset(); |
| 129 | + par_io_init(np); |
| 130 | + of_node_put(np); |
| 131 | + |
| 132 | + np = NULL; |
| 133 | + while (1) { |
| 134 | + np = of_find_node_by_name(np, "ucc"); |
| 135 | + if (!np) break; |
| 136 | + |
| 137 | + par_io_of_config(np); |
| 138 | + } |
| 139 | + } |
| 140 | +#endif |
| 141 | + |
| 142 | +} |
| 143 | + |
| 144 | +void __init rbppc_init_IRQ(void) |
| 145 | +{ |
| 146 | + struct device_node *np; |
| 147 | + |
| 148 | + np = of_find_node_by_type(NULL, "ipic"); |
| 149 | + if (np) { |
| 150 | + ipic_init(np, 0); |
| 151 | + ipic_set_default_priority(); |
| 152 | + of_node_put(np); |
| 153 | + } |
| 154 | + |
| 155 | +#ifdef CONFIG_QUICC_ENGINE |
| 156 | + np = of_find_node_by_type(NULL, "qeic"); |
| 157 | + if (np) { |
| 158 | + qe_ic_init(np, 0, qe_ic_cascade_low_ipic, qe_ic_cascade_high_ipic); |
| 159 | + of_node_put(np); |
| 160 | + } |
| 161 | +#endif |
| 162 | +} |
| 163 | + |
| 164 | +static int __init rbppc_probe(void) |
| 165 | +{ |
| 166 | + char *model; |
| 167 | + |
| 168 | + model = of_get_flat_dt_prop(of_get_flat_dt_root(), "model", NULL); |
| 169 | + |
| 170 | + if (!model) |
| 171 | + return 0; |
| 172 | + |
| 173 | + if (strcmp(model, "RB600") == 0) |
| 174 | + return 1; |
| 175 | + |
| 176 | + return 0; |
| 177 | +} |
| 178 | + |
| 179 | +static void __init rbppc_beeper_init(struct device_node *beeper) |
| 180 | +{ |
| 181 | + struct resource res; |
| 182 | + struct device_node *gpio; |
| 183 | + const unsigned *pin; |
| 184 | + const unsigned *gpio_id; |
| 185 | + |
| 186 | + if (of_address_to_resource(beeper, 0, &res)) { |
| 187 | + printk(KERN_ERR "rbppc_beeper_init(%s): Beeper error: No region specified\n", beeper->full_name); |
| 188 | + return; |
| 189 | + } |
| 190 | + |
| 191 | + pin = of_get_property(beeper, "gpio", NULL); |
| 192 | + if (pin) { |
| 193 | + gpio = of_find_node_by_phandle(pin[0]); |
| 194 | + |
| 195 | + if (!gpio) { |
| 196 | + printk(KERN_ERR "rbppc_beeper_init(%s): Beeper error: GPIO handle %x not found\n", beeper->full_name, pin[0]); |
| 197 | + return; |
| 198 | + } |
| 199 | + |
| 200 | + gpio_id = of_get_property(gpio, "device-id", NULL); |
| 201 | + if (!gpio_id) { |
| 202 | + printk(KERN_ERR "rbppc_beeper_init(%s): Beeper error: No device-id specified in GPIO\n", beeper->full_name); |
| 203 | + return; |
| 204 | + } |
| 205 | + |
| 206 | + beeper_gpio_pin[0] = *gpio_id; |
| 207 | + beeper_gpio_pin[1] = pin[1]; |
| 208 | + |
| 209 | + par_io_config_pin(*gpio_id, pin[1], 1, 0, 0, 0); |
| 210 | + } else { |
| 211 | + void *sysctl; |
| 212 | + |
| 213 | + sysctl = ioremap_nocache(get_immrbase() + SYSCTL, 0x100); |
| 214 | + out_be32(sysctl + SICRL, |
| 215 | + in_be32(sysctl + SICRL) | (1 << (31 - 19))); |
| 216 | + iounmap(sysctl); |
| 217 | + } |
| 218 | + |
| 219 | + gtm = ioremap_nocache(res.start, res.end - res.start + 1); |
| 220 | + |
| 221 | + beeper_irq = irq_of_parse_and_map(beeper, 0); |
| 222 | + if (beeper_irq != NO_IRQ) { |
| 223 | + int e = request_irq(beeper_irq, rbppc_timer_irq, 0, "beeper", NULL); |
| 224 | + if (e) { |
| 225 | + printk(KERN_ERR "rbppc_beeper_init(%s): Request of beeper irq failed!\n", beeper->full_name); |
| 226 | + } |
| 227 | + } |
| 228 | +} |
| 229 | + |
| 230 | +#define SBIT(x) (0x80000000 >> (x)) |
| 231 | +#define DBIT(x, y) ((y) << (32 - (((x % 16) + 1) * 2))) |
| 232 | + |
| 233 | +#define SICRL_RB600(x) ((x) + (0x114 >> 2)) |
| 234 | +#define GPIO_DIR_RB600(x) ((x) + (0xc00 >> 2)) |
| 235 | +#define GPIO_DATA_RB600(x) ((x) + (0xc08 >> 2)) |
| 236 | + |
| 237 | +static void rbppc_restart(char *cmd) |
| 238 | +{ |
| 239 | + __be32 __iomem *reg; |
| 240 | + |
| 241 | + reg = ioremap(get_immrbase(), 0x1000); |
| 242 | + local_irq_disable(); |
| 243 | + out_be32(SICRL_RB600(reg), in_be32(SICRL_RB600(reg)) & ~0x00800000); |
| 244 | + out_be32(GPIO_DIR_RB600(reg), in_be32(GPIO_DIR_RB600(reg)) | SBIT(2)); |
| 245 | + out_be32(GPIO_DATA_RB600(reg), in_be32(GPIO_DATA_RB600(reg)) & ~SBIT(2)); |
| 246 | + |
| 247 | + while (1); |
| 248 | +} |
| 249 | + |
| 250 | +static void rbppc_halt(void) |
| 251 | +{ |
| 252 | + while (1); |
| 253 | +} |
| 254 | + |
| 255 | +static struct of_device_id rbppc_ids[] = { |
| 256 | + { .type = "soc", }, |
| 257 | + { .compatible = "soc", }, |
| 258 | + { .compatible = "simple-bus", }, |
| 259 | + { .compatible = "gianfar", }, |
| 260 | + { }, |
| 261 | +}; |
| 262 | + |
| 263 | +static int __init rbppc_declare_of_platform_devices(void) |
| 264 | +{ |
| 265 | + struct device_node *np; |
| 266 | + unsigned idx; |
| 267 | + |
| 268 | + of_platform_bus_probe(NULL, rbppc_ids, NULL); |
| 269 | + |
| 270 | + np = of_find_node_by_type(NULL, "mdio"); |
| 271 | + if (np) { |
| 272 | + unsigned len; |
| 273 | + unsigned *res; |
| 274 | + const unsigned *eres; |
| 275 | + struct device_node *ep; |
| 276 | + |
| 277 | + ep = of_find_compatible_node(NULL, "network", "ucc_geth"); |
| 278 | + if (ep) { |
| 279 | + eres = of_get_property(ep, "reg", &len); |
| 280 | + res = (unsigned *) of_get_property(np, "reg", &len); |
| 281 | + if (res && eres) { |
| 282 | + res[0] = eres[0] + 0x120; |
| 283 | + } |
| 284 | + } |
| 285 | + } |
| 286 | + |
| 287 | + np = of_find_node_by_name(NULL, "nand"); |
| 288 | + if (np) { |
| 289 | + of_platform_device_create(np, "nand", NULL); |
| 290 | + } |
| 291 | + |
| 292 | + idx = 0; |
| 293 | + for_each_node_by_type(np, "rb,cf") { |
| 294 | + char dev_name[12]; |
| 295 | + snprintf(dev_name, sizeof(dev_name), "cf.%u", idx); |
| 296 | + of_platform_device_create(np, dev_name, NULL); |
| 297 | + ++idx; |
| 298 | + } |
| 299 | + |
| 300 | + np = of_find_node_by_name(NULL, "beeper"); |
| 301 | + if (np) { |
| 302 | + rbppc_beeper_init(np); |
| 303 | + } |
| 304 | + |
| 305 | + return 0; |
| 306 | +} |
| 307 | +device_initcall(rbppc_declare_of_platform_devices); |
| 308 | + |
| 309 | +define_machine(rb600) { |
| 310 | + .name = "MikroTik RouterBOARD 600 series", |
| 311 | + .probe = rbppc_probe, |
| 312 | + .setup_arch = rbppc_setup_arch, |
| 313 | + .init_IRQ = rbppc_init_IRQ, |
| 314 | + .get_irq = ipic_get_irq, |
| 315 | + .restart = rbppc_restart, |
| 316 | + .halt = rbppc_halt, |
| 317 | + .time_init = mpc83xx_time_init, |
| 318 | + .calibrate_decr = generic_calibrate_decr, |
| 319 | +}; |
| 320 | |