📄 feature.c
字号:
/* * arch/ppc/platforms/pmac_feature.c * * Copyright (C) 1996-2001 Paul Mackerras (paulus@cs.anu.edu.au) * Ben. Herrenschmidt (benh@kernel.crashing.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * TODO: * * - Replace mdelay with some schedule loop if possible * - Shorten some obfuscated delays on some routines (like modem * power) * - Refcount some clocks (see darwin) * - Split split split... * */#include <linux/config.h>#include <linux/types.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/spinlock.h>#include <linux/adb.h>#include <linux/pmu.h>#include <linux/ioport.h>#include <linux/pci.h>#include <asm/sections.h>#include <asm/errno.h>#include <asm/ohare.h>#include <asm/heathrow.h>#include <asm/keylargo.h>#include <asm/uninorth.h>#include <asm/io.h>#include <asm/prom.h>#include <asm/machdep.h>#include <asm/pmac_feature.h>#include <asm/dbdma.h>#include <asm/pci-bridge.h>#include <asm/pmac_low_i2c.h>#undef DEBUG_FEATURE#ifdef DEBUG_FEATURE#define DBG(fmt...) printk(KERN_DEBUG fmt)#else#define DBG(fmt...)#endif#ifdef CONFIG_6xxextern int powersave_lowspeed;#endifextern int powersave_nap;extern struct device_node *k2_skiplist[2];/* * We use a single global lock to protect accesses. Each driver has * to take care of its own locking */static DEFINE_SPINLOCK(feature_lock);#define LOCK(flags) spin_lock_irqsave(&feature_lock, flags);#define UNLOCK(flags) spin_unlock_irqrestore(&feature_lock, flags);/* * Instance of some macio stuffs */struct macio_chip macio_chips[MAX_MACIO_CHIPS];struct macio_chip *macio_find(struct device_node *child, int type){ while(child) { int i; for (i=0; i < MAX_MACIO_CHIPS && macio_chips[i].of_node; i++) if (child == macio_chips[i].of_node && (!type || macio_chips[i].type == type)) return &macio_chips[i]; child = child->parent; } return NULL;}EXPORT_SYMBOL_GPL(macio_find);static const char *macio_names[] ={ "Unknown", "Grand Central", "OHare", "OHareII", "Heathrow", "Gatwick", "Paddington", "Keylargo", "Pangea", "Intrepid", "K2"};/* * Uninorth reg. access. Note that Uni-N regs are big endian */#define UN_REG(r) (uninorth_base + ((r) >> 2))#define UN_IN(r) (in_be32(UN_REG(r)))#define UN_OUT(r,v) (out_be32(UN_REG(r), (v)))#define UN_BIS(r,v) (UN_OUT((r), UN_IN(r) | (v)))#define UN_BIC(r,v) (UN_OUT((r), UN_IN(r) & ~(v)))static struct device_node *uninorth_node;static u32 __iomem *uninorth_base;static u32 uninorth_rev;static int uninorth_u3;static void __iomem *u3_ht;/* * For each motherboard family, we have a table of functions pointers * that handle the various features. */typedef long (*feature_call)(struct device_node *node, long param, long value);struct feature_table_entry { unsigned int selector; feature_call function;};struct pmac_mb_def{ const char* model_string; const char* model_name; int model_id; struct feature_table_entry* features; unsigned long board_flags;};static struct pmac_mb_def pmac_mb;/* * Here are the chip specific feature functions */static inline int simple_feature_tweak(struct device_node *node, int type, int reg, u32 mask, int value){ struct macio_chip* macio; unsigned long flags; macio = macio_find(node, type); if (!macio) return -ENODEV; LOCK(flags); if (value) MACIO_BIS(reg, mask); else MACIO_BIC(reg, mask); (void)MACIO_IN32(reg); UNLOCK(flags); return 0;}#ifndef CONFIG_POWER4static long ohare_htw_scc_enable(struct device_node *node, long param, long value){ struct macio_chip* macio; unsigned long chan_mask; unsigned long fcr; unsigned long flags; int htw, trans; unsigned long rmask; macio = macio_find(node, 0); if (!macio) return -ENODEV; if (!strcmp(node->name, "ch-a")) chan_mask = MACIO_FLAG_SCCA_ON; else if (!strcmp(node->name, "ch-b")) chan_mask = MACIO_FLAG_SCCB_ON; else return -ENODEV; htw = (macio->type == macio_heathrow || macio->type == macio_paddington || macio->type == macio_gatwick); /* On these machines, the HRW_SCC_TRANS_EN_N bit mustn't be touched */ trans = (pmac_mb.model_id != PMAC_TYPE_YOSEMITE && pmac_mb.model_id != PMAC_TYPE_YIKES); if (value) {#ifdef CONFIG_ADB_PMU if ((param & 0xfff) == PMAC_SCC_IRDA) pmu_enable_irled(1);#endif /* CONFIG_ADB_PMU */ LOCK(flags); fcr = MACIO_IN32(OHARE_FCR); /* Check if scc cell need enabling */ if (!(fcr & OH_SCC_ENABLE)) { fcr |= OH_SCC_ENABLE; if (htw) { /* Side effect: this will also power up the * modem, but it's too messy to figure out on which * ports this controls the tranceiver and on which * it controls the modem */ if (trans) fcr &= ~HRW_SCC_TRANS_EN_N; MACIO_OUT32(OHARE_FCR, fcr); fcr |= (rmask = HRW_RESET_SCC); MACIO_OUT32(OHARE_FCR, fcr); } else { fcr |= (rmask = OH_SCC_RESET); MACIO_OUT32(OHARE_FCR, fcr); } UNLOCK(flags); (void)MACIO_IN32(OHARE_FCR); mdelay(15); LOCK(flags); fcr &= ~rmask; MACIO_OUT32(OHARE_FCR, fcr); } if (chan_mask & MACIO_FLAG_SCCA_ON) fcr |= OH_SCCA_IO; if (chan_mask & MACIO_FLAG_SCCB_ON) fcr |= OH_SCCB_IO; MACIO_OUT32(OHARE_FCR, fcr); macio->flags |= chan_mask; UNLOCK(flags); if (param & PMAC_SCC_FLAG_XMON) macio->flags |= MACIO_FLAG_SCC_LOCKED; } else { if (macio->flags & MACIO_FLAG_SCC_LOCKED) return -EPERM; LOCK(flags); fcr = MACIO_IN32(OHARE_FCR); if (chan_mask & MACIO_FLAG_SCCA_ON) fcr &= ~OH_SCCA_IO; if (chan_mask & MACIO_FLAG_SCCB_ON) fcr &= ~OH_SCCB_IO; MACIO_OUT32(OHARE_FCR, fcr); if ((fcr & (OH_SCCA_IO | OH_SCCB_IO)) == 0) { fcr &= ~OH_SCC_ENABLE; if (htw && trans) fcr |= HRW_SCC_TRANS_EN_N; MACIO_OUT32(OHARE_FCR, fcr); } macio->flags &= ~(chan_mask); UNLOCK(flags); mdelay(10);#ifdef CONFIG_ADB_PMU if ((param & 0xfff) == PMAC_SCC_IRDA) pmu_enable_irled(0);#endif /* CONFIG_ADB_PMU */ } return 0;}static long ohare_floppy_enable(struct device_node *node, long param, long value){ return simple_feature_tweak(node, macio_ohare, OHARE_FCR, OH_FLOPPY_ENABLE, value);}static long ohare_mesh_enable(struct device_node *node, long param, long value){ return simple_feature_tweak(node, macio_ohare, OHARE_FCR, OH_MESH_ENABLE, value);}static long ohare_ide_enable(struct device_node *node, long param, long value){ switch(param) { case 0: /* For some reason, setting the bit in set_initial_features() * doesn't stick. I'm still investigating... --BenH. */ if (value) simple_feature_tweak(node, macio_ohare, OHARE_FCR, OH_IOBUS_ENABLE, 1); return simple_feature_tweak(node, macio_ohare, OHARE_FCR, OH_IDE0_ENABLE, value); case 1: return simple_feature_tweak(node, macio_ohare, OHARE_FCR, OH_BAY_IDE_ENABLE, value); default: return -ENODEV; }}static long ohare_ide_reset(struct device_node *node, long param, long value){ switch(param) { case 0: return simple_feature_tweak(node, macio_ohare, OHARE_FCR, OH_IDE0_RESET_N, !value); case 1: return simple_feature_tweak(node, macio_ohare, OHARE_FCR, OH_IDE1_RESET_N, !value); default: return -ENODEV; }}static long ohare_sleep_state(struct device_node *node, long param, long value){ struct macio_chip* macio = &macio_chips[0]; if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0) return -EPERM; if (value == 1) { MACIO_BIC(OHARE_FCR, OH_IOBUS_ENABLE); } else if (value == 0) { MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE); } return 0;}static long heathrow_modem_enable(struct device_node *node, long param, long value){ struct macio_chip* macio; u8 gpio; unsigned long flags; macio = macio_find(node, macio_unknown); if (!macio) return -ENODEV; gpio = MACIO_IN8(HRW_GPIO_MODEM_RESET) & ~1; if (!value) { LOCK(flags); MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio); UNLOCK(flags); (void)MACIO_IN8(HRW_GPIO_MODEM_RESET); mdelay(250); } if (pmac_mb.model_id != PMAC_TYPE_YOSEMITE && pmac_mb.model_id != PMAC_TYPE_YIKES) { LOCK(flags); if (value) MACIO_BIC(HEATHROW_FCR, HRW_SCC_TRANS_EN_N); else MACIO_BIS(HEATHROW_FCR, HRW_SCC_TRANS_EN_N); UNLOCK(flags); (void)MACIO_IN32(HEATHROW_FCR); mdelay(250); } if (value) { LOCK(flags); MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio | 1); (void)MACIO_IN8(HRW_GPIO_MODEM_RESET); UNLOCK(flags); mdelay(250); LOCK(flags); MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio); (void)MACIO_IN8(HRW_GPIO_MODEM_RESET); UNLOCK(flags); mdelay(250); LOCK(flags); MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio | 1); (void)MACIO_IN8(HRW_GPIO_MODEM_RESET); UNLOCK(flags); mdelay(250); } return 0;}static long heathrow_floppy_enable(struct device_node *node, long param, long value){ return simple_feature_tweak(node, macio_unknown, HEATHROW_FCR, HRW_SWIM_ENABLE|HRW_BAY_FLOPPY_ENABLE, value);}static long heathrow_mesh_enable(struct device_node *node, long param, long value){ struct macio_chip* macio; unsigned long flags; macio = macio_find(node, macio_unknown); if (!macio) return -ENODEV; LOCK(flags); /* Set clear mesh cell enable */ if (value) MACIO_BIS(HEATHROW_FCR, HRW_MESH_ENABLE); else MACIO_BIC(HEATHROW_FCR, HRW_MESH_ENABLE); (void)MACIO_IN32(HEATHROW_FCR); udelay(10); /* Set/Clear termination power */ if (value) MACIO_BIC(HEATHROW_MBCR, 0x04000000); else MACIO_BIS(HEATHROW_MBCR, 0x04000000); (void)MACIO_IN32(HEATHROW_MBCR); udelay(10); UNLOCK(flags); return 0;}static long heathrow_ide_enable(struct device_node *node, long param, long value){ switch(param) { case 0: return simple_feature_tweak(node, macio_unknown, HEATHROW_FCR, HRW_IDE0_ENABLE, value); case 1: return simple_feature_tweak(node, macio_unknown, HEATHROW_FCR, HRW_BAY_IDE_ENABLE, value); default: return -ENODEV; }}static long heathrow_ide_reset(struct device_node *node, long param, long value){ switch(param) { case 0: return simple_feature_tweak(node, macio_unknown, HEATHROW_FCR, HRW_IDE0_RESET_N, !value); case 1: return simple_feature_tweak(node, macio_unknown, HEATHROW_FCR, HRW_IDE1_RESET_N, !value); default: return -ENODEV; }}static long heathrow_bmac_enable(struct device_node *node, long param, long value){ struct macio_chip* macio; unsigned long flags; macio = macio_find(node, 0); if (!macio) return -ENODEV; if (value) { LOCK(flags); MACIO_BIS(HEATHROW_FCR, HRW_BMAC_IO_ENABLE); MACIO_BIS(HEATHROW_FCR, HRW_BMAC_RESET); UNLOCK(flags); (void)MACIO_IN32(HEATHROW_FCR); mdelay(10); LOCK(flags); MACIO_BIC(HEATHROW_FCR, HRW_BMAC_RESET); UNLOCK(flags); (void)MACIO_IN32(HEATHROW_FCR); mdelay(10); } else { LOCK(flags); MACIO_BIC(HEATHROW_FCR, HRW_BMAC_IO_ENABLE); UNLOCK(flags); } return 0;}static long heathrow_sound_enable(struct device_node *node, long param, long value){ struct macio_chip* macio; unsigned long flags; /* B&W G3 and Yikes don't support that properly (the * sound appear to never come back after beeing shut down). */ if (pmac_mb.model_id == PMAC_TYPE_YOSEMITE || pmac_mb.model_id == PMAC_TYPE_YIKES) return 0; macio = macio_find(node, 0); if (!macio) return -ENODEV; if (value) { LOCK(flags); MACIO_BIS(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -