386 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later2/*3 * Copyright 2008 - 2015 Freescale Semiconductor Inc.4 */5 6#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt7 8#include <linux/init.h>9#include <linux/module.h>10#include <linux/of_address.h>11#include <linux/of_platform.h>12#include <linux/of_net.h>13#include <linux/of_mdio.h>14#include <linux/device.h>15#include <linux/phy.h>16#include <linux/netdevice.h>17#include <linux/phy_fixed.h>18#include <linux/phylink.h>19#include <linux/etherdevice.h>20#include <linux/libfdt_env.h>21#include <linux/platform_device.h>22 23#include "mac.h"24#include "fman_mac.h"25#include "fman_dtsec.h"26#include "fman_tgec.h"27#include "fman_memac.h"28 29MODULE_LICENSE("Dual BSD/GPL");30MODULE_DESCRIPTION("FSL FMan MAC API based driver");31 32struct mac_priv_s {33 u8 cell_index;34 struct fman *fman;35 /* List of multicast addresses */36 struct list_head mc_addr_list;37 struct platform_device *eth_dev;38 u16 speed;39};40 41struct mac_address {42 u8 addr[ETH_ALEN];43 struct list_head list;44};45 46static void mac_exception(struct mac_device *mac_dev,47 enum fman_mac_exceptions ex)48{49 if (ex == FM_MAC_EX_10G_RX_FIFO_OVFL) {50 /* don't flag RX FIFO after the first */51 mac_dev->set_exception(mac_dev->fman_mac,52 FM_MAC_EX_10G_RX_FIFO_OVFL, false);53 dev_err(mac_dev->dev, "10G MAC got RX FIFO Error = %x\n", ex);54 }55 56 dev_dbg(mac_dev->dev, "%s:%s() -> %d\n", KBUILD_BASENAME ".c",57 __func__, ex);58}59 60int fman_set_multi(struct net_device *net_dev, struct mac_device *mac_dev)61{62 struct mac_priv_s *priv;63 struct mac_address *old_addr, *tmp;64 struct netdev_hw_addr *ha;65 int err;66 enet_addr_t *addr;67 68 priv = mac_dev->priv;69 70 /* Clear previous address list */71 list_for_each_entry_safe(old_addr, tmp, &priv->mc_addr_list, list) {72 addr = (enet_addr_t *)old_addr->addr;73 err = mac_dev->remove_hash_mac_addr(mac_dev->fman_mac, addr);74 if (err < 0)75 return err;76 77 list_del(&old_addr->list);78 kfree(old_addr);79 }80 81 /* Add all the addresses from the new list */82 netdev_for_each_mc_addr(ha, net_dev) {83 addr = (enet_addr_t *)ha->addr;84 err = mac_dev->add_hash_mac_addr(mac_dev->fman_mac, addr);85 if (err < 0)86 return err;87 88 tmp = kmalloc(sizeof(*tmp), GFP_ATOMIC);89 if (!tmp)90 return -ENOMEM;91 92 ether_addr_copy(tmp->addr, ha->addr);93 list_add(&tmp->list, &priv->mc_addr_list);94 }95 return 0;96}97 98static DEFINE_MUTEX(eth_lock);99 100static struct platform_device *dpaa_eth_add_device(int fman_id,101 struct mac_device *mac_dev)102{103 struct platform_device *pdev;104 struct dpaa_eth_data data;105 struct mac_priv_s *priv;106 static int dpaa_eth_dev_cnt;107 int ret;108 109 priv = mac_dev->priv;110 111 data.mac_dev = mac_dev;112 data.mac_hw_id = priv->cell_index;113 data.fman_hw_id = fman_id;114 115 mutex_lock(ð_lock);116 pdev = platform_device_alloc("dpaa-ethernet", dpaa_eth_dev_cnt);117 if (!pdev) {118 ret = -ENOMEM;119 goto no_mem;120 }121 122 pdev->dev.parent = mac_dev->dev;123 124 ret = platform_device_add_data(pdev, &data, sizeof(data));125 if (ret)126 goto err;127 128 ret = platform_device_add(pdev);129 if (ret)130 goto err;131 132 dpaa_eth_dev_cnt++;133 mutex_unlock(ð_lock);134 135 return pdev;136 137err:138 platform_device_put(pdev);139no_mem:140 mutex_unlock(ð_lock);141 142 return ERR_PTR(ret);143}144 145static const struct of_device_id mac_match[] = {146 { .compatible = "fsl,fman-dtsec", .data = dtsec_initialization },147 { .compatible = "fsl,fman-xgec", .data = tgec_initialization },148 { .compatible = "fsl,fman-memac", .data = memac_initialization },149 {}150};151MODULE_DEVICE_TABLE(of, mac_match);152 153static int mac_probe(struct platform_device *_of_dev)154{155 int err, i, nph;156 int (*init)(struct mac_device *mac_dev, struct device_node *mac_node,157 struct fman_mac_params *params);158 struct device *dev;159 struct device_node *mac_node, *dev_node;160 struct mac_device *mac_dev;161 struct platform_device *of_dev;162 struct mac_priv_s *priv;163 struct fman_mac_params params;164 u32 val;165 u8 fman_id;166 phy_interface_t phy_if;167 168 dev = &_of_dev->dev;169 mac_node = dev->of_node;170 init = of_device_get_match_data(dev);171 172 mac_dev = devm_kzalloc(dev, sizeof(*mac_dev), GFP_KERNEL);173 if (!mac_dev)174 return -ENOMEM;175 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);176 if (!priv)177 return -ENOMEM;178 platform_set_drvdata(_of_dev, mac_dev);179 180 /* Save private information */181 mac_dev->priv = priv;182 mac_dev->dev = dev;183 184 INIT_LIST_HEAD(&priv->mc_addr_list);185 186 /* Get the FM node */187 dev_node = of_get_parent(mac_node);188 if (!dev_node) {189 dev_err(dev, "of_get_parent(%pOF) failed\n",190 mac_node);191 return -EINVAL;192 }193 194 of_dev = of_find_device_by_node(dev_node);195 if (!of_dev) {196 dev_err(dev, "of_find_device_by_node(%pOF) failed\n", dev_node);197 err = -EINVAL;198 goto _return_of_node_put;199 }200 mac_dev->fman_dev = &of_dev->dev;201 202 /* Get the FMan cell-index */203 err = of_property_read_u32(dev_node, "cell-index", &val);204 if (err) {205 dev_err(dev, "failed to read cell-index for %pOF\n", dev_node);206 err = -EINVAL;207 goto _return_dev_put;208 }209 /* cell-index 0 => FMan id 1 */210 fman_id = (u8)(val + 1);211 212 priv->fman = fman_bind(mac_dev->fman_dev);213 if (!priv->fman) {214 dev_err(dev, "fman_bind(%pOF) failed\n", dev_node);215 err = -ENODEV;216 goto _return_dev_put;217 }218 219 /* Two references have been taken in of_find_device_by_node()220 * and fman_bind(). Release one of them here. The second one221 * will be released in mac_remove().222 */223 put_device(mac_dev->fman_dev);224 of_node_put(dev_node);225 dev_node = NULL;226 227 /* Get the address of the memory mapped registers */228 mac_dev->res = platform_get_mem_or_io(_of_dev, 0);229 if (!mac_dev->res) {230 dev_err(dev, "could not get registers\n");231 err = -EINVAL;232 goto _return_dev_put;233 }234 235 err = devm_request_resource(dev, fman_get_mem_region(priv->fman),236 mac_dev->res);237 if (err) {238 dev_err_probe(dev, err, "could not request resource\n");239 goto _return_dev_put;240 }241 242 mac_dev->vaddr = devm_ioremap(dev, mac_dev->res->start,243 resource_size(mac_dev->res));244 if (!mac_dev->vaddr) {245 dev_err(dev, "devm_ioremap() failed\n");246 err = -EIO;247 goto _return_dev_put;248 }249 250 if (!of_device_is_available(mac_node)) {251 err = -ENODEV;252 goto _return_dev_put;253 }254 255 /* Get the cell-index */256 err = of_property_read_u32(mac_node, "cell-index", &val);257 if (err) {258 dev_err(dev, "failed to read cell-index for %pOF\n", mac_node);259 err = -EINVAL;260 goto _return_dev_put;261 }262 priv->cell_index = (u8)val;263 264 /* Get the MAC address */265 err = of_get_mac_address(mac_node, mac_dev->addr);266 if (err)267 dev_warn(dev, "of_get_mac_address(%pOF) failed\n", mac_node);268 269 /* Get the port handles */270 nph = of_count_phandle_with_args(mac_node, "fsl,fman-ports", NULL);271 if (unlikely(nph < 0)) {272 dev_err(dev, "of_count_phandle_with_args(%pOF, fsl,fman-ports) failed\n",273 mac_node);274 err = nph;275 goto _return_dev_put;276 }277 278 if (nph != ARRAY_SIZE(mac_dev->port)) {279 dev_err(dev, "Not supported number of fman-ports handles of mac node %pOF from device tree\n",280 mac_node);281 err = -EINVAL;282 goto _return_dev_put;283 }284 285 /* PORT_NUM determines the size of the port array */286 for (i = 0; i < PORT_NUM; i++) {287 /* Find the port node */288 dev_node = of_parse_phandle(mac_node, "fsl,fman-ports", i);289 if (!dev_node) {290 dev_err(dev, "of_parse_phandle(%pOF, fsl,fman-ports) failed\n",291 mac_node);292 err = -EINVAL;293 goto _return_dev_arr_put;294 }295 296 of_dev = of_find_device_by_node(dev_node);297 if (!of_dev) {298 dev_err(dev, "of_find_device_by_node(%pOF) failed\n",299 dev_node);300 err = -EINVAL;301 goto _return_dev_arr_put;302 }303 mac_dev->fman_port_devs[i] = &of_dev->dev;304 305 mac_dev->port[i] = fman_port_bind(mac_dev->fman_port_devs[i]);306 if (!mac_dev->port[i]) {307 dev_err(dev, "dev_get_drvdata(%pOF) failed\n",308 dev_node);309 err = -EINVAL;310 goto _return_dev_arr_put;311 }312 /* Two references have been taken in of_find_device_by_node()313 * and fman_port_bind(). Release one of them here. The second314 * one will be released in mac_remove().315 */316 put_device(mac_dev->fman_port_devs[i]);317 of_node_put(dev_node);318 dev_node = NULL;319 }320 321 /* Get the PHY connection type */322 err = of_get_phy_mode(mac_node, &phy_if);323 if (err) {324 dev_warn(dev,325 "of_get_phy_mode() for %pOF failed. Defaulting to SGMII\n",326 mac_node);327 phy_if = PHY_INTERFACE_MODE_SGMII;328 }329 mac_dev->phy_if = phy_if;330 331 params.mac_id = priv->cell_index;332 params.fm = (void *)priv->fman;333 params.exception_cb = mac_exception;334 params.event_cb = mac_exception;335 336 err = init(mac_dev, mac_node, ¶ms);337 if (err < 0)338 goto _return_dev_arr_put;339 340 if (!is_zero_ether_addr(mac_dev->addr))341 dev_info(dev, "FMan MAC address: %pM\n", mac_dev->addr);342 343 priv->eth_dev = dpaa_eth_add_device(fman_id, mac_dev);344 if (IS_ERR(priv->eth_dev)) {345 err = PTR_ERR(priv->eth_dev);346 dev_err(dev, "failed to add Ethernet platform device for MAC %d\n",347 priv->cell_index);348 priv->eth_dev = NULL;349 }350 351 return err;352 353_return_dev_arr_put:354 /* mac_dev is kzalloc'ed */355 for (i = 0; i < PORT_NUM; i++)356 put_device(mac_dev->fman_port_devs[i]);357_return_dev_put:358 put_device(mac_dev->fman_dev);359_return_of_node_put:360 of_node_put(dev_node);361 return err;362}363 364static void mac_remove(struct platform_device *pdev)365{366 struct mac_device *mac_dev = platform_get_drvdata(pdev);367 int i;368 369 for (i = 0; i < PORT_NUM; i++)370 put_device(mac_dev->fman_port_devs[i]);371 put_device(mac_dev->fman_dev);372 373 platform_device_unregister(mac_dev->priv->eth_dev);374}375 376static struct platform_driver mac_driver = {377 .driver = {378 .name = KBUILD_MODNAME,379 .of_match_table = mac_match,380 },381 .probe = mac_probe,382 .remove_new = mac_remove,383};384 385builtin_platform_driver(mac_driver);386