^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /* DSA driver for:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Vitesse VSC7385 SparX-G5 5+1-port Integrated Gigabit Ethernet Switch
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Vitesse VSC7388 SparX-G8 8-port Integrated Gigabit Ethernet Switch
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Vitesse VSC7395 SparX-G5e 5+1-port Integrated Gigabit Ethernet Switch
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Vitesse VSC7398 SparX-G8e 8-port Integrated Gigabit Ethernet Switch
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * This driver takes control of the switch chip connected over CPU-attached
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * address bus and configures it to route packages around when connected to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * a CPU port.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * Copyright (C) 2019 Pawel Dembicki <paweldembicki@gmail.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * Based on vitesse-vsc-spi.c by:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * Copyright (C) 2018 Linus Wallej <linus.walleij@linaro.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * Includes portions of code from the firmware uploader by:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/of.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include "vitesse-vsc73xx.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #define VSC73XX_CMD_PLATFORM_BLOCK_SHIFT 14
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define VSC73XX_CMD_PLATFORM_BLOCK_MASK 0x7
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #define VSC73XX_CMD_PLATFORM_SUBBLOCK_SHIFT 10
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #define VSC73XX_CMD_PLATFORM_SUBBLOCK_MASK 0xf
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #define VSC73XX_CMD_PLATFORM_REGISTER_SHIFT 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) * struct vsc73xx_platform - VSC73xx Platform state container
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) struct vsc73xx_platform {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) struct platform_device *pdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) void __iomem *base_addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) struct vsc73xx vsc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) static const struct vsc73xx_ops vsc73xx_platform_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) static u32 vsc73xx_make_addr(u8 block, u8 subblock, u8 reg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) u32 ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) ret = (block & VSC73XX_CMD_PLATFORM_BLOCK_MASK)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) << VSC73XX_CMD_PLATFORM_BLOCK_SHIFT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) ret |= (subblock & VSC73XX_CMD_PLATFORM_SUBBLOCK_MASK)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) << VSC73XX_CMD_PLATFORM_SUBBLOCK_SHIFT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) ret |= reg << VSC73XX_CMD_PLATFORM_REGISTER_SHIFT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) static int vsc73xx_platform_read(struct vsc73xx *vsc, u8 block, u8 subblock,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) u8 reg, u32 *val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) struct vsc73xx_platform *vsc_platform = vsc->priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) u32 offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) if (!vsc73xx_is_addr_valid(block, subblock))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) offset = vsc73xx_make_addr(block, subblock, reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) /* By default vsc73xx running in big-endian mode.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) * (See "Register Addressing" section 5.5.3 in the VSC7385 manual.)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) *val = ioread32be(vsc_platform->base_addr + offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) static int vsc73xx_platform_write(struct vsc73xx *vsc, u8 block, u8 subblock,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) u8 reg, u32 val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) struct vsc73xx_platform *vsc_platform = vsc->priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) u32 offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) if (!vsc73xx_is_addr_valid(block, subblock))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) offset = vsc73xx_make_addr(block, subblock, reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) iowrite32be(val, vsc_platform->base_addr + offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) static int vsc73xx_platform_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) struct device *dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) struct vsc73xx_platform *vsc_platform;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) vsc_platform = devm_kzalloc(dev, sizeof(*vsc_platform), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) if (!vsc_platform)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) platform_set_drvdata(pdev, vsc_platform);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) vsc_platform->pdev = pdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) vsc_platform->vsc.dev = dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) vsc_platform->vsc.priv = vsc_platform;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) vsc_platform->vsc.ops = &vsc73xx_platform_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) /* obtain I/O memory space */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) vsc_platform->base_addr = devm_platform_ioremap_resource(pdev, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) if (IS_ERR(vsc_platform->base_addr)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) dev_err(&pdev->dev, "cannot request I/O memory space\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) ret = -ENXIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) return vsc73xx_probe(&vsc_platform->vsc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) static int vsc73xx_platform_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) struct vsc73xx_platform *vsc_platform = platform_get_drvdata(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) return vsc73xx_remove(&vsc_platform->vsc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) static const struct vsc73xx_ops vsc73xx_platform_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) .read = vsc73xx_platform_read,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) .write = vsc73xx_platform_write,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) static const struct of_device_id vsc73xx_of_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) .compatible = "vitesse,vsc7385",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) .compatible = "vitesse,vsc7388",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) .compatible = "vitesse,vsc7395",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) .compatible = "vitesse,vsc7398",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) { },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) MODULE_DEVICE_TABLE(of, vsc73xx_of_match);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) static struct platform_driver vsc73xx_platform_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) .probe = vsc73xx_platform_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) .remove = vsc73xx_platform_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) .name = "vsc73xx-platform",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) .of_match_table = vsc73xx_of_match,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) module_platform_driver(vsc73xx_platform_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) MODULE_AUTHOR("Pawel Dembicki <paweldembicki@gmail.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) MODULE_DESCRIPTION("Vitesse VSC7385/7388/7395/7398 Platform driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) MODULE_LICENSE("GPL v2");