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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).