mii-bitbang.c 8.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/*
 * Combined Ethernet driver for Motorola MPC8xx and MPC82xx.
 *
 * Copyright (c) 2003 Intracom S.A. 
 *  by Pantelis Antoniou <panto@intracom.gr>
 * 
 * 2005 (c) MontaVista Software, Inc. 
 * Vitaly Bordug <vbordug@ru.mvista.com>
 *
 * This file is licensed under the terms of the GNU General Public License 
 * version 2. This program is licensed "as is" without any warranty of any 
 * kind, whether express or implied.
 */


#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/ioport.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/mii.h>
#include <linux/ethtool.h>
#include <linux/bitops.h>
34
#include <linux/platform_device.h>
35 36 37 38 39 40 41

#include <asm/pgtable.h>
#include <asm/irq.h>
#include <asm/uaccess.h>

#include "fs_enet.h"

42 43
static int bitbang_prep_bit(u8 **datp, u8 *mskp,
		struct fs_mii_bit *mii_bit)
44
{
45
	void *dat;
46 47 48
	int adv;
	u8 msk;

49
	dat = (void*) mii_bit->offset;
50

51
	adv = mii_bit->bit >> 3;
52 53
	dat = (char *)dat + adv;

54
	msk = 1 << (7 - (mii_bit->bit & 7));
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

	*datp = dat;
	*mskp = msk;

	return 0;
}

static inline void bb_set(u8 *p, u8 m)
{
	out_8(p, in_8(p) | m);
}

static inline void bb_clr(u8 *p, u8 m)
{
	out_8(p, in_8(p) & ~m);
}

static inline int bb_read(u8 *p, u8 m)
{
	return (in_8(p) & m) != 0;
}

77
static inline void mdio_active(struct bb_info *bitbang)
78
{
79
	bb_set(bitbang->mdio_dir, bitbang->mdio_dir_msk);
80 81
}

82
static inline void mdio_tristate(struct bb_info *bitbang )
83
{
84
	bb_clr(bitbang->mdio_dir, bitbang->mdio_dir_msk);
85 86
}

87
static inline int mdio_read(struct bb_info *bitbang )
88
{
89
	return bb_read(bitbang->mdio_dat, bitbang->mdio_dat_msk);
90 91
}

92
static inline void mdio(struct bb_info *bitbang , int what)
93 94
{
	if (what)
95
		bb_set(bitbang->mdio_dat, bitbang->mdio_dat_msk);
96
	else
97
		bb_clr(bitbang->mdio_dat, bitbang->mdio_dat_msk);
98 99
}

100
static inline void mdc(struct bb_info *bitbang , int what)
101 102
{
	if (what)
103
		bb_set(bitbang->mdc_dat, bitbang->mdc_msk);
104
	else
105
		bb_clr(bitbang->mdc_dat, bitbang->mdc_msk);
106 107
}

108
static inline void mii_delay(struct bb_info *bitbang )
109
{
110
	udelay(bitbang->delay);
111 112 113
}

/* Utility to send the preamble, address, and register (common to read and write). */
114
static void bitbang_pre(struct bb_info *bitbang , int read, u8 addr, u8 reg)
115 116 117 118 119 120 121 122 123 124 125
{
	int j;

	/*
	 * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure.
	 * The IEEE spec says this is a PHY optional requirement.  The AMD
	 * 79C874 requires one after power up and one after a MII communications
	 * error.  This means that we are doing more preambles than we need,
	 * but it is safer and will be much more robust.
	 */

126 127
	mdio_active(bitbang);
	mdio(bitbang, 1);
128
	for (j = 0; j < 32; j++) {
129 130 131 132
		mdc(bitbang, 0);
		mii_delay(bitbang);
		mdc(bitbang, 1);
		mii_delay(bitbang);
133 134 135
	}

	/* send the start bit (01) and the read opcode (10) or write (10) */
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
	mdc(bitbang, 0);
	mdio(bitbang, 0);
	mii_delay(bitbang);
	mdc(bitbang, 1);
	mii_delay(bitbang);
	mdc(bitbang, 0);
	mdio(bitbang, 1);
	mii_delay(bitbang);
	mdc(bitbang, 1);
	mii_delay(bitbang);
	mdc(bitbang, 0);
	mdio(bitbang, read);
	mii_delay(bitbang);
	mdc(bitbang, 1);
	mii_delay(bitbang);
	mdc(bitbang, 0);
	mdio(bitbang, !read);
	mii_delay(bitbang);
	mdc(bitbang, 1);
	mii_delay(bitbang);
156 157 158

	/* send the PHY address */
	for (j = 0; j < 5; j++) {
159 160 161 162 163
		mdc(bitbang, 0);
		mdio(bitbang, (addr & 0x10) != 0);
		mii_delay(bitbang);
		mdc(bitbang, 1);
		mii_delay(bitbang);
164 165 166 167 168
		addr <<= 1;
	}

	/* send the register address */
	for (j = 0; j < 5; j++) {
169 170 171 172 173
		mdc(bitbang, 0);
		mdio(bitbang, (reg & 0x10) != 0);
		mii_delay(bitbang);
		mdc(bitbang, 1);
		mii_delay(bitbang);
174 175 176 177
		reg <<= 1;
	}
}

178
static int fs_enet_mii_bb_read(struct mii_bus *bus , int phy_id, int location)
179 180 181 182 183
{
	u16 rdreg;
	int ret, j;
	u8 addr = phy_id & 0xff;
	u8 reg = location & 0xff;
184
	struct bb_info* bitbang = bus->priv;
185

186
	bitbang_pre(bitbang, 1, addr, reg);
187 188

	/* tri-state our MDIO I/O pin so we can read */
189 190 191 192 193
	mdc(bitbang, 0);
	mdio_tristate(bitbang);
	mii_delay(bitbang);
	mdc(bitbang, 1);
	mii_delay(bitbang);
194 195

	/* check the turnaround bit: the PHY should be driving it to zero */
196
	if (mdio_read(bitbang) != 0) {
197 198
		/* PHY didn't drive TA low */
		for (j = 0; j < 32; j++) {
199 200 201 202
			mdc(bitbang, 0);
			mii_delay(bitbang);
			mdc(bitbang, 1);
			mii_delay(bitbang);
203 204 205 206 207
		}
		ret = -1;
		goto out;
	}

208 209
	mdc(bitbang, 0);
	mii_delay(bitbang);
210 211 212 213

	/* read 16 bits of register data, MSB first */
	rdreg = 0;
	for (j = 0; j < 16; j++) {
214 215
		mdc(bitbang, 1);
		mii_delay(bitbang);
216
		rdreg <<= 1;
217 218 219
		rdreg |= mdio_read(bitbang);
		mdc(bitbang, 0);
		mii_delay(bitbang);
220 221
	}

222 223 224 225 226 227
	mdc(bitbang, 1);
	mii_delay(bitbang);
	mdc(bitbang, 0);
	mii_delay(bitbang);
	mdc(bitbang, 1);
	mii_delay(bitbang);
228 229 230 231 232 233

	ret = rdreg;
out:
	return ret;
}

234
static int fs_enet_mii_bb_write(struct mii_bus *bus, int phy_id, int location, u16 val)
235 236
{
	int j;
237 238
	struct bb_info* bitbang = bus->priv;

239 240 241 242
	u8 addr = phy_id & 0xff;
	u8 reg = location & 0xff;
	u16 value = val & 0xffff;

243
	bitbang_pre(bitbang, 0, addr, reg);
244 245

	/* send the turnaround (10) */
246 247 248 249 250 251 252 253 254 255
	mdc(bitbang, 0);
	mdio(bitbang, 1);
	mii_delay(bitbang);
	mdc(bitbang, 1);
	mii_delay(bitbang);
	mdc(bitbang, 0);
	mdio(bitbang, 0);
	mii_delay(bitbang);
	mdc(bitbang, 1);
	mii_delay(bitbang);
256 257 258

	/* write 16 bits of register data, MSB first */
	for (j = 0; j < 16; j++) {
259 260 261 262 263
		mdc(bitbang, 0);
		mdio(bitbang, (value & 0x8000) != 0);
		mii_delay(bitbang);
		mdc(bitbang, 1);
		mii_delay(bitbang);
264 265 266 267 268 269
		value <<= 1;
	}

	/*
	 * Tri-state the MDIO line.
	 */
270 271 272 273 274 275
	mdio_tristate(bitbang);
	mdc(bitbang, 0);
	mii_delay(bitbang);
	mdc(bitbang, 1);
	mii_delay(bitbang);
	return 0;
276 277
}

278 279 280 281 282 283 284
static int fs_enet_mii_bb_reset(struct mii_bus *bus)
{
	/*nothing here - dunno how to reset it*/
	return 0;
}

static int fs_mii_bitbang_init(struct bb_info *bitbang, struct fs_mii_bb_platform_info* fmpi)
285 286 287
{
	int r;

288 289 290 291 292
	bitbang->delay = fmpi->delay;

	r = bitbang_prep_bit(&bitbang->mdio_dir,
			 &bitbang->mdio_dir_msk,
			 &fmpi->mdio_dir);
293 294 295
	if (r != 0)
		return r;

296 297 298
	r = bitbang_prep_bit(&bitbang->mdio_dat,
			 &bitbang->mdio_dat_msk,
			 &fmpi->mdio_dat);
299 300 301
	if (r != 0)
		return r;

302 303 304 305 306
	r = bitbang_prep_bit(&bitbang->mdc_dat,
			 &bitbang->mdc_msk,
			 &fmpi->mdc_dat);
	if (r != 0)
		return r;
307 308 309

	return 0;
}
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406


static int __devinit fs_enet_mdio_probe(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct fs_mii_bb_platform_info *pdata;
	struct mii_bus *new_bus;
	struct bb_info *bitbang;
	int err = 0;

	if (NULL == dev)
		return -EINVAL;

	new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);

	if (NULL == new_bus)
		return -ENOMEM;

	bitbang = kzalloc(sizeof(struct bb_info), GFP_KERNEL);

	if (NULL == bitbang)
		return -ENOMEM;

	new_bus->name = "BB MII Bus",
	new_bus->read = &fs_enet_mii_bb_read,
	new_bus->write = &fs_enet_mii_bb_write,
	new_bus->reset = &fs_enet_mii_bb_reset,
	new_bus->id = pdev->id;

	new_bus->phy_mask = ~0x9;
	pdata = (struct fs_mii_bb_platform_info *)pdev->dev.platform_data;

	if (NULL == pdata) {
		printk(KERN_ERR "gfar mdio %d: Missing platform data!\n", pdev->id);
		return -ENODEV;
	}

	/*set up workspace*/
	fs_mii_bitbang_init(bitbang, pdata);

	new_bus->priv = bitbang;

	new_bus->irq = pdata->irq;

	new_bus->dev = dev;
	dev_set_drvdata(dev, new_bus);

	err = mdiobus_register(new_bus);

	if (0 != err) {
		printk (KERN_ERR "%s: Cannot register as MDIO bus\n",
				new_bus->name);
		goto bus_register_fail;
	}

	return 0;

bus_register_fail:
	kfree(bitbang);
	kfree(new_bus);

	return err;
}


static int fs_enet_mdio_remove(struct device *dev)
{
	struct mii_bus *bus = dev_get_drvdata(dev);

	mdiobus_unregister(bus);

	dev_set_drvdata(dev, NULL);

	iounmap((void *) (&bus->priv));
	bus->priv = NULL;
	kfree(bus);

	return 0;
}

static struct device_driver fs_enet_bb_mdio_driver = {
	.name = "fsl-bb-mdio",
	.bus = &platform_bus_type,
	.probe = fs_enet_mdio_probe,
	.remove = fs_enet_mdio_remove,
};

int fs_enet_mdio_bb_init(void)
{
	return driver_register(&fs_enet_bb_mdio_driver);
}

void fs_enet_mdio_bb_exit(void)
{
	driver_unregister(&fs_enet_bb_mdio_driver);
}