From: "Magnus Aagaard Sørensen" <mas@csselectronics.com>
To: linux-can@vger.kernel.org
Cc: "Magnus Aagaard Sørensen" <mas@csselectronics.com>
Subject: [PATCH 2/2] Enable support for internal PLL in mcp251xfd.
Date: Thu, 15 Oct 2020 13:44:01 +0100 [thread overview]
Message-ID: <20201015124401.2766-3-mas@csselectronics.com> (raw)
In-Reply-To: <20201015124401.2766-1-mas@csselectronics.com>
The PLL is enabled if the configured clock is less than or equal to 10 times the max clock frequency.
The device will operate with two different SPI speeds. A slow speed determined by the clock without the PLL enabled, and a fast speed derived from the frequency with the PLL enabled.
---
.../net/can/spi/mcp251xfd/mcp251xfd-core.c | 89 ++++++++++++++++---
drivers/net/can/spi/mcp251xfd/mcp251xfd.h | 3 +
2 files changed, 81 insertions(+), 11 deletions(-)
diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
index 3c440f9c8..2436eaed2 100644
--- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
+++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
@@ -561,6 +561,49 @@ static int mcp251xfd_chip_wake(const struct mcp251xfd_priv *priv)
return 0;
}
+static int mcp251xfd_chip_setup_clock(const struct mcp251xfd_priv *priv)
+{
+ u32 osc, osc_reference, osc_mask;
+ int err;
+
+ if (priv->pll_enabled == false) {
+ return 0;
+ }
+
+ err = regmap_read(priv->map_reg, MCP251XFD_REG_OSC, &osc);
+ if (err)
+ return err;
+
+ osc |= MCP251XFD_REG_OSC_PLLEN;
+ osc_reference = MCP251XFD_REG_OSC_OSCRDY | MCP251XFD_REG_OSC_PLLRDY;
+ osc_mask = MCP251XFD_REG_OSC_OSCRDY | MCP251XFD_REG_OSC_PLLRDY;
+
+ err = regmap_write(priv->map_reg, MCP251XFD_REG_OSC, osc);
+ if (err)
+ return err;
+
+ /* Wait for "Oscillator Ready" and "PLL Ready" bit */
+ err = regmap_read_poll_timeout(priv->map_reg, MCP251XFD_REG_OSC, osc,
+ (osc & osc_mask) == osc_reference,
+ MCP251XFD_OSC_STAB_SLEEP_US,
+ MCP251XFD_OSC_STAB_TIMEOUT_US);
+ if (mcp251xfd_osc_invalid(osc)) {
+ netdev_err(priv->ndev,
+ "Failed to detect %s (osc=0x%08x).\n",
+ mcp251xfd_get_model_str(priv), osc);
+ return -ENODEV;
+ } else if (err == -ETIMEDOUT) {
+ netdev_err(priv->ndev,
+ "Timeout waiting for Oscillator Ready (osc=0x%08x, osc_reference=0x%08x)\n",
+ osc, osc_reference);
+ return -ETIMEDOUT;
+ } else if (err) {
+ return err;
+ }
+
+ return 0;
+}
+
static int mcp251xfd_chip_softreset_do(const struct mcp251xfd_priv *priv)
{
const __be16 cmd = mcp251xfd_cmd_reset();
@@ -2568,11 +2611,12 @@ mcp251xfd_register_done(const struct mcp251xfd_priv *priv)
return err;
netdev_info(priv->ndev,
- "%s rev%lu.%lu (%cRX_INT %cMAB_NO_WARN %cCRC_REG %cCRC_RX %cCRC_TX %cECC %cHD c:%u.%02uMHz m:%u.%02uMHz r:%u.%02uMHz e:%u.%02uMHz) successfully initialized.\n",
+ "%s rev%lu.%lu (%cRX_INT %cPLL %cMAB_NO_WARN %cCRC_REG %cCRC_RX %cCRC_TX %cECC %cHD c:%u.%02uMHz m:%u.%02uMHz rs:%u.%02uMHz rf:%u.%02uMHz e:%u.%02uMHz) successfully initialized.\n",
mcp251xfd_get_model_str(priv),
FIELD_GET(MCP251XFD_REG_DEVID_ID_MASK, dev_id),
FIELD_GET(MCP251XFD_REG_DEVID_REV_MASK, dev_id),
priv->rx_int ? '+' : '-',
+ priv->pll_enabled ? '+' : '-',
MCP251XFD_QUIRK_ACTIVE(MAB_NO_WARN),
MCP251XFD_QUIRK_ACTIVE(CRC_REG),
MCP251XFD_QUIRK_ACTIVE(CRC_RX),
@@ -2583,8 +2627,10 @@ mcp251xfd_register_done(const struct mcp251xfd_priv *priv)
priv->can.clock.freq % 1000000 / 1000 / 10,
priv->spi_max_speed_hz_orig / 1000000,
priv->spi_max_speed_hz_orig % 1000000 / 1000 / 10,
- priv->spi->max_speed_hz / 1000000,
- priv->spi->max_speed_hz % 1000000 / 1000 / 10,
+ priv->spi_max_speed_hz_slow / 1000000,
+ priv->spi_max_speed_hz_slow % 1000000 / 1000 / 10,
+ priv->spi_max_speed_hz_fast / 1000000,
+ priv->spi_max_speed_hz_fast % 1000000 / 1000 / 10,
effective_speed_hz / 1000000,
effective_speed_hz % 1000000 / 1000 / 10);
@@ -2614,6 +2660,12 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv)
if (err)
goto out_chip_set_mode_sleep;
+ err = mcp251xfd_chip_setup_clock(priv);
+ if (err)
+ return err;
+
+ priv->spi->max_speed_hz = priv->spi_max_speed_hz_fast;
+
err = mcp251xfd_register_chip_detect(priv);
if (err)
goto out_chip_set_mode_sleep;
@@ -2706,6 +2758,7 @@ static int mcp251xfd_probe(struct spi_device *spi)
struct clk *clk;
u32 freq;
int err;
+ bool pll_enabled = false;
rx_int = devm_gpiod_get_optional(&spi->dev, "microchip,rx-int",
GPIOD_IN);
@@ -2747,10 +2800,7 @@ static int mcp251xfd_probe(struct spi_device *spi)
}
if (freq <= MCP251XFD_SYSCLOCK_HZ_MAX / MCP251XFD_OSC_PLL_MULTIPLIER) {
- dev_err(&spi->dev,
- "Oscillator frequency (%u Hz) is too low and PLL is not supported.\n",
- freq);
- return -ERANGE;
+ pll_enabled = true;
}
ndev = alloc_candev(sizeof(struct mcp251xfd_priv),
@@ -2766,7 +2816,7 @@ static int mcp251xfd_probe(struct spi_device *spi)
priv = netdev_priv(ndev);
spi_set_drvdata(spi, priv);
- priv->can.clock.freq = freq;
+ priv->can.clock.freq = pll_enabled ? freq * MCP251XFD_OSC_PLL_MULTIPLIER : freq;
priv->can.do_set_mode = mcp251xfd_set_mode;
priv->can.do_get_berr_counter = mcp251xfd_get_berr_counter;
priv->can.bittiming_const = &mcp251xfd_bittiming_const;
@@ -2778,6 +2828,7 @@ static int mcp251xfd_probe(struct spi_device *spi)
priv->spi = spi;
priv->rx_int = rx_int;
priv->clk = clk;
+ priv->pll_enabled = pll_enabled;
priv->reg_vdd = reg_vdd;
priv->reg_xceiver = reg_xceiver;
@@ -2817,7 +2868,15 @@ static int mcp251xfd_probe(struct spi_device *spi)
*
*/
priv->spi_max_speed_hz_orig = spi->max_speed_hz;
- spi->max_speed_hz = min(spi->max_speed_hz, freq / 2 / 1000 * 850);
+
+ priv->spi_max_speed_hz_slow = min(spi->max_speed_hz, freq / 2 / 1000 * 850);
+ if (priv->pll_enabled == true) {
+ priv->spi_max_speed_hz_fast = min(spi->max_speed_hz, freq * MCP251XFD_OSC_PLL_MULTIPLIER / 2 / 1000 * 850);
+ } else {
+ priv->spi_max_speed_hz_fast = priv->spi_max_speed_hz_slow;
+ }
+
+ spi->max_speed_hz = priv->spi_max_speed_hz_slow;
spi->bits_per_word = 8;
spi->rt = true;
err = spi_setup(spi);
@@ -2866,6 +2925,8 @@ static int __maybe_unused mcp251xfd_runtime_suspend(struct device *device)
int err;
const struct mcp251xfd_priv *priv = dev_get_drvdata(device);
+ priv->spi->max_speed_hz = priv->spi_max_speed_hz_slow;
+
/* Activate Low Power Mode on Oscillator Disable. This only
* works on the MCP2518FD. The MCP2517FD will go into normal
* Sleep Mode instead.
@@ -2887,7 +2948,7 @@ static int __maybe_unused mcp251xfd_runtime_suspend(struct device *device)
if (err)
return err;
- return err;
+ return 0;
}
static int __maybe_unused mcp251xfd_runtime_resume(struct device *device)
@@ -2907,7 +2968,13 @@ static int __maybe_unused mcp251xfd_runtime_resume(struct device *device)
if (err)
return err;
- return err;
+ err = mcp251xfd_chip_setup_clock(priv);
+ if (err)
+ return err;
+
+ priv->spi->max_speed_hz = priv->spi_max_speed_hz_fast;
+
+ return 0;
}
static const struct dev_pm_ops mcp251xfd_pm_ops = {
diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
index fa1246e39..fc1a3ba5a 100644
--- a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
+++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
@@ -579,6 +579,8 @@ struct mcp251xfd_priv {
struct spi_device *spi;
u32 spi_max_speed_hz_orig;
+ u32 spi_max_speed_hz_fast;
+ u32 spi_max_speed_hz_slow;
struct mcp251xfd_tef_ring tef;
struct mcp251xfd_tx_ring tx[1];
@@ -591,6 +593,7 @@ struct mcp251xfd_priv {
struct gpio_desc *rx_int;
struct clk *clk;
+ bool pll_enabled;
struct regulator *reg_vdd;
struct regulator *reg_xceiver;
--
2.20.1
prev parent reply other threads:[~2020-10-15 12:45 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-10-15 12:43 [PATCH 0/2] can: mcp251xfd: Enable support for PLL Magnus Aagaard Sørensen
2020-10-15 12:44 ` [PATCH 1/2] Preparation for support of internal PLL in mcp251xfd Magnus Aagaard Sørensen
2020-10-15 12:44 ` Magnus Aagaard Sørensen [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20201015124401.2766-3-mas@csselectronics.com \
--to=mas@csselectronics.com \
--cc=linux-can@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.