📄 pmac_feature.c
字号:
/* * BK Id: %F% %I% %G% %U% %#% *//* * arch/ppc/kernel/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) * */#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 <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>#undef DEBUG_FEATURE#ifdef DEBUG_FEATURE#define DBG(fmt,...) printk(KERN_DEBUG fmt)#else#define DBG(fmt,...)#endif/* Exported from arch/ppc/kernel/idle.c */extern unsigned long powersave_nap;/* * We use a single global lock to protect accesses. Each driver has * to take care of it's own locking */static spinlock_t feature_lock __pmacdata = SPIN_LOCK_UNLOCKED;#define LOCK(flags) spin_lock_irqsave(&feature_lock, flags);#define UNLOCK(flags) spin_unlock_irqrestore(&feature_lock, flags);/* * Helper functions regarding the various flavors of mac-io */ #define MAX_MACIO_CHIPS 2enum { macio_unknown = 0, macio_grand_central, macio_ohare, macio_ohareII, macio_heathrow, macio_gatwick, macio_paddington, macio_keylargo, macio_pangea};static const char* macio_names[] __pmacdata = { "Unknown", "Grand Central", "OHare", "OHareII", "Heathrow", "Gatwick", "Paddington", "Keylargo", "Pangea"};static struct macio_chip{ struct device_node* of_node; int type; int rev; volatile u32* base; unsigned long flags;} macio_chips[MAX_MACIO_CHIPS] __pmacdata;#define MACIO_FLAG_SCCA_ON 0x00000001#define MACIO_FLAG_SCCB_ON 0x00000002#define MACIO_FLAG_SCC_LOCKED 0x00000004#define MACIO_FLAG_AIRPORT_ON 0x00000010#define MACIO_FLAG_FW_SUPPORTED 0x00000020static struct macio_chip* __pmacmacio_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;}#define MACIO_FCR32(macio, r) ((macio)->base + ((r) >> 2))#define MACIO_FCR8(macio, r) (((volatile u8*)((macio)->base)) + (r))#define MACIO_IN32(r) (in_le32(MACIO_FCR32(macio,r)))#define MACIO_OUT32(r,v) (out_le32(MACIO_FCR32(macio,r), (v)))#define MACIO_BIS(r,v) (MACIO_OUT32((r), MACIO_IN32(r) | (v)))#define MACIO_BIC(r,v) (MACIO_OUT32((r), MACIO_IN32(r) & ~(v)))#define MACIO_IN8(r) (in_8(MACIO_FCR8(macio,r)))#define MACIO_OUT8(r,v) (out_8(MACIO_FCR8(macio,r), (v)))/* * 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 __pmacdata;static u32* uninorth_base __pmacdata;static u32 uninorth_rev __pmacdata;/* * For each motherboard family, we have a table of functions pointers * that handle the various features. */typedef int (*feature_call)(struct device_node* node, int param, int 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 __pmacdata;/* * Here are the chip specific feature functions */static inline int __pmacsimple_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;}static int __pmacgeneric_scc_enable(struct device_node* node, u32 enable_mask, u32 reset_mask, int param, int value){ struct macio_chip* macio; unsigned long chan_mask; unsigned long fcr; unsigned long flags; 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; if (value) { LOCK(flags); fcr = MACIO_IN32(OHARE_FCR); /* Check if scc cell need enabling */ if (!(fcr & OH_SCC_ENABLE)) { fcr |= enable_mask; MACIO_OUT32(OHARE_FCR, fcr); fcr |= reset_mask; MACIO_OUT32(OHARE_FCR, fcr); UNLOCK(flags); (void)MACIO_IN32(OHARE_FCR); mdelay(15); LOCK(flags); fcr &= ~reset_mask; 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 &= ~enable_mask; MACIO_OUT32(OHARE_FCR, fcr); } macio->flags &= ~(chan_mask); UNLOCK(flags); mdelay(10); } return 0;}static int __pmacohare_scc_enable(struct device_node* node, int param, int value){ int rc;#ifdef CONFIG_ADB_PMU if (value && (param & 0xfff) == PMAC_SCC_IRDA) pmu_enable_irled(1);#endif /* CONFIG_ADB_PMU */ rc = generic_scc_enable(node, OH_SCC_ENABLE, OH_SCC_RESET, param, value);#ifdef CONFIG_ADB_PMU if ((param & 0xfff) == PMAC_SCC_IRDA && (rc || !value)) pmu_enable_irled(0);#endif /* CONFIG_ADB_PMU */ return rc;}static int __pmacohare_floppy_enable(struct device_node* node, int param, int value){ return simple_feature_tweak(node, macio_ohare, OHARE_FCR, OH_FLOPPY_ENABLE, value);}static int __pmacohare_mesh_enable(struct device_node* node, int param, int value){ return simple_feature_tweak(node, macio_ohare, OHARE_FCR, OH_MESH_ENABLE, value);}static int __pmacohare_ide_enable(struct device_node* node, int param, int 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 int __pmacohare_ide_reset(struct device_node* node, int param, int 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 int __pmacohare_sleep_state(struct device_node* node, int param, int value){ struct macio_chip* macio = &macio_chips[0]; if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0) return -EPERM; if (value) { MACIO_BIC(OHARE_FCR, OH_IOBUS_ENABLE); } else { MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE); } return 0;}static int __pmacheathrow_scc_enable(struct device_node* node, int param, int value){ int rc; #ifdef CONFIG_ADB_PMU if (value && param == PMAC_SCC_IRDA) pmu_enable_irled(1);#endif /* CONFIG_ADB_PMU */ /* Fixme: It's possible that wallstreet (heathrow) is different * than other paddington machines. I still have to figure that * out exactly, for now, the paddington values are used */ rc = generic_scc_enable(node, HRW_SCC_ENABLE, PADD_RESET_SCC, param, value);#ifdef CONFIG_ADB_PMU if (param == PMAC_SCC_IRDA && (rc || !value)) pmu_enable_irled(0);#endif /* CONFIG_ADB_PMU */ return rc;}static int __pmacheathrow_modem_enable(struct device_node* node, int param, int 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); /* We use the paddington values as they seem to work properly * on the wallstreet (heathrow) as well. I can't tell why we * had to flip them on older feature.c, the fact is that new * code uses the paddington values which are also the ones used * in Darwin, and that works on wallstreet ! */ if (value) MACIO_BIC(HEATHROW_FCR, PADD_MODEM_POWER_N); else MACIO_BIS(HEATHROW_FCR, PADD_MODEM_POWER_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); LOCK(flags); } return 0;}static int __pmacheathrow_floppy_enable(struct device_node* node, int param, int value){ return simple_feature_tweak(node, macio_unknown, HEATHROW_FCR, HRW_SWIM_ENABLE|HRW_BAY_FLOPPY_ENABLE, value);}static int __pmacheathrow_mesh_enable(struct device_node* node, int param, int 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 (todo: test ! the bit value * used by Darwin doesn't seem to match what we used so * far. If you experience problems, turn #if 1 into #if 0 * and tell me about it --BenH. */#if 1 if (value) MACIO_BIC(HEATHROW_MBCR, 0x00000004); else MACIO_BIS(HEATHROW_MBCR, 0x00000004);#else if (value) MACIO_BIC(HEATHROW_MBCR, 0x00040000); else MACIO_BIS(HEATHROW_MBCR, 0x00040000);#endif (void)MACIO_IN32(HEATHROW_MBCR); udelay(10); UNLOCK(flags); return 0;}static int __pmacheathrow_ide_enable(struct device_node* node, int param, int 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 int __pmacheathrow_ide_reset(struct device_node* node, int param, int 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 int __pmacheathrow_bmac_enable(struct device_node* node, int param, int 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 int __pmacheathrow_sound_enable(struct device_node* node, int param, int 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 ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -