diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-03 19:30:59 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-03 19:30:59 -0700 |
commit | 822ef14e9dc73079c646d33aa77e2ac42361b39e (patch) | |
tree | 34a8f741f64ef1e617575204db81aa6484df55ac /drivers/spi | |
parent | 6ce076f4159fcf7436cce1299b05eabe200592f4 (diff) | |
parent | d76cfc7c3ad23a79eaf348a1b483e89f8ac3041a (diff) |
Merge tag 'arm-drivers-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
Pull ARM SoC driver updates from Arnd Bergmann:
"A couple of subsystems have their own subsystem maintainers but choose
to have the code merged through the soc tree as upstream, as the code
tends to be used across multiple SoCs or has SoC specific drivers
itself:
- memory controllers:
Krzysztof Kozlowski takes ownership of the drivers/memory subsystem
and its drivers, starting out with a set of cleanup patches.
A larger driver for the Tegra memory controller that was
accidentally missed for v5.8 is now added.
- reset controllers:
Only minor updates to drivers/reset this time
- firmware:
The "turris mox" firmware driver gains support for signed firmware
blobs The tegra firmware driver gets extended to export some debug
information Various updates to i.MX firmware drivers, mostly
cosmetic
- ARM SCMI/SCPI:
A new mechanism for platform notifications is added, among a number
of minor changes.
- optee:
Probing of the TEE bus is rewritten to better support detection of
devices that depend on the tee-supplicant user space. A new
firmware based trusted platform module (fTPM) driver is added based
on OP-TEE
- SoC attributes:
A new driver is added to provide a generic soc_device for
identifying a machine through the SMCCC ARCH_SOC_ID firmware
interface rather than by probing SoC family specific registers.
The series also contains some cleanups to the common soc_device
code.
There are also a number of updates to SoC specific drivers, the main
ones are:
- Mediatek cmdq driver gains a few in-kernel interfaces
- Minor updates to Qualcomm RPMh, socinfo, rpm drivers, mostly adding
support for additional SoC variants
- The Qualcomm GENI core code gains interconnect path voting and
performance level support, and integrating this into a number of
device drivers.
- A new driver for Samsung Exynos5800 voltage coupler for
- Renesas RZ/G2H (R8A774E1) SoC support gets added to a couple of SoC
specific device drivers
- Updates to the TI K3 Ring Accelerator driver"
* tag 'arm-drivers-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (164 commits)
soc: qcom: geni: Fix unused label warning
soc: qcom: smd-rpm: Fix kerneldoc
memory: jz4780_nemc: Only request IO memory the driver will use
soc: qcom: pdr: Reorder the PD state indication ack
MAINTAINERS: Add Git repository for memory controller drivers
memory: brcmstb_dpfe: Fix language typo
memory: samsung: exynos5422-dmc: Correct white space issues
memory: samsung: exynos-srom: Correct alignment
memory: pl172: Enclose macro argument usage in parenthesis
memory: of: Correct kerneldoc
memory: omap-gpmc: Fix language typo
memory: omap-gpmc: Correct white space issues
memory: omap-gpmc: Use 'unsigned int' for consistency
memory: omap-gpmc: Enclose macro argument usage in parenthesis
memory: omap-gpmc: Correct kerneldoc
memory: mvebu-devbus: Align with open parenthesis
memory: mvebu-devbus: Add missing braces to all arms of if statement
memory: bt1-l2-ctl: Add blank lines after declarations
soc: TI knav_qmss: make symbol 'knav_acc_range_ops' static
firmware: ti_sci: Replace HTTP links with HTTPS ones
...
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/spi-geni-qcom.c | 193 | ||||
-rw-r--r-- | drivers/spi/spi-qcom-qspi.c | 117 |
2 files changed, 232 insertions, 78 deletions
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index c3972424af71..7a88cc036004 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -7,6 +7,7 @@ #include <linux/log2.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm_opp.h> #include <linux/pm_runtime.h> #include <linux/qcom-geni-se.h> #include <linux/spi/spi.h> @@ -76,7 +77,9 @@ struct spi_geni_master { u32 tx_fifo_depth; u32 fifo_width_bits; u32 tx_wm; + u32 last_mode; unsigned long cur_speed_hz; + unsigned long cur_sclk_hz; unsigned int cur_bits_per_word; unsigned int tx_rem_bytes; unsigned int rx_rem_bytes; @@ -95,7 +98,6 @@ static int get_spi_clk_cfg(unsigned int speed_hz, { unsigned long sclk_freq; unsigned int actual_hz; - struct geni_se *se = &mas->se; int ret; ret = geni_se_clk_freq_match(&mas->se, @@ -112,9 +114,12 @@ static int get_spi_clk_cfg(unsigned int speed_hz, dev_dbg(mas->dev, "req %u=>%u sclk %lu, idx %d, div %d\n", speed_hz, actual_hz, sclk_freq, *clk_idx, *clk_div); - ret = clk_set_rate(se->clk, sclk_freq); + ret = dev_pm_opp_set_rate(mas->dev, sclk_freq); if (ret) - dev_err(mas->dev, "clk_set_rate failed %d\n", ret); + dev_err(mas->dev, "dev_pm_opp_set_rate failed %d\n", ret); + else + mas->cur_sclk_hz = sclk_freq; + return ret; } @@ -177,8 +182,6 @@ static void spi_setup_word_len(struct spi_geni_master *mas, u16 mode, struct geni_se *se = &mas->se; u32 word_len; - word_len = readl(se->base + SE_SPI_WORD_LEN); - /* * If bits_per_word isn't a byte aligned value, set the packing to be * 1 SPI word per FIFO word. @@ -187,74 +190,94 @@ static void spi_setup_word_len(struct spi_geni_master *mas, u16 mode, pack_words = mas->fifo_width_bits / bits_per_word; else pack_words = 1; - word_len &= ~WORD_LEN_MSK; - word_len |= ((bits_per_word - MIN_WORD_LEN) & WORD_LEN_MSK); geni_se_config_packing(&mas->se, bits_per_word, pack_words, msb_first, true, true); + word_len = (bits_per_word - MIN_WORD_LEN) & WORD_LEN_MSK; writel(word_len, se->base + SE_SPI_WORD_LEN); } -static int setup_fifo_params(struct spi_device *spi_slv, - struct spi_master *spi) +static int geni_spi_set_clock_and_bw(struct spi_geni_master *mas, + unsigned long clk_hz) { - struct spi_geni_master *mas = spi_master_get_devdata(spi); + u32 clk_sel, m_clk_cfg, idx, div; struct geni_se *se = &mas->se; - u32 loopback_cfg, cpol, cpha, demux_output_inv; - u32 demux_sel, clk_sel, m_clk_cfg, idx, div; int ret; - loopback_cfg = readl(se->base + SE_SPI_LOOPBACK); - cpol = readl(se->base + SE_SPI_CPOL); - cpha = readl(se->base + SE_SPI_CPHA); - demux_output_inv = 0; - loopback_cfg &= ~LOOPBACK_MSK; - cpol &= ~CPOL; - cpha &= ~CPHA; - - if (spi_slv->mode & SPI_LOOP) - loopback_cfg |= LOOPBACK_ENABLE; - - if (spi_slv->mode & SPI_CPOL) - cpol |= CPOL; - - if (spi_slv->mode & SPI_CPHA) - cpha |= CPHA; - - if (spi_slv->mode & SPI_CS_HIGH) - demux_output_inv = BIT(spi_slv->chip_select); - - demux_sel = spi_slv->chip_select; - mas->cur_speed_hz = spi_slv->max_speed_hz; - mas->cur_bits_per_word = spi_slv->bits_per_word; + if (clk_hz == mas->cur_speed_hz) + return 0; - ret = get_spi_clk_cfg(mas->cur_speed_hz, mas, &idx, &div); + ret = get_spi_clk_cfg(clk_hz, mas, &idx, &div); if (ret) { - dev_err(mas->dev, "Err setting clks ret(%d) for %ld\n", - ret, mas->cur_speed_hz); + dev_err(mas->dev, "Err setting clk to %lu: %d\n", clk_hz, ret); return ret; } + /* + * SPI core clock gets configured with the requested frequency + * or the frequency closer to the requested frequency. + * For that reason requested frequency is stored in the + * cur_speed_hz and referred in the consecutive transfer instead + * of calling clk_get_rate() API. + */ + mas->cur_speed_hz = clk_hz; + clk_sel = idx & CLK_SEL_MSK; m_clk_cfg = (div << CLK_DIV_SHFT) | SER_CLK_EN; - spi_setup_word_len(mas, spi_slv->mode, spi_slv->bits_per_word); - writel(loopback_cfg, se->base + SE_SPI_LOOPBACK); - writel(demux_sel, se->base + SE_SPI_DEMUX_SEL); - writel(cpha, se->base + SE_SPI_CPHA); - writel(cpol, se->base + SE_SPI_CPOL); - writel(demux_output_inv, se->base + SE_SPI_DEMUX_OUTPUT_INV); writel(clk_sel, se->base + SE_GENI_CLK_SEL); writel(m_clk_cfg, se->base + GENI_SER_M_CLK_CFG); + + /* Set BW quota for CPU as driver supports FIFO mode only. */ + se->icc_paths[CPU_TO_GENI].avg_bw = Bps_to_icc(mas->cur_speed_hz); + ret = geni_icc_set_bw(se); + if (ret) + return ret; + return 0; } +static int setup_fifo_params(struct spi_device *spi_slv, + struct spi_master *spi) +{ + struct spi_geni_master *mas = spi_master_get_devdata(spi); + struct geni_se *se = &mas->se; + u32 loopback_cfg = 0, cpol = 0, cpha = 0, demux_output_inv = 0; + u32 demux_sel; + + if (mas->last_mode != spi_slv->mode) { + if (spi_slv->mode & SPI_LOOP) + loopback_cfg = LOOPBACK_ENABLE; + + if (spi_slv->mode & SPI_CPOL) + cpol = CPOL; + + if (spi_slv->mode & SPI_CPHA) + cpha = CPHA; + + if (spi_slv->mode & SPI_CS_HIGH) + demux_output_inv = BIT(spi_slv->chip_select); + + demux_sel = spi_slv->chip_select; + mas->cur_bits_per_word = spi_slv->bits_per_word; + + spi_setup_word_len(mas, spi_slv->mode, spi_slv->bits_per_word); + writel(loopback_cfg, se->base + SE_SPI_LOOPBACK); + writel(demux_sel, se->base + SE_SPI_DEMUX_SEL); + writel(cpha, se->base + SE_SPI_CPHA); + writel(cpol, se->base + SE_SPI_CPOL); + writel(demux_output_inv, se->base + SE_SPI_DEMUX_OUTPUT_INV); + + mas->last_mode = spi_slv->mode; + } + + return geni_spi_set_clock_and_bw(mas, spi_slv->max_speed_hz); +} + static int spi_geni_prepare_message(struct spi_master *spi, struct spi_message *spi_msg) { int ret; struct spi_geni_master *mas = spi_master_get_devdata(spi); - struct geni_se *se = &mas->se; - geni_se_select_mode(se, GENI_SE_FIFO); ret = setup_fifo_params(spi_msg->spi, spi); if (ret) dev_err(mas->dev, "Couldn't select mode %d\n", ret); @@ -295,6 +318,8 @@ static int spi_geni_init(struct spi_geni_master *mas) else mas->oversampling = 1; + geni_se_select_mode(se, GENI_SE_FIFO); + pm_runtime_put(mas->dev); return 0; } @@ -306,6 +331,7 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, u32 m_cmd = 0; u32 spi_tx_cfg, len; struct geni_se *se = &mas->se; + int ret; spi_tx_cfg = readl(se->base + SE_SPI_TRANS_CFG); if (xfer->bits_per_word != mas->cur_bits_per_word) { @@ -314,29 +340,9 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, } /* Speed and bits per word can be overridden per transfer */ - if (xfer->speed_hz != mas->cur_speed_hz) { - int ret; - u32 clk_sel, m_clk_cfg; - unsigned int idx, div; - - ret = get_spi_clk_cfg(xfer->speed_hz, mas, &idx, &div); - if (ret) { - dev_err(mas->dev, "Err setting clks:%d\n", ret); - return; - } - /* - * SPI core clock gets configured with the requested frequency - * or the frequency closer to the requested frequency. - * For that reason requested frequency is stored in the - * cur_speed_hz and referred in the consecutive transfer instead - * of calling clk_get_rate() API. - */ - mas->cur_speed_hz = xfer->speed_hz; - clk_sel = idx & CLK_SEL_MSK; - m_clk_cfg = (div << CLK_DIV_SHFT) | SER_CLK_EN; - writel(clk_sel, se->base + SE_GENI_CLK_SEL); - writel(m_clk_cfg, se->base + GENI_SER_M_CLK_CFG); - } + ret = geni_spi_set_clock_and_bw(mas, xfer->speed_hz); + if (ret) + return; mas->tx_rem_bytes = 0; mas->rx_rem_bytes = 0; @@ -561,6 +567,17 @@ static int spi_geni_probe(struct platform_device *pdev) mas->se.wrapper = dev_get_drvdata(dev->parent); mas->se.base = base; mas->se.clk = clk; + mas->se.opp_table = dev_pm_opp_set_clkname(&pdev->dev, "se"); + if (IS_ERR(mas->se.opp_table)) + return PTR_ERR(mas->se.opp_table); + /* OPP table is optional */ + ret = dev_pm_opp_of_add_table(&pdev->dev); + if (!ret) { + mas->se.has_opp_table = true; + } else if (ret != -ENODEV) { + dev_err(&pdev->dev, "invalid OPP table in device tree\n"); + return ret; + } spi->bus_num = -1; spi->dev.of_node = dev->of_node; @@ -578,6 +595,17 @@ static int spi_geni_probe(struct platform_device *pdev) spin_lock_init(&mas->lock); pm_runtime_enable(dev); + ret = geni_icc_get(&mas->se, NULL); + if (ret) + goto spi_geni_probe_runtime_disable; + /* Set the bus quota to a reasonable value for register access */ + mas->se.icc_paths[GENI_TO_CORE].avg_bw = Bps_to_icc(CORE_2X_50_MHZ); + mas->se.icc_paths[CPU_TO_GENI].avg_bw = GENI_DEFAULT_BW; + + ret = geni_icc_set_bw(&mas->se); + if (ret) + goto spi_geni_probe_runtime_disable; + ret = spi_geni_init(mas); if (ret) goto spi_geni_probe_runtime_disable; @@ -596,6 +624,9 @@ spi_geni_probe_free_irq: spi_geni_probe_runtime_disable: pm_runtime_disable(dev); spi_master_put(spi); + if (mas->se.has_opp_table) + dev_pm_opp_of_remove_table(&pdev->dev); + dev_pm_opp_put_clkname(mas->se.opp_table); return ret; } @@ -609,6 +640,9 @@ static int spi_geni_remove(struct platform_device *pdev) free_irq(mas->irq, spi); pm_runtime_disable(&pdev->dev); + if (mas->se.has_opp_table) + dev_pm_opp_of_remove_table(&pdev->dev); + dev_pm_opp_put_clkname(mas->se.opp_table); return 0; } @@ -616,16 +650,33 @@ static int __maybe_unused spi_geni_runtime_suspend(struct device *dev) { struct spi_master *spi = dev_get_drvdata(dev); struct spi_geni_master *mas = spi_master_get_devdata(spi); + int ret; + + /* Drop the performance state vote */ + dev_pm_opp_set_rate(dev, 0); + + ret = geni_se_resources_off(&mas->se); + if (ret) + return ret; - return geni_se_resources_off(&mas->se); + return geni_icc_disable(&mas->se); } static int __maybe_unused spi_geni_runtime_resume(struct device *dev) { struct spi_master *spi = dev_get_drvdata(dev); struct spi_geni_master *mas = spi_master_get_devdata(spi); + int ret; + + ret = geni_icc_enable(&mas->se); + if (ret) + return ret; + + ret = geni_se_resources_on(&mas->se); + if (ret) + return ret; - return geni_se_resources_on(&mas->se); + return dev_pm_opp_set_rate(mas->dev, mas->cur_sclk_hz); } static int __maybe_unused spi_geni_suspend(struct device *dev) diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index 3c4f83bf7084..b8857a97f40a 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -2,12 +2,14 @@ // Copyright (c) 2017-2018, The Linux foundation. All rights reserved. #include <linux/clk.h> +#include <linux/interconnect.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_platform.h> #include <linux/pm_runtime.h> +#include <linux/pm_opp.h> #include <linux/spi/spi.h> #include <linux/spi/spi-mem.h> @@ -139,7 +141,11 @@ struct qcom_qspi { struct device *dev; struct clk_bulk_data *clks; struct qspi_xfer xfer; - /* Lock to protect xfer and IRQ accessed registers */ + struct icc_path *icc_path_cpu_to_qspi; + struct opp_table *opp_table; + bool has_opp_table; + unsigned long last_speed; + /* Lock to protect data accessed by IRQs */ spinlock_t lock; }; @@ -221,6 +227,38 @@ static void qcom_qspi_handle_err(struct spi_master *master, spin_unlock_irqrestore(&ctrl->lock, flags); } +static int qcom_qspi_set_speed(struct qcom_qspi *ctrl, unsigned long speed_hz) +{ + int ret; + unsigned int avg_bw_cpu; + + if (speed_hz == ctrl->last_speed) + return 0; + + /* In regular operation (SBL_EN=1) core must be 4x transfer clock */ + ret = dev_pm_opp_set_rate(ctrl->dev, speed_hz * 4); + if (ret) { + dev_err(ctrl->dev, "Failed to set core clk %d\n", ret); + return ret; + } + + /* + * Set BW quota for CPU as driver supports FIFO mode only. + * We don't have explicit peak requirement so keep it equal to avg_bw. + */ + avg_bw_cpu = Bps_to_icc(speed_hz); + ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, avg_bw_cpu, avg_bw_cpu); + if (ret) { + dev_err(ctrl->dev, "%s: ICC BW voting failed for cpu: %d\n", + __func__, ret); + return ret; + } + + ctrl->last_speed = speed_hz; + + return 0; +} + static int qcom_qspi_transfer_one(struct spi_master *master, struct spi_device *slv, struct spi_transfer *xfer) @@ -234,12 +272,9 @@ static int qcom_qspi_transfer_one(struct spi_master *master, if (xfer->speed_hz) speed_hz = xfer->speed_hz; - /* In regular operation (SBL_EN=1) core must be 4x transfer clock */ - ret = clk_set_rate(ctrl->clks[QSPI_CLK_CORE].clk, speed_hz * 4); - if (ret) { - dev_err(ctrl->dev, "Failed to set core clk %d\n", ret); + ret = qcom_qspi_set_speed(ctrl, speed_hz); + if (ret) return ret; - } spin_lock_irqsave(&ctrl->lock, flags); @@ -458,6 +493,29 @@ static int qcom_qspi_probe(struct platform_device *pdev) if (ret) goto exit_probe_master_put; + ctrl->icc_path_cpu_to_qspi = devm_of_icc_get(dev, "qspi-config"); + if (IS_ERR(ctrl->icc_path_cpu_to_qspi)) { + ret = PTR_ERR(ctrl->icc_path_cpu_to_qspi); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get cpu path: %d\n", ret); + goto exit_probe_master_put; + } + /* Set BW vote for register access */ + ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, Bps_to_icc(1000), + Bps_to_icc(1000)); + if (ret) { + dev_err(ctrl->dev, "%s: ICC BW voting failed for cpu: %d\n", + __func__, ret); + goto exit_probe_master_put; + } + + ret = icc_disable(ctrl->icc_path_cpu_to_qspi); + if (ret) { + dev_err(ctrl->dev, "%s: ICC disable failed for cpu: %d\n", + __func__, ret); + goto exit_probe_master_put; + } + ret = platform_get_irq(pdev, 0); if (ret < 0) goto exit_probe_master_put; @@ -481,6 +539,22 @@ static int qcom_qspi_probe(struct platform_device *pdev) master->handle_err = qcom_qspi_handle_err; master->auto_runtime_pm = true; + ctrl->opp_table = dev_pm_opp_set_clkname(&pdev->dev, "core"); + if (IS_ERR(ctrl->opp_table)) { + ret = PTR_ERR(ctrl->opp_table); + goto exit_probe_master_put; + } + /* OPP table is optional */ + ret = dev_pm_opp_of_add_table(&pdev->dev); + if (!ret) { + ctrl->has_opp_table = true; + } else if (ret != -ENODEV) { + dev_err(&pdev->dev, "invalid OPP table in device tree\n"); + goto exit_probe_master_put; + } + + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 250); pm_runtime_enable(dev); ret = spi_register_master(master); @@ -488,6 +562,9 @@ static int qcom_qspi_probe(struct platform_device *pdev) return 0; pm_runtime_disable(dev); + if (ctrl->has_opp_table) + dev_pm_opp_of_remove_table(&pdev->dev); + dev_pm_opp_put_clkname(ctrl->opp_table); exit_probe_master_put: spi_master_put(master); @@ -498,11 +575,15 @@ exit_probe_master_put: static int qcom_qspi_remove(struct platform_device *pdev) { struct spi_master *master = platform_get_drvdata(pdev); + struct qcom_qspi *ctrl = spi_master_get_devdata(master); /* Unregister _before_ disabling pm_runtime() so we stop transfers */ spi_unregister_master(master); pm_runtime_disable(&pdev->dev); + if (ctrl->has_opp_table) + dev_pm_opp_of_remove_table(&pdev->dev); + dev_pm_opp_put_clkname(ctrl->opp_table); return 0; } @@ -511,9 +592,19 @@ static int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); struct qcom_qspi *ctrl = spi_master_get_devdata(master); + int ret; + /* Drop the performance state vote */ + dev_pm_opp_set_rate(dev, 0); clk_bulk_disable_unprepare(QSPI_NUM_CLKS, ctrl->clks); + ret = icc_disable(ctrl->icc_path_cpu_to_qspi); + if (ret) { + dev_err_ratelimited(ctrl->dev, "%s: ICC disable failed for cpu: %d\n", + __func__, ret); + return ret; + } + return 0; } @@ -521,8 +612,20 @@ static int __maybe_unused qcom_qspi_runtime_resume(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); struct qcom_qspi *ctrl = spi_master_get_devdata(master); + int ret; + + ret = icc_enable(ctrl->icc_path_cpu_to_qspi); + if (ret) { + dev_err_ratelimited(ctrl->dev, "%s: ICC enable failed for cpu: %d\n", + __func__, ret); + return ret; + } + + ret = clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks); + if (ret) + return ret; - return clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks); + return dev_pm_opp_set_rate(dev, ctrl->last_speed * 4); } static int __maybe_unused qcom_qspi_suspend(struct device *dev) |