forked from torvalds/linux
Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
mfd: ocelot: add support for external mfd control over SPI for the VS…
…C7512 Create a single SPI MFD ocelot device that manages the SPI bus on the external chip and can handle requests for regmaps. This should allow any ocelot driver (pinctrl, miim, etc.) to be used externally, provided they utilize regmaps. Signed-off-by: Colin Foster <colin.foster@in-advantage.com>
- Loading branch information
1 parent
c122052
commit ec7f2a8290ac0b1f74a64884f16444c56edb274f
Showing
5 changed files
with
560 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| // SPDX-License-Identifier: (GPL-2.0 OR MIT) | ||
| /* | ||
| * Copyright 2021 Innovative Advantage Inc. | ||
| */ | ||
|
|
||
| #include <asm/byteorder.h> | ||
| #include <linux/spi/spi.h> | ||
| #include <linux/kconfig.h> | ||
| #include <linux/module.h> | ||
| #include <linux/regmap.h> | ||
|
|
||
| #include "ocelot-mfd.h" | ||
|
|
||
| #define REG(reg, offset) [reg] = offset | ||
|
|
||
| enum ocelot_mfd_gcb_regs { | ||
| GCB_SOFT_RST, | ||
| GCB_REG_MAX, | ||
| }; | ||
|
|
||
| enum ocelot_mfd_gcb_regfields { | ||
| GCB_SOFT_RST_CHIP_RST, | ||
| GCB_REGFIELD_MAX, | ||
| }; | ||
|
|
||
| static const u32 vsc7512_gcb_regmap[] = { | ||
| REG(GCB_SOFT_RST, 0x0008), | ||
| }; | ||
|
|
||
| static const struct reg_field vsc7512_mfd_gcb_regfields[GCB_REGFIELD_MAX] = { | ||
| [GCB_SOFT_RST_CHIP_RST] = REG_FIELD(vsc7512_gcb_regmap[GCB_SOFT_RST], 0, 0), | ||
| }; | ||
|
|
||
| struct ocelot_mfd_core { | ||
| struct ocelot_mfd_config *config; | ||
| struct regmap *gcb_regmap; | ||
| struct regmap_field *gcb_regfields[GCB_REGFIELD_MAX]; | ||
| }; | ||
|
|
||
| static const struct resource vsc7512_gcb_resource = { | ||
| .start = 0x71070000, | ||
| .end = 0x7107022b, | ||
| .name = "devcpu_gcb", | ||
| }; | ||
|
|
||
| static int ocelot_mfd_reset(struct ocelot_mfd_core *core) | ||
| { | ||
| int ret; | ||
|
|
||
| dev_info(core->config->dev, "resetting ocelot chip\n"); | ||
|
|
||
| ret = regmap_field_write(core->gcb_regfields[GCB_SOFT_RST_CHIP_RST], 1); | ||
| if (ret) | ||
| return ret; | ||
|
|
||
| /* | ||
| * Note: This is adapted from the PCIe reset strategy. The manual doesn't | ||
| * suggest how to do a reset over SPI, and the register strategy isn't | ||
| * possible. | ||
| */ | ||
| msleep(100); | ||
|
|
||
| ret = core->config->init_bus(core->config); | ||
| if (ret) | ||
| return ret; | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| void ocelot_mfd_get_resource_name(char *name, const struct resource *res, | ||
| int size) | ||
| { | ||
| if (res->name) | ||
| snprintf(name, size - 1, "ocelot_mfd-%s", res->name); | ||
| else | ||
| snprintf(name, size - 1, "ocelot_mfd@0x%08x", res->start); | ||
| } | ||
| EXPORT_SYMBOL(ocelot_mfd_get_resource_name); | ||
|
|
||
| static struct regmap *ocelot_mfd_regmap_init(struct ocelot_mfd_core *core, | ||
| const struct resource *res) | ||
| { | ||
| struct device *dev = core->config->dev; | ||
| struct regmap *regmap; | ||
| char name[32]; | ||
|
|
||
| ocelot_mfd_get_resource_name(name, res, sizeof(name) - 1); | ||
|
|
||
| regmap = dev_get_regmap(dev, name); | ||
|
|
||
| if (!regmap) | ||
| regmap = core->config->get_regmap(core->config, res, name); | ||
|
|
||
| return regmap; | ||
| } | ||
|
|
||
| int ocelot_mfd_init(struct ocelot_mfd_config *config) | ||
| { | ||
| struct device *dev = config->dev; | ||
| const struct reg_field *regfield; | ||
| struct ocelot_mfd_core *core; | ||
| int i, ret; | ||
|
|
||
| core = devm_kzalloc(dev, sizeof(struct ocelot_mfd_config), GFP_KERNEL); | ||
| if (!core) | ||
| return -ENOMEM; | ||
|
|
||
| dev_set_drvdata(dev, core); | ||
|
|
||
| core->config = config; | ||
|
|
||
| /* Create regmaps and regfields here */ | ||
| core->gcb_regmap = ocelot_mfd_regmap_init(core, &vsc7512_gcb_resource); | ||
| if (!core->gcb_regmap) | ||
| return -ENOMEM; | ||
|
|
||
| for (i = 0; i < GCB_REGFIELD_MAX; i++) { | ||
| regfield = &vsc7512_mfd_gcb_regfields[i]; | ||
| core->gcb_regfields[i] = | ||
| devm_regmap_field_alloc(dev, core->gcb_regmap, | ||
| *regfield); | ||
| if (!core->gcb_regfields[i]) | ||
| return -ENOMEM; | ||
| } | ||
|
|
||
| /* Prepare the chip */ | ||
| ret = ocelot_mfd_reset(core); | ||
| if (ret) { | ||
| dev_err(dev, "ocelot mfd reset failed with code %d\n", ret); | ||
| return ret; | ||
| } | ||
|
|
||
| /* Create and loop over all child devices here */ | ||
|
|
||
| return 0; | ||
| } | ||
| EXPORT_SYMBOL(ocelot_mfd_init); | ||
|
|
||
| int ocelot_mfd_remove(struct ocelot_mfd_config *config) | ||
| { | ||
| /* Loop over all children and remove them */ | ||
|
|
||
| return 0; | ||
| } | ||
| EXPORT_SYMBOL(ocelot_mfd_remove); | ||
|
|
||
| MODULE_DESCRIPTION("Ocelot Chip MFD driver"); | ||
| MODULE_AUTHOR("Colin Foster <colin.foster@in-advantage.com>"); | ||
| MODULE_LICENSE("GPL v2"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0 OR MIT */ | ||
| /* | ||
| * Copyright 2021 Innovative Advantage Inc. | ||
| */ | ||
|
|
||
| #include <linux/regmap.h> | ||
|
|
||
| struct ocelot_mfd_config { | ||
| struct device *dev; | ||
| struct regmap *(*get_regmap)(struct ocelot_mfd_config *config, | ||
| const struct resource *res, | ||
| const char *name); | ||
| int (*init_bus)(struct ocelot_mfd_config *config); | ||
| }; | ||
|
|
||
| void ocelot_mfd_get_resource_name(char *name, const struct resource *res, | ||
| int size); | ||
| int ocelot_mfd_init(struct ocelot_mfd_config *config); | ||
| int ocelot_mfd_remove(struct ocelot_mfd_config *config); |
Oops, something went wrong.