linux/arch/ia64/sn/kernel/tiocx.c
Linus Torvalds 0195c00244 Disintegrate and delete asm/system.h
-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (GNU/Linux)
 
 iQIVAwUAT3NKzROxKuMESys7AQKElw/+JyDxJSlj+g+nymkx8IVVuU8CsEwNLgRk
 8KEnRfLhGtkXFLSJYWO6jzGo16F8Uqli1PdMFte/wagSv0285/HZaKlkkBVHdJ/m
 u40oSjgT013bBh6MQ0Oaf8pFezFUiQB5zPOA9QGaLVGDLXCmgqUgd7exaD5wRIwB
 ZmyItjZeAVnDfk1R+ZiNYytHAi8A5wSB+eFDCIQYgyulA1Igd1UnRtx+dRKbvc/m
 rWQ6KWbZHIdvP1ksd8wHHkrlUD2pEeJ8glJLsZUhMm/5oMf/8RmOCvmo8rvE/qwl
 eDQ1h4cGYlfjobxXZMHqAN9m7Jg2bI946HZjdb7/7oCeO6VW3FwPZ/Ic75p+wp45
 HXJTItufERYk6QxShiOKvA+QexnYwY0IT5oRP4DrhdVB/X9cl2MoaZHC+RbYLQy+
 /5VNZKi38iK4F9AbFamS7kd0i5QszA/ZzEzKZ6VMuOp3W/fagpn4ZJT1LIA3m4A9
 Q0cj24mqeyCfjysu0TMbPtaN+Yjeu1o1OFRvM8XffbZsp5bNzuTDEvviJ2NXw4vK
 4qUHulhYSEWcu9YgAZXvEWDEM78FXCkg2v/CrZXH5tyc95kUkMPcgG+QZBB5wElR
 FaOKpiC/BuNIGEf02IZQ4nfDxE90QwnDeoYeV+FvNj9UEOopJ5z5bMPoTHxm4cCD
 NypQthI85pc=
 =G9mT
 -----END PGP SIGNATURE-----

Merge tag 'split-asm_system_h-for-linus-20120328' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-asm_system

Pull "Disintegrate and delete asm/system.h" from David Howells:
 "Here are a bunch of patches to disintegrate asm/system.h into a set of
  separate bits to relieve the problem of circular inclusion
  dependencies.

  I've built all the working defconfigs from all the arches that I can
  and made sure that they don't break.

  The reason for these patches is that I recently encountered a circular
  dependency problem that came about when I produced some patches to
  optimise get_order() by rewriting it to use ilog2().

  This uses bitops - and on the SH arch asm/bitops.h drags in
  asm-generic/get_order.h by a circuituous route involving asm/system.h.

  The main difficulty seems to be asm/system.h.  It holds a number of
  low level bits with no/few dependencies that are commonly used (eg.
  memory barriers) and a number of bits with more dependencies that
  aren't used in many places (eg.  switch_to()).

  These patches break asm/system.h up into the following core pieces:

    (1) asm/barrier.h

        Move memory barriers here.  This already done for MIPS and Alpha.

    (2) asm/switch_to.h

        Move switch_to() and related stuff here.

    (3) asm/exec.h

        Move arch_align_stack() here.  Other process execution related bits
        could perhaps go here from asm/processor.h.

    (4) asm/cmpxchg.h

        Move xchg() and cmpxchg() here as they're full word atomic ops and
        frequently used by atomic_xchg() and atomic_cmpxchg().

    (5) asm/bug.h

        Move die() and related bits.

    (6) asm/auxvec.h

        Move AT_VECTOR_SIZE_ARCH here.

  Other arch headers are created as needed on a per-arch basis."

Fixed up some conflicts from other header file cleanups and moving code
around that has happened in the meantime, so David's testing is somewhat
weakened by that.  We'll find out anything that got broken and fix it..

* tag 'split-asm_system_h-for-linus-20120328' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-asm_system: (38 commits)
  Delete all instances of asm/system.h
  Remove all #inclusions of asm/system.h
  Add #includes needed to permit the removal of asm/system.h
  Move all declarations of free_initmem() to linux/mm.h
  Disintegrate asm/system.h for OpenRISC
  Split arch_align_stack() out from asm-generic/system.h
  Split the switch_to() wrapper out of asm-generic/system.h
  Move the asm-generic/system.h xchg() implementation to asm-generic/cmpxchg.h
  Create asm-generic/barrier.h
  Make asm-generic/cmpxchg.h #include asm-generic/cmpxchg-local.h
  Disintegrate asm/system.h for Xtensa
  Disintegrate asm/system.h for Unicore32 [based on ver #3, changed by gxt]
  Disintegrate asm/system.h for Tile
  Disintegrate asm/system.h for Sparc
  Disintegrate asm/system.h for SH
  Disintegrate asm/system.h for Score
  Disintegrate asm/system.h for S390
  Disintegrate asm/system.h for PowerPC
  Disintegrate asm/system.h for PA-RISC
  Disintegrate asm/system.h for MN10300
  ...
2012-03-28 15:58:21 -07:00

567 lines
13 KiB
C

/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (c) 2005 Silicon Graphics, Inc. All rights reserved.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/proc_fs.h>
#include <linux/capability.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/sn/sn_sal.h>
#include <asm/sn/addrs.h>
#include <asm/sn/io.h>
#include <asm/sn/types.h>
#include <asm/sn/shubio.h>
#include <asm/sn/tiocx.h>
#include <asm/sn/l1.h>
#include <asm/sn/module.h>
#include "tio.h"
#include "xtalk/xwidgetdev.h"
#include "xtalk/hubdev.h"
#define CX_DEV_NONE 0
#define DEVICE_NAME "tiocx"
#define WIDGET_ID 0
#define TIOCX_DEBUG 0
#if TIOCX_DEBUG
#define DBG(fmt...) printk(KERN_ALERT fmt)
#else
#define DBG(fmt...)
#endif
struct device_attribute dev_attr_cxdev_control;
/**
* tiocx_match - Try to match driver id list with device.
* @dev: device pointer
* @drv: driver pointer
*
* Returns 1 if match, 0 otherwise.
*/
static int tiocx_match(struct device *dev, struct device_driver *drv)
{
struct cx_dev *cx_dev = to_cx_dev(dev);
struct cx_drv *cx_drv = to_cx_driver(drv);
const struct cx_device_id *ids = cx_drv->id_table;
if (!ids)
return 0;
while (ids->part_num) {
if (ids->part_num == cx_dev->cx_id.part_num)
return 1;
ids++;
}
return 0;
}
static int tiocx_uevent(struct device *dev, struct kobj_uevent_env *env)
{
return -ENODEV;
}
static void tiocx_bus_release(struct device *dev)
{
kfree(to_cx_dev(dev));
}
/**
* cx_device_match - Find cx_device in the id table.
* @ids: id table from driver
* @cx_device: part/mfg id for the device
*
*/
static const struct cx_device_id *cx_device_match(const struct cx_device_id
*ids,
struct cx_dev *cx_device)
{
/*
* NOTES: We may want to check for CX_ANY_ID too.
* Do we want to match against nasid too?
* CX_DEV_NONE == 0, if the driver tries to register for
* part/mfg == 0 we should return no-match (NULL) here.
*/
while (ids->part_num && ids->mfg_num) {
if (ids->part_num == cx_device->cx_id.part_num &&
ids->mfg_num == cx_device->cx_id.mfg_num)
return ids;
ids++;
}
return NULL;
}
/**
* cx_device_probe - Look for matching device.
* Call driver probe routine if found.
* @cx_driver: driver table (cx_drv struct) from driver
* @cx_device: part/mfg id for the device
*/
static int cx_device_probe(struct device *dev)
{
const struct cx_device_id *id;
struct cx_drv *cx_drv = to_cx_driver(dev->driver);
struct cx_dev *cx_dev = to_cx_dev(dev);
int error = 0;
if (!cx_dev->driver && cx_drv->probe) {
id = cx_device_match(cx_drv->id_table, cx_dev);
if (id) {
if ((error = cx_drv->probe(cx_dev, id)) < 0)
return error;
else
cx_dev->driver = cx_drv;
}
}
return error;
}
/**
* cx_driver_remove - Remove driver from device struct.
* @dev: device
*/
static int cx_driver_remove(struct device *dev)
{
struct cx_dev *cx_dev = to_cx_dev(dev);
struct cx_drv *cx_drv = cx_dev->driver;
if (cx_drv->remove)
cx_drv->remove(cx_dev);
cx_dev->driver = NULL;
return 0;
}
struct bus_type tiocx_bus_type = {
.name = "tiocx",
.match = tiocx_match,
.uevent = tiocx_uevent,
.probe = cx_device_probe,
.remove = cx_driver_remove,
};
/**
* cx_driver_register - Register the driver.
* @cx_driver: driver table (cx_drv struct) from driver
*
* Called from the driver init routine to register a driver.
* The cx_drv struct contains the driver name, a pointer to
* a table of part/mfg numbers and a pointer to the driver's
* probe/attach routine.
*/
int cx_driver_register(struct cx_drv *cx_driver)
{
cx_driver->driver.name = cx_driver->name;
cx_driver->driver.bus = &tiocx_bus_type;
return driver_register(&cx_driver->driver);
}
/**
* cx_driver_unregister - Unregister the driver.
* @cx_driver: driver table (cx_drv struct) from driver
*/
int cx_driver_unregister(struct cx_drv *cx_driver)
{
driver_unregister(&cx_driver->driver);
return 0;
}
/**
* cx_device_register - Register a device.
* @nasid: device's nasid
* @part_num: device's part number
* @mfg_num: device's manufacturer number
* @hubdev: hub info associated with this device
* @bt: board type of the device
*
*/
int
cx_device_register(nasid_t nasid, int part_num, int mfg_num,
struct hubdev_info *hubdev, int bt)
{
struct cx_dev *cx_dev;
int r;
cx_dev = kzalloc(sizeof(struct cx_dev), GFP_KERNEL);
DBG("cx_dev= 0x%p\n", cx_dev);
if (cx_dev == NULL)
return -ENOMEM;
cx_dev->cx_id.part_num = part_num;
cx_dev->cx_id.mfg_num = mfg_num;
cx_dev->cx_id.nasid = nasid;
cx_dev->hubdev = hubdev;
cx_dev->bt = bt;
cx_dev->dev.parent = NULL;
cx_dev->dev.bus = &tiocx_bus_type;
cx_dev->dev.release = tiocx_bus_release;
dev_set_name(&cx_dev->dev, "%d", cx_dev->cx_id.nasid);
r = device_register(&cx_dev->dev);
if (r) {
kfree(cx_dev);
return r;
}
get_device(&cx_dev->dev);
device_create_file(&cx_dev->dev, &dev_attr_cxdev_control);
return 0;
}
/**
* cx_device_unregister - Unregister a device.
* @cx_dev: part/mfg id for the device
*/
int cx_device_unregister(struct cx_dev *cx_dev)
{
put_device(&cx_dev->dev);
device_unregister(&cx_dev->dev);
return 0;
}
/**
* cx_device_reload - Reload the device.
* @nasid: device's nasid
* @part_num: device's part number
* @mfg_num: device's manufacturer number
*
* Remove the device associated with 'nasid' from device list and then
* call device-register with the given part/mfg numbers.
*/
static int cx_device_reload(struct cx_dev *cx_dev)
{
cx_device_unregister(cx_dev);
return cx_device_register(cx_dev->cx_id.nasid, cx_dev->cx_id.part_num,
cx_dev->cx_id.mfg_num, cx_dev->hubdev,
cx_dev->bt);
}
static inline u64 tiocx_intr_alloc(nasid_t nasid, int widget,
u64 sn_irq_info,
int req_irq, nasid_t req_nasid,
int req_slice)
{
struct ia64_sal_retval rv;
rv.status = 0;
rv.v0 = 0;
ia64_sal_oemcall_nolock(&rv, SN_SAL_IOIF_INTERRUPT,
SAL_INTR_ALLOC, nasid,
widget, sn_irq_info, req_irq,
req_nasid, req_slice);
return rv.status;
}
static inline void tiocx_intr_free(nasid_t nasid, int widget,
struct sn_irq_info *sn_irq_info)
{
struct ia64_sal_retval rv;
rv.status = 0;
rv.v0 = 0;
ia64_sal_oemcall_nolock(&rv, SN_SAL_IOIF_INTERRUPT,
SAL_INTR_FREE, nasid,
widget, sn_irq_info->irq_irq,
sn_irq_info->irq_cookie, 0, 0);
}
struct sn_irq_info *tiocx_irq_alloc(nasid_t nasid, int widget, int irq,
nasid_t req_nasid, int slice)
{
struct sn_irq_info *sn_irq_info;
int status;
int sn_irq_size = sizeof(struct sn_irq_info);
if ((nasid & 1) == 0)
return NULL;
sn_irq_info = kzalloc(sn_irq_size, GFP_KERNEL);
if (sn_irq_info == NULL)
return NULL;
status = tiocx_intr_alloc(nasid, widget, __pa(sn_irq_info), irq,
req_nasid, slice);
if (status) {
kfree(sn_irq_info);
return NULL;
} else {
return sn_irq_info;
}
}
void tiocx_irq_free(struct sn_irq_info *sn_irq_info)
{
u64 bridge = (u64) sn_irq_info->irq_bridge;
nasid_t nasid = NASID_GET(bridge);
int widget;
if (nasid & 1) {
widget = TIO_SWIN_WIDGETNUM(bridge);
tiocx_intr_free(nasid, widget, sn_irq_info);
kfree(sn_irq_info);
}
}
u64 tiocx_dma_addr(u64 addr)
{
return PHYS_TO_TIODMA(addr);
}
u64 tiocx_swin_base(int nasid)
{
return TIO_SWIN_BASE(nasid, TIOCX_CORELET);
}
EXPORT_SYMBOL(cx_driver_register);
EXPORT_SYMBOL(cx_driver_unregister);
EXPORT_SYMBOL(cx_device_register);
EXPORT_SYMBOL(cx_device_unregister);
EXPORT_SYMBOL(tiocx_irq_alloc);
EXPORT_SYMBOL(tiocx_irq_free);
EXPORT_SYMBOL(tiocx_bus_type);
EXPORT_SYMBOL(tiocx_dma_addr);
EXPORT_SYMBOL(tiocx_swin_base);
static void tio_conveyor_set(nasid_t nasid, int enable_flag)
{
u64 ice_frz;
u64 disable_cb = (1ull << 61);
if (!(nasid & 1))
return;
ice_frz = REMOTE_HUB_L(nasid, TIO_ICE_FRZ_CFG);
if (enable_flag) {
if (!(ice_frz & disable_cb)) /* already enabled */
return;
ice_frz &= ~disable_cb;
} else {
if (ice_frz & disable_cb) /* already disabled */
return;
ice_frz |= disable_cb;
}
DBG(KERN_ALERT "TIO_ICE_FRZ_CFG= 0x%lx\n", ice_frz);
REMOTE_HUB_S(nasid, TIO_ICE_FRZ_CFG, ice_frz);
}
#define tio_conveyor_enable(nasid) tio_conveyor_set(nasid, 1)
#define tio_conveyor_disable(nasid) tio_conveyor_set(nasid, 0)
static void tio_corelet_reset(nasid_t nasid, int corelet)
{
if (!(nasid & 1))
return;
REMOTE_HUB_S(nasid, TIO_ICE_PMI_TX_CFG, 1 << corelet);
udelay(2000);
REMOTE_HUB_S(nasid, TIO_ICE_PMI_TX_CFG, 0);
udelay(2000);
}
static int is_fpga_tio(int nasid, int *bt)
{
u16 uninitialized_var(ioboard_type); /* GCC be quiet */
long rc;
rc = ia64_sn_sysctl_ioboard_get(nasid, &ioboard_type);
if (rc) {
printk(KERN_WARNING "ia64_sn_sysctl_ioboard_get failed: %ld\n",
rc);
return 0;
}
switch (ioboard_type) {
case L1_BRICKTYPE_SA:
case L1_BRICKTYPE_ATHENA:
case L1_BOARDTYPE_DAYTONA:
*bt = ioboard_type;
return 1;
}
return 0;
}
static int bitstream_loaded(nasid_t nasid)
{
u64 cx_credits;
cx_credits = REMOTE_HUB_L(nasid, TIO_ICE_PMI_TX_DYN_CREDIT_STAT_CB3);
cx_credits &= TIO_ICE_PMI_TX_DYN_CREDIT_STAT_CB3_CREDIT_CNT_MASK;
DBG("cx_credits= 0x%lx\n", cx_credits);
return (cx_credits == 0xf) ? 1 : 0;
}
static int tiocx_reload(struct cx_dev *cx_dev)
{
int part_num = CX_DEV_NONE;
int mfg_num = CX_DEV_NONE;
nasid_t nasid = cx_dev->cx_id.nasid;
if (bitstream_loaded(nasid)) {
u64 cx_id;
int rv;
rv = ia64_sn_sysctl_tio_clock_reset(nasid);
if (rv) {
printk(KERN_ALERT "CX port JTAG reset failed.\n");
} else {
cx_id = *(volatile u64 *)
(TIO_SWIN_BASE(nasid, TIOCX_CORELET) +
WIDGET_ID);
part_num = XWIDGET_PART_NUM(cx_id);
mfg_num = XWIDGET_MFG_NUM(cx_id);
DBG("part= 0x%x, mfg= 0x%x\n", part_num, mfg_num);
/* just ignore it if it's a CE */
if (part_num == TIO_CE_ASIC_PARTNUM)
return 0;
}
}
cx_dev->cx_id.part_num = part_num;
cx_dev->cx_id.mfg_num = mfg_num;
/*
* Delete old device and register the new one. It's ok if
* part_num/mfg_num == CX_DEV_NONE. We want to register
* devices in the table even if a bitstream isn't loaded.
* That allows use to see that a bitstream isn't loaded via
* TIOCX_IOCTL_DEV_LIST.
*/
return cx_device_reload(cx_dev);
}
static ssize_t show_cxdev_control(struct device *dev, struct device_attribute *attr, char *buf)
{
struct cx_dev *cx_dev = to_cx_dev(dev);
return sprintf(buf, "0x%x 0x%x 0x%x 0x%x\n",
cx_dev->cx_id.nasid,
cx_dev->cx_id.part_num, cx_dev->cx_id.mfg_num,
cx_dev->bt);
}
static ssize_t store_cxdev_control(struct device *dev, struct device_attribute *attr, const char *buf,
size_t count)
{
int n;
struct cx_dev *cx_dev = to_cx_dev(dev);
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (count <= 0)
return 0;
n = simple_strtoul(buf, NULL, 0);
switch (n) {
case 1:
tio_corelet_reset(cx_dev->cx_id.nasid, TIOCX_CORELET);
tiocx_reload(cx_dev);
break;
case 2:
tiocx_reload(cx_dev);
break;
case 3:
tio_corelet_reset(cx_dev->cx_id.nasid, TIOCX_CORELET);
break;
default:
break;
}
return count;
}
DEVICE_ATTR(cxdev_control, 0644, show_cxdev_control, store_cxdev_control);
static int __init tiocx_init(void)
{
cnodeid_t cnodeid;
int found_tiocx_device = 0;
if (!ia64_platform_is("sn2"))
return 0;
bus_register(&tiocx_bus_type);
for (cnodeid = 0; cnodeid < num_cnodes; cnodeid++) {
nasid_t nasid;
int bt;
nasid = cnodeid_to_nasid(cnodeid);
if ((nasid & 0x1) && is_fpga_tio(nasid, &bt)) {
struct hubdev_info *hubdev;
struct xwidget_info *widgetp;
DBG("Found TIO at nasid 0x%x\n", nasid);
hubdev =
(struct hubdev_info *)(NODEPDA(cnodeid)->pdinfo);
widgetp = &hubdev->hdi_xwidget_info[TIOCX_CORELET];
/* The CE hangs off of the CX port but is not an FPGA */
if (widgetp->xwi_hwid.part_num == TIO_CE_ASIC_PARTNUM)
continue;
tio_corelet_reset(nasid, TIOCX_CORELET);
tio_conveyor_enable(nasid);
if (cx_device_register
(nasid, widgetp->xwi_hwid.part_num,
widgetp->xwi_hwid.mfg_num, hubdev, bt) < 0)
return -ENXIO;
else
found_tiocx_device++;
}
}
/* It's ok if we find zero devices. */
DBG("found_tiocx_device= %d\n", found_tiocx_device);
return 0;
}
static int cx_remove_device(struct device * dev, void * data)
{
struct cx_dev *cx_dev = to_cx_dev(dev);
device_remove_file(dev, &dev_attr_cxdev_control);
cx_device_unregister(cx_dev);
return 0;
}
static void __exit tiocx_exit(void)
{
DBG("tiocx_exit\n");
/*
* Unregister devices.
*/
bus_for_each_dev(&tiocx_bus_type, NULL, NULL, cx_remove_device);
bus_unregister(&tiocx_bus_type);
}
fs_initcall(tiocx_init);
module_exit(tiocx_exit);
/************************************************************************
* Module licensing and description
************************************************************************/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Bruce Losure <blosure@sgi.com>");
MODULE_DESCRIPTION("TIOCX module");
MODULE_SUPPORTED_DEVICE(DEVICE_NAME);