125d967b72a92d72b6e0263a0337dfc940bd6c044David Daney/*
225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney * This file is subject to the terms and conditions of the GNU General Public
325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney * License.  See the file "COPYING" in the main directory of this archive
425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney * for more details.
525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney *
625d967b72a92d72b6e0263a0337dfc940bd6c044David Daney * Copyright (C) 2009 Cavium Networks
725d967b72a92d72b6e0263a0337dfc940bd6c044David Daney */
825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
95a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/gfp.h>
1025d967b72a92d72b6e0263a0337dfc940bd6c044David Daney#include <linux/init.h>
1125d967b72a92d72b6e0263a0337dfc940bd6c044David Daney#include <linux/module.h>
1225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney#include <linux/platform_device.h>
1325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney#include <linux/phy.h>
1425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
1525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney#include <asm/octeon/octeon.h>
1625d967b72a92d72b6e0263a0337dfc940bd6c044David Daney#include <asm/octeon/cvmx-smix-defs.h>
1725d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
1825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney#define DRV_VERSION "1.0"
1925d967b72a92d72b6e0263a0337dfc940bd6c044David Daney#define DRV_DESCRIPTION "Cavium Networks Octeon SMI/MDIO driver"
2025d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
2125d967b72a92d72b6e0263a0337dfc940bd6c044David Daneystruct octeon_mdiobus {
2225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	struct mii_bus *mii_bus;
2325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	int unit;
2425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	int phy_irq[PHY_MAX_ADDR];
2525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney};
2625d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
2725d967b72a92d72b6e0263a0337dfc940bd6c044David Daneystatic int octeon_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum)
2825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney{
2925d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	struct octeon_mdiobus *p = bus->priv;
3025d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	union cvmx_smix_cmd smi_cmd;
3125d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	union cvmx_smix_rd_dat smi_rd;
3225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	int timeout = 1000;
3325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
3425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	smi_cmd.u64 = 0;
3525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	smi_cmd.s.phy_op = 1; /* MDIO_CLAUSE_22_READ */
3625d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	smi_cmd.s.phy_adr = phy_id;
3725d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	smi_cmd.s.reg_adr = regnum;
3825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	cvmx_write_csr(CVMX_SMIX_CMD(p->unit), smi_cmd.u64);
3925d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
4025d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	do {
4125d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		/*
4225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		 * Wait 1000 clocks so we don't saturate the RSL bus
4325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		 * doing reads.
4425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		 */
4525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		cvmx_wait(1000);
4625d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		smi_rd.u64 = cvmx_read_csr(CVMX_SMIX_RD_DAT(p->unit));
4725d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	} while (smi_rd.s.pending && --timeout);
4825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
4925d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	if (smi_rd.s.val)
5025d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		return smi_rd.s.dat;
5125d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	else
5225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		return -EIO;
5325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney}
5425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
5525d967b72a92d72b6e0263a0337dfc940bd6c044David Daneystatic int octeon_mdiobus_write(struct mii_bus *bus, int phy_id,
5625d967b72a92d72b6e0263a0337dfc940bd6c044David Daney				int regnum, u16 val)
5725d967b72a92d72b6e0263a0337dfc940bd6c044David Daney{
5825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	struct octeon_mdiobus *p = bus->priv;
5925d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	union cvmx_smix_cmd smi_cmd;
6025d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	union cvmx_smix_wr_dat smi_wr;
6125d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	int timeout = 1000;
6225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
6325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	smi_wr.u64 = 0;
6425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	smi_wr.s.dat = val;
6525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	cvmx_write_csr(CVMX_SMIX_WR_DAT(p->unit), smi_wr.u64);
6625d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
6725d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	smi_cmd.u64 = 0;
6825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_22_WRITE */
6925d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	smi_cmd.s.phy_adr = phy_id;
7025d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	smi_cmd.s.reg_adr = regnum;
7125d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	cvmx_write_csr(CVMX_SMIX_CMD(p->unit), smi_cmd.u64);
7225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
7325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	do {
7425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		/*
7525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		 * Wait 1000 clocks so we don't saturate the RSL bus
7625d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		 * doing reads.
7725d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		 */
7825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		cvmx_wait(1000);
7925d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(p->unit));
8025d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	} while (smi_wr.s.pending && --timeout);
8125d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
8225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	if (timeout <= 0)
8325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		return -EIO;
8425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
8525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	return 0;
8625d967b72a92d72b6e0263a0337dfc940bd6c044David Daney}
8725d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
88a71e8329170ece5f684aa363dfa69828cbfd5184David Daneystatic int __devinit octeon_mdiobus_probe(struct platform_device *pdev)
8925d967b72a92d72b6e0263a0337dfc940bd6c044David Daney{
9025d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	struct octeon_mdiobus *bus;
916c17812d622a74950e2cd65f368f0518491cca61David Daney	union cvmx_smix_en smi_en;
9225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	int i;
9325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	int err = -ENOENT;
9425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
9525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
9625d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	if (!bus)
9725d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		return -ENOMEM;
9825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
9925d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	/* The platform_device id is our unit number.  */
10025d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	bus->unit = pdev->id;
10125d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
10225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	bus->mii_bus = mdiobus_alloc();
10325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
10425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	if (!bus->mii_bus)
10525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		goto err;
10625d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
1076c17812d622a74950e2cd65f368f0518491cca61David Daney	smi_en.u64 = 0;
1086c17812d622a74950e2cd65f368f0518491cca61David Daney	smi_en.s.en = 1;
1096c17812d622a74950e2cd65f368f0518491cca61David Daney	cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64);
1106c17812d622a74950e2cd65f368f0518491cca61David Daney
11125d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	/*
11225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	 * Standard Octeon evaluation boards don't support phy
11325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	 * interrupts, we need to poll.
11425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	 */
11525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	for (i = 0; i < PHY_MAX_ADDR; i++)
11625d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		bus->phy_irq[i] = PHY_POLL;
11725d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
11825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	bus->mii_bus->priv = bus;
11925d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	bus->mii_bus->irq = bus->phy_irq;
12025d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	bus->mii_bus->name = "mdio-octeon";
121d6c25beba35a76c002bff9235484d75a6f8e7e6bFlorian Fainelli	snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
122d6c25beba35a76c002bff9235484d75a6f8e7e6bFlorian Fainelli		bus->mii_bus->name, bus->unit);
12325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	bus->mii_bus->parent = &pdev->dev;
12425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
12525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	bus->mii_bus->read = octeon_mdiobus_read;
12625d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	bus->mii_bus->write = octeon_mdiobus_write;
12725d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
12825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	dev_set_drvdata(&pdev->dev, bus);
12925d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
13025d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	err = mdiobus_register(bus->mii_bus);
13125d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	if (err)
13225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		goto err_register;
13325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
13425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	dev_info(&pdev->dev, "Version " DRV_VERSION "\n");
13525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
13625d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	return 0;
13725d967b72a92d72b6e0263a0337dfc940bd6c044David Daneyerr_register:
13825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	mdiobus_free(bus->mii_bus);
13925d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
14025d967b72a92d72b6e0263a0337dfc940bd6c044David Daneyerr:
14125d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	devm_kfree(&pdev->dev, bus);
1426c17812d622a74950e2cd65f368f0518491cca61David Daney	smi_en.u64 = 0;
1436c17812d622a74950e2cd65f368f0518491cca61David Daney	cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64);
14425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	return err;
14525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney}
14625d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
147a71e8329170ece5f684aa363dfa69828cbfd5184David Daneystatic int __devexit octeon_mdiobus_remove(struct platform_device *pdev)
14825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney{
14925d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	struct octeon_mdiobus *bus;
1506c17812d622a74950e2cd65f368f0518491cca61David Daney	union cvmx_smix_en smi_en;
15125d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
15225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	bus = dev_get_drvdata(&pdev->dev);
15325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
15425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	mdiobus_unregister(bus->mii_bus);
15525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	mdiobus_free(bus->mii_bus);
1566c17812d622a74950e2cd65f368f0518491cca61David Daney	smi_en.u64 = 0;
1576c17812d622a74950e2cd65f368f0518491cca61David Daney	cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64);
15825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	return 0;
15925d967b72a92d72b6e0263a0337dfc940bd6c044David Daney}
16025d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
16125d967b72a92d72b6e0263a0337dfc940bd6c044David Daneystatic struct platform_driver octeon_mdiobus_driver = {
16225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	.driver = {
16325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		.name		= "mdio-octeon",
16425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney		.owner		= THIS_MODULE,
16525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	},
16625d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	.probe		= octeon_mdiobus_probe,
167a71e8329170ece5f684aa363dfa69828cbfd5184David Daney	.remove		= __devexit_p(octeon_mdiobus_remove),
16825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney};
16925d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
17025d967b72a92d72b6e0263a0337dfc940bd6c044David Daneyvoid octeon_mdiobus_force_mod_depencency(void)
17125d967b72a92d72b6e0263a0337dfc940bd6c044David Daney{
17225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	/* Let ethernet drivers force us to be loaded.  */
17325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney}
17425d967b72a92d72b6e0263a0337dfc940bd6c044David DaneyEXPORT_SYMBOL(octeon_mdiobus_force_mod_depencency);
17525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
17625d967b72a92d72b6e0263a0337dfc940bd6c044David Daneystatic int __init octeon_mdiobus_mod_init(void)
17725d967b72a92d72b6e0263a0337dfc940bd6c044David Daney{
17825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	return platform_driver_register(&octeon_mdiobus_driver);
17925d967b72a92d72b6e0263a0337dfc940bd6c044David Daney}
18025d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
18125d967b72a92d72b6e0263a0337dfc940bd6c044David Daneystatic void __exit octeon_mdiobus_mod_exit(void)
18225d967b72a92d72b6e0263a0337dfc940bd6c044David Daney{
18325d967b72a92d72b6e0263a0337dfc940bd6c044David Daney	platform_driver_unregister(&octeon_mdiobus_driver);
18425d967b72a92d72b6e0263a0337dfc940bd6c044David Daney}
18525d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
18625d967b72a92d72b6e0263a0337dfc940bd6c044David Daneymodule_init(octeon_mdiobus_mod_init);
18725d967b72a92d72b6e0263a0337dfc940bd6c044David Daneymodule_exit(octeon_mdiobus_mod_exit);
18825d967b72a92d72b6e0263a0337dfc940bd6c044David Daney
18925d967b72a92d72b6e0263a0337dfc940bd6c044David DaneyMODULE_DESCRIPTION(DRV_DESCRIPTION);
19025d967b72a92d72b6e0263a0337dfc940bd6c044David DaneyMODULE_VERSION(DRV_VERSION);
19125d967b72a92d72b6e0263a0337dfc940bd6c044David DaneyMODULE_AUTHOR("David Daney");
19225d967b72a92d72b6e0263a0337dfc940bd6c044David DaneyMODULE_LICENSE("GPL");
193