Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * parport-to-butterfly adapter
0004  *
0005  * Copyright (C) 2005 David Brownell
0006  */
0007 #include <linux/kernel.h>
0008 #include <linux/init.h>
0009 #include <linux/delay.h>
0010 #include <linux/module.h>
0011 #include <linux/device.h>
0012 #include <linux/parport.h>
0013 
0014 #include <linux/sched.h>
0015 #include <linux/spi/spi.h>
0016 #include <linux/spi/spi_bitbang.h>
0017 #include <linux/spi/flash.h>
0018 
0019 #include <linux/mtd/partitions.h>
0020 
0021 /*
0022  * This uses SPI to talk with an "AVR Butterfly", which is a $US20 card
0023  * with a battery powered AVR microcontroller and lots of goodies.  You
0024  * can use GCC to develop firmware for this.
0025  *
0026  * See Documentation/spi/butterfly.rst for information about how to build
0027  * and use this custom parallel port cable.
0028  */
0029 
0030 /* DATA output bits (pins 2..9 == D0..D7) */
0031 #define butterfly_nreset (1 << 1)       /* pin 3 */
0032 
0033 #define spi_sck_bit (1 << 0)        /* pin 2 */
0034 #define spi_mosi_bit    (1 << 7)        /* pin 9 */
0035 
0036 #define vcc_bits    ((1 << 6) | (1 << 5))   /* pins 7, 8 */
0037 
0038 /* STATUS input bits */
0039 #define spi_miso_bit    PARPORT_STATUS_BUSY /* pin 11 */
0040 
0041 /* CONTROL output bits */
0042 #define spi_cs_bit  PARPORT_CONTROL_SELECT  /* pin 17 */
0043 
0044 static inline struct butterfly *spidev_to_pp(struct spi_device *spi)
0045 {
0046     return spi->controller_data;
0047 }
0048 
0049 struct butterfly {
0050     /* REVISIT ... for now, this must be first */
0051     struct spi_bitbang  bitbang;
0052 
0053     struct parport      *port;
0054     struct pardevice    *pd;
0055 
0056     u8          lastbyte;
0057 
0058     struct spi_device   *dataflash;
0059     struct spi_device   *butterfly;
0060     struct spi_board_info   info[2];
0061 
0062 };
0063 
0064 /*----------------------------------------------------------------------*/
0065 
0066 static inline void
0067 setsck(struct spi_device *spi, int is_on)
0068 {
0069     struct butterfly    *pp = spidev_to_pp(spi);
0070     u8          bit, byte = pp->lastbyte;
0071 
0072     bit = spi_sck_bit;
0073 
0074     if (is_on)
0075         byte |= bit;
0076     else
0077         byte &= ~bit;
0078     parport_write_data(pp->port, byte);
0079     pp->lastbyte = byte;
0080 }
0081 
0082 static inline void
0083 setmosi(struct spi_device *spi, int is_on)
0084 {
0085     struct butterfly    *pp = spidev_to_pp(spi);
0086     u8          bit, byte = pp->lastbyte;
0087 
0088     bit = spi_mosi_bit;
0089 
0090     if (is_on)
0091         byte |= bit;
0092     else
0093         byte &= ~bit;
0094     parport_write_data(pp->port, byte);
0095     pp->lastbyte = byte;
0096 }
0097 
0098 static inline int getmiso(struct spi_device *spi)
0099 {
0100     struct butterfly    *pp = spidev_to_pp(spi);
0101     int         value;
0102     u8          bit;
0103 
0104     bit = spi_miso_bit;
0105 
0106     /* only STATUS_BUSY is NOT negated */
0107     value = !(parport_read_status(pp->port) & bit);
0108     return (bit == PARPORT_STATUS_BUSY) ? value : !value;
0109 }
0110 
0111 static void butterfly_chipselect(struct spi_device *spi, int value)
0112 {
0113     struct butterfly    *pp = spidev_to_pp(spi);
0114 
0115     /* set default clock polarity */
0116     if (value != BITBANG_CS_INACTIVE)
0117         setsck(spi, spi->mode & SPI_CPOL);
0118 
0119     /* here, value == "activate or not";
0120      * most PARPORT_CONTROL_* bits are negated, so we must
0121      * morph it to value == "bit value to write in control register"
0122      */
0123     if (spi_cs_bit == PARPORT_CONTROL_INIT)
0124         value = !value;
0125 
0126     parport_frob_control(pp->port, spi_cs_bit, value ? spi_cs_bit : 0);
0127 }
0128 
0129 /* we only needed to implement one mode here, and choose SPI_MODE_0 */
0130 
0131 #define spidelay(X) do { } while (0)
0132 /* #define spidelay ndelay */
0133 
0134 #include "spi-bitbang-txrx.h"
0135 
0136 static u32
0137 butterfly_txrx_word_mode0(struct spi_device *spi, unsigned nsecs, u32 word,
0138               u8 bits, unsigned flags)
0139 {
0140     return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits);
0141 }
0142 
0143 /*----------------------------------------------------------------------*/
0144 
0145 /* override default partitioning with cmdlinepart */
0146 static struct mtd_partition partitions[] = { {
0147     /* JFFS2 wants partitions of 4*N blocks for this device,
0148      * so sectors 0 and 1 can't be partitions by themselves.
0149      */
0150 
0151     /* sector 0 = 8 pages * 264 bytes/page (1 block)
0152      * sector 1 = 248 pages * 264 bytes/page
0153      */
0154     .name       = "bookkeeping",    /* 66 KB */
0155     .offset     = 0,
0156     .size       = (8 + 248) * 264,
0157     /* .mask_flags  = MTD_WRITEABLE, */
0158 }, {
0159     /* sector 2 = 256 pages * 264 bytes/page
0160      * sectors 3-5 = 512 pages * 264 bytes/page
0161      */
0162     .name       = "filesystem",     /* 462 KB */
0163     .offset     = MTDPART_OFS_APPEND,
0164     .size       = MTDPART_SIZ_FULL,
0165 } };
0166 
0167 static struct flash_platform_data flash = {
0168     .name       = "butterflash",
0169     .parts      = partitions,
0170     .nr_parts   = ARRAY_SIZE(partitions),
0171 };
0172 
0173 /* REVISIT remove this ugly global and its "only one" limitation */
0174 static struct butterfly *butterfly;
0175 
0176 static void butterfly_attach(struct parport *p)
0177 {
0178     struct pardevice    *pd;
0179     int         status;
0180     struct butterfly    *pp;
0181     struct spi_master   *master;
0182     struct device       *dev = p->physport->dev;
0183     struct pardev_cb    butterfly_cb;
0184 
0185     if (butterfly || !dev)
0186         return;
0187 
0188     /* REVISIT:  this just _assumes_ a butterfly is there ... no probe,
0189      * and no way to be selective about what it binds to.
0190      */
0191 
0192     master = spi_alloc_master(dev, sizeof(*pp));
0193     if (!master) {
0194         status = -ENOMEM;
0195         goto done;
0196     }
0197     pp = spi_master_get_devdata(master);
0198 
0199     /*
0200      * SPI and bitbang hookup
0201      *
0202      * use default setup(), cleanup(), and transfer() methods; and
0203      * only bother implementing mode 0.  Start it later.
0204      */
0205     master->bus_num = 42;
0206     master->num_chipselect = 2;
0207 
0208     pp->bitbang.master = master;
0209     pp->bitbang.chipselect = butterfly_chipselect;
0210     pp->bitbang.txrx_word[SPI_MODE_0] = butterfly_txrx_word_mode0;
0211 
0212     /*
0213      * parport hookup
0214      */
0215     pp->port = p;
0216     memset(&butterfly_cb, 0, sizeof(butterfly_cb));
0217     butterfly_cb.private = pp;
0218     pd = parport_register_dev_model(p, "spi_butterfly", &butterfly_cb, 0);
0219     if (!pd) {
0220         status = -ENOMEM;
0221         goto clean0;
0222     }
0223     pp->pd = pd;
0224 
0225     status = parport_claim(pd);
0226     if (status < 0)
0227         goto clean1;
0228 
0229     /*
0230      * Butterfly reset, powerup, run firmware
0231      */
0232     pr_debug("%s: powerup/reset Butterfly\n", p->name);
0233 
0234     /* nCS for dataflash (this bit is inverted on output) */
0235     parport_frob_control(pp->port, spi_cs_bit, 0);
0236 
0237     /* stabilize power with chip in reset (nRESET), and
0238      * spi_sck_bit clear (CPOL=0)
0239      */
0240     pp->lastbyte |= vcc_bits;
0241     parport_write_data(pp->port, pp->lastbyte);
0242     msleep(5);
0243 
0244     /* take it out of reset; assume long reset delay */
0245     pp->lastbyte |= butterfly_nreset;
0246     parport_write_data(pp->port, pp->lastbyte);
0247     msleep(100);
0248 
0249     /*
0250      * Start SPI ... for now, hide that we're two physical busses.
0251      */
0252     status = spi_bitbang_start(&pp->bitbang);
0253     if (status < 0)
0254         goto clean2;
0255 
0256     /* Bus 1 lets us talk to at45db041b (firmware disables AVR SPI), AVR
0257      * (firmware resets at45, acts as spi slave) or neither (we ignore
0258      * both, AVR uses AT45).  Here we expect firmware for the first option.
0259      */
0260 
0261     pp->info[0].max_speed_hz = 15 * 1000 * 1000;
0262     strcpy(pp->info[0].modalias, "mtd_dataflash");
0263     pp->info[0].platform_data = &flash;
0264     pp->info[0].chip_select = 1;
0265     pp->info[0].controller_data = pp;
0266     pp->dataflash = spi_new_device(pp->bitbang.master, &pp->info[0]);
0267     if (pp->dataflash)
0268         pr_debug("%s: dataflash at %s\n", p->name,
0269              dev_name(&pp->dataflash->dev));
0270 
0271     pr_info("%s: AVR Butterfly\n", p->name);
0272     butterfly = pp;
0273     return;
0274 
0275 clean2:
0276     /* turn off VCC */
0277     parport_write_data(pp->port, 0);
0278 
0279     parport_release(pp->pd);
0280 clean1:
0281     parport_unregister_device(pd);
0282 clean0:
0283     spi_master_put(pp->bitbang.master);
0284 done:
0285     pr_debug("%s: butterfly probe, fail %d\n", p->name, status);
0286 }
0287 
0288 static void butterfly_detach(struct parport *p)
0289 {
0290     struct butterfly    *pp;
0291 
0292     /* FIXME this global is ugly ... but, how to quickly get from
0293      * the parport to the "struct butterfly" associated with it?
0294      * "old school" driver-internal device lists?
0295      */
0296     if (!butterfly || butterfly->port != p)
0297         return;
0298     pp = butterfly;
0299     butterfly = NULL;
0300 
0301     /* stop() unregisters child devices too */
0302     spi_bitbang_stop(&pp->bitbang);
0303 
0304     /* turn off VCC */
0305     parport_write_data(pp->port, 0);
0306     msleep(10);
0307 
0308     parport_release(pp->pd);
0309     parport_unregister_device(pp->pd);
0310 
0311     spi_master_put(pp->bitbang.master);
0312 }
0313 
0314 static struct parport_driver butterfly_driver = {
0315     .name =     "spi_butterfly",
0316     .match_port =   butterfly_attach,
0317     .detach =   butterfly_detach,
0318     .devmodel = true,
0319 };
0320 module_parport_driver(butterfly_driver);
0321 
0322 MODULE_DESCRIPTION("Parport Adapter driver for AVR Butterfly");
0323 MODULE_LICENSE("GPL");