104 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			104 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			C
		
	
	
	
| // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
 | |
| /*
 | |
|  * Copyright (c) 2018 Microsemi Corporation
 | |
|  */
 | |
| 
 | |
| #include <miiphy.h>
 | |
| #include <wait_bit.h>
 | |
| #include <linux/bitops.h>
 | |
| #include "mscc_miim.h"
 | |
| 
 | |
| #define MIIM_STATUS			0x0
 | |
| #define		MIIM_STAT_BUSY			BIT(3)
 | |
| #define MIIM_CMD			0x8
 | |
| #define		MIIM_CMD_SCAN		BIT(0)
 | |
| #define		MIIM_CMD_OPR_WRITE	BIT(1)
 | |
| #define		MIIM_CMD_OPR_READ	BIT(2)
 | |
| #define		MIIM_CMD_SINGLE_SCAN	BIT(3)
 | |
| #define		MIIM_CMD_WRDATA(x)	((x) << 4)
 | |
| #define		MIIM_CMD_REGAD(x)	((x) << 20)
 | |
| #define		MIIM_CMD_PHYAD(x)	((x) << 25)
 | |
| #define		MIIM_CMD_VLD		BIT(31)
 | |
| #define MIIM_DATA			0xC
 | |
| #define		MIIM_DATA_ERROR		(0x2 << 16)
 | |
| 
 | |
| static int mscc_miim_wait_ready(struct mscc_miim_dev *miim)
 | |
| {
 | |
| 	return wait_for_bit_le32(miim->regs + MIIM_STATUS, MIIM_STAT_BUSY,
 | |
| 				 false, 250, false);
 | |
| }
 | |
| 
 | |
| int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg)
 | |
| {
 | |
| 	struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv;
 | |
| 	u32 val;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = mscc_miim_wait_ready(miim);
 | |
| 	if (ret)
 | |
| 		goto out;
 | |
| 
 | |
| 	writel(MIIM_CMD_VLD | MIIM_CMD_PHYAD(addr) |
 | |
| 	       MIIM_CMD_REGAD(reg) | MIIM_CMD_OPR_READ,
 | |
| 	       miim->regs + MIIM_CMD);
 | |
| 
 | |
| 	ret = mscc_miim_wait_ready(miim);
 | |
| 	if (ret)
 | |
| 		goto out;
 | |
| 
 | |
| 	val = readl(miim->regs + MIIM_DATA);
 | |
| 	if (val & MIIM_DATA_ERROR) {
 | |
| 		ret = -EIO;
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	ret = val & 0xFFFF;
 | |
|  out:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg,
 | |
| 		    u16 val)
 | |
| {
 | |
| 	struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = mscc_miim_wait_ready(miim);
 | |
| 	if (ret < 0)
 | |
| 		goto out;
 | |
| 
 | |
| 	writel(MIIM_CMD_VLD | MIIM_CMD_PHYAD(addr) |
 | |
| 	       MIIM_CMD_REGAD(reg) | MIIM_CMD_WRDATA(val) |
 | |
| 	       MIIM_CMD_OPR_WRITE, miim->regs + MIIM_CMD);
 | |
|  out:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| struct mii_dev *mscc_mdiobus_init(struct mscc_miim_dev *miim, int *miim_count,
 | |
| 				  phys_addr_t miim_base,
 | |
| 				  unsigned long miim_size)
 | |
| {
 | |
| 	struct mii_dev *bus;
 | |
| 
 | |
| 	bus = mdio_alloc();
 | |
| 
 | |
| 	if (!bus)
 | |
| 		return NULL;
 | |
| 
 | |
| 	*miim_count += 1;
 | |
| 	sprintf(bus->name, "miim-bus%d", *miim_count);
 | |
| 
 | |
| 	miim[*miim_count].regs = ioremap(miim_base, miim_size);
 | |
| 	miim[*miim_count].miim_base = miim_base;
 | |
| 	miim[*miim_count].miim_size = miim_size;
 | |
| 	bus->priv = &miim[*miim_count];
 | |
| 	bus->read = mscc_miim_read;
 | |
| 	bus->write = mscc_miim_write;
 | |
| 
 | |
| 	if (mdio_register(bus))
 | |
| 		return NULL;
 | |
| 
 | |
| 	miim[*miim_count].bus = bus;
 | |
| 	return bus;
 | |
| }
 |