From b42faeee718ce13ef6eb99c24880b58deb54c8fa Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 5 Sep 2019 04:01:12 +0300 Subject: spi: Add a PTP system timestamp to the transfer structure SPI is one of the interfaces used to access devices which have a POSIX clock driver (real time clocks, 1588 timers etc). The fact that the SPI bus is slow is not what the main problem is, but rather the fact that drivers don't take a constant amount of time in transferring data over SPI. When there is a high delay in the readout of time, there will be uncertainty in the value that has been read out of the peripheral. When that delay is constant, the uncertainty can at least be approximated with a certain accuracy which is fine more often than not. Timing jitter occurs all over in the kernel code, and is mainly caused by having to let go of the CPU for various reasons such as preemption, servicing interrupts, going to sleep, etc. Another major reason is CPU dynamic frequency scaling. It turns out that the problem of retrieving time from a SPI peripheral with high accuracy can be solved by the use of "PTP system timestamping" - a mechanism to correlate the time when the device has snapshotted its internal time counter with the Linux system time at that same moment. This is sufficient for having a precise time measurement - it is not necessary for the whole SPI transfer to be transmitted "as fast as possible", or "as low-jitter as possible". The system has to be low-jitter for a very short amount of time to be effective. This patch introduces a PTP system timestamping mechanism in struct spi_transfer. This is to be used by SPI device drivers when they need to know the exact time at which the underlying device's time was snapshotted. More often than not, SPI peripherals have a very exact timing for when their SPI-to-interconnect bridge issues a transaction for snapshotting and reading the time register, and that will be dependent on when the SPI-to-interconnect bridge figures out that this is what it should do, aka as soon as it sees byte N of the SPI transfer. Since spi_device drivers are the ones who'd know best how the peripheral behaves in this regard, expose a mechanism in spi_transfer which allows them to specify which word (or word range) from the transfer should be timestamped. Add a default implementation of the PTP system timestamping in the SPI core. This is not going to be satisfactory performance-wise, but should at least increase the likelihood that SPI device drivers will use PTP system timestamping in the future. There are 3 entry points from the core towards the SPI controller drivers: - transfer_one: The driver is passed individual spi_transfers to execute. This is the easiest to timestamp. - transfer_one_message: The core passes the driver an entire spi_message (a potential batch of spi_transfers). The core puts the same pre and post timestamp to all transfers within a message. This is not ideal, but nothing better can be done by default anyway, since the core has no insight into how the driver batches the transfers. - transfer: Like transfer_one_message, but for unqueued drivers (i.e. the driver implements its own queue scheduling). Signed-off-by: Vladimir Oltean Link: https://lore.kernel.org/r/20190905010114.26718-3-olteanv@gmail.com Signed-off-by: Mark Brown --- include/linux/spi/spi.h | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'include') diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index af4f265d0f67..27f6b046cf92 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -13,6 +13,7 @@ #include #include #include +#include struct dma_chan; struct property_entry; @@ -409,6 +410,12 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @fw_translate_cs: If the boot firmware uses different numbering scheme * what Linux expects, this optional hook can be used to translate * between the two. + * @ptp_sts_supported: If the driver sets this to true, it must provide a + * time snapshot in @spi_transfer->ptp_sts as close as possible to the + * moment in time when @spi_transfer->ptp_sts_word_pre and + * @spi_transfer->ptp_sts_word_post were transmitted. + * If the driver does not set this, the SPI core takes the snapshot as + * close to the driver hand-over as possible. * * Each SPI controller can communicate with one or more @spi_device * children. These make a small bus, sharing MOSI, MISO and SCK signals @@ -604,6 +611,15 @@ struct spi_controller { void *dummy_tx; int (*fw_translate_cs)(struct spi_controller *ctlr, unsigned cs); + + /* + * Driver sets this field to indicate it is able to snapshot SPI + * transfers (needed e.g. for reading the time of POSIX clocks) + */ + bool ptp_sts_supported; + + /* Interrupt enable state during PTP system timestamping */ + unsigned long irq_flags; }; static inline void *spi_controller_get_devdata(struct spi_controller *ctlr) @@ -644,6 +660,14 @@ extern struct spi_message *spi_get_next_queued_message(struct spi_controller *ct extern void spi_finalize_current_message(struct spi_controller *ctlr); extern void spi_finalize_current_transfer(struct spi_controller *ctlr); +/* Helper calls for driver to timestamp transfer */ +void spi_take_timestamp_pre(struct spi_controller *ctlr, + struct spi_transfer *xfer, + const void *tx, bool irqs_off); +void spi_take_timestamp_post(struct spi_controller *ctlr, + struct spi_transfer *xfer, + const void *tx, bool irqs_off); + /* the spi driver core manages memory for the spi_controller classdev */ extern struct spi_controller *__spi_alloc_controller(struct device *host, unsigned int size, bool slave); @@ -753,6 +777,35 @@ extern void spi_res_release(struct spi_controller *ctlr, * @transfer_list: transfers are sequenced through @spi_message.transfers * @tx_sg: Scatterlist for transmit, currently not for client use * @rx_sg: Scatterlist for receive, currently not for client use + * @ptp_sts_word_pre: The word (subject to bits_per_word semantics) offset + * within @tx_buf for which the SPI device is requesting that the time + * snapshot for this transfer begins. Upon completing the SPI transfer, + * this value may have changed compared to what was requested, depending + * on the available snapshotting resolution (DMA transfer, + * @ptp_sts_supported is false, etc). + * @ptp_sts_word_post: See @ptp_sts_word_post. The two can be equal (meaning + * that a single byte should be snapshotted). + * If the core takes care of the timestamp (if @ptp_sts_supported is false + * for this controller), it will set @ptp_sts_word_pre to 0, and + * @ptp_sts_word_post to the length of the transfer. This is done + * purposefully (instead of setting to spi_transfer->len - 1) to denote + * that a transfer-level snapshot taken from within the driver may still + * be of higher quality. + * @ptp_sts: Pointer to a memory location held by the SPI slave device where a + * PTP system timestamp structure may lie. If drivers use PIO or their + * hardware has some sort of assist for retrieving exact transfer timing, + * they can (and should) assert @ptp_sts_supported and populate this + * structure using the ptp_read_system_*ts helper functions. + * The timestamp must represent the time at which the SPI slave device has + * processed the word, i.e. the "pre" timestamp should be taken before + * transmitting the "pre" word, and the "post" timestamp after receiving + * transmit confirmation from the controller for the "post" word. + * @timestamped_pre: Set by the SPI controller driver to denote it has acted + * upon the @ptp_sts request. Not set when the SPI core has taken care of + * the task. SPI device drivers are free to print a warning if this comes + * back unset and they need the better resolution. + * @timestamped_post: See above. The reason why both exist is that these + * booleans are also used to keep state in the core SPI logic. * * SPI transfers always write the same number of bytes as they read. * Protocol drivers should always provide @rx_buf and/or @tx_buf. @@ -842,6 +895,14 @@ struct spi_transfer { u32 effective_speed_hz; + unsigned int ptp_sts_word_pre; + unsigned int ptp_sts_word_post; + + struct ptp_system_timestamp *ptp_sts; + + bool timestamped_pre; + bool timestamped_post; + struct list_head transfer_list; }; -- cgit v1.2.3 From b2c98153f45fc17b9fcb241000f2d131ddea6030 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Thu, 26 Sep 2019 13:51:30 +0300 Subject: spi: introduce spi_delay struct as "value + unit" & spi_delay_exec() There are plenty of delays that have been introduced in SPI core. Most of them are in micro-seconds, some need to be in nano-seconds, and some in clock-cycles. For some of these delays (related to transfers & CS timing) it may make sense to have a `spi_delay` struct that abstracts these a bit. The important element of these delays [for unification] seems to be the `unit` of the delay. It looks like micro-seconds is good enough for most people, but every-once in a while, some delays seem to require other units of measurement. This change adds the `spi_delay` struct & a `spi_delay_exec()` function that processes a `spi_delay` object/struct to execute the delay. It's a copy of the `cs_change_delay` mechanism, but without the default for 10 uS. The clock-cycle delay unit is a bit special, as it needs to be bound to an `spi_transfer` object to execute. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20190926105147.7839-3-alexandru.ardelean@analog.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/spi/spi.h | 18 ++++++++++++++--- 2 files changed, 66 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 7ba981cdb86b..7499a4efbaba 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1106,6 +1106,57 @@ static void _spi_transfer_delay_ns(u32 ns) } } +static int _spi_delay_to_ns(struct spi_delay *_delay, struct spi_transfer *xfer) +{ + u32 delay = _delay->value; + u32 unit = _delay->unit; + u32 hz; + + if (!delay) + return 0; + + switch (unit) { + case SPI_DELAY_UNIT_USECS: + delay *= 1000; + break; + case SPI_DELAY_UNIT_NSECS: /* nothing to do here */ + break; + case SPI_DELAY_UNIT_SCK: + /* clock cycles need to be obtained from spi_transfer */ + if (!xfer) + return -EINVAL; + /* if there is no effective speed know, then approximate + * by underestimating with half the requested hz + */ + hz = xfer->effective_speed_hz ?: xfer->speed_hz / 2; + if (!hz) + return -EINVAL; + delay *= DIV_ROUND_UP(1000000000, hz); + break; + default: + return -EINVAL; + } + + return delay; +} + +int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer) +{ + int delay; + + if (!_delay) + return -EINVAL; + + delay = _spi_delay_to_ns(_delay, xfer); + if (delay < 0) + return delay; + + _spi_transfer_delay_ns(delay); + + return 0; +} +EXPORT_SYMBOL_GPL(spi_delay_exec); + static void _spi_transfer_cs_change_delay(struct spi_message *msg, struct spi_transfer *xfer) { diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 27f6b046cf92..8f643de3197b 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -90,6 +90,21 @@ void spi_statistics_add_transfer_stats(struct spi_statistics *stats, #define SPI_STATISTICS_INCREMENT_FIELD(stats, field) \ SPI_STATISTICS_ADD_TO_FIELD(stats, field, 1) +/** + * struct spi_delay - SPI delay information + * @value: Value for the delay + * @unit: Unit for the delay + */ +struct spi_delay { +#define SPI_DELAY_UNIT_USECS 0 +#define SPI_DELAY_UNIT_NSECS 1 +#define SPI_DELAY_UNIT_SCK 2 + u16 value; + u8 unit; +}; + +extern int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer); + /** * struct spi_device - Controller side proxy for an SPI slave device * @dev: Driver model representation of the device. @@ -887,9 +902,6 @@ struct spi_transfer { u16 delay_usecs; u16 cs_change_delay; u8 cs_change_delay_unit; -#define SPI_DELAY_UNIT_USECS 0 -#define SPI_DELAY_UNIT_NSECS 1 -#define SPI_DELAY_UNIT_SCK 2 u32 speed_hz; u16 word_delay; -- cgit v1.2.3 From 329f0dac4cad9fa4b1439a88180d91bcb5c4eaf8 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Thu, 26 Sep 2019 13:51:31 +0300 Subject: spi: make `cs_change_delay` the first user of the `spi_delay` logic Since the logic for `spi_delay` struct + `spi_delay_exec()` has been copied from the `cs_change_delay` logic, it's natural to make this delay, the first user. The `cs_change_delay` logic requires that the default remain 10 uS, in case it is unspecified/unconfigured. So, there is some special handling needed to do that. The ADIS library is one of the few users of the new `cs_change_delay` parameter for an spi_transfer. The introduction of the `spi_delay` struct, requires that the users of of `cs_change_delay` get an update. This change also updates the ADIS library. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20190926105147.7839-4-alexandru.ardelean@analog.com Signed-off-by: Mark Brown --- drivers/iio/imu/adis.c | 24 ++++++++++++------------ drivers/spi/spi.c | 28 +++++++--------------------- include/linux/spi/spi.h | 4 +--- 3 files changed, 20 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 1631c255deab..2cd2cc2316c6 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -39,24 +39,24 @@ int adis_write_reg(struct adis *adis, unsigned int reg, .len = 2, .cs_change = 1, .delay_usecs = adis->data->write_delay, - .cs_change_delay = adis->data->cs_change_delay, - .cs_change_delay_unit = SPI_DELAY_UNIT_USECS, + .cs_change_delay.value = adis->data->cs_change_delay, + .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, }, { .tx_buf = adis->tx + 2, .bits_per_word = 8, .len = 2, .cs_change = 1, .delay_usecs = adis->data->write_delay, - .cs_change_delay = adis->data->cs_change_delay, - .cs_change_delay_unit = SPI_DELAY_UNIT_USECS, + .cs_change_delay.value = adis->data->cs_change_delay, + .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, }, { .tx_buf = adis->tx + 4, .bits_per_word = 8, .len = 2, .cs_change = 1, .delay_usecs = adis->data->write_delay, - .cs_change_delay = adis->data->cs_change_delay, - .cs_change_delay_unit = SPI_DELAY_UNIT_USECS, + .cs_change_delay.value = adis->data->cs_change_delay, + .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, }, { .tx_buf = adis->tx + 6, .bits_per_word = 8, @@ -139,16 +139,16 @@ int adis_read_reg(struct adis *adis, unsigned int reg, .len = 2, .cs_change = 1, .delay_usecs = adis->data->write_delay, - .cs_change_delay = adis->data->cs_change_delay, - .cs_change_delay_unit = SPI_DELAY_UNIT_USECS, + .cs_change_delay.value = adis->data->cs_change_delay, + .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, }, { .tx_buf = adis->tx + 2, .bits_per_word = 8, .len = 2, .cs_change = 1, .delay_usecs = adis->data->read_delay, - .cs_change_delay = adis->data->cs_change_delay, - .cs_change_delay_unit = SPI_DELAY_UNIT_USECS, + .cs_change_delay.value = adis->data->cs_change_delay, + .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, }, { .tx_buf = adis->tx + 4, .rx_buf = adis->rx, @@ -156,8 +156,8 @@ int adis_read_reg(struct adis *adis, unsigned int reg, .len = 2, .cs_change = 1, .delay_usecs = adis->data->read_delay, - .cs_change_delay = adis->data->cs_change_delay, - .cs_change_delay_unit = SPI_DELAY_UNIT_USECS, + .cs_change_delay.value = adis->data->cs_change_delay, + .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, }, { .rx_buf = adis->rx + 2, .bits_per_word = 8, diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 7499a4efbaba..b69c14082c52 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1160,9 +1160,9 @@ EXPORT_SYMBOL_GPL(spi_delay_exec); static void _spi_transfer_cs_change_delay(struct spi_message *msg, struct spi_transfer *xfer) { - u32 delay = xfer->cs_change_delay; - u32 unit = xfer->cs_change_delay_unit; - u32 hz; + u32 delay = xfer->cs_change_delay.value; + u32 unit = xfer->cs_change_delay.unit; + int ret; /* return early on "fast" mode - for everything but USECS */ if (!delay) { @@ -1171,27 +1171,13 @@ static void _spi_transfer_cs_change_delay(struct spi_message *msg, return; } - switch (unit) { - case SPI_DELAY_UNIT_USECS: - delay *= 1000; - break; - case SPI_DELAY_UNIT_NSECS: /* nothing to do here */ - break; - case SPI_DELAY_UNIT_SCK: - /* if there is no effective speed know, then approximate - * by underestimating with half the requested hz - */ - hz = xfer->effective_speed_hz ?: xfer->speed_hz / 2; - delay *= DIV_ROUND_UP(1000000000, hz); - break; - default: + ret = spi_delay_exec(&xfer->cs_change_delay, xfer); + if (ret) { dev_err_once(&msg->spi->dev, "Use of unsupported delay unit %i, using default of 10us\n", - xfer->cs_change_delay_unit); - delay = 10000; + unit); + _spi_transfer_delay_ns(10000); } - /* now sleep for the requested amount of time */ - _spi_transfer_delay_ns(delay); } /* diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 8f643de3197b..7670be934643 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -778,7 +778,6 @@ extern void spi_res_release(struct spi_controller *ctlr, * @cs_change: affects chipselect after this transfer completes * @cs_change_delay: delay between cs deassert and assert when * @cs_change is set and @spi_transfer is not the last in @spi_message - * @cs_change_delay_unit: unit of cs_change_delay * @delay_usecs: microseconds to delay after this transfer before * (optionally) changing the chipselect status, then starting * the next transfer or completing this @spi_message. @@ -900,8 +899,7 @@ struct spi_transfer { u8 bits_per_word; u8 word_delay_usecs; u16 delay_usecs; - u16 cs_change_delay; - u8 cs_change_delay_unit; + struct spi_delay cs_change_delay; u32 speed_hz; u16 word_delay; -- cgit v1.2.3 From 84593a131c3af21d686d05c4b4432290a415d399 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Thu, 26 Sep 2019 13:51:32 +0300 Subject: spi: sprd: convert transfer word delay to spi_delay struct The Spreadtrum SPI driver is the only user of the `word_delay` field in the `spi_transfer` struct. This change converts the field to use the `spi_delay` struct. This also enforces the users to specify the delay unit to be `SPI_DELAY_UNIT_SCK`. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20190926105147.7839-5-alexandru.ardelean@analog.com Signed-off-by: Mark Brown --- drivers/spi/spi-sprd.c | 15 ++++++++++++--- include/linux/spi/spi.h | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi-sprd.c b/drivers/spi/spi-sprd.c index 8c9021b7f7a9..2ee1feb41681 100644 --- a/drivers/spi/spi-sprd.c +++ b/drivers/spi/spi-sprd.c @@ -669,11 +669,15 @@ static void sprd_spi_set_speed(struct sprd_spi *ss, u32 speed_hz) writel_relaxed(clk_div, ss->base + SPRD_SPI_CLKD); } -static void sprd_spi_init_hw(struct sprd_spi *ss, struct spi_transfer *t) +static int sprd_spi_init_hw(struct sprd_spi *ss, struct spi_transfer *t) { + struct spi_delay *d = &t->word_delay; u16 word_delay, interval; u32 val; + if (d->unit != SPI_DELAY_UNIT_SCK) + return -EINVAL; + val = readl_relaxed(ss->base + SPRD_SPI_CTL7); val &= ~(SPRD_SPI_SCK_REV | SPRD_SPI_NG_TX | SPRD_SPI_NG_RX); /* Set default chip selection, clock phase and clock polarity */ @@ -686,7 +690,7 @@ static void sprd_spi_init_hw(struct sprd_spi *ss, struct spi_transfer *t) * formula as below per datasheet: * interval time (source clock cycles) = interval * 4 + 10. */ - word_delay = clamp_t(u16, t->word_delay, SPRD_SPI_MIN_DELAY_CYCLE, + word_delay = clamp_t(u16, d->value, SPRD_SPI_MIN_DELAY_CYCLE, SPRD_SPI_MAX_DELAY_CYCLE); interval = DIV_ROUND_UP(word_delay - 10, 4); ss->word_delay = interval * 4 + 10; @@ -711,6 +715,8 @@ static void sprd_spi_init_hw(struct sprd_spi *ss, struct spi_transfer *t) val &= ~SPRD_SPI_DATA_LINE2_EN; writel_relaxed(val, ss->base + SPRD_SPI_CTL7); + + return 0; } static int sprd_spi_setup_transfer(struct spi_device *sdev, @@ -719,13 +725,16 @@ static int sprd_spi_setup_transfer(struct spi_device *sdev, struct sprd_spi *ss = spi_controller_get_devdata(sdev->controller); u8 bits_per_word = t->bits_per_word; u32 val, mode = 0; + int ret; ss->len = t->len; ss->tx_buf = t->tx_buf; ss->rx_buf = t->rx_buf; ss->hw_mode = sdev->mode; - sprd_spi_init_hw(ss, t); + ret = sprd_spi_init_hw(ss, t); + if (ret) + return ret; /* Set tansfer speed and valid bits */ sprd_spi_set_speed(ss, t->speed_hz); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 7670be934643..6cb67ad53ffa 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -783,7 +783,7 @@ extern void spi_res_release(struct spi_controller *ctlr, * the next transfer or completing this @spi_message. * @word_delay_usecs: microseconds to inter word delay after each word size * (set by bits_per_word) transmission. - * @word_delay: clock cycles to inter word delay after each word size + * @word_delay: inter word delay to be introduced after each word size * (set by bits_per_word) transmission. * @effective_speed_hz: the effective SCK-speed that was used to * transfer this transfer. Set to 0 if the spi bus driver does @@ -900,8 +900,8 @@ struct spi_transfer { u8 word_delay_usecs; u16 delay_usecs; struct spi_delay cs_change_delay; + struct spi_delay word_delay; u32 speed_hz; - u16 word_delay; u32 effective_speed_hz; -- cgit v1.2.3 From 6c613f68aabf33385c01e949204ac5ed30887161 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Thu, 26 Sep 2019 13:51:35 +0300 Subject: spi: core,atmel: convert `word_delay_usecs` -> `word_delay` for spi_device This change does a conversion from the `word_delay_usecs` -> `word_delay` for the `spi_device` struct. This allows users to specify inter-word delays in other unit types (nano-seconds or clock cycles), depending on how users want. The Atmel SPI driver is the only current user of the `word_delay_usecs` field (from the `spi_device` struct). So, it needed a slight conversion to use the `word_delay` as an `spi_delay` struct. In SPI core, the only required mechanism is to update the `word_delay` information per `spi_transfer`. This requires a bit more logic than before, because it needs that both delays be converted to a common unit (nano-seconds) for comparison. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20190926105147.7839-8-alexandru.ardelean@analog.com Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 26 ++++++++++++++++++++++++-- drivers/spi/spi.c | 24 ++++++++++++++++++++++-- include/linux/spi/spi.h | 7 ++----- 3 files changed, 48 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 3ed5e663da6f..1471b049f99a 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1149,12 +1149,31 @@ atmel_spi_pdc_interrupt(int irq, void *dev_id) return ret; } +static int atmel_word_delay_csr(struct spi_device *spi, struct atmel_spi *as) +{ + struct spi_delay *delay = &spi->word_delay; + u32 value = delay->value; + + switch (delay->unit) { + case SPI_DELAY_UNIT_NSECS: + value /= 1000; + break; + case SPI_DELAY_UNIT_USECS: + break; + default: + return -EINVAL; + } + + return (as->spi_clk / 1000000 * value) >> 5; +} + static int atmel_spi_setup(struct spi_device *spi) { struct atmel_spi *as; struct atmel_spi_device *asd; u32 csr; unsigned int bits = spi->bits_per_word; + int word_delay_csr; as = spi_master_get_devdata(spi->master); @@ -1178,11 +1197,14 @@ static int atmel_spi_setup(struct spi_device *spi) */ csr |= SPI_BF(DLYBS, 0); + word_delay_csr = atmel_word_delay_csr(spi, as); + if (word_delay_csr < 0) + return word_delay_csr; + /* DLYBCT adds delays between words. This is useful for slow devices * that need a bit of time to setup the next transfer. */ - csr |= SPI_BF(DLYBCT, - (as->spi_clk / 1000000 * spi->word_delay_usecs) >> 5); + csr |= SPI_BF(DLYBCT, word_delay_csr); asd = spi->controller_state; if (!asd) { diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b69c14082c52..307e440dd92d 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -3281,6 +3281,26 @@ void spi_set_cs_timing(struct spi_device *spi, u8 setup, u8 hold, } EXPORT_SYMBOL_GPL(spi_set_cs_timing); +static int _spi_xfer_word_delay_update(struct spi_transfer *xfer, + struct spi_device *spi) +{ + int delay1, delay2; + + delay1 = _spi_delay_to_ns(&xfer->word_delay, xfer); + if (delay1 < 0) + return delay1; + + delay2 = _spi_delay_to_ns(&spi->word_delay, xfer); + if (delay2 < 0) + return delay2; + + if (delay1 < delay2) + memcpy(&xfer->word_delay, &spi->word_delay, + sizeof(xfer->word_delay)); + + return 0; +} + static int __spi_validate(struct spi_device *spi, struct spi_message *message) { struct spi_controller *ctlr = spi->controller; @@ -3416,8 +3436,8 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message) return -EINVAL; } - if (xfer->word_delay_usecs < spi->word_delay_usecs) - xfer->word_delay_usecs = spi->word_delay_usecs; + if (_spi_xfer_word_delay_update(xfer, spi)) + return -EINVAL; } message->status = -EINPROGRESS; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 6cb67ad53ffa..ebeb272aeb0f 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -139,7 +139,7 @@ extern int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer); * the spi_master. * @cs_gpiod: gpio descriptor of the chipselect line (optional, NULL when * not using a GPIO line) - * @word_delay_usecs: microsecond delay to be inserted between consecutive + * @word_delay: delay to be inserted between consecutive * words of a transfer * * @statistics: statistics for the spi_device @@ -189,7 +189,7 @@ struct spi_device { const char *driver_override; int cs_gpio; /* LEGACY: chip select gpio */ struct gpio_desc *cs_gpiod; /* chip select gpio desc */ - uint8_t word_delay_usecs; /* inter-word delay */ + struct spi_delay word_delay; /* inter-word delay */ /* the statistics */ struct spi_statistics statistics; @@ -781,8 +781,6 @@ extern void spi_res_release(struct spi_controller *ctlr, * @delay_usecs: microseconds to delay after this transfer before * (optionally) changing the chipselect status, then starting * the next transfer or completing this @spi_message. - * @word_delay_usecs: microseconds to inter word delay after each word size - * (set by bits_per_word) transmission. * @word_delay: inter word delay to be introduced after each word size * (set by bits_per_word) transmission. * @effective_speed_hz: the effective SCK-speed that was used to @@ -897,7 +895,6 @@ struct spi_transfer { #define SPI_NBITS_DUAL 0x02 /* 2bits transfer */ #define SPI_NBITS_QUAD 0x04 /* 4bits transfer */ u8 bits_per_word; - u8 word_delay_usecs; u16 delay_usecs; struct spi_delay cs_change_delay; struct spi_delay word_delay; -- cgit v1.2.3 From bebcfd272df648542c458d28fbd6a8f9428b5310 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Thu, 26 Sep 2019 13:51:36 +0300 Subject: spi: introduce `delay` field for `spi_transfer` + spi_transfer_delay_exec() The change introduces the `delay` field to the `spi_transfer` struct as an `struct spi_delay` type. This intends to eventually replace `delay_usecs`. But, since there are many users of `delay_usecs`, this needs some intermediate work. A helper called `spi_transfer_delay_exec()` is also added, which maintains backwards compatibility with `delay_usecs`, by assigning the value to `delay` if non-zero. This should maintain backwards compatibility with current users of `udelay_usecs`. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20190926105147.7839-9-alexandru.ardelean@analog.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 6 +++--- include/linux/spi/spi.h | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 307e440dd92d..2e448078a117 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1248,8 +1248,7 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, if (msg->status != -EINPROGRESS) goto out; - if (xfer->delay_usecs) - _spi_transfer_delay_ns(xfer->delay_usecs * 1000); + spi_transfer_delay_exec(xfer); if (xfer->cs_change) { if (list_is_last(&xfer->transfer_list, @@ -3026,10 +3025,11 @@ struct spi_replaced_transfers *spi_replace_transfers( /* add to list */ list_add(&xfer->transfer_list, rxfer->replaced_after); - /* clear cs_change and delay_usecs for all but the last */ + /* clear cs_change and delay for all but the last */ if (i) { xfer->cs_change = false; xfer->delay_usecs = 0; + xfer->delay.value = 0; } } diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index ebeb272aeb0f..fe5b85df2c79 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -778,6 +778,9 @@ extern void spi_res_release(struct spi_controller *ctlr, * @cs_change: affects chipselect after this transfer completes * @cs_change_delay: delay between cs deassert and assert when * @cs_change is set and @spi_transfer is not the last in @spi_message + * @delay: delay to be introduced after this transfer before + * (optionally) changing the chipselect status, then starting + * the next transfer or completing this @spi_message. * @delay_usecs: microseconds to delay after this transfer before * (optionally) changing the chipselect status, then starting * the next transfer or completing this @spi_message. @@ -896,6 +899,7 @@ struct spi_transfer { #define SPI_NBITS_QUAD 0x04 /* 4bits transfer */ u8 bits_per_word; u16 delay_usecs; + struct spi_delay delay; struct spi_delay cs_change_delay; struct spi_delay word_delay; u32 speed_hz; @@ -1003,6 +1007,20 @@ spi_transfer_del(struct spi_transfer *t) list_del(&t->transfer_list); } +static inline int +spi_transfer_delay_exec(struct spi_transfer *t) +{ + struct spi_delay d; + + if (t->delay_usecs) { + d.value = t->delay_usecs; + d.unit = SPI_DELAY_UNIT_USECS; + return spi_delay_exec(&d, NULL); + } + + return spi_delay_exec(&t->delay, t); +} + /** * spi_message_init_with_transfers - Initialize spi_message and append transfers * @m: spi_message to be initialized -- cgit v1.2.3 From 8105936684681195d9073880b06a123b2e316811 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Thu, 26 Sep 2019 13:51:42 +0300 Subject: spi: tegra114: change format for `spi_set_cs_timing()` function The initial version of `spi_set_cs_timing()` was implemented with consideration only for clock-cycles as delay. For cases like `CS setup` time, it's sometimes needed that micro-seconds (or nano-seconds) are required, or sometimes even longer delays, for cases where the device needs a little longer to start transferring that after CS is asserted. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20190926105147.7839-15-alexandru.ardelean@analog.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 22 ++++++++++++++++++++-- drivers/spi/spi.c | 16 ++++++++++------ include/linux/spi/spi.h | 9 ++++++--- 3 files changed, 36 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 8133dc49d34f..e6a450d9b4f0 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -723,15 +723,31 @@ static void tegra_spi_deinit_dma_param(struct tegra_spi_data *tspi, dma_release_channel(dma_chan); } -static void tegra_spi_set_hw_cs_timing(struct spi_device *spi, u8 setup_dly, - u8 hold_dly, u8 inactive_dly) +static int tegra_spi_set_hw_cs_timing(struct spi_device *spi, + struct spi_delay *setup, + struct spi_delay *hold, + struct spi_delay *inactive) { struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master); + u8 setup_dly, hold_dly, inactive_dly; u32 setup_hold; u32 spi_cs_timing; u32 inactive_cycles; u8 cs_state; + if ((setup && setup->unit != SPI_DELAY_UNIT_SCK) || + (hold && hold->unit != SPI_DELAY_UNIT_SCK) || + (inactive && inactive->unit != SPI_DELAY_UNIT_SCK)) { + dev_err(&spi->dev, + "Invalid delay unit %d, should be SPI_DELAY_UNIT_SCK\n", + SPI_DELAY_UNIT_SCK); + return -EINVAL; + } + + setup_dly = setup ? setup->value : 0; + hold_dly = hold ? hold->value : 0; + inactive_dly = inactive ? inactive->value : 0; + setup_dly = min_t(u8, setup_dly, MAX_SETUP_HOLD_CYCLES); hold_dly = min_t(u8, hold_dly, MAX_SETUP_HOLD_CYCLES); if (setup_dly && hold_dly) { @@ -758,6 +774,8 @@ static void tegra_spi_set_hw_cs_timing(struct spi_device *spi, u8 setup_dly, tspi->spi_cs_timing2 = spi_cs_timing; tegra_spi_writel(tspi, spi_cs_timing, SPI_CS_TIMING2); } + + return 0; } static u32 tegra_spi_setup_transfer_one(struct spi_device *spi, diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 2e448078a117..6beeb363515c 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -3269,15 +3269,19 @@ EXPORT_SYMBOL_GPL(spi_setup); /** * spi_set_cs_timing - configure CS setup, hold, and inactive delays * @spi: the device that requires specific CS timing configuration - * @setup: CS setup time in terms of clock count - * @hold: CS hold time in terms of clock count - * @inactive_dly: CS inactive delay between transfers in terms of clock count + * @setup: CS setup time specified via @spi_delay + * @hold: CS hold time specified via @spi_delay + * @inactive: CS inactive delay between transfers specified via @spi_delay + * + * Return: zero on success, else a negative error code. */ -void spi_set_cs_timing(struct spi_device *spi, u8 setup, u8 hold, - u8 inactive_dly) +int spi_set_cs_timing(struct spi_device *spi, struct spi_delay *setup, + struct spi_delay *hold, struct spi_delay *inactive) { if (spi->controller->set_cs_timing) - spi->controller->set_cs_timing(spi, setup, hold, inactive_dly); + return spi->controller->set_cs_timing(spi, setup, hold, + inactive); + return -ENOTSUPP; } EXPORT_SYMBOL_GPL(spi_set_cs_timing); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index fe5b85df2c79..f9b4ba2db08d 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -524,8 +524,8 @@ struct spi_controller { * to configure specific CS timing through spi_set_cs_timing() after * spi_setup(). */ - void (*set_cs_timing)(struct spi_device *spi, u8 setup_clk_cycles, - u8 hold_clk_cycles, u8 inactive_clk_cycles); + int (*set_cs_timing)(struct spi_device *spi, struct spi_delay *setup, + struct spi_delay *hold, struct spi_delay *inactive); /* bidirectional bulk transfers * @@ -1068,7 +1068,10 @@ static inline void spi_message_free(struct spi_message *m) kfree(m); } -extern void spi_set_cs_timing(struct spi_device *spi, u8 setup, u8 hold, u8 inactive_dly); +extern int spi_set_cs_timing(struct spi_device *spi, + struct spi_delay *setup, + struct spi_delay *hold, + struct spi_delay *inactive); extern int spi_setup(struct spi_device *spi); extern int spi_async(struct spi_device *spi, struct spi_message *message); -- cgit v1.2.3 From 25093bdeb6bcae728e12e3795261dbd3677060a9 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Thu, 26 Sep 2019 13:51:43 +0300 Subject: spi: implement SW control for CS times This change implements CS control for setup, hold & inactive delays. The `cs_setup` delay is completely new, and can help with cases where asserting the CS, also brings the device out of power-sleep, where there needs to be a longer (than usual), before transferring data. The `cs_hold` time can overlap with the `delay` (or `delay_usecs`) from an SPI transfer. The main difference is that `cs_hold` implies that CS will be de-asserted. The `cs_inactive` delay does not have a clear use-case yet. It has been implemented mostly because the `spi_set_cs_timing()` function implements it. To some degree, this could overlap or replace `cs_change_delay`, but this will require more consideration/investigation in the future. All these delays have been added to the `spi_controller` struct, as they would typically be configured by calling `spi_set_cs_timing()` after an `spi_setup()` call. Software-mode for CS control, implies that the `set_cs_timing()` hook has not been provided for the `spi_controller` object. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20190926105147.7839-16-alexandru.ardelean@analog.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- include/linux/spi/spi.h | 5 +++++ 2 files changed, 49 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 6beeb363515c..21628b0728f1 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -775,6 +775,15 @@ int spi_register_board_info(struct spi_board_info const *info, unsigned n) static void spi_set_cs(struct spi_device *spi, bool enable) { + bool enable1 = enable; + + if (!spi->controller->set_cs_timing) { + if (enable1) + spi_delay_exec(&spi->controller->cs_setup, NULL); + else + spi_delay_exec(&spi->controller->cs_hold, NULL); + } + if (spi->mode & SPI_CS_HIGH) enable = !enable; @@ -800,6 +809,11 @@ static void spi_set_cs(struct spi_device *spi, bool enable) } else if (spi->controller->set_cs) { spi->controller->set_cs(spi, !enable); } + + if (!spi->controller->set_cs_timing) { + if (!enable1) + spi_delay_exec(&spi->controller->cs_inactive, NULL); + } } #ifdef CONFIG_HAS_DMA @@ -3278,10 +3292,39 @@ EXPORT_SYMBOL_GPL(spi_setup); int spi_set_cs_timing(struct spi_device *spi, struct spi_delay *setup, struct spi_delay *hold, struct spi_delay *inactive) { + size_t len; + if (spi->controller->set_cs_timing) return spi->controller->set_cs_timing(spi, setup, hold, inactive); - return -ENOTSUPP; + + if ((setup && setup->unit == SPI_DELAY_UNIT_SCK) || + (hold && hold->unit == SPI_DELAY_UNIT_SCK) || + (inactive && inactive->unit == SPI_DELAY_UNIT_SCK)) { + dev_err(&spi->dev, + "Clock-cycle delays for CS not supported in SW mode\n"); + return -ENOTSUPP; + } + + len = sizeof(struct spi_delay); + + /* copy delays to controller */ + if (setup) + memcpy(&spi->controller->cs_setup, setup, len); + else + memset(&spi->controller->cs_setup, 0, len); + + if (hold) + memcpy(&spi->controller->cs_hold, hold, len); + else + memset(&spi->controller->cs_hold, 0, len); + + if (inactive) + memcpy(&spi->controller->cs_inactive, inactive, len); + else + memset(&spi->controller->cs_inactive, 0, len); + + return 0; } EXPORT_SYMBOL_GPL(spi_set_cs_timing); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index f9b4ba2db08d..cfd87b18f077 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -609,6 +609,11 @@ struct spi_controller { /* Optimized handlers for SPI memory-like operations. */ const struct spi_controller_mem_ops *mem_ops; + /* CS delays */ + struct spi_delay cs_setup; + struct spi_delay cs_hold; + struct spi_delay cs_inactive; + /* gpio chip select */ int *cs_gpios; struct gpio_desc **cs_gpiods; -- cgit v1.2.3 From 3984d39b0e41ac4de8b4530ae3911ccf52ed4bbf Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Thu, 26 Sep 2019 13:51:44 +0300 Subject: spi: spi-fsl-espi: convert transfer delay to `spi_delay` format The way the max delay is computed for this controller, it looks like it is searching for the max delay from an SPI message a using that. No idea if this is valid. But this change should support both `delay_usecs` and the new `delay` data which is of `spi_delay` type. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20190926105147.7839-17-alexandru.ardelean@analog.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 16 +++++++++++++--- drivers/spi/spi.c | 9 +++++---- include/linux/spi/spi.h | 1 + 3 files changed, 19 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index f20326714b9d..f72daf66f3f3 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -437,6 +437,7 @@ static int fsl_espi_do_one_msg(struct spi_master *master, struct spi_message *m) { unsigned int delay_usecs = 0, rx_nbits = 0; + unsigned int delay_nsecs = 0, delay_nsecs1 = 0; struct spi_transfer *t, trans = {}; int ret; @@ -445,8 +446,16 @@ static int fsl_espi_do_one_msg(struct spi_master *master, goto out; list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->delay_usecs > delay_usecs) - delay_usecs = t->delay_usecs; + if (t->delay_usecs) { + if (t->delay_usecs > delay_usecs) { + delay_usecs = t->delay_usecs; + delay_nsecs = delay_usecs * 1000; + } + } else { + delay_nsecs1 = spi_delay_to_ns(&t->delay, t); + if (delay_nsecs1 > delay_nsecs) + delay_nsecs = delay_nsecs1; + } if (t->rx_nbits > rx_nbits) rx_nbits = t->rx_nbits; } @@ -457,7 +466,8 @@ static int fsl_espi_do_one_msg(struct spi_master *master, trans.len = m->frame_length; trans.speed_hz = t->speed_hz; trans.bits_per_word = t->bits_per_word; - trans.delay_usecs = delay_usecs; + trans.delay.value = delay_nsecs; + trans.delay.unit = SPI_DELAY_UNIT_NSECS; trans.rx_nbits = rx_nbits; if (trans.len) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 21628b0728f1..5414a10afd65 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1120,7 +1120,7 @@ static void _spi_transfer_delay_ns(u32 ns) } } -static int _spi_delay_to_ns(struct spi_delay *_delay, struct spi_transfer *xfer) +int spi_delay_to_ns(struct spi_delay *_delay, struct spi_transfer *xfer) { u32 delay = _delay->value; u32 unit = _delay->unit; @@ -1153,6 +1153,7 @@ static int _spi_delay_to_ns(struct spi_delay *_delay, struct spi_transfer *xfer) return delay; } +EXPORT_SYMBOL_GPL(spi_delay_to_ns); int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer) { @@ -1161,7 +1162,7 @@ int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer) if (!_delay) return -EINVAL; - delay = _spi_delay_to_ns(_delay, xfer); + delay = spi_delay_to_ns(_delay, xfer); if (delay < 0) return delay; @@ -3333,11 +3334,11 @@ static int _spi_xfer_word_delay_update(struct spi_transfer *xfer, { int delay1, delay2; - delay1 = _spi_delay_to_ns(&xfer->word_delay, xfer); + delay1 = spi_delay_to_ns(&xfer->word_delay, xfer); if (delay1 < 0) return delay1; - delay2 = _spi_delay_to_ns(&spi->word_delay, xfer); + delay2 = spi_delay_to_ns(&spi->word_delay, xfer); if (delay2 < 0) return delay2; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index cfd87b18f077..c40d6af2bf07 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -103,6 +103,7 @@ struct spi_delay { u8 unit; }; +extern int spi_delay_to_ns(struct spi_delay *_delay, struct spi_transfer *xfer); extern int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer); /** -- cgit v1.2.3 From 4f3d957718e7f0ac2b033dbf48c7cddecd0a8dd3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 18 Oct 2019 13:54:25 +0300 Subject: spi: pxa2xx: No need to keep pointer to platform device There is no need to keep a pointer to the platform device. Currently there are no users of it directly, and if there will be in the future we may restore it from pointer to the struct device. Convert all users at the same time. Cc: Russell King Cc: Jaroslav Kysela Cc: Takashi Iwai Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20191018105429.82782-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- arch/arm/plat-pxa/ssp.c | 4 ++-- drivers/spi/spi-pxa2xx.c | 2 +- include/linux/pxa2xx_ssp.h | 2 +- sound/soc/pxa/mmp-sspa.c | 2 +- sound/soc/pxa/pxa-ssp.c | 10 +++++----- 5 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/arch/arm/plat-pxa/ssp.c b/arch/arm/plat-pxa/ssp.c index 9a6e4923bd69..563440315acd 100644 --- a/arch/arm/plat-pxa/ssp.c +++ b/arch/arm/plat-pxa/ssp.c @@ -89,7 +89,7 @@ void pxa_ssp_free(struct ssp_device *ssp) ssp->use_count--; ssp->label = NULL; } else - dev_err(&ssp->pdev->dev, "device already free\n"); + dev_err(ssp->dev, "device already free\n"); mutex_unlock(&ssp_lock); } EXPORT_SYMBOL(pxa_ssp_free); @@ -118,7 +118,7 @@ static int pxa_ssp_probe(struct platform_device *pdev) if (ssp == NULL) return -ENOMEM; - ssp->pdev = pdev; + ssp->dev = dev; ssp->clk = devm_clk_get(dev, NULL); if (IS_ERR(ssp->clk)) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index bb6a14d1ab0f..59d1406a9c96 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1567,7 +1567,7 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) ssp->clk = devm_clk_get(&pdev->dev, NULL); ssp->irq = platform_get_irq(pdev, 0); ssp->type = type; - ssp->pdev = pdev; + ssp->dev = &pdev->dev; ssp->port_id = pxa2xx_spi_get_port_id(adev); pdata->is_slave = of_property_read_bool(pdev->dev.of_node, "spi-slave"); diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index a5d1837e4f35..6facf27865f9 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -206,7 +206,7 @@ enum pxa_ssp_type { }; struct ssp_device { - struct platform_device *pdev; + struct device *dev; struct list_head node; struct clk *clk; diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c index e3e5425b5c62..e701637a9ae9 100644 --- a/sound/soc/pxa/mmp-sspa.c +++ b/sound/soc/pxa/mmp-sspa.c @@ -177,7 +177,7 @@ static int mmp_sspa_set_dai_fmt(struct snd_soc_dai *cpu_dai, /* we can only change the settings if the port is not in use */ if ((mmp_sspa_read_reg(sspa, SSPA_TXSP) & SSPA_SP_S_EN) || (mmp_sspa_read_reg(sspa, SSPA_RXSP) & SSPA_SP_S_EN)) { - dev_err(&sspa->pdev->dev, + dev_err(sspa->dev, "can't change hardware dai format: stream is in use\n"); return -EINVAL; } diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 5fdd1a24c232..6c5201431f6e 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -52,11 +52,11 @@ struct ssp_priv { static void dump_registers(struct ssp_device *ssp) { - dev_dbg(&ssp->pdev->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n", + dev_dbg(ssp->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n", pxa_ssp_read_reg(ssp, SSCR0), pxa_ssp_read_reg(ssp, SSCR1), pxa_ssp_read_reg(ssp, SSTO)); - dev_dbg(&ssp->pdev->dev, "SSPSP 0x%08x SSSR 0x%08x SSACD 0x%08x\n", + dev_dbg(ssp->dev, "SSPSP 0x%08x SSSR 0x%08x SSACD 0x%08x\n", pxa_ssp_read_reg(ssp, SSPSP), pxa_ssp_read_reg(ssp, SSSR), pxa_ssp_read_reg(ssp, SSACD)); } @@ -223,7 +223,7 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, clk_id = PXA_SSP_CLK_EXT; } - dev_dbg(&ssp->pdev->dev, + dev_dbg(ssp->dev, "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %u\n", cpu_dai->id, clk_id, freq); @@ -316,7 +316,7 @@ static int pxa_ssp_set_pll(struct ssp_priv *priv, unsigned int freq) ssacd |= (0x6 << 4); - dev_dbg(&ssp->pdev->dev, + dev_dbg(ssp->dev, "Using SSACDD %x to supply %uHz\n", val, freq); break; @@ -687,7 +687,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, * - complain loudly and fail if they've not been set up yet. */ if ((sscr0 & SSCR0_MOD) && !ttsa) { - dev_err(&ssp->pdev->dev, "No TDM timeslot configured\n"); + dev_err(ssp->dev, "No TDM timeslot configured\n"); return -EINVAL; } -- cgit v1.2.3 From a3470c1829c0c856a19c10af58f8e7792ae27d7a Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Wed, 23 Oct 2019 10:00:46 +0300 Subject: spi: document CS setup, hold & inactive times in header This change documents the CS setup, host & inactive times. They were omitted when the fields were added, and were caught by one of the build bots. Fixes: 25093bdeb6bc ("spi: implement SW control for CS times") Reported-by: kbuild test robot Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20191023070046.12478-1-alexandru.ardelean@analog.com Signed-off-by: Mark Brown --- include/linux/spi/spi.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index c40d6af2bf07..98fe8663033a 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -407,6 +407,11 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * controller has native support for memory like operations. * @unprepare_message: undo any work done by prepare_message(). * @slave_abort: abort the ongoing transfer request on an SPI slave controller + * @cs_setup: delay to be introduced by the controller after CS is asserted + * @cs_hold: delay to be introduced by the controller before CS is deasserted + * @cs_inactive: delay to be introduced by the controller after CS is + * deasserted. If @cs_change_delay is used from @spi_transfer, then the + * two delays will be added up. * @cs_gpios: LEGACY: array of GPIO descs to use as chip select lines; one per * CS number. Any individual value may be -ENOENT for CS lines that * are not GPIOs (driven by the SPI controller itself). Use the cs_gpiods -- cgit v1.2.3 From ae7c2d342a10dbef1e054482f46498b6282a1df0 Mon Sep 17 00:00:00 2001 From: Luhua Xu Date: Mon, 18 Nov 2019 12:57:16 +0800 Subject: spi: mediatek: add SPI_CS_HIGH support Change to use SPI_CS_HIGH to support spi CS polarity setting for chips support enhance_timing. Signed-off-by: Luhua Xu Link: https://lore.kernel.org/r/1574053037-26721-2-git-send-email-luhua.xu@mediatek.com Signed-off-by: Mark Brown --- drivers/spi/spi-mt65xx.c | 12 ++++++++++-- include/linux/platform_data/spi-mt65xx.h | 1 - 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 25fe149a8d9a..6783e12c40c2 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -139,7 +139,6 @@ static const struct mtk_spi_compatible mt8183_compat = { * supplies it. */ static const struct mtk_chip_config mtk_default_chip_info = { - .cs_pol = 0, .sample_sel = 0, }; @@ -230,10 +229,12 @@ static int mtk_spi_prepare_message(struct spi_master *master, #endif if (mdata->dev_comp->enhance_timing) { - if (chip_config->cs_pol) + /* set CS polarity */ + if (spi->mode & SPI_CS_HIGH) reg_val |= SPI_CMD_CS_POL; else reg_val &= ~SPI_CMD_CS_POL; + if (chip_config->sample_sel) reg_val |= SPI_CMD_SAMPLE_SEL; else @@ -264,6 +265,9 @@ static void mtk_spi_set_cs(struct spi_device *spi, bool enable) u32 reg_val; struct mtk_spi *mdata = spi_master_get_devdata(spi->master); + if (spi->mode & SPI_CS_HIGH) + enable = !enable; + reg_val = readl(mdata->base + SPI_CMD_REG); if (!enable) { reg_val |= SPI_CMD_PAUSE_EN; @@ -646,6 +650,10 @@ static int mtk_spi_probe(struct platform_device *pdev) mdata = spi_master_get_devdata(master); mdata->dev_comp = of_id->data; + + if (mdata->dev_comp->enhance_timing) + master->mode_bits |= SPI_CS_HIGH; + if (mdata->dev_comp->must_tx) master->flags = SPI_MASTER_MUST_TX; diff --git a/include/linux/platform_data/spi-mt65xx.h b/include/linux/platform_data/spi-mt65xx.h index f0e6d6483e62..65fd5ffd257c 100644 --- a/include/linux/platform_data/spi-mt65xx.h +++ b/include/linux/platform_data/spi-mt65xx.h @@ -11,7 +11,6 @@ /* Board specific platform_data */ struct mtk_chip_config { - u32 cs_pol; u32 sample_sel; }; #endif -- cgit v1.2.3