Commit fc389d1c authored by David S. Miller's avatar David S. Miller

Merge branch 'Add--clock-config-and-pm-support-to-bcm-iProc-mdio-mux'

Arun Parameswaran says:

====================
Add clock config and pm support to bcm iProc mdio mux

The patchset extends the Broadcom iProc mdio mux to add support for
suspend/resume and the ability to configure the internal clock
divider. The patchset also sets the scan control register to
disable external master access.

The base address of the mdio-mux-bcm-iproc is modified to point to the
start of the mdio block's address space, to be able to access all the
mdio's registers. The missing registers are required to configure the
internal clock divider registers in some of the Broadcom SoC's.

Changes from v3:
 - Removed 'platform_set_drvdata(pdev, NULL)' call (in patch 5/8)
 - Fixed the return code handling for the devm_clk_get() call (in patch
   7/8)
 - Added Reviewed-by tag to Patch 8/8

Changes from v2:
 - Addressed Andrew's comments:
     - Moved to using devm_mdiobus_alloc. Added this as a separate patch.
     - Changed to reverse christmas tree order for variable declaration in
       the clock patch
 - Addressed Florian's comments:
     - Removed null checks for the clock before calling unprepare in
       both clock and pm patches.
     - Added check for EPROBE_DEFER when fetching the clock in the clock
       patch.
 - The patch to use the devm API has been added before the clock & pm
   patches. This patch is now patch '5' in the series.
 - Added reviewed-by tags to commit messages of patches which remain
   unmodified from v2.
 - Modified PM patch to use platform_get_drvdata() in suspend/resume
   API's, similar to the recent fix that went in for the remove()
   api.

Changes from v1:
 - Addressed Andrew's comments.
   - Reworked the patches to be based on 'net-next'
   - Removed 'fixes' from the commit messages, the changes are related
     to the new features being added.
   - Maintained backward compatibility to older dt-blob's specifying
     base addresse with an offset. The correction is applied in the
     driver and a message is printed to update the dt-blob.
   - Re-worked and re-ordered the last four patches (4-7).
     - Added setting of the scan control register as a new patch
     - Added a call to 'clk_prepare_enable()' in the patch that adds
       the clock config support, removed the debug message when clock
       is not passed.
     - Simplified the pm support patch (removed the array used for the
       save/restore logic).
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 89b1698c 2c723044
......@@ -13,14 +13,17 @@ MDIO multiplexer node:
Every non-ethernet PHY requires a compatible so that it could be probed based
on this compatible string.
Optional properties:
- clocks: phandle of the core clock which drives the mdio block.
Additional information regarding generic multiplexer properties can be found
at- Documentation/devicetree/bindings/net/mdio-mux.txt
for example:
mdio_mux_iproc: mdio-mux@6602023c {
mdio_mux_iproc: mdio-mux@66020000 {
compatible = "brcm,mdio-mux-iproc";
reg = <0x6602023c 0x14>;
reg = <0x66020000 0x250>;
#address-cells = <1>;
#size-cells = <0>;
......
......@@ -482,9 +482,9 @@ pwm: pwm@66010000 {
status = "disabled";
};
mdio_mux_iproc: mdio-mux@6602023c {
mdio_mux_iproc: mdio-mux@66020000 {
compatible = "brcm,mdio-mux-iproc";
reg = <0x6602023c 0x14>;
reg = <0x66020000 0x250>;
#address-cells = <1>;
#size-cells = <0>;
......
......@@ -278,9 +278,9 @@ hsls {
#include "stingray-pinctrl.dtsi"
mdio_mux_iproc: mdio-mux@2023c {
mdio_mux_iproc: mdio-mux@20000 {
compatible = "brcm,mdio-mux-iproc";
reg = <0x0002023c 0x14>;
reg = <0x00020000 0x250>;
#address-cells = <1>;
#size-cells = <0>;
......
......@@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License
* version 2 (GPLv2) along with this source code.
*/
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/of_mdio.h>
......@@ -22,7 +22,14 @@
#include <linux/mdio-mux.h>
#include <linux/delay.h>
#define MDIO_PARAM_OFFSET 0x00
#define MDIO_RATE_ADJ_EXT_OFFSET 0x000
#define MDIO_RATE_ADJ_INT_OFFSET 0x004
#define MDIO_RATE_ADJ_DIVIDENT_SHIFT 16
#define MDIO_SCAN_CTRL_OFFSET 0x008
#define MDIO_SCAN_CTRL_OVRIDE_EXT_MSTR 28
#define MDIO_PARAM_OFFSET 0x23c
#define MDIO_PARAM_MIIM_CYCLE 29
#define MDIO_PARAM_INTERNAL_SEL 25
#define MDIO_PARAM_BUS_ID 22
......@@ -30,27 +37,56 @@
#define MDIO_PARAM_PHY_ID 16
#define MDIO_PARAM_PHY_DATA 0
#define MDIO_READ_OFFSET 0x04
#define MDIO_READ_OFFSET 0x240
#define MDIO_READ_DATA_MASK 0xffff
#define MDIO_ADDR_OFFSET 0x08
#define MDIO_ADDR_OFFSET 0x244
#define MDIO_CTRL_OFFSET 0x0C
#define MDIO_CTRL_OFFSET 0x248
#define MDIO_CTRL_WRITE_OP 0x1
#define MDIO_CTRL_READ_OP 0x2
#define MDIO_STAT_OFFSET 0x10
#define MDIO_STAT_OFFSET 0x24c
#define MDIO_STAT_DONE 1
#define BUS_MAX_ADDR 32
#define EXT_BUS_START_ADDR 16
#define MDIO_REG_ADDR_SPACE_SIZE 0x250
#define MDIO_OPERATING_FREQUENCY 11000000
#define MDIO_RATE_ADJ_DIVIDENT 1
struct iproc_mdiomux_desc {
void *mux_handle;
void __iomem *base;
struct device *dev;
struct mii_bus *mii_bus;
struct clk *core_clk;
};
static void mdio_mux_iproc_config(struct iproc_mdiomux_desc *md)
{
u32 divisor;
u32 val;
/* Disable external mdio master access */
val = readl(md->base + MDIO_SCAN_CTRL_OFFSET);
val |= BIT(MDIO_SCAN_CTRL_OVRIDE_EXT_MSTR);
writel(val, md->base + MDIO_SCAN_CTRL_OFFSET);
if (md->core_clk) {
/* use rate adjust regs to derrive the mdio's operating
* frequency from the specified core clock
*/
divisor = clk_get_rate(md->core_clk) / MDIO_OPERATING_FREQUENCY;
divisor = divisor / (MDIO_RATE_ADJ_DIVIDENT + 1);
val = divisor;
val |= MDIO_RATE_ADJ_DIVIDENT << MDIO_RATE_ADJ_DIVIDENT_SHIFT;
writel(val, md->base + MDIO_RATE_ADJ_EXT_OFFSET);
writel(val, md->base + MDIO_RATE_ADJ_INT_OFFSET);
}
}
static int iproc_mdio_wait_for_idle(void __iomem *base, bool result)
{
unsigned int timeout = 1000; /* loop for 1s */
......@@ -169,18 +205,39 @@ static int mdio_mux_iproc_probe(struct platform_device *pdev)
md->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res->start & 0xfff) {
/* For backward compatibility in case the
* base address is specified with an offset.
*/
dev_info(&pdev->dev, "fix base address in dt-blob\n");
res->start &= ~0xfff;
res->end = res->start + MDIO_REG_ADDR_SPACE_SIZE - 1;
}
md->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(md->base)) {
dev_err(&pdev->dev, "failed to ioremap register\n");
return PTR_ERR(md->base);
}
md->mii_bus = mdiobus_alloc();
md->mii_bus = devm_mdiobus_alloc(&pdev->dev);
if (!md->mii_bus) {
dev_err(&pdev->dev, "mdiomux bus alloc failed\n");
return -ENOMEM;
}
md->core_clk = devm_clk_get(&pdev->dev, NULL);
if (md->core_clk == ERR_PTR(-ENOENT) ||
md->core_clk == ERR_PTR(-EINVAL))
md->core_clk = NULL;
else if (IS_ERR(md->core_clk))
return PTR_ERR(md->core_clk);
rc = clk_prepare_enable(md->core_clk);
if (rc) {
dev_err(&pdev->dev, "failed to enable core clk\n");
return rc;
}
bus = md->mii_bus;
bus->priv = md;
bus->name = "iProc MDIO mux bus";
......@@ -194,7 +251,7 @@ static int mdio_mux_iproc_probe(struct platform_device *pdev)
rc = mdiobus_register(bus);
if (rc) {
dev_err(&pdev->dev, "mdiomux registration failed\n");
goto out;
goto out_clk;
}
platform_set_drvdata(pdev, md);
......@@ -206,13 +263,15 @@ static int mdio_mux_iproc_probe(struct platform_device *pdev)
goto out_register;
}
mdio_mux_iproc_config(md);
dev_info(md->dev, "iProc mdiomux registered\n");
return 0;
out_register:
mdiobus_unregister(bus);
out:
mdiobus_free(bus);
out_clk:
clk_disable_unprepare(md->core_clk);
return rc;
}
......@@ -222,11 +281,37 @@ static int mdio_mux_iproc_remove(struct platform_device *pdev)
mdio_mux_uninit(md->mux_handle);
mdiobus_unregister(md->mii_bus);
mdiobus_free(md->mii_bus);
clk_disable_unprepare(md->core_clk);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int mdio_mux_iproc_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct iproc_mdiomux_desc *md = platform_get_drvdata(pdev);
clk_disable_unprepare(md->core_clk);
return 0;
}
static int mdio_mux_iproc_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct iproc_mdiomux_desc *md = platform_get_drvdata(pdev);
clk_prepare_enable(md->core_clk);
mdio_mux_iproc_config(md);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(mdio_mux_iproc_pm_ops,
mdio_mux_iproc_suspend, mdio_mux_iproc_resume);
static const struct of_device_id mdio_mux_iproc_match[] = {
{
.compatible = "brcm,mdio-mux-iproc",
......@@ -239,6 +324,7 @@ static struct platform_driver mdiomux_iproc_driver = {
.driver = {
.name = "mdio-mux-iproc",
.of_match_table = mdio_mux_iproc_match,
.pm = &mdio_mux_iproc_pm_ops,
},
.probe = mdio_mux_iproc_probe,
.remove = mdio_mux_iproc_remove,
......
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