* [PATCH] WDT Driver for Book-E [2/2] Device driver part.
From: Takeharu KATO @ 2005-03-09 4:57 UTC (permalink / raw)
To: ppcembed, mporter, kumar.gala, jwboyer
Matt,Kurmar and Josh:
I post the device driver part with this mail.
Please apply.
Regards,
Signed-off-by: Takeharu KATO <kato.takeharu@jp.fujitsu.com>
--- linux-2.6.11/drivers/char/watchdog/Kconfig 2005-03-09 12:56:45.428165328 +0900
+++ linux-2.6.11-wdt/drivers/char/watchdog/Kconfig 2005-03-09 13:07:53.668577344 +0900
@@ -346,6 +346,13 @@ config 8xx_WDT
tristate "MPC8xx Watchdog Timer"
depends on WATCHDOG && 8xx
+config BOOKE_WDT
+ bool "Book E(PowerPC 4xx/e500) Watchdog Timer"
+ depends on WATCHDOG && ( 4xx || E500 )
+ ---help---
+ This is the driver for the watchdog timers on
+ PowerPC 4xx and PowerPC e500.
+
# MIPS Architecture
config INDYDOG
--- linux-2.6.11/drivers/char/watchdog/Makefile 2005-03-09 12:57:07.855755816 +0900
+++ linux-2.6.11-wdt/drivers/char/watchdog/Makefile 2005-03-09 13:07:53.681575368 +0900
@@ -39,3 +39,4 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.
obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o
obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o
obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o
+obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
--- linux-2.6.11/drivers/char/watchdog/booke_wdt.c 1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.11-wdt/drivers/char/watchdog/booke_wdt.c 2005-03-09 13:07:53.684574912 +0900
@@ -0,0 +1,628 @@
+/*
+ * Copyright (c) 2005 Fujitsu Limited
+ *
+ * Module name: booke_wdt.c
+ * Author: Takeharu KATO<kato.takeharu@jp.fujitsu.com>
+ *
+ * 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.
+ *
+ * Neither Takeharu KATO nor Fujitsu Ltd. admit liability nor provide
+ * warranty for any of this software.
+ *
+ * Description:
+ * Watchdog driver for PowerPC Book E (PowerPC 4xx series processors and
+ * PowerPC e500 series processors).
+ * Derived from drivers/char/watchdog/wdt.c by Alan cox
+ * and drivers/char/watchdog/ppc405_wdt.c by Armin Kuster.
+ * PPC4xx WDT operation is driverd from Appendix of
+ * PowerPC Embedded Processors Application Note
+ * ``PowerPC 40x Watch Dog Timer'' published from IBM.
+ * This driver is written according to ``PowerPC e500 Core Complex
+ * Reference Manual'' for e500 part.
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/capability.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <asm/reg.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include "booke_wdt.h"
+
+/* micro seconds per one milli-second(used to calculatewatchdog
+ * counter to be set). */
+#define US_PER_MS 1000
+/* Calculate watchdog count */
+#define calculate_wdt_count(t) ((((unsigned long)(t))*HZ)/1000)
+
+int wdt_enable=0; /* WDT start on boot */
+int wdt_period=WDT_TIMO; /* Time out in ms */
+
+#ifdef CONFIG_WATCHDOG_NOWAYOUT
+static int nowayout = 1;
+#else
+static int nowayout = 0;
+#endif
+
+/*
+ * Global variables
+ */
+static int wdt_count = 0; /* WDT intrrupt counter to be reloaded */
+static volatile int wdt_heartbeat_count = 0; /* WDT intrrupt counter(compatible mode)*/
+static unsigned long driver_state; /* Driver status (see: booke_wdt.h) */
+/*
+ * Identifier for this watchdog
+ */
+static struct watchdog_info ident = {
+ .options=WDIOF_SETTIMEOUT|WDIOF_KEEPALIVEPING|WDIOF_MAGICCLOSE,
+ .firmware_version = 0, /* This is filled with PVR in initialization. */
+ .identity = "Book E(PPC 4xx/e500) WDT",
+};
+
+/*
+ * PowerPC Linux common exception handler
+ */
+extern void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr);
+/* Panic notifier */
+extern struct notifier_block *panic_notifier_list;
+/*
+ * Watchdog operations on PPC4xx/e500 MPU
+ */
+/**
+ * __booke_wdt_setup_val
+ * Enable Watchdog, sets up passed in values for TCR[WP],
+ * TCR[WRC]
+ *
+ * @period: Input Watchdog Period - TCR[WP]
+ * 0...2^17(PPC405) or 2^21(PPC440/e500) clocks
+ * 1...2^21(PPC405) or 2^25(PPC440/e500) clocks
+ * 2...2^25(PPC405) or 2^29(PPC440/e500) clocks
+ * 3...2^25(PPC405) or 2^29(PPC440/e500) clocks
+ * @reset: Watchdog reset control - TCR[WRC]
+ * 0 = No reset
+ * 1 = PPC Core reset only
+ * 2 = PPC Chip reset
+ * 3 = System reset
+ * Note: The meaning of period number is differ PPC405 from PPC440.
+ * Current version assume that this function called with
+ * WRC_NONE as reset, if someone who want to remove this
+ * constraint, please note that when we disable WDT, we should
+ * set WRC with WRC_NONE and s/he should also modify
+ * disable/enable_wdt function.
+ *
+ */
+static __inline__ void
+__booke_wdt_setup_val(int period,int reset)
+{
+ unsigned long val;
+
+ /* Set up TCR */
+ val=wdt_calculate_tcr(period,reset)| mfspr(SPRN_TCR);
+ /* Disable WDT */
+ val &= ~(TCR_WIE);
+
+ mtspr(SPRN_TCR,val);
+}
+/**
+ * __booke_wdt_enable
+ * Enable Watchdog
+ */
+static __inline__ void
+__booke_wdt_enable(void)
+{
+ mtspr(SPRN_TCR,(mfspr(SPRN_TCR)|TCR_WIE));
+}
+/**
+ * __booke_wdt_disable
+ * Disable Watchdog
+ */
+static __inline__ void
+__booke_wdt_disable(void)
+{
+ mtspr(SPRN_TCR,(mfspr(SPRN_TCR)&(~(TCR_WIE))));
+}
+/**
+ * __booke_wdt_is_enabled
+ * Check whether Watchdog is enabled.
+ */
+static __inline__ int
+__booke_wdt_is_enabled(void)
+{
+ return (mfspr(SPRN_TCR) & TCR_WIE);
+}
+/**
+ * __booke_wdt_clear_init_stat
+ * Clear interrupt status of watchdog to ping it.
+ */
+static __inline__ void
+__booke_wdt_clear_int_stat(void)
+{
+ mtspr(SPRN_TSR, (TSR_ENW|TSR_WIS));
+}
+/**
+ * __booke_wdt_set_timeout:
+ * @t: the new time out value that needs to be set.
+ *
+ * Set a new time out value for the watchdog device.
+ *
+ */
+static __inline__ void
+__booke_wdt_set_timeout(int t)
+{
+ wdt_count=calculate_wdt_count(t);
+ return;
+}
+
+/*
+ * Driver specific functions
+ */
+
+/**
+ * booke_wdt_setup_options
+ * @cmd_line : a pointer to kernel command line.
+ *
+ */
+void
+booke_wdt_setup_options(char *cmd_line)
+{
+/*
+ * Look for wdt= option on command line
+ */
+ if (strstr(cmd_line, "wdt=")) {
+ int valid_wdt = 0;
+ char *p, *q;
+
+ for (q = cmd_line; (p = strstr(q, "wdt=")) != 0;) {
+ q = p + 4;
+ if (p > cmd_line && p[-1] != ' ')
+ continue;
+ wdt_period = simple_strtoul(q, &q, 0);
+ valid_wdt = 1;
+ ++q;
+ }
+ wdt_enable = valid_wdt;
+ }
+ return;
+}
+/**
+ * booke_wdt_heartbeat:
+ * Ping routine called from kernel.
+ */
+void
+booke_wdt_heartbeat(void)
+{
+ /* Disable watchdog */
+ __booke_wdt_disable();
+
+ /* Write a watchdog value */
+ __booke_wdt_clear_int_stat();
+
+ if (!wdt_enable)
+ goto out;
+
+ if (wdt_heartbeat_count > 0)
+ wdt_heartbeat_count--;
+ else
+ panic(booke_mkmsg("Initiating system reboot.\n"));
+
+ /* Enable watchdog */
+ __booke_wdt_enable();
+ out:
+ /* Reset count */
+ ppc_md.heartbeat_count = 0;
+}
+/**
+ * booke_wdt_exception:
+ * WatchDog Exception handler for PPC4xx/e500.
+ * @regs : A registers information.
+ */
+void
+booke_wdt_exception(struct pt_regs *regs)
+{
+
+ wdt_enable=0;
+ __booke_wdt_disable();
+ printk("WDT Exception at PC: %lx, MSR: %lx, vector=%lx %s\n",
+ regs->nip, regs->msr, regs->trap, print_tainted());
+ panic(booke_mkmsg("Initiating system reboot.\n"));
+}
+/*
+ * Driver Logic functions
+ */
+static __inline__ int
+booke_wdt_is_enabled(void)
+{
+ return __booke_wdt_is_enabled();
+}
+/**
+ * booke_wdt_start:
+ *
+ * Start the watchdog driver.
+ */
+static __inline__ int
+booke_wdt_start(void)
+{
+
+ __booke_wdt_enable();
+ return 0;
+}
+
+/**
+ * booke_wdt_stop:
+ *
+ * Stop the watchdog driver.
+ */
+static __inline__ int
+booke_wdt_stop (void)
+{
+
+ __booke_wdt_disable();
+ return 0;
+}
+/**
+ * booke_wdt_ping:
+ *
+ * Reload counter one with the watchdog heartbeat. We don't bother reloading
+ * the cascade counter.
+ */
+static __inline__ int
+booke_wdt_ping(void)
+{
+
+ /* Disable watchdog */
+ __booke_wdt_disable();
+ /* Write a watchdog value */
+ __booke_wdt_clear_int_stat();
+ /* Reset count */
+ wdt_heartbeat_count=wdt_count;
+ /* Enable watchdog */
+ __booke_wdt_enable();
+
+ return 0;
+}
+/**
+ * booke_wdt_set_timeout:
+ * @t: the new timeout value that needs to be set.
+ *
+ * Set a new time out value for the watchdog device.
+ * If the heartbeat value is incorrect we keep the old value
+ * and return -EINVAL. If successfull we return 0.
+ */
+static __inline__ int
+booke_wdt_set_timeout(int t)
+{
+
+ if ((t < WDT_HEARTBEAT_MIN) || (t > WDT_HEARTBEAT_MAX))
+ return -EINVAL;
+
+ wdt_period = t;
+ __booke_wdt_set_timeout(t);
+ wdt_heartbeat_count=wdt_count;
+ booke_wdt_dbg("The WDT counter set %d.\n",wdt_count);
+
+ return 0;
+}
+
+/**
+ * booke_wdt_get_status:
+ * @status: the new status.
+ *
+ * Return the enable/disable card status.
+ */
+static __inline__ int
+booke_wdt_get_status(int *status)
+{
+
+ if (wdt_enable)
+ *status = WDIOS_ENABLECARD;
+ else
+ *status = WDIOS_DISABLECARD;
+
+ return 0;
+}
+/*
+ * Kernel Interfaces
+ */
+/**
+ * booke_wdt_init_device:
+ *
+ * Initilize PowerPC 4xx/e500 family Watch Dog facility.
+ */
+static void
+booke_wdt_init_device(void)
+{
+
+ /* Hardware WDT provided by the processor.
+ * So, we set firmware version as processor version number.
+ */
+ ident.firmware_version=mfspr(PVR);
+ __booke_wdt_setup_val(WDT_WP,WRC_NONE);
+}
+/**
+ * booke_wdt_write:
+ * @file: file handle to the watchdog
+ * @buf: buffer to write (unused as data does not matter here
+ * @count: count of bytes
+ * @ppos: pointer to the position to write. No seeks allowed
+ *
+ * A write to a watchdog device is defined as a keepalive signal. Any
+ * write of data will do, as we we don't define content meaning expept
+ * 'V' character. It is performed as a sign to set stop-on-close mode.
+ */
+
+static ssize_t
+booke_wdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+ size_t i;
+
+ if (!nowayout) {
+ /* In case it was set long ago */
+ clear_bit(WDT_STATE_STOP_ON_CLOSE, &driver_state);
+
+ for (i = 0; i < count; i++) {
+ char c;
+
+ if (get_user(c, buf + i))
+ return -EFAULT;
+
+ if (c == 'V') {
+ set_bit(WDT_STATE_STOP_ON_CLOSE, &driver_state);
+ }
+ }
+ }
+ booke_wdt_ping();
+
+ return count;
+}
+
+/**
+ * booke_wdt_ioctl:
+ * @inode: inode of the device
+ * @file: file handle to the device
+ * @cmd: watchdog command
+ * @arg: argument pointer
+ *
+ */
+static int
+booke_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int new_timeout;
+ int status;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM; /* It may be too strict manner. */
+ switch(cmd)
+ {
+ default:
+ return -ENOIOCTLCMD;
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(struct watchdog_info)))
+ return -EFAULT;
+ else
+ break;
+ case WDIOC_GETSTATUS:
+ booke_wdt_get_status(&status);
+ return put_user(status,(int *)arg);
+ case WDIOC_KEEPALIVE:
+ booke_wdt_ping();
+ break;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, (int *)arg))
+ return -EFAULT;
+ if (booke_wdt_set_timeout(new_timeout))
+ return -EINVAL;
+ booke_wdt_ping();
+ break;
+ case WDIOC_GETTIMEOUT:
+ return put_user(wdt_period, (int *)arg);
+ case WDIOC_SETOPTIONS:
+ if (get_user(status, (int *)arg))
+ return -EFAULT;
+ /* Return -EINVAL when the driver can not figure out
+ * what it should do. Unknown cases are just ignored.
+ */
+ if ( (status & (WDIOS_DISABLECARD|WDIOS_ENABLECARD))
+ == (WDIOS_DISABLECARD|WDIOS_ENABLECARD) )
+ return -EINVAL;
+ if (status & WDIOS_DISABLECARD) {
+ wdt_enable = 0;
+ booke_wdt_stop();
+ booke_wdt_info("Watchdog timer is disabled\n");
+ }
+ if (status & WDIOS_ENABLECARD) {
+ wdt_enable = 1;
+ booke_wdt_start();
+ booke_wdt_info("Watchdog timer is enabled\n");
+ }
+ break;
+ }
+ return 0;
+}
+/**
+ * booke_wdt_open:
+ * @inode: inode of device
+ * @file: file handle to device
+ *
+ * The watchdog device has been opened. The watchdog device is single
+ * open and start the WDT timer.
+ */
+static int
+booke_wdt_open(struct inode *inode, struct file *file)
+{
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (test_and_set_bit(WDT_STATE_OPEN, &driver_state))
+ return -EBUSY;
+ /*
+ * Activate
+ */
+ booke_wdt_start();
+ wdt_enable=1;
+
+ if (nowayout)
+ set_bit(WDT_STATE_STOP_ON_CLOSE, &driver_state);
+
+ return 0;
+}
+
+/**
+ * booke_wdt_release:
+ * @inode: inode to board
+ * @file: file handle to board
+ *
+ */
+static int
+booke_wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDT_STATE_STOP_ON_CLOSE, &driver_state)) {
+ booke_wdt_info("WDT device is stopped.\n");
+ booke_wdt_stop();
+ wdt_enable=0;
+ } else {
+ if ( (booke_wdt_is_enabled()) && (!nowayout) ) {
+ booke_wdt_info("WDT device may be closed unexpectedly. WDT will not stop!\n");
+ booke_wdt_ping();
+ }
+ }
+ clear_bit(WDT_STATE_OPEN, &driver_state);
+
+ return 0;
+}
+/**
+ * notify_sys:
+ * @this: our notifier block
+ * @code: the event being reported
+ * @unused: unused
+ * Note: This function assume that the panic notifier is called with CODE=0
+ * (see panic function in kernel/panic.c).
+ */
+static int
+booke_wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+
+ if (code != SYS_POWER_OFF) /* Turn the card off */
+ booke_wdt_stop();
+
+ return NOTIFY_DONE;
+}
+
+static struct file_operations booke_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = booke_wdt_write,
+ .ioctl = booke_wdt_ioctl,
+ .open = booke_wdt_open,
+ .release = booke_wdt_release,
+};
+
+static struct miscdevice booke_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &booke_wdt_fops,
+};
+
+/*
+ * The WDT card needs to know about shutdowns in order to
+ * turn WDT off.
+ */
+
+static struct notifier_block booke_wdt_notifier = {
+ .notifier_call = booke_wdt_notify_sys,
+};
+
+/**
+ * cleanup_module:
+ *
+ * If your watchdog is set to continue ticking on close and you unload
+ * it, well it keeps ticking. You just have to load a new
+ * module in 60 seconds or reboot.
+ * This behavior(more over the comments as above) is borrowed from
+ * Alan cox's driver.
+ */
+
+static void __exit
+booke_wdt_exit(void)
+{
+ misc_deregister(&booke_wdt_miscdev);
+ unregister_reboot_notifier(&booke_wdt_notifier);
+ notifier_chain_unregister(&panic_notifier_list,&booke_wdt_notifier);
+}
+
+/**
+ * booke_wdt_init:
+ *
+ * Set up the WDT relevant timer facility.
+ */
+
+static int __init
+booke_wdt_init(void)
+{
+ int ret;
+ unsigned long flags;
+
+ ret = register_reboot_notifier(&booke_wdt_notifier);
+ if(ret) {
+ booke_wdt_err("Cannot register reboot notifier (err=%d)\n", ret);
+ return ret;
+ }
+
+ /* Register panic notifier */
+ ret = notifier_chain_register(&panic_notifier_list,&booke_wdt_notifier);
+ if(ret) {
+ booke_wdt_err("Cannot register panic notifier (err=%d)\n", ret);
+ unregister_reboot_notifier(&booke_wdt_notifier);
+ return ret;
+ }
+
+ ret = 0;
+ booke_wdt_init_device();
+ /* Check that the heartbeat value is within it's range ; if not reset to the default */
+ if (booke_wdt_set_timeout(wdt_period)) {
+ if (wdt_period)
+ booke_wdt_info("The heartbeat value must be %d < wdt_period < %d, using %d\n",WDT_HEARTBEAT_MIN,WDT_HEARTBEAT_MAX,WDT_TIMO);
+ booke_wdt_set_timeout(WDT_TIMO);
+ }
+
+ local_irq_save(flags); /* Prevent timer interrupt */
+ ppc_md.heartbeat_count = 0;
+ ppc_md.heartbeat=booke_wdt_heartbeat;
+ local_irq_restore(flags);
+
+ booke_wdt_info("Book E(PPC 4xx/e500) Watchdog Driver. period=%d ms (nowayout=%d)\n",wdt_period, nowayout);
+
+ ret = misc_register(&booke_wdt_miscdev);
+ if (ret) {
+ booke_wdt_err("Cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto outmisc;
+ }
+
+ if (wdt_enable) {
+ booke_wdt_info("WDT start on boot.\n");
+ booke_wdt_start();
+ }
+out:
+ return ret;
+outmisc:
+ unregister_reboot_notifier(&booke_wdt_notifier);
+ local_irq_save(flags);
+ ppc_md.heartbeat=NULL;
+ ppc_md.heartbeat_count = 0;
+ local_irq_restore(flags);
+ goto out;
+}
+
+device_initcall(booke_wdt_init);
--- linux-2.6.11/drivers/char/watchdog/booke_wdt.h 1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.11-wdt/drivers/char/watchdog/booke_wdt.h 2005-03-09 13:07:53.685574760 +0900
@@ -0,0 +1,110 @@
+/*
+ *
+ * Copyright (c) 2004 Fujitsu Limited
+ *
+ * Module name: booke_wdt.h
+ * Author: Takeharu KATO<kato.takeharu@jp.fujitsu.com>
+ * Description:
+ * Header file for PowerPC Book E(PPC 4xx/e500) watchdog driver.
+ *
+ * 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.
+ *
+ * Neither Takeharu KATO nor Fujitsu Ltd. admit liability nor provide
+ * warranty for any of this software.
+ *
+ */
+#ifndef _DRIVERS_CHAR_WATCHDOG_BOOKE_WDT_H
+#define _DRIVERS_CHAR_WATCHDOG_BOOKE_WDT_H
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/watchdog.h>
+#include <asm/reg_booke.h>
+
+/*
+ * Driver state flags(bit position)
+ */
+#define WDT_STATE_OPEN 0 /* driver is opend */
+#define WDT_STATE_STOP_ON_CLOSE 1 /* Stop with close is expected */
+/*
+ * Configurations
+ */
+#define WDT_TIMO 60000 /* Default timeout = 60000 ms(1min) */
+#define WDT_HEARTBEAT_MIN 100 /* Minimum timeout = 100 ms */
+#define WDT_HEARTBEAT_MAX 600000 /* Maximum timeout = 600000ms(1hour) */
+#ifdef __KERNEL__
+//#define BOOKE_WDT_DEBUG /* Debug switch */
+
+/* Watchdog timer periods can be set on PPC 4xx cpus. */
+#if defined(CONFIG_4xx)
+/*
+ * For PowerPC 4xx
+ */
+#define WDT_WP0 0
+#define WDT_WP1 1
+#define WDT_WP2 2
+#define WDT_WP3 3
+#define wdt_calculate_tcr(period,reset) ( TCR_WP(period) | TCR_WRC(reset) )
+#else
+#if defined(CONFIG_E500)
+/*
+ * For e500 CPU
+ * Actually, e500 can arbitrary periods can be set,
+ * But this driver uses fix period value as same as PPC440
+ * on purpose for simplicity.
+ * Following values split into WP and WP_EXT parts in booke_wdt.c.
+ */
+#define WDT_WP0 21
+#define WDT_WP1 25
+#define WDT_WP2 29
+#define WDT_WP3 33
+#define WDT_TCR_WP_BITLEN 2 /* 2bit length */
+#define WDT_TCR_WP_BITMSK ((1<<(WDT_TCR_WP_BITLEN))-1) /* mask */
+#define WDT_TCR_WPEXT_BITMSK 0xf /* 4bit length */
+#define WDT_TCR_WPEXT_SHIFT 17
+#define WDT_WPEXT_VAL(period) (((period)>>WDT_TCR_WP_BITLEN)& \
+ (WDT_TCR_WPEXT_BITMSK))
+#define wdt_calculate_tcr(period,reset) \
+ ( TCR_WP((period)&(WDT_TCR_WP_BITMSK)) |\
+ TCR_WRC((reset)) |\
+ ( WDT_WPEXT_VAL(period) << WDT_TCR_WPEXT_SHIFT))
+#else
+#error "Book E WDT detects invalid configuration(Unknown CPU)"
+#endif /* CONFIG_E500 */
+#endif /* CONFIG_4xx */
+/*
+ * WP relevant values used in our driver.
+ * Note:WDT period must be more than HZ(Timer ticks)
+ */
+#define WDT_WP WDT_WP3
+
+/*
+ * IOCTL commands for comaptiblity for old driver
+ */
+#define WDIOC_GETPERIOD WDIOC_GETTIMEOUT
+#define WDIOC_SETPERIOD WDIOC_SETTIMEOUT
+
+/*
+ * output messages
+ */
+#define __BOOKE_WDT_MSG "BookE-WDT : "
+#define booke_mkmsg(str) __BOOKE_WDT_MSG str
+#define booke_wdt_info(fmt,arg...) \
+ printk(KERN_INFO __BOOKE_WDT_MSG fmt,##arg)
+#define booke_wdt_note(fmt,arg...) \
+ printk(KERN_NOTICE __BOOKE_WDT_MSG fmt,##arg)
+#define booke_wdt_err(fmt,arg...) \
+ printk(KERN_ALERT __BOOKE_WDT_MSG fmt,##arg)
+#if defined(BOOKE_WDT_DEBUG)
+#define booke_wdt_dbg(fmt,arg...) \
+ printk(KERN_ALERT __BOOKE_WDT_MSG fmt,##arg)
+#else
+#define booke_wdt_dbg(fmt,arg...) \
+ do{}while(0)
+#endif /* WDT_DEBUG */
+
+#endif /* __KERNEL__ */
+#endif /* _DRIVERS_CHAR_WATCHDOG_BOOKE_WDT_H */
^ permalink raw reply
* [PATCH] WDT Driver for Book-E [1/2] Architecture specific part.
From: Takeharu KATO @ 2005-03-09 4:55 UTC (permalink / raw)
To: ppcembed
Matt,Kurmar and Josh:
I performed minor cleanups which Josh pointed out.
It has been fine-tuned that it suits current linux-2.6.11(linus-tree).
(These changes has been performed on architecture specific part.).
I tested the driver on PowerPC440GP(ebony) again and I confirmed
that the driver work.
At first, I post the architecture specific part with this mail.
Please apply.
Regards,
Signed-off-by: Takeharu KATO <kato.takeharu@jp.fujitsu.com>
diff -Nupr linux-2.6.11/arch/ppc/kernel/head_44x.S linux-2.6.11-wdt/arch/ppc/kernel/head_44x.S
--- linux-2.6.11/arch/ppc/kernel/head_44x.S 2005-03-09 12:55:02.725778464 +0900
+++ linux-2.6.11-wdt/arch/ppc/kernel/head_44x.S 2005-03-09 13:07:38.355905224 +0900
@@ -444,8 +444,11 @@ interrupt_base:
EXCEPTION(0x1010, FixedIntervalTimer, UnknownException, EXC_XFER_EE)
/* Watchdog Timer Interrupt */
- /* TODO: Add watchdog support */
+#if defined(CONFIG_BOOKE_WDT)
+ CRITICAL_EXCEPTION(0x1020, WatchdogTimer, booke_wdt_exception)
+#else
CRITICAL_EXCEPTION(0x1020, WatchdogTimer, UnknownException)
+#endif /* CONFIG_BOOKE_WDT */
/* Data TLB Error Interrupt */
START_EXCEPTION(DataTLBError)
diff -Nupr linux-2.6.11/arch/ppc/kernel/head_4xx.S linux-2.6.11-wdt/arch/ppc/kernel/head_4xx.S
--- linux-2.6.11/arch/ppc/kernel/head_4xx.S 2005-03-09 12:58:03.942229376 +0900
+++ linux-2.6.11-wdt/arch/ppc/kernel/head_4xx.S 2005-03-09 13:07:38.357904920 +0900
@@ -469,27 +469,24 @@ label:
/* 0x1000 - Programmable Interval Timer (PIT) Exception */
START_EXCEPTION(0x1000, Decrementer)
- NORMAL_EXCEPTION_PROLOG
- lis r0,TSR_PIS@h
- mtspr SPRN_TSR,r0 /* Clear the PIT exception */
- addi r3,r1,STACK_FRAME_OVERHEAD
- EXC_XFER_LITE(0x1000, timer_interrupt)
+ b DecrementerHandler
#if 0
/* NOTE:
- * FIT and WDT handlers are not implemented yet.
+ * FIT handler is not implemented yet.
*/
/* 0x1010 - Fixed Interval Timer (FIT) Exception
*/
STND_EXCEPTION(0x1010, FITException, UnknownException)
-/* 0x1020 - Watchdog Timer (WDT) Exception
-*/
-
- CRITICAL_EXCEPTION(0x1020, WDTException, UnknownException)
#endif
+/* 0x1020 - Watchdog Timer (WDT) Exception
+ */
+ START_EXCEPTION(0x1020, WDTException)
+ b WatchDogHandler
+
/* 0x1100 - Data TLB Miss Exception
* As the name implies, translation is not in the MMU, so search the
* page tables and fix it. The only purpose of this function is to
@@ -771,6 +768,13 @@ label:
(MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \
NOCOPY, crit_transfer_to_handler, ret_from_crit_exc)
+DecrementerHandler:
+ NORMAL_EXCEPTION_PROLOG
+ lis r0,TSR_PIS@h
+ mtspr SPRN_TSR,r0 /* Clear the PIT exception */
+ addi r3,r1,STACK_FRAME_OVERHEAD
+ EXC_XFER_LITE(0x1000, timer_interrupt)
+
/*
* The other Data TLB exceptions bail out to this point
* if they can't resolve the lightweight TLB fault.
@@ -843,6 +847,19 @@ finish_tlb_load:
PPC405_ERR77_SYNC
rfi /* Should sync shadow TLBs */
b . /* prevent prefetch past rfi */
+/*
+ * WatchDog Exception
+ */
+WatchDogHandler:
+ CRITICAL_EXCEPTION_PROLOG;
+ addi r3,r1,STACK_FRAME_OVERHEAD;
+#if defined(CONFIG_BOOKE_WDT)
+ EXC_XFER_TEMPLATE(booke_wdt_exception, 0x1022, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)),NOCOPY, crit_transfer_to_handler, ret_from_crit_exc)
+
+#else
+ EXC_XFER_TEMPLATE(UnknownException, 0x1022, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)),NOCOPY, crit_transfer_to_handler, ret_from_crit_exc)
+#endif /* CONFIG_BOOKE_WDT */
+
/* extern void giveup_fpu(struct task_struct *prev)
*
diff -Nupr linux-2.6.11/arch/ppc/kernel/head_e500.S linux-2.6.11-wdt/arch/ppc/kernel/head_e500.S
--- linux-2.6.11/arch/ppc/kernel/head_e500.S 2005-03-09 12:57:14.058812808 +0900
+++ linux-2.6.11-wdt/arch/ppc/kernel/head_e500.S 2005-03-09 13:07:38.357904920 +0900
@@ -494,8 +494,11 @@ interrupt_base:
EXCEPTION(0x3100, FixedIntervalTimer, UnknownException, EXC_XFER_EE)
/* Watchdog Timer Interrupt */
- /* TODO: Add watchdog support */
+#if defined(CONFIG_BOOKE_WDT)
+ CRITICAL_EXCEPTION(0x3200, WatchdogTimer, booke_wdt_exception)
+#else
CRITICAL_EXCEPTION(0x3200, WatchdogTimer, UnknownException)
+#endif /* CONFIG_BOOKE_WDT */
/* Data TLB Error Interrupt */
START_EXCEPTION(DataTLBError)
diff -Nupr linux-2.6.11/arch/ppc/kernel/time.c linux-2.6.11-wdt/arch/ppc/kernel/time.c
diff -Nupr linux-2.6.11/arch/ppc/platforms/85xx/mpc8540_ads.c linux-2.6.11-wdt/arch/ppc/platforms/85xx/mpc8540_ads.c
--- linux-2.6.11/arch/ppc/platforms/85xx/mpc8540_ads.c 2005-03-09 12:55:23.468625072 +0900
+++ linux-2.6.11-wdt/arch/ppc/platforms/85xx/mpc8540_ads.c 2005-03-09 13:07:38.358904768 +0900
@@ -187,6 +187,14 @@ platform_init(unsigned long r3, unsigned
strcpy(cmd_line, (char *) (r6 + KERNELBASE));
}
+#ifdef CONFIG_BOOKE_WDT
+ {
+ extern void booke_wdt_setup_options(char *cmd_line);
+
+ booke_wdt_setup_options(cmd_line);
+ }
+#endif /* CONFIG_BOOKE_WDT */
+
identify_ppc_sys_by_id(mfspr(SVR));
/* setup the PowerPC module struct */
diff -Nupr linux-2.6.11/arch/ppc/platforms/85xx/mpc8560_ads.c linux-2.6.11-wdt/arch/ppc/platforms/85xx/mpc8560_ads.c
--- linux-2.6.11/arch/ppc/platforms/85xx/mpc8560_ads.c 2005-03-09 12:59:02.157379328 +0900
+++ linux-2.6.11-wdt/arch/ppc/platforms/85xx/mpc8560_ads.c 2005-03-09 13:07:38.358904768 +0900
@@ -197,6 +197,14 @@ platform_init(unsigned long r3, unsigned
strcpy(cmd_line, (char *) (r6 + KERNELBASE));
}
+#ifdef CONFIG_BOOKE_WDT
+ {
+ extern void booke_wdt_setup_options(char *cmd_line);
+
+ booke_wdt_setup_options(cmd_line);
+ }
+#endif /* CONFIG_BOOKE_WDT */
+
identify_ppc_sys_by_id(mfspr(SVR));
/* setup the PowerPC module struct */
diff -Nupr linux-2.6.11/arch/ppc/platforms/85xx/mpc85xx_cds_common.c linux-2.6.11-wdt/arch/ppc/platforms/85xx/mpc85xx_cds_common.c
--- linux-2.6.11/arch/ppc/platforms/85xx/mpc85xx_cds_common.c 2005-03-09 12:57:51.908058848 +0900
+++ linux-2.6.11-wdt/arch/ppc/platforms/85xx/mpc85xx_cds_common.c 2005-03-09 13:07:38.359904616 +0900
@@ -448,6 +448,14 @@ platform_init(unsigned long r3, unsigned
strcpy(cmd_line, (char *) (r6 + KERNELBASE));
}
+#ifdef CONFIG_BOOKE_WDT
+ {
+ extern void booke_wdt_setup_options(char *cmd_line);
+
+ booke_wdt_setup_options(cmd_line);
+ }
+#endif /* CONFIG_BOOKE_WDT */
+
identify_ppc_sys_by_id(mfspr(SVR));
/* setup the PowerPC module struct */
diff -Nupr linux-2.6.11/arch/ppc/platforms/85xx/mpc85xx_cds_common.c.orig linux-2.6.11-wdt/arch/ppc/platforms/85xx/mpc85xx_cds_common.c.orig
diff -Nupr linux-2.6.11/arch/ppc/platforms/85xx/sbc8560.c linux-2.6.11-wdt/arch/ppc/platforms/85xx/sbc8560.c
--- linux-2.6.11/arch/ppc/platforms/85xx/sbc8560.c 2005-03-09 12:58:37.742091016 +0900
+++ linux-2.6.11-wdt/arch/ppc/platforms/85xx/sbc8560.c 2005-03-09 13:07:38.360904464 +0900
@@ -198,6 +198,14 @@ platform_init(unsigned long r3, unsigned
strcpy(cmd_line, (char *) (r6 + KERNELBASE));
}
+#ifdef CONFIG_BOOKE_WDT
+ {
+ extern void booke_wdt_setup_options(char *cmd_line);
+
+ booke_wdt_setup_options(cmd_line);
+ }
+#endif /* CONFIG_BOOKE_WDT */
+
identify_ppc_sys_by_id(mfspr(SVR));
/* setup the PowerPC module struct */
diff -Nupr linux-2.6.11/arch/ppc/platforms/85xx/stx_gp3.c linux-2.6.11-wdt/arch/ppc/platforms/85xx/stx_gp3.c
--- linux-2.6.11/arch/ppc/platforms/85xx/stx_gp3.c 2005-03-09 12:56:20.477958336 +0900
+++ linux-2.6.11-wdt/arch/ppc/platforms/85xx/stx_gp3.c 2005-03-09 13:07:38.361904312 +0900
@@ -349,6 +349,14 @@ platform_init(unsigned long r3, unsigned
strcpy(cmd_line, (char *) (r6 + KERNELBASE));
}
+#ifdef CONFIG_BOOKE_WDT
+ {
+ extern void booke_wdt_setup_options(char *cmd_line);
+
+ booke_wdt_setup_options(cmd_line);
+ }
+#endif /* CONFIG_BOOKE_WDT */
+
identify_ppc_sys_by_id(mfspr(SVR));
/* setup the PowerPC module struct */
diff -Nupr linux-2.6.11/arch/ppc/platforms/85xx/stx_gp3.c.orig linux-2.6.11-wdt/arch/ppc/platforms/85xx/stx_gp3.c.orig
diff -Nupr linux-2.6.11/arch/ppc/syslib/open_pic.c linux-2.6.11-wdt/arch/ppc/syslib/open_pic.c
diff -Nupr linux-2.6.11/arch/ppc/syslib/ppc4xx_setup.c linux-2.6.11-wdt/arch/ppc/syslib/ppc4xx_setup.c
--- linux-2.6.11/arch/ppc/syslib/ppc4xx_setup.c 2005-03-09 12:54:06.589312504 +0900
+++ linux-2.6.11-wdt/arch/ppc/syslib/ppc4xx_setup.c 2005-03-09 13:07:38.361904312 +0900
@@ -48,10 +48,6 @@
extern void abort(void);
extern void ppc4xx_find_bridges(void);
-extern void ppc4xx_wdt_heartbeat(void);
-extern int wdt_enable;
-extern unsigned long wdt_period;
-
/* Global Variables */
bd_t __res;
@@ -257,22 +253,14 @@ ppc4xx_init(unsigned long r3, unsigned l
*(char *) (r7 + KERNELBASE) = 0;
strcpy(cmd_line, (char *) (r6 + KERNELBASE));
}
-#if defined(CONFIG_PPC405_WDT)
-/* Look for wdt= option on command line */
- if (strstr(cmd_line, "wdt=")) {
- int valid_wdt = 0;
- char *p, *q;
- for (q = cmd_line; (p = strstr(q, "wdt=")) != 0;) {
- q = p + 4;
- if (p > cmd_line && p[-1] != ' ')
- continue;
- wdt_period = simple_strtoul(q, &q, 0);
- valid_wdt = 1;
- ++q;
- }
- wdt_enable = valid_wdt;
- }
-#endif
+
+#ifdef CONFIG_BOOKE_WDT
+ {
+ extern void booke_wdt_setup_options(char *cmd_line);
+
+ booke_wdt_setup_options(cmd_line);
+ }
+#endif /* CONFIG_BOOKE_WDT */
/* Initialize machine-dependent vectors */
^ permalink raw reply
* Re: [PATCH] Non-DMA mode for floppy on PowerPC, new version
From: Benjamin Herrenschmidt @ 2005-03-09 4:03 UTC (permalink / raw)
To: Pavel Fedin; +Cc: linuxppc-dev list
In-Reply-To: <20050224115307.79c1d32c.sonic_amiga@rambler.ru>
On Thu, 2005-02-24 at 11:53 -0500, Pavel Fedin wrote:
> Here is a cleaned up version of my 2.6.8 kernel patch.
>
> .../...
Where is "can_use_virtual_dma" defined ? I think that should be made
a bit more clean. Except for that, it looks ok I suppose (my knowledge
of this legacy cruft is quite poor :)
Also, do you need to re-implement your own allocation routine ? Just
use get_dma_pages in both...
A pure style issue also: your static initialization should use C99
initializers (.field = value)
"fd_routine" could be renamed to something nicer, like fd_dma_ops (more
linux-style).
I haven't looked at the logic of the vdma code itself, I suppose that if
it works, it's good enough :)
Ben.
^ permalink raw reply
* Re: RFC: PHY Abstraction Layer II
From: Benjamin Herrenschmidt @ 2005-03-09 3:50 UTC (permalink / raw)
To: David S. Miller; +Cc: netdev, linuxppc-embedded
In-Reply-To: <20050308194229.41c23707.davem@davemloft.net>
On Tue, 2005-03-08 at 19:42 -0800, David S. Miller wrote:
> On Wed, 09 Mar 2005 13:14:16 +1100
> Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
>
> > I'll have a closer look when I find some time, see if it makes sense to
> > adapt sungem or not.
>
> Especially because of the Broadcom PHYs I bet it doesn't.
>
> Too many chips have to reset the MAC, or do other fancy stuff
> when programming the PHY to make this genphy thing very useful.
Oh, I think genphy is just a generic driver, but his layer has hooks for
other PHY drivers (wasn't it based on sungem_phy in the first place ?)
I discussed several steps of the design with Andy, the idea was to have
something a bit like sungem_phy.c with addditional common library for
doing the link polling & fallback stuff etc... that could be easily
shared by drivers.
Ben.
^ permalink raw reply
* Re: RFC: PHY Abstraction Layer II
From: David S. Miller @ 2005-03-09 3:42 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: netdev, linuxppc-embedded
In-Reply-To: <1110334456.32556.21.camel@gaston>
On Wed, 09 Mar 2005 13:14:16 +1100
Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
> I'll have a closer look when I find some time, see if it makes sense to
> adapt sungem or not.
Especially because of the Broadcom PHYs I bet it doesn't.
Too many chips have to reset the MAC, or do other fancy stuff
when programming the PHY to make this genphy thing very useful.
^ permalink raw reply
* Re: RFC: PHY Abstraction Layer II
From: Benjamin Herrenschmidt @ 2005-03-09 2:14 UTC (permalink / raw)
To: Andy Fleming; +Cc: Netdev, Embedded PPC Linux list
In-Reply-To: <1107b64b01fb8e9a6c84359bb56881a6@freescale.com>
On Tue, 2005-03-08 at 19:47 -0600, Andy Fleming wrote:
> I've finally gotten all of ebs's suggestions into the PHY code. Here
> is the new version. It has the following improvements:
>
> * All PHYs now determine speed,duplex, etc using the same generic code,
> rather than PHY-specific registers.
Some PHY are doing a better job with PHY specific registers I think ...
The gigabit for example isn't standard, and some PHYs sort-of manage to
deal with non-autoneg hubs in such a way that the "normal" aneg doesn't
succeeds, but the phy specific stuff does work. At least from stuff I've
been told a while ago, I have no direct experience here.
> * The genphy driver works for gigabit PHYs now, as well. In theory, if
> your PHY isn't broken in some way (I've encountered a number that are),
> you should be able to just use genphy.
Isn't the speed reporting of gigabit an implementation specific bit in
lots of PHYs ?
> * The genphy driver now detects what features the PHY has, rather than
> relying on arbitrarily hard-coded values
>
> * Pause negotiation and advertising has been added
>
> * PHY read and write functions now return errors if the bus read/write
> functions return errors. These errors are handled properly, and should
> not cause any problem. It is the bus's responsibility, however, to
> make sure that the PHY is started again once the error is cleared.
>
> This patch contains just the PHY code. A later email will contain the
> gianfar driver and 85xx patches, which will serve as an example for how
> to use the PHY code.
>
> A note about size: I did some rough size comparisons, and it looks
> like this code adds ~10 K to the binary size of the kernel (for PPC 32,
> 85xx). However, that's a rough estimate, since my tree includes
> features added to the gianfar driver (eg: ethtool support for setting
> speed/duplex).
I'll have a closer look when I find some time, see if it makes sense to
adapt sungem or not.
Ben.
^ permalink raw reply
* Re: mv64x60 updates
From: Benjamin Herrenschmidt @ 2005-03-09 2:03 UTC (permalink / raw)
To: Nicolas DET; +Cc: linuxppc-dev list
In-Reply-To: <20050307125808.6585A200008A@mwinf0308.wanadoo.fr>
>
> About arch/ppc/syslibs/ mv64x60 code. Well, it's a bit evil as it has
> hardcoded IRQ, hardcoded register base, it changes
> chipset configuration... whereas all of these is already done by the
> Pegasos II OpenFirmWare.
Embedded people don't understand what a firmware is ;)
(sorry, couldn't resist ...)
^ permalink raw reply
* Re: mv64x60 updates
From: Benjamin Herrenschmidt @ 2005-03-09 2:01 UTC (permalink / raw)
To: James Chapman; +Cc: Nicolas DET, Sven Luther, linuxppc-dev list
In-Reply-To: <422C495B.8090805@katalix.com>
On Mon, 2005-03-07 at 12:30 +0000, James Chapman wrote:
> Hi Nicolas,
>
> A few general comments:-
>
> - mv64x60 stuff is best posted to linuxppc-embedded
>
> - you change several generic files to support your platform. It should
> be possible to support new mv64x60 platforms by writing a new
> xxx_setup.c file in arch/ppc/platforms with no other generic changes.
> It is a goal that all mv64x60 boards can be supported by the generic
> code in arch/ppc/syslib. If some changes need to be made outside
> arch/ppc/platforms to support your board, try to make them generic so
> that other similar boards would be able to use them. I suggest you
> clone chrp_setup.c or katana.c rather than adding conditionals in
> chrp_setup.c for your board. Then use code in your board specific
> setup file to call arch/ppc/syslib mv64x60 routines as appropriate.
Welcome to the world of embedded people who use #ifdef's for their
platforms :)
Pegasos claims to be chrp compliant, and so uses chrp_setup.c and relies
on other chrp related bits. The point here is to limit the platform
specific code and use the device-tree as much as is possible. Though I
agree that Nicals patch introduce wrong dependencies (I need to do a
deeper review of it).
If chrp_setup.c indeed ends up beeing cluttered by too much Pegasos
specific stuff, then it will be time to do split pegasos to a different
platform type as part of CONFIG_PPC_MULTIPLATFORM.
Now as far as his patch is concernd, the whole is_pegasos2() stuff is
too ugly for words, I agree. If he really needs so, then he should add
an argument to mv64x60_init() in order to customize it's behaviour.
Though I haven't looked at the code to see why he's trying to skip
stuffs here.
> - you shouldn't need to add board-specific changes in mv643xx_eth.c.
> Setup device platform data for your board in your platform file.
> If something needs to be added to the platform data for a generic
> change to mv643xx_eth, do that rather than add platform conditionals
> in the driver.
Agreed, though I think that's almost there now.
> - why do you need to use SA_SHIRQ in the ethernet driver?
Why don't use use it ? SA_INTERRUPT is crap and should probably be
removed from the driver.
> /james
>
> Nicolas DET wrote:
>
> > Hello Sven,
> >
> > On 07/03/2005, you wrote:
> >
> >
> >>But i hear Nicolas has done some useful work yesterday evening, i will
> >>review it as soon as he is back from dreamland :)
> >
> >
> > You can find the patch against 2.6.11 from kernel.org here:
> > http://powernico.free.fr/patch_2.6.11_mv64x60.diff.bz2
> >
> > This patch shouldn't break mv code for others platform (non PegasosII), and
> > fix Pegasos II init...
> >
> > Basicly, I added mv64360_ispegasos2() in include/asm-ppc/mv64x60.h. Then:
> >
> > in arch/ppc/syslibs/mv64360_pic.c, I skip the IRQ init code
> > in arch/ppc/syslibs/mv64x60.c, I skip all the chip init & patch the
> > ressources tables for Pegasos II hardware (register base & IRQ).
> > in include/asm-ppc/mv64x60.h: added mv64360_ispegasos2()
> > in arch/ppc/kernel/chrp_setup.c, rename/added pegasos2_stuff() and call
> > mv64x60_init() if CONFIG_MV64x60
> > in drivers/net/mv64xx_eth.c, use SA_SHIRQ instead of SA_INTERRUPT for
> > request_irq if pegasos II detected
> >
> > The only thing to do is to add mv64360_ispegasos2() in include/asm-mips/...
> > because I use this function to use the correct flags in the ethernet
> > driver.
> >
> > Of course, this patch may be discuss as there are several architecture
> > using Marvell chipsets and each requieres some specific code.
> > I don't know where it's the best to place mv64360_ispeasos2(), maybe this
> > func could even be renamed mv64x60_ispegasos2()..
> >
> > Please, people from others MV64x60 architectures review this patch, modify
> > if it neeeded and check it doesn't break your architecture (I shouldn't but
> > for MIPS ethernet).
> >
> > Regards
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
--
Benjamin Herrenschmidt <benh@kernel.crashing.org>
^ permalink raw reply
* RFC: PHY Abstraction Layer II
From: Andy Fleming @ 2005-03-09 1:47 UTC (permalink / raw)
To: Netdev, Embedded PPC Linux list
[-- Attachment #1: Type: text/plain, Size: 1335 bytes --]
I've finally gotten all of ebs's suggestions into the PHY code. Here
is the new version. It has the following improvements:
* All PHYs now determine speed,duplex, etc using the same generic code,
rather than PHY-specific registers.
* The genphy driver works for gigabit PHYs now, as well. In theory, if
your PHY isn't broken in some way (I've encountered a number that are),
you should be able to just use genphy.
* The genphy driver now detects what features the PHY has, rather than
relying on arbitrarily hard-coded values
* Pause negotiation and advertising has been added
* PHY read and write functions now return errors if the bus read/write
functions return errors. These errors are handled properly, and should
not cause any problem. It is the bus's responsibility, however, to
make sure that the PHY is started again once the error is cleared.
This patch contains just the PHY code. A later email will contain the
gianfar driver and 85xx patches, which will serve as an example for how
to use the PHY code.
A note about size: I did some rough size comparisons, and it looks
like this code adds ~10 K to the binary size of the kernel (for PPC 32,
85xx). However, that's a rough estimate, since my tree includes
features added to the gianfar driver (eg: ethtool support for setting
speed/duplex).
[-- Attachment #2: phy_only_20050308.patch --]
[-- Type: application/octet-stream, Size: 77298 bytes --]
diff -Nru a/drivers/net/Kconfig b/drivers/net/Kconfig
--- a/drivers/net/Kconfig 2005-03-08 19:12:44 -06:00
+++ b/drivers/net/Kconfig 2005-03-08 19:12:44 -06:00
@@ -153,6 +153,8 @@
source "drivers/net/arcnet/Kconfig"
endif
+source "drivers/net/phy/Kconfig"
+
#
# Ethernet
#
@@ -2056,6 +2058,8 @@
config GIANFAR
tristate "Gianfar Ethernet"
depends on 85xx || 83xx
+ select PHYLIB
+ select PHYCONTROL
help
This driver supports the Gigabit TSEC on the MPC85xx
family of chips, and the FEC on the 8540
diff -Nru a/drivers/net/Makefile b/drivers/net/Makefile
--- a/drivers/net/Makefile 2005-03-08 19:12:44 -06:00
+++ b/drivers/net/Makefile 2005-03-08 19:12:44 -06:00
@@ -12,7 +12,7 @@
obj-$(CONFIG_BONDING) += bonding/
obj-$(CONFIG_GIANFAR) += gianfar_driver.o
-gianfar_driver-objs := gianfar.o gianfar_ethtool.o gianfar_phy.o
+gianfar_driver-objs := gianfar.o gianfar_ethtool.o gianfar_mii.o
#
# link order important here
@@ -63,6 +63,7 @@
#
obj-$(CONFIG_MII) += mii.o
+obj-$(CONFIG_PHYLIB) += phy/
obj-$(CONFIG_SUNDANCE) += sundance.o
obj-$(CONFIG_HAMACHI) += hamachi.o
diff -Nru a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/net/phy/Kconfig 2005-03-08 19:12:45 -06:00
@@ -0,0 +1,57 @@
+#
+# PHY Layer Configuration
+#
+
+menu "PHY device support"
+
+config PHYLIB
+ bool "PHY Device support and infrastructure"
+ depends on NET_ETHERNET
+ help
+ Ethernet controllers are usually attached to PHY
+ devices. This option provides infrastructure for
+ managing PHY devices.
+
+config PHYCONTROL
+ bool "Support for automatically handling PHY state changes"
+ depends on PHYLIB
+ help
+ Adds code to perform all the work for keeping PHY link
+ state (speed/duplex/etc) up-to-date. Also handles
+ interrupts.
+
+comment "MII PHY device drivers"
+ depends on PHYLIB
+
+config MARVELL_PHY
+ bool "Drivers for Marvell PHYs"
+ depends on PHYLIB
+ ---help---
+ Currently has a driver for the 88E1011S
+
+config DAVICOM_PHY
+ bool "Drivers for Davicom PHYs"
+ depends on PHYLIB
+ ---help---
+ Currently supports dm9161e and dm9131
+
+config QSEMI_PHY
+ bool "Drivers for Quality Semiconductor PHYs"
+ depends on PHYLIB
+ ---help---
+ Currently supports the qs6612
+
+config LXT_PHY
+ bool "Drivers for the Intel LXT PHYs"
+ depends on PHYLIB
+ ---help---
+ Currently supports the lxt970, lxt971
+
+config CICADA_PHY
+ bool "Drivers for the Cicada PHYs"
+ depends on PHYLIB
+ ---help---
+ Currently supports the cis8204
+
+endmenu
+
diff -Nru a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/net/phy/Makefile 2005-03-08 19:12:45 -06:00
@@ -0,0 +1,9 @@
+# Makefile for Linux PHY drivers
+
+obj-$(CONFIG_PHYLIB) += phy.o phy_device.o mdio_bus.o
+
+obj-$(CONFIG_MARVELL_PHY) += marvell.o
+obj-$(CONFIG_DAVICOM_PHY) += davicom.o
+obj-$(CONFIG_CICADA_PHY) += cicada.o
+obj-$(CONFIG_LXT_PHY) += lxt.o
+obj-$(CONFIG_QSEMI_PHY) += qsemi.o
diff -Nru a/drivers/net/phy/cicada.c b/drivers/net/phy/cicada.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/net/phy/cicada.c 2005-03-08 19:12:45 -06:00
@@ -0,0 +1,134 @@
+/*
+ * drivers/net/phy/cicada.c
+ *
+ * Driver for Cicada PHYs
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* Cicada Extended Control Register 1 */
+#define MII_CIS8201_EXT_CON1 0x17
+#define MII_CIS8201_EXTCON1_INIT 0x0000
+
+/* Cicada Interrupt Mask Register */
+#define MII_CIS8201_IMASK 0x19
+#define MII_CIS8201_IMASK_IEN 0x8000
+#define MII_CIS8201_IMASK_SPEED 0x4000
+#define MII_CIS8201_IMASK_LINK 0x2000
+#define MII_CIS8201_IMASK_DUPLEX 0x1000
+#define MII_CIS8201_IMASK_MASK 0xf000
+
+/* Cicada Interrupt Status Register */
+#define MII_CIS8201_ISTAT 0x1a
+#define MII_CIS8201_ISTAT_STATUS 0x8000
+#define MII_CIS8201_ISTAT_SPEED 0x4000
+#define MII_CIS8201_ISTAT_LINK 0x2000
+#define MII_CIS8201_ISTAT_DUPLEX 0x1000
+
+/* Cicada Auxiliary Control/Status Register */
+#define MII_CIS8201_AUX_CONSTAT 0x1c
+#define MII_CIS8201_AUXCONSTAT_INIT 0x0004
+#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020
+#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018
+#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010
+#define MII_CIS8201_AUXCONSTAT_100 0x0008
+
+
+static int cis820x_probe(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_write(phydev, MII_CIS8201_AUX_CONSTAT,
+ MII_CIS8201_AUXCONSTAT_INIT);
+
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, MII_CIS8201_EXT_CON1,
+ MII_CIS8201_EXTCON1_INIT);
+
+ return err;
+}
+
+static int cis820x_ack_interrupt(struct phy_device *phydev)
+{
+ int err = phy_read(phydev, MII_CIS8201_ISTAT);
+
+ return (err < 0) ? err : 0;
+}
+
+static int cis820x_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ if(phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, MII_CIS8201_IMASK,
+ MII_CIS8201_IMASK_MASK);
+ else
+ err = phy_write(phydev, MII_CIS8201_IMASK, 0);
+
+ return err;
+}
+
+/* Cicada 820x */
+static struct phy_driver cis8204_driver = {
+ 0x000fc440,
+ "Cicada Cis8204",
+ 0x000fffc0,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .probe = &cis820x_probe,
+ .config_aneg = &genphy_config_aneg,
+ .read_status = &genphy_read_status,
+ .ack_interrupt = &cis820x_ack_interrupt,
+ .config_intr = &cis820x_config_intr,
+};
+
+int __init cis8204_init(void)
+{
+ int retval;
+
+ retval = phy_driver_register(&cis8204_driver);
+
+ return retval;
+}
+
+static void __exit cis8204_exit(void)
+{
+ phy_driver_unregister(&cis8204_driver);
+}
+
+module_init(cis8204_init);
+module_exit(cis8204_exit);
diff -Nru a/drivers/net/phy/davicom.c b/drivers/net/phy/davicom.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/net/phy/davicom.c 2005-03-08 19:12:45 -06:00
@@ -0,0 +1,275 @@
+/*
+ * drivers/net/phy/davicom.c
+ *
+ * Driver for Davicom PHYs
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#define MII_DM9161_SCR 0x10
+#define MII_DM9161_SCR_INIT 0x0610
+
+/* DM9161 Interrupt Register */
+#define MII_DM9161_INTR 0x15
+#define MII_DM9161_INTR_PEND 0x8000
+#define MII_DM9161_INTR_DPLX_MASK 0x0800
+#define MII_DM9161_INTR_SPD_MASK 0x0400
+#define MII_DM9161_INTR_LINK_MASK 0x0200
+#define MII_DM9161_INTR_MASK 0x0100
+#define MII_DM9161_INTR_DPLX_CHANGE 0x0010
+#define MII_DM9161_INTR_SPD_CHANGE 0x0008
+#define MII_DM9161_INTR_LINK_CHANGE 0x0004
+#define MII_DM9161_INTR_INIT 0x0000
+#define MII_DM9161_INTR_STOP \
+(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \
+ | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
+
+/* DM9161 10BT Configuration/Status */
+#define MII_DM9161_10BTCSR 0x12
+#define MII_DM9161_10BTCSR_INIT 0x7800
+
+struct dm9161_private {
+ struct timer_list timer;
+ int resetdone;
+};
+
+#define DM9161_DELAY 1
+int dm9161_config_intr(struct phy_device *phydev)
+{
+ int temp;
+
+ temp = phy_read(phydev, MII_DM9161_INTR);
+
+ if (temp < 0)
+ return temp;
+
+ if(PHY_INTERRUPT_ENABLED == phydev->interrupts )
+ temp &= ~(MII_DM9161_INTR_STOP);
+ else
+ temp |= MII_DM9161_INTR_STOP;
+
+ temp = phy_write(phydev, MII_DM9161_INTR, temp);
+
+ return temp;
+}
+
+
+#if 0
+static void dm9161_timer(unsigned long data)
+{
+ struct phy_device *phydev = (struct phy_device *)data;
+ struct dm9161_private *priv = phydev->priv;
+ int status = phy_read(phydev, MII_BMSR);
+
+ if (status < 0) {
+ mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
+ return;
+ }
+
+ spin_lock(&phydev->lock);
+ if (status & BMSR_ANEGCOMPLETE) {
+ if (PHY_PENDING == phydev->state)
+ phydev->state = PHY_UP;
+ else
+ phydev->state = PHY_READY;
+ } else
+ mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
+
+ spin_unlock(&phydev->lock);
+}
+#endif
+
+
+static int dm9161_config_aneg(struct phy_device *phydev)
+{
+ int err;
+
+ /* Isolate the PHY */
+ err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
+
+ if (err < 0)
+ return err;
+
+ /* Configure the new settings */
+ err = genphy_config_advert(phydev);
+
+ if (err < 0)
+ return err;
+
+ /* Reconnect the PHY, and enable Autonegotiation */
+ err = phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
+
+ if (err < 0)
+ return err;
+
+#if 0
+ /* Start a timer for DM9161_DELAY seconds to wait
+ * for the PHY to be ready */
+ init_timer(&priv->timer);
+ priv->timer.function = &dm9161_timer;
+ priv->timer.data = (unsigned long) phydev;
+ mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
+#endif
+
+ return 0;
+}
+
+static int dm9161_probe(struct phy_device *phydev)
+{
+ struct dm9161_private *priv;
+ int err;
+
+ /* Allocate the private data structure */
+ priv = kmalloc(sizeof(struct dm9161_private), GFP_KERNEL);
+
+ if (NULL == priv)
+ return -ENOMEM;
+
+ phydev->priv = priv;
+
+ /* Reset is not done yet */
+ priv->resetdone = 0;
+
+ /* Isolate the PHY */
+ err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
+
+ if (err < 0)
+ return err;
+
+ /* Do not bypass the scrambler/descrambler */
+ err = phy_write(phydev, MII_DM9161_SCR, MII_DM9161_SCR_INIT);
+
+ if (err < 0)
+ return err;
+
+ /* Clear 10BTCSR to default */
+ err = phy_write(phydev, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT);
+
+ if (err < 0)
+ return err;
+
+ /* Reconnect the PHY, and enable Autonegotiation */
+ err = phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
+
+ if (err < 0)
+ return err;
+
+#if 0
+ phydev->state = PHY_STARTING;
+
+ /* Start a timer for DM9161_DELAY seconds to wait
+ * for the PHY to be ready */
+ init_timer(&priv->timer);
+ priv->timer.function = &dm9161_timer;
+ priv->timer.data = (unsigned long) phydev;
+ mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
+
+ printk(KERN_INFO "Bringing up a Davicom PHY, this could take"
+ " a while...\n");
+#endif
+ return 0;
+}
+
+static void dm9161_remove(struct phy_device *phydev)
+{
+ struct dm9161_private *priv = phydev->priv;
+
+// del_timer_sync(&priv->timer);
+ kfree(priv);
+}
+
+static int dm9161_ack_interrupt(struct phy_device *phydev)
+{
+ int err = phy_read(phydev, MII_DM9161_INTR);
+
+ return (err < 0) ? err : 0;
+}
+
+static struct phy_driver dm9161_driver = {
+ .phy_id = 0x0181b880,
+ .name = "Davicom DM9161E",
+ .phy_id_mask = 0x0ffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .probe = dm9161_probe,
+ .config_aneg = dm9161_config_aneg,
+ .read_status = genphy_read_status,
+ .remove = dm9161_remove,
+};
+
+static struct phy_driver dm9131_driver = {
+ .phy_id = 0x00181b80,
+ .name = "Davicom DM9131",
+ .phy_id_mask = 0x0ffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = dm9161_ack_interrupt,
+ .config_intr = dm9161_config_intr,
+};
+
+int __init dm9161_init(void)
+{
+ int retval;
+
+ retval = phy_driver_register(&dm9161_driver);
+
+ return retval;
+}
+
+static void __exit dm9161_exit(void)
+{
+ phy_driver_unregister(&dm9161_driver);
+}
+
+module_init(dm9161_init);
+module_exit(dm9161_exit);
+
+int __init dm9131_init(void)
+{
+ int retval;
+
+ retval = phy_driver_register(&dm9131_driver);
+
+ return retval;
+}
+
+static void __exit dm9131_exit(void)
+{
+ phy_driver_unregister(&dm9131_driver);
+}
+
+module_init(dm9131_init);
+module_exit(dm9131_exit);
diff -Nru a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/net/phy/lxt.c 2005-03-08 19:12:45 -06:00
@@ -0,0 +1,179 @@
+/*
+ * drivers/net/phy/lxt.c
+ *
+ * Driver for Intel LXT PHYs
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* The Level one LXT970 is used by many boards */
+
+#define MII_LXT970_IER 17 /* Interrupt Enable Register */
+
+#define MII_LXT970_IER_IEN 0x0002
+
+#define MII_LXT970_ISR 18 /* Interrupt Status Register */
+
+#define MII_LXT970_CONFIG 19 /* Configuration Register */
+
+/* ------------------------------------------------------------------------- */
+/* The Level one LXT971 is used on some of my custom boards */
+
+/* register definitions for the 971 */
+#define MII_LXT971_IER 18 /* Interrupt Enable Register */
+#define MII_LXT971_IER_IEN 0x00f2
+
+#define MII_LXT971_ISR 19 /* Interrupt Status Register */
+
+
+static int lxt970_ack_interrupt(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_read(phydev, MII_BMSR);
+
+ if (err < 0)
+ return err;
+
+ err = phy_read(phydev, MII_LXT970_ISR);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int lxt970_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ if(phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, MII_LXT970_IER, MII_LXT970_IER_IEN);
+ else
+ err = phy_write(phydev, MII_LXT970_IER, 0);
+
+ return err;
+}
+
+static int lxt970_probe(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_write(phydev, MII_LXT970_CONFIG, 0);
+
+ return err;
+}
+
+
+static int lxt971_ack_interrupt(struct phy_device *phydev)
+{
+ int err = phy_read(phydev, MII_LXT971_ISR);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int lxt971_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ if(phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, MII_LXT971_IER, MII_LXT971_IER_IEN);
+ else
+ err = phy_write(phydev, MII_LXT971_IER, 0);
+
+ return err;
+}
+
+static struct phy_driver lxt970_driver = {
+ .phy_id = 0x07810000,
+ .name = "LXT970",
+ .phy_id_mask = 0x0fffffff,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .probe = lxt970_probe,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = lxt970_ack_interrupt,
+ .config_intr = lxt970_config_intr,
+};
+
+static struct phy_driver lxt971_driver = {
+ .phy_id = 0x0001378e,
+ .name = "LXT971",
+ .phy_id_mask = 0x0fffffff,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = lxt971_ack_interrupt,
+ .config_intr = lxt971_config_intr,
+};
+
+int __init lxt970_init(void)
+{
+ int retval;
+
+ retval = phy_driver_register(&lxt970_driver);
+
+ return retval;
+}
+
+static void __exit lxt970_exit(void)
+{
+ phy_driver_unregister(&lxt970_driver);
+}
+
+module_init(lxt970_init);
+module_exit(lxt970_exit);
+
+int __init lxt971_init(void)
+{
+ int retval;
+
+ retval = phy_driver_register(&lxt971_driver);
+
+ return retval;
+}
+
+static void __exit lxt971_exit(void)
+{
+ phy_driver_unregister(&lxt971_driver);
+}
+
+module_init(lxt971_init);
+module_exit(lxt971_exit);
diff -Nru a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/net/phy/marvell.c 2005-03-08 19:12:45 -06:00
@@ -0,0 +1,139 @@
+/*
+ * drivers/net/phy/marvell.c
+ *
+ * Driver for Marvell PHYs
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#define MII_M1011_IEVENT 0x13
+#define MII_M1011_IEVENT_CLEAR 0x0000
+
+#define MII_M1011_IMASK 0x12
+#define MII_M1011_IMASK_INIT 0x6400
+#define MII_M1011_IMASK_CLEAR 0x0000
+
+static int marvell_ack_interrupt(struct phy_device *phydev)
+{
+ int err;
+
+ /* Clear the interrupts by reading the reg */
+ err = phy_read(phydev, MII_M1011_IEVENT);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int marvell_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ if(phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
+ else
+ err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
+
+ return err;
+}
+
+static int marvell_config_aneg(struct phy_device *phydev)
+{
+ int err;
+
+ /* The Marvell PHY has an errata which requires
+ * that certain registers get written in order
+ * to restart autonegotiation */
+ err = phy_write(phydev, MII_BMCR, BMCR_RESET);
+
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1d, 0x1f);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1e, 0x200c);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1d, 0x5);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1e, 0);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1e, 0x100);
+ if (err < 0)
+ return err;
+
+
+ err = genphy_config_aneg(phydev);
+
+ return err;
+}
+
+
+static struct phy_driver m88e1101_driver = {
+ .phy_id = 0x01410c00,
+ .phy_id_mask = 0xffffff00,
+ .name = "Marvell 88E1101",
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_aneg = &marvell_config_aneg,
+ .read_status = &genphy_read_status,
+ .ack_interrupt = &marvell_ack_interrupt,
+ .config_intr = &marvell_config_intr,
+};
+
+int __init marvell_init(void)
+{
+ int retval;
+
+ retval = phy_driver_register(&m88e1101_driver);
+
+ return retval;
+}
+
+static void __exit marvell_exit(void)
+{
+ phy_driver_unregister(&m88e1101_driver);
+}
+
+module_init(marvell_init);
+module_exit(marvell_exit);
diff -Nru a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/net/phy/mdio_bus.c 2005-03-08 19:12:45 -06:00
@@ -0,0 +1,178 @@
+/*
+ * drivers/net/phy/mdio_bus.c
+ *
+ * MDIO Bus interface
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* register_mdiobus
+ * bus: The bus being registered
+ *
+ * description: Called by a bus driver to bring up all the PHYs
+ * on the bus, and attach them to the bus
+ */
+int register_mdiobus(struct mii_bus *bus)
+{
+ int i;
+ int err = 0;
+
+ spin_lock_init(&bus->mdio_lock);
+
+ if (NULL == bus || NULL == bus->name ||
+ NULL == bus->read ||
+ NULL == bus->write)
+ return -EINVAL;
+
+ if (bus->reset)
+ bus->reset(bus);
+
+ for (i=0; i < PHY_MAX_ADDR; i++) {
+ struct phy_device *phydev;
+
+ phydev = get_phy_device(bus, i);
+
+ /* There's a PHY at this address
+ * We need to set:
+ * 1) IRQ
+ * 2) bus_id
+ * 3) parent
+ * 4) bus
+ * 5) mii_bus
+ * And, we need to register it */
+ if (phydev) {
+ phydev->irq = bus->irq[i];
+
+ phydev->dev.parent = bus->dev;
+
+ phydev->dev.bus = &mdio_bus_type;
+
+ phydev->bus = bus;
+
+ sprintf(phydev->dev.bus_id, "phy%d:%d", bus->id, i);
+
+ err = device_register(&phydev->dev);
+
+ if (err)
+ printk("phy %d did not register (%d)\n",
+ i, err);
+
+ /* If get_phy_device returned NULL, it may be
+ * because an error occurred. If so, we return
+ * that error */
+ } else if (errno)
+ return errno;
+
+ bus->phy_map[i] = phydev;
+ }
+
+ pr_info("%s: probed\n", bus->name);
+
+ return err;
+}
+EXPORT_SYMBOL(register_mdiobus);
+
+void unregister_mdiobus(struct mii_bus *bus)
+{
+ int i;
+
+ for (i=0; i < PHY_MAX_ADDR; i++)
+ if (bus->phy_map[i]) {
+ device_unregister(&bus->phy_map[i]->dev);
+ kfree(bus->phy_map[i]);
+ }
+
+}
+EXPORT_SYMBOL(unregister_mdiobus);
+
+/* mdio_bus_match
+ * dev: a PHY device
+ * drv: a PHY driver
+ *
+ * description: Given a PHY device, and a PHY driver, return 1 if
+ * the driver supports the device. Otherwise, return 0
+ */
+int mdio_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+ struct phy_driver *phydrv = to_phy_driver(drv);
+
+ return (phydrv->phy_id == (phydev->phy_id & phydrv->phy_id_mask));
+}
+
+/* Suspend and resume. Copied from platform_suspend and
+ * platform_resume
+ */
+static int mdio_bus_suspend(struct device * dev, u32 state)
+{
+ int ret = 0;
+
+ if (dev->driver && dev->driver->suspend) {
+ ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE);
+ if (ret == 0)
+ ret = dev->driver->suspend(dev, state, SUSPEND_SAVE_STATE);
+ if (ret == 0)
+ ret = dev->driver->suspend(dev, state, SUSPEND_POWER_DOWN);
+ }
+ return ret;
+}
+
+static int mdio_bus_resume(struct device * dev)
+{
+ int ret = 0;
+
+ if (dev->driver && dev->driver->resume) {
+ ret = dev->driver->resume(dev, RESUME_POWER_ON);
+ if (ret == 0)
+ ret = dev->driver->resume(dev, RESUME_RESTORE_STATE);
+ if (ret == 0)
+ ret = dev->driver->resume(dev, RESUME_ENABLE);
+ }
+ return ret;
+}
+
+struct bus_type mdio_bus_type = {
+ .name = "mdio_bus",
+ .match = mdio_bus_match,
+ .suspend= mdio_bus_suspend,
+ .resume = mdio_bus_resume,
+};
+
+int __init mdio_bus_init(void)
+{
+ return bus_register(&mdio_bus_type);
+}
+
+subsys_initcall(mdio_bus_init);
diff -Nru a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/net/phy/phy.c 2005-03-08 19:12:45 -06:00
@@ -0,0 +1,625 @@
+/*
+ * drivers/net/phy/phy.c
+ *
+ * Framework for configuring and reading PHY devices
+ * Based on code in sungem_phy.c and gianfar_phy.c
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+int phy_read(struct phy_device *phydev, u16 regnum);
+int phy_write(struct phy_device *phydev, u16 regnum, u16 val);
+
+/* Convenience functions for reading a given PHY register.
+ * This MUST NOT be called from interrupt context,
+ * because the bus read function may sleep
+ * or generally lock up. */
+int phy_read(struct phy_device *phydev, u16 regnum)
+{
+ int retval;
+ struct mii_bus *bus = phydev->bus;
+
+ spin_lock_bh(&bus->mdio_lock);
+ retval = bus->read(bus, phydev->addr, regnum);
+ spin_unlock_bh(&bus->mdio_lock);
+
+ return retval;
+}
+
+int phy_write(struct phy_device *phydev, u16 regnum, u16 val)
+{
+ int err;
+ struct mii_bus *bus = phydev->bus;
+
+ spin_lock_bh(&bus->mdio_lock);
+ err = bus->write(bus, phydev->addr, regnum, val);
+ spin_unlock_bh(&bus->mdio_lock);
+
+ return err;
+}
+
+
+int phy_clear_interrupt(struct phy_device *phydev)
+{
+ int err = 0;
+
+ if (phydev->drv->ack_interrupt)
+ err = phydev->drv->ack_interrupt(phydev);
+
+ return err;
+}
+
+
+int phy_config_interrupt(struct phy_device *phydev, u32 interrupts)
+{
+ int err = 0;
+
+ phydev->interrupts = interrupts;
+ if (phydev->drv->config_intr)
+ err = phydev->drv->config_intr(phydev);
+
+ return err;
+}
+
+
+static inline int phy_read_status(struct phy_device *phydev)
+{
+ return phydev->drv->read_status(phydev);
+}
+
+/* phy_aneg_done
+ *
+ * description: Reads the status register and returns 0 either if
+ * auto-negotiation is incomplete, or if there was an error.
+ * Returns BMSR_ANEGCOMPLETE if auto-negotiation is done.
+ * Must be called with phydev->lock locked.
+ */
+static inline int phy_aneg_done(struct phy_device *phydev)
+{
+ int retval;
+
+ retval = phy_read(phydev, MII_BMSR);
+
+ if (retval < 0)
+ return retval;
+
+ return retval & BMSR_ANEGCOMPLETE;
+}
+
+/* phy_start_aneg
+ * phydev: The PHY on which to initiate auto-negotiation
+ *
+ * description: Calls the PHY driver's config_aneg, and then
+ * sets the PHY state to PHY_AN if auto-negotiation is enabled,
+ * and to PHY_FORCING if auto-negotiation is disabled. Unless
+ * the PHY is currently HALTED.
+ */
+int phy_start_aneg(struct phy_device *phydev)
+{
+ int err = 0;
+
+ spin_lock(&phydev->lock);
+
+ if (AUTONEG_DISABLE == phydev->autoneg)
+ phy_sanitize_settings(phydev);
+
+ err = phydev->drv->config_aneg(phydev);
+
+ if (err < 0)
+ return err;
+
+ if (phydev->state != PHY_HALTED) {
+ if (AUTONEG_ENABLE == phydev->autoneg) {
+ phydev->state = PHY_AN;
+ phydev->link_timeout = PHY_AN_TIMEOUT;
+ } else {
+ phydev->state = PHY_FORCING;
+ phydev->link_timeout = PHY_FORCE_TIMEOUT;
+ }
+ }
+
+ spin_unlock(&phydev->lock);
+
+ return err;
+}
+
+
+/* A structure for mapping a particular speed and duplex
+ * combination to a particular SUPPORTED and ADVERTISED value */
+struct phy_setting {
+ int speed;
+ int duplex;
+ u32 setting;
+};
+
+/* A mapping of all SUPPORTED settings to speed/duplex */
+static struct phy_setting settings[] = {
+ { .speed = 10000, .duplex = DUPLEX_FULL,
+ .setting = SUPPORTED_10000baseT_Full,
+ },
+ { .speed = SPEED_1000, .duplex = DUPLEX_FULL,
+ .setting = SUPPORTED_1000baseT_Full,
+ },
+ { .speed = SPEED_1000, .duplex = DUPLEX_HALF,
+ .setting = SUPPORTED_1000baseT_Half,
+ },
+ { .speed = SPEED_100, .duplex = DUPLEX_FULL,
+ .setting = SUPPORTED_100baseT_Full,
+ },
+ { .speed = SPEED_100, .duplex = DUPLEX_HALF,
+ .setting = SUPPORTED_100baseT_Half,
+ },
+ { .speed = SPEED_10, .duplex = DUPLEX_FULL,
+ .setting = SUPPORTED_10baseT_Full,
+ },
+ { .speed = SPEED_10, .duplex = DUPLEX_HALF,
+ .setting = SUPPORTED_10baseT_Half,
+ },
+};
+
+#define MAX_NUM_SETTINGS (sizeof(settings)/sizeof(struct phy_setting))
+
+/* phy_find_setting
+ * speed: desired speed of setting
+ * duplex: desired duplex of setting
+ *
+ * description: Searches the settings array for the setting which
+ * matches the desired speed and duplex, and returns the index
+ * of that setting. Returns the index of the last setting if
+ * none of the others match.
+ */
+static inline int phy_find_setting(int speed, int duplex)
+{
+ int idx = 0;
+
+ while (idx < MAX_NUM_SETTINGS &&
+ (settings[idx].speed != speed ||
+ settings[idx].duplex != duplex))
+ idx++;
+
+ return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
+}
+
+/* phy_find_valid
+ * idx: The first index in settings[] to search
+ * features: A mask of the valid settings
+ *
+ * description: Returns the index of the first valid setting less
+ * than or equal to the one pointed to by idx, as determined by
+ * the mask in features. Returns the index of the last setting
+ * if nothing else matches.
+ */
+static inline int phy_find_valid(int idx, u32 features)
+{
+ while (idx < MAX_NUM_SETTINGS && !(settings[idx].setting & features))
+ idx++;
+
+ return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
+}
+
+/* phy_sanitize_settings
+ * phydev: The PHY in question
+ *
+ * description: Make sure the PHY is set to supported speeds and
+ * duplexes. Drop down by one in this order: 1000/FULL,
+ * 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF
+ */
+void phy_sanitize_settings(struct phy_device *phydev)
+{
+ u32 features = phydev->supported;
+ int idx;
+
+ /* Sanitize settings based on PHY capabilities */
+ if ((features & SUPPORTED_Autoneg) == 0)
+ phydev->autoneg = 0;
+
+ idx = phy_find_valid(phy_find_setting(phydev->speed, phydev->duplex),
+ features);
+
+ phydev->speed = settings[idx].speed;
+ phydev->duplex = settings[idx].duplex;
+}
+
+/* phy_force_reduction
+ * phydev: The PHY in question
+ *
+ * description: Reduces the speed/duplex settings by
+ * one notch. The order is so:
+ * 1000/FULL, 1000/HALF, 100/FULL, 100/HALF,
+ * 10/FULL, 10/HALF. The function bottoms out at 10/HALF.
+ */
+void phy_force_reduction(struct phy_device *phydev)
+{
+ int idx;
+
+ idx = phy_find_setting(phydev->speed, phydev->duplex);
+
+ idx++;
+
+ idx = phy_find_valid(idx, phydev->supported);
+
+ phydev->speed = settings[idx].speed;
+ phydev->duplex = settings[idx].duplex;
+
+ pr_info("Trying %d/%s\n", phydev->speed,
+ DUPLEX_FULL == phydev->duplex ? "FULL" : "HALF");
+}
+
+#ifdef CONFIG_PHYCONTROL
+/* phy_error:
+ *
+ * Moves the PHY to the HALTED state in response to a read
+ * or write error, and tells the controller the link is down.
+ * Must not be called from interrupt context, or while the
+ * phydev->lock is held.
+ */
+void phy_error(struct phy_device *phydev)
+{
+ spin_lock(&phydev->lock);
+ phydev->state = PHY_HALTED;
+ spin_unlock(&phydev->lock);
+}
+
+/* phy_interrupt
+ * irq: Interrupt number
+ * phy_dat: PHY device which caused the interrupt (presumably)
+ * regs: --
+ *
+ * description: When a PHY interrupt occurs, the handler disables
+ * interrupts, and schedules a work task to clear the interrupt.
+ */
+static irqreturn_t phy_interrupt(int irq, void *phy_dat, struct pt_regs *regs)
+{
+ struct phy_device *phydev = phy_dat;
+
+ /* The MDIO bus is not allowed to be written in interrupt
+ * context, so we need to disable the irq here. A work
+ * queue will write the PHY to disable and clear the
+ * interrupt, and then reenable the irq line. */
+ disable_irq_nosync(irq);
+
+ schedule_work(&phydev->phy_queue);
+
+ return IRQ_HANDLED;
+}
+
+/* phy_start_interrupts
+ * phydev: The PHY whose interrupts are being enabled
+ *
+ * description: Request the interrupt for the given PHY. If
+ * this fails, then we set irq to -1 so that we do polling.
+ * Otherwise, we enable the interrupts in the PHY.
+ * Returns 0 on success.
+ */
+int phy_start_interrupts(struct phy_device *phydev)
+{
+ int err = 0;
+
+ if (request_irq(phydev->irq, phy_interrupt,
+ SA_SHIRQ,
+ "phy_interrupt",
+ phydev) < 0) {
+ printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
+ phydev->bus->name,
+ phydev->irq);
+ phydev->irq = -1;
+ return 0;
+ }
+
+ err = phy_clear_interrupt(phydev);
+
+ if (err < 0)
+ return err;
+
+ err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED);
+
+ return err;
+}
+
+/* Scheduled by the phy_interrupt/timer to handle PHY changes */
+void phy_change(void *data)
+{
+ int err;
+ struct phy_device *phydev = data;
+
+ /* Disable PHY interrupts */
+ err = phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
+
+ if (err)
+ goto phy_err;
+
+ /* Clear the interrupt */
+ err = phy_clear_interrupt(phydev);
+
+ if (err)
+ goto phy_err;
+
+ spin_lock(&phydev->lock);
+ if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state))
+ phydev->state = PHY_CHANGELINK;
+ spin_unlock(&phydev->lock);
+
+ enable_irq(phydev->irq);
+
+ /* Reenable interrupts */
+ err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED);
+
+ if (err)
+ goto irq_enable_err;
+
+ return;
+
+irq_enable_err:
+ disable_irq(phydev->irq);
+phy_err:
+ phy_error(phydev);
+}
+
+/* Bring down the PHY link, and stop checking the status. */
+void phy_stop(struct phy_device *phydev)
+{
+ spin_lock(&phydev->lock);
+
+ if (PHY_HALTED == phydev->state) {
+ spin_unlock(&phydev->lock);
+ return;
+ }
+
+ if (phydev->irq != -1) {
+ /* Clear any pending interrupts */
+ phy_clear_interrupt(phydev);
+
+ /* Disable PHY Interrupts */
+ phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
+ }
+
+ phydev->state = PHY_HALTED;
+
+ spin_unlock(&phydev->lock);
+}
+
+
+/* phy_start
+ * phydev: The PHY device being started
+ *
+ * description: Indicates the attached device's readiness to
+ * handle PHY-related work. Used during startup to start the
+ * PHY, and after a call to phy_stop() to resume operation.
+ * Also used to indicate the MDIO bus has cleared an error
+ * condition.
+ */
+void phy_start(struct phy_device *phydev)
+{
+ spin_lock(&phydev->lock);
+
+ switch (phydev->state) {
+ case PHY_STARTING:
+ phydev->state = PHY_PENDING;
+ break;
+ case PHY_READY:
+ phydev->state = PHY_UP;
+ break;
+ case PHY_HALTED:
+ phydev->state = PHY_RESUMING;
+ default:
+ break;
+ }
+ spin_unlock(&phydev->lock);
+}
+EXPORT_SYMBOL(phy_stop);
+EXPORT_SYMBOL(phy_start);
+
+/* PHY timer which handles the state machine */
+void phy_timer(unsigned long data)
+{
+ struct phy_device *phydev = (struct phy_device *)data;
+ int needs_aneg = 0;
+ int err = 0;
+
+ spin_lock(&phydev->lock);
+
+ if (phydev->adjust_state)
+ phydev->adjust_state(phydev->attached_dev);
+
+ switch(phydev->state) {
+ case PHY_DOWN:
+ case PHY_STARTING:
+ case PHY_READY:
+ case PHY_PENDING:
+ break;
+ case PHY_UP:
+ needs_aneg = 1;
+
+ phydev->link_timeout = PHY_AN_TIMEOUT;
+
+ if (phydev->irq != -1)
+ err = phy_start_interrupts(phydev);
+
+ break;
+ case PHY_AN:
+ /* Check if negotiation is done. Break
+ * if there's an error */
+ err = phy_aneg_done(phydev);
+ if (err < 0)
+ break;
+
+ /* If auto-negotiation is done, we change to
+ * either RUNNING, or NOLINK */
+ if (err > 0) {
+ err = phy_read_status(phydev);
+
+ if (err)
+ break;
+
+ if (phydev->link)
+ phydev->state = PHY_RUNNING;
+ else
+ phydev->state = PHY_NOLINK;
+
+ phydev->adjust_link(phydev->attached_dev);
+
+ } else if (0 == phydev->link_timeout--) {
+ /* The counter expired, so either we
+ * switch to forced mode, or the
+ * magic_aneg bit exists, and we try aneg
+ * again */
+ if (!(phydev->drv->flags & PHY_HAS_MAGICANEG)) {
+ int idx;
+
+ /* We'll start from the
+ * fastest speed, and work
+ * our way down */
+ idx = phy_find_valid(0,
+ phydev->supported);
+
+ phydev->speed = settings[idx].speed;
+ phydev->duplex = settings[idx].duplex;
+
+ phydev->autoneg = AUTONEG_DISABLE;
+ phydev->state = PHY_FORCING;
+ phydev->link_timeout =
+ PHY_FORCE_TIMEOUT;
+
+ pr_info("Trying %d/%s\n", phydev->speed,
+ DUPLEX_FULL ==
+ phydev->duplex ?
+ "FULL" : "HALF");
+ }
+
+ needs_aneg = 1;
+ }
+ break;
+ case PHY_NOLINK:
+ err = phy_read_status(phydev);
+
+ if (err)
+ break;
+
+ if (phydev->link) {
+ phydev->state = PHY_RUNNING;
+ phydev->adjust_link(phydev->attached_dev);
+ }
+ break;
+ case PHY_FORCING:
+ err = phy_read_status(phydev);
+
+ if (err)
+ break;
+
+ if (phydev->link) {
+ phydev->state = PHY_RUNNING;
+ } else {
+ if (0 == phydev->link_timeout--) {
+ phy_force_reduction(phydev);
+ needs_aneg = 1;
+ }
+ }
+
+ phydev->adjust_link(phydev->attached_dev);
+ break;
+ case PHY_RUNNING:
+ /* Only register a CHANGE if we aren't
+ * using interrupts */
+ if (-1 == phydev->irq)
+ phydev->state = PHY_CHANGELINK;
+ break;
+ case PHY_CHANGELINK:
+ err = phy_read_status(phydev);
+
+ if (err)
+ break;
+
+ if (phydev->link)
+ phydev->state = PHY_RUNNING;
+ else {
+ phydev->state = PHY_NOLINK;
+ }
+
+ phydev->adjust_link(phydev->attached_dev);
+
+ if (-1 != phydev->irq)
+ err = phy_config_interrupt(phydev,
+ PHY_INTERRUPT_ENABLED);
+ break;
+ case PHY_HALTED:
+ if (phydev->link) {
+ phydev->link = 0;
+ phydev->adjust_link(phydev->attached_dev);
+ }
+ break;
+ case PHY_RESUMING:
+
+ err = phy_clear_interrupt(phydev);
+
+ if (err)
+ break;
+
+ err = phy_config_interrupt(phydev,
+ PHY_INTERRUPT_ENABLED);
+
+ if (err)
+ break;
+
+ if (AUTONEG_ENABLE == phydev->autoneg) {
+ err = phy_aneg_done(phydev);
+ if (err < 0)
+ break;
+
+ /* err > 0 if AN is done.
+ * Otherwise, it's 0, and we're
+ * still waiting for AN */
+ if (err > 0) {
+ phydev->state = PHY_RUNNING;
+ } else {
+ phydev->state = PHY_AN;
+ phydev->link_timeout = PHY_AN_TIMEOUT;
+ }
+ } else
+ phydev->state = PHY_RUNNING;
+ break;
+ }
+
+ spin_unlock(&phydev->lock);
+
+ if (needs_aneg)
+ err = phy_start_aneg(phydev);
+
+ if (err < 0)
+ phy_error(phydev);
+
+ mod_timer(&phydev->phy_timer, jiffies + PHY_STATE_TIME * HZ);
+}
+
+#endif /* CONFIG_PHYCONTROL */
diff -Nru a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/net/phy/phy_device.c 2005-03-08 19:12:45 -06:00
@@ -0,0 +1,735 @@
+/*
+ * drivers/net/phy/phy_device.c
+ *
+ * Framework for finding and configuring PHYs.
+ * Also contains generic PHY driver
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* get_phy_device
+ * bus: The bus the PHY is on
+ * addr: The address of the desired PHY device
+ *
+ * description: Reads the ID registers of the desired PHY,
+ * then allocates and returns the phy_device which
+ * represents it.
+ */
+struct phy_device * get_phy_device(struct mii_bus *bus, uint addr)
+{
+ int phy_reg;
+ u32 phy_id;
+ struct phy_device *dev = NULL;
+
+ errno = 0;
+
+ /* Grab the bits from PHYIR1, and put them
+ * in the upper half */
+ phy_reg = bus->read(bus, addr, MII_PHYSID1);
+
+ if (phy_reg < 0) {
+ errno = phy_reg;
+ return NULL;
+ }
+
+ phy_id = (phy_reg & 0xffff) << 16;
+
+ /* Grab the bits from PHYIR2, and put them in the lower half */
+ phy_reg = bus->read(bus, addr, MII_PHYSID2);
+
+ if (phy_reg < 0) {
+ errno = phy_reg;
+ return NULL;
+ }
+
+ phy_id |= (phy_reg & 0xffff);
+
+ /* If the phy_id is all Fs, there is no device there */
+ if (0xffffffff == phy_id)
+ return NULL;
+
+ /* Otherwise, we allocate the device, and initialize the
+ * default values */
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+
+ if (NULL == dev) {
+ errno = -ENOMEM;
+ return NULL;
+ }
+
+ memset(dev, 0, sizeof(*dev));
+
+ dev->speed = 0;
+ dev->duplex = -1;
+ dev->pause = dev->asym_pause = 0;
+ dev->link = 1;
+
+ dev->autoneg = AUTONEG_ENABLE;
+
+ dev->addr = addr;
+ dev->phy_id = phy_id;
+ dev->bus = bus;
+
+ dev->state = PHY_DOWN;
+
+ spin_lock_init(&dev->lock);
+
+ INIT_WORK(&dev->phy_queue, phy_change, dev);
+
+ return dev;
+}
+
+/* phy_prepare_link:
+ * phydev: The PHY device whose link is being prepped
+ * adjust_link: The link change handler for the controller
+ *
+ * description: Tells the PHY infrastructure to handle the
+ * gory details on monitoring link status (whether through
+ * polling or an interrupt), and to call back to the
+ * connected device driver when the link status changes.
+ * If you want to monitor your own link state, don't call
+ * this function */
+void phy_prepare_link(struct phy_device *phydev,
+ void (*handler)(struct device *))
+{
+ phydev->adjust_link = handler;
+}
+
+#ifdef CONFIG_PHYCONTROL
+/* phy_start_machine:
+ * phydev: The PHY device whose state machine is being started
+ * handler: The callback function for state change notifications.
+ *
+ * description: The PHY infrastructure can run a state machine
+ * which tracks whether the PHY is starting up, negotiating,
+ * etc. This function starts the timer which tracks the state
+ * of the PHY. If you want to be notified when the state
+ * changes, pass in the callback, otherwise, pass NULL. If you
+ * want to maintain your own state machine, do not call this
+ * function. */
+void phy_start_machine(struct phy_device *phydev,
+ void (*handler)(struct device *))
+{
+ phydev->adjust_state = handler;
+
+ init_timer(&phydev->phy_timer);
+ phydev->phy_timer.function = &phy_timer;
+ phydev->phy_timer.data = (unsigned long) phydev;
+ mod_timer(&phydev->phy_timer, jiffies + HZ);
+}
+
+/* phy_stop_machine
+ *
+ * description: Stops the state machine timer, sets the state to
+ * UP (unless it wasn't up yet), and then frees the interrupt,
+ * if it is in use. This function must be called BEFORE
+ * phy_detach.
+ */
+void phy_stop_machine(struct phy_device *phydev)
+{
+ del_timer_sync(&phydev->phy_timer);
+
+ spin_lock(&phydev->lock);
+ if (phydev->state > PHY_UP)
+ phydev->state = PHY_UP;
+ spin_unlock(&phydev->lock);
+
+ if (phydev->irq != -1) {
+ phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
+ phy_clear_interrupt(phydev);
+ free_irq(phydev->irq, phydev);
+ }
+
+ phydev->adjust_state = NULL;
+}
+
+
+/* phy_connect:
+ * dev: The requesting device
+ * phy_id: The name of the requested PHY device
+ * adjust_link: A callback function for handling link status
+ * changes
+ *
+ * description: Convenience function for connecting ethernet (or
+ * other) devices to PHY devices. The default behavior is for
+ * the PHY infrastructure to handle everything, and only notify
+ * the connected driver when the link status changes. If you
+ * don't want, or can't use the provided functionality, you may
+ * choose to call only the subset of functions which provide
+ * the desired functionality.
+ */
+struct phy_device * phy_connect(struct device *dev, const char *phy_id,
+ void (*handler)(struct device *))
+{
+ struct phy_device *phydev;
+
+ phydev = phy_attach(dev, phy_id);
+
+ if (NULL == phydev)
+ return phydev;
+
+ phy_prepare_link(phydev, handler);
+
+ phy_start_machine(phydev, NULL);
+
+ return phydev;
+}
+EXPORT_SYMBOL(phy_connect);
+
+void phy_disconnect(struct phy_device *phydev)
+{
+ phy_stop_machine(phydev);
+
+ phydev->adjust_link = NULL;
+
+ phy_detach(phydev);
+}
+EXPORT_SYMBOL(phy_disconnect);
+
+#endif /* CONFIG_PHYCONTROL */
+
+/* phy_attach:
+ * dev: The requesting device
+ * phy_id: The name of the requested PHY device
+ *
+ * description: Called by drivers to attach to a particular PHY
+ * device. The phy_device is found, and properly hooked up
+ * to the phy_driver. If no driver is attached, then the
+ * genphy_driver is used. The phy_device is given a ptr to
+ * the attaching device, and given a callback for link status
+ * change. The phy_device is returned to the attaching
+ * driver.
+ */
+struct phy_device *phy_attach(struct device *dev, const char *phy_id)
+{
+ struct phy_device *phydev = NULL;
+ struct bus_type *bus = &mdio_bus_type;
+ struct list_head *entry;
+
+ /* Search the list of PHY devices on the mdio bus for the
+ * PHY with the requested name */
+ list_for_each(entry, &bus->devices.list)
+ {
+ struct device *d = container_of(entry, struct device, bus_list);
+
+ if (!strcmp(phy_id, d->bus_id)) {
+ phydev = to_phy_device(d);
+ break;
+ }
+ }
+
+ if (NULL == phydev) {
+ printk(KERN_ERR "%s not found\n", phy_id);
+ errno = -ENODEV;
+ return NULL;
+ }
+
+ /* Assume that if there is no driver, that it doesn't
+ * exist, and we should use the genphy driver. */
+ if (NULL == phydev->dev.driver) {
+ int err;
+ down_write(&phydev->dev.bus->subsys.rwsem);
+ phydev->dev.driver = &genphy_driver.driver;
+
+ err = phydev->dev.driver->probe(&phydev->dev);
+
+ if (err < 0) {
+ errno = err;
+ return NULL;
+ }
+
+ device_bind_driver(&phydev->dev);
+ up_write(&phydev->dev.bus->subsys.rwsem);
+ }
+
+ if (phydev->attached_dev) {
+ printk(KERN_ERR "%s: %s already attached\n",
+ dev->bus_id, phy_id);
+ errno = -EBUSY;
+ return NULL;
+ }
+
+ phydev->attached_dev = dev;
+
+ return phydev;
+}
+EXPORT_SYMBOL(phy_attach);
+
+void phy_detach(struct phy_device *phydev)
+{
+ phydev->attached_dev = NULL;
+
+ /* If the device had no specific driver before (i.e. - it
+ * was using the generic driver), we unbind the device
+ * from the generic driver so that there's a chance a
+ * real driver could be loaded */
+ if (phydev->dev.driver == &genphy_driver.driver) {
+ down_write(&phydev->dev.bus->subsys.rwsem);
+ device_release_driver(&phydev->dev);
+ up_write(&phydev->dev.bus->subsys.rwsem);
+ }
+}
+EXPORT_SYMBOL(phy_detach);
+
+
+/* Generic PHY support and helper functions */
+
+/* genphy_config_advert
+ *
+ * description: Writes MII_ADVERTISE with the appropriate values,
+ * after sanitizing the values to make sure we only advertise
+ * what is supported
+ */
+int genphy_config_advert(struct phy_device *phydev)
+{
+ u32 advertise;
+ int adv;
+ int err;
+
+ /* Only allow advertising what
+ * this PHY supports */
+ phydev->advertising &= phydev->supported;
+ advertise = phydev->advertising;
+
+ /* Setup standard advertisement */
+ adv = phy_read(phydev, MII_ADVERTISE);
+
+ if (adv < 0)
+ return adv;
+
+ adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP |
+ ADVERTISE_PAUSE_ASYM);
+ if (advertise & ADVERTISED_10baseT_Half)
+ adv |= ADVERTISE_10HALF;
+ if (advertise & ADVERTISED_10baseT_Full)
+ adv |= ADVERTISE_10FULL;
+ if (advertise & ADVERTISED_100baseT_Half)
+ adv |= ADVERTISE_100HALF;
+ if (advertise & ADVERTISED_100baseT_Full)
+ adv |= ADVERTISE_100FULL;
+ if (advertise & ADVERTISED_Pause)
+ adv |= ADVERTISE_PAUSE_CAP;
+ if (advertise & ADVERTISED_Asym_Pause)
+ adv |= ADVERTISE_PAUSE_ASYM;
+
+ err = phy_write(phydev, MII_ADVERTISE, adv);
+
+ if (err < 0)
+ return err;
+
+ /* Configure gigabit if it's supported */
+ if (phydev->supported & (SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full)) {
+ adv = phy_read(phydev, MII_CTRL1000);
+
+ if (adv < 0)
+ return adv;
+
+ adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
+ if (advertise & SUPPORTED_1000baseT_Half)
+ adv |= ADVERTISE_1000HALF;
+ if (advertise & SUPPORTED_1000baseT_Full)
+ adv |= ADVERTISE_1000FULL;
+ err = phy_write(phydev, MII_CTRL1000, adv);
+
+ if (err < 0)
+ return err;
+ }
+
+ return adv;
+}
+
+
+/* genphy_setup_forced
+ *
+ * description: Configures MII_BMCR to force speed/duplex
+ * to the values in phydev. Assumes that the values are valid.
+ * Please see phy_sanitize_settings() */
+int genphy_setup_forced(struct phy_device *phydev)
+{
+ int ctl = phy_read(phydev, MII_BMCR);
+
+ if (ctl < 0)
+ return ctl;
+
+ phydev->pause = phydev->asym_pause = 0;
+
+ ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPEED1000|BMCR_ANENABLE);
+ ctl |= BMCR_RESET;
+
+ if (SPEED_1000 == phydev->speed)
+ ctl |= BMCR_SPEED1000;
+ else if (SPEED_100 == phydev->speed)
+ ctl |= BMCR_SPEED100;
+
+ if (DUPLEX_FULL == phydev->duplex)
+ ctl |= BMCR_FULLDPLX;
+
+ ctl = phy_write(phydev, MII_BMCR, ctl);
+
+ return ctl;
+}
+
+
+/* Enable and Restart Autonegotiation */
+int genphy_restart_aneg(struct phy_device *phydev)
+{
+ int ctl;
+
+ ctl = phy_read(phydev, MII_BMCR);
+
+ if (ctl < 0)
+ return ctl;
+
+ ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
+
+ ctl = phy_write(phydev, MII_BMCR, ctl);
+
+ return ctl;
+}
+
+
+/* genphy_config_aneg
+ *
+ * description: If auto-negotiation is enabled, we configure the
+ * advertising, and then restart auto-negotiation. If it is not
+ * enabled, then we write the BMCR
+ */
+int genphy_config_aneg(struct phy_device *phydev)
+{
+ int err = 0;
+
+ if (AUTONEG_ENABLE == phydev->autoneg) {
+ err = genphy_config_advert(phydev);
+
+ if (err < 0)
+ return err;
+
+ err = genphy_restart_aneg(phydev);
+ } else
+ err = genphy_setup_forced(phydev);
+
+ return err;
+}
+
+
+/* genphy_update_link
+ *
+ * description: Update the value in phydev->link to reflect the
+ * current link value. In order to do this, we need to read
+ * the status register twice, keeping the second value
+ */
+int genphy_update_link(struct phy_device *phydev)
+{
+ int status;
+
+ /* Do a fake read */
+ status = phy_read(phydev, MII_BMSR);
+
+ if (status < 0)
+ return status;
+
+ /* Read link and autonegotiation status */
+ status = phy_read(phydev, MII_BMSR);
+
+ if (status < 0)
+ return status;
+
+ if ((status & BMSR_LSTATUS) == 0)
+ phydev->link = 0;
+ else
+ phydev->link = 1;
+
+ return 0;
+}
+
+/* genphy_read_status
+ *
+ * description: Check the link, then figure out the current state
+ * by comparing what we advertise with what the link partner
+ * advertises. Start by checking the gigabit possibilities,
+ * then move on to 10/100.
+ */
+int genphy_read_status(struct phy_device *phydev)
+{
+ int adv;
+ int err;
+ int lpa;
+ int lpagb = 0;
+
+ /* Update the link, but return if there
+ * was an error */
+ err = genphy_update_link(phydev);
+ if (err)
+ return err;
+
+ if (AUTONEG_ENABLE == phydev->autoneg) {
+ if (phydev->supported & (SUPPORTED_1000baseT_Half
+ | SUPPORTED_1000baseT_Full)) {
+ lpagb = phy_read(phydev, MII_STAT1000);
+
+ if (lpagb < 0)
+ return lpagb;
+
+ adv = phy_read(phydev, MII_CTRL1000);
+
+ if (adv < 0)
+ return adv;
+
+ lpagb &= adv << 2;
+ }
+
+ lpa = phy_read(phydev, MII_LPA);
+
+ if (lpa < 0)
+ return lpa;
+
+ adv = phy_read(phydev, MII_ADVERTISE);
+
+ if (adv < 0)
+ return adv;
+
+ lpa &= adv;
+
+ phydev->speed = SPEED_10;
+ phydev->duplex = DUPLEX_HALF;
+ phydev->pause = phydev->asym_pause = 0;
+
+ if (lpagb & (LPA_1000FULL | LPA_1000HALF)) {
+ phydev->speed = SPEED_1000;
+
+ if (lpagb & LPA_1000FULL)
+ phydev->duplex = DUPLEX_FULL;
+ } else if (lpa & (LPA_100FULL | LPA_100HALF)) {
+ phydev->speed = SPEED_100;
+
+ if (lpa & LPA_100FULL)
+ phydev->duplex = DUPLEX_FULL;
+ } else
+ if (lpa & LPA_10FULL)
+ phydev->duplex = DUPLEX_FULL;
+
+ if (phydev->duplex == DUPLEX_FULL){
+ phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
+ phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
+ }
+ } else {
+ int bmcr = phy_read(phydev, MII_BMCR);
+ if (bmcr < 0)
+ return bmcr;
+
+ if (bmcr & BMCR_FULLDPLX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ if (bmcr & BMCR_SPEED1000)
+ phydev->speed = SPEED_1000;
+ else if (bmcr & BMCR_SPEED100)
+ phydev->speed = SPEED_100;
+ else
+ phydev->speed = SPEED_10;
+
+ phydev->pause = phydev->asym_pause = 0;
+ }
+
+ return 0;
+}
+
+
+static int genphy_probe(struct phy_device *phydev)
+{
+ u32 val;
+ u32 features;
+
+ /* For now, I'll claim that the generic driver supports
+ * all possible port types */
+ features = (SUPPORTED_TP | SUPPORTED_MII
+ | SUPPORTED_AUI | SUPPORTED_FIBRE |
+ SUPPORTED_BNC);
+
+ /* Do we support autonegotiation? */
+ val = phy_read(phydev, MII_BMSR);
+
+ if (val < 0)
+ return val;
+
+ if (val & BMSR_ANEGCAPABLE)
+ features |= SUPPORTED_Autoneg;
+
+ if (val & BMSR_100FULL)
+ features |= SUPPORTED_100baseT_Full;
+ if (val & BMSR_100HALF)
+ features |= SUPPORTED_100baseT_Half;
+ if (val & BMSR_10FULL)
+ features |= SUPPORTED_10baseT_Full;
+ if (val & BMSR_10HALF)
+ features |= SUPPORTED_10baseT_Half;
+
+ if (val & BMSR_ESTATEN) {
+ val = phy_read(phydev, MII_ESTATUS);
+
+ if (val < 0)
+ return val;
+
+ if (val & ESTATUS_1000_TFULL)
+ features |= SUPPORTED_1000baseT_Full;
+ if (val & ESTATUS_1000_THALF)
+ features |= SUPPORTED_1000baseT_Half;
+ }
+
+ phydev->supported = features;
+ phydev->advertising = features;
+
+ return 0;
+}
+
+
+/* phy_probe
+ * dev: The device belonging to a PHY device
+ *
+ * description: Take care of setting up the phy_device structure,
+ * set the state to READY (the driver's probe function should
+ * set it to STARTING if needed).
+ */
+int phy_probe(struct device *dev)
+{
+ struct phy_device *phydev;
+ struct phy_driver *phydrv;
+ struct device_driver *drv;
+ int err = 0;
+
+ phydev = to_phy_device(dev);
+
+ /* Make sure the driver is held.
+ * XXX -- Is this correct? */
+ drv = get_driver(phydev->dev.driver);
+ phydrv = to_phy_driver(drv);
+ phydev->drv = phydrv;
+
+ /* Disable the interrupt if the PHY doesn't support it */
+ if (!(phydrv->flags & PHY_HAS_INTERRUPT))
+ phydev->irq = -1;
+
+ spin_lock(&phydev->lock);
+
+ /* Start out supporting everything. Eventually,
+ * a controller will attach, and may modify one
+ * or both of these values */
+ phydev->supported = phydrv->features;
+ phydev->advertising = phydrv->features;
+
+ /* Set the state to READY by default */
+ phydev->state = PHY_READY;
+
+ if (phydev->drv->probe)
+ err = phydev->drv->probe(phydev);
+
+ spin_unlock(&phydev->lock);
+
+ return err;
+}
+
+int phy_remove(struct device *dev)
+{
+ struct phy_device *phydev;
+
+ phydev = to_phy_device(dev);
+
+ spin_lock(&phydev->lock);
+ phydev->state = PHY_DOWN;
+ spin_unlock(&phydev->lock);
+
+ if (phydev->drv->remove)
+ phydev->drv->remove(phydev);
+
+ put_driver(phydev->dev.driver);
+ phydev->drv = NULL;
+
+ return 0;
+}
+
+int phy_driver_register(struct phy_driver *new_driver)
+{
+ int retval;
+
+ memset(&new_driver->driver, 0, sizeof(new_driver->driver));
+ new_driver->driver.name = new_driver->name;
+ new_driver->driver.bus = &mdio_bus_type;
+ new_driver->driver.probe = phy_probe;
+ new_driver->driver.remove = phy_remove;
+
+ retval = driver_register(&new_driver->driver);
+
+ if (!retval)
+ pr_info("%s: Registered new driver\n", new_driver->name);
+ else
+ printk(KERN_ERR "%s: Error %d in registering driver\n",
+ new_driver->name, retval);
+
+ return retval;
+}
+
+void phy_driver_unregister(struct phy_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+
+struct phy_driver genphy_driver = {
+ .phy_id = 0xffffffff,
+ .phy_id_mask = 0xffffffff,
+ .name = "Generic PHY",
+ .probe = genphy_probe,
+ .features = 0,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+};
+
+static int __init genphy_init(void)
+{
+ int retval;
+
+ retval = phy_driver_register(&genphy_driver);
+
+ return retval;
+}
+
+static void __exit genphy_exit(void)
+{
+ phy_driver_unregister(&genphy_driver);
+}
+
+module_init(genphy_init);
+module_exit(genphy_exit);
diff -Nru a/drivers/net/phy/qsemi.c b/drivers/net/phy/qsemi.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/net/phy/qsemi.c 2005-03-08 19:12:45 -06:00
@@ -0,0 +1,143 @@
+/*
+ * drivers/net/phy/qsemi.c
+ *
+ * Driver for Quality Semiconductor PHYs
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* ------------------------------------------------------------------------- */
+/* The Quality Semiconductor QS6612 is used on the RPX CLLF */
+
+/* register definitions */
+
+#define MII_QS6612_MCR 17 /* Mode Control Register */
+#define MII_QS6612_FTR 27 /* Factory Test Register */
+#define MII_QS6612_MCO 28 /* Misc. Control Register */
+#define MII_QS6612_ISR 29 /* Interrupt Source Register */
+#define MII_QS6612_IMR 30 /* Interrupt Mask Register */
+#define MII_QS6612_IMR_INIT 0x003a
+#define MII_QS6612_PCR 31 /* 100BaseTx PHY Control Reg. */
+
+#define QS6612_PCR_AN_COMPLETE 0x1000
+#define QS6612_PCR_RLBEN 0x0200
+#define QS6612_PCR_DCREN 0x0100
+#define QS6612_PCR_4B5BEN 0x0040
+#define QS6612_PCR_TX_ISOLATE 0x0020
+#define QS6612_PCR_MLT3_DIS 0x0002
+#define QS6612_PCR_SCRM_DESCRM 0x0001
+
+
+/* Returns 0, unless there's a write error */
+int qs6612_probe(struct phy_device *phydev)
+{
+ /* The PHY powers up isolated on the RPX,
+ * so send a command to allow operation.
+ * XXX - My docs indicate this should be 0x0940
+ * ...or something. The current value sets three
+ * reserved bits, bit 11, which specifies it should be
+ * set to one, bit 10, which specifies it should be set
+ * to 0, and bit 7, which doesn't specify. However, my
+ * docs are preliminary, and I will leave it like this
+ * until someone more knowledgable corrects me or it.
+ * -- Andy Fleming
+ */
+ return phy_write(phydev, MII_QS6612_PCR, 0x0dc0);
+}
+
+int qs6612_ack_interrupt(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_read(phydev, MII_QS6612_ISR);
+
+ if (err < 0)
+ return err;
+
+ err = phy_read(phydev, MII_BMSR);
+
+ if (err < 0)
+ return err;
+
+ err = phy_read(phydev, MII_EXPANSION);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+int qs6612_config_intr(struct phy_device *phydev)
+{
+ int err;
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, MII_QS6612_IMR,
+ MII_QS6612_IMR_INIT);
+ else
+ err = phy_write(phydev, MII_QS6612_IMR, 0);
+
+ return err;
+
+}
+
+static struct phy_driver qs6612_driver = {
+ .phy_id = 0x00181440,
+ .name = "QS6612",
+ .phy_id_mask = 0xfffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .probe = qs6612_probe,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = qs6612_ack_interrupt,
+ .config_intr = qs6612_config_intr,
+};
+
+int __init qs6612_init(void)
+{
+ int retval;
+
+ retval = phy_driver_register(&qs6612_driver);
+
+ return retval;
+}
+
+static void __exit qs6612_exit(void)
+{
+ phy_driver_unregister(&qs6612_driver);
+}
+
+module_init(qs6612_init);
+module_exit(qs6612_exit);
diff -Nru a/include/linux/ethtool.h b/include/linux/ethtool.h
--- a/include/linux/ethtool.h 2005-03-08 19:12:44 -06:00
+++ b/include/linux/ethtool.h 2005-03-08 19:12:44 -06:00
@@ -389,6 +389,7 @@
#define ETHTOOL_GTSO 0x0000001e /* Get TSO enable (ethtool_value) */
#define ETHTOOL_STSO 0x0000001f /* Set TSO enable (ethtool_value) */
+
/* compatibility with older code */
#define SPARC_ETH_GSET ETHTOOL_GSET
#define SPARC_ETH_SSET ETHTOOL_SSET
@@ -407,6 +408,8 @@
#define SUPPORTED_FIBRE (1 << 10)
#define SUPPORTED_BNC (1 << 11)
#define SUPPORTED_10000baseT_Full (1 << 12)
+#define SUPPORTED_Pause (1 << 13)
+#define SUPPORTED_Asym_Pause (1 << 14)
/* Indicates what features are advertised by the interface. */
#define ADVERTISED_10baseT_Half (1 << 0)
@@ -422,6 +425,8 @@
#define ADVERTISED_FIBRE (1 << 10)
#define ADVERTISED_BNC (1 << 11)
#define ADVERTISED_10000baseT_Full (1 << 12)
+#define ADVERTISED_Pause (1 << 13)
+#define ADVERTISED_Asym_Pause (1 << 14)
/* The following are all involved in forcing a particular link
* mode for the device for setting things. When getting the
diff -Nru a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
--- a/include/linux/fsl_devices.h 2005-03-08 19:12:45 -06:00
+++ b/include/linux/fsl_devices.h 2005-03-08 19:12:45 -06:00
@@ -47,13 +47,19 @@
struct gianfar_platform_data {
/* device specific information */
u32 device_flags;
- u32 phy_reg_addr;
/* board specific information */
u32 board_flags;
- u32 phyid;
- u32 interruptPHY;
+ const char *bus_id;
u8 mac_addr[6];
+};
+
+struct gianfar_mdio_data {
+ /* device specific information */
+ u32 paddr;
+
+ /* board specific information */
+ int irq[32];
};
/* Flags related to gianfar device features */
diff -Nru a/include/linux/mii.h b/include/linux/mii.h
--- a/include/linux/mii.h 2005-03-08 19:12:44 -06:00
+++ b/include/linux/mii.h 2005-03-08 19:12:44 -06:00
@@ -22,6 +22,7 @@
#define MII_EXPANSION 0x06 /* Expansion register */
#define MII_CTRL1000 0x09 /* 1000BASE-T control */
#define MII_STAT1000 0x0a /* 1000BASE-T status */
+#define MII_ESTATUS 0x0f /* Extended Status */
#define MII_DCOUNTER 0x12 /* Disconnect counter */
#define MII_FCSCOUNTER 0x13 /* False carrier counter */
#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */
@@ -54,7 +55,10 @@
#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */
#define BMSR_RFAULT 0x0010 /* Remote fault detected */
#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */
-#define BMSR_RESV 0x07c0 /* Unused... */
+#define BMSR_RESV 0x00c0 /* Unused... */
+#define BMSR_ESTATEN 0x0100 /* Extended Status in R15 */
+#define BMSR_100FULL2 0x0200 /* Can do 100BASE-T2 HDX */
+#define BMSR_100HALF2 0x0400 /* Can do 100BASE-T2 FDX */
#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */
#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */
#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */
@@ -105,6 +109,9 @@
#define EXPANSION_NPCAPABLE 0x0008 /* Link partner supports npage */
#define EXPANSION_MFAULTS 0x0010 /* Multiple faults detected */
#define EXPANSION_RESV 0xffe0 /* Unused... */
+
+#define ESTATUS_1000_TFULL 0x2000 /* Can do 1000BT Full */
+#define ESTATUS_1000_THALF 0x1000 /* Can do 1000BT Half */
/* N-way test register. */
#define NWAYTEST_RESV1 0x00ff /* Unused... */
diff -Nru a/include/linux/phy.h b/include/linux/phy.h
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/include/linux/phy.h 2005-03-08 19:12:45 -06:00
@@ -0,0 +1,352 @@
+/*
+ * include/linux/phy.h
+ *
+ * Framework and drivers for configuring and reading different PHYs
+ * Based on code in sungem_phy.c and gianfar_phy.c
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __PHY_H
+#define __PHY_H
+
+#include <linux/spinlock.h>
+#include <linux/device.h>
+
+#define PHY_BASIC_FEATURES (SUPPORTED_10baseT_Half | \
+ SUPPORTED_10baseT_Full | \
+ SUPPORTED_100baseT_Half | \
+ SUPPORTED_100baseT_Full | \
+ SUPPORTED_Autoneg | \
+ SUPPORTED_TP | \
+ SUPPORTED_MII)
+
+#define PHY_GBIT_FEATURES (PHY_BASIC_FEATURES | \
+ SUPPORTED_1000baseT_Half | \
+ SUPPORTED_1000baseT_Full)
+
+#define PHY_HAS_INTERRUPT 0x00000001
+#define PHY_HAS_MAGICANEG 0x00000002
+
+#define MII_BUS_MAX 4
+
+
+#define PHY_INIT_TIMEOUT 100000
+#define PHY_STATE_TIME 1
+#define PHY_FORCE_TIMEOUT 10
+#define PHY_AN_TIMEOUT 10
+
+#define PHY_MAX_ADDR 32
+
+/* The Bus class for PHYs. Devices which provide access to
+ * PHYs should register using this structure */
+struct mii_bus {
+ const char *name;
+ int id;
+ void *priv;
+ int (*read)(struct mii_bus *bus, int phy_id, int regnum);
+ int (*write)(struct mii_bus *bus, int phy_id, int regnum, u16 val);
+ int (*reset)(struct mii_bus *bus);
+
+ /* A lock to ensure that only one thing can read/write
+ * the MDIO bus at a time */
+ spinlock_t mdio_lock;
+
+ struct device *dev;
+
+ /* list of all PHYs on bus */
+ struct phy_device *phy_map[PHY_MAX_ADDR];
+
+ /* Pointer to an array of interrupts, each PHY's
+ * interrupt at the index matching its address */
+ int *irq;
+};
+
+#define PHY_INTERRUPT_DISABLED 0x0
+#define PHY_INTERRUPT_ENABLED 0x80000000
+
+/* PHY state machine states:
+ *
+ * DOWN: PHY device and driver are not ready for anything. probe
+ * should be called if and only if the PHY is in this state,
+ * given that the PHY device exists.
+ * - PHY driver probe function will, depending on the PHY, set
+ * the state to STARTING or READY
+ *
+ * STARTING: PHY device is coming up, and the ethernet driver is
+ * not ready. PHY drivers may set this in the probe function.
+ * If they do, they are responsible for making sure the state is
+ * eventually set to indicate whether the PHY is UP or READY,
+ * depending on the state when the PHY is done starting up.
+ * - PHY driver will set the state to READY
+ * - start will set the state to PENDING
+ *
+ * READY: PHY is ready to send and receive packets, but the
+ * controller is not. By default, PHYs which do not implement
+ * probe will be set to this state by phy_probe(). If the PHY
+ * driver knows the PHY is ready, and the PHY state is STARTING,
+ * then it sets this STATE.
+ * - start will set the state to UP
+ *
+ * PENDING: PHY device is coming up, but the ethernet driver is
+ * ready. phy_start will set this state if the PHY state is
+ * STARTING.
+ * - PHY driver will set the state to UP when the PHY is ready
+ *
+ * UP: The PHY and attached device are ready to do work.
+ * Interrupts should be started here.
+ * - timer moves to AN
+ *
+ * AN: The PHY is currently negotiating the link state. Link is
+ * therefore down for now. phy_timer will set this state when it
+ * detects the state is UP. config_aneg will set this state
+ * whenever called with phydev->autoneg set to AUTONEG_ENABLE.
+ * - If autonegotiation finishes, but there's no link, it sets
+ * the state to NOLINK.
+ * - If aneg finishes with link, it sets the state to RUNNING,
+ * and calls adjust_link
+ * - If autonegotiation did not finish after an arbitrary amount
+ * of time, autonegotiation should be tried again if the PHY
+ * supports "magic" autonegotiation (back to AN)
+ * - If it didn't finish, and no magic_aneg, move to FORCING.
+ *
+ * NOLINK: PHY is up, but not currently plugged in.
+ * - If the timer notes that the link comes back, we move to RUNNING
+ * - config_aneg moves to AN
+ * - phy_stop moves to HALTED
+ *
+ * FORCING: PHY is being configured with forced settings
+ * - if link is up, move to RUNNING
+ * - If link is down, we drop to the next highest setting, and
+ * retry (FORCING) after a timeout
+ * - phy_stop moves to HALTED
+ *
+ * RUNNING: PHY is currently up, running, and possibly sending
+ * and/or receiving packets
+ * - timer will set CHANGELINK if we're polling (this ensures the
+ * link state is polled every other cycle of this state machine,
+ * which makes it every other second)
+ * - irq will set CHANGELINK
+ * - config_aneg will set AN
+ * - phy_stop moves to HALTED
+ *
+ * CHANGELINK: PHY experienced a change in link state
+ * - timer moves to RUNNING if link
+ * - timer moves to NOLINK if the link is down
+ * - phy_stop moves to HALTED
+ *
+ * HALTED: PHY is up, but no polling or interrupts are done. Or
+ * PHY is in an error state.
+ *
+ * - phy_start moves to RESUMING
+ *
+ * RESUMING: PHY was halted, but now wants to run again.
+ * - If we are forcing, or aneg is done, timer moves to RUNNING
+ * - If aneg is not done, timer moves to AN
+ * - phy_stop moves to HALTED
+ */
+enum phy_state {
+ PHY_DOWN=0,
+ PHY_STARTING,
+ PHY_READY,
+ PHY_PENDING,
+ PHY_UP,
+ PHY_AN,
+ PHY_RUNNING,
+ PHY_NOLINK,
+ PHY_FORCING,
+ PHY_CHANGELINK,
+ PHY_HALTED,
+ PHY_RESUMING
+};
+
+/* phy_device: An instance of a PHY
+ *
+ * drv: Pointer to the driver for this PHY instance
+ * bus: Pointer to the bus this PHY is on
+ * dev: driver model device structure for this PHY
+ * phy_id: UID for this device found during discovery
+ * state: state of the PHY for management purposes
+ * addr: Bus address of PHY
+ * link_timeout: The number of timer firings to wait before the
+ * giving up on the current attempt at acquiring a link
+ * irq: IRQ number of the PHY's interrupt (-1 if none)
+ * phy_timer: The timer for handling the state machine
+ * phy_queue: A work_queue for the interrupt
+ * attached_dev: The attached enet driver's device instance ptr
+ * adjust_link: Callback for the enet controller to respond to
+ * changes in the link state.
+ * adjust_state: Callback for the enet driver to respond to
+ * changes in the state machine.
+ *
+ * speed, duplex, pause, supported, advertising, and
+ * autoneg are used like in mii_if_info
+ *
+ * interrupts currently only supports enabled or disabled,
+ * but could be changed in the future to support enabling
+ * and disabling specific interrupts
+ *
+ * Contains some infrastructure for polling and interrupt
+ * handling, as well as handling shifts in PHY hardware state
+ */
+struct phy_device {
+ /* Information about the PHY type */
+ /* And management functions */
+ struct phy_driver *drv;
+
+ struct mii_bus *bus;
+
+ struct device dev;
+
+ u32 phy_id;
+
+ enum phy_state state;
+
+ /* Bus address of the PHY (0-32) */
+ int addr;
+
+ /* forced speed & duplex (no autoneg)
+ * partner speed & duplex & pause (autoneg)
+ */
+ int speed;
+ int duplex;
+ int pause;
+ int asym_pause;
+
+ /* The most recently read link state */
+ int link;
+
+ /* Enabled Interrupts */
+ u32 interrupts;
+
+ /* Union of PHY and Attached devices' supported modes */
+ /* See mii.h for more info */
+ u32 supported;
+ u32 advertising;
+
+ int autoneg;
+
+ int link_timeout;
+
+ /* Interrupt number for this PHY
+ * -1 means no interrupt */
+ int irq;
+
+ /* private data pointer */
+ /* For use by PHYs to maintain extra state */
+ void *priv;
+
+ /* Interrupt and Polling infrastructure */
+ struct work_struct phy_queue;
+ struct timer_list phy_timer;
+
+ spinlock_t lock;
+
+ struct device *attached_dev;
+
+ void (*adjust_link)(struct device *dev);
+
+ void (*adjust_state)(struct device *dev);
+};
+#define to_phy_device(d) container_of(d, struct phy_device, dev)
+
+/* struct phy_driver: Driver structure for a particular PHY type
+ *
+ * phy_id: The result of reading the UID registers of this PHY
+ * type, and ANDing them with the phy_id_mask. This driver
+ * only works for PHYs with IDs which match this field
+ * name: The friendly name of this PHY type
+ * phy_id_mask: Defines the important bits of the phy_id
+ * features: A list of features (speed, duplex, etc) supported
+ * by this PHY
+ * flags: A bitfield defining certain other features this PHY
+ * supports (like interrupts)
+ *
+ * The drivers must implement config_aneg and read_status. All
+ * other functions are optional. Note that none of these
+ * functions should be called from interrupt time. The goal is
+ * for the bus read/write functions to be able to block when the
+ * bus transaction is happening, and be freed up by an interrupt
+ * (The MPC85xx has this ability, though it is not currently
+ * supported in the driver).
+ */
+struct phy_driver {
+ u32 phy_id;
+ char *name;
+ unsigned int phy_id_mask;
+ u32 features;
+ u32 flags;
+
+ /* Called to initialize the PHY */
+ int (*probe)(struct phy_device *phydev);
+
+ /* PHY Power Management */
+ int (*suspend)(struct phy_device *phydev);
+ int (*resume)(struct phy_device *phydev);
+
+ /* Configures the advertisement and resets
+ * autonegotiation if phydev->autoneg is on,
+ * forces the speed to the current settings in phydev
+ * if phydev->autoneg is off */
+ int (*config_aneg)(struct phy_device *phydev);
+
+ /* Determines the negotiated speed and duplex */
+ int (*read_status)(struct phy_device *phydev);
+
+ /* Clears any pending interrupts */
+ int (*ack_interrupt)(struct phy_device *phydev);
+
+ /* Enables or disables interrupts */
+ int (*config_intr)(struct phy_device *phydev);
+
+ /* Clears up any memory if needed */
+ void (*remove)(struct phy_device *phydev);
+
+ struct device_driver driver;
+};
+#define to_phy_driver(d) container_of(d, struct phy_driver, driver)
+
+int phy_read(struct phy_device *phydev, u16 regnum);
+int phy_write(struct phy_device *phydev, u16 regnum, u16 val);
+struct phy_device* get_phy_device(struct mii_bus *bus, uint addr);
+int phy_clear_interrupt(struct phy_device *phydev);
+int phy_config_interrupt(struct phy_device *phydev, u32 interrupts);
+struct phy_device * phy_attach(struct device *dev, const char *phy_id);
+struct phy_device * phy_connect(struct device *dev, const char *phy_id,
+ void (*handler)(struct device *));
+void phy_disconnect(struct phy_device *phydev);
+void phy_detach(struct phy_device *phydev);
+void phy_start(struct phy_device *phydev);
+void phy_stop(struct phy_device *phydev);
+int phy_start_aneg(struct phy_device *phydev);
+int register_mdiobus(struct mii_bus *bus);
+void phy_change(void *data);
+void phy_timer(unsigned long data);
+void phy_sanitize_settings(struct phy_device *phydev);
+
+int genphy_config_advert(struct phy_device *phydev);
+int genphy_setup_forced(struct phy_device *phydev);
+int genphy_restart_aneg(struct phy_device *phydev);
+int gbit_config_aneg(struct phy_device *phydev);
+int genphy_config_aneg(struct phy_device *phydev);
+int genphy_update_link(struct phy_device *phydev);
+int genphy_read_status(struct phy_device *phydev);
+void phy_driver_unregister(struct phy_driver *drv);
+int phy_driver_register(struct phy_driver *new_driver);
+void phy_prepare_link(struct phy_device *phydev,
+ void (*adjust_link)(struct device *));
+void phy_start_machine(struct phy_device *phydev,
+ void (*handler)(struct device *));
+void phy_stop_machine(struct phy_device *phydev);
+
+extern struct bus_type mdio_bus_type;
+extern struct phy_driver genphy_driver;
+#endif /* __PHY_H */
[-- Attachment #3: Type: text/plain, Size: 60 bytes --]
Andy Fleming
Open Source Team
Freescale Semiconductor, Inc
^ permalink raw reply
* Re: [RFC][PATCH] combining header files
From: Alan Modra @ 2005-03-09 1:19 UTC (permalink / raw)
To: Stephen Rothwell; +Cc: ppc64-dev, ppc-dev
In-Reply-To: <20050309120343.0c22eb0f.sfr@canb.auug.org.au>
On Wed, Mar 09, 2005 at 12:03:43PM +1100, Stephen Rothwell wrote:
> I would just like to start a discussion about consolidating (some of) the
> ppc and ppc64 header files.
Marvellous! In case it isn't completely obvious, you can often share
structure definitions between ppc32 and ppc64 by judicious selection of
types. eg.
struct stays_the_same {
long long some_64bit_var;
int some_32bit_var;
}
struct bigger_in_64bit {
long var_sized_by_arch;
}
--
Alan Modra
IBM OzLabs - Linux Technology Centre
^ permalink raw reply
* [RFC][PATCH] combining header files
From: Stephen Rothwell @ 2005-03-09 1:03 UTC (permalink / raw)
To: ppc64-dev; +Cc: ppc-dev
Hi all,
I would just like to start a discussion about consolidating (some of) the
ppc and ppc64 header files. As a starting point (am I am not saying that
this is the right way to go) the following patch replaces (semantically)
equivalent ppc64 headers files by just including the asm-ppc file.
We *could* use this method to make the journey incremental until there
are no nontrivial files left in asm-ppc64 ....
Diffstat looks like:
asm-ppc/ipc.h | 2
asm-ppc64/ioctl.h | 75 ---------------
asm-ppc64/ioctls.h | 115 ------------------------
asm-ppc64/ipc.h | 35 -------
asm-ppc64/mman.h | 53 -----------
asm-ppc64/param.h | 30 ------
asm-ppc64/parport.h | 19 ----
asm-ppc64/poll.h | 33 ------
asm-ppc64/string.h | 36 -------
asm-ppc64/termbits.h | 194 -----------------------------------------
asm-ppc64/termios.h | 236 --------------------------------------------------
asm-ppc64/unaligned.h | 22 ----
12 files changed, 12 insertions(+), 838 deletions(-)
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
diff -ruNp linus/include/asm-ppc/ipc.h linus-headers/include/asm-ppc/ipc.h
--- linus/include/asm-ppc/ipc.h 2003-09-24 10:56:02.000000000 +1000
+++ linus-headers/include/asm-ppc/ipc.h 2005-03-09 11:54:36.000000000 +1100
@@ -4,7 +4,7 @@
/*
* These are used to wrap system calls on PowerPC.
*
- * See arch/ppc/kernel/syscalls.c for ugly details..
+ * See arch/ppc{,64}/kernel/syscalls.c for ugly details..
*/
struct ipc_kludge {
struct msgbuf __user *msgp;
diff -ruNp linus/include/asm-ppc64/ioctl.h linus-headers/include/asm-ppc64/ioctl.h
--- linus/include/asm-ppc64/ioctl.h 2003-12-31 09:39:13.000000000 +1100
+++ linus-headers/include/asm-ppc64/ioctl.h 2005-03-09 01:10:54.000000000 +1100
@@ -1,74 +1 @@
-#ifndef _PPC64_IOCTL_H
-#define _PPC64_IOCTL_H
-
-
-/*
- * This was copied from the alpha as it's a bit cleaner there.
- * -- Cort
- *
- * 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.
- */
-
-#define _IOC_NRBITS 8
-#define _IOC_TYPEBITS 8
-#define _IOC_SIZEBITS 13
-#define _IOC_DIRBITS 3
-
-#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)
-#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)
-#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)
-#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)
-
-#define _IOC_NRSHIFT 0
-#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
-#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)
-#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)
-
-/*
- * Direction bits _IOC_NONE could be 0, but OSF/1 gives it a bit.
- * And this turns out useful to catch old ioctl numbers in header
- * files for us.
- */
-#define _IOC_NONE 1U
-#define _IOC_READ 2U
-#define _IOC_WRITE 4U
-
-#define _IOC(dir,type,nr,size) \
- (((dir) << _IOC_DIRSHIFT) | \
- ((type) << _IOC_TYPESHIFT) | \
- ((nr) << _IOC_NRSHIFT) | \
- ((size) << _IOC_SIZESHIFT))
-
-/* provoke compile error for invalid uses of size argument */
-extern unsigned int __invalid_size_argument_for_IOC;
-#define _IOC_TYPECHECK(t) \
- ((sizeof(t) == sizeof(t[1]) && \
- sizeof(t) < (1 << _IOC_SIZEBITS)) ? \
- sizeof(t) : __invalid_size_argument_for_IOC)
-
-/* used to create numbers */
-#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
-#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
-#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
-#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
-#define _IOR_BAD(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
-#define _IOW_BAD(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
-#define _IOWR_BAD(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
-
-/* used to decode them.. */
-#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
-#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
-#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
-#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
-
-/* various drivers, such as the pcmcia stuff, need these... */
-#define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT)
-#define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT)
-#define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
-#define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT)
-#define IOCSIZE_SHIFT (_IOC_SIZESHIFT)
-
-#endif /* _PPC64_IOCTL_H */
+#include <asm-ppc/ioctl.h>
diff -ruNp linus/include/asm-ppc64/ioctls.h linus-headers/include/asm-ppc64/ioctls.h
--- linus/include/asm-ppc64/ioctls.h 2003-04-03 08:55:29.000000000 +1000
+++ linus-headers/include/asm-ppc64/ioctls.h 2005-03-09 01:13:05.000000000 +1100
@@ -1,114 +1 @@
-#ifndef _ASM_PPC64_IOCTLS_H
-#define _ASM_PPC64_IOCTLS_H
-
-/*
- * 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.
- */
-
-#include <asm/ioctl.h>
-
-#define FIOCLEX _IO('f', 1)
-#define FIONCLEX _IO('f', 2)
-#define FIOASYNC _IOW('f', 125, int)
-#define FIONBIO _IOW('f', 126, int)
-#define FIONREAD _IOR('f', 127, int)
-#define TIOCINQ FIONREAD
-#define FIOQSIZE _IOR('f', 128, loff_t)
-
-#define TIOCGETP _IOR('t', 8, struct sgttyb)
-#define TIOCSETP _IOW('t', 9, struct sgttyb)
-#define TIOCSETN _IOW('t', 10, struct sgttyb) /* TIOCSETP wo flush */
-
-#define TIOCSETC _IOW('t', 17, struct tchars)
-#define TIOCGETC _IOR('t', 18, struct tchars)
-#define TCGETS _IOR('t', 19, struct termios)
-#define TCSETS _IOW('t', 20, struct termios)
-#define TCSETSW _IOW('t', 21, struct termios)
-#define TCSETSF _IOW('t', 22, struct termios)
-
-#define TCGETA _IOR('t', 23, struct termio)
-#define TCSETA _IOW('t', 24, struct termio)
-#define TCSETAW _IOW('t', 25, struct termio)
-#define TCSETAF _IOW('t', 28, struct termio)
-
-#define TCSBRK _IO('t', 29)
-#define TCXONC _IO('t', 30)
-#define TCFLSH _IO('t', 31)
-
-#define TIOCSWINSZ _IOW('t', 103, struct winsize)
-#define TIOCGWINSZ _IOR('t', 104, struct winsize)
-#define TIOCSTART _IO('t', 110) /* start output, like ^Q */
-#define TIOCSTOP _IO('t', 111) /* stop output, like ^S */
-#define TIOCOUTQ _IOR('t', 115, int) /* output queue size */
-
-#define TIOCGLTC _IOR('t', 116, struct ltchars)
-#define TIOCSLTC _IOW('t', 117, struct ltchars)
-#define TIOCSPGRP _IOW('t', 118, int)
-#define TIOCGPGRP _IOR('t', 119, int)
-
-#define TIOCEXCL 0x540C
-#define TIOCNXCL 0x540D
-#define TIOCSCTTY 0x540E
-
-#define TIOCSTI 0x5412
-#define TIOCMGET 0x5415
-#define TIOCMBIS 0x5416
-#define TIOCMBIC 0x5417
-#define TIOCMSET 0x5418
-# define TIOCM_LE 0x001
-# define TIOCM_DTR 0x002
-# define TIOCM_RTS 0x004
-# define TIOCM_ST 0x008
-# define TIOCM_SR 0x010
-# define TIOCM_CTS 0x020
-# define TIOCM_CAR 0x040
-# define TIOCM_RNG 0x080
-# define TIOCM_DSR 0x100
-# define TIOCM_CD TIOCM_CAR
-# define TIOCM_RI TIOCM_RNG
-
-#define TIOCGSOFTCAR 0x5419
-#define TIOCSSOFTCAR 0x541A
-#define TIOCLINUX 0x541C
-#define TIOCCONS 0x541D
-#define TIOCGSERIAL 0x541E
-#define TIOCSSERIAL 0x541F
-#define TIOCPKT 0x5420
-# define TIOCPKT_DATA 0
-# define TIOCPKT_FLUSHREAD 1
-# define TIOCPKT_FLUSHWRITE 2
-# define TIOCPKT_STOP 4
-# define TIOCPKT_START 8
-# define TIOCPKT_NOSTOP 16
-# define TIOCPKT_DOSTOP 32
-
-
-#define TIOCNOTTY 0x5422
-#define TIOCSETD 0x5423
-#define TIOCGETD 0x5424
-#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */
-#define TIOCSBRK 0x5427 /* BSD compatibility */
-#define TIOCCBRK 0x5428 /* BSD compatibility */
-#define TIOCGSID 0x5429 /* Return the session ID of FD */
-#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
-#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
-
-#define TIOCSERCONFIG 0x5453
-#define TIOCSERGWILD 0x5454
-#define TIOCSERSWILD 0x5455
-#define TIOCGLCKTRMIOS 0x5456
-#define TIOCSLCKTRMIOS 0x5457
-#define TIOCSERGSTRUCT 0x5458 /* For debugging only */
-#define TIOCSERGETLSR 0x5459 /* Get line status register */
- /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
-# define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
-#define TIOCSERGETMULTI 0x545A /* Get multiport config */
-#define TIOCSERSETMULTI 0x545B /* Set multiport config */
-
-#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
-#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
-
-#endif /* _ASM_PPC64_IOCTLS_H */
+#include <asm-ppc/ioctls.h>
diff -ruNp linus/include/asm-ppc64/ipc.h linus-headers/include/asm-ppc64/ipc.h
--- linus/include/asm-ppc64/ipc.h 2004-05-30 11:50:26.000000000 +1000
+++ linus-headers/include/asm-ppc64/ipc.h 2005-03-09 01:15:40.000000000 +1100
@@ -1,34 +1 @@
-#ifndef __PPC64_IPC_H__
-#define __PPC64_IPC_H__
-
-/*
- * These are used to wrap system calls on PowerPC.
- *
- * See arch/ppc64/kernel/syscalls.c for ugly details..
- *
- * 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.
- */
-struct ipc_kludge {
- struct msgbuf __user *msgp;
- long msgtyp;
-};
-
-#define SEMOP 1
-#define SEMGET 2
-#define SEMCTL 3
-#define SEMTIMEDOP 4
-#define MSGSND 11
-#define MSGRCV 12
-#define MSGGET 13
-#define MSGCTL 14
-#define SHMAT 21
-#define SHMDT 22
-#define SHMGET 23
-#define SHMCTL 24
-
-#define IPCCALL(version,op) ((version)<<16 | (op))
-
-#endif /* __PPC64_IPC_H__ */
+#include <asm-ppc/ipc.h>
diff -ruNp linus/include/asm-ppc64/mman.h linus-headers/include/asm-ppc64/mman.h
--- linus/include/asm-ppc64/mman.h 2003-09-26 07:54:24.000000000 +1000
+++ linus-headers/include/asm-ppc64/mman.h 2005-03-09 01:25:14.000000000 +1100
@@ -1,52 +1 @@
-#ifndef __PPC64_MMAN_H__
-#define __PPC64_MMAN_H__
-
-/*
- * 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.
- */
-
-#define PROT_READ 0x1 /* page can be read */
-#define PROT_WRITE 0x2 /* page can be written */
-#define PROT_EXEC 0x4 /* page can be executed */
-#define PROT_SEM 0x8 /* page may be used for atomic ops */
-#define PROT_NONE 0x0 /* page can not be accessed */
-#define PROT_GROWSDOWN 0x01000000 /* mprotect flag: extend change to start of growsdown vma */
-#define PROT_GROWSUP 0x02000000 /* mprotect flag: extend change to end of growsup vma */
-
-#define MAP_SHARED 0x01 /* Share changes */
-#define MAP_PRIVATE 0x02 /* Changes are private */
-#define MAP_TYPE 0x0f /* Mask for type of mapping */
-#define MAP_FIXED 0x10 /* Interpret addr exactly */
-#define MAP_ANONYMOUS 0x20 /* don't use a file */
-#define MAP_RENAME MAP_ANONYMOUS /* In SunOS terminology */
-#define MAP_NORESERVE 0x40 /* don't reserve swap pages */
-#define MAP_LOCKED 0x80
-
-#define MAP_GROWSDOWN 0x0100 /* stack-like segment */
-#define MAP_DENYWRITE 0x0800 /* ETXTBSY */
-#define MAP_EXECUTABLE 0x1000 /* mark it as an executable */
-
-#define MS_ASYNC 1 /* sync memory asynchronously */
-#define MS_INVALIDATE 2 /* invalidate the caches */
-#define MS_SYNC 4 /* synchronous memory sync */
-
-#define MCL_CURRENT 0x2000 /* lock all currently mapped pages */
-#define MCL_FUTURE 0x4000 /* lock all additions to address space */
-
-#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */
-#define MAP_NONBLOCK 0x10000 /* do not block on IO */
-
-#define MADV_NORMAL 0x0 /* default page-in behavior */
-#define MADV_RANDOM 0x1 /* page-in minimum required */
-#define MADV_SEQUENTIAL 0x2 /* read-ahead aggressively */
-#define MADV_WILLNEED 0x3 /* pre-fault pages */
-#define MADV_DONTNEED 0x4 /* discard these pages */
-
-/* compatibility flags */
-#define MAP_ANON MAP_ANONYMOUS
-#define MAP_FILE 0
-
-#endif /* __PPC64_MMAN_H__ */
+#include <asm-ppc/mman.h>
diff -ruNp linus/include/asm-ppc64/param.h linus-headers/include/asm-ppc64/param.h
--- linus/include/asm-ppc64/param.h 2004-02-23 12:05:19.000000000 +1100
+++ linus-headers/include/asm-ppc64/param.h 2005-03-09 01:38:01.000000000 +1100
@@ -1,29 +1 @@
-#ifndef _ASM_PPC64_PARAM_H
-#define _ASM_PPC64_PARAM_H
-
-/*
- * 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.
- */
-
-#ifdef __KERNEL__
-# define HZ 1000 /* Internal kernel timer frequency */
-# define USER_HZ 100 /* .. some user interfaces are in "ticks" */
-# define CLOCKS_PER_SEC (USER_HZ) /* like times() */
-#endif
-
-#ifndef HZ
-#define HZ 100
-#endif
-
-#define EXEC_PAGESIZE 4096
-
-#ifndef NOGROUP
-#define NOGROUP (-1)
-#endif
-
-#define MAXHOSTNAMELEN 64 /* max length of hostname */
-
-#endif /* _ASM_PPC64_PARAM_H */
+#include <asm-ppc/param.h>
diff -ruNp linus/include/asm-ppc64/parport.h linus-headers/include/asm-ppc64/parport.h
--- linus/include/asm-ppc64/parport.h 2002-02-14 23:14:36.000000000 +1100
+++ linus-headers/include/asm-ppc64/parport.h 2005-03-09 01:40:11.000000000 +1100
@@ -1,18 +1 @@
-/*
- * parport.h: platform-specific PC-style parport initialisation
- *
- * Copyright (C) 1999, 2000 Tim Waugh <tim@cyberelk.demon.co.uk>
- *
- * This file should only be included by drivers/parport/parport_pc.c.
- */
-
-#ifndef _ASM_PPC64_PARPORT_H
-#define _ASM_PPC64_PARPORT_H
-
-static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
-static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
-{
- return parport_pc_find_isa_ports (autoirq, autodma);
-}
-
-#endif /* !(_ASM_PPC_PARPORT_H) */
+#include <asm-ppc/parport.h>
diff -ruNp linus/include/asm-ppc64/poll.h linus-headers/include/asm-ppc64/poll.h
--- linus/include/asm-ppc64/poll.h 2002-11-01 05:18:30.000000000 +1100
+++ linus-headers/include/asm-ppc64/poll.h 2005-03-09 01:45:19.000000000 +1100
@@ -1,32 +1 @@
-#ifndef __PPC64_POLL_H
-#define __PPC64_POLL_H
-
-/*
- * Copyright (C) 2001 PPC64 Team, IBM Corp
- *
- * 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.
- */
-
-#define POLLIN 0x0001
-#define POLLPRI 0x0002
-#define POLLOUT 0x0004
-#define POLLERR 0x0008
-#define POLLHUP 0x0010
-#define POLLNVAL 0x0020
-#define POLLRDNORM 0x0040
-#define POLLRDBAND 0x0080
-#define POLLWRNORM 0x0100
-#define POLLWRBAND 0x0200
-#define POLLMSG 0x0400
-#define POLLREMOVE 0x1000
-
-struct pollfd {
- int fd;
- short events;
- short revents;
-};
-
-#endif /* __PPC64_POLL_H */
+#include <asm-ppc/poll.h>
diff -ruNp linus/include/asm-ppc64/string.h linus-headers/include/asm-ppc64/string.h
--- linus/include/asm-ppc64/string.h 2005-01-29 06:05:47.000000000 +1100
+++ linus-headers/include/asm-ppc64/string.h 2005-03-09 02:01:45.000000000 +1100
@@ -1,35 +1 @@
-#ifndef _PPC64_STRING_H_
-#define _PPC64_STRING_H_
-
-/*
- * 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.
- */
-
-#define __HAVE_ARCH_STRCPY
-#define __HAVE_ARCH_STRNCPY
-#define __HAVE_ARCH_STRLEN
-#define __HAVE_ARCH_STRCMP
-#define __HAVE_ARCH_STRCAT
-#define __HAVE_ARCH_MEMSET
-#define __HAVE_ARCH_MEMCPY
-#define __HAVE_ARCH_MEMMOVE
-#define __HAVE_ARCH_MEMCMP
-#define __HAVE_ARCH_MEMCHR
-
-extern int strcasecmp(const char *, const char *);
-extern int strncasecmp(const char *, const char *, int);
-extern char * strcpy(char *,const char *);
-extern char * strncpy(char *,const char *, __kernel_size_t);
-extern __kernel_size_t strlen(const char *);
-extern int strcmp(const char *,const char *);
-extern char * strcat(char *, const char *);
-extern void * memset(void *,int,__kernel_size_t);
-extern void * memcpy(void *,const void *,__kernel_size_t);
-extern void * memmove(void *,const void *,__kernel_size_t);
-extern int memcmp(const void *,const void *,__kernel_size_t);
-extern void * memchr(const void *,int,__kernel_size_t);
-
-#endif /* _PPC64_STRING_H_ */
+#include <asm-ppc/string.h>
diff -ruNp linus/include/asm-ppc64/termbits.h linus-headers/include/asm-ppc64/termbits.h
--- linus/include/asm-ppc64/termbits.h 2004-05-11 07:53:05.000000000 +1000
+++ linus-headers/include/asm-ppc64/termbits.h 2005-03-09 02:04:35.000000000 +1100
@@ -1,193 +1 @@
-#ifndef _PPC64_TERMBITS_H
-#define _PPC64_TERMBITS_H
-
-/*
- * 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.
- */
-
-#include <linux/posix_types.h>
-
-typedef unsigned char cc_t;
-typedef unsigned int speed_t;
-typedef unsigned int tcflag_t;
-
-/*
- * termios type and macro definitions. Be careful about adding stuff
- * to this file since it's used in GNU libc and there are strict rules
- * concerning namespace pollution.
- */
-
-#define NCCS 19
-struct termios {
- tcflag_t c_iflag; /* input mode flags */
- tcflag_t c_oflag; /* output mode flags */
- tcflag_t c_cflag; /* control mode flags */
- tcflag_t c_lflag; /* local mode flags */
- cc_t c_cc[NCCS]; /* control characters */
- cc_t c_line; /* line discipline (== c_cc[19]) */
- speed_t c_ispeed; /* input speed */
- speed_t c_ospeed; /* output speed */
-};
-
-/* c_cc characters */
-#define VINTR 0
-#define VQUIT 1
-#define VERASE 2
-#define VKILL 3
-#define VEOF 4
-#define VMIN 5
-#define VEOL 6
-#define VTIME 7
-#define VEOL2 8
-#define VSWTC 9
-#define VWERASE 10
-#define VREPRINT 11
-#define VSUSP 12
-#define VSTART 13
-#define VSTOP 14
-#define VLNEXT 15
-#define VDISCARD 16
-
-/* c_iflag bits */
-#define IGNBRK 0000001
-#define BRKINT 0000002
-#define IGNPAR 0000004
-#define PARMRK 0000010
-#define INPCK 0000020
-#define ISTRIP 0000040
-#define INLCR 0000100
-#define IGNCR 0000200
-#define ICRNL 0000400
-#define IXON 0001000
-#define IXOFF 0002000
-#define IXANY 0004000
-#define IUCLC 0010000
-#define IMAXBEL 0020000
-#define IUTF8 0040000
-
-/* c_oflag bits */
-#define OPOST 0000001
-#define ONLCR 0000002
-#define OLCUC 0000004
-
-#define OCRNL 0000010
-#define ONOCR 0000020
-#define ONLRET 0000040
-
-#define OFILL 00000100
-#define OFDEL 00000200
-#define NLDLY 00001400
-#define NL0 00000000
-#define NL1 00000400
-#define NL2 00001000
-#define NL3 00001400
-#define TABDLY 00006000
-#define TAB0 00000000
-#define TAB1 00002000
-#define TAB2 00004000
-#define TAB3 00006000
-#define XTABS 00006000 /* required by POSIX to == TAB3 */
-#define CRDLY 00030000
-#define CR0 00000000
-#define CR1 00010000
-#define CR2 00020000
-#define CR3 00030000
-#define FFDLY 00040000
-#define FF0 00000000
-#define FF1 00040000
-#define BSDLY 00100000
-#define BS0 00000000
-#define BS1 00100000
-#define VTDLY 00200000
-#define VT0 00000000
-#define VT1 00200000
-
-/* c_cflag bit meaning */
-#define CBAUD 0000377
-#define B0 0000000 /* hang up */
-#define B50 0000001
-#define B75 0000002
-#define B110 0000003
-#define B134 0000004
-#define B150 0000005
-#define B200 0000006
-#define B300 0000007
-#define B600 0000010
-#define B1200 0000011
-#define B1800 0000012
-#define B2400 0000013
-#define B4800 0000014
-#define B9600 0000015
-#define B19200 0000016
-#define B38400 0000017
-#define EXTA B19200
-#define EXTB B38400
-#define CBAUDEX 0000000
-#define B57600 00020
-#define B115200 00021
-#define B230400 00022
-#define B460800 00023
-#define B500000 00024
-#define B576000 00025
-#define B921600 00026
-#define B1000000 00027
-#define B1152000 00030
-#define B1500000 00031
-#define B2000000 00032
-#define B2500000 00033
-#define B3000000 00034
-#define B3500000 00035
-#define B4000000 00036
-
-#define CSIZE 00001400
-#define CS5 00000000
-#define CS6 00000400
-#define CS7 00001000
-#define CS8 00001400
-
-#define CSTOPB 00002000
-#define CREAD 00004000
-#define PARENB 00010000
-#define PARODD 00020000
-#define HUPCL 00040000
-
-#define CLOCAL 00100000
-#define CRTSCTS 020000000000 /* flow control */
-
-/* c_lflag bits */
-#define ISIG 0x00000080
-#define ICANON 0x00000100
-#define XCASE 0x00004000
-#define ECHO 0x00000008
-#define ECHOE 0x00000002
-#define ECHOK 0x00000004
-#define ECHONL 0x00000010
-#define NOFLSH 0x80000000
-#define TOSTOP 0x00400000
-#define ECHOCTL 0x00000040
-#define ECHOPRT 0x00000020
-#define ECHOKE 0x00000001
-#define FLUSHO 0x00800000
-#define PENDIN 0x20000000
-#define IEXTEN 0x00000400
-
-/* Values for the ACTION argument to `tcflow'. */
-#define TCOOFF 0
-#define TCOON 1
-#define TCIOFF 2
-#define TCION 3
-
-/* Values for the QUEUE_SELECTOR argument to `tcflush'. */
-#define TCIFLUSH 0
-#define TCOFLUSH 1
-#define TCIOFLUSH 2
-
-/* Values for the OPTIONAL_ACTIONS argument to `tcsetattr'. */
-#define TCSANOW 0
-#define TCSADRAIN 1
-#define TCSAFLUSH 2
-
-#endif /* _PPC64_TERMBITS_H */
+#include <asm-ppc/termbits.h>
diff -ruNp linus/include/asm-ppc64/termios.h linus-headers/include/asm-ppc64/termios.h
--- linus/include/asm-ppc64/termios.h 2003-04-03 08:55:29.000000000 +1000
+++ linus-headers/include/asm-ppc64/termios.h 2005-03-09 02:13:20.000000000 +1100
@@ -1,235 +1 @@
-#ifndef _PPC64_TERMIOS_H
-#define _PPC64_TERMIOS_H
-
-/*
- * Liberally adapted from alpha/termios.h. In particular, the c_cc[]
- * fields have been reordered so that termio & termios share the
- * common subset in the same order (for brain dead programs that don't
- * know or care about the differences).
- *
- * 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.
- */
-
-#include <asm/ioctls.h>
-#include <asm/termbits.h>
-
-struct sgttyb {
- char sg_ispeed;
- char sg_ospeed;
- char sg_erase;
- char sg_kill;
- short sg_flags;
-};
-
-struct tchars {
- char t_intrc;
- char t_quitc;
- char t_startc;
- char t_stopc;
- char t_eofc;
- char t_brkc;
-};
-
-struct ltchars {
- char t_suspc;
- char t_dsuspc;
- char t_rprntc;
- char t_flushc;
- char t_werasc;
- char t_lnextc;
-};
-
-struct winsize {
- unsigned short ws_row;
- unsigned short ws_col;
- unsigned short ws_xpixel;
- unsigned short ws_ypixel;
-};
-
-#define NCC 10
-struct termio {
- unsigned short c_iflag; /* input mode flags */
- unsigned short c_oflag; /* output mode flags */
- unsigned short c_cflag; /* control mode flags */
- unsigned short c_lflag; /* local mode flags */
- unsigned char c_line; /* line discipline */
- unsigned char c_cc[NCC]; /* control characters */
-};
-
-/* c_cc characters */
-#define _VINTR 0
-#define _VQUIT 1
-#define _VERASE 2
-#define _VKILL 3
-#define _VEOF 4
-#define _VMIN 5
-#define _VEOL 6
-#define _VTIME 7
-#define _VEOL2 8
-#define _VSWTC 9
-
-/* line disciplines */
-#define N_TTY 0
-#define N_SLIP 1
-#define N_MOUSE 2
-#define N_PPP 3
-#define N_STRIP 4
-#define N_AX25 5
-#define N_X25 6 /* X.25 async */
-#define N_6PACK 7
-#define N_MASC 8 /* Reserved for Mobitex module <kaz@cafe.net> */
-#define N_R3964 9 /* Reserved for Simatic R3964 module */
-#define N_PROFIBUS_FDL 10 /* Reserved for Profibus <Dave@mvhi.com> */
-#define N_IRDA 11 /* Linux IrDa - http://www.cs.uit.no/~dagb/irda/irda.html */
-#define N_SMSBLOCK 12 /* SMS block mode - for talking to GSM data cards about SMS messages */
-#define N_HDLC 13 /* synchronous HDLC */
-#define N_SYNC_PPP 14
-
-#ifdef __KERNEL__
-/* ^C ^\ del ^U ^D 1 0 0 0 0 ^W ^R ^Z ^Q ^S ^V ^U */
-#define INIT_C_CC "\003\034\177\025\004\001\000\000\000\000\027\022\032\021\023\026\025"
-#endif
-
-#define FIOCLEX _IO('f', 1)
-#define FIONCLEX _IO('f', 2)
-#define FIOASYNC _IOW('f', 125, int)
-#define FIONBIO _IOW('f', 126, int)
-#define FIONREAD _IOR('f', 127, int)
-#define TIOCINQ FIONREAD
-
-#define TIOCGETP _IOR('t', 8, struct sgttyb)
-#define TIOCSETP _IOW('t', 9, struct sgttyb)
-#define TIOCSETN _IOW('t', 10, struct sgttyb) /* TIOCSETP wo flush */
-
-#define TIOCSETC _IOW('t', 17, struct tchars)
-#define TIOCGETC _IOR('t', 18, struct tchars)
-#define TCGETS _IOR('t', 19, struct termios)
-#define TCSETS _IOW('t', 20, struct termios)
-#define TCSETSW _IOW('t', 21, struct termios)
-#define TCSETSF _IOW('t', 22, struct termios)
-
-#define TCGETA _IOR('t', 23, struct termio)
-#define TCSETA _IOW('t', 24, struct termio)
-#define TCSETAW _IOW('t', 25, struct termio)
-#define TCSETAF _IOW('t', 28, struct termio)
-
-#define TCSBRK _IO('t', 29)
-#define TCXONC _IO('t', 30)
-#define TCFLSH _IO('t', 31)
-
-#define TIOCSWINSZ _IOW('t', 103, struct winsize)
-#define TIOCGWINSZ _IOR('t', 104, struct winsize)
-#define TIOCSTART _IO('t', 110) /* start output, like ^Q */
-#define TIOCSTOP _IO('t', 111) /* stop output, like ^S */
-#define TIOCOUTQ _IOR('t', 115, int) /* output queue size */
-
-#define TIOCGLTC _IOR('t', 116, struct ltchars)
-#define TIOCSLTC _IOW('t', 117, struct ltchars)
-#define TIOCSPGRP _IOW('t', 118, int)
-#define TIOCGPGRP _IOR('t', 119, int)
-
-#define TIOCEXCL 0x540C
-#define TIOCNXCL 0x540D
-#define TIOCSCTTY 0x540E
-
-#define TIOCSTI 0x5412
-#define TIOCMGET 0x5415
-#define TIOCMBIS 0x5416
-#define TIOCMBIC 0x5417
-#define TIOCMSET 0x5418
-#define TIOCGSOFTCAR 0x5419
-#define TIOCSSOFTCAR 0x541A
-#define TIOCLINUX 0x541C
-#define TIOCCONS 0x541D
-#define TIOCGSERIAL 0x541E
-#define TIOCSSERIAL 0x541F
-#define TIOCPKT 0x5420
-
-#define TIOCNOTTY 0x5422
-#define TIOCSETD 0x5423
-#define TIOCGETD 0x5424
-#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */
-
-#define TIOCSERCONFIG 0x5453
-#define TIOCSERGWILD 0x5454
-#define TIOCSERSWILD 0x5455
-#define TIOCGLCKTRMIOS 0x5456
-#define TIOCSLCKTRMIOS 0x5457
-#define TIOCSERGSTRUCT 0x5458 /* For debugging only */
-#define TIOCSERGETLSR 0x5459 /* Get line status register */
-#define TIOCSERGETMULTI 0x545A /* Get multiport config */
-#define TIOCSERSETMULTI 0x545B /* Set multiport config */
-
-#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
-#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
-
-/* Used for packet mode */
-#define TIOCPKT_DATA 0
-#define TIOCPKT_FLUSHREAD 1
-#define TIOCPKT_FLUSHWRITE 2
-#define TIOCPKT_STOP 4
-#define TIOCPKT_START 8
-#define TIOCPKT_NOSTOP 16
-#define TIOCPKT_DOSTOP 32
-
-/* modem lines */
-#define TIOCM_LE 0x001
-#define TIOCM_DTR 0x002
-#define TIOCM_RTS 0x004
-#define TIOCM_ST 0x008
-#define TIOCM_SR 0x010
-#define TIOCM_CTS 0x020
-#define TIOCM_CAR 0x040
-#define TIOCM_RNG 0x080
-#define TIOCM_DSR 0x100
-#define TIOCM_CD TIOCM_CAR
-#define TIOCM_RI TIOCM_RNG
-#define TIOCM_OUT1 0x2000
-#define TIOCM_OUT2 0x4000
-#define TIOCM_LOOP 0x8000
-
-/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
-#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
-
-#ifdef __KERNEL__
-
-/*
- * Translate a "termio" structure into a "termios". Ugh.
- */
-#define SET_LOW_TERMIOS_BITS(termios, termio, x) { \
- unsigned short __tmp; \
- get_user(__tmp,&(termio)->x); \
- (termios)->x = (0xffff0000 & (termios)->x) | __tmp; \
-}
-
-#define user_termio_to_kernel_termios(termios, termio) \
-({ \
- SET_LOW_TERMIOS_BITS(termios, termio, c_iflag); \
- SET_LOW_TERMIOS_BITS(termios, termio, c_oflag); \
- SET_LOW_TERMIOS_BITS(termios, termio, c_cflag); \
- SET_LOW_TERMIOS_BITS(termios, termio, c_lflag); \
- copy_from_user((termios)->c_cc, (termio)->c_cc, NCC); \
-})
-
-/*
- * Translate a "termios" structure into a "termio". Ugh.
- */
-#define kernel_termios_to_user_termio(termio, termios) \
-({ \
- put_user((termios)->c_iflag, &(termio)->c_iflag); \
- put_user((termios)->c_oflag, &(termio)->c_oflag); \
- put_user((termios)->c_cflag, &(termio)->c_cflag); \
- put_user((termios)->c_lflag, &(termio)->c_lflag); \
- put_user((termios)->c_line, &(termio)->c_line); \
- copy_to_user((termio)->c_cc, (termios)->c_cc, NCC); \
-})
-
-#define user_termios_to_kernel_termios(k, u) copy_from_user(k, u, sizeof(struct termios))
-#define kernel_termios_to_user_termios(u, k) copy_to_user(u, k, sizeof(struct termios))
-
-#endif /* __KERNEL__ */
-
-#endif /* _PPC64_TERMIOS_H */
+#include <asm-ppc/termios.h>
diff -ruNp linus/include/asm-ppc64/unaligned.h linus-headers/include/asm-ppc64/unaligned.h
--- linus/include/asm-ppc64/unaligned.h 2002-02-14 23:14:36.000000000 +1100
+++ linus-headers/include/asm-ppc64/unaligned.h 2005-03-09 02:16:30.000000000 +1100
@@ -1,21 +1 @@
-#ifndef __PPC64_UNALIGNED_H
-#define __PPC64_UNALIGNED_H
-
-/*
- * The PowerPC can do unaligned accesses itself in big endian mode.
- *
- * The strange macros are there to make sure these can't
- * be misused in a way that makes them not work on other
- * architectures where unaligned accesses aren't as simple.
- *
- * 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.
- */
-
-#define get_unaligned(ptr) (*(ptr))
-
-#define put_unaligned(val, ptr) ((void)( *(ptr) = (val) ))
-
-#endif /* __PPC64_UNALIGNED_H */
+#include <asm-ppc/unaligned.h>
^ permalink raw reply
* Re: Recommended kernel version/tree for use with MPC8555E?
From: Kumar Gala @ 2005-03-09 0:34 UTC (permalink / raw)
To: Kylo Ginsberg; +Cc: inuxppc-embedded List
In-Reply-To: <422E3CCD.4020204@kylo.net>
Kylo,
All active development for PPC (including the MPCE8555E) is being done=20=
in 2.6 only at this point. I would recommend using 2.6.11 as a=20
starting point. Any code to support new features of the MPC8555E will=20=
be developed for 2.6 at this point.
The 2.6.11 tree has support for both the MPC8560 ADS board=20
(mpc8560_ads_defconfig) and the MPC8555 CDS board=20
(mpc8555_cds_defconfig). Those should be good starting points for any=20=
work you are doing.
- kumar
On Mar 8, 2005, at 6:01 PM, Kylo Ginsberg wrote:
> I've just started working on s/w for an 8555E-based board.=A0 On the=20=
> 8555E
> I will be using the TSECs (Gigabit ethernet interfaces), DMA engines,
> the security engine, UARTs, DDR and Local Bus controllers and that's
> about it.
>
> Right now I've got an MPC8560ADS eval board to test against.=A0 This=20=
> board
> came with a 2.4.26-based kernel; it boots ok and runs the basic apps
> fine, however I haven't yet had an opportunity to exercise the wide
> array of functionality on the chip, so I don't know what gotchas or
> porting difficulties I might meet ahead.
>
> Based on that, what kernel version/tree would you recommend?=A0 Should =
I
> stick with what I've got, move to a ppc-specific 2.4 tree, or jump to
> 2.6.x?=A0 Are there performance/features vs. stability tradeoffs I =
should
> be warned of?
>
> Thanks in advance for any thoughts you may have.
>
> Kylo
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded
^ permalink raw reply
* Recommended kernel version/tree for use with MPC8555E?
From: Kylo Ginsberg @ 2005-03-09 0:01 UTC (permalink / raw)
To: linuxppc-embedded
I've just started working on s/w for an 8555E-based board. On the 8555E
I will be using the TSECs (Gigabit ethernet interfaces), DMA engines,
the security engine, UARTs, DDR and Local Bus controllers and that's
about it.
Right now I've got an MPC8560ADS eval board to test against. This board
came with a 2.4.26-based kernel; it boots ok and runs the basic apps
fine, however I haven't yet had an opportunity to exercise the wide
array of functionality on the chip, so I don't know what gotchas or
porting difficulties I might meet ahead.
Based on that, what kernel version/tree would you recommend? Should I
stick with what I've got, move to a ppc-specific 2.4 tree, or jump to
2.6.x? Are there performance/features vs. stability tradeoffs I should
be warned of?
Thanks in advance for any thoughts you may have.
Kylo
^ permalink raw reply
* Request for testing: [PATCH] remove SPR short-hand defines
From: Kumar Gala @ 2005-03-09 0:21 UTC (permalink / raw)
To: inuxppc-embedded List, linuxppc-dev list
[-- Attachment #1: Type: text/plain, Size: 293 bytes --]
All,
Can you please take a whirl at testing this patch. I think I got all
the cases in which we used SPR short-hand defines and replaced them
with SPRN_<FOO> for ppc32.
Let me know if you have any issues (I've done simple build tests on
4xx, 8xx, 82xx, 85xx, and pmac)
Thanks
- kumar
[-- Attachment #2: spr-remove-shorthand.patch.gz --]
[-- Type: application/x-gzip, Size: 22620 bytes --]
^ permalink raw reply
* Re: Lots of memory on 826x
From: Matt Porter @ 2005-03-08 23:39 UTC (permalink / raw)
To: Rune Torgersen; +Cc: linuxppc-embedded
In-Reply-To: <DCEAAC0833DD314AB0B58112AD99B93B8592ED@ismail.innsys.innovsys.com>
On Tue, Mar 08, 2005 at 05:35:19PM -0600, Rune Torgersen wrote:
> I can use 1GB of RAM now, if I move kernel base to 0xA0000000 and set
> lowmem size to 1GB. (No Highmem at all)
Yup, that's one of many many options. Since TASK_SIZE is only
0x80000000 by default on ppc32, there's no loss anyway.
-Matt
^ permalink raw reply
* RE: Lots of memory on 826x
From: Rune Torgersen @ 2005-03-08 23:35 UTC (permalink / raw)
To: Matt Porter; +Cc: linuxppc-embedded
I can use 1GB of RAM now, if I move kernel base to 0xA0000000 and set=20
lowmem size to 1GB. (No Highmem at all)
> -----Original Message-----
> From: Matt Porter [mailto:mporter@kernel.crashing.org]=20
> Sent: Tuesday, March 08, 2005 17:30
> To: Rune Torgersen
> Cc: Kumar Gala; linuxppc-embedded@ozlabs.org
> Subject: Re: Lots of memory on 826x
> Ahh, but early ioremaps (before mm is inited) grow down from=20
> PKMAP_BASE,
> so you will be overlapping the top of lowmem with those allocations.
> It wants to be above vmalloc space. Set HIGHMEM_START to 0xF6000000,
> IMMR/IO to 0xF8000000 and you then have vmalloc space between
> 0xF1000000-0xF5FFFFFF.
I'll try this tomorrow and see what happens.=20
(Still need to make sure our board supports 2GB....:)
^ permalink raw reply
* Re: Lots of memory on 826x
From: Matt Porter @ 2005-03-08 23:30 UTC (permalink / raw)
To: Rune Torgersen; +Cc: linuxppc-embedded
In-Reply-To: <DCEAAC0833DD314AB0B58112AD99B93B8592EB@ismail.innsys.innovsys.com>
On Tue, Mar 08, 2005 at 05:20:38PM -0600, Rune Torgersen wrote:
> > -----Original Message-----
> > From: Kumar Gala [mailto:kumar.gala@freescale.com]
> > Sent: Tuesday, March 08, 2005 17:14
> > To: Rune Torgersen
> > Cc: linuxppc-embedded@ozlabs.org; Matt Porter
> > Subject: Re: Lots of memory on 826x
> >
> > Hmm, I think there is an expectation that virt->phys is a simple
> > translation (ie, subtract KERNELBASE) for lowmem. lowmem most parts
> > tops out at 768M. So changing VMALLOC_START would break that.
>
> Yep noticed..... (tried changing it...)
Yeah, sorry, got busy and couldn't reply to say, "don't do that!". :)
> > Is there an issue with moving the IMMR virt address up higher?
> Not really. Will have to redo a whole lot of address maps for some
> external devices, but not too bad...
>
> I did change the kernel start address, and that seems to work better,
> except that things is still broken when CONFIG_HIGHMEM is set
> When that option is set, it maps the highmem to 0xFE000000, which is in
> the IO area mapped for IMMR and other memory mapped devices.
> (0xF0000000-0xFFFFFFFF)
>
> This can be changed by CONFIG_HIGHMEM_START. Any good ideas on where to
> move it to?
>
> (Maybe change IMMR/IO to 0xF8000000 and up, and set HIGHMEM_START to
> 0xF0000000?)
Ahh, but early ioremaps (before mm is inited) grow down from PKMAP_BASE,
so you will be overlapping the top of lowmem with those allocations.
It wants to be above vmalloc space. Set HIGHMEM_START to 0xF6000000,
IMMR/IO to 0xF8000000 and you then have vmalloc space between
0xF1000000-0xF5FFFFFF.
-Matt
^ permalink raw reply
* RE: Lots of memory on 826x
From: Rune Torgersen @ 2005-03-08 23:20 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-embedded
=20
> -----Original Message-----
> From: Kumar Gala [mailto:kumar.gala@freescale.com]=20
> Sent: Tuesday, March 08, 2005 17:14
> To: Rune Torgersen
> Cc: linuxppc-embedded@ozlabs.org; Matt Porter
> Subject: Re: Lots of memory on 826x
>=20
> Hmm, I think there is an expectation that virt->phys is a simple=20
> translation (ie, subtract KERNELBASE) for lowmem. lowmem most parts=20
> tops out at 768M. So changing VMALLOC_START would break that.
Yep noticed..... (tried changing it...)
>=20
> Is there an issue with moving the IMMR virt address up higher?
Not really. Will have to redo a whole lot of address maps for some
external devices, but not too bad...
I did change the kernel start address, and that seems to work better,
except that things is still broken when CONFIG_HIGHMEM is set
When that option is set, it maps the highmem to 0xFE000000, which is in
the IO area mapped for IMMR and other memory mapped devices.
(0xF0000000-0xFFFFFFFF)
This can be changed by CONFIG_HIGHMEM_START. Any good ideas on where to
move it to?
(Maybe change IMMR/IO to 0xF8000000 and up, and set HIGHMEM_START to
0xF0000000?)
^ permalink raw reply
* Re: final mv643xx_eth pegasos patch set ...
From: Nicolas DET @ 2005-03-08 23:18 UTC (permalink / raw)
To: Sven Luther, Dale Farnsworth, Nicolas DET, linuxppc-dev list,
linuxppc-embedded
In-Reply-To: <20050308195239.GA14126@pegasos>
Hello Sven,
On 08/03/2005, you wrote:
> On Tue, Mar 08, 2005 at 08:28:05PM +0100, luther wrote:
>> Hello,
>>
>> Ok, thanks for your help, here is the final (hopefully) and cleaned up
>> version of my patches :
>>
>> 1) mv643xx-eth-pegasos.diff
>> This is the arch/ppc/platform diff, benh, could you look it over, and
>> merge it in ? It is not really usefull without the mv643xx_eth.c
>> changes, but dale is going to move those in together with his patches.
>>
>> 2) linux-2.5-mv643xx-enet-pegasos.diff
>> This is just two small hunks against dale's linux-2.5-mv643xx-enet
>> tree, which include the SA_INTERRUPT -> SA_IRQ and the Kconfig change.
>> Dale said he would merge them with his stuff, hopefully for 2.6.12.
>>
>> 3) mv643xx-eth.diff
>> This is the linux-2.5-mv643xx-enet diff against 2.6.11, together with
>> the linux-2.5-mv643xx-enet-pegasos.diff changes, for those who want to
>> build against 2.6.11.
>>
>> So to build against the linux-2.5-mv643xx-enet tree, you need 1) and 2)
>> and to build against 2.6.11 you need 1) and 3).
>>
This patch is requiered to get the ethernet driver working.
http://powernico.free.fr/patch_2.6.11_mv64360_peg2
It only check deepers if we have a Marvell chipset.
this patch has been made against 2.6.11 from kernel.org and only touch:
arch/ppc/platform/Makefile
arch/ppc/mv643xx_eth_pegasos.c
Regards
--
Nicolas DET
MorphOS & Linux developer
^ permalink raw reply
* Re: Lots of memory on 826x
From: Kumar Gala @ 2005-03-08 23:13 UTC (permalink / raw)
To: Rune Torgersen; +Cc: linuxppc-embedded
In-Reply-To: <DCEAAC0833DD314AB0B58112AD99B93B8592EA@ismail.innsys.innovsys.com>
Hmm, I think there is an expectation that virt->phys is a simple=20
translation (ie, subtract KERNELBASE) for lowmem. lowmem most parts=20
tops out at 768M. So changing VMALLOC_START would break that.
Is there an issue with moving the IMMR virt address up higher?
- kumar
On Mar 8, 2005, at 3:16 PM, Rune Torgersen wrote:
> What about moving the VMALLOC_START and VMALLOC_END #defines to
> something else (like start at 0x80000000 and end at 0xc0000000), or=20=
> will
> that break some assumptions in the kernel about the position of the
> vmalloc area?
>
> > -----Original Message-----
> > From: Matt Porter [mailto:mporter@kernel.crashing.org]
> > Sent: Tuesday, March 08, 2005 14:33
> > To: Rune Torgersen
> > Cc: Kumar Gala; linuxppc-embedded@ozlabs.org
> > Subject: Re: Lots of memory on 826x
> >
> > On Tue, Mar 08, 2005 at 12:37:33PM -0600, Rune Torgersen wrote:
> > > Ok... Got a little closer....
> > >
> > > I have PCI (outbound) mapped in the area 0x80000000-0x8fffffff
> > > (prefetch/non-prefetch and IO)
> > > IMMR of cource is at 0xf0000000
> > >
> > > So I map the IO as following:
> > > =A0=A0=A0 /* Map IMMR region to a 256MB BAT */
> > > =A0=A0=A0 addr =3D (cpm2_immr !=3D NULL) ? (uint)cpm2_immr : =
CPM_MAP_ADDR;
> > > =A0=A0=A0 io_block_mapping(addr, addr, 0x10000000, _PAGE_IO);
> > >
> > > =A0=A0=A0 io_block_mapping(0x80000000, 0x80000000, 0x10000000, =
_PAGE_IO);
> > >
> > > Now I can almost boot....:
> > > It craches when trying to read from harddisk
> > > (only happens with more than 512MB)
> >
> > That's expected if you are going to 768MB. You have kernel lowmem
> > being mapped at 0xc0000000+<size_of_system_memory>. With 768MB that
> > takes you all the way to where you have the IMMR mapped 1:1 I
> > guess. This leaves no room for vmalloc space.=A0 With 768MB of ram
> > the kernel will calculate VMALLOC_START at 0xf1000000 which is a
> > problem.
> >
> > A few options are:
> >
> > * Move the IMMR much higher to allow vmalloc space
> > * Modify maximum low memory to limit to 512MB using the advanced
> >=A0=A0 options menu
> > * Modify KERNELBASE to something like 0xa0000000 using the advanced
> >=A0=A0 options menu
> >
> > All of these methods will provide addition vmalloc space.
> >
> > Don't forget to turn on HIGHMEM to use everything above MAX_LOW_MEM.
> >
> > -Matt
> >
> >
^ permalink raw reply
* Re: mv643xx_eth SA_SHIRQ support patch
From: Benjamin Herrenschmidt @ 2005-03-08 22:31 UTC (permalink / raw)
To: sl; +Cc: linuxppc-dev list, Dale Farnsworth, linuxppc-embedd, Nicolas DET
In-Reply-To: <20050308164310.GA9891@pegasos>
> Ok, i should have something working. adding here both sven2-dale.diff which is
> a patch against linux-2.5-mv643xx-enet and sven2.diff which is a patch against
> 2.6.11.
>
> There is still a bit of cleanup needed in arch/ppc/platforms/mv643xx_eth_pegasos.c
> especially with regard the headers, and also the proper copyright/attibution
> of it (since dale wrote it and i just pasted it and did the detection stuff
> benh mentioned above.
>
> I am unsure also about the :
>
> @@ -44,6 +44,9 @@
> #include <asm/pgtable.h>
> #include <asm/system.h>
> #include <asm/delay.h>
> +#ifdef PPC_MULTIPLATEFORM
> +#include <mv64x60.h>
> +#endif
> #include "mv643xx_eth.h"
>
> /*
>
> hunk. dale can you check it ?
Looks broken...
> Comments are welcome, in particular benh, i guess my detection code will beak
> horribly if there is another host node prior to the marvell one in the OF
> tree, which is not the case currently on pegasos though.
Why are you looking at vid/did ? Isn't there some name string (model,
compatible, whatever) you can use ? Or is your OF too bad to even give
such info ?
You can also iterate after the find_devices() using np->next
Ben.
^ permalink raw reply
* Re: MPC5200 kernel with IDE *and* FEC?
From: Wolfgang Denk @ 2005-03-08 21:27 UTC (permalink / raw)
To: Stephen Warren; +Cc: linuxppc-embedded
In-Reply-To: <DBFABB80F7FD3143A911F9E6CFD477B0056A92E3@hqemmail02.nvidia.com>
In message <DBFABB80F7FD3143A911F9E6CFD477B0056A92E3@hqemmail02.nvidia.com> you wrote:
>
> http://www.denx.de/twiki/bin/view/DULG/DebianOnPowerpcInstallationHowto
>
> That could save a lot of time, but is this kernel 2.4-based? The docs
> there imply that (buildling a kernel natively section references paths
> with 2_4 in them).
Yes, it is.
> We already have a 2.4-based MontaVista, but we're trying to get a 2.6
> kernel up and running with NPTL to compare the pthreads performance
> differences...
You can probably use the 2.4 kernel for installation (or - as
mentioned before - just use another PPC box and transfewr the disk
drive).
Best regards,
Wolfgang Denk
--
Software Engineering: Embedded and Realtime Systems, Embedded Linux
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
When properly administered, vacations do not diminish productivity:
for every week you're away and get nothing done, there's another when
your boss is away and you get twice as much done. -- Daniel B. Luten
^ permalink raw reply
* RE: Lots of memory on 826x
From: Rune Torgersen @ 2005-03-08 21:16 UTC (permalink / raw)
To: Matt Porter; +Cc: linuxppc-embedded
What about moving the VMALLOC_START and VMALLOC_END #defines to
something else (like start at 0x80000000 and end at 0xc0000000), or will
that break some assumptions in the kernel about the position of the
vmalloc area?
> -----Original Message-----
> From: Matt Porter [mailto:mporter@kernel.crashing.org]=20
> Sent: Tuesday, March 08, 2005 14:33
> To: Rune Torgersen
> Cc: Kumar Gala; linuxppc-embedded@ozlabs.org
> Subject: Re: Lots of memory on 826x
>=20
> On Tue, Mar 08, 2005 at 12:37:33PM -0600, Rune Torgersen wrote:
> > Ok... Got a little closer....
> >=20
> > I have PCI (outbound) mapped in the area 0x80000000-0x8fffffff
> > (prefetch/non-prefetch and IO)
> > IMMR of cource is at 0xf0000000
> >=20
> > So I map the IO as following:
> > /* Map IMMR region to a 256MB BAT */
> > addr =3D (cpm2_immr !=3D NULL) ? (uint)cpm2_immr : CPM_MAP_ADDR;
> > io_block_mapping(addr, addr, 0x10000000, _PAGE_IO);
> >=20
> > io_block_mapping(0x80000000, 0x80000000, 0x10000000, _PAGE_IO);
> >=20
> > Now I can almost boot....:
> > It craches when trying to read from harddisk
> > (only happens with more than 512MB)
>=20
> That's expected if you are going to 768MB. You have kernel lowmem
> being mapped at 0xc0000000+<size_of_system_memory>. With 768MB that
> takes you all the way to where you have the IMMR mapped 1:1 I
> guess. This leaves no room for vmalloc space. With 768MB of ram
> the kernel will calculate VMALLOC_START at 0xf1000000 which is a
> problem.
>=20
> A few options are:
>=20
> * Move the IMMR much higher to allow vmalloc space
> * Modify maximum low memory to limit to 512MB using the advanced
> options menu
> * Modify KERNELBASE to something like 0xa0000000 using the advanced
> options menu
>=20
> All of these methods will provide addition vmalloc space.
>=20
> Don't forget to turn on HIGHMEM to use everything above MAX_LOW_MEM.
>=20
> -Matt
>=20
>=20
^ permalink raw reply
* Re: Lots of memory on 826x
From: Matt Porter @ 2005-03-08 20:32 UTC (permalink / raw)
To: Rune Torgersen; +Cc: linuxppc-embedded
In-Reply-To: <DCEAAC0833DD314AB0B58112AD99B93B8592E9@ismail.innsys.innovsys.com>
On Tue, Mar 08, 2005 at 12:37:33PM -0600, Rune Torgersen wrote:
> Ok... Got a little closer....
>
> I have PCI (outbound) mapped in the area 0x80000000-0x8fffffff
> (prefetch/non-prefetch and IO)
> IMMR of cource is at 0xf0000000
>
> So I map the IO as following:
> /* Map IMMR region to a 256MB BAT */
> addr = (cpm2_immr != NULL) ? (uint)cpm2_immr : CPM_MAP_ADDR;
> io_block_mapping(addr, addr, 0x10000000, _PAGE_IO);
>
> io_block_mapping(0x80000000, 0x80000000, 0x10000000, _PAGE_IO);
>
> Now I can almost boot....:
> It craches when trying to read from harddisk
> (only happens with more than 512MB)
That's expected if you are going to 768MB. You have kernel lowmem
being mapped at 0xc0000000+<size_of_system_memory>. With 768MB that
takes you all the way to where you have the IMMR mapped 1:1 I
guess. This leaves no room for vmalloc space. With 768MB of ram
the kernel will calculate VMALLOC_START at 0xf1000000 which is a
problem.
A few options are:
* Move the IMMR much higher to allow vmalloc space
* Modify maximum low memory to limit to 512MB using the advanced
options menu
* Modify KERNELBASE to something like 0xa0000000 using the advanced
options menu
All of these methods will provide addition vmalloc space.
Don't forget to turn on HIGHMEM to use everything above MAX_LOW_MEM.
-Matt
^ permalink raw reply
* RE: MPC5200 kernel with IDE *and* FEC?
From: Stephen Warren @ 2005-03-08 20:16 UTC (permalink / raw)
To: wd; +Cc: linuxppc-embedded
From: wd@denx.de [mailto:wd@denx.de]=20
> In message
<DBFABB80F7FD3143A911F9E6CFD477B0056A92B5@hqemmail02.nvidia.com> you
wrote:
> >=20
> > By the way, to the people who mentioned using Debian and Gentoo on
the
> > MPC5200, are there any web-sites that have info on how to do this
> > (toolchain and/or distro)? I haven't managed to find anything
remotely
> > detailed on cross-building either of these distros. But I'll
probably
> > just do it from scratch now I have NPTL-aware toolchain:-)
>=20
> You don't have to cross-build anything; you can just use the existing
> ISO images for installation. For Debian Woody you can find a detailed
> description (thanks to bernhard Kuhn for this!) at
>
http://www.denx.de/twiki/bin/view/DULG/DebianOnPowerpcInstallationHowto=20
That could save a lot of time, but is this kernel 2.4-based? The docs
there imply that (buildling a kernel natively section references paths
with 2_4 in them).
We already have a 2.4-based MontaVista, but we're trying to get a 2.6
kernel up and running with NPTL to compare the pthreads performance
differences...
Thanks for the pointer though.
--=20
Stephen Warren, Software Engineer, NVIDIA, Fort Collins, CO
swarren@nvidia.com http://www.nvidia.com/
swarren@wwwdotorg.org http://www.wwwdotorg.org/pgp.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox