static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
struct device_node *nc)
{
- u32 value, cs[SPI_DEVICE_CS_CNT_MAX];
- int rc, idx;
+ u32 value, cs[SPI_DEVICE_CS_CNT_MAX], map[SPI_DEVICE_DATA_LANE_CNT_MAX];
+ int rc, idx, max_num_data_lanes;
/* Mode (clock phase/polarity/etc.) */
if (of_property_read_bool(nc, "spi-cpha"))
spi->mode |= SPI_CS_HIGH;
/* Device DUAL/QUAD mode */
- if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {
+
+ rc = of_property_read_variable_u32_array(nc, "spi-tx-lane-map", map, 1,
+ ARRAY_SIZE(map));
+ if (rc >= 0) {
+ max_num_data_lanes = rc;
+ for (idx = 0; idx < max_num_data_lanes; idx++)
+ spi->tx_lane_map[idx] = map[idx];
+ } else if (rc == -EINVAL) {
+ /* Default lane map is identity mapping. */
+ max_num_data_lanes = ARRAY_SIZE(spi->tx_lane_map);
+ for (idx = 0; idx < max_num_data_lanes; idx++)
+ spi->tx_lane_map[idx] = idx;
+ } else {
+ dev_err(&ctlr->dev,
+ "failed to read spi-tx-lane-map property: %d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_count_u32_elems(nc, "spi-tx-bus-width");
+ if (rc < 0 && rc != -EINVAL) {
+ dev_err(&ctlr->dev,
+ "failed to read spi-tx-bus-width property: %d\n", rc);
+ return rc;
+ }
+ if (rc > max_num_data_lanes) {
+ dev_err(&ctlr->dev,
+ "spi-tx-bus-width has more elements (%d) than spi-tx-lane-map (%d)\n",
+ rc, max_num_data_lanes);
+ return -EINVAL;
+ }
+
+ if (rc == -EINVAL) {
+ /* Default when property is not present. */
+ spi->num_tx_lanes = 1;
+ } else {
+ u32 first_value;
+
+ spi->num_tx_lanes = rc;
+
+ for (idx = 0; idx < spi->num_tx_lanes; idx++) {
+ rc = of_property_read_u32_index(nc, "spi-tx-bus-width",
+ idx, &value);
+ if (rc)
+ return rc;
+
+ /*
+ * For now, we only support all lanes having the same
+ * width so we can keep using the existing mode flags.
+ */
+ if (!idx)
+ first_value = value;
+ else if (first_value != value) {
+ dev_err(&ctlr->dev,
+ "spi-tx-bus-width has inconsistent values: first %d vs later %d\n",
+ first_value, value);
+ return -EINVAL;
+ }
+ }
+
switch (value) {
case 0:
spi->mode |= SPI_NO_TX;
}
}
- if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) {
+ for (idx = 0; idx < spi->num_tx_lanes; idx++) {
+ if (spi->tx_lane_map[idx] >= spi->controller->num_data_lanes) {
+ dev_err(&ctlr->dev,
+ "spi-tx-lane-map has invalid value %d (num_data_lanes=%d)\n",
+ spi->tx_lane_map[idx],
+ spi->controller->num_data_lanes);
+ return -EINVAL;
+ }
+ }
+
+ rc = of_property_read_variable_u32_array(nc, "spi-rx-lane-map", map, 1,
+ ARRAY_SIZE(map));
+ if (rc >= 0) {
+ max_num_data_lanes = rc;
+ for (idx = 0; idx < max_num_data_lanes; idx++)
+ spi->rx_lane_map[idx] = map[idx];
+ } else if (rc == -EINVAL) {
+ /* Default lane map is identity mapping. */
+ max_num_data_lanes = ARRAY_SIZE(spi->rx_lane_map);
+ for (idx = 0; idx < max_num_data_lanes; idx++)
+ spi->rx_lane_map[idx] = idx;
+ } else {
+ dev_err(&ctlr->dev,
+ "failed to read spi-rx-lane-map property: %d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_count_u32_elems(nc, "spi-rx-bus-width");
+ if (rc < 0 && rc != -EINVAL) {
+ dev_err(&ctlr->dev,
+ "failed to read spi-rx-bus-width property: %d\n", rc);
+ return rc;
+ }
+ if (rc > max_num_data_lanes) {
+ dev_err(&ctlr->dev,
+ "spi-rx-bus-width has more elements (%d) than spi-rx-lane-map (%d)\n",
+ rc, max_num_data_lanes);
+ return -EINVAL;
+ }
+
+ if (rc == -EINVAL) {
+ /* Default when property is not present. */
+ spi->num_rx_lanes = 1;
+ } else {
+ u32 first_value;
+
+ spi->num_rx_lanes = rc;
+
+ for (idx = 0; idx < spi->num_rx_lanes; idx++) {
+ rc = of_property_read_u32_index(nc, "spi-rx-bus-width",
+ idx, &value);
+ if (rc)
+ return rc;
+
+ /*
+ * For now, we only support all lanes having the same
+ * width so we can keep using the existing mode flags.
+ */
+ if (!idx)
+ first_value = value;
+ else if (first_value != value) {
+ dev_err(&ctlr->dev,
+ "spi-rx-bus-width has inconsistent values: first %d vs later %d\n",
+ first_value, value);
+ return -EINVAL;
+ }
+ }
+
switch (value) {
case 0:
spi->mode |= SPI_NO_RX;
}
}
+ for (idx = 0; idx < spi->num_rx_lanes; idx++) {
+ if (spi->rx_lane_map[idx] >= spi->controller->num_data_lanes) {
+ dev_err(&ctlr->dev,
+ "spi-rx-lane-map has invalid value %d (num_data_lanes=%d)\n",
+ spi->rx_lane_map[idx],
+ spi->controller->num_data_lanes);
+ return -EINVAL;
+ }
+ }
+
if (spi_controller_is_target(ctlr)) {
if (!of_node_name_eq(nc, "slave")) {
dev_err(&ctlr->dev, "%pOF is not called 'slave'\n",
mutex_init(&ctlr->add_lock);
ctlr->bus_num = -1;
ctlr->num_chipselect = 1;
+ ctlr->num_data_lanes = 1;
ctlr->target = target;
if (IS_ENABLED(CONFIG_SPI_SLAVE) && target)
ctlr->dev.class = &spi_target_class;
/* Max no. of CS supported per spi device */
#define SPI_DEVICE_CS_CNT_MAX 4
+/* Max no. of data lanes supported per spi device */
+#define SPI_DEVICE_DATA_LANE_CNT_MAX 8
+
struct dma_chan;
struct software_node;
struct ptp_system_timestamp;
* @cs_index_mask: Bit mask of the active chipselect(s) in the chipselect array
* @cs_gpiod: Array of GPIO descriptors of the corresponding chipselect lines
* (optional, NULL when not using a GPIO line)
+ * @tx_lane_map: Map of peripheral lanes (index) to controller lanes (value).
+ * @num_tx_lanes: Number of transmit lanes wired up.
+ * @rx_lane_map: Map of peripheral lanes (index) to controller lanes (value).
+ * @num_rx_lanes: Number of receive lanes wired up.
*
* A @spi_device is used to interchange data between an SPI target device
* (usually a discrete chip) and CPU memory.
struct gpio_desc *cs_gpiod[SPI_DEVICE_CS_CNT_MAX]; /* Chip select gpio desc */
+ /* Multi-lane SPI controller support. */
+ u8 tx_lane_map[SPI_DEVICE_DATA_LANE_CNT_MAX];
+ u8 num_tx_lanes;
+ u8 rx_lane_map[SPI_DEVICE_DATA_LANE_CNT_MAX];
+ u8 num_rx_lanes;
+
/*
* Likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
* SPI targets, and are numbered from zero to num_chipselects.
* each target has a chipselect signal, but it's common that not
* every chipselect is connected to a target.
+ * @num_data_lanes: Number of data lanes supported by this controller. Default is 1.
* @dma_alignment: SPI controller constraint on DMA buffers alignment.
* @mode_bits: flags understood by this controller driver
* @buswidth_override_bits: flags to override for this controller driver
*/
u16 num_chipselect;
+ /*
+ * Some specialized SPI controllers can have more than one physical
+ * data lane interface per controller (each having it's own serializer).
+ * This specifies the number of data lanes in that case. Other
+ * controllers do not need to set this (defaults to 1).
+ */
+ u16 num_data_lanes;
+
/* Some SPI controllers pose alignment requirements on DMAable
* buffers; let protocol drivers know about these requirements.
*/