162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020 NovaTech LLC 462306a36Sopenharmony_ci * George McCollister <george.mccollister@gmail.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/bitfield.h> 862306a36Sopenharmony_ci#include <linux/bits.h> 962306a36Sopenharmony_ci#include <linux/mdio.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/phy.h> 1262306a36Sopenharmony_ci#include <linux/if_vlan.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include "xrs700x.h" 1562306a36Sopenharmony_ci#include "xrs700x_reg.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define XRS_MDIO_IBA0 0x10 1862306a36Sopenharmony_ci#define XRS_MDIO_IBA1 0x11 1962306a36Sopenharmony_ci#define XRS_MDIO_IBD 0x14 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define XRS_IB_READ 0x0 2262306a36Sopenharmony_ci#define XRS_IB_WRITE 0x1 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int xrs700x_mdio_reg_read(void *context, unsigned int reg, 2562306a36Sopenharmony_ci unsigned int *val) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci struct mdio_device *mdiodev = context; 2862306a36Sopenharmony_ci struct device *dev = &mdiodev->dev; 2962306a36Sopenharmony_ci u16 uval; 3062306a36Sopenharmony_ci int ret; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci uval = (u16)FIELD_GET(GENMASK(31, 16), reg); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci ret = mdiodev_write(mdiodev, XRS_MDIO_IBA1, uval); 3562306a36Sopenharmony_ci if (ret < 0) { 3662306a36Sopenharmony_ci dev_err(dev, "xrs mdiobus_write returned %d\n", ret); 3762306a36Sopenharmony_ci return ret; 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci uval = (u16)((reg & GENMASK(15, 1)) | XRS_IB_READ); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci ret = mdiodev_write(mdiodev, XRS_MDIO_IBA0, uval); 4362306a36Sopenharmony_ci if (ret < 0) { 4462306a36Sopenharmony_ci dev_err(dev, "xrs mdiobus_write returned %d\n", ret); 4562306a36Sopenharmony_ci return ret; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci ret = mdiodev_read(mdiodev, XRS_MDIO_IBD); 4962306a36Sopenharmony_ci if (ret < 0) { 5062306a36Sopenharmony_ci dev_err(dev, "xrs mdiobus_read returned %d\n", ret); 5162306a36Sopenharmony_ci return ret; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci *val = (unsigned int)ret; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic int xrs700x_mdio_reg_write(void *context, unsigned int reg, 6062306a36Sopenharmony_ci unsigned int val) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct mdio_device *mdiodev = context; 6362306a36Sopenharmony_ci struct device *dev = &mdiodev->dev; 6462306a36Sopenharmony_ci u16 uval; 6562306a36Sopenharmony_ci int ret; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci ret = mdiodev_write(mdiodev, XRS_MDIO_IBD, (u16)val); 6862306a36Sopenharmony_ci if (ret < 0) { 6962306a36Sopenharmony_ci dev_err(dev, "xrs mdiobus_write returned %d\n", ret); 7062306a36Sopenharmony_ci return ret; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci uval = (u16)FIELD_GET(GENMASK(31, 16), reg); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci ret = mdiodev_write(mdiodev, XRS_MDIO_IBA1, uval); 7662306a36Sopenharmony_ci if (ret < 0) { 7762306a36Sopenharmony_ci dev_err(dev, "xrs mdiobus_write returned %d\n", ret); 7862306a36Sopenharmony_ci return ret; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci uval = (u16)((reg & GENMASK(15, 1)) | XRS_IB_WRITE); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci ret = mdiodev_write(mdiodev, XRS_MDIO_IBA0, uval); 8462306a36Sopenharmony_ci if (ret < 0) { 8562306a36Sopenharmony_ci dev_err(dev, "xrs mdiobus_write returned %d\n", ret); 8662306a36Sopenharmony_ci return ret; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return 0; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic const struct regmap_config xrs700x_mdio_regmap_config = { 9362306a36Sopenharmony_ci .val_bits = 16, 9462306a36Sopenharmony_ci .reg_stride = 2, 9562306a36Sopenharmony_ci .reg_bits = 32, 9662306a36Sopenharmony_ci .pad_bits = 0, 9762306a36Sopenharmony_ci .write_flag_mask = 0, 9862306a36Sopenharmony_ci .read_flag_mask = 0, 9962306a36Sopenharmony_ci .reg_read = xrs700x_mdio_reg_read, 10062306a36Sopenharmony_ci .reg_write = xrs700x_mdio_reg_write, 10162306a36Sopenharmony_ci .max_register = XRS_VLAN(VLAN_N_VID - 1), 10262306a36Sopenharmony_ci .cache_type = REGCACHE_NONE, 10362306a36Sopenharmony_ci .reg_format_endian = REGMAP_ENDIAN_BIG, 10462306a36Sopenharmony_ci .val_format_endian = REGMAP_ENDIAN_BIG 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int xrs700x_mdio_probe(struct mdio_device *mdiodev) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct xrs700x *priv; 11062306a36Sopenharmony_ci int ret; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci priv = xrs700x_switch_alloc(&mdiodev->dev, mdiodev); 11362306a36Sopenharmony_ci if (!priv) 11462306a36Sopenharmony_ci return -ENOMEM; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci priv->regmap = devm_regmap_init(&mdiodev->dev, NULL, mdiodev, 11762306a36Sopenharmony_ci &xrs700x_mdio_regmap_config); 11862306a36Sopenharmony_ci if (IS_ERR(priv->regmap)) { 11962306a36Sopenharmony_ci ret = PTR_ERR(priv->regmap); 12062306a36Sopenharmony_ci dev_err(&mdiodev->dev, "Failed to initialize regmap: %d\n", ret); 12162306a36Sopenharmony_ci return ret; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci dev_set_drvdata(&mdiodev->dev, priv); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci ret = xrs700x_switch_register(priv); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* Main DSA driver may not be started yet. */ 12962306a36Sopenharmony_ci if (ret) 13062306a36Sopenharmony_ci return ret; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void xrs700x_mdio_remove(struct mdio_device *mdiodev) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct xrs700x *priv = dev_get_drvdata(&mdiodev->dev); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (!priv) 14062306a36Sopenharmony_ci return; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci xrs700x_switch_remove(priv); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic void xrs700x_mdio_shutdown(struct mdio_device *mdiodev) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct xrs700x *priv = dev_get_drvdata(&mdiodev->dev); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (!priv) 15062306a36Sopenharmony_ci return; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci xrs700x_switch_shutdown(priv); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci dev_set_drvdata(&mdiodev->dev, NULL); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic const struct of_device_id __maybe_unused xrs700x_mdio_dt_ids[] = { 15862306a36Sopenharmony_ci { .compatible = "arrow,xrs7003e", .data = &xrs7003e_info }, 15962306a36Sopenharmony_ci { .compatible = "arrow,xrs7003f", .data = &xrs7003f_info }, 16062306a36Sopenharmony_ci { .compatible = "arrow,xrs7004e", .data = &xrs7004e_info }, 16162306a36Sopenharmony_ci { .compatible = "arrow,xrs7004f", .data = &xrs7004f_info }, 16262306a36Sopenharmony_ci {}, 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, xrs700x_mdio_dt_ids); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic struct mdio_driver xrs700x_mdio_driver = { 16762306a36Sopenharmony_ci .mdiodrv.driver = { 16862306a36Sopenharmony_ci .name = "xrs700x-mdio", 16962306a36Sopenharmony_ci .of_match_table = of_match_ptr(xrs700x_mdio_dt_ids), 17062306a36Sopenharmony_ci }, 17162306a36Sopenharmony_ci .probe = xrs700x_mdio_probe, 17262306a36Sopenharmony_ci .remove = xrs700x_mdio_remove, 17362306a36Sopenharmony_ci .shutdown = xrs700x_mdio_shutdown, 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cimdio_module_driver(xrs700x_mdio_driver); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ciMODULE_AUTHOR("George McCollister <george.mccollister@gmail.com>"); 17962306a36Sopenharmony_ciMODULE_DESCRIPTION("Arrow SpeedChips XRS700x DSA MDIO driver"); 18062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 181