| David Gibson | 1d3bb99 | 2007-08-23 13:56:01 +1000 | [diff] [blame] | 1 | /* | 
|  | 2 | * drivers/net/ibm_newemac/tah.c | 
|  | 3 | * | 
|  | 4 | * Driver for PowerPC 4xx on-chip ethernet controller, TAH support. | 
|  | 5 | * | 
| Benjamin Herrenschmidt | 17cf803 | 2007-12-05 11:14:33 +1100 | [diff] [blame] | 6 | * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. | 
|  | 7 | *                <benh@kernel.crashing.org> | 
|  | 8 | * | 
|  | 9 | * Based on the arch/ppc version of the driver: | 
|  | 10 | * | 
| David Gibson | 1d3bb99 | 2007-08-23 13:56:01 +1000 | [diff] [blame] | 11 | * Copyright 2004 MontaVista Software, Inc. | 
|  | 12 | * Matt Porter <mporter@kernel.crashing.org> | 
|  | 13 | * | 
|  | 14 | * Copyright (c) 2005 Eugene Surovegin <ebs@ebshome.net> | 
|  | 15 | * | 
|  | 16 | * This program is free software; you can redistribute  it and/or modify it | 
|  | 17 | * under  the terms of  the GNU General  Public License as published by the | 
|  | 18 | * Free Software Foundation;  either version 2 of the  License, or (at your | 
|  | 19 | * option) any later version. | 
|  | 20 | */ | 
|  | 21 | #include <asm/io.h> | 
|  | 22 |  | 
|  | 23 | #include "emac.h" | 
|  | 24 | #include "core.h" | 
|  | 25 |  | 
|  | 26 | int __devinit tah_attach(struct of_device *ofdev, int channel) | 
|  | 27 | { | 
|  | 28 | struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); | 
|  | 29 |  | 
|  | 30 | mutex_lock(&dev->lock); | 
|  | 31 | /* Reset has been done at probe() time... nothing else to do for now */ | 
|  | 32 | ++dev->users; | 
|  | 33 | mutex_unlock(&dev->lock); | 
|  | 34 |  | 
|  | 35 | return 0; | 
|  | 36 | } | 
|  | 37 |  | 
| Josh Boyer | 51d4a1c | 2008-04-22 10:46:43 +1000 | [diff] [blame] | 38 | void tah_detach(struct of_device *ofdev, int channel) | 
| David Gibson | 1d3bb99 | 2007-08-23 13:56:01 +1000 | [diff] [blame] | 39 | { | 
|  | 40 | struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); | 
|  | 41 |  | 
|  | 42 | mutex_lock(&dev->lock); | 
|  | 43 | --dev->users; | 
|  | 44 | mutex_unlock(&dev->lock); | 
|  | 45 | } | 
|  | 46 |  | 
|  | 47 | void tah_reset(struct of_device *ofdev) | 
|  | 48 | { | 
|  | 49 | struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); | 
| Al Viro | eb4d84f | 2007-10-14 19:36:10 +0100 | [diff] [blame] | 50 | struct tah_regs __iomem *p = dev->base; | 
| David Gibson | 1d3bb99 | 2007-08-23 13:56:01 +1000 | [diff] [blame] | 51 | int n; | 
|  | 52 |  | 
|  | 53 | /* Reset TAH */ | 
|  | 54 | out_be32(&p->mr, TAH_MR_SR); | 
|  | 55 | n = 100; | 
|  | 56 | while ((in_be32(&p->mr) & TAH_MR_SR) && n) | 
|  | 57 | --n; | 
|  | 58 |  | 
|  | 59 | if (unlikely(!n)) | 
|  | 60 | printk(KERN_ERR "%s: reset timeout\n", ofdev->node->full_name); | 
|  | 61 |  | 
|  | 62 | /* 10KB TAH TX FIFO accomodates the max MTU of 9000 */ | 
|  | 63 | out_be32(&p->mr, | 
|  | 64 | TAH_MR_CVR | TAH_MR_ST_768 | TAH_MR_TFS_10KB | TAH_MR_DTFP | | 
|  | 65 | TAH_MR_DIG); | 
|  | 66 | } | 
|  | 67 |  | 
|  | 68 | int tah_get_regs_len(struct of_device *ofdev) | 
|  | 69 | { | 
|  | 70 | return sizeof(struct emac_ethtool_regs_subhdr) + | 
|  | 71 | sizeof(struct tah_regs); | 
|  | 72 | } | 
|  | 73 |  | 
|  | 74 | void *tah_dump_regs(struct of_device *ofdev, void *buf) | 
|  | 75 | { | 
|  | 76 | struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); | 
|  | 77 | struct emac_ethtool_regs_subhdr *hdr = buf; | 
|  | 78 | struct tah_regs *regs = (struct tah_regs *)(hdr + 1); | 
|  | 79 |  | 
|  | 80 | hdr->version = 0; | 
|  | 81 | hdr->index = 0; /* for now, are there chips with more than one | 
|  | 82 | * zmii ? if yes, then we'll add a cell_index | 
|  | 83 | * like we do for emac | 
|  | 84 | */ | 
|  | 85 | memcpy_fromio(regs, dev->base, sizeof(struct tah_regs)); | 
|  | 86 | return regs + 1; | 
|  | 87 | } | 
|  | 88 |  | 
|  | 89 | static int __devinit tah_probe(struct of_device *ofdev, | 
|  | 90 | const struct of_device_id *match) | 
|  | 91 | { | 
|  | 92 | struct device_node *np = ofdev->node; | 
|  | 93 | struct tah_instance *dev; | 
|  | 94 | struct resource regs; | 
|  | 95 | int rc; | 
|  | 96 |  | 
|  | 97 | rc = -ENOMEM; | 
|  | 98 | dev = kzalloc(sizeof(struct tah_instance), GFP_KERNEL); | 
|  | 99 | if (dev == NULL) { | 
|  | 100 | printk(KERN_ERR "%s: could not allocate TAH device!\n", | 
|  | 101 | np->full_name); | 
|  | 102 | goto err_gone; | 
|  | 103 | } | 
|  | 104 |  | 
|  | 105 | mutex_init(&dev->lock); | 
|  | 106 | dev->ofdev = ofdev; | 
|  | 107 |  | 
|  | 108 | rc = -ENXIO; | 
|  | 109 | if (of_address_to_resource(np, 0, ®s)) { | 
|  | 110 | printk(KERN_ERR "%s: Can't get registers address\n", | 
|  | 111 | np->full_name); | 
|  | 112 | goto err_free; | 
|  | 113 | } | 
|  | 114 |  | 
|  | 115 | rc = -ENOMEM; | 
| Al Viro | eb4d84f | 2007-10-14 19:36:10 +0100 | [diff] [blame] | 116 | dev->base = (struct tah_regs __iomem *)ioremap(regs.start, | 
| David Gibson | 1d3bb99 | 2007-08-23 13:56:01 +1000 | [diff] [blame] | 117 | sizeof(struct tah_regs)); | 
|  | 118 | if (dev->base == NULL) { | 
|  | 119 | printk(KERN_ERR "%s: Can't map device registers!\n", | 
|  | 120 | np->full_name); | 
|  | 121 | goto err_free; | 
|  | 122 | } | 
|  | 123 |  | 
| Valentine Barshak | d09e18b | 2007-12-05 11:14:32 +1100 | [diff] [blame] | 124 | dev_set_drvdata(&ofdev->dev, dev); | 
|  | 125 |  | 
| David Gibson | 1d3bb99 | 2007-08-23 13:56:01 +1000 | [diff] [blame] | 126 | /* Initialize TAH and enable IPv4 checksum verification, no TSO yet */ | 
|  | 127 | tah_reset(ofdev); | 
|  | 128 |  | 
|  | 129 | printk(KERN_INFO | 
|  | 130 | "TAH %s initialized\n", ofdev->node->full_name); | 
|  | 131 | wmb(); | 
| David Gibson | 1d3bb99 | 2007-08-23 13:56:01 +1000 | [diff] [blame] | 132 |  | 
|  | 133 | return 0; | 
|  | 134 |  | 
|  | 135 | err_free: | 
|  | 136 | kfree(dev); | 
|  | 137 | err_gone: | 
|  | 138 | return rc; | 
|  | 139 | } | 
|  | 140 |  | 
|  | 141 | static int __devexit tah_remove(struct of_device *ofdev) | 
|  | 142 | { | 
|  | 143 | struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); | 
|  | 144 |  | 
|  | 145 | dev_set_drvdata(&ofdev->dev, NULL); | 
|  | 146 |  | 
|  | 147 | WARN_ON(dev->users != 0); | 
|  | 148 |  | 
|  | 149 | iounmap(dev->base); | 
|  | 150 | kfree(dev); | 
|  | 151 |  | 
|  | 152 | return 0; | 
|  | 153 | } | 
|  | 154 |  | 
|  | 155 | static struct of_device_id tah_match[] = | 
|  | 156 | { | 
|  | 157 | { | 
| Stefan Roese | cdb3469 | 2008-03-13 16:59:43 +0100 | [diff] [blame] | 158 | .compatible	= "ibm,tah", | 
|  | 159 | }, | 
|  | 160 | /* For backward compat with old DT */ | 
|  | 161 | { | 
| David Gibson | 1d3bb99 | 2007-08-23 13:56:01 +1000 | [diff] [blame] | 162 | .type		= "tah", | 
|  | 163 | }, | 
|  | 164 | {}, | 
|  | 165 | }; | 
|  | 166 |  | 
|  | 167 | static struct of_platform_driver tah_driver = { | 
|  | 168 | .name = "emac-tah", | 
|  | 169 | .match_table = tah_match, | 
|  | 170 |  | 
|  | 171 | .probe = tah_probe, | 
|  | 172 | .remove = tah_remove, | 
|  | 173 | }; | 
|  | 174 |  | 
|  | 175 | int __init tah_init(void) | 
|  | 176 | { | 
|  | 177 | return of_register_platform_driver(&tah_driver); | 
|  | 178 | } | 
|  | 179 |  | 
|  | 180 | void tah_exit(void) | 
|  | 181 | { | 
|  | 182 | of_unregister_platform_driver(&tah_driver); | 
|  | 183 | } |