1/* 2 * CLPS711X SPI bus driver 3 * 4 * Copyright (C) 2012-2014 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/gpio.h> 15#include <linux/delay.h> 16#include <linux/module.h> 17#include <linux/interrupt.h> 18#include <linux/platform_device.h> 19#include <linux/regmap.h> 20#include <linux/mfd/syscon.h> 21#include <linux/mfd/syscon/clps711x.h> 22#include <linux/spi/spi.h> 23#include <linux/platform_data/spi-clps711x.h> 24 25#define DRIVER_NAME "spi-clps711x" 26 27#define SYNCIO_FRMLEN(x) ((x) << 8) 28#define SYNCIO_TXFRMEN (1 << 14) 29 30struct spi_clps711x_data { 31 void __iomem *syncio; 32 struct regmap *syscon; 33 struct clk *spi_clk; 34 35 u8 *tx_buf; 36 u8 *rx_buf; 37 unsigned int bpw; 38 int len; 39}; 40 41static int spi_clps711x_setup(struct spi_device *spi) 42{ 43 /* We are expect that SPI-device is not selected */ 44 gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); 45 46 return 0; 47} 48 49static int spi_clps711x_prepare_message(struct spi_master *master, 50 struct spi_message *msg) 51{ 52 struct spi_clps711x_data *hw = spi_master_get_devdata(master); 53 struct spi_device *spi = msg->spi; 54 55 /* Setup mode for transfer */ 56 return regmap_update_bits(hw->syscon, SYSCON_OFFSET, SYSCON3_ADCCKNSEN, 57 (spi->mode & SPI_CPHA) ? 58 SYSCON3_ADCCKNSEN : 0); 59} 60 61static int spi_clps711x_transfer_one(struct spi_master *master, 62 struct spi_device *spi, 63 struct spi_transfer *xfer) 64{ 65 struct spi_clps711x_data *hw = spi_master_get_devdata(master); 66 u8 data; 67 68 clk_set_rate(hw->spi_clk, xfer->speed_hz ? : spi->max_speed_hz); 69 70 hw->len = xfer->len; 71 hw->bpw = xfer->bits_per_word; 72 hw->tx_buf = (u8 *)xfer->tx_buf; 73 hw->rx_buf = (u8 *)xfer->rx_buf; 74 75 /* Initiate transfer */ 76 data = hw->tx_buf ? *hw->tx_buf++ : 0; 77 writel(data | SYNCIO_FRMLEN(hw->bpw) | SYNCIO_TXFRMEN, hw->syncio); 78 79 return 1; 80} 81 82static irqreturn_t spi_clps711x_isr(int irq, void *dev_id) 83{ 84 struct spi_master *master = dev_id; 85 struct spi_clps711x_data *hw = spi_master_get_devdata(master); 86 u8 data; 87 88 /* Handle RX */ 89 data = readb(hw->syncio); 90 if (hw->rx_buf) 91 *hw->rx_buf++ = data; 92 93 /* Handle TX */ 94 if (--hw->len > 0) { 95 data = hw->tx_buf ? *hw->tx_buf++ : 0; 96 writel(data | SYNCIO_FRMLEN(hw->bpw) | SYNCIO_TXFRMEN, 97 hw->syncio); 98 } else 99 spi_finalize_current_transfer(master); 100 101 return IRQ_HANDLED; 102} 103 104static int spi_clps711x_probe(struct platform_device *pdev) 105{ 106 struct spi_clps711x_data *hw; 107 struct spi_clps711x_pdata *pdata = dev_get_platdata(&pdev->dev); 108 struct spi_master *master; 109 struct resource *res; 110 int i, irq, ret; 111 112 if (!pdata) { 113 dev_err(&pdev->dev, "No platform data supplied\n"); 114 return -EINVAL; 115 } 116 117 if (pdata->num_chipselect < 1) { 118 dev_err(&pdev->dev, "At least one CS must be defined\n"); 119 return -EINVAL; 120 } 121 122 irq = platform_get_irq(pdev, 0); 123 if (irq < 0) 124 return irq; 125 126 master = spi_alloc_master(&pdev->dev, sizeof(*hw)); 127 if (!master) 128 return -ENOMEM; 129 130 master->cs_gpios = devm_kzalloc(&pdev->dev, sizeof(int) * 131 pdata->num_chipselect, GFP_KERNEL); 132 if (!master->cs_gpios) { 133 ret = -ENOMEM; 134 goto err_out; 135 } 136 137 master->bus_num = pdev->id; 138 master->mode_bits = SPI_CPHA | SPI_CS_HIGH; 139 master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 8); 140 master->num_chipselect = pdata->num_chipselect; 141 master->setup = spi_clps711x_setup; 142 master->prepare_message = spi_clps711x_prepare_message; 143 master->transfer_one = spi_clps711x_transfer_one; 144 145 hw = spi_master_get_devdata(master); 146 147 for (i = 0; i < master->num_chipselect; i++) { 148 master->cs_gpios[i] = pdata->chipselect[i]; 149 ret = devm_gpio_request(&pdev->dev, master->cs_gpios[i], 150 DRIVER_NAME); 151 if (ret) { 152 dev_err(&pdev->dev, "Can't get CS GPIO %i\n", i); 153 goto err_out; 154 } 155 } 156 157 hw->spi_clk = devm_clk_get(&pdev->dev, NULL); 158 if (IS_ERR(hw->spi_clk)) { 159 ret = PTR_ERR(hw->spi_clk); 160 goto err_out; 161 } 162 163 hw->syscon = syscon_regmap_lookup_by_pdevname("syscon.3"); 164 if (IS_ERR(hw->syscon)) { 165 ret = PTR_ERR(hw->syscon); 166 goto err_out; 167 } 168 169 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 170 hw->syncio = devm_ioremap_resource(&pdev->dev, res); 171 if (IS_ERR(hw->syncio)) { 172 ret = PTR_ERR(hw->syncio); 173 goto err_out; 174 } 175 176 /* Disable extended mode due hardware problems */ 177 regmap_update_bits(hw->syscon, SYSCON_OFFSET, SYSCON3_ADCCON, 0); 178 179 /* Clear possible pending interrupt */ 180 readl(hw->syncio); 181 182 ret = devm_request_irq(&pdev->dev, irq, spi_clps711x_isr, 0, 183 dev_name(&pdev->dev), master); 184 if (ret) 185 goto err_out; 186 187 ret = devm_spi_register_master(&pdev->dev, master); 188 if (!ret) { 189 dev_info(&pdev->dev, 190 "SPI bus driver initialized. Master clock %u Hz\n", 191 master->max_speed_hz); 192 return 0; 193 } 194 195 dev_err(&pdev->dev, "Failed to register master\n"); 196 197err_out: 198 spi_master_put(master); 199 200 return ret; 201} 202 203static struct platform_driver clps711x_spi_driver = { 204 .driver = { 205 .name = DRIVER_NAME, 206 }, 207 .probe = spi_clps711x_probe, 208}; 209module_platform_driver(clps711x_spi_driver); 210 211MODULE_LICENSE("GPL"); 212MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); 213MODULE_DESCRIPTION("CLPS711X SPI bus driver"); 214MODULE_ALIAS("platform:" DRIVER_NAME); 215