1ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou/* 2ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou * OpenCores tiny SPI master driver 3ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou * 4ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou * http://opencores.org/project,tiny_spi 5ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou * 6ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou * Copyright (C) 2011 Thomas Chou <thomas@wytron.com.tw> 7ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou * 8ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou * Based on spi_s3c24xx.c, which is: 9ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou * Copyright (c) 2006 Ben Dooks 10ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou * Copyright (c) 2006 Simtec Electronics 11ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou * Ben Dooks <ben@simtec.co.uk> 12ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou * 13ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou * This program is free software; you can redistribute it and/or modify 14ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou * it under the terms of the GNU General Public License version 2 as 15ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou * published by the Free Software Foundation. 16ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou */ 17ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 18ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#include <linux/interrupt.h> 19ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#include <linux/errno.h> 20d7614de422c0b55db0c1013a6c72330187536004Paul Gortmaker#include <linux/module.h> 21ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#include <linux/platform_device.h> 22ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#include <linux/spi/spi.h> 23ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#include <linux/spi/spi_bitbang.h> 24ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#include <linux/spi/spi_oc_tiny.h> 25ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#include <linux/io.h> 26ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#include <linux/gpio.h> 27ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#include <linux/of.h> 28ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 29ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#define DRV_NAME "spi_oc_tiny" 30ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 31ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#define TINY_SPI_RXDATA 0 32ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#define TINY_SPI_TXDATA 4 33ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#define TINY_SPI_STATUS 8 34ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#define TINY_SPI_CONTROL 12 35ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#define TINY_SPI_BAUD 16 36ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 37ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#define TINY_SPI_STATUS_TXE 0x1 38ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#define TINY_SPI_STATUS_TXR 0x2 39ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 40ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Choustruct tiny_spi { 41ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou /* bitbang has to be first */ 42ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou struct spi_bitbang bitbang; 43ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou struct completion done; 44ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 45ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou void __iomem *base; 46ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou int irq; 47ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou unsigned int freq; 48ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou unsigned int baudwidth; 49ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou unsigned int baud; 50ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou unsigned int speed_hz; 51ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou unsigned int mode; 52ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou unsigned int len; 53ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou unsigned int txc, rxc; 54ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou const u8 *txp; 55ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou u8 *rxp; 56e80beb27d2f81a1c3c8887e0e0a82d77bb392d28Grant Likely int gpio_cs_count; 57ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou int *gpio_cs; 58ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou}; 59ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 60ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Choustatic inline struct tiny_spi *tiny_spi_to_hw(struct spi_device *sdev) 61ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou{ 62ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou return spi_master_get_devdata(sdev->master); 63ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou} 64ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 65ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Choustatic unsigned int tiny_spi_baud(struct spi_device *spi, unsigned int hz) 66ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou{ 67ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou struct tiny_spi *hw = tiny_spi_to_hw(spi); 68ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 69ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou return min(DIV_ROUND_UP(hw->freq, hz * 2), (1U << hw->baudwidth)) - 1; 70ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou} 71ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 72ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Choustatic void tiny_spi_chipselect(struct spi_device *spi, int is_active) 73ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou{ 74ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou struct tiny_spi *hw = tiny_spi_to_hw(spi); 75ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 76e80beb27d2f81a1c3c8887e0e0a82d77bb392d28Grant Likely if (hw->gpio_cs_count > 0) { 77ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou gpio_set_value(hw->gpio_cs[spi->chip_select], 78ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou (spi->mode & SPI_CS_HIGH) ? is_active : !is_active); 79ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } 80ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou} 81ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 82ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Choustatic int tiny_spi_setup_transfer(struct spi_device *spi, 83ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou struct spi_transfer *t) 84ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou{ 85ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou struct tiny_spi *hw = tiny_spi_to_hw(spi); 86ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou unsigned int baud = hw->baud; 87ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 88ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (t) { 89ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (t->speed_hz && t->speed_hz != hw->speed_hz) 90ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou baud = tiny_spi_baud(spi, t->speed_hz); 91ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } 92ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou writel(baud, hw->base + TINY_SPI_BAUD); 93ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou writel(hw->mode, hw->base + TINY_SPI_CONTROL); 94ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou return 0; 95ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou} 96ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 97ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Choustatic int tiny_spi_setup(struct spi_device *spi) 98ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou{ 99ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou struct tiny_spi *hw = tiny_spi_to_hw(spi); 100ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 101ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (spi->max_speed_hz != hw->speed_hz) { 102ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->speed_hz = spi->max_speed_hz; 103ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->baud = tiny_spi_baud(spi, hw->speed_hz); 104ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } 105ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->mode = spi->mode & (SPI_CPOL | SPI_CPHA); 106ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou return 0; 107ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou} 108ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 109ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Choustatic inline void tiny_spi_wait_txr(struct tiny_spi *hw) 110ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou{ 111ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou while (!(readb(hw->base + TINY_SPI_STATUS) & 112ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou TINY_SPI_STATUS_TXR)) 113ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou cpu_relax(); 114ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou} 115ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 116ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Choustatic inline void tiny_spi_wait_txe(struct tiny_spi *hw) 117ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou{ 118ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou while (!(readb(hw->base + TINY_SPI_STATUS) & 119ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou TINY_SPI_STATUS_TXE)) 120ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou cpu_relax(); 121ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou} 122ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 123ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Choustatic int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) 124ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou{ 125ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou struct tiny_spi *hw = tiny_spi_to_hw(spi); 126ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou const u8 *txp = t->tx_buf; 127ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou u8 *rxp = t->rx_buf; 128ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou unsigned int i; 129ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 130ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (hw->irq >= 0) { 131886db6acf468bb6684e936a5456d470c69a75ef8Masanari Iida /* use interrupt driven data transfer */ 132ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->len = t->len; 133ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->txp = t->tx_buf; 134ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->rxp = t->rx_buf; 135ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->txc = 0; 136ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->rxc = 0; 137ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 138ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou /* send the first byte */ 139ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (t->len > 1) { 140ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou writeb(hw->txp ? *hw->txp++ : 0, 141ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->base + TINY_SPI_TXDATA); 142ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->txc++; 143ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou writeb(hw->txp ? *hw->txp++ : 0, 144ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->base + TINY_SPI_TXDATA); 145ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->txc++; 146ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou writeb(TINY_SPI_STATUS_TXR, hw->base + TINY_SPI_STATUS); 147ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } else { 148ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou writeb(hw->txp ? *hw->txp++ : 0, 149ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->base + TINY_SPI_TXDATA); 150ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->txc++; 151ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou writeb(TINY_SPI_STATUS_TXE, hw->base + TINY_SPI_STATUS); 152ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } 153ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 154ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou wait_for_completion(&hw->done); 155ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } else { 156e826a7ff69b285ea295144303653c01f781ea9dcAxel Lin /* we need to tighten the transfer loop */ 157e826a7ff69b285ea295144303653c01f781ea9dcAxel Lin writeb(txp ? *txp++ : 0, hw->base + TINY_SPI_TXDATA); 158e826a7ff69b285ea295144303653c01f781ea9dcAxel Lin for (i = 1; i < t->len; i++) { 159e826a7ff69b285ea295144303653c01f781ea9dcAxel Lin writeb(txp ? *txp++ : 0, hw->base + TINY_SPI_TXDATA); 160e826a7ff69b285ea295144303653c01f781ea9dcAxel Lin 161e826a7ff69b285ea295144303653c01f781ea9dcAxel Lin if (rxp || (i != t->len - 1)) 162ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou tiny_spi_wait_txr(hw); 163e826a7ff69b285ea295144303653c01f781ea9dcAxel Lin if (rxp) 164e826a7ff69b285ea295144303653c01f781ea9dcAxel Lin *rxp++ = readb(hw->base + TINY_SPI_TXDATA); 165ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } 166ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou tiny_spi_wait_txe(hw); 167e826a7ff69b285ea295144303653c01f781ea9dcAxel Lin if (rxp) 168e826a7ff69b285ea295144303653c01f781ea9dcAxel Lin *rxp++ = readb(hw->base + TINY_SPI_RXDATA); 169ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } 170e826a7ff69b285ea295144303653c01f781ea9dcAxel Lin 171ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou return t->len; 172ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou} 173ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 174ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Choustatic irqreturn_t tiny_spi_irq(int irq, void *dev) 175ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou{ 176ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou struct tiny_spi *hw = dev; 177ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 178ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou writeb(0, hw->base + TINY_SPI_STATUS); 179ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (hw->rxc + 1 == hw->len) { 180ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (hw->rxp) 181ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou *hw->rxp++ = readb(hw->base + TINY_SPI_RXDATA); 182ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->rxc++; 183ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou complete(&hw->done); 184ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } else { 185ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (hw->rxp) 186ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou *hw->rxp++ = readb(hw->base + TINY_SPI_TXDATA); 187ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->rxc++; 188ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (hw->txc < hw->len) { 189ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou writeb(hw->txp ? *hw->txp++ : 0, 190ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->base + TINY_SPI_TXDATA); 191ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->txc++; 192ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou writeb(TINY_SPI_STATUS_TXR, 193ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->base + TINY_SPI_STATUS); 194ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } else { 195ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou writeb(TINY_SPI_STATUS_TXE, 196ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->base + TINY_SPI_STATUS); 197ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } 198ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } 199ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou return IRQ_HANDLED; 200ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou} 201ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 202ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#ifdef CONFIG_OF 203ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#include <linux/of_gpio.h> 204ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 205fd4a319bc933ae93e68935b21924a9ca4ba2d060Grant Likelystatic int tiny_spi_of_probe(struct platform_device *pdev) 206ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou{ 207ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou struct tiny_spi *hw = platform_get_drvdata(pdev); 208ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou struct device_node *np = pdev->dev.of_node; 209ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou unsigned int i; 210ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou const __be32 *val; 211ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou int len; 212ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 213ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (!np) 214ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou return 0; 215ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->gpio_cs_count = of_gpio_count(np); 216e80beb27d2f81a1c3c8887e0e0a82d77bb392d28Grant Likely if (hw->gpio_cs_count > 0) { 217ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->gpio_cs = devm_kzalloc(&pdev->dev, 218ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->gpio_cs_count * sizeof(unsigned int), 219ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou GFP_KERNEL); 220ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (!hw->gpio_cs) 221ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou return -ENOMEM; 222ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } 223ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou for (i = 0; i < hw->gpio_cs_count; i++) { 224ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->gpio_cs[i] = of_get_gpio_flags(np, i, NULL); 225ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (hw->gpio_cs[i] < 0) 226ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou return -ENODEV; 227ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } 228ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->bitbang.master->dev.of_node = pdev->dev.of_node; 229ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou val = of_get_property(pdev->dev.of_node, 230ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou "clock-frequency", &len); 231ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (val && len >= sizeof(__be32)) 232ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->freq = be32_to_cpup(val); 233ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou val = of_get_property(pdev->dev.of_node, "baud-width", &len); 234ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (val && len >= sizeof(__be32)) 235ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->baudwidth = be32_to_cpup(val); 236ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou return 0; 237ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou} 238ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#else /* !CONFIG_OF */ 239fd4a319bc933ae93e68935b21924a9ca4ba2d060Grant Likelystatic int tiny_spi_of_probe(struct platform_device *pdev) 240ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou{ 241ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou return 0; 242ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou} 243ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#endif /* CONFIG_OF */ 244ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 245fd4a319bc933ae93e68935b21924a9ca4ba2d060Grant Likelystatic int tiny_spi_probe(struct platform_device *pdev) 246ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou{ 2478074cf063e410a2c0cf1704c3b31002e21f5df7cJingoo Han struct tiny_spi_platform_data *platp = dev_get_platdata(&pdev->dev); 248ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou struct tiny_spi *hw; 249ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou struct spi_master *master; 250ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou struct resource *res; 251ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou unsigned int i; 252ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou int err = -ENODEV; 253ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 254ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou master = spi_alloc_master(&pdev->dev, sizeof(struct tiny_spi)); 255ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (!master) 256ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou return err; 257ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 258ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou /* setup the master state. */ 259ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou master->bus_num = pdev->id; 260ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou master->num_chipselect = 255; 261ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; 262ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou master->setup = tiny_spi_setup; 263ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 264ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw = spi_master_get_devdata(master); 265ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou platform_set_drvdata(pdev, hw); 266ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 267ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou /* setup the state for the bitbang driver */ 26894c69f765f1b4a658d96905ec59928e3e3e07e6aAxel Lin hw->bitbang.master = master; 269ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->bitbang.setup_transfer = tiny_spi_setup_transfer; 270ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->bitbang.chipselect = tiny_spi_chipselect; 271ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs; 272ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 273ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou /* find and map our resources */ 274ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 275b3136f8f7c49bb4ca71247046f688fdffa104310Julia Lawall hw->base = devm_ioremap_resource(&pdev->dev, res); 276b3136f8f7c49bb4ca71247046f688fdffa104310Julia Lawall if (IS_ERR(hw->base)) { 277b3136f8f7c49bb4ca71247046f688fdffa104310Julia Lawall err = PTR_ERR(hw->base); 278b3136f8f7c49bb4ca71247046f688fdffa104310Julia Lawall goto exit; 279b3136f8f7c49bb4ca71247046f688fdffa104310Julia Lawall } 280ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou /* irq is optional */ 281ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->irq = platform_get_irq(pdev, 0); 282ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (hw->irq >= 0) { 283ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou init_completion(&hw->done); 284ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0, 285ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou pdev->name, hw); 286ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (err) 287ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou goto exit; 288ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } 289ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou /* find platform data */ 290ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (platp) { 291ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->gpio_cs_count = platp->gpio_cs_count; 292ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->gpio_cs = platp->gpio_cs; 293b3136f8f7c49bb4ca71247046f688fdffa104310Julia Lawall if (platp->gpio_cs_count && !platp->gpio_cs) { 294b3136f8f7c49bb4ca71247046f688fdffa104310Julia Lawall err = -EBUSY; 295b3136f8f7c49bb4ca71247046f688fdffa104310Julia Lawall goto exit; 296b3136f8f7c49bb4ca71247046f688fdffa104310Julia Lawall } 297ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->freq = platp->freq; 298ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou hw->baudwidth = platp->baudwidth; 299ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } else { 300ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou err = tiny_spi_of_probe(pdev); 301ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (err) 302ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou goto exit; 303ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } 304ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou for (i = 0; i < hw->gpio_cs_count; i++) { 305ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou err = gpio_request(hw->gpio_cs[i], dev_name(&pdev->dev)); 306ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (err) 307ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou goto exit_gpio; 308ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou gpio_direction_output(hw->gpio_cs[i], 1); 309ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou } 310e80beb27d2f81a1c3c8887e0e0a82d77bb392d28Grant Likely hw->bitbang.master->num_chipselect = max(1, hw->gpio_cs_count); 311ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 312ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou /* register our spi controller */ 313ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou err = spi_bitbang_start(&hw->bitbang); 314ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou if (err) 315ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou goto exit; 316ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq); 317ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 318ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou return 0; 319ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 320ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chouexit_gpio: 321ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou while (i-- > 0) 322ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou gpio_free(hw->gpio_cs[i]); 323ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chouexit: 324ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou spi_master_put(master); 325ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou return err; 326ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou} 327ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 328fd4a319bc933ae93e68935b21924a9ca4ba2d060Grant Likelystatic int tiny_spi_remove(struct platform_device *pdev) 329ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou{ 330ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou struct tiny_spi *hw = platform_get_drvdata(pdev); 331ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou struct spi_master *master = hw->bitbang.master; 332ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou unsigned int i; 333ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 334ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou spi_bitbang_stop(&hw->bitbang); 335ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou for (i = 0; i < hw->gpio_cs_count; i++) 336ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou gpio_free(hw->gpio_cs[i]); 337ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou spi_master_put(master); 338ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou return 0; 339ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou} 340ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 341ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#ifdef CONFIG_OF 342ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Choustatic const struct of_device_id tiny_spi_match[] = { 343ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou { .compatible = "opencores,tiny-spi-rtlsvn2", }, 344ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou {}, 345ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou}; 346ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas ChouMODULE_DEVICE_TABLE(of, tiny_spi_match); 347ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou#endif /* CONFIG_OF */ 348ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 349ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Choustatic struct platform_driver tiny_spi_driver = { 350ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou .probe = tiny_spi_probe, 351fd4a319bc933ae93e68935b21924a9ca4ba2d060Grant Likely .remove = tiny_spi_remove, 352ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou .driver = { 353ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou .name = DRV_NAME, 354ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou .owner = THIS_MODULE, 355ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou .pm = NULL, 3569547acce669ec7b5613eb9be0838bff47258ccbfSachin Kamat .of_match_table = of_match_ptr(tiny_spi_match), 357ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou }, 358ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou}; 359940ab88962bc1aff3273a8356d64577a6e386736Grant Likelymodule_platform_driver(tiny_spi_driver); 360ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas Chou 361ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas ChouMODULE_DESCRIPTION("OpenCores tiny SPI driver"); 362ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas ChouMODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); 363ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas ChouMODULE_LICENSE("GPL"); 364ce792580ea2ce6f7259b45124e9ccc4574c31606Thomas ChouMODULE_ALIAS("platform:" DRV_NAME); 365