Commit 339800d5 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'spi-fix-v6.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi

Pull spi fixes from Mark Brown:
 "A few fixes that came in since my pull request, the Meson fix is a
  little large since it's fixing all possible cases of the problem that
  was observed with the driver and clock API trying to share
  configuration by integrating the device clocking fully with the clock
  API rather than spot fixing the one instance that was observed"

* tag 'spi-fix-v6.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi:
  spi: dt-bindings: Drop Pratyush Yadav
  spi: meson-spicc: add local pow2 clock ops to preserve rate between messages
  MAINTAINERS: rectify entry for ARM/HPE GXP ARCHITECTURE
  spi: spi.c: Add missing __percpu annotations in users of spi_statistics
parents 15df6486 2fd92c7b
......@@ -10,7 +10,7 @@ description:
See spi-peripheral-props.yaml for more info.
maintainers:
- Pratyush Yadav <p.yadav@ti.com>
- Vaishnav Achath <vaishnav.a@ti.com>
properties:
# cdns,qspi-nor.yaml
......
......@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Cadence Quad SPI controller
maintainers:
- Pratyush Yadav <p.yadav@ti.com>
- Vaishnav Achath <vaishnav.a@ti.com>
allOf:
- $ref: spi-controller.yaml#
......
......@@ -16,7 +16,7 @@ description:
their own separate schema that should be referenced from here.
maintainers:
- Pratyush Yadav <p.yadav@ti.com>
- Mark Brown <broonie@kernel.org>
properties:
reg:
......
......@@ -2178,7 +2178,7 @@ M: Jean-Marie Verdun <verdun@hpe.com>
M: Nick Hawkins <nick.hawkins@hpe.com>
S: Maintained
F: Documentation/devicetree/bindings/arm/hpe,gxp.yaml
F: Documentation/devicetree/bindings/spi/hpe,gxp-spi.yaml
F: Documentation/devicetree/bindings/spi/hpe,gxp-spifi.yaml
F: Documentation/devicetree/bindings/timer/hpe,gxp-timer.yaml
F: arch/arm/boot/dts/hpe-bmc*
F: arch/arm/boot/dts/hpe-gxp*
......
......@@ -156,6 +156,7 @@ struct meson_spicc_device {
void __iomem *base;
struct clk *core;
struct clk *pclk;
struct clk_divider pow2_div;
struct clk *clk;
struct spi_message *message;
struct spi_transfer *xfer;
......@@ -168,6 +169,8 @@ struct meson_spicc_device {
unsigned long xfer_remain;
};
#define pow2_clk_to_spicc(_div) container_of(_div, struct meson_spicc_device, pow2_div)
static void meson_spicc_oen_enable(struct meson_spicc_device *spicc)
{
u32 conf;
......@@ -421,7 +424,7 @@ static int meson_spicc_prepare_message(struct spi_master *master,
{
struct meson_spicc_device *spicc = spi_master_get_devdata(master);
struct spi_device *spi = message->spi;
u32 conf = 0;
u32 conf = readl_relaxed(spicc->base + SPICC_CONREG) & SPICC_DATARATE_MASK;
/* Store current message */
spicc->message = message;
......@@ -458,8 +461,6 @@ static int meson_spicc_prepare_message(struct spi_master *master,
/* Select CS */
conf |= FIELD_PREP(SPICC_CS_MASK, spi->chip_select);
/* Default Clock rate core/4 */
/* Default 8bit word */
conf |= FIELD_PREP(SPICC_BITLENGTH_MASK, 8 - 1);
......@@ -476,12 +477,16 @@ static int meson_spicc_prepare_message(struct spi_master *master,
static int meson_spicc_unprepare_transfer(struct spi_master *master)
{
struct meson_spicc_device *spicc = spi_master_get_devdata(master);
u32 conf = readl_relaxed(spicc->base + SPICC_CONREG) & SPICC_DATARATE_MASK;
/* Disable all IRQs */
writel(0, spicc->base + SPICC_INTREG);
device_reset_optional(&spicc->pdev->dev);
/* Set default configuration, keeping datarate field */
writel_relaxed(conf, spicc->base + SPICC_CONREG);
return 0;
}
......@@ -518,14 +523,60 @@ static void meson_spicc_cleanup(struct spi_device *spi)
* Clk path for G12A series:
* pclk -> pow2 fixed div -> pow2 div -> mux -> out
* pclk -> enh fixed div -> enh div -> mux -> out
*
* The pow2 divider is tied to the controller HW state, and the
* divider is only valid when the controller is initialized.
*
* A set of clock ops is added to make sure we don't read/set this
* clock rate while the controller is in an unknown state.
*/
static int meson_spicc_clk_init(struct meson_spicc_device *spicc)
static unsigned long meson_spicc_pow2_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_divider *divider = to_clk_divider(hw);
struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider);
if (!spicc->master->cur_msg || !spicc->master->busy)
return 0;
return clk_divider_ops.recalc_rate(hw, parent_rate);
}
static int meson_spicc_pow2_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_divider *divider = to_clk_divider(hw);
struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider);
if (!spicc->master->cur_msg || !spicc->master->busy)
return -EINVAL;
return clk_divider_ops.determine_rate(hw, req);
}
static int meson_spicc_pow2_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_divider *divider = to_clk_divider(hw);
struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider);
if (!spicc->master->cur_msg || !spicc->master->busy)
return -EINVAL;
return clk_divider_ops.set_rate(hw, rate, parent_rate);
}
const struct clk_ops meson_spicc_pow2_clk_ops = {
.recalc_rate = meson_spicc_pow2_recalc_rate,
.determine_rate = meson_spicc_pow2_determine_rate,
.set_rate = meson_spicc_pow2_set_rate,
};
static int meson_spicc_pow2_clk_init(struct meson_spicc_device *spicc)
{
struct device *dev = &spicc->pdev->dev;
struct clk_fixed_factor *pow2_fixed_div, *enh_fixed_div;
struct clk_divider *pow2_div, *enh_div;
struct clk_mux *mux;
struct clk_fixed_factor *pow2_fixed_div;
struct clk_init_data init;
struct clk *clk;
struct clk_parent_data parent_data[2];
......@@ -560,31 +611,45 @@ static int meson_spicc_clk_init(struct meson_spicc_device *spicc)
if (WARN_ON(IS_ERR(clk)))
return PTR_ERR(clk);
pow2_div = devm_kzalloc(dev, sizeof(*pow2_div), GFP_KERNEL);
if (!pow2_div)
return -ENOMEM;
snprintf(name, sizeof(name), "%s#pow2_div", dev_name(dev));
init.name = name;
init.ops = &clk_divider_ops;
init.flags = CLK_SET_RATE_PARENT;
init.ops = &meson_spicc_pow2_clk_ops;
/*
* Set NOCACHE here to make sure we read the actual HW value
* since we reset the HW after each transfer.
*/
init.flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE;
parent_data[0].hw = &pow2_fixed_div->hw;
init.num_parents = 1;
pow2_div->shift = 16,
pow2_div->width = 3,
pow2_div->flags = CLK_DIVIDER_POWER_OF_TWO,
pow2_div->reg = spicc->base + SPICC_CONREG;
pow2_div->hw.init = &init;
spicc->pow2_div.shift = 16,
spicc->pow2_div.width = 3,
spicc->pow2_div.flags = CLK_DIVIDER_POWER_OF_TWO,
spicc->pow2_div.reg = spicc->base + SPICC_CONREG;
spicc->pow2_div.hw.init = &init;
clk = devm_clk_register(dev, &pow2_div->hw);
if (WARN_ON(IS_ERR(clk)))
return PTR_ERR(clk);
spicc->clk = devm_clk_register(dev, &spicc->pow2_div.hw);
if (WARN_ON(IS_ERR(spicc->clk)))
return PTR_ERR(spicc->clk);
if (!spicc->data->has_enhance_clk_div) {
spicc->clk = clk;
return 0;
}
}
static int meson_spicc_enh_clk_init(struct meson_spicc_device *spicc)
{
struct device *dev = &spicc->pdev->dev;
struct clk_fixed_factor *enh_fixed_div;
struct clk_divider *enh_div;
struct clk_mux *mux;
struct clk_init_data init;
struct clk *clk;
struct clk_parent_data parent_data[2];
char name[64];
memset(&init, 0, sizeof(init));
memset(&parent_data, 0, sizeof(parent_data));
init.parent_data = parent_data;
/* algorithm for enh div: rate = freq / 2 / (N + 1) */
......@@ -637,7 +702,7 @@ static int meson_spicc_clk_init(struct meson_spicc_device *spicc)
snprintf(name, sizeof(name), "%s#sel", dev_name(dev));
init.name = name;
init.ops = &clk_mux_ops;
parent_data[0].hw = &pow2_div->hw;
parent_data[0].hw = &spicc->pow2_div.hw;
parent_data[1].hw = &enh_div->hw;
init.num_parents = 2;
init.flags = CLK_SET_RATE_PARENT;
......@@ -754,11 +819,19 @@ static int meson_spicc_probe(struct platform_device *pdev)
meson_spicc_oen_enable(spicc);
ret = meson_spicc_clk_init(spicc);
ret = meson_spicc_pow2_clk_init(spicc);
if (ret) {
dev_err(&pdev->dev, "pow2 clock registration failed\n");
goto out_clk;
}
if (spicc->data->has_enhance_clk_div) {
ret = meson_spicc_enh_clk_init(spicc);
if (ret) {
dev_err(&pdev->dev, "clock registration failed\n");
goto out_clk;
}
}
ret = devm_spi_register_master(&pdev->dev, master);
if (ret) {
......
......@@ -95,7 +95,7 @@ static ssize_t driver_override_show(struct device *dev,
}
static DEVICE_ATTR_RW(driver_override);
static struct spi_statistics *spi_alloc_pcpu_stats(struct device *dev)
static struct spi_statistics __percpu *spi_alloc_pcpu_stats(struct device *dev)
{
struct spi_statistics __percpu *pcpu_stats;
......@@ -162,7 +162,7 @@ static struct device_attribute dev_attr_spi_device_##field = { \
}
#define SPI_STATISTICS_SHOW_NAME(name, file, field) \
static ssize_t spi_statistics_##name##_show(struct spi_statistics *stat, \
static ssize_t spi_statistics_##name##_show(struct spi_statistics __percpu *stat, \
char *buf) \
{ \
ssize_t len; \
......@@ -309,7 +309,7 @@ static const struct attribute_group *spi_master_groups[] = {
NULL,
};
static void spi_statistics_add_transfer_stats(struct spi_statistics *pcpu_stats,
static void spi_statistics_add_transfer_stats(struct spi_statistics __percpu *pcpu_stats,
struct spi_transfer *xfer,
struct spi_controller *ctlr)
{
......@@ -1275,8 +1275,8 @@ static int spi_transfer_wait(struct spi_controller *ctlr,
struct spi_message *msg,
struct spi_transfer *xfer)
{
struct spi_statistics *statm = ctlr->pcpu_statistics;
struct spi_statistics *stats = msg->spi->pcpu_statistics;
struct spi_statistics __percpu *statm = ctlr->pcpu_statistics;
struct spi_statistics __percpu *stats = msg->spi->pcpu_statistics;
u32 speed_hz = xfer->speed_hz;
unsigned long long ms;
......@@ -1432,8 +1432,8 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
struct spi_transfer *xfer;
bool keep_cs = false;
int ret = 0;
struct spi_statistics *statm = ctlr->pcpu_statistics;
struct spi_statistics *stats = msg->spi->pcpu_statistics;
struct spi_statistics __percpu *statm = ctlr->pcpu_statistics;
struct spi_statistics __percpu *stats = msg->spi->pcpu_statistics;
spi_set_cs(msg->spi, true, false);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment