On Mon, 2014-09-29 at 07:22 -0700, Weike Chen wrote:
> There are two SPI controllers exported by PCI subsystem for Intel Quark X1000.
> The SPI memory mapped I/O registers supported by Quark are different from
> the current implementation, and Quark only supports the registers of 'SSCR0',
> 'SSCR1', 'SSSR', 'SSDR', and 'DDS_RATE'. This patch is to enable the SPI for
> Intel Quark X1000.
> 
> This piece of work is derived from Dan O'Donovan's initial work for Intel 
> Quark
> X1000 SPI enabling.

Minor comments are below.

After addressing them
Reviewed-by: Andy Shevchenko <andriy.shevche...@linux.intel.com>


> 
> Signed-off-by: Weike Chen <alvin.c...@intel.com>
> ---
>  drivers/spi/spi-pxa2xx-pci.c |    8 ++
>  drivers/spi/spi-pxa2xx.c     |  204 
> +++++++++++++++++++++++++++++++++++++-----
>  drivers/spi/spi-pxa2xx.h     |   16 ++--
>  include/linux/pxa2xx_ssp.h   |   21 +++++
>  4 files changed, 224 insertions(+), 25 deletions(-)
> 
> diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c
> index b285294..b888aeb 100644
> --- a/drivers/spi/spi-pxa2xx-pci.c
> +++ b/drivers/spi/spi-pxa2xx-pci.c
> @@ -19,6 +19,7 @@ enum {
>       PORT_BSW0,
>       PORT_BSW1,
>       PORT_BSW2,
> +     PORT_QUARK_X1000,
>  };
>  
>  struct pxa_spi_info {
> @@ -92,6 +93,12 @@ static struct pxa_spi_info spi_info_configs[] = {
>               .tx_param = &bsw2_tx_param,
>               .rx_param = &bsw2_rx_param,
>       },
> +     [PORT_QUARK_X1000] = {
> +             .type = QUARK_X1000_SSP,
> +             .port_id = -1,
> +             .num_chipselect = 4,
> +             .max_clk_rate = 50000000,
> +     },
>  };
>  
>  static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
> @@ -192,6 +199,7 @@ static void pxa2xx_spi_pci_remove(struct pci_dev *dev)
>  
>  static const struct pci_device_id pxa2xx_spi_pci_devices[] = {
>       { PCI_VDEVICE(INTEL, 0x2e6a), PORT_CE4100 },
> +     { PCI_VDEVICE(INTEL, 0x0935), PORT_QUARK_X1000 },
>       { PCI_VDEVICE(INTEL, 0x0f0e), PORT_BYT },
>       { PCI_VDEVICE(INTEL, 0x228e), PORT_BSW0 },
>       { PCI_VDEVICE(INTEL, 0x2290), PORT_BSW1 },
> diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
> index e7ff9c5..7efa55e 100644
> --- a/drivers/spi/spi-pxa2xx.c
> +++ b/drivers/spi/spi-pxa2xx.c
> @@ -63,10 +63,64 @@ MODULE_ALIAS("platform:pxa2xx-spi");
>                               | SSCR1_RFT | SSCR1_TFT | SSCR1_MWDS \
>                               | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)
>  
> +#define QUARK_X1000_SSCR1_CHANGE_MASK (QUARK_X1000_SSCR1_STRF        \
> +                             | QUARK_X1000_SSCR1_EFWR        \
> +                             | QUARK_X1000_SSCR1_RFT         \
> +                             | QUARK_X1000_SSCR1_TFT         \
> +                             | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)
> +
>  #define LPSS_RX_THRESH_DFLT  64
>  #define LPSS_TX_LOTHRESH_DFLT        160
>  #define LPSS_TX_HITHRESH_DFLT        224
>  
> +struct quark_spi_rate {
> +     u32 bitrate;
> +     u32 dds_clk_rate;
> +     u32 clk_div;
> +};
> +
> +/*
> + * 'rate', 'dds', 'clk_div' lookup table, which is defined in
> + * the Quark SPI datasheet.
> + */
> +static const struct quark_spi_rate quark_spi_rate_table[] = {
> +/*   bitrate,        dds_clk_rate,   clk_div */
> +     {50000000,      0x800000,       0},
> +     {40000000,      0x666666,       0},
> +     {25000000,      0x400000,       0},
> +     {20000000,      0x666666,       1},
> +     {16667000,      0x800000,       2},
> +     {13333000,      0x666666,       2},
> +     {12500000,      0x200000,       0},
> +     {10000000,      0x800000,       4},
> +     {8000000,       0x666666,       4},
> +     {6250000,       0x400000,       3},
> +     {5000000,       0x400000,       4},
> +     {4000000,       0x666666,       9},
> +     {3125000,       0x80000,        0},
> +     {2500000,       0x400000,       9},
> +     {2000000,       0x666666,       19},
> +     {1563000,       0x40000,        0},
> +     {1250000,       0x200000,       9},
> +     {1000000,       0x400000,       24},
> +     {800000,        0x666666,       49},
> +     {781250,        0x20000,        0},
> +     {625000,        0x200000,       19},
> +     {500000,        0x400000,       49},
> +     {400000,        0x666666,       99},
> +     {390625,        0x10000,        0},
> +     {250000,        0x400000,       99},
> +     {200000,        0x666666,       199},
> +     {195313,        0x8000,         0},
> +     {125000,        0x100000,       49},
> +     {100000,        0x200000,       124},
> +     {50000,         0x100000,       124},
> +     {25000,         0x80000,        124},
> +     {10016,         0x20000,        77},
> +     {5040,          0x20000,        154},
> +     {1002,          0x8000,         194},
> +};
> +

Regarding to table vs. formula I will agree with whatever Mark suggested.

>  /* Offset from drv_data->lpss_base */
>  #define GENERAL_REG          0x08
>  #define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
> @@ -80,9 +134,16 @@ static bool is_lpss_ssp(const struct driver_data 
> *drv_data)
>       return drv_data->ssp_type == LPSS_SSP;
>  }
>  
> +static bool is_quark_x1000_ssp(const struct driver_data *drv_data)
> +{
> +     return drv_data->ssp_type == QUARK_X1000_SSP;
> +}
> +
>  static u32 pxa2xx_spi_get_ssrc1_change_mask(const struct driver_data 
> *drv_data)
>  {
>       switch (drv_data->ssp_type) {
> +     case QUARK_X1000_SSP:
> +             return QUARK_X1000_SSCR1_CHANGE_MASK;
>       default:
>               return SSCR1_CHANGE_MASK;
>       }
> @@ -92,9 +153,12 @@ static u32
>  pxa2xx_spi_get_rx_default_thre(const struct driver_data *drv_data)
>  {
>       switch (drv_data->ssp_type) {
> +     case QUARK_X1000_SSP:
> +             return RX_THRESH_QUARK_X1000_DFLT;
>       default:
>               return RX_THRESH_DFLT;
>       }
> +

Redundant empty line.

>  }
>  
>  static bool pxa2xx_spi_txfifo_full(const struct driver_data *drv_data)
> @@ -103,6 +167,9 @@ static bool pxa2xx_spi_txfifo_full(const struct 
> driver_data *drv_data)
>       u32 mask;
>  
>       switch (drv_data->ssp_type) {
> +     case QUARK_X1000_SSP:
> +             mask = QUARK_X1000_SSSR_TFL_MASK;
> +             break;
>       default:
>               mask = SSSR_TFL_MASK;
>               break;
> @@ -112,11 +179,14 @@ static bool pxa2xx_spi_txfifo_full(const struct 
> driver_data *drv_data)
>  }
>  
>  static void pxa2xx_spi_clear_rx_thre(const struct driver_data *drv_data,
> -                                  u32 *sccr1_reg)
> +                                       u32 *sccr1_reg)

Unnecessary change.

>  {
>       u32 mask;
>  
>       switch (drv_data->ssp_type) {
> +     case QUARK_X1000_SSP:
> +             mask = QUARK_X1000_SSCR1_RFT;
> +             break;
>       default:
>               mask = SSCR1_RFT;
>               break;
> @@ -125,9 +195,12 @@ static void pxa2xx_spi_clear_rx_thre(const struct 
> driver_data *drv_data,
>  }
>  
>  static void pxa2xx_spi_set_rx_thre(const struct driver_data *drv_data,
> -                                u32 *sccr1_reg, u32 threshold)
> +                                     u32 *sccr1_reg, u32 threshold)

Ditto.

Or you may do that in the first patch.

>  {
>       switch (drv_data->ssp_type) {
> +     case QUARK_X1000_SSP:
> +             *sccr1_reg |= QUARK_X1000_SSCR1_RxTresh(threshold);
> +             break;
>       default:
>               *sccr1_reg |= SSCR1_RxTresh(threshold);
>               break;
> @@ -138,6 +211,11 @@ static u32 pxa2xx_configure_sscr0(const struct 
> driver_data *drv_data,
>                                 u32 clk_div, u8 bits)
>  {
>       switch (drv_data->ssp_type) {
> +     case QUARK_X1000_SSP:
> +             return clk_div
> +                     | QUARK_X1000_SSCR0_Motorola
> +                     | QUARK_X1000_SSCR0_DataSize(bits > 32 ? 8 : bits)
> +                     | SSCR0_SSE;
>       default:
>               return clk_div
>                       | SSCR0_Motorola
> @@ -592,7 +670,7 @@ static irqreturn_t interrupt_transfer(struct driver_data 
> *drv_data)
>                               rx_thre = bytes_left;
>  
>                       pxa2xx_spi_set_rx_thre(drv_data, &sccr1_reg,
> -                                            rx_thre);
> +                                                 rx_thre);

Ditto.


>               }
>               write_SSCR1(sccr1_reg, reg);
>       }
> @@ -655,6 +733,28 @@ static irqreturn_t ssp_int(int irq, void *dev_id)
>       return drv_data->transfer_handler(drv_data);
>  }
>  
> +/*
> + * The Quark SPI data sheet gives a table, and for the given 'rate',
> + * the 'dds' and 'clk_div' can be found in the table.
> + */
> +static u32 quark_x1000_set_clk_regvals(u32 rate, u32 *dds, u32 *clk_div)
> +{
> +     unsigned int i;
> +
> +     for (i = 0; i < ARRAY_SIZE(quark_spi_rate_table); i++) {
> +             if (rate >= quark_spi_rate_table[i].bitrate) {
> +                     *dds = quark_spi_rate_table[i].dds_clk_rate;
> +                     *clk_div = quark_spi_rate_table[i].clk_div;
> +                     return quark_spi_rate_table[i].bitrate;
> +             }
> +     }
> +
> +     *dds = quark_spi_rate_table[i-1].dds_clk_rate;
> +     *clk_div = quark_spi_rate_table[i-1].clk_div;
> +
> +     return quark_spi_rate_table[i-1].bitrate;
> +}
> +
>  static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate)
>  {
>       unsigned long ssp_clk = drv_data->max_clk_rate;
> @@ -668,6 +768,20 @@ static unsigned int ssp_get_clk_div(struct driver_data 
> *drv_data, int rate)
>               return ((ssp_clk / rate - 1) & 0xfff) << 8;
>  }
>  
> +static unsigned int pxa2xx_ssp_get_clk_div(struct driver_data *drv_data,
> +                                        struct chip_data *chip, int rate)
> +{
> +     u32 clk_div;
> +
> +     switch (drv_data->ssp_type) {
> +     case QUARK_X1000_SSP:
> +             quark_x1000_set_clk_regvals(rate, &chip->dds_rate, &clk_div);
> +             return clk_div << 8;
> +     default:
> +             return ssp_get_clk_div(drv_data, rate);
> +     }
> +}
> +
>  static void pump_transfers(unsigned long data)
>  {
>       struct driver_data *drv_data = (struct driver_data *)data;
> @@ -770,7 +884,7 @@ static void pump_transfers(unsigned long data)
>               if (transfer->bits_per_word)
>                       bits = transfer->bits_per_word;
>  
> -             clk_div = ssp_get_clk_div(drv_data, speed);
> +             clk_div = pxa2xx_ssp_get_clk_div(drv_data, chip, speed);
>  
>               if (bits <= 8) {
>                       drv_data->n_bytes = 1;
> @@ -838,6 +952,10 @@ static void pump_transfers(unsigned long data)
>                       write_SSITF(chip->lpss_tx_threshold, reg);
>       }
>  
> +     if (is_quark_x1000_ssp(drv_data) &&
> +         (read_DDS_RATE(reg) != chip->dds_rate))

Could it be one line?

> +             write_DDS_RATE(chip->dds_rate, reg);
> +
>       /* see if we need to reload the config registers */
>       if ((read_SSCR0(reg) != cr0) ||
>           (read_SSCR1(reg) & change_mask) != (cr1 & change_mask)) {
> @@ -941,14 +1059,22 @@ static int setup(struct spi_device *spi)
>       unsigned int clk_div;
>       uint tx_thres, tx_hi_thres, rx_thres;
>  
> -     if (is_lpss_ssp(drv_data)) {
> +     switch (drv_data->ssp_type) {
> +     case QUARK_X1000_SSP:
> +             tx_thres = TX_THRESH_QUARK_X1000_DFLT;
> +             tx_hi_thres = 0;
> +             rx_thres = RX_THRESH_QUARK_X1000_DFLT;
> +             break;
> +     case LPSS_SSP:
>               tx_thres = LPSS_TX_LOTHRESH_DFLT;
>               tx_hi_thres = LPSS_TX_HITHRESH_DFLT;
>               rx_thres = LPSS_RX_THRESH_DFLT;
> -     } else {
> +             break;
> +     default:
>               tx_thres = TX_THRESH_DFLT;
>               tx_hi_thres = 0;
>               rx_thres = RX_THRESH_DFLT;
> +             break;
>       }
>  
>       /* Only alloc on first setup */
> @@ -1001,9 +1127,6 @@ static int setup(struct spi_device *spi)
>               chip->enable_dma = drv_data->master_info->enable_dma;
>       }
>  
> -     chip->threshold = (SSCR1_RxTresh(rx_thres) & SSCR1_RFT) |
> -                     (SSCR1_TxTresh(tx_thres) & SSCR1_TFT);
> -
>       chip->lpss_rx_threshold = SSIRF_RxThresh(rx_thres);
>       chip->lpss_tx_threshold = SSITF_TxLoThresh(tx_thres)
>                               | SSITF_TxHiThresh(tx_hi_thres);
> @@ -1022,11 +1145,24 @@ static int setup(struct spi_device *spi)
>               }
>       }
>  
> -     clk_div = ssp_get_clk_div(drv_data, spi->max_speed_hz);
> +     clk_div = pxa2xx_ssp_get_clk_div(drv_data, chip, spi->max_speed_hz);
>       chip->speed_hz = spi->max_speed_hz;
>  
>       chip->cr0 = pxa2xx_configure_sscr0(drv_data, clk_div,
>                                          spi->bits_per_word);
> +     switch (drv_data->ssp_type) {
> +     case QUARK_X1000_SSP:
> +             chip->threshold = (QUARK_X1000_SSCR1_RxTresh(rx_thres)
> +                                & QUARK_X1000_SSCR1_RFT)
> +                                | (QUARK_X1000_SSCR1_TxTresh(tx_thres)
> +                                & QUARK_X1000_SSCR1_TFT);
> +             break;
> +     default:
> +             chip->threshold = (SSCR1_RxTresh(rx_thres) & SSCR1_RFT) |
> +                     (SSCR1_TxTresh(tx_thres) & SSCR1_TFT);
> +             break;
> +     }
> +
>       chip->cr1 &= ~(SSCR1_SPO | SSCR1_SPH);
>       chip->cr1 |= (((spi->mode & SPI_CPHA) != 0) ? SSCR1_SPH : 0)
>                       | (((spi->mode & SPI_CPOL) != 0) ? SSCR1_SPO : 0);
> @@ -1055,7 +1191,8 @@ static int setup(struct spi_device *spi)
>               chip->read = u16_reader;
>               chip->write = u16_writer;
>       } else if (spi->bits_per_word <= 32) {
> -             chip->cr0 |= SSCR0_EDSS;
> +             if (!is_quark_x1000_ssp(drv_data))
> +                     chip->cr0 |= SSCR0_EDSS;
>               chip->n_bytes = 4;
>               chip->read = u32_reader;
>               chip->write = u32_writer;
> @@ -1205,7 +1342,15 @@ static int pxa2xx_spi_probe(struct platform_device 
> *pdev)
>       drv_data->ioaddr = ssp->mmio_base;
>       drv_data->ssdr_physical = ssp->phys_base + SSDR;
>       if (pxa25x_ssp_comp(drv_data)) {
> -             master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
> +             switch (drv_data->ssp_type) {
> +             case QUARK_X1000_SSP:
> +                     master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
> +                     break;
> +             default:
> +                     master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
> +                     break;
> +             }
> +
>               drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE;
>               drv_data->dma_cr1 = 0;
>               drv_data->clear_sr = SSSR_ROR;
> @@ -1243,16 +1388,35 @@ static int pxa2xx_spi_probe(struct platform_device 
> *pdev)
>  
>       /* Load default SSP configuration */
>       write_SSCR0(0, drv_data->ioaddr);
> -     write_SSCR1(SSCR1_RxTresh(RX_THRESH_DFLT) |
> -                             SSCR1_TxTresh(TX_THRESH_DFLT),
> -                             drv_data->ioaddr);
> -     write_SSCR0(SSCR0_SCR(2)
> -                     | SSCR0_Motorola
> -                     | SSCR0_DataSize(8),
> -                     drv_data->ioaddr);
> +     switch (drv_data->ssp_type) {
> +     case QUARK_X1000_SSP:
> +             write_SSCR1(QUARK_X1000_SSCR1_RxTresh(
> +                                     RX_THRESH_QUARK_X1000_DFLT) |
> +                         QUARK_X1000_SSCR1_TxTresh(
> +                                     TX_THRESH_QUARK_X1000_DFLT),
> +                         drv_data->ioaddr);
> +
> +             /* using the Motorola SPI protocol and use 8 bit frame */
> +             write_SSCR0(QUARK_X1000_SSCR0_Motorola
> +                         | QUARK_X1000_SSCR0_DataSize(8),
> +                         drv_data->ioaddr);
> +             break;
> +     default:
> +             write_SSCR1(SSCR1_RxTresh(RX_THRESH_DFLT) |
> +                         SSCR1_TxTresh(TX_THRESH_DFLT),
> +                         drv_data->ioaddr);
> +             write_SSCR0(SSCR0_SCR(2)
> +                         | SSCR0_Motorola
> +                         | SSCR0_DataSize(8),
> +                         drv_data->ioaddr);
> +             break;
> +     }
> +
>       if (!pxa25x_ssp_comp(drv_data))
>               write_SSTO(0, drv_data->ioaddr);
> -     write_SSPSP(0, drv_data->ioaddr);
> +
> +     if (!is_quark_x1000_ssp(drv_data))
> +             write_SSPSP(0, drv_data->ioaddr);
>  
>       lpss_ssp_setup(drv_data);
>  
> diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h
> index 5adc2a1..6bec59c 100644
> --- a/drivers/spi/spi-pxa2xx.h
> +++ b/drivers/spi/spi-pxa2xx.h
> @@ -93,6 +93,7 @@ struct driver_data {
>  struct chip_data {
>       u32 cr0;
>       u32 cr1;
> +     u32 dds_rate;
>       u32 psp;
>       u32 timeout;
>       u8 n_bytes;
> @@ -126,6 +127,7 @@ DEFINE_SSP_REG(SSCR1, 0x04)
>  DEFINE_SSP_REG(SSSR, 0x08)
>  DEFINE_SSP_REG(SSITR, 0x0c)
>  DEFINE_SSP_REG(SSDR, 0x10)
> +DEFINE_SSP_REG(DDS_RATE, 0x28)  /* DDS Clock Rate */
>  DEFINE_SSP_REG(SSTO, 0x28)
>  DEFINE_SSP_REG(SSPSP, 0x2c)
>  DEFINE_SSP_REG(SSITF, SSITF)
> @@ -141,18 +143,22 @@ DEFINE_SSP_REG(SSIRF, SSIRF)
>  
>  static inline int pxa25x_ssp_comp(struct driver_data *drv_data)
>  {
> -     if (drv_data->ssp_type == PXA25x_SSP)
> +     switch (drv_data->ssp_type) {
> +     case PXA25x_SSP:
> +     case CE4100_SSP:
> +     case QUARK_X1000_SSP:
>               return 1;
> -     if (drv_data->ssp_type == CE4100_SSP)
> -             return 1;
> -     return 0;
> +     default:
> +             return 0;
> +     }
>  }
>  
>  static inline void write_SSSR_CS(struct driver_data *drv_data, u32 val)
>  {
>       void __iomem *reg = drv_data->ioaddr;
>  
> -     if (drv_data->ssp_type == CE4100_SSP)
> +     if (drv_data->ssp_type == CE4100_SSP ||
> +         drv_data->ssp_type == QUARK_X1000_SSP)
>               val |= read_SSSR(reg) & SSSR_ALT_FRM_MASK;
>  
>       write_SSSR(val, reg);
> diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h
> index f2b4051..a4668e6 100644
> --- a/include/linux/pxa2xx_ssp.h
> +++ b/include/linux/pxa2xx_ssp.h
> @@ -106,6 +106,26 @@
>  #define SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..4] */
>  #define SSCR1_RFT    (0x00000c00)    /* Receive FIFO Threshold (mask) */
>  #define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..4] */
> +
> +/* QUARK_X1000 SSCR0 bit definition */
> +#define QUARK_X1000_SSCR0_DSS        (0x1F)          /* Data Size Select 
> (mask) */
> +#define QUARK_X1000_SSCR0_DataSize(x)  ((x) - 1)     /* Data Size Select 
> [4..32] */
> +#define QUARK_X1000_SSCR0_FRF        (0x3 << 5)      /* FRame Format (mask) 
> */
> +#define QUARK_X1000_SSCR0_Motorola   (0x0 << 5)      /* Motorola's Serial 
> Peripheral Interface (SPI) */
> +
> +#define RX_THRESH_QUARK_X1000_DFLT   1
> +#define TX_THRESH_QUARK_X1000_DFLT   16
> +
> +#define QUARK_X1000_SSSR_TFL_MASK    (0x1F << 8)     /* Transmit FIFO Level 
> mask */
> +#define QUARK_X1000_SSSR_RFL_MASK    (0x1F << 13)    /* Receive FIFO Level 
> mask */
> +
> +#define QUARK_X1000_SSCR1_TFT        (0x1F << 6)     /* Transmit FIFO 
> Threshold (mask) */
> +#define QUARK_X1000_SSCR1_TxTresh(x) (((x) - 1) << 6)        /* level 
> [1..32] */
> +#define QUARK_X1000_SSCR1_RFT        (0x1F << 11)    /* Receive FIFO 
> Threshold (mask) */
> +#define QUARK_X1000_SSCR1_RxTresh(x) (((x) - 1) << 11)       /* level 
> [1..32] */
> +#define QUARK_X1000_SSCR1_STRF       (1 << 17)               /* Select FIFO 
> or EFWR */
> +#define QUARK_X1000_SSCR1_EFWR       (1 << 16)               /* Enable FIFO 
> Write/Read */
> +
>  #endif
>  
>  /* extra bits in PXA255, PXA26x and PXA27x SSP ports */
> @@ -175,6 +195,7 @@ enum pxa_ssp_type {
>       PXA910_SSP,
>       CE4100_SSP,
>       LPSS_SSP,
> +     QUARK_X1000_SSP,
>  };
>  
>  struct ssp_device {


-- 
Andy Shevchenko <andriy.shevche...@linux.intel.com>
Intel Finland Oy


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to