linux/drivers/spi/spi-clps711x.c
<<
>>
Prefs
   1/*
   2 *  CLPS711X SPI bus driver
   3 *
   4 *  Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 */
  11
  12#include <linux/io.h>
  13#include <linux/clk.h>
  14#include <linux/init.h>
  15#include <linux/gpio.h>
  16#include <linux/delay.h>
  17#include <linux/module.h>
  18#include <linux/interrupt.h>
  19#include <linux/platform_device.h>
  20#include <linux/spi/spi.h>
  21#include <linux/platform_data/spi-clps711x.h>
  22
  23#include <mach/hardware.h>
  24
  25#define DRIVER_NAME     "spi-clps711x"
  26
  27struct spi_clps711x_data {
  28        struct completion       done;
  29
  30        struct clk              *spi_clk;
  31        u32                     max_speed_hz;
  32
  33        u8                      *tx_buf;
  34        u8                      *rx_buf;
  35        int                     count;
  36        int                     len;
  37
  38        int                     chipselect[0];
  39};
  40
  41static int spi_clps711x_setup(struct spi_device *spi)
  42{
  43        struct spi_clps711x_data *hw = spi_master_get_devdata(spi->master);
  44
  45        /* We are expect that SPI-device is not selected */
  46        gpio_direction_output(hw->chipselect[spi->chip_select],
  47                              !(spi->mode & SPI_CS_HIGH));
  48
  49        return 0;
  50}
  51
  52static void spi_clps711x_setup_mode(struct spi_device *spi)
  53{
  54        /* Setup edge for transfer */
  55        if (spi->mode & SPI_CPHA)
  56                clps_writew(clps_readw(SYSCON3) | SYSCON3_ADCCKNSEN, SYSCON3);
  57        else
  58                clps_writew(clps_readw(SYSCON3) & ~SYSCON3_ADCCKNSEN, SYSCON3);
  59}
  60
  61static int spi_clps711x_setup_xfer(struct spi_device *spi,
  62                                   struct spi_transfer *xfer)
  63{
  64        u32 speed = xfer->speed_hz ? : spi->max_speed_hz;
  65        u8 bpw = xfer->bits_per_word;
  66        struct spi_clps711x_data *hw = spi_master_get_devdata(spi->master);
  67
  68        if (bpw != 8) {
  69                dev_err(&spi->dev, "Unsupported master bus width %i\n", bpw);
  70                return -EINVAL;
  71        }
  72
  73        /* Setup SPI frequency divider */
  74        if (!speed || (speed >= hw->max_speed_hz))
  75                clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) |
  76                            SYSCON1_ADCKSEL(3), SYSCON1);
  77        else if (speed >= (hw->max_speed_hz / 2))
  78                clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) |
  79                            SYSCON1_ADCKSEL(2), SYSCON1);
  80        else if (speed >= (hw->max_speed_hz / 8))
  81                clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) |
  82                            SYSCON1_ADCKSEL(1), SYSCON1);
  83        else
  84                clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) |
  85                            SYSCON1_ADCKSEL(0), SYSCON1);
  86
  87        return 0;
  88}
  89
  90static int spi_clps711x_transfer_one_message(struct spi_master *master,
  91                                             struct spi_message *msg)
  92{
  93        struct spi_clps711x_data *hw = spi_master_get_devdata(master);
  94        struct spi_transfer *xfer;
  95        int status = 0, cs = hw->chipselect[msg->spi->chip_select];
  96        u32 data;
  97
  98        spi_clps711x_setup_mode(msg->spi);
  99
 100        list_for_each_entry(xfer, &msg->transfers, transfer_list) {
 101                if (spi_clps711x_setup_xfer(msg->spi, xfer)) {
 102                        status = -EINVAL;
 103                        goto out_xfr;
 104                }
 105
 106                gpio_set_value(cs, !!(msg->spi->mode & SPI_CS_HIGH));
 107
 108                INIT_COMPLETION(hw->done);
 109
 110                hw->count = 0;
 111                hw->len = xfer->len;
 112                hw->tx_buf = (u8 *)xfer->tx_buf;
 113                hw->rx_buf = (u8 *)xfer->rx_buf;
 114
 115                /* Initiate transfer */
 116                data = hw->tx_buf ? hw->tx_buf[hw->count] : 0;
 117                clps_writel(data | SYNCIO_FRMLEN(8) | SYNCIO_TXFRMEN, SYNCIO);
 118
 119                wait_for_completion(&hw->done);
 120
 121                if (xfer->delay_usecs)
 122                        udelay(xfer->delay_usecs);
 123
 124                if (xfer->cs_change ||
 125                    list_is_last(&xfer->transfer_list, &msg->transfers))
 126                        gpio_set_value(cs, !(msg->spi->mode & SPI_CS_HIGH));
 127
 128                msg->actual_length += xfer->len;
 129        }
 130
 131out_xfr:
 132        msg->status = status;
 133        spi_finalize_current_message(master);
 134
 135        return 0;
 136}
 137
 138static irqreturn_t spi_clps711x_isr(int irq, void *dev_id)
 139{
 140        struct spi_clps711x_data *hw = (struct spi_clps711x_data *)dev_id;
 141        u32 data;
 142
 143        /* Handle RX */
 144        data = clps_readb(SYNCIO);
 145        if (hw->rx_buf)
 146                hw->rx_buf[hw->count] = (u8)data;
 147
 148        hw->count++;
 149
 150        /* Handle TX */
 151        if (hw->count < hw->len) {
 152                data = hw->tx_buf ? hw->tx_buf[hw->count] : 0;
 153                clps_writel(data | SYNCIO_FRMLEN(8) | SYNCIO_TXFRMEN, SYNCIO);
 154        } else
 155                complete(&hw->done);
 156
 157        return IRQ_HANDLED;
 158}
 159
 160static int spi_clps711x_probe(struct platform_device *pdev)
 161{
 162        int i, ret;
 163        struct spi_master *master;
 164        struct spi_clps711x_data *hw;
 165        struct spi_clps711x_pdata *pdata = dev_get_platdata(&pdev->dev);
 166
 167        if (!pdata) {
 168                dev_err(&pdev->dev, "No platform data supplied\n");
 169                return -EINVAL;
 170        }
 171
 172        if (pdata->num_chipselect < 1) {
 173                dev_err(&pdev->dev, "At least one CS must be defined\n");
 174                return -EINVAL;
 175        }
 176
 177        master = spi_alloc_master(&pdev->dev,
 178                                  sizeof(struct spi_clps711x_data) +
 179                                  sizeof(int) * pdata->num_chipselect);
 180        if (!master) {
 181                dev_err(&pdev->dev, "SPI allocating memory error\n");
 182                return -ENOMEM;
 183        }
 184
 185        master->bus_num = pdev->id;
 186        master->mode_bits = SPI_CPHA | SPI_CS_HIGH;
 187        master->bits_per_word_mask = SPI_BPW_MASK(8);
 188        master->num_chipselect = pdata->num_chipselect;
 189        master->setup = spi_clps711x_setup;
 190        master->transfer_one_message = spi_clps711x_transfer_one_message;
 191
 192        hw = spi_master_get_devdata(master);
 193
 194        for (i = 0; i < master->num_chipselect; i++) {
 195                hw->chipselect[i] = pdata->chipselect[i];
 196                if (!gpio_is_valid(hw->chipselect[i])) {
 197                        dev_err(&pdev->dev, "Invalid CS GPIO %i\n", i);
 198                        ret = -EINVAL;
 199                        goto err_out;
 200                }
 201                if (gpio_request(hw->chipselect[i], DRIVER_NAME)) {
 202                        dev_err(&pdev->dev, "Can't get CS GPIO %i\n", i);
 203                        ret = -EINVAL;
 204                        goto err_out;
 205                }
 206        }
 207
 208        hw->spi_clk = devm_clk_get(&pdev->dev, "spi");
 209        if (IS_ERR(hw->spi_clk)) {
 210                dev_err(&pdev->dev, "Can't get clocks\n");
 211                ret = PTR_ERR(hw->spi_clk);
 212                goto err_out;
 213        }
 214        hw->max_speed_hz = clk_get_rate(hw->spi_clk);
 215
 216        init_completion(&hw->done);
 217        platform_set_drvdata(pdev, master);
 218
 219        /* Disable extended mode due hardware problems */
 220        clps_writew(clps_readw(SYSCON3) & ~SYSCON3_ADCCON, SYSCON3);
 221
 222        /* Clear possible pending interrupt */
 223        clps_readl(SYNCIO);
 224
 225        ret = devm_request_irq(&pdev->dev, IRQ_SSEOTI, spi_clps711x_isr, 0,
 226                               dev_name(&pdev->dev), hw);
 227        if (ret) {
 228                dev_err(&pdev->dev, "Can't request IRQ\n");
 229                goto clk_out;
 230        }
 231
 232        ret = spi_register_master(master);
 233        if (!ret) {
 234                dev_info(&pdev->dev,
 235                         "SPI bus driver initialized. Master clock %u Hz\n",
 236                         hw->max_speed_hz);
 237                return 0;
 238        }
 239
 240        dev_err(&pdev->dev, "Failed to register master\n");
 241
 242clk_out:
 243err_out:
 244        while (--i >= 0)
 245                if (gpio_is_valid(hw->chipselect[i]))
 246                        gpio_free(hw->chipselect[i]);
 247
 248        spi_master_put(master);
 249
 250        return ret;
 251}
 252
 253static int spi_clps711x_remove(struct platform_device *pdev)
 254{
 255        int i;
 256        struct spi_master *master = platform_get_drvdata(pdev);
 257        struct spi_clps711x_data *hw = spi_master_get_devdata(master);
 258
 259        for (i = 0; i < master->num_chipselect; i++)
 260                if (gpio_is_valid(hw->chipselect[i]))
 261                        gpio_free(hw->chipselect[i]);
 262
 263        spi_unregister_master(master);
 264
 265        return 0;
 266}
 267
 268static struct platform_driver clps711x_spi_driver = {
 269        .driver = {
 270                .name   = DRIVER_NAME,
 271                .owner  = THIS_MODULE,
 272        },
 273        .probe  = spi_clps711x_probe,
 274        .remove = spi_clps711x_remove,
 275};
 276module_platform_driver(clps711x_spi_driver);
 277
 278MODULE_LICENSE("GPL");
 279MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
 280MODULE_DESCRIPTION("CLPS711X SPI bus driver");
 281
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.