diff options
Diffstat (limited to 'drivers/spi')
| -rw-r--r-- | drivers/spi/spi-cadence-quadspi.c | 3 | ||||
| -rw-r--r-- | drivers/spi/spi-cs42l43.c | 97 | ||||
| -rw-r--r-- | drivers/spi/spi-nxp-fspi.c | 22 | ||||
| -rw-r--r-- | drivers/spi/spi-sg2044-nor.c | 56 |
4 files changed, 160 insertions, 18 deletions
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index ecb0be394696..5a5f1a852cbe 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1056,8 +1056,7 @@ static int cqspi_write_setup(struct cqspi_flash_pdata *f_pdata, * care of polling the status register. */ if (cqspi->wr_completion) { - reg = readl(reg_base + CQSPI_REG_WR_COMPLETION_CTRL); - reg |= CQSPI_REG_WR_DISABLE_AUTO_POLL; + reg = CQSPI_REG_WR_DISABLE_AUTO_POLL; writel(reg, reg_base + CQSPI_REG_WR_COMPLETION_CTRL); /* * DAC mode require auto polling as flash needs to be polled diff --git a/drivers/spi/spi-cs42l43.c b/drivers/spi/spi-cs42l43.c index 7106a8a4f280..6772abb483dd 100644 --- a/drivers/spi/spi-cs42l43.c +++ b/drivers/spi/spi-cs42l43.c @@ -11,6 +11,7 @@ #include <linux/bitfield.h> #include <linux/cleanup.h> #include <linux/device.h> +#include <linux/dmi.h> #include <linux/errno.h> #include <linux/gpio/consumer.h> #include <linux/gpio/machine.h> @@ -215,12 +216,108 @@ static size_t cs42l43_spi_max_length(struct spi_device *spi) return CS42L43_SPI_MAX_LENGTH; } +/* + * Workaround needed for two speaker ID pins in one ACPI GpioIo() but + * the Linux-specific _DSD property only contains one pin. + * Create a temporary acpi_gpio_mapping pointing at both pins. + */ +static const struct acpi_gpio_params cs42l43_2bit_speaker_id_from_one_gpioio_params[] = { + [0] = { + .crs_entry_index = 0, + .line_index = 0, + }, + [1] = { + .crs_entry_index = 0, + .line_index = 1, + }, +}; + +static const struct acpi_gpio_mapping cs42l43_2bit_speaker_id_from_one_gpioio_mapping[] = { + { + .name = "spk-id-quirk-gpios", + .data = cs42l43_2bit_speaker_id_from_one_gpioio_params, + .size = ARRAY_SIZE(cs42l43_2bit_speaker_id_from_one_gpioio_params), + }, + { } +}; + +static int cs42l43_get_2bit_speaker_id_from_one_gpioio(struct cs42l43_spi *priv, int *result) +{ + struct fwnode_reference_args args; + struct acpi_device *adev; + struct gpio_desc *desc; + u32 spkid = 0; + int i, ret; + + /* Use the _DSD property to get the node containing the GpioIo() */ + ret = fwnode_property_get_reference_args(dev_fwnode(priv->dev), "spk-id-gpios", + NULL, 3, 0, &args); + if (ret) + return ret; + + struct fwnode_handle *fwnode __free(fwnode_handle) = args.fwnode; + + /* An acpi_gpio_mapping must be added to the node that contains the GpioIo() */ + adev = to_acpi_device_node(fwnode); + if (!adev) + return -EINVAL; + + ret = acpi_dev_add_driver_gpios(adev, cs42l43_2bit_speaker_id_from_one_gpioio_mapping); + if (ret) + return ret; + + /* gpiod_get_array() can't read from a mapping in a child node */ + for (i = 0; i < ARRAY_SIZE(cs42l43_2bit_speaker_id_from_one_gpioio_params); i++) { + desc = fwnode_gpiod_get_index(fwnode, "spk-id-quirk", i, GPIOD_IN, + dev_name(priv->dev)); + if (IS_ERR(desc)) { + ret = PTR_ERR(desc); + goto out; + } + + ret = gpiod_get_value_cansleep(desc); + gpiod_put(desc); + if (ret < 0) + goto out; + + spkid |= (u32)ret << i; + } + + dev_dbg(priv->dev, "spk-id-gpios = %u\n", spkid); + *result = spkid; + ret = 0; +out: + acpi_dev_remove_driver_gpios(adev); + + return ret; +} + +static const struct dmi_system_id cs42l43_spk_id_quirks[] = { + { + .ident = "Dell XPS 13 DX13260", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0E53"), + }, + .driver_data = cs42l43_get_2bit_speaker_id_from_one_gpioio, + }, + { } +}; + static int cs42l43_get_speaker_id_gpios(struct cs42l43_spi *priv, int *result) { + const struct dmi_system_id *dmi_id; struct gpio_descs *descs; u32 spkid; int i, ret; + dmi_id = dmi_first_match(cs42l43_spk_id_quirks); + if (dmi_id) { + int (*get_speaker_id)(struct cs42l43_spi *priv, int *result) = dmi_id->driver_data; + + return get_speaker_id(priv, result); + } + descs = gpiod_get_array_optional(priv->dev, "spk-id", GPIOD_IN); if (!descs) return 0; diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index 67d991dd249d..66cb58f95fdb 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -898,10 +898,10 @@ static int nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, dev_dbg(f->dev, "Target device [CS:%x] selected\n", spi_get_chipselect(spi, 0)); /* - * Per the FlexSPI reference manual (initialization sequence), MCR0 and - * the DLL control registers should be configured while the module is in - * stop mode (MCR0[MDIS] = 1). Enter stop mode before reconfiguring the - * RX sample clock source and the DLL, then exit stop mode afterwards. + * Per the FlexSPI reference manual (initialization sequence), MCR0 + * should be configured while the module is in stop mode (MCR0[MDIS] = 1). + * Enter stop mode before reconfiguring the RX sample clock source, then + * exit stop mode afterwards. */ reg = fspi_readl(f, f->iobase + FSPI_MCR0); fspi_writel(f, reg | FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0); @@ -937,6 +937,16 @@ static int nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, return ret; /* + * Exit stop mode before running the DLL calibration. The DLL + * reference clock is derived from the module clock domain, which is + * gated while MCR0[MDIS] = 1. Calibrating the DLL in stop mode leaves + * the STS2 lock bits stuck at 0 (the DLL never runs), so clear MDIS + * first and let the module and reference clock run. + */ + reg = fspi_readl(f, f->iobase + FSPI_MCR0); + fspi_writel(f, reg & ~FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0); + + /* * If clock rate > 100MHz, then switch from DLL override mode to * DLL calibration mode. */ @@ -945,10 +955,6 @@ static int nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, else nxp_fspi_dll_override(f); - /* Exit stop mode now that MCR0 and the DLL have been reconfigured. */ - reg = fspi_readl(f, f->iobase + FSPI_MCR0); - fspi_writel(f, reg & ~FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0); - f->pre_op_rate = op->max_freq; f->selected = spi_get_chipselect(spi, 0); diff --git a/drivers/spi/spi-sg2044-nor.c b/drivers/spi/spi-sg2044-nor.c index f3bcb8a1b92b..bc9a8ee9bce4 100644 --- a/drivers/spi/spi-sg2044-nor.c +++ b/drivers/spi/spi-sg2044-nor.c @@ -307,15 +307,15 @@ static ssize_t sg2044_spifmc_tran_cmd(struct sg2044_spifmc *spifmc, return 0; } -static void sg2044_spifmc_trans(struct sg2044_spifmc *spifmc, - const struct spi_mem_op *op) +static int sg2044_spifmc_trans(struct sg2044_spifmc *spifmc, + const struct spi_mem_op *op) { if (op->data.dir == SPI_MEM_DATA_IN) - sg2044_spifmc_read(spifmc, op); + return sg2044_spifmc_read(spifmc, op); else if (op->data.dir == SPI_MEM_DATA_OUT) - sg2044_spifmc_write(spifmc, op); + return sg2044_spifmc_write(spifmc, op); else - sg2044_spifmc_tran_cmd(spifmc, op); + return sg2044_spifmc_tran_cmd(spifmc, op); } static ssize_t sg2044_spifmc_trans_reg(struct sg2044_spifmc *spifmc, @@ -385,29 +385,64 @@ static ssize_t sg2044_spifmc_trans_reg(struct sg2044_spifmc *spifmc, return 0; } +static int sg2044_spifmc_set_speed(struct sg2044_spifmc *spifmc, + unsigned int speed_hz) +{ + unsigned long rate = clk_get_rate(spifmc->clk); + u64 div; + u32 reg; + + if (!rate) + return -EINVAL; + + /* SCK = HCLK / (2 * (SckDiv + 1)); round down the resulting SCK. */ + div = speed_hz ? DIV_ROUND_UP_ULL(rate, 2ULL * speed_hz) : 1; + if (div > SPIFMC_CTRL_SCK_DIV_MASK + 1) + return -EINVAL; + + reg = readl(spifmc->io_base + SPIFMC_CTRL); + reg &= ~SPIFMC_CTRL_SCK_DIV_MASK; + reg |= div - 1; + writel(reg, spifmc->io_base + SPIFMC_CTRL); + + return 0; +} + static int sg2044_spifmc_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) { struct sg2044_spifmc *spifmc; + int ret; spifmc = spi_controller_get_devdata(mem->spi->controller); mutex_lock(&spifmc->lock); + ret = sg2044_spifmc_set_speed(spifmc, op->max_freq ? + min(op->max_freq, mem->spi->max_speed_hz) : + mem->spi->max_speed_hz); + if (ret) + goto out_unlock; + if (op->addr.nbytes == 0) - sg2044_spifmc_trans_reg(spifmc, op); + ret = sg2044_spifmc_trans_reg(spifmc, op); else - sg2044_spifmc_trans(spifmc, op); + ret = sg2044_spifmc_trans(spifmc, op); +out_unlock: mutex_unlock(&spifmc->lock); - return 0; + return ret; } static const struct spi_controller_mem_ops sg2044_spifmc_mem_ops = { .exec_op = sg2044_spifmc_exec_op, }; +static const struct spi_controller_mem_caps sg2044_spifmc_mem_caps = { + .per_op_freq = true, +}; + static void sg2044_spifmc_init(struct sg2044_spifmc *spifmc) { u32 tran_csr; @@ -447,6 +482,10 @@ static int sg2044_spifmc_probe(struct platform_device *pdev) if (IS_ERR(spifmc->clk)) return dev_err_probe(dev, PTR_ERR(spifmc->clk), "Cannot get and enable AHB clock\n"); + ctrl->max_speed_hz = clk_get_rate(spifmc->clk) / 2; + if (!ctrl->max_speed_hz) + return dev_err_probe(dev, -EINVAL, "Invalid AHB clock rate\n"); + spifmc->dev = &pdev->dev; spifmc->ctrl = ctrl; @@ -458,6 +497,7 @@ static int sg2044_spifmc_probe(struct platform_device *pdev) ctrl->bits_per_word_mask = SPI_BPW_MASK(8); ctrl->auto_runtime_pm = false; ctrl->mem_ops = &sg2044_spifmc_mem_ops; + ctrl->mem_caps = &sg2044_spifmc_mem_caps; ctrl->mode_bits = SPI_RX_DUAL | SPI_TX_DUAL | SPI_RX_QUAD | SPI_TX_QUAD; ret = devm_mutex_init(dev, &spifmc->lock); |
