12e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell/* 2ca632f556697d45d67ed5cada7cedf3ddfe0db4bGrant Likely * parport-to-butterfly adapter 32e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * 42e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * Copyright (C) 2005 David Brownell 52e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * 62e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * This program is free software; you can redistribute it and/or modify 72e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * it under the terms of the GNU General Public License as published by 82e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * the Free Software Foundation; either version 2 of the License, or 92e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * (at your option) any later version. 102e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * 112e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * This program is distributed in the hope that it will be useful, 122e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * but WITHOUT ANY WARRANTY; without even the implied warranty of 132e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 142e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * GNU General Public License for more details. 152e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * 162e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * You should have received a copy of the GNU General Public License 172e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * along with this program; if not, write to the Free Software 182e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 192e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell */ 202e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell#include <linux/kernel.h> 212e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell#include <linux/init.h> 222e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell#include <linux/delay.h> 23d7614de422c0b55db0c1013a6c72330187536004Paul Gortmaker#include <linux/module.h> 24da6752964290567a6b4ea180d1becda75e810e87David Brownell#include <linux/device.h> 252e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell#include <linux/parport.h> 262e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 27914e26379decf1fd984b22e51fd2e4209b7a7f1bAl Viro#include <linux/sched.h> 282e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell#include <linux/spi/spi.h> 292e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell#include <linux/spi/spi_bitbang.h> 302e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell#include <linux/spi/flash.h> 312e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 322e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell#include <linux/mtd/partitions.h> 332e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 342e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 352e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell/* 362e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * This uses SPI to talk with an "AVR Butterfly", which is a $US20 card 372e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * with a battery powered AVR microcontroller and lots of goodies. You 382e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * can use GCC to develop firmware for this. 392e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * 402e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * See Documentation/spi/butterfly for information about how to build 412e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * and use this custom parallel port cable. 422e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell */ 432e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 442e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 452e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell/* DATA output bits (pins 2..9 == D0..D7) */ 462e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell#define butterfly_nreset (1 << 1) /* pin 3 */ 472e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 482e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell#define spi_sck_bit (1 << 0) /* pin 2 */ 492e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell#define spi_mosi_bit (1 << 7) /* pin 9 */ 502e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 512e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell#define vcc_bits ((1 << 6) | (1 << 5)) /* pins 7, 8 */ 522e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 532e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell/* STATUS input bits */ 542e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell#define spi_miso_bit PARPORT_STATUS_BUSY /* pin 11 */ 552e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 562e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell/* CONTROL output bits */ 572e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell#define spi_cs_bit PARPORT_CONTROL_SELECT /* pin 17 */ 582e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 592e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 602e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 612e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellstatic inline struct butterfly *spidev_to_pp(struct spi_device *spi) 622e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell{ 632e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell return spi->controller_data; 642e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell} 652e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 662e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 672e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellstruct butterfly { 682e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell /* REVISIT ... for now, this must be first */ 692e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell struct spi_bitbang bitbang; 702e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 712e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell struct parport *port; 722e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell struct pardevice *pd; 732e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 742e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell u8 lastbyte; 752e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 762e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell struct spi_device *dataflash; 772e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell struct spi_device *butterfly; 782e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell struct spi_board_info info[2]; 792e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 802e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell}; 812e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 822e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell/*----------------------------------------------------------------------*/ 832e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 842e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellstatic inline void 852e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellsetsck(struct spi_device *spi, int is_on) 862e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell{ 872e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell struct butterfly *pp = spidev_to_pp(spi); 882e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell u8 bit, byte = pp->lastbyte; 892e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 90735ce95e6b9a262d1fbc0ddb5620deb3a29d1067David Brownell bit = spi_sck_bit; 912e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 922e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell if (is_on) 932e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell byte |= bit; 942e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell else 952e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell byte &= ~bit; 962e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell parport_write_data(pp->port, byte); 972e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pp->lastbyte = byte; 982e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell} 992e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1002e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellstatic inline void 1012e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellsetmosi(struct spi_device *spi, int is_on) 1022e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell{ 1032e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell struct butterfly *pp = spidev_to_pp(spi); 1042e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell u8 bit, byte = pp->lastbyte; 1052e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 106735ce95e6b9a262d1fbc0ddb5620deb3a29d1067David Brownell bit = spi_mosi_bit; 1072e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1082e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell if (is_on) 1092e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell byte |= bit; 1102e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell else 1112e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell byte &= ~bit; 1122e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell parport_write_data(pp->port, byte); 1132e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pp->lastbyte = byte; 1142e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell} 1152e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1162e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellstatic inline int getmiso(struct spi_device *spi) 1172e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell{ 1182e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell struct butterfly *pp = spidev_to_pp(spi); 1192e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell int value; 1202e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell u8 bit; 1212e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 122735ce95e6b9a262d1fbc0ddb5620deb3a29d1067David Brownell bit = spi_miso_bit; 1232e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1242e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell /* only STATUS_BUSY is NOT negated */ 1252e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell value = !(parport_read_status(pp->port) & bit); 1262e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell return (bit == PARPORT_STATUS_BUSY) ? value : !value; 1272e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell} 1282e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1292e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellstatic void butterfly_chipselect(struct spi_device *spi, int value) 1302e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell{ 1312e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell struct butterfly *pp = spidev_to_pp(spi); 1322e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1332e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell /* set default clock polarity */ 1349c1da3cb46316e40bac766ce45556dc4fd8df3caDavid Brownell if (value != BITBANG_CS_INACTIVE) 1352e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell setsck(spi, spi->mode & SPI_CPOL); 1362e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1379c1da3cb46316e40bac766ce45556dc4fd8df3caDavid Brownell /* here, value == "activate or not"; 1389c1da3cb46316e40bac766ce45556dc4fd8df3caDavid Brownell * most PARPORT_CONTROL_* bits are negated, so we must 1399c1da3cb46316e40bac766ce45556dc4fd8df3caDavid Brownell * morph it to value == "bit value to write in control register" 1409c1da3cb46316e40bac766ce45556dc4fd8df3caDavid Brownell */ 1412e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell if (spi_cs_bit == PARPORT_CONTROL_INIT) 1422e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell value = !value; 1432e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1442e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell parport_frob_control(pp->port, spi_cs_bit, value ? spi_cs_bit : 0); 1452e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell} 1462e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1472e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1482e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell/* we only needed to implement one mode here, and choose SPI_MODE_0 */ 1492e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1502e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell#define spidelay(X) do{}while(0) 1512e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell//#define spidelay ndelay 1522e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 153ca632f556697d45d67ed5cada7cedf3ddfe0db4bGrant Likely#include "spi-bitbang-txrx.h" 1542e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1552e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellstatic u32 1562e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellbutterfly_txrx_word_mode0(struct spi_device *spi, 1572e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell unsigned nsecs, 1582e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell u32 word, u8 bits) 1592e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell{ 16004bb2a031cf95b34b7432dd47b318a932a895b4cMarek Szyprowski return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits); 1612e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell} 1622e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1632e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell/*----------------------------------------------------------------------*/ 1642e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1652e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell/* override default partitioning with cmdlinepart */ 1662e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellstatic struct mtd_partition partitions[] = { { 1679c1da3cb46316e40bac766ce45556dc4fd8df3caDavid Brownell /* JFFS2 wants partitions of 4*N blocks for this device, 1689c1da3cb46316e40bac766ce45556dc4fd8df3caDavid Brownell * so sectors 0 and 1 can't be partitions by themselves. 1699c1da3cb46316e40bac766ce45556dc4fd8df3caDavid Brownell */ 1702e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1712e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell /* sector 0 = 8 pages * 264 bytes/page (1 block) 1722e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * sector 1 = 248 pages * 264 bytes/page 1732e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell */ 1742e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell .name = "bookkeeping", // 66 KB 1752e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell .offset = 0, 1762e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell .size = (8 + 248) * 264, 1772e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell// .mask_flags = MTD_WRITEABLE, 1782e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell}, { 1792e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell /* sector 2 = 256 pages * 264 bytes/page 1802e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * sectors 3-5 = 512 pages * 264 bytes/page 1812e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell */ 1822e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell .name = "filesystem", // 462 KB 1832e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell .offset = MTDPART_OFS_APPEND, 1842e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell .size = MTDPART_SIZ_FULL, 1852e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell} }; 1862e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1872e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellstatic struct flash_platform_data flash = { 1882e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell .name = "butterflash", 1892e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell .parts = partitions, 1902e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell .nr_parts = ARRAY_SIZE(partitions), 1912e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell}; 1922e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1932e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1942e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell/* REVISIT remove this ugly global and its "only one" limitation */ 1952e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellstatic struct butterfly *butterfly; 1962e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 1972e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellstatic void butterfly_attach(struct parport *p) 1982e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell{ 1992e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell struct pardevice *pd; 2002e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell int status; 2012e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell struct butterfly *pp; 2022e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell struct spi_master *master; 203da6752964290567a6b4ea180d1becda75e810e87David Brownell struct device *dev = p->physport->dev; 2042e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 205da6752964290567a6b4ea180d1becda75e810e87David Brownell if (butterfly || !dev) 2062e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell return; 2072e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 2082e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell /* REVISIT: this just _assumes_ a butterfly is there ... no probe, 2092e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * and no way to be selective about what it binds to. 2102e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell */ 2112e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 212da6752964290567a6b4ea180d1becda75e810e87David Brownell master = spi_alloc_master(dev, sizeof *pp); 2132e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell if (!master) { 2142e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell status = -ENOMEM; 2152e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell goto done; 2162e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell } 2172e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pp = spi_master_get_devdata(master); 2182e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 2192e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell /* 2202e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * SPI and bitbang hookup 2212e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * 2222e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * use default setup(), cleanup(), and transfer() methods; and 2232e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * only bother implementing mode 0. Start it later. 2242e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell */ 2252e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell master->bus_num = 42; 2262e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell master->num_chipselect = 2; 2272e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 2282e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pp->bitbang.master = spi_master_get(master); 2292e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pp->bitbang.chipselect = butterfly_chipselect; 2302e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pp->bitbang.txrx_word[SPI_MODE_0] = butterfly_txrx_word_mode0; 2312e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 2322e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell /* 2332e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * parport hookup 2342e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell */ 2352e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pp->port = p; 2362e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pd = parport_register_device(p, "spi_butterfly", 2372e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell NULL, NULL, NULL, 2382e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 0 /* FLAGS */, pp); 2392e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell if (!pd) { 2402e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell status = -ENOMEM; 2412e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell goto clean0; 2422e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell } 2432e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pp->pd = pd; 2442e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 2452e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell status = parport_claim(pd); 2462e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell if (status < 0) 2472e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell goto clean1; 2482e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 2492e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell /* 2502e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * Butterfly reset, powerup, run firmware 2512e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell */ 2522e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pr_debug("%s: powerup/reset Butterfly\n", p->name); 2532e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 2542e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell /* nCS for dataflash (this bit is inverted on output) */ 2552e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell parport_frob_control(pp->port, spi_cs_bit, 0); 2562e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 2572e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell /* stabilize power with chip in reset (nRESET), and 258735ce95e6b9a262d1fbc0ddb5620deb3a29d1067David Brownell * spi_sck_bit clear (CPOL=0) 2592e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell */ 2602e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pp->lastbyte |= vcc_bits; 2612e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell parport_write_data(pp->port, pp->lastbyte); 2622e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell msleep(5); 2632e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 2642e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell /* take it out of reset; assume long reset delay */ 2652e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pp->lastbyte |= butterfly_nreset; 2662e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell parport_write_data(pp->port, pp->lastbyte); 2672e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell msleep(100); 2682e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 2692e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 2702e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell /* 2712e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * Start SPI ... for now, hide that we're two physical busses. 2722e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell */ 2732e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell status = spi_bitbang_start(&pp->bitbang); 2742e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell if (status < 0) 2752e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell goto clean2; 2762e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 2779c1da3cb46316e40bac766ce45556dc4fd8df3caDavid Brownell /* Bus 1 lets us talk to at45db041b (firmware disables AVR SPI), AVR 2789c1da3cb46316e40bac766ce45556dc4fd8df3caDavid Brownell * (firmware resets at45, acts as spi slave) or neither (we ignore 2799c1da3cb46316e40bac766ce45556dc4fd8df3caDavid Brownell * both, AVR uses AT45). Here we expect firmware for the first option. 2802e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell */ 2811fc7547d4bfe5c8c8c79e196b955b6fbaa21bfd2Ben Dooks 2822e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pp->info[0].max_speed_hz = 15 * 1000 * 1000; 2832e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell strcpy(pp->info[0].modalias, "mtd_dataflash"); 2842e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pp->info[0].platform_data = &flash; 2852e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pp->info[0].chip_select = 1; 2862e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pp->info[0].controller_data = pp; 2872e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pp->dataflash = spi_new_device(pp->bitbang.master, &pp->info[0]); 2882e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell if (pp->dataflash) 2892e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pr_debug("%s: dataflash at %s\n", p->name, 29035f74fcab1228be03eab5f4d21ddc89fca1bc5b8Kay Sievers dev_name(&pp->dataflash->dev)); 2912e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 2922e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell // dev_info(_what?_, ...) 2932e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pr_info("%s: AVR Butterfly\n", p->name); 2942e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell butterfly = pp; 2952e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell return; 2962e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 2972e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellclean2: 2982e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell /* turn off VCC */ 2992e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell parport_write_data(pp->port, 0); 3002e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 3012e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell parport_release(pp->pd); 3022e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellclean1: 3032e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell parport_unregister_device(pd); 3042e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellclean0: 3052e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell (void) spi_master_put(pp->bitbang.master); 3062e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownelldone: 3072e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pr_debug("%s: butterfly probe, fail %d\n", p->name, status); 3082e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell} 3092e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 3102e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellstatic void butterfly_detach(struct parport *p) 3112e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell{ 3122e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell struct butterfly *pp; 3132e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell int status; 3142e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 3152e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell /* FIXME this global is ugly ... but, how to quickly get from 3162e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * the parport to the "struct butterfly" associated with it? 3172e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell * "old school" driver-internal device lists? 3182e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell */ 3192e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell if (!butterfly || butterfly->port != p) 3202e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell return; 3212e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell pp = butterfly; 3222e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell butterfly = NULL; 3232e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 3249c1da3cb46316e40bac766ce45556dc4fd8df3caDavid Brownell /* stop() unregisters child devices too */ 3252e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell status = spi_bitbang_stop(&pp->bitbang); 3262e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 3272e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell /* turn off VCC */ 3282e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell parport_write_data(pp->port, 0); 3292e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell msleep(10); 3302e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 3312e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell parport_release(pp->pd); 3322e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell parport_unregister_device(pp->pd); 3332e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 3342e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell (void) spi_master_put(pp->bitbang.master); 3352e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell} 3362e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 3372e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellstatic struct parport_driver butterfly_driver = { 3382e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell .name = "spi_butterfly", 3392e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell .attach = butterfly_attach, 3402e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell .detach = butterfly_detach, 3412e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell}; 3422e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 3432e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 3442e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellstatic int __init butterfly_init(void) 3452e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell{ 3462e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell return parport_register_driver(&butterfly_driver); 3472e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell} 3482e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownelldevice_initcall(butterfly_init); 3492e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 3502e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellstatic void __exit butterfly_exit(void) 3512e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell{ 3522e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell parport_unregister_driver(&butterfly_driver); 3532e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell} 3542e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownellmodule_exit(butterfly_exit); 3552e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid Brownell 3569c1da3cb46316e40bac766ce45556dc4fd8df3caDavid BrownellMODULE_DESCRIPTION("Parport Adapter driver for AVR Butterfly"); 3572e10c84b9cf0b2d269c5629048d8d6e35eaf6b2bDavid BrownellMODULE_LICENSE("GPL"); 358