Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/spi/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 7 kB image not shown  

Quelle  spi-ljca.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Intel La Jolla Cove Adapter USB-SPI driver
 *
 * Copyright (c) 2023, Intel Corporation.
 */


#include <linux/auxiliary_bus.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/dev_printk.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/usb/ljca.h>

#define LJCA_SPI_BUS_MAX_HZ  48000000

#define LJCA_SPI_BUF_SIZE  60u
#define LJCA_SPI_MAX_XFER_SIZE  \
 (LJCA_SPI_BUF_SIZE - sizeof(struct ljca_spi_xfer_packet))

#define LJCA_SPI_CLK_MODE_POLARITY BIT(0)
#define LJCA_SPI_CLK_MODE_PHASE  BIT(1)

#define LJCA_SPI_XFER_INDICATOR_ID GENMASK(5, 0)
#define LJCA_SPI_XFER_INDICATOR_CMPL BIT(6)
#define LJCA_SPI_XFER_INDICATOR_INDEX BIT(7)

/* SPI commands */
enum ljca_spi_cmd {
 LJCA_SPI_INIT = 1,
 LJCA_SPI_READ,
 LJCA_SPI_WRITE,
 LJCA_SPI_WRITEREAD,
 LJCA_SPI_DEINIT,
};

enum {
 LJCA_SPI_BUS_SPEED_24M,
 LJCA_SPI_BUS_SPEED_12M,
 LJCA_SPI_BUS_SPEED_8M,
 LJCA_SPI_BUS_SPEED_6M,
 LJCA_SPI_BUS_SPEED_4_8M, /*4.8MHz*/
 LJCA_SPI_BUS_SPEED_MIN = LJCA_SPI_BUS_SPEED_4_8M,
};

enum {
 LJCA_SPI_CLOCK_LOW_POLARITY,
 LJCA_SPI_CLOCK_HIGH_POLARITY,
};

enum {
 LJCA_SPI_CLOCK_FIRST_PHASE,
 LJCA_SPI_CLOCK_SECOND_PHASE,
};

struct ljca_spi_init_packet {
 u8 index;
 u8 speed;
 u8 mode;
} __packed;

struct ljca_spi_xfer_packet {
 u8 indicator;
 u8 len;
 u8 data[] __counted_by(len);
} __packed;

struct ljca_spi_dev {
 struct ljca_client *ljca;
 struct spi_controller *controller;
 struct ljca_spi_info *spi_info;
 u8 speed;
 u8 mode;

 u8 obuf[LJCA_SPI_BUF_SIZE];
 u8 ibuf[LJCA_SPI_BUF_SIZE];
};

static int ljca_spi_read_write(struct ljca_spi_dev *ljca_spi, const u8 *w_data,
          u8 *r_data, int len, int id, int complete,
          int cmd)
{
 struct ljca_spi_xfer_packet *w_packet =
   (struct ljca_spi_xfer_packet *)ljca_spi->obuf;
 struct ljca_spi_xfer_packet *r_packet =
   (struct ljca_spi_xfer_packet *)ljca_spi->ibuf;
 int ret;

 w_packet->indicator = FIELD_PREP(LJCA_SPI_XFER_INDICATOR_ID, id) |
         FIELD_PREP(LJCA_SPI_XFER_INDICATOR_CMPL, complete) |
         FIELD_PREP(LJCA_SPI_XFER_INDICATOR_INDEX,
      ljca_spi->spi_info->id);

 if (cmd == LJCA_SPI_READ) {
  w_packet->len = sizeof(u16);
  *(__le16 *)&w_packet->data[0] = cpu_to_le16(len);
 } else {
  w_packet->len = len;
  memcpy(w_packet->data, w_data, len);
 }

 ret = ljca_transfer(ljca_spi->ljca, cmd, (u8 *)w_packet,
       struct_size(w_packet, data, w_packet->len),
       (u8 *)r_packet, LJCA_SPI_BUF_SIZE);
 if (ret < 0)
  return ret;
 else if (ret < sizeof(*r_packet) || r_packet->len <= 0)
  return -EIO;

 if (r_data)
  memcpy(r_data, r_packet->data, r_packet->len);

 return 0;
}

static int ljca_spi_init(struct ljca_spi_dev *ljca_spi, u8 div, u8 mode)
{
 struct ljca_spi_init_packet w_packet = {};
 int ret;

 if (ljca_spi->mode == mode && ljca_spi->speed == div)
  return 0;

 w_packet.index = ljca_spi->spi_info->id;
 w_packet.speed = div;
 w_packet.mode = FIELD_PREP(LJCA_SPI_CLK_MODE_POLARITY,
       (mode & SPI_CPOL) ? LJCA_SPI_CLOCK_HIGH_POLARITY :
             LJCA_SPI_CLOCK_LOW_POLARITY) |
   FIELD_PREP(LJCA_SPI_CLK_MODE_PHASE,
       (mode & SPI_CPHA) ? LJCA_SPI_CLOCK_SECOND_PHASE :
             LJCA_SPI_CLOCK_FIRST_PHASE);

 ret = ljca_transfer(ljca_spi->ljca, LJCA_SPI_INIT, (u8 *)&w_packet,
       sizeof(w_packet), NULL, 0);
 if (ret < 0)
  return ret;

 ljca_spi->mode = mode;
 ljca_spi->speed = div;

 return 0;
}

static int ljca_spi_deinit(struct ljca_spi_dev *ljca_spi)
{
 struct ljca_spi_init_packet w_packet = {};
 int ret;

 w_packet.index = ljca_spi->spi_info->id;

 ret = ljca_transfer(ljca_spi->ljca, LJCA_SPI_DEINIT, (u8 *)&w_packet,
       sizeof(w_packet), NULL, 0);

 return ret < 0 ? ret : 0;
}

static inline int ljca_spi_transfer(struct ljca_spi_dev *ljca_spi,
        const u8 *tx_data, u8 *rx_data, u16 len)
{
 int complete, cur_len;
 int remaining = len;
 int cmd, ret, i;
 int offset = 0;

 if (tx_data && rx_data)
  cmd = LJCA_SPI_WRITEREAD;
 else if (tx_data)
  cmd = LJCA_SPI_WRITE;
 else if (rx_data)
  cmd = LJCA_SPI_READ;
 else
  return -EINVAL;

 for (i = 0; remaining > 0; i++) {
  cur_len = min_t(unsigned int, remaining, LJCA_SPI_MAX_XFER_SIZE);
  complete = (cur_len == remaining);

  ret = ljca_spi_read_write(ljca_spi,
       tx_data ? tx_data + offset : NULL,
       rx_data ? rx_data + offset : NULL,
       cur_len, i, complete, cmd);
  if (ret)
   return ret;

  offset += cur_len;
  remaining -= cur_len;
 }

 return 0;
}

static int ljca_spi_transfer_one(struct spi_controller *controller,
     struct spi_device *spi,
     struct spi_transfer *xfer)
{
 u8 div = DIV_ROUND_UP(controller->max_speed_hz, xfer->speed_hz) / 2 - 1;
 struct ljca_spi_dev *ljca_spi = spi_controller_get_devdata(controller);
 int ret;

 div = min_t(u8, LJCA_SPI_BUS_SPEED_MIN, div);

 ret = ljca_spi_init(ljca_spi, div, spi->mode);
 if (ret) {
  dev_err(&ljca_spi->ljca->auxdev.dev,
   "cannot initialize transfer ret %d\n", ret);
  return ret;
 }

 ret = ljca_spi_transfer(ljca_spi, xfer->tx_buf, xfer->rx_buf, xfer->len);
 if (ret)
  dev_err(&ljca_spi->ljca->auxdev.dev,
   "transfer failed len: %d\n", xfer->len);

 return ret;
}

static int ljca_spi_probe(struct auxiliary_device *auxdev,
     const struct auxiliary_device_id *aux_dev_id)
{
 struct ljca_client *ljca = auxiliary_dev_to_ljca_client(auxdev);
 struct spi_controller *controller;
 struct ljca_spi_dev *ljca_spi;
 int ret;

 controller = devm_spi_alloc_host(&auxdev->dev, sizeof(*ljca_spi));
 if (!controller)
  return -ENOMEM;

 ljca_spi = spi_controller_get_devdata(controller);
 ljca_spi->ljca = ljca;
 ljca_spi->spi_info = dev_get_platdata(&auxdev->dev);
 ljca_spi->controller = controller;

 controller->bus_num = -1;
 controller->mode_bits = SPI_CPHA | SPI_CPOL;
 controller->transfer_one = ljca_spi_transfer_one;
 controller->auto_runtime_pm = false;
 controller->max_speed_hz = LJCA_SPI_BUS_MAX_HZ;

 device_set_node(&ljca_spi->controller->dev, dev_fwnode(&auxdev->dev));
 auxiliary_set_drvdata(auxdev, controller);

 ret = spi_register_controller(controller);
 if (ret)
  dev_err(&auxdev->dev, "Failed to register controller\n");

 return ret;
}

static void ljca_spi_dev_remove(struct auxiliary_device *auxdev)
{
 struct spi_controller *controller = auxiliary_get_drvdata(auxdev);
 struct ljca_spi_dev *ljca_spi = spi_controller_get_devdata(controller);

 spi_unregister_controller(controller);
 ljca_spi_deinit(ljca_spi);
}

static int ljca_spi_dev_suspend(struct device *dev)
{
 struct spi_controller *controller = dev_get_drvdata(dev);

 return spi_controller_suspend(controller);
}

static int ljca_spi_dev_resume(struct device *dev)
{
 struct spi_controller *controller = dev_get_drvdata(dev);

 return spi_controller_resume(controller);
}

static const struct dev_pm_ops ljca_spi_pm = {
 SYSTEM_SLEEP_PM_OPS(ljca_spi_dev_suspend, ljca_spi_dev_resume)
};

static const struct auxiliary_device_id ljca_spi_id_table[] = {
 { "usb_ljca.ljca-spi", 0 },
 { /* sentinel */ }
};
MODULE_DEVICE_TABLE(auxiliary, ljca_spi_id_table);

static struct auxiliary_driver ljca_spi_driver = {
 .driver.pm = &ljca_spi_pm,
 .probe  = ljca_spi_probe,
 .remove  = ljca_spi_dev_remove,
 .id_table = ljca_spi_id_table,
};
module_auxiliary_driver(ljca_spi_driver);

MODULE_AUTHOR("Wentong Wu ");
MODULE_AUTHOR("Zhifeng Wang ");
MODULE_AUTHOR("Lixu Zhang ");
MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB-SPI driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("LJCA");

Messung V0.5
C=95 H=94 G=94

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.