Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* DSA driver for:
0003  * Vitesse VSC7385 SparX-G5 5+1-port Integrated Gigabit Ethernet Switch
0004  * Vitesse VSC7388 SparX-G8 8-port Integrated Gigabit Ethernet Switch
0005  * Vitesse VSC7395 SparX-G5e 5+1-port Integrated Gigabit Ethernet Switch
0006  * Vitesse VSC7398 SparX-G8e 8-port Integrated Gigabit Ethernet Switch
0007  *
0008  * This driver takes control of the switch chip connected over CPU-attached
0009  * address bus and configures it to route packages around when connected to
0010  * a CPU port.
0011  *
0012  * Copyright (C) 2019 Pawel Dembicki <paweldembicki@gmail.com>
0013  * Based on vitesse-vsc-spi.c by:
0014  * Copyright (C) 2018 Linus Wallej <linus.walleij@linaro.org>
0015  * Includes portions of code from the firmware uploader by:
0016  * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
0017  */
0018 #include <linux/kernel.h>
0019 #include <linux/module.h>
0020 #include <linux/of.h>
0021 #include <linux/platform_device.h>
0022 
0023 #include "vitesse-vsc73xx.h"
0024 
0025 #define VSC73XX_CMD_PLATFORM_BLOCK_SHIFT        14
0026 #define VSC73XX_CMD_PLATFORM_BLOCK_MASK         0x7
0027 #define VSC73XX_CMD_PLATFORM_SUBBLOCK_SHIFT     10
0028 #define VSC73XX_CMD_PLATFORM_SUBBLOCK_MASK      0xf
0029 #define VSC73XX_CMD_PLATFORM_REGISTER_SHIFT     2
0030 
0031 /*
0032  * struct vsc73xx_platform - VSC73xx Platform state container
0033  */
0034 struct vsc73xx_platform {
0035     struct platform_device  *pdev;
0036     void __iomem        *base_addr;
0037     struct vsc73xx      vsc;
0038 };
0039 
0040 static const struct vsc73xx_ops vsc73xx_platform_ops;
0041 
0042 static u32 vsc73xx_make_addr(u8 block, u8 subblock, u8 reg)
0043 {
0044     u32 ret;
0045 
0046     ret = (block & VSC73XX_CMD_PLATFORM_BLOCK_MASK)
0047         << VSC73XX_CMD_PLATFORM_BLOCK_SHIFT;
0048     ret |= (subblock & VSC73XX_CMD_PLATFORM_SUBBLOCK_MASK)
0049         << VSC73XX_CMD_PLATFORM_SUBBLOCK_SHIFT;
0050     ret |= reg << VSC73XX_CMD_PLATFORM_REGISTER_SHIFT;
0051 
0052     return ret;
0053 }
0054 
0055 static int vsc73xx_platform_read(struct vsc73xx *vsc, u8 block, u8 subblock,
0056                  u8 reg, u32 *val)
0057 {
0058     struct vsc73xx_platform *vsc_platform = vsc->priv;
0059     u32 offset;
0060 
0061     if (!vsc73xx_is_addr_valid(block, subblock))
0062         return -EINVAL;
0063 
0064     offset = vsc73xx_make_addr(block, subblock, reg);
0065     /* By default vsc73xx running in big-endian mode.
0066      * (See "Register Addressing" section 5.5.3 in the VSC7385 manual.)
0067      */
0068     *val = ioread32be(vsc_platform->base_addr + offset);
0069 
0070     return 0;
0071 }
0072 
0073 static int vsc73xx_platform_write(struct vsc73xx *vsc, u8 block, u8 subblock,
0074                   u8 reg, u32 val)
0075 {
0076     struct vsc73xx_platform *vsc_platform = vsc->priv;
0077     u32 offset;
0078 
0079     if (!vsc73xx_is_addr_valid(block, subblock))
0080         return -EINVAL;
0081 
0082     offset = vsc73xx_make_addr(block, subblock, reg);
0083     iowrite32be(val, vsc_platform->base_addr + offset);
0084 
0085     return 0;
0086 }
0087 
0088 static int vsc73xx_platform_probe(struct platform_device *pdev)
0089 {
0090     struct device *dev = &pdev->dev;
0091     struct vsc73xx_platform *vsc_platform;
0092     int ret;
0093 
0094     vsc_platform = devm_kzalloc(dev, sizeof(*vsc_platform), GFP_KERNEL);
0095     if (!vsc_platform)
0096         return -ENOMEM;
0097 
0098     platform_set_drvdata(pdev, vsc_platform);
0099     vsc_platform->pdev = pdev;
0100     vsc_platform->vsc.dev = dev;
0101     vsc_platform->vsc.priv = vsc_platform;
0102     vsc_platform->vsc.ops = &vsc73xx_platform_ops;
0103 
0104     /* obtain I/O memory space */
0105     vsc_platform->base_addr = devm_platform_ioremap_resource(pdev, 0);
0106     if (IS_ERR(vsc_platform->base_addr)) {
0107         dev_err(&pdev->dev, "cannot request I/O memory space\n");
0108         ret = -ENXIO;
0109         return ret;
0110     }
0111 
0112     return vsc73xx_probe(&vsc_platform->vsc);
0113 }
0114 
0115 static int vsc73xx_platform_remove(struct platform_device *pdev)
0116 {
0117     struct vsc73xx_platform *vsc_platform = platform_get_drvdata(pdev);
0118 
0119     if (!vsc_platform)
0120         return 0;
0121 
0122     vsc73xx_remove(&vsc_platform->vsc);
0123 
0124     platform_set_drvdata(pdev, NULL);
0125 
0126     return 0;
0127 }
0128 
0129 static void vsc73xx_platform_shutdown(struct platform_device *pdev)
0130 {
0131     struct vsc73xx_platform *vsc_platform = platform_get_drvdata(pdev);
0132 
0133     if (!vsc_platform)
0134         return;
0135 
0136     vsc73xx_shutdown(&vsc_platform->vsc);
0137 
0138     platform_set_drvdata(pdev, NULL);
0139 }
0140 
0141 static const struct vsc73xx_ops vsc73xx_platform_ops = {
0142     .read = vsc73xx_platform_read,
0143     .write = vsc73xx_platform_write,
0144 };
0145 
0146 static const struct of_device_id vsc73xx_of_match[] = {
0147     {
0148         .compatible = "vitesse,vsc7385",
0149     },
0150     {
0151         .compatible = "vitesse,vsc7388",
0152     },
0153     {
0154         .compatible = "vitesse,vsc7395",
0155     },
0156     {
0157         .compatible = "vitesse,vsc7398",
0158     },
0159     { },
0160 };
0161 MODULE_DEVICE_TABLE(of, vsc73xx_of_match);
0162 
0163 static struct platform_driver vsc73xx_platform_driver = {
0164     .probe = vsc73xx_platform_probe,
0165     .remove = vsc73xx_platform_remove,
0166     .shutdown = vsc73xx_platform_shutdown,
0167     .driver = {
0168         .name = "vsc73xx-platform",
0169         .of_match_table = vsc73xx_of_match,
0170     },
0171 };
0172 module_platform_driver(vsc73xx_platform_driver);
0173 
0174 MODULE_AUTHOR("Pawel Dembicki <paweldembicki@gmail.com>");
0175 MODULE_DESCRIPTION("Vitesse VSC7385/7388/7395/7398 Platform driver");
0176 MODULE_LICENSE("GPL v2");