linux/drivers/net/dsa/microchip/ksz_spi.c
<<
>>
Prefs
   1/*
   2 * Microchip KSZ series register access through SPI
   3 *
   4 * Copyright (C) 2017
   5 *
   6 * Permission to use, copy, modify, and/or distribute this software for any
   7 * purpose with or without fee is hereby granted, provided that the above
   8 * copyright notice and this permission notice appear in all copies.
   9 *
  10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17 */
  18
  19#include <asm/unaligned.h>
  20
  21#include <linux/delay.h>
  22#include <linux/kernel.h>
  23#include <linux/module.h>
  24#include <linux/spi/spi.h>
  25
  26#include "ksz_priv.h"
  27
  28/* SPI frame opcodes */
  29#define KS_SPIOP_RD                     3
  30#define KS_SPIOP_WR                     2
  31
  32#define SPI_ADDR_SHIFT                  24
  33#define SPI_ADDR_MASK                   (BIT(SPI_ADDR_SHIFT) - 1)
  34#define SPI_TURNAROUND_SHIFT            5
  35
  36static int ksz_spi_read_reg(struct spi_device *spi, u32 reg, u8 *val,
  37                            unsigned int len)
  38{
  39        u32 txbuf;
  40        int ret;
  41
  42        txbuf = reg & SPI_ADDR_MASK;
  43        txbuf |= KS_SPIOP_RD << SPI_ADDR_SHIFT;
  44        txbuf <<= SPI_TURNAROUND_SHIFT;
  45        txbuf = cpu_to_be32(txbuf);
  46
  47        ret = spi_write_then_read(spi, &txbuf, 4, val, len);
  48        return ret;
  49}
  50
  51static int ksz_spi_read(struct ksz_device *dev, u32 reg, u8 *data,
  52                        unsigned int len)
  53{
  54        struct spi_device *spi = dev->priv;
  55
  56        return ksz_spi_read_reg(spi, reg, data, len);
  57}
  58
  59static int ksz_spi_read8(struct ksz_device *dev, u32 reg, u8 *val)
  60{
  61        return ksz_spi_read(dev, reg, val, 1);
  62}
  63
  64static int ksz_spi_read16(struct ksz_device *dev, u32 reg, u16 *val)
  65{
  66        int ret = ksz_spi_read(dev, reg, (u8 *)val, 2);
  67
  68        if (!ret)
  69                *val = be16_to_cpu(*val);
  70
  71        return ret;
  72}
  73
  74static int ksz_spi_read24(struct ksz_device *dev, u32 reg, u32 *val)
  75{
  76        int ret;
  77
  78        *val = 0;
  79        ret = ksz_spi_read(dev, reg, (u8 *)val, 3);
  80        if (!ret) {
  81                *val = be32_to_cpu(*val);
  82                /* convert to 24bit */
  83                *val >>= 8;
  84        }
  85
  86        return ret;
  87}
  88
  89static int ksz_spi_read32(struct ksz_device *dev, u32 reg, u32 *val)
  90{
  91        int ret = ksz_spi_read(dev, reg, (u8 *)val, 4);
  92
  93        if (!ret)
  94                *val = be32_to_cpu(*val);
  95
  96        return ret;
  97}
  98
  99static int ksz_spi_write_reg(struct spi_device *spi, u32 reg, u8 *val,
 100                             unsigned int len)
 101{
 102        u32 txbuf;
 103        u8 data[12];
 104        int i;
 105
 106        txbuf = reg & SPI_ADDR_MASK;
 107        txbuf |= (KS_SPIOP_WR << SPI_ADDR_SHIFT);
 108        txbuf <<= SPI_TURNAROUND_SHIFT;
 109        txbuf = cpu_to_be32(txbuf);
 110
 111        data[0] = txbuf & 0xFF;
 112        data[1] = (txbuf & 0xFF00) >> 8;
 113        data[2] = (txbuf & 0xFF0000) >> 16;
 114        data[3] = (txbuf & 0xFF000000) >> 24;
 115        for (i = 0; i < len; i++)
 116                data[i + 4] = val[i];
 117
 118        return spi_write(spi, &data, 4 + len);
 119}
 120
 121static int ksz_spi_write8(struct ksz_device *dev, u32 reg, u8 value)
 122{
 123        struct spi_device *spi = dev->priv;
 124
 125        return ksz_spi_write_reg(spi, reg, &value, 1);
 126}
 127
 128static int ksz_spi_write16(struct ksz_device *dev, u32 reg, u16 value)
 129{
 130        struct spi_device *spi = dev->priv;
 131
 132        value = cpu_to_be16(value);
 133        return ksz_spi_write_reg(spi, reg, (u8 *)&value, 2);
 134}
 135
 136static int ksz_spi_write24(struct ksz_device *dev, u32 reg, u32 value)
 137{
 138        struct spi_device *spi = dev->priv;
 139
 140        /* make it to big endian 24bit from MSB */
 141        value <<= 8;
 142        value = cpu_to_be32(value);
 143        return ksz_spi_write_reg(spi, reg, (u8 *)&value, 3);
 144}
 145
 146static int ksz_spi_write32(struct ksz_device *dev, u32 reg, u32 value)
 147{
 148        struct spi_device *spi = dev->priv;
 149
 150        value = cpu_to_be32(value);
 151        return ksz_spi_write_reg(spi, reg, (u8 *)&value, 4);
 152}
 153
 154static const struct ksz_io_ops ksz_spi_ops = {
 155        .read8 = ksz_spi_read8,
 156        .read16 = ksz_spi_read16,
 157        .read24 = ksz_spi_read24,
 158        .read32 = ksz_spi_read32,
 159        .write8 = ksz_spi_write8,
 160        .write16 = ksz_spi_write16,
 161        .write24 = ksz_spi_write24,
 162        .write32 = ksz_spi_write32,
 163};
 164
 165static int ksz_spi_probe(struct spi_device *spi)
 166{
 167        struct ksz_device *dev;
 168        int ret;
 169
 170        dev = ksz_switch_alloc(&spi->dev, &ksz_spi_ops, spi);
 171        if (!dev)
 172                return -ENOMEM;
 173
 174        if (spi->dev.platform_data)
 175                dev->pdata = spi->dev.platform_data;
 176
 177        ret = ksz_switch_register(dev);
 178        if (ret)
 179                return ret;
 180
 181        spi_set_drvdata(spi, dev);
 182
 183        return 0;
 184}
 185
 186static int ksz_spi_remove(struct spi_device *spi)
 187{
 188        struct ksz_device *dev = spi_get_drvdata(spi);
 189
 190        if (dev)
 191                ksz_switch_remove(dev);
 192
 193        return 0;
 194}
 195
 196static const struct of_device_id ksz_dt_ids[] = {
 197        { .compatible = "microchip,ksz9477" },
 198        {},
 199};
 200MODULE_DEVICE_TABLE(of, ksz_dt_ids);
 201
 202static struct spi_driver ksz_spi_driver = {
 203        .driver = {
 204                .name   = "ksz9477-switch",
 205                .owner  = THIS_MODULE,
 206                .of_match_table = of_match_ptr(ksz_dt_ids),
 207        },
 208        .probe  = ksz_spi_probe,
 209        .remove = ksz_spi_remove,
 210};
 211
 212module_spi_driver(ksz_spi_driver);
 213
 214MODULE_AUTHOR("Woojung Huh <Woojung.Huh@microchip.com>");
 215MODULE_DESCRIPTION("Microchip KSZ Series Switch SPI access Driver");
 216MODULE_LICENSE("GPL");
 217