| 1 | |
| 2 | |
| 3 | * RackMac vu-meter driver
|
| 4 | *
|
| 5 | * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp.
|
| 6 | * <[email protected]>
|
| 7 | *
|
| 8 | * Support the CPU-meter LEDs of the Xserve G5
|
| 9 | *
|
| 10 | * TODO: Implement PWM to do variable intensity and provide userland
|
| 11 | * interface for fun. Also, the CPU-meter could be made nicer by being
|
| 12 | * a bit less "immediate" but giving instead a more average load over
|
| 13 | * time. Patches welcome :-)
|
| 14 |
|
| 15 | #undef DEBUG
|
| 16 |
|
| 17 | #include <linux/types.h>
|
| 18 | #include <linux/kernel.h>
|
| 19 | #include <linux/slab.h>
|
| 20 | #include <linux/device.h>
|
| 21 | #include <linux/interrupt.h>
|
| 22 | #include <linux/module.h>
|
| 23 | #include <linux/pci.h>
|
| 24 | #include <linux/dma-mapping.h>
|
| 25 | #include <linux/kernel_stat.h>
|
| 26 | #include <linux/of_address.h>
|
| 27 | #include <linux/of_irq.h>
|
| 28 |
|
| 29 | #include <asm/io.h>
|
| 30 | #include <asm/machdep.h>
|
| 31 | #include <asm/pmac_feature.h>
|
| 32 | #include <asm/dbdma.h>
|
| 33 | #include <asm/macio.h>
|
| 34 | #include <asm/keylargo.h>
|
| 35 |
|
| 36 |
|
| 37 | #define SAMPLE_COUNT 256
|
| 38 |
|
| 39 |
|
| 40 | #define CPU_SAMPLING_RATE 250
|
| 41 |
|
| 42 | struct rackmeter_dma {
|
| 43 | struct dbdma_cmd cmd[4] ____cacheline_aligned;
|
| 44 | u32 mark ____cacheline_aligned;
|
| 45 | u32 buf1[SAMPLE_COUNT] ____cacheline_aligned;
|
| 46 | u32 buf2[SAMPLE_COUNT] ____cacheline_aligned;
|
| 47 | } ____cacheline_aligned;
|
| 48 |
|
| 49 | struct rackmeter_cpu {
|
| 50 | struct delayed_work sniffer;
|
| 51 | struct rackmeter *rm;
|
| 52 | u64 prev_wall;
|
| 53 | u64 prev_idle;
|
| 54 | int zero;
|
| 55 | } ____cacheline_aligned;
|
| 56 |
|
| 57 | struct rackmeter {
|
| 58 | struct macio_dev *mdev;
|
| 59 | unsigned int irq;
|
| 60 | struct device_node *i2s;
|
| 61 | u8 *ubuf;
|
| 62 | struct dbdma_regs __iomem *dma_regs;
|
| 63 | void __iomem *i2s_regs;
|
| 64 | dma_addr_t dma_buf_p;
|
| 65 | struct rackmeter_dma *dma_buf_v;
|
| 66 | int stale_irq;
|
| 67 | struct rackmeter_cpu cpu[2];
|
| 68 | int paused;
|
| 69 | struct mutex sem;
|
| 70 | };
|
| 71 |
|
| 72 |
|
| 73 | static int rackmeter_ignore_nice;
|
| 74 |
|
| 75 |
|
| 76 | #define RACKMETER_MAGIC_GPIO 0x78
|
| 77 |
|
| 78 | |
| 79 | * a common header somewhere
|
| 80 |
|
| 81 | static inline u64 get_cpu_idle_time(unsigned int cpu)
|
| 82 | {
|
| 83 | struct kernel_cpustat *kcpustat = &kcpustat_cpu(cpu);
|
| 84 | u64 retval;
|
| 85 |
|
| 86 | retval = kcpustat->cpustat[CPUTIME_IDLE] +
|
| 87 | kcpustat->cpustat[CPUTIME_IOWAIT];
|
| 88 |
|
| 89 | if (rackmeter_ignore_nice)
|
| 90 | retval += kcpustat_field(CPUTIME_NICE, cpu);
|
| 91 |
|
| 92 | return retval;
|
| 93 | }
|
| 94 |
|
| 95 | static void rackmeter_setup_i2s(struct rackmeter *rm)
|
| 96 | {
|
| 97 | struct macio_chip *macio = rm->mdev->bus->chip;
|
| 98 |
|
| 99 |
|
| 100 | pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, RACKMETER_MAGIC_GPIO, 5);
|
| 101 |
|
| 102 |
|
| 103 | |
| 104 | * clock sources
|
| 105 |
|
| 106 | pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, rm->i2s, 0, 1);
|
| 107 |
|
| 108 | |
| 109 | * This is a bit racy, thus we should add new platform functions to
|
| 110 | * handle that. snd-aoa needs that too
|
| 111 |
|
| 112 | MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_ENABLE);
|
| 113 | MACIO_BIC(KEYLARGO_FCR1, KL1_I2S0_CLK_ENABLE_BIT);
|
| 114 | (void)MACIO_IN32(KEYLARGO_FCR1);
|
| 115 | udelay(10);
|
| 116 |
|
| 117 | |
| 118 | * the OS X driver seems to use. We might want to play around
|
| 119 | * with the clock divisors later
|
| 120 |
|
| 121 | out_le32(rm->i2s_regs + 0x10, 0x01fa0000);
|
| 122 | (void)in_le32(rm->i2s_regs + 0x10);
|
| 123 | udelay(10);
|
| 124 |
|
| 125 |
|
| 126 | MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_CELL_ENABLE |
|
| 127 | KL1_I2S0_CLK_ENABLE_BIT);
|
| 128 | (void)MACIO_IN32(KEYLARGO_FCR1);
|
| 129 | udelay(10);
|
| 130 | }
|
| 131 |
|
| 132 | static void rackmeter_set_default_pattern(struct rackmeter *rm)
|
| 133 | {
|
| 134 | int i;
|
| 135 |
|
| 136 | for (i = 0; i < 16; i++) {
|
| 137 | if (i < 8)
|
| 138 | rm->ubuf[i] = (i & 1) * 255;
|
| 139 | else
|
| 140 | rm->ubuf[i] = ((~i) & 1) * 255;
|
| 141 | }
|
| 142 | }
|
| 143 |
|
| 144 | static void rackmeter_do_pause(struct rackmeter *rm, int pause)
|
| 145 | {
|
| 146 | struct rackmeter_dma *rdma = rm->dma_buf_v;
|
| 147 |
|
| 148 | pr_debug("rackmeter: %s\n", pause ? "paused" : "started");
|
| 149 |
|
| 150 | rm->paused = pause;
|
| 151 | if (pause) {
|
| 152 | DBDMA_DO_STOP(rm->dma_regs);
|
| 153 | return;
|
| 154 | }
|
| 155 | memset(rdma->buf1, 0, sizeof(rdma->buf1));
|
| 156 | memset(rdma->buf2, 0, sizeof(rdma->buf2));
|
| 157 |
|
| 158 | rm->dma_buf_v->mark = 0;
|
| 159 |
|
| 160 | mb();
|
| 161 | out_le32(&rm->dma_regs->cmdptr_hi, 0);
|
| 162 | out_le32(&rm->dma_regs->cmdptr, rm->dma_buf_p);
|
| 163 | out_le32(&rm->dma_regs->control, (RUN << 16) | RUN);
|
| 164 | }
|
| 165 |
|
| 166 | static void rackmeter_setup_dbdma(struct rackmeter *rm)
|
| 167 | {
|
| 168 | struct rackmeter_dma *db = rm->dma_buf_v;
|
| 169 | struct dbdma_cmd *cmd = db->cmd;
|
| 170 |
|
| 171 |
|
| 172 | DBDMA_DO_RESET(rm->dma_regs);
|
| 173 |
|
| 174 | pr_debug("rackmeter: mark offset=0x%zx\n",
|
| 175 | offsetof(struct rackmeter_dma, mark));
|
| 176 | pr_debug("rackmeter: buf1 offset=0x%zx\n",
|
| 177 | offsetof(struct rackmeter_dma, buf1));
|
| 178 | pr_debug("rackmeter: buf2 offset=0x%zx\n",
|
| 179 | offsetof(struct rackmeter_dma, buf2));
|
| 180 |
|
| 181 |
|
| 182 | memset(cmd, 0, 4 * sizeof(struct dbdma_cmd));
|
| 183 | cmd->req_count = cpu_to_le16(4);
|
| 184 | cmd->command = cpu_to_le16(STORE_WORD | INTR_ALWAYS | KEY_SYSTEM);
|
| 185 | cmd->phy_addr = cpu_to_le32(rm->dma_buf_p +
|
| 186 | offsetof(struct rackmeter_dma, mark));
|
| 187 | cmd->cmd_dep = cpu_to_le32(0x02000000);
|
| 188 | cmd++;
|
| 189 |
|
| 190 | cmd->req_count = cpu_to_le16(SAMPLE_COUNT * 4);
|
| 191 | cmd->command = cpu_to_le16(OUTPUT_MORE);
|
| 192 | cmd->phy_addr = cpu_to_le32(rm->dma_buf_p +
|
| 193 | offsetof(struct rackmeter_dma, buf1));
|
| 194 | cmd++;
|
| 195 |
|
| 196 | cmd->req_count = cpu_to_le16(4);
|
| 197 | cmd->command = cpu_to_le16(STORE_WORD | INTR_ALWAYS | KEY_SYSTEM);
|
| 198 | cmd->phy_addr = cpu_to_le32(rm->dma_buf_p +
|
| 199 | offsetof(struct rackmeter_dma, mark));
|
| 200 | cmd->cmd_dep = cpu_to_le32(0x01000000);
|
| 201 | cmd++;
|
| 202 |
|
| 203 | cmd->req_count = cpu_to_le16(SAMPLE_COUNT * 4);
|
| 204 | cmd->command = cpu_to_le16(OUTPUT_MORE | BR_ALWAYS);
|
| 205 | cmd->phy_addr = cpu_to_le32(rm->dma_buf_p +
|
| 206 | offsetof(struct rackmeter_dma, buf2));
|
| 207 | cmd->cmd_dep = cpu_to_le32(rm->dma_buf_p);
|
| 208 |
|
| 209 | rackmeter_do_pause(rm, 0);
|
| 210 | }
|
| 211 |
|
| 212 | static void rackmeter_do_timer(struct work_struct *work)
|
| 213 | {
|
| 214 | struct rackmeter_cpu *rcpu =
|
| 215 | container_of(work, struct rackmeter_cpu, sniffer.work);
|
| 216 | struct rackmeter *rm = rcpu->rm;
|
| 217 | unsigned int cpu = smp_processor_id();
|
| 218 | u64 cur_nsecs, total_idle_nsecs;
|
| 219 | u64 total_nsecs, idle_nsecs;
|
| 220 | int i, offset, load, cumm, pause;
|
| 221 |
|
| 222 | cur_nsecs = jiffies64_to_nsecs(get_jiffies_64());
|
| 223 | total_nsecs = cur_nsecs - rcpu->prev_wall;
|
| 224 | rcpu->prev_wall = cur_nsecs;
|
| 225 |
|
| 226 | total_idle_nsecs = get_cpu_idle_time(cpu);
|
| 227 | idle_nsecs = total_idle_nsecs - rcpu->prev_idle;
|
| 228 | idle_nsecs = min(idle_nsecs, total_nsecs);
|
| 229 | rcpu->prev_idle = total_idle_nsecs;
|
| 230 |
|
| 231 | |
| 232 | * we'll do better once we have actual PWM implemented
|
| 233 |
|
| 234 | load = div64_u64(9 * (total_nsecs - idle_nsecs), total_nsecs);
|
| 235 |
|
| 236 | offset = cpu << 3;
|
| 237 | cumm = 0;
|
| 238 | for (i = 0; i < 8; i++) {
|
| 239 | u8 ub = (load > i) ? 0xff : 0;
|
| 240 | rm->ubuf[i + offset] = ub;
|
| 241 | cumm |= ub;
|
| 242 | }
|
| 243 | rcpu->zero = (cumm == 0);
|
| 244 |
|
| 245 |
|
| 246 | pause = (rm->cpu[0].zero && rm->cpu[1].zero);
|
| 247 | if (pause != rm->paused) {
|
| 248 | mutex_lock(&rm->sem);
|
| 249 | pause = (rm->cpu[0].zero && rm->cpu[1].zero);
|
| 250 | rackmeter_do_pause(rm, pause);
|
| 251 | mutex_unlock(&rm->sem);
|
| 252 | }
|
| 253 | schedule_delayed_work_on(cpu, &rcpu->sniffer,
|
| 254 | msecs_to_jiffies(CPU_SAMPLING_RATE));
|
| 255 | }
|
| 256 |
|
| 257 | static void rackmeter_init_cpu_sniffer(struct rackmeter *rm)
|
| 258 | {
|
| 259 | unsigned int cpu;
|
| 260 |
|
| 261 | |
| 262 | * but that's really all we have on Apple Xserve. It doesn't
|
| 263 | * play very nice with CPU hotplug neither but we don't do that
|
| 264 | * on those machines yet
|
| 265 |
|
| 266 |
|
| 267 | rm->cpu[0].rm = rm;
|
| 268 | INIT_DELAYED_WORK(&rm->cpu[0].sniffer, rackmeter_do_timer);
|
| 269 | rm->cpu[1].rm = rm;
|
| 270 | INIT_DELAYED_WORK(&rm->cpu[1].sniffer, rackmeter_do_timer);
|
| 271 |
|
| 272 | for_each_online_cpu(cpu) {
|
| 273 | struct rackmeter_cpu *rcpu;
|
| 274 |
|
| 275 | if (cpu > 1)
|
| 276 | continue;
|
| 277 | rcpu = &rm->cpu[cpu];
|
| 278 | rcpu->prev_idle = get_cpu_idle_time(cpu);
|
| 279 | rcpu->prev_wall = jiffies64_to_nsecs(get_jiffies_64());
|
| 280 | schedule_delayed_work_on(cpu, &rm->cpu[cpu].sniffer,
|
| 281 | msecs_to_jiffies(CPU_SAMPLING_RATE));
|
| 282 | }
|
| 283 | }
|
| 284 |
|
| 285 | static void rackmeter_stop_cpu_sniffer(struct rackmeter *rm)
|
| 286 | {
|
| 287 | cancel_delayed_work_sync(&rm->cpu[0].sniffer);
|
| 288 | cancel_delayed_work_sync(&rm->cpu[1].sniffer);
|
| 289 | }
|
| 290 |
|
| 291 | static int rackmeter_setup(struct rackmeter *rm)
|
| 292 | {
|
| 293 | pr_debug("rackmeter: setting up i2s..\n");
|
| 294 | rackmeter_setup_i2s(rm);
|
| 295 |
|
| 296 | pr_debug("rackmeter: setting up default pattern..\n");
|
| 297 | rackmeter_set_default_pattern(rm);
|
| 298 |
|
| 299 | pr_debug("rackmeter: setting up dbdma..\n");
|
| 300 | rackmeter_setup_dbdma(rm);
|
| 301 |
|
| 302 | pr_debug("rackmeter: start CPU measurements..\n");
|
| 303 | rackmeter_init_cpu_sniffer(rm);
|
| 304 |
|
| 305 | printk(KERN_INFO "RackMeter initialized\n");
|
| 306 |
|
| 307 | return 0;
|
| 308 | }
|
| 309 |
|
| 310 |
|
| 311 | static u32 rackmeter_calc_sample(struct rackmeter *rm, unsigned int index)
|
| 312 | {
|
| 313 | int led;
|
| 314 | u32 sample = 0;
|
| 315 |
|
| 316 | for (led = 0; led < 16; led++) {
|
| 317 | sample >>= 1;
|
| 318 | sample |= ((rm->ubuf[led] >= 0x80) << 15);
|
| 319 | }
|
| 320 | return (sample << 17) | (sample >> 15);
|
| 321 | }
|
| 322 |
|
| 323 | static irqreturn_t rackmeter_irq(int irq, void *arg)
|
| 324 | {
|
| 325 | struct rackmeter *rm = arg;
|
| 326 | struct rackmeter_dma *db = rm->dma_buf_v;
|
| 327 | unsigned int mark, i;
|
| 328 | u32 *buf;
|
| 329 |
|
| 330 | |
| 331 | * check the status one day ... in case things go wrong, though
|
| 332 | * this never happened to me
|
| 333 |
|
| 334 | (void)in_le32(&rm->dma_regs->status);
|
| 335 |
|
| 336 |
|
| 337 | rmb();
|
| 338 |
|
| 339 |
|
| 340 | mark = db->mark;
|
| 341 | if (mark != 1 && mark != 2) {
|
| 342 | printk(KERN_WARNING "rackmeter: Incorrect DMA mark 0x%08x\n",
|
| 343 | mark);
|
| 344 |
|
| 345 | if (++rm->stale_irq > 3) {
|
| 346 | printk(KERN_ERR "rackmeter: Too many errors,"
|
| 347 | " stopping DMA\n");
|
| 348 | DBDMA_DO_RESET(rm->dma_regs);
|
| 349 | }
|
| 350 | return IRQ_HANDLED;
|
| 351 | }
|
| 352 |
|
| 353 |
|
| 354 | buf = mark == 1 ? db->buf1 : db->buf2;
|
| 355 |
|
| 356 | |
| 357 | * into the PWM bitmap for each LED.
|
| 358 |
|
| 359 | for (i = 0; i < SAMPLE_COUNT; i++)
|
| 360 | buf[i] = rackmeter_calc_sample(rm, i);
|
| 361 |
|
| 362 |
|
| 363 | return IRQ_HANDLED;
|
| 364 | }
|
| 365 |
|
| 366 | static int rackmeter_probe(struct macio_dev* mdev,
|
| 367 | const struct of_device_id *match)
|
| 368 | {
|
| 369 | struct device_node *i2s = NULL, *np = NULL;
|
| 370 | struct rackmeter *rm = NULL;
|
| 371 | struct resource ri2s, rdma;
|
| 372 | int rc = -ENODEV;
|
| 373 |
|
| 374 | pr_debug("rackmeter_probe()\n");
|
| 375 |
|
| 376 |
|
| 377 | for_each_child_of_node(mdev->ofdev.dev.of_node, i2s)
|
| 378 | if (of_node_name_eq(i2s, "i2s-a"))
|
| 379 | break;
|
| 380 |
|
| 381 | if (i2s == NULL) {
|
| 382 | pr_debug(" i2s-a child not found\n");
|
| 383 | goto bail;
|
| 384 | }
|
| 385 |
|
| 386 | for_each_child_of_node(i2s, np) {
|
| 387 | if (of_node_name_eq(np, "lightshow"))
|
| 388 | break;
|
| 389 | if (of_node_name_eq(np, "sound") &&
|
| 390 | of_property_present(np, "virtual"))
|
| 391 | break;
|
| 392 | }
|
| 393 | if (np == NULL) {
|
| 394 | pr_debug(" lightshow or sound+virtual child not found\n");
|
| 395 | goto bail;
|
| 396 | }
|
| 397 |
|
| 398 |
|
| 399 | rm = kzalloc_obj(*rm);
|
| 400 | if (rm == NULL) {
|
| 401 | printk(KERN_ERR "rackmeter: failed to allocate memory !\n");
|
| 402 | rc = -ENOMEM;
|
| 403 | goto bail_release;
|
| 404 | }
|
| 405 | rm->mdev = mdev;
|
| 406 | rm->i2s = i2s;
|
| 407 | mutex_init(&rm->sem);
|
| 408 | dev_set_drvdata(&mdev->ofdev.dev, rm);
|
| 409 |
|
| 410 | #if 0 |
| 411 | if (macio_resource_count(mdev) < 2 || macio_irq_count(mdev) < 2) {
|
| 412 | printk(KERN_ERR
|
| 413 | "rackmeter: found match but lacks resources: %pOF"
|
| 414 | " (%d resources, %d interrupts)\n",
|
| 415 | mdev->ofdev.dev.of_node);
|
| 416 | rc = -ENXIO;
|
| 417 | goto bail_free;
|
| 418 | }
|
| 419 | if (macio_request_resources(mdev, "rackmeter")) {
|
| 420 | printk(KERN_ERR
|
| 421 | "rackmeter: failed to request resources: %pOF\n",
|
| 422 | mdev->ofdev.dev.of_node);
|
| 423 | rc = -EBUSY;
|
| 424 | goto bail_free;
|
| 425 | }
|
| 426 | rm->irq = macio_irq(mdev, 1);
|
| 427 | #else
|
| 428 | rm->irq = irq_of_parse_and_map(i2s, 1);
|
| 429 | if (!rm->irq ||
|
| 430 | of_address_to_resource(i2s, 0, &ri2s) ||
|
| 431 | of_address_to_resource(i2s, 1, &rdma)) {
|
| 432 | printk(KERN_ERR
|
| 433 | "rackmeter: found match but lacks resources: %pOF",
|
| 434 | mdev->ofdev.dev.of_node);
|
| 435 | rc = -ENXIO;
|
| 436 | goto bail_free;
|
| 437 | }
|
| 438 | #endif
|
| 439 |
|
| 440 | pr_debug(" i2s @0x%08x\n", (unsigned int)ri2s.start);
|
| 441 | pr_debug(" dma @0x%08x\n", (unsigned int)rdma.start);
|
| 442 | pr_debug(" irq %d\n", rm->irq);
|
| 443 |
|
| 444 | rm->ubuf = (u8 *)__get_free_page(GFP_KERNEL);
|
| 445 | if (rm->ubuf == NULL) {
|
| 446 | printk(KERN_ERR
|
| 447 | "rackmeter: failed to allocate samples page !\n");
|
| 448 | rc = -ENOMEM;
|
| 449 | goto bail_release;
|
| 450 | }
|
| 451 |
|
| 452 | rm->dma_buf_v = dma_alloc_coherent(&macio_get_pci_dev(mdev)->dev,
|
| 453 | sizeof(struct rackmeter_dma),
|
| 454 | &rm->dma_buf_p, GFP_KERNEL);
|
| 455 | if (rm->dma_buf_v == NULL) {
|
| 456 | printk(KERN_ERR
|
| 457 | "rackmeter: failed to allocate dma buffer !\n");
|
| 458 | rc = -ENOMEM;
|
| 459 | goto bail_free_samples;
|
| 460 | }
|
| 461 | #if 0 |
| 462 | rm->i2s_regs = ioremap(macio_resource_start(mdev, 0), 0x1000);
|
| 463 | #else
|
| 464 | rm->i2s_regs = ioremap(ri2s.start, 0x1000);
|
| 465 | #endif
|
| 466 | if (rm->i2s_regs == NULL) {
|
| 467 | printk(KERN_ERR
|
| 468 | "rackmeter: failed to map i2s registers !\n");
|
| 469 | rc = -ENXIO;
|
| 470 | goto bail_free_dma;
|
| 471 | }
|
| 472 | #if 0 |
| 473 | rm->dma_regs = ioremap(macio_resource_start(mdev, 1), 0x100);
|
| 474 | #else
|
| 475 | rm->dma_regs = ioremap(rdma.start, 0x100);
|
| 476 | #endif
|
| 477 | if (rm->dma_regs == NULL) {
|
| 478 | printk(KERN_ERR
|
| 479 | "rackmeter: failed to map dma registers !\n");
|
| 480 | rc = -ENXIO;
|
| 481 | goto bail_unmap_i2s;
|
| 482 | }
|
| 483 |
|
| 484 | rc = rackmeter_setup(rm);
|
| 485 | if (rc) {
|
| 486 | printk(KERN_ERR
|
| 487 | "rackmeter: failed to initialize !\n");
|
| 488 | rc = -ENXIO;
|
| 489 | goto bail_unmap_dma;
|
| 490 | }
|
| 491 |
|
| 492 | rc = request_irq(rm->irq, rackmeter_irq, 0, "rackmeter", rm);
|
| 493 | if (rc != 0) {
|
| 494 | printk(KERN_ERR
|
| 495 | "rackmeter: failed to request interrupt !\n");
|
| 496 | goto bail_stop_dma;
|
| 497 | }
|
| 498 | of_node_put(np);
|
| 499 | return 0;
|
| 500 |
|
| 501 | bail_stop_dma:
|
| 502 | DBDMA_DO_RESET(rm->dma_regs);
|
| 503 | bail_unmap_dma:
|
| 504 | iounmap(rm->dma_regs);
|
| 505 | bail_unmap_i2s:
|
| 506 | iounmap(rm->i2s_regs);
|
| 507 | bail_free_dma:
|
| 508 | dma_free_coherent(&macio_get_pci_dev(mdev)->dev,
|
| 509 | sizeof(struct rackmeter_dma),
|
| 510 | rm->dma_buf_v, rm->dma_buf_p);
|
| 511 | bail_free_samples:
|
| 512 | free_page((unsigned long)rm->ubuf);
|
| 513 | bail_release:
|
| 514 | #if 0 |
| 515 | macio_release_resources(mdev);
|
| 516 | #endif
|
| 517 | bail_free:
|
| 518 | kfree(rm);
|
| 519 | bail:
|
| 520 | of_node_put(i2s);
|
| 521 | of_node_put(np);
|
| 522 | dev_set_drvdata(&mdev->ofdev.dev, NULL);
|
| 523 | return rc;
|
| 524 | }
|
| 525 |
|
| 526 | static void rackmeter_remove(struct macio_dev *mdev)
|
| 527 | {
|
| 528 | struct rackmeter *rm = dev_get_drvdata(&mdev->ofdev.dev);
|
| 529 |
|
| 530 |
|
| 531 | rackmeter_stop_cpu_sniffer(rm);
|
| 532 |
|
| 533 |
|
| 534 | dev_set_drvdata(&mdev->ofdev.dev, NULL);
|
| 535 |
|
| 536 |
|
| 537 | DBDMA_DO_RESET(rm->dma_regs);
|
| 538 |
|
| 539 |
|
| 540 | free_irq(rm->irq, rm);
|
| 541 |
|
| 542 |
|
| 543 | iounmap(rm->dma_regs);
|
| 544 | iounmap(rm->i2s_regs);
|
| 545 |
|
| 546 |
|
| 547 | dma_free_coherent(&macio_get_pci_dev(mdev)->dev,
|
| 548 | sizeof(struct rackmeter_dma),
|
| 549 | rm->dma_buf_v, rm->dma_buf_p);
|
| 550 |
|
| 551 |
|
| 552 | free_page((unsigned long)rm->ubuf);
|
| 553 |
|
| 554 | #if 0 |
| 555 | /* Release resources */
|
| 556 | macio_release_resources(mdev);
|
| 557 | #endif
|
| 558 |
|
| 559 |
|
| 560 | kfree(rm);
|
| 561 | }
|
| 562 |
|
| 563 | static int rackmeter_shutdown(struct macio_dev* mdev)
|
| 564 | {
|
| 565 | struct rackmeter *rm = dev_get_drvdata(&mdev->ofdev.dev);
|
| 566 |
|
| 567 | if (rm == NULL)
|
| 568 | return -ENODEV;
|
| 569 |
|
| 570 |
|
| 571 | rackmeter_stop_cpu_sniffer(rm);
|
| 572 |
|
| 573 |
|
| 574 | DBDMA_DO_RESET(rm->dma_regs);
|
| 575 |
|
| 576 | return 0;
|
| 577 | }
|
| 578 |
|
| 579 | static const struct of_device_id rackmeter_match[] = {
|
| 580 | { .name = "i2s" },
|
| 581 | { }
|
| 582 | };
|
| 583 | MODULE_DEVICE_TABLE(of, rackmeter_match);
|
| 584 |
|
| 585 | static struct macio_driver rackmeter_driver = {
|
| 586 | .driver = {
|
| 587 | .name = "rackmeter",
|
| 588 | .owner = THIS_MODULE,
|
| 589 | .of_match_table = rackmeter_match,
|
| 590 | },
|
| 591 | .probe = rackmeter_probe,
|
| 592 | .remove = rackmeter_remove,
|
| 593 | .shutdown = rackmeter_shutdown,
|
| 594 | };
|
| 595 |
|
| 596 |
|
| 597 | static int __init rackmeter_init(void)
|
| 598 | {
|
| 599 | pr_debug("rackmeter_init()\n");
|
| 600 |
|
| 601 | return macio_register_driver(&rackmeter_driver);
|
| 602 | }
|
| 603 |
|
| 604 | static void __exit rackmeter_exit(void)
|
| 605 | {
|
| 606 | pr_debug("rackmeter_exit()\n");
|
| 607 |
|
| 608 | macio_unregister_driver(&rackmeter_driver);
|
| 609 | }
|
| 610 |
|
| 611 | module_init(rackmeter_init);
|
| 612 | module_exit(rackmeter_exit);
|
| 613 |
|
| 614 |
|
| 615 | MODULE_LICENSE("GPL");
|
| 616 | MODULE_AUTHOR("Benjamin Herrenschmidt <[email protected]>");
|
| 617 | MODULE_DESCRIPTION("RackMeter: Support vu-meter on XServe front panel");
|