linux/drivers/spi/spi-xcomm.c
<<
>>
Prefs
   1/*
   2 * Analog Devices AD-FMCOMMS1-EBZ board I2C-SPI bridge driver
   3 *
   4 * Copyright 2012 Analog Devices Inc.
   5 * Author: Lars-Peter Clausen <lars@metafoo.de>
   6 *
   7 * Licensed under the GPL-2 or later.
   8 */
   9
  10#include <linux/kernel.h>
  11#include <linux/init.h>
  12#include <linux/module.h>
  13#include <linux/delay.h>
  14#include <linux/i2c.h>
  15#include <linux/spi/spi.h>
  16#include <asm/unaligned.h>
  17
  18#define SPI_XCOMM_SETTINGS_LEN_OFFSET           10
  19#define SPI_XCOMM_SETTINGS_3WIRE                BIT(6)
  20#define SPI_XCOMM_SETTINGS_CS_HIGH              BIT(5)
  21#define SPI_XCOMM_SETTINGS_SAMPLE_END           BIT(4)
  22#define SPI_XCOMM_SETTINGS_CPHA                 BIT(3)
  23#define SPI_XCOMM_SETTINGS_CPOL                 BIT(2)
  24#define SPI_XCOMM_SETTINGS_CLOCK_DIV_MASK       0x3
  25#define SPI_XCOMM_SETTINGS_CLOCK_DIV_64         0x2
  26#define SPI_XCOMM_SETTINGS_CLOCK_DIV_16         0x1
  27#define SPI_XCOMM_SETTINGS_CLOCK_DIV_4          0x0
  28
  29#define SPI_XCOMM_CMD_UPDATE_CONFIG     0x03
  30#define SPI_XCOMM_CMD_WRITE             0x04
  31
  32#define SPI_XCOMM_CLOCK 48000000
  33
  34struct spi_xcomm {
  35        struct i2c_client *i2c;
  36
  37        uint16_t settings;
  38        uint16_t chipselect;
  39
  40        unsigned int current_speed;
  41
  42        uint8_t buf[63];
  43};
  44
  45static int spi_xcomm_sync_config(struct spi_xcomm *spi_xcomm, unsigned int len)
  46{
  47        uint16_t settings;
  48        uint8_t *buf = spi_xcomm->buf;
  49
  50        settings = spi_xcomm->settings;
  51        settings |= len << SPI_XCOMM_SETTINGS_LEN_OFFSET;
  52
  53        buf[0] = SPI_XCOMM_CMD_UPDATE_CONFIG;
  54        put_unaligned_be16(settings, &buf[1]);
  55        put_unaligned_be16(spi_xcomm->chipselect, &buf[3]);
  56
  57        return i2c_master_send(spi_xcomm->i2c, buf, 5);
  58}
  59
  60static void spi_xcomm_chipselect(struct spi_xcomm *spi_xcomm,
  61        struct spi_device *spi, int is_active)
  62{
  63        unsigned long cs = spi->chip_select;
  64        uint16_t chipselect = spi_xcomm->chipselect;
  65
  66        if (is_active)
  67                chipselect |= BIT(cs);
  68        else
  69                chipselect &= ~BIT(cs);
  70
  71        spi_xcomm->chipselect = chipselect;
  72}
  73
  74static int spi_xcomm_setup_transfer(struct spi_xcomm *spi_xcomm,
  75        struct spi_device *spi, struct spi_transfer *t, unsigned int *settings)
  76{
  77        unsigned int speed;
  78
  79        if ((t->bits_per_word && t->bits_per_word != 8) || t->len > 62)
  80                return -EINVAL;
  81
  82        speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz;
  83
  84        if (speed != spi_xcomm->current_speed) {
  85                unsigned int divider = DIV_ROUND_UP(SPI_XCOMM_CLOCK, speed);
  86                if (divider >= 64)
  87                        *settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_64;
  88                else if (divider >= 16)
  89                        *settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_16;
  90                else
  91                        *settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_4;
  92
  93                spi_xcomm->current_speed = speed;
  94        }
  95
  96        if (spi->mode & SPI_CPOL)
  97                *settings |= SPI_XCOMM_SETTINGS_CPOL;
  98        else
  99                *settings &= ~SPI_XCOMM_SETTINGS_CPOL;
 100
 101        if (spi->mode & SPI_CPHA)
 102                *settings &= ~SPI_XCOMM_SETTINGS_CPHA;
 103        else
 104                *settings |= SPI_XCOMM_SETTINGS_CPHA;
 105
 106        if (spi->mode & SPI_3WIRE)
 107                *settings |= SPI_XCOMM_SETTINGS_3WIRE;
 108        else
 109                *settings &= ~SPI_XCOMM_SETTINGS_3WIRE;
 110
 111        return 0;
 112}
 113
 114static int spi_xcomm_txrx_bufs(struct spi_xcomm *spi_xcomm,
 115        struct spi_device *spi, struct spi_transfer *t)
 116{
 117        int ret;
 118
 119        if (t->tx_buf) {
 120                spi_xcomm->buf[0] = SPI_XCOMM_CMD_WRITE;
 121                memcpy(spi_xcomm->buf + 1, t->tx_buf, t->len);
 122
 123                ret = i2c_master_send(spi_xcomm->i2c, spi_xcomm->buf, t->len + 1);
 124                if (ret < 0)
 125                        return ret;
 126                else if (ret != t->len + 1)
 127                        return -EIO;
 128        } else if (t->rx_buf) {
 129                ret = i2c_master_recv(spi_xcomm->i2c, t->rx_buf, t->len);
 130                if (ret < 0)
 131                        return ret;
 132                else if (ret != t->len)
 133                        return -EIO;
 134        }
 135
 136        return t->len;
 137}
 138
 139static int spi_xcomm_transfer_one(struct spi_master *master,
 140        struct spi_message *msg)
 141{
 142        struct spi_xcomm *spi_xcomm = spi_master_get_devdata(master);
 143        unsigned int settings = spi_xcomm->settings;
 144        struct spi_device *spi = msg->spi;
 145        unsigned cs_change = 0;
 146        struct spi_transfer *t;
 147        bool is_first = true;
 148        int status = 0;
 149        bool is_last;
 150
 151        is_first = true;
 152
 153        spi_xcomm_chipselect(spi_xcomm, spi, true);
 154
 155        list_for_each_entry(t, &msg->transfers, transfer_list) {
 156
 157                if (!t->tx_buf && !t->rx_buf && t->len) {
 158                        status = -EINVAL;
 159                        break;
 160                }
 161
 162                status = spi_xcomm_setup_transfer(spi_xcomm, spi, t, &settings);
 163                if (status < 0)
 164                        break;
 165
 166                is_last = list_is_last(&t->transfer_list, &msg->transfers);
 167                cs_change = t->cs_change;
 168
 169                if (cs_change ^ is_last)
 170                        settings |= BIT(5);
 171                else
 172                        settings &= ~BIT(5);
 173
 174                if (t->rx_buf) {
 175                        spi_xcomm->settings = settings;
 176                        status = spi_xcomm_sync_config(spi_xcomm, t->len);
 177                        if (status < 0)
 178                                break;
 179                } else if (settings != spi_xcomm->settings || is_first) {
 180                        spi_xcomm->settings = settings;
 181                        status = spi_xcomm_sync_config(spi_xcomm, 0);
 182                        if (status < 0)
 183                                break;
 184                }
 185
 186                if (t->len) {
 187                        status = spi_xcomm_txrx_bufs(spi_xcomm, spi, t);
 188
 189                        if (status < 0)
 190                                break;
 191
 192                        if (status > 0)
 193                                msg->actual_length += status;
 194                }
 195                status = 0;
 196
 197                if (t->delay_usecs)
 198                        udelay(t->delay_usecs);
 199
 200                is_first = false;
 201        }
 202
 203        if (status != 0 || !cs_change)
 204                spi_xcomm_chipselect(spi_xcomm, spi, false);
 205
 206        msg->status = status;
 207        spi_finalize_current_message(master);
 208
 209        return status;
 210}
 211
 212static int spi_xcomm_setup(struct spi_device *spi)
 213{
 214        if (spi->bits_per_word != 8)
 215                return -EINVAL;
 216
 217        return 0;
 218}
 219
 220static int __devinit spi_xcomm_probe(struct i2c_client *i2c,
 221        const struct i2c_device_id *id)
 222{
 223        struct spi_xcomm *spi_xcomm;
 224        struct spi_master *master;
 225        int ret;
 127     226"> 221)mm.c#L201L189"> 189            1 221)mm.c#L201L189"> 189  ect =  spi-xcomm.c#L220" lloc"line" name="L224"> 224udelay 210
 224        struct  224 ode" class="sref">mode & master;
id)
spi_xco9>)
<8" id="L138" cla0xMEM5           0xMEMa>        int len<2a>);
 &l2; 0)
  71        spi_xcomm = spi_master_get_devdata(ret2/a>;
i2c_master_recv(spi_xcom          spi_xcoma>( 222)
EIO2/a>;
spi_master_get_devdata< href="+code=spi_xconu4"> 204                nu4"> 204 xcom   16a>(master<   }
spi_master_get_devdata< href="+code=spi_xcoef">_a>-&">spi_master_getf">_a>-&xcom          mode &   href="+code=sett=mode" class="sref">mode &   href="+code=sett=mode" class="sref">mode & (ret<
spi_master_get_devdata< href="+code=spi_xcofla class="sref">setfla cxcom          modeMASTER_HALF_DUPLEX; ( 221/a>;
 189  ect =  spi-x href="+code=spi_xcomm"2"> 212static f="+code     212static int  189  ect =  spi-x href="+code=spi_xcode=spi_xcomm207                  139static int 
  ect =  spi-x href="+code=spi_xcoclass="s/ads="sres="+GS_8.="+code=spi_xcoof_node=spi" class="srof_nodexcom          spi_xcom href="+code=spi_xcoclass="s/ads="sres="+GS_8.="+code=spi_xcoof_node=spi" class="srof_nodexcoma>(master2/a>,
spi_xcomm->spi_master_get_devdata(msg2/a>)
 1412/a>{
 129                 224spi_master_get_devdata(master<2a>);
 130                if (2>settings2/a>;
spi_master_get_devdata(master;
 225        int t2/a>;
true2/a>;
 220statiexe=t static int spi_xcomm_probe(struct i2c_clf (2">is_last2/a>;
 152
 224        struct spi_masterspi_xcomm = spir_get_devdata" classmm" class="sref">spi_xcom/a>(true2/a>;
 152
 224spi_master_get_devdata(true<2a>);
 152
 217   er_list) {
 152
true2>) {
 221        const struct status = -#L222" id="L222" class="line" name="LLatus2/a>;
status""line" na" }ient * * 152   }
 217   L161"> 162
);
        const strucL152"f="+chref="+code=__devinit" cl"L152">        const evinit" cl"L152"er &l2; 0)
        const "L152"er 152eak;
status""line" na"ient *        const own2"mm.c#L         modeTHIS_MODULlc_client * *true2/a>;
status =  *__devinitef="+code id="L166"   __devinit is_last2/a>)
static int  152(5);
 217            2else
        const ef"ul hr221"L152"r_get_devdata" classevinit" cl"L152">        const evinit" cl"L152"er(BIT 172
modeMODULl_LICENSlr_get_span">status"GPL"/a>( 152>) {
modeMODULl_AUTHORr_get_span">status"Lars-Peevd Clausen     lars@metafoo.dehref"/a>(modeMODULl_DESCRIPTIONr_get_span">status"Analog Dt /a>(


The orige=sp LXR software by the (LXR ass=unityxcommlxf@f="ux.noGS_8.
lxf.f="ux.no kindly hodevd by (Redpill L="pro ASxcomm