* [PATCH v4 00/21] Move Hisilicon 6421v600 SPMI driver set out of staging @ 2021-01-19 16:10 Mauro Carvalho Chehab 2021-01-19 16:10 ` [PATCH v4 17/21] spmi: hisi-spmi-controller: move driver from staging Mauro Carvalho Chehab 0 siblings, 1 reply; 4+ messages in thread From: Mauro Carvalho Chehab @ 2021-01-19 16:10 UTC (permalink / raw) To: Mark Brown, Lee Jones Cc: Mauro Carvalho Chehab, Colin Ian King, Dan Carpenter, Greg Kroah-Hartman, Liam Girdwood, Mayulong, Rob Herring, Stephen Boyd, Wei Xu, YueHaibing, devel, devicetree, linux-arm-kernel, linux-arm-msm, linux-kernel Hi Mark/Lee, This patch series finish addressing support for Hikey 970 SPMI controller, PMIC and regulators. I removed some unrelated DT patches from this series, plus the Hikey 970 PHY USB3 code from it, in order to avoid mixing different stuff on this series[1]. [1] Those unrelated patches were submitted last week on separate series. The entire patchset is on this branch: https://git.linuxtv.org/mchehab/experimental.git/log/?h=hikey970-destage-usb In order to make easier for review, this series was generated with --no-renames. So, you don't need to take a look at the staging patches, as the entire code will be there on patches 9-11. The last two patches on this series will likely require that other patch series to get merged first. It probably makes sense to be merged via DT tree. Regards, Mauro v4: - use regmap for mfd and spmi drivers; - a few minor cleanups at the mfd driver. v3: - fixed a bug with eco-mode at get_optimum_mode; - changed the sleep logic when enabling/disabling a power line; - additional cleanups, as requested by Mark. v2: - this driver's probe routine is very similar to the one at the non-SPMI variant of Hisilicon 6421; - The register/voltage data were moved from DT into the driver itself; - It doesn't have anymore any static data; - All debug messages got removed; - Addressed a few be32 warnings from sparse. Mauro Carvalho Chehab (21): staging: hikey9xx: hisilicon,hisi-spmi-controller.yaml fix bindings staging: hikey9xx: hisilicon,hi6421-spmi-pmic.yaml: simplify props staging: hikey9xx: hisi-spmi-controller: clean sparse warnings staging: hikey9xx: hi6421v600-regulator: do some cleanups staging: hikey9xx: hi6421v600-regulator: move LDO config from DT staging: hikey9xx: hi6421v600-regulator: cleanup debug msgs staging: hikey9xx: hi6421v600-regulator: get rid of an static data staging: hikey9xx: hi6421v600-regulator: do some cleanups staging: hikey9xx: hi6421v600-regulator: update copyright staging: hikey9xx: hi6421v600-regulator: fix delay logic staging: hikey9xx: hi6421v600-regulator: cleanup comments staging: hikey9xx: hi6421v600-regulator: fix get_optimum_mode staging: hikey9xx: hisilicon,hi6421-spmi-pmic.yaml: cleanup a warning staging: hikey9xx: spmi driver: convert to regmap staging: hikey9xx: hi6421-spmi-pmic: update copyright staging: hikey9xx: simplify includes spmi: hisi-spmi-controller: move driver from staging mfd: hi6421-spmi-pmic: move driver from staging regulator: hi6421v600-regulator: move it from staging dts: hisilicon: add support for USB3 on Hikey 970 dts: hisilicon: add support for the PMIC found on Hikey 970 .../mfd/hisilicon,hi6421-spmi-pmic.yaml | 135 +++++ .../spmi/hisilicon,hisi-spmi-controller.yaml | 75 +++ MAINTAINERS | 15 +- .../boot/dts/hisilicon/hi3670-hikey970.dts | 124 ++++- arch/arm64/boot/dts/hisilicon/hi3670.dtsi | 58 +++ .../boot/dts/hisilicon/hikey970-pmic.dtsi | 87 ++++ drivers/mfd/Kconfig | 15 + drivers/mfd/Makefile | 1 + drivers/mfd/hi6421-spmi-pmic.c | 281 ++++++++++ drivers/regulator/Kconfig | 8 + drivers/regulator/Makefile | 1 + drivers/regulator/hi6421v600-regulator.c | 338 +++++++++++++ drivers/spmi/Kconfig | 9 + drivers/spmi/Makefile | 1 + drivers/spmi/hisi-spmi-controller.c | 358 +++++++++++++ drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/hikey9xx/Kconfig | 38 -- drivers/staging/hikey9xx/Makefile | 5 - drivers/staging/hikey9xx/TODO | 5 - drivers/staging/hikey9xx/hi6421-spmi-pmic.c | 342 ------------- .../staging/hikey9xx/hi6421v600-regulator.c | 478 ------------------ .../staging/hikey9xx/hisi-spmi-controller.c | 358 ------------- .../hikey9xx/hisilicon,hi6421-spmi-pmic.yaml | 159 ------ .../hisilicon,hisi-spmi-controller.yaml | 62 --- include/linux/mfd/hi6421-spmi-pmic.h | 8 +- 26 files changed, 1486 insertions(+), 1478 deletions(-) create mode 100644 Documentation/devicetree/bindings/mfd/hisilicon,hi6421-spmi-pmic.yaml create mode 100644 Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml create mode 100644 arch/arm64/boot/dts/hisilicon/hikey970-pmic.dtsi create mode 100644 drivers/mfd/hi6421-spmi-pmic.c create mode 100644 drivers/regulator/hi6421v600-regulator.c create mode 100644 drivers/spmi/hisi-spmi-controller.c delete mode 100644 drivers/staging/hikey9xx/Kconfig delete mode 100644 drivers/staging/hikey9xx/Makefile delete mode 100644 drivers/staging/hikey9xx/TODO delete mode 100644 drivers/staging/hikey9xx/hi6421-spmi-pmic.c delete mode 100644 drivers/staging/hikey9xx/hi6421v600-regulator.c delete mode 100644 drivers/staging/hikey9xx/hisi-spmi-controller.c delete mode 100644 drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml delete mode 100644 drivers/staging/hikey9xx/hisilicon,hisi-spmi-controller.yaml -- 2.29.2 ^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v4 17/21] spmi: hisi-spmi-controller: move driver from staging 2021-01-19 16:10 [PATCH v4 00/21] Move Hisilicon 6421v600 SPMI driver set out of staging Mauro Carvalho Chehab @ 2021-01-19 16:10 ` Mauro Carvalho Chehab 2021-02-05 22:19 ` Rob Herring 0 siblings, 1 reply; 4+ messages in thread From: Mauro Carvalho Chehab @ 2021-01-19 16:10 UTC (permalink / raw) To: Mark Brown, Lee Jones Cc: Mauro Carvalho Chehab, Colin Ian King, Dan Carpenter, Greg Kroah-Hartman, Mayulong, Rob Herring, Stephen Boyd, YueHaibing, devel, devicetree, linux-arm-msm, linux-kernel The Hisilicon 6421v600 SPMI driver is ready for mainstream. So, move it from staging. Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> --- .../spmi/hisilicon,hisi-spmi-controller.yaml | 75 ++++ MAINTAINERS | 7 + drivers/spmi/Kconfig | 9 + drivers/spmi/Makefile | 1 + drivers/spmi/hisi-spmi-controller.c | 358 ++++++++++++++++++ drivers/staging/hikey9xx/Kconfig | 11 - drivers/staging/hikey9xx/Makefile | 1 - .../staging/hikey9xx/hisi-spmi-controller.c | 358 ------------------ .../hisilicon,hisi-spmi-controller.yaml | 75 ---- 9 files changed, 450 insertions(+), 445 deletions(-) create mode 100644 Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml create mode 100644 drivers/spmi/hisi-spmi-controller.c delete mode 100644 drivers/staging/hikey9xx/hisi-spmi-controller.c delete mode 100644 drivers/staging/hikey9xx/hisilicon,hisi-spmi-controller.yaml diff --git a/Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml b/Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml new file mode 100644 index 000000000000..21f68a9c2df1 --- /dev/null +++ b/Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spmi/hisilicon,hisi-spmi-controller.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: HiSilicon SPMI controller + +maintainers: + - Mauro Carvalho Chehab <mchehab+huawei@kernel.org> + +description: | + The HiSilicon SPMI BUS controller is found on some Kirin-based designs. + It is a MIPI System Power Management (SPMI) controller. + + The PMIC part is provided by + drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml. + +properties: + $nodename: + pattern: "spmi@[0-9a-f]" + + compatible: + const: hisilicon,kirin970-spmi-controller + + reg: + maxItems: 1 + + "#address-cells": + const: 2 + + "#size-cells": + const: 0 + + spmi-channel: + description: | + number of the Kirin 970 SPMI channel where the SPMI devices are connected. + +required: + - compatible + - reg + - spmi-channel + - "#address-cells" + - "#size-cells" + +patternProperties: + "^pmic@[0-9a-f]$": + description: | + PMIC properties, which are specific to the used SPMI PMIC device(s). + When used in combination with HiSilicon 6421v600, the properties + are documented at + drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml. + +additionalProperties: false + +examples: + - | + bus { + #address-cells = <2>; + #size-cells = <2>; + + spmi: spmi@fff24000 { + compatible = "hisilicon,kirin970-spmi-controller"; + #address-cells = <2>; + #size-cells = <0>; + status = "ok"; + reg = <0x0 0xfff24000 0x0 0x1000>; + spmi-channel = <2>; + + pmic@0 { + reg = <0 0>; + /* pmic properties */ + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 8d858e8d5a52..85e5b6ab57ca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7999,6 +7999,13 @@ F: drivers/crypto/hisilicon/sec2/sec_crypto.c F: drivers/crypto/hisilicon/sec2/sec_crypto.h F: drivers/crypto/hisilicon/sec2/sec_main.c +HISILICON SPMI CONTROLLER DRIVER FOR HIKEY 970 +M: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> +L: linux-kernel@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml +F: drivers/spmi/hisi-spmi-controller.c + HISILICON STAGING DRIVERS FOR HIKEY 960/970 M: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> L: devel@driverdev.osuosl.org diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig index a53bad541f1a..2874b6c26028 100644 --- a/drivers/spmi/Kconfig +++ b/drivers/spmi/Kconfig @@ -11,6 +11,15 @@ menuconfig SPMI if SPMI +config SPMI_HISI3670 + tristate "Hisilicon 3670 SPMI Controller" + select IRQ_DOMAIN_HIERARCHY + depends on HAS_IOMEM + help + If you say yes to this option, support will be included for the + built-in SPMI PMIC Arbiter interface on Hisilicon 3670 + processors. + config SPMI_MSM_PMIC_ARB tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)" select IRQ_DOMAIN_HIERARCHY diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile index 55a94cadeffe..6e092e6f290c 100644 --- a/drivers/spmi/Makefile +++ b/drivers/spmi/Makefile @@ -4,4 +4,5 @@ # obj-$(CONFIG_SPMI) += spmi.o +obj-$(CONFIG_SPMI_HISI3670) += hisi-spmi-controller.o obj-$(CONFIG_SPMI_MSM_PMIC_ARB) += spmi-pmic-arb.o diff --git a/drivers/spmi/hisi-spmi-controller.c b/drivers/spmi/hisi-spmi-controller.c new file mode 100644 index 000000000000..4be2344ad7b5 --- /dev/null +++ b/drivers/spmi/hisi-spmi-controller.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/spmi.h> + +/* + * SPMI register addr + */ +#define SPMI_CHANNEL_OFFSET 0x0300 +#define SPMI_SLAVE_OFFSET 0x20 + +#define SPMI_APB_SPMI_CMD_BASE_ADDR 0x0100 + +#define SPMI_APB_SPMI_WDATA0_BASE_ADDR 0x0104 +#define SPMI_APB_SPMI_WDATA1_BASE_ADDR 0x0108 +#define SPMI_APB_SPMI_WDATA2_BASE_ADDR 0x010c +#define SPMI_APB_SPMI_WDATA3_BASE_ADDR 0x0110 + +#define SPMI_APB_SPMI_STATUS_BASE_ADDR 0x0200 + +#define SPMI_APB_SPMI_RDATA0_BASE_ADDR 0x0204 +#define SPMI_APB_SPMI_RDATA1_BASE_ADDR 0x0208 +#define SPMI_APB_SPMI_RDATA2_BASE_ADDR 0x020c +#define SPMI_APB_SPMI_RDATA3_BASE_ADDR 0x0210 + +#define SPMI_PER_DATAREG_BYTE 4 +/* + * SPMI cmd register + */ +#define SPMI_APB_SPMI_CMD_EN BIT(31) +#define SPMI_APB_SPMI_CMD_TYPE_OFFSET 24 +#define SPMI_APB_SPMI_CMD_LENGTH_OFFSET 20 +#define SPMI_APB_SPMI_CMD_SLAVEID_OFFSET 16 +#define SPMI_APB_SPMI_CMD_ADDR_OFFSET 0 + +/* Command Opcodes */ + +enum spmi_controller_cmd_op_code { + SPMI_CMD_REG_ZERO_WRITE = 0, + SPMI_CMD_REG_WRITE = 1, + SPMI_CMD_REG_READ = 2, + SPMI_CMD_EXT_REG_WRITE = 3, + SPMI_CMD_EXT_REG_READ = 4, + SPMI_CMD_EXT_REG_WRITE_L = 5, + SPMI_CMD_EXT_REG_READ_L = 6, + SPMI_CMD_REG_RESET = 7, + SPMI_CMD_REG_SLEEP = 8, + SPMI_CMD_REG_SHUTDOWN = 9, + SPMI_CMD_REG_WAKEUP = 10, +}; + +/* + * SPMI status register + */ +#define SPMI_APB_TRANS_DONE BIT(0) +#define SPMI_APB_TRANS_FAIL BIT(2) + +/* Command register fields */ +#define SPMI_CONTROLLER_CMD_MAX_BYTE_COUNT 16 + +/* Maximum number of support PMIC peripherals */ +#define SPMI_CONTROLLER_TIMEOUT_US 1000 +#define SPMI_CONTROLLER_MAX_TRANS_BYTES 16 + +struct spmi_controller_dev { + struct spmi_controller *controller; + struct device *dev; + void __iomem *base; + spinlock_t lock; + u32 channel; +}; + +static int spmi_controller_wait_for_done(struct device *dev, + struct spmi_controller_dev *ctrl_dev, + void __iomem *base, u8 sid, u16 addr) +{ + u32 timeout = SPMI_CONTROLLER_TIMEOUT_US; + u32 status, offset; + + offset = SPMI_APB_SPMI_STATUS_BASE_ADDR; + offset += SPMI_CHANNEL_OFFSET * ctrl_dev->channel + SPMI_SLAVE_OFFSET * sid; + + do { + status = readl(base + offset); + + if (status & SPMI_APB_TRANS_DONE) { + if (status & SPMI_APB_TRANS_FAIL) { + dev_err(dev, "%s: transaction failed (0x%x)\n", + __func__, status); + return -EIO; + } + dev_dbg(dev, "%s: status 0x%x\n", __func__, status); + return 0; + } + udelay(1); + } while (timeout--); + + dev_err(dev, "%s: timeout, status 0x%x\n", __func__, status); + return -ETIMEDOUT; +} + +static int spmi_read_cmd(struct spmi_controller *ctrl, + u8 opc, u8 slave_id, u16 slave_addr, u8 *__buf, size_t bc) +{ + struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev); + u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel; + unsigned long flags; + u8 *buf = __buf; + u32 cmd, data; + int rc; + u8 op_code, i; + + if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) { + dev_err(&ctrl->dev, + "spmi_controller supports 1..%d bytes per trans, but:%zu requested\n", + SPMI_CONTROLLER_MAX_TRANS_BYTES, bc); + return -EINVAL; + } + + switch (opc) { + case SPMI_CMD_READ: + op_code = SPMI_CMD_REG_READ; + break; + case SPMI_CMD_EXT_READ: + op_code = SPMI_CMD_EXT_REG_READ; + break; + case SPMI_CMD_EXT_READL: + op_code = SPMI_CMD_EXT_REG_READ_L; + break; + default: + dev_err(&ctrl->dev, "invalid read cmd 0x%x\n", opc); + return -EINVAL; + } + + cmd = SPMI_APB_SPMI_CMD_EN | + (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) | + ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) | + ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) | /* slvid */ + ((slave_addr & 0xffff) << SPMI_APB_SPMI_CMD_ADDR_OFFSET); /* slave_addr */ + + spin_lock_irqsave(&spmi_controller->lock, flags); + + writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR); + + rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller, + spmi_controller->base, slave_id, slave_addr); + if (rc) + goto done; + + for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) { + data = readl(spmi_controller->base + chnl_ofst + + SPMI_SLAVE_OFFSET * slave_id + + SPMI_APB_SPMI_RDATA0_BASE_ADDR + + i * SPMI_PER_DATAREG_BYTE); + data = be32_to_cpu((__force __be32)data); + if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) { + memcpy(buf, &data, sizeof(data)); + buf += sizeof(data); + } else { + memcpy(buf, &data, bc % SPMI_PER_DATAREG_BYTE); + buf += (bc % SPMI_PER_DATAREG_BYTE); + } + } + +done: + spin_unlock_irqrestore(&spmi_controller->lock, flags); + if (rc) + dev_err(&ctrl->dev, + "spmi read wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n", + opc, slave_id, slave_addr, bc + 1); + else + dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, read value: %*ph\n", + __func__, slave_id, slave_addr, (int)bc, __buf); + + return rc; +} + +static int spmi_write_cmd(struct spmi_controller *ctrl, + u8 opc, u8 slave_id, u16 slave_addr, const u8 *__buf, size_t bc) +{ + struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev); + u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel; + const u8 *buf = __buf; + unsigned long flags; + u32 cmd, data; + int rc; + u8 op_code, i; + + if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) { + dev_err(&ctrl->dev, + "spmi_controller supports 1..%d bytes per trans, but:%zu requested\n", + SPMI_CONTROLLER_MAX_TRANS_BYTES, bc); + return -EINVAL; + } + + switch (opc) { + case SPMI_CMD_WRITE: + op_code = SPMI_CMD_REG_WRITE; + break; + case SPMI_CMD_EXT_WRITE: + op_code = SPMI_CMD_EXT_REG_WRITE; + break; + case SPMI_CMD_EXT_WRITEL: + op_code = SPMI_CMD_EXT_REG_WRITE_L; + break; + default: + dev_err(&ctrl->dev, "invalid write cmd 0x%x\n", opc); + return -EINVAL; + } + + cmd = SPMI_APB_SPMI_CMD_EN | + (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) | + ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) | + ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) | + ((slave_addr & 0xffff) << SPMI_APB_SPMI_CMD_ADDR_OFFSET); + + /* Write data to FIFOs */ + spin_lock_irqsave(&spmi_controller->lock, flags); + + for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) { + data = 0; + if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) { + memcpy(&data, buf, sizeof(data)); + buf += sizeof(data); + } else { + memcpy(&data, buf, bc % SPMI_PER_DATAREG_BYTE); + buf += (bc % SPMI_PER_DATAREG_BYTE); + } + + writel((__force u32)cpu_to_be32(data), + spmi_controller->base + chnl_ofst + + SPMI_APB_SPMI_WDATA0_BASE_ADDR + + SPMI_PER_DATAREG_BYTE * i); + } + + /* Start the transaction */ + writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR); + + rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller, + spmi_controller->base, slave_id, + slave_addr); + spin_unlock_irqrestore(&spmi_controller->lock, flags); + + if (rc) + dev_err(&ctrl->dev, "spmi write wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n", + opc, slave_id, slave_addr, bc); + else + dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, wrote value: %*ph\n", + __func__, slave_id, slave_addr, (int)bc, __buf); + + return rc; +} + +static int spmi_controller_probe(struct platform_device *pdev) +{ + struct spmi_controller_dev *spmi_controller; + struct spmi_controller *ctrl; + struct resource *iores; + int ret; + + ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*spmi_controller)); + if (!ctrl) { + dev_err(&pdev->dev, "can not allocate spmi_controller data\n"); + return -ENOMEM; + } + spmi_controller = spmi_controller_get_drvdata(ctrl); + spmi_controller->controller = ctrl; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iores) { + dev_err(&pdev->dev, "can not get resource!\n"); + return -EINVAL; + } + + spmi_controller->base = devm_ioremap(&pdev->dev, iores->start, + resource_size(iores)); + if (!spmi_controller->base) { + dev_err(&pdev->dev, "can not remap base addr!\n"); + return -EADDRNOTAVAIL; + } + + ret = of_property_read_u32(pdev->dev.of_node, "spmi-channel", + &spmi_controller->channel); + if (ret) { + dev_err(&pdev->dev, "can not get channel\n"); + return -ENODEV; + } + + platform_set_drvdata(pdev, spmi_controller); + dev_set_drvdata(&ctrl->dev, spmi_controller); + + spin_lock_init(&spmi_controller->lock); + + ctrl->nr = spmi_controller->channel; + ctrl->dev.parent = pdev->dev.parent; + ctrl->dev.of_node = of_node_get(pdev->dev.of_node); + + /* Callbacks */ + ctrl->read_cmd = spmi_read_cmd; + ctrl->write_cmd = spmi_write_cmd; + + ret = spmi_controller_add(ctrl); + if (ret) + dev_err(&pdev->dev, "spmi_add_controller failed with error %d!\n", ret); + + return ret; +} + +static int spmi_del_controller(struct platform_device *pdev) +{ + struct spmi_controller *ctrl = platform_get_drvdata(pdev); + + spmi_controller_remove(ctrl); + kfree(ctrl); + return 0; +} + +static const struct of_device_id spmi_controller_match_table[] = { + { + .compatible = "hisilicon,kirin970-spmi-controller", + }, + {} +}; +MODULE_DEVICE_TABLE(of, spmi_controller_match_table); + +static struct platform_driver spmi_controller_driver = { + .probe = spmi_controller_probe, + .remove = spmi_del_controller, + .driver = { + .name = "hisi_spmi_controller", + .of_match_table = spmi_controller_match_table, + }, +}; + +static int __init spmi_controller_init(void) +{ + return platform_driver_register(&spmi_controller_driver); +} +postcore_initcall(spmi_controller_init); + +static void __exit spmi_controller_exit(void) +{ + platform_driver_unregister(&spmi_controller_driver); +} +module_exit(spmi_controller_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("platform:spmi_controller"); diff --git a/drivers/staging/hikey9xx/Kconfig b/drivers/staging/hikey9xx/Kconfig index 0e97b5b9a56a..69392e42cd0d 100644 --- a/drivers/staging/hikey9xx/Kconfig +++ b/drivers/staging/hikey9xx/Kconfig @@ -1,16 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -# to be placed at drivers/spmi -config SPMI_HISI3670 - tristate "Hisilicon 3670 SPMI Controller" - select IRQ_DOMAIN_HIERARCHY - depends on HAS_IOMEM - depends on SPMI - help - If you say yes to this option, support will be included for the - built-in SPMI PMIC Arbiter interface on Hisilicon 3670 - processors. - # to be placed at drivers/mfd config MFD_HI6421_SPMI tristate "HiSilicon Hi6421v600 SPMI PMU/Codec IC" diff --git a/drivers/staging/hikey9xx/Makefile b/drivers/staging/hikey9xx/Makefile index 9371dcc3d35b..347880fd378f 100644 --- a/drivers/staging/hikey9xx/Makefile +++ b/drivers/staging/hikey9xx/Makefile @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_SPMI_HISI3670) += hisi-spmi-controller.o obj-$(CONFIG_MFD_HI6421_SPMI) += hi6421-spmi-pmic.o obj-$(CONFIG_REGULATOR_HI6421V600) += hi6421v600-regulator.o diff --git a/drivers/staging/hikey9xx/hisi-spmi-controller.c b/drivers/staging/hikey9xx/hisi-spmi-controller.c deleted file mode 100644 index 4be2344ad7b5..000000000000 --- a/drivers/staging/hikey9xx/hisi-spmi-controller.c +++ /dev/null @@ -1,358 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/platform_device.h> -#include <linux/seq_file.h> -#include <linux/slab.h> -#include <linux/spmi.h> - -/* - * SPMI register addr - */ -#define SPMI_CHANNEL_OFFSET 0x0300 -#define SPMI_SLAVE_OFFSET 0x20 - -#define SPMI_APB_SPMI_CMD_BASE_ADDR 0x0100 - -#define SPMI_APB_SPMI_WDATA0_BASE_ADDR 0x0104 -#define SPMI_APB_SPMI_WDATA1_BASE_ADDR 0x0108 -#define SPMI_APB_SPMI_WDATA2_BASE_ADDR 0x010c -#define SPMI_APB_SPMI_WDATA3_BASE_ADDR 0x0110 - -#define SPMI_APB_SPMI_STATUS_BASE_ADDR 0x0200 - -#define SPMI_APB_SPMI_RDATA0_BASE_ADDR 0x0204 -#define SPMI_APB_SPMI_RDATA1_BASE_ADDR 0x0208 -#define SPMI_APB_SPMI_RDATA2_BASE_ADDR 0x020c -#define SPMI_APB_SPMI_RDATA3_BASE_ADDR 0x0210 - -#define SPMI_PER_DATAREG_BYTE 4 -/* - * SPMI cmd register - */ -#define SPMI_APB_SPMI_CMD_EN BIT(31) -#define SPMI_APB_SPMI_CMD_TYPE_OFFSET 24 -#define SPMI_APB_SPMI_CMD_LENGTH_OFFSET 20 -#define SPMI_APB_SPMI_CMD_SLAVEID_OFFSET 16 -#define SPMI_APB_SPMI_CMD_ADDR_OFFSET 0 - -/* Command Opcodes */ - -enum spmi_controller_cmd_op_code { - SPMI_CMD_REG_ZERO_WRITE = 0, - SPMI_CMD_REG_WRITE = 1, - SPMI_CMD_REG_READ = 2, - SPMI_CMD_EXT_REG_WRITE = 3, - SPMI_CMD_EXT_REG_READ = 4, - SPMI_CMD_EXT_REG_WRITE_L = 5, - SPMI_CMD_EXT_REG_READ_L = 6, - SPMI_CMD_REG_RESET = 7, - SPMI_CMD_REG_SLEEP = 8, - SPMI_CMD_REG_SHUTDOWN = 9, - SPMI_CMD_REG_WAKEUP = 10, -}; - -/* - * SPMI status register - */ -#define SPMI_APB_TRANS_DONE BIT(0) -#define SPMI_APB_TRANS_FAIL BIT(2) - -/* Command register fields */ -#define SPMI_CONTROLLER_CMD_MAX_BYTE_COUNT 16 - -/* Maximum number of support PMIC peripherals */ -#define SPMI_CONTROLLER_TIMEOUT_US 1000 -#define SPMI_CONTROLLER_MAX_TRANS_BYTES 16 - -struct spmi_controller_dev { - struct spmi_controller *controller; - struct device *dev; - void __iomem *base; - spinlock_t lock; - u32 channel; -}; - -static int spmi_controller_wait_for_done(struct device *dev, - struct spmi_controller_dev *ctrl_dev, - void __iomem *base, u8 sid, u16 addr) -{ - u32 timeout = SPMI_CONTROLLER_TIMEOUT_US; - u32 status, offset; - - offset = SPMI_APB_SPMI_STATUS_BASE_ADDR; - offset += SPMI_CHANNEL_OFFSET * ctrl_dev->channel + SPMI_SLAVE_OFFSET * sid; - - do { - status = readl(base + offset); - - if (status & SPMI_APB_TRANS_DONE) { - if (status & SPMI_APB_TRANS_FAIL) { - dev_err(dev, "%s: transaction failed (0x%x)\n", - __func__, status); - return -EIO; - } - dev_dbg(dev, "%s: status 0x%x\n", __func__, status); - return 0; - } - udelay(1); - } while (timeout--); - - dev_err(dev, "%s: timeout, status 0x%x\n", __func__, status); - return -ETIMEDOUT; -} - -static int spmi_read_cmd(struct spmi_controller *ctrl, - u8 opc, u8 slave_id, u16 slave_addr, u8 *__buf, size_t bc) -{ - struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev); - u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel; - unsigned long flags; - u8 *buf = __buf; - u32 cmd, data; - int rc; - u8 op_code, i; - - if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) { - dev_err(&ctrl->dev, - "spmi_controller supports 1..%d bytes per trans, but:%zu requested\n", - SPMI_CONTROLLER_MAX_TRANS_BYTES, bc); - return -EINVAL; - } - - switch (opc) { - case SPMI_CMD_READ: - op_code = SPMI_CMD_REG_READ; - break; - case SPMI_CMD_EXT_READ: - op_code = SPMI_CMD_EXT_REG_READ; - break; - case SPMI_CMD_EXT_READL: - op_code = SPMI_CMD_EXT_REG_READ_L; - break; - default: - dev_err(&ctrl->dev, "invalid read cmd 0x%x\n", opc); - return -EINVAL; - } - - cmd = SPMI_APB_SPMI_CMD_EN | - (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) | - ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) | - ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) | /* slvid */ - ((slave_addr & 0xffff) << SPMI_APB_SPMI_CMD_ADDR_OFFSET); /* slave_addr */ - - spin_lock_irqsave(&spmi_controller->lock, flags); - - writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR); - - rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller, - spmi_controller->base, slave_id, slave_addr); - if (rc) - goto done; - - for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) { - data = readl(spmi_controller->base + chnl_ofst + - SPMI_SLAVE_OFFSET * slave_id + - SPMI_APB_SPMI_RDATA0_BASE_ADDR + - i * SPMI_PER_DATAREG_BYTE); - data = be32_to_cpu((__force __be32)data); - if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) { - memcpy(buf, &data, sizeof(data)); - buf += sizeof(data); - } else { - memcpy(buf, &data, bc % SPMI_PER_DATAREG_BYTE); - buf += (bc % SPMI_PER_DATAREG_BYTE); - } - } - -done: - spin_unlock_irqrestore(&spmi_controller->lock, flags); - if (rc) - dev_err(&ctrl->dev, - "spmi read wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n", - opc, slave_id, slave_addr, bc + 1); - else - dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, read value: %*ph\n", - __func__, slave_id, slave_addr, (int)bc, __buf); - - return rc; -} - -static int spmi_write_cmd(struct spmi_controller *ctrl, - u8 opc, u8 slave_id, u16 slave_addr, const u8 *__buf, size_t bc) -{ - struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev); - u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel; - const u8 *buf = __buf; - unsigned long flags; - u32 cmd, data; - int rc; - u8 op_code, i; - - if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) { - dev_err(&ctrl->dev, - "spmi_controller supports 1..%d bytes per trans, but:%zu requested\n", - SPMI_CONTROLLER_MAX_TRANS_BYTES, bc); - return -EINVAL; - } - - switch (opc) { - case SPMI_CMD_WRITE: - op_code = SPMI_CMD_REG_WRITE; - break; - case SPMI_CMD_EXT_WRITE: - op_code = SPMI_CMD_EXT_REG_WRITE; - break; - case SPMI_CMD_EXT_WRITEL: - op_code = SPMI_CMD_EXT_REG_WRITE_L; - break; - default: - dev_err(&ctrl->dev, "invalid write cmd 0x%x\n", opc); - return -EINVAL; - } - - cmd = SPMI_APB_SPMI_CMD_EN | - (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) | - ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) | - ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) | - ((slave_addr & 0xffff) << SPMI_APB_SPMI_CMD_ADDR_OFFSET); - - /* Write data to FIFOs */ - spin_lock_irqsave(&spmi_controller->lock, flags); - - for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) { - data = 0; - if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) { - memcpy(&data, buf, sizeof(data)); - buf += sizeof(data); - } else { - memcpy(&data, buf, bc % SPMI_PER_DATAREG_BYTE); - buf += (bc % SPMI_PER_DATAREG_BYTE); - } - - writel((__force u32)cpu_to_be32(data), - spmi_controller->base + chnl_ofst + - SPMI_APB_SPMI_WDATA0_BASE_ADDR + - SPMI_PER_DATAREG_BYTE * i); - } - - /* Start the transaction */ - writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR); - - rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller, - spmi_controller->base, slave_id, - slave_addr); - spin_unlock_irqrestore(&spmi_controller->lock, flags); - - if (rc) - dev_err(&ctrl->dev, "spmi write wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n", - opc, slave_id, slave_addr, bc); - else - dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, wrote value: %*ph\n", - __func__, slave_id, slave_addr, (int)bc, __buf); - - return rc; -} - -static int spmi_controller_probe(struct platform_device *pdev) -{ - struct spmi_controller_dev *spmi_controller; - struct spmi_controller *ctrl; - struct resource *iores; - int ret; - - ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*spmi_controller)); - if (!ctrl) { - dev_err(&pdev->dev, "can not allocate spmi_controller data\n"); - return -ENOMEM; - } - spmi_controller = spmi_controller_get_drvdata(ctrl); - spmi_controller->controller = ctrl; - - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iores) { - dev_err(&pdev->dev, "can not get resource!\n"); - return -EINVAL; - } - - spmi_controller->base = devm_ioremap(&pdev->dev, iores->start, - resource_size(iores)); - if (!spmi_controller->base) { - dev_err(&pdev->dev, "can not remap base addr!\n"); - return -EADDRNOTAVAIL; - } - - ret = of_property_read_u32(pdev->dev.of_node, "spmi-channel", - &spmi_controller->channel); - if (ret) { - dev_err(&pdev->dev, "can not get channel\n"); - return -ENODEV; - } - - platform_set_drvdata(pdev, spmi_controller); - dev_set_drvdata(&ctrl->dev, spmi_controller); - - spin_lock_init(&spmi_controller->lock); - - ctrl->nr = spmi_controller->channel; - ctrl->dev.parent = pdev->dev.parent; - ctrl->dev.of_node = of_node_get(pdev->dev.of_node); - - /* Callbacks */ - ctrl->read_cmd = spmi_read_cmd; - ctrl->write_cmd = spmi_write_cmd; - - ret = spmi_controller_add(ctrl); - if (ret) - dev_err(&pdev->dev, "spmi_add_controller failed with error %d!\n", ret); - - return ret; -} - -static int spmi_del_controller(struct platform_device *pdev) -{ - struct spmi_controller *ctrl = platform_get_drvdata(pdev); - - spmi_controller_remove(ctrl); - kfree(ctrl); - return 0; -} - -static const struct of_device_id spmi_controller_match_table[] = { - { - .compatible = "hisilicon,kirin970-spmi-controller", - }, - {} -}; -MODULE_DEVICE_TABLE(of, spmi_controller_match_table); - -static struct platform_driver spmi_controller_driver = { - .probe = spmi_controller_probe, - .remove = spmi_del_controller, - .driver = { - .name = "hisi_spmi_controller", - .of_match_table = spmi_controller_match_table, - }, -}; - -static int __init spmi_controller_init(void) -{ - return platform_driver_register(&spmi_controller_driver); -} -postcore_initcall(spmi_controller_init); - -static void __exit spmi_controller_exit(void) -{ - platform_driver_unregister(&spmi_controller_driver); -} -module_exit(spmi_controller_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_VERSION("1.0"); -MODULE_ALIAS("platform:spmi_controller"); diff --git a/drivers/staging/hikey9xx/hisilicon,hisi-spmi-controller.yaml b/drivers/staging/hikey9xx/hisilicon,hisi-spmi-controller.yaml deleted file mode 100644 index 21f68a9c2df1..000000000000 --- a/drivers/staging/hikey9xx/hisilicon,hisi-spmi-controller.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/spmi/hisilicon,hisi-spmi-controller.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: HiSilicon SPMI controller - -maintainers: - - Mauro Carvalho Chehab <mchehab+huawei@kernel.org> - -description: | - The HiSilicon SPMI BUS controller is found on some Kirin-based designs. - It is a MIPI System Power Management (SPMI) controller. - - The PMIC part is provided by - drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml. - -properties: - $nodename: - pattern: "spmi@[0-9a-f]" - - compatible: - const: hisilicon,kirin970-spmi-controller - - reg: - maxItems: 1 - - "#address-cells": - const: 2 - - "#size-cells": - const: 0 - - spmi-channel: - description: | - number of the Kirin 970 SPMI channel where the SPMI devices are connected. - -required: - - compatible - - reg - - spmi-channel - - "#address-cells" - - "#size-cells" - -patternProperties: - "^pmic@[0-9a-f]$": - description: | - PMIC properties, which are specific to the used SPMI PMIC device(s). - When used in combination with HiSilicon 6421v600, the properties - are documented at - drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml. - -additionalProperties: false - -examples: - - | - bus { - #address-cells = <2>; - #size-cells = <2>; - - spmi: spmi@fff24000 { - compatible = "hisilicon,kirin970-spmi-controller"; - #address-cells = <2>; - #size-cells = <0>; - status = "ok"; - reg = <0x0 0xfff24000 0x0 0x1000>; - spmi-channel = <2>; - - pmic@0 { - reg = <0 0>; - /* pmic properties */ - }; - }; - }; -- 2.29.2 ^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v4 17/21] spmi: hisi-spmi-controller: move driver from staging 2021-01-19 16:10 ` [PATCH v4 17/21] spmi: hisi-spmi-controller: move driver from staging Mauro Carvalho Chehab @ 2021-02-05 22:19 ` Rob Herring 2021-03-25 13:47 ` Mauro Carvalho Chehab 0 siblings, 1 reply; 4+ messages in thread From: Rob Herring @ 2021-02-05 22:19 UTC (permalink / raw) To: Mauro Carvalho Chehab Cc: Mark Brown, Lee Jones, Colin Ian King, Dan Carpenter, Greg Kroah-Hartman, Mayulong, Stephen Boyd, YueHaibing, devel, devicetree, linux-arm-msm, linux-kernel On Tue, Jan 19, 2021 at 05:10:43PM +0100, Mauro Carvalho Chehab wrote: > The Hisilicon 6421v600 SPMI driver is ready for mainstream. > > So, move it from staging. > > Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> > --- > .../spmi/hisilicon,hisi-spmi-controller.yaml | 75 ++++ > MAINTAINERS | 7 + > drivers/spmi/Kconfig | 9 + > drivers/spmi/Makefile | 1 + > drivers/spmi/hisi-spmi-controller.c | 358 ++++++++++++++++++ > drivers/staging/hikey9xx/Kconfig | 11 - > drivers/staging/hikey9xx/Makefile | 1 - > .../staging/hikey9xx/hisi-spmi-controller.c | 358 ------------------ > .../hisilicon,hisi-spmi-controller.yaml | 75 ---- > 9 files changed, 450 insertions(+), 445 deletions(-) > create mode 100644 Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml > create mode 100644 drivers/spmi/hisi-spmi-controller.c > delete mode 100644 drivers/staging/hikey9xx/hisi-spmi-controller.c > delete mode 100644 drivers/staging/hikey9xx/hisilicon,hisi-spmi-controller.yaml > > diff --git a/Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml b/Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml > new file mode 100644 > index 000000000000..21f68a9c2df1 > --- /dev/null > +++ b/Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml > @@ -0,0 +1,75 @@ > +# SPDX-License-Identifier: GPL-2.0 > +%YAML 1.2 > +--- > +$id: http://devicetree.org/schemas/spmi/hisilicon,hisi-spmi-controller.yaml# > +$schema: http://devicetree.org/meta-schemas/core.yaml# > + > +title: HiSilicon SPMI controller > + > +maintainers: > + - Mauro Carvalho Chehab <mchehab+huawei@kernel.org> > + > +description: | > + The HiSilicon SPMI BUS controller is found on some Kirin-based designs. > + It is a MIPI System Power Management (SPMI) controller. > + > + The PMIC part is provided by > + drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml. > + > +properties: > + $nodename: > + pattern: "spmi@[0-9a-f]" > + > + compatible: > + const: hisilicon,kirin970-spmi-controller '-controller' is kind of redundant. > + > + reg: > + maxItems: 1 > + > + "#address-cells": > + const: 2 > + > + "#size-cells": > + const: 0 These 2 are covered by spmi.yaml > + > + spmi-channel: > + description: | > + number of the Kirin 970 SPMI channel where the SPMI devices are connected. Common to SPMI? If not, needs a vendor prefix. Type? Range of values? > + > +required: > + - compatible > + - reg > + - spmi-channel > + - "#address-cells" > + - "#size-cells" Covered by spmi.yaml. > + > +patternProperties: > + "^pmic@[0-9a-f]$": Presumably you could have something besides a PMIC. > + description: | > + PMIC properties, which are specific to the used SPMI PMIC device(s). > + When used in combination with HiSilicon 6421v600, the properties > + are documented at > + drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml. > + > +additionalProperties: false > + > +examples: > + - | > + bus { > + #address-cells = <2>; > + #size-cells = <2>; > + > + spmi: spmi@fff24000 { > + compatible = "hisilicon,kirin970-spmi-controller"; > + #address-cells = <2>; > + #size-cells = <0>; > + status = "ok"; Drop status. > + reg = <0x0 0xfff24000 0x0 0x1000>; > + spmi-channel = <2>; > + > + pmic@0 { > + reg = <0 0>; > + /* pmic properties */ > + }; > + }; > + }; > diff --git a/MAINTAINERS b/MAINTAINERS > index 8d858e8d5a52..85e5b6ab57ca 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -7999,6 +7999,13 @@ F: drivers/crypto/hisilicon/sec2/sec_crypto.c > F: drivers/crypto/hisilicon/sec2/sec_crypto.h > F: drivers/crypto/hisilicon/sec2/sec_main.c > > +HISILICON SPMI CONTROLLER DRIVER FOR HIKEY 970 > +M: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> > +L: linux-kernel@vger.kernel.org > +S: Maintained > +F: Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml > +F: drivers/spmi/hisi-spmi-controller.c > + > HISILICON STAGING DRIVERS FOR HIKEY 960/970 > M: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> > L: devel@driverdev.osuosl.org > diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig > index a53bad541f1a..2874b6c26028 100644 > --- a/drivers/spmi/Kconfig > +++ b/drivers/spmi/Kconfig > @@ -11,6 +11,15 @@ menuconfig SPMI > > if SPMI > > +config SPMI_HISI3670 > + tristate "Hisilicon 3670 SPMI Controller" > + select IRQ_DOMAIN_HIERARCHY > + depends on HAS_IOMEM > + help > + If you say yes to this option, support will be included for the > + built-in SPMI PMIC Arbiter interface on Hisilicon 3670 > + processors. > + > config SPMI_MSM_PMIC_ARB > tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)" > select IRQ_DOMAIN_HIERARCHY > diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile > index 55a94cadeffe..6e092e6f290c 100644 > --- a/drivers/spmi/Makefile > +++ b/drivers/spmi/Makefile > @@ -4,4 +4,5 @@ > # > obj-$(CONFIG_SPMI) += spmi.o > > +obj-$(CONFIG_SPMI_HISI3670) += hisi-spmi-controller.o > obj-$(CONFIG_SPMI_MSM_PMIC_ARB) += spmi-pmic-arb.o > diff --git a/drivers/spmi/hisi-spmi-controller.c b/drivers/spmi/hisi-spmi-controller.c > new file mode 100644 > index 000000000000..4be2344ad7b5 > --- /dev/null > +++ b/drivers/spmi/hisi-spmi-controller.c > @@ -0,0 +1,358 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +#include <linux/delay.h> > +#include <linux/err.h> > +#include <linux/interrupt.h> > +#include <linux/io.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/platform_device.h> > +#include <linux/seq_file.h> > +#include <linux/slab.h> > +#include <linux/spmi.h> > + > +/* > + * SPMI register addr > + */ > +#define SPMI_CHANNEL_OFFSET 0x0300 > +#define SPMI_SLAVE_OFFSET 0x20 > + > +#define SPMI_APB_SPMI_CMD_BASE_ADDR 0x0100 > + > +#define SPMI_APB_SPMI_WDATA0_BASE_ADDR 0x0104 > +#define SPMI_APB_SPMI_WDATA1_BASE_ADDR 0x0108 > +#define SPMI_APB_SPMI_WDATA2_BASE_ADDR 0x010c > +#define SPMI_APB_SPMI_WDATA3_BASE_ADDR 0x0110 > + > +#define SPMI_APB_SPMI_STATUS_BASE_ADDR 0x0200 > + > +#define SPMI_APB_SPMI_RDATA0_BASE_ADDR 0x0204 > +#define SPMI_APB_SPMI_RDATA1_BASE_ADDR 0x0208 > +#define SPMI_APB_SPMI_RDATA2_BASE_ADDR 0x020c > +#define SPMI_APB_SPMI_RDATA3_BASE_ADDR 0x0210 > + > +#define SPMI_PER_DATAREG_BYTE 4 > +/* > + * SPMI cmd register > + */ > +#define SPMI_APB_SPMI_CMD_EN BIT(31) > +#define SPMI_APB_SPMI_CMD_TYPE_OFFSET 24 > +#define SPMI_APB_SPMI_CMD_LENGTH_OFFSET 20 > +#define SPMI_APB_SPMI_CMD_SLAVEID_OFFSET 16 > +#define SPMI_APB_SPMI_CMD_ADDR_OFFSET 0 > + > +/* Command Opcodes */ > + > +enum spmi_controller_cmd_op_code { > + SPMI_CMD_REG_ZERO_WRITE = 0, > + SPMI_CMD_REG_WRITE = 1, > + SPMI_CMD_REG_READ = 2, > + SPMI_CMD_EXT_REG_WRITE = 3, > + SPMI_CMD_EXT_REG_READ = 4, > + SPMI_CMD_EXT_REG_WRITE_L = 5, > + SPMI_CMD_EXT_REG_READ_L = 6, > + SPMI_CMD_REG_RESET = 7, > + SPMI_CMD_REG_SLEEP = 8, > + SPMI_CMD_REG_SHUTDOWN = 9, > + SPMI_CMD_REG_WAKEUP = 10, > +}; > + > +/* > + * SPMI status register > + */ > +#define SPMI_APB_TRANS_DONE BIT(0) > +#define SPMI_APB_TRANS_FAIL BIT(2) > + > +/* Command register fields */ > +#define SPMI_CONTROLLER_CMD_MAX_BYTE_COUNT 16 > + > +/* Maximum number of support PMIC peripherals */ > +#define SPMI_CONTROLLER_TIMEOUT_US 1000 > +#define SPMI_CONTROLLER_MAX_TRANS_BYTES 16 > + > +struct spmi_controller_dev { > + struct spmi_controller *controller; > + struct device *dev; > + void __iomem *base; > + spinlock_t lock; > + u32 channel; > +}; > + > +static int spmi_controller_wait_for_done(struct device *dev, > + struct spmi_controller_dev *ctrl_dev, > + void __iomem *base, u8 sid, u16 addr) > +{ > + u32 timeout = SPMI_CONTROLLER_TIMEOUT_US; > + u32 status, offset; > + > + offset = SPMI_APB_SPMI_STATUS_BASE_ADDR; > + offset += SPMI_CHANNEL_OFFSET * ctrl_dev->channel + SPMI_SLAVE_OFFSET * sid; > + > + do { > + status = readl(base + offset); > + > + if (status & SPMI_APB_TRANS_DONE) { > + if (status & SPMI_APB_TRANS_FAIL) { > + dev_err(dev, "%s: transaction failed (0x%x)\n", > + __func__, status); > + return -EIO; > + } > + dev_dbg(dev, "%s: status 0x%x\n", __func__, status); > + return 0; > + } > + udelay(1); > + } while (timeout--); > + > + dev_err(dev, "%s: timeout, status 0x%x\n", __func__, status); > + return -ETIMEDOUT; > +} > + > +static int spmi_read_cmd(struct spmi_controller *ctrl, > + u8 opc, u8 slave_id, u16 slave_addr, u8 *__buf, size_t bc) > +{ > + struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev); > + u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel; > + unsigned long flags; > + u8 *buf = __buf; > + u32 cmd, data; > + int rc; > + u8 op_code, i; > + > + if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) { > + dev_err(&ctrl->dev, > + "spmi_controller supports 1..%d bytes per trans, but:%zu requested\n", > + SPMI_CONTROLLER_MAX_TRANS_BYTES, bc); > + return -EINVAL; > + } > + > + switch (opc) { > + case SPMI_CMD_READ: > + op_code = SPMI_CMD_REG_READ; > + break; > + case SPMI_CMD_EXT_READ: > + op_code = SPMI_CMD_EXT_REG_READ; > + break; > + case SPMI_CMD_EXT_READL: > + op_code = SPMI_CMD_EXT_REG_READ_L; > + break; > + default: > + dev_err(&ctrl->dev, "invalid read cmd 0x%x\n", opc); > + return -EINVAL; > + } > + > + cmd = SPMI_APB_SPMI_CMD_EN | > + (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) | > + ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) | > + ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) | /* slvid */ > + ((slave_addr & 0xffff) << SPMI_APB_SPMI_CMD_ADDR_OFFSET); /* slave_addr */ > + > + spin_lock_irqsave(&spmi_controller->lock, flags); > + > + writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR); > + > + rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller, > + spmi_controller->base, slave_id, slave_addr); > + if (rc) > + goto done; > + > + for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) { > + data = readl(spmi_controller->base + chnl_ofst + > + SPMI_SLAVE_OFFSET * slave_id + > + SPMI_APB_SPMI_RDATA0_BASE_ADDR + > + i * SPMI_PER_DATAREG_BYTE); > + data = be32_to_cpu((__force __be32)data); > + if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) { > + memcpy(buf, &data, sizeof(data)); > + buf += sizeof(data); > + } else { > + memcpy(buf, &data, bc % SPMI_PER_DATAREG_BYTE); > + buf += (bc % SPMI_PER_DATAREG_BYTE); > + } > + } > + > +done: > + spin_unlock_irqrestore(&spmi_controller->lock, flags); > + if (rc) > + dev_err(&ctrl->dev, > + "spmi read wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n", > + opc, slave_id, slave_addr, bc + 1); > + else > + dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, read value: %*ph\n", > + __func__, slave_id, slave_addr, (int)bc, __buf); > + > + return rc; > +} > + > +static int spmi_write_cmd(struct spmi_controller *ctrl, > + u8 opc, u8 slave_id, u16 slave_addr, const u8 *__buf, size_t bc) > +{ > + struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev); > + u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel; > + const u8 *buf = __buf; > + unsigned long flags; > + u32 cmd, data; > + int rc; > + u8 op_code, i; > + > + if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) { > + dev_err(&ctrl->dev, > + "spmi_controller supports 1..%d bytes per trans, but:%zu requested\n", > + SPMI_CONTROLLER_MAX_TRANS_BYTES, bc); > + return -EINVAL; > + } > + > + switch (opc) { > + case SPMI_CMD_WRITE: > + op_code = SPMI_CMD_REG_WRITE; > + break; > + case SPMI_CMD_EXT_WRITE: > + op_code = SPMI_CMD_EXT_REG_WRITE; > + break; > + case SPMI_CMD_EXT_WRITEL: > + op_code = SPMI_CMD_EXT_REG_WRITE_L; > + break; > + default: > + dev_err(&ctrl->dev, "invalid write cmd 0x%x\n", opc); > + return -EINVAL; > + } > + > + cmd = SPMI_APB_SPMI_CMD_EN | > + (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) | > + ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) | > + ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) | > + ((slave_addr & 0xffff) << SPMI_APB_SPMI_CMD_ADDR_OFFSET); > + > + /* Write data to FIFOs */ > + spin_lock_irqsave(&spmi_controller->lock, flags); > + > + for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) { > + data = 0; > + if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) { > + memcpy(&data, buf, sizeof(data)); > + buf += sizeof(data); > + } else { > + memcpy(&data, buf, bc % SPMI_PER_DATAREG_BYTE); > + buf += (bc % SPMI_PER_DATAREG_BYTE); > + } > + > + writel((__force u32)cpu_to_be32(data), > + spmi_controller->base + chnl_ofst + > + SPMI_APB_SPMI_WDATA0_BASE_ADDR + > + SPMI_PER_DATAREG_BYTE * i); > + } > + > + /* Start the transaction */ > + writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR); > + > + rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller, > + spmi_controller->base, slave_id, > + slave_addr); > + spin_unlock_irqrestore(&spmi_controller->lock, flags); > + > + if (rc) > + dev_err(&ctrl->dev, "spmi write wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n", > + opc, slave_id, slave_addr, bc); > + else > + dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, wrote value: %*ph\n", > + __func__, slave_id, slave_addr, (int)bc, __buf); > + > + return rc; > +} > + > +static int spmi_controller_probe(struct platform_device *pdev) > +{ > + struct spmi_controller_dev *spmi_controller; > + struct spmi_controller *ctrl; > + struct resource *iores; > + int ret; > + > + ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*spmi_controller)); > + if (!ctrl) { > + dev_err(&pdev->dev, "can not allocate spmi_controller data\n"); > + return -ENOMEM; > + } > + spmi_controller = spmi_controller_get_drvdata(ctrl); > + spmi_controller->controller = ctrl; > + > + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!iores) { > + dev_err(&pdev->dev, "can not get resource!\n"); > + return -EINVAL; > + } > + > + spmi_controller->base = devm_ioremap(&pdev->dev, iores->start, > + resource_size(iores)); > + if (!spmi_controller->base) { > + dev_err(&pdev->dev, "can not remap base addr!\n"); > + return -EADDRNOTAVAIL; > + } > + > + ret = of_property_read_u32(pdev->dev.of_node, "spmi-channel", > + &spmi_controller->channel); > + if (ret) { > + dev_err(&pdev->dev, "can not get channel\n"); > + return -ENODEV; > + } > + > + platform_set_drvdata(pdev, spmi_controller); > + dev_set_drvdata(&ctrl->dev, spmi_controller); > + > + spin_lock_init(&spmi_controller->lock); > + > + ctrl->nr = spmi_controller->channel; > + ctrl->dev.parent = pdev->dev.parent; > + ctrl->dev.of_node = of_node_get(pdev->dev.of_node); > + > + /* Callbacks */ > + ctrl->read_cmd = spmi_read_cmd; > + ctrl->write_cmd = spmi_write_cmd; > + > + ret = spmi_controller_add(ctrl); > + if (ret) > + dev_err(&pdev->dev, "spmi_add_controller failed with error %d!\n", ret); > + > + return ret; > +} > + > +static int spmi_del_controller(struct platform_device *pdev) > +{ > + struct spmi_controller *ctrl = platform_get_drvdata(pdev); > + > + spmi_controller_remove(ctrl); > + kfree(ctrl); > + return 0; > +} > + > +static const struct of_device_id spmi_controller_match_table[] = { > + { > + .compatible = "hisilicon,kirin970-spmi-controller", > + }, > + {} > +}; > +MODULE_DEVICE_TABLE(of, spmi_controller_match_table); > + > +static struct platform_driver spmi_controller_driver = { > + .probe = spmi_controller_probe, > + .remove = spmi_del_controller, > + .driver = { > + .name = "hisi_spmi_controller", > + .of_match_table = spmi_controller_match_table, > + }, > +}; > + > +static int __init spmi_controller_init(void) > +{ > + return platform_driver_register(&spmi_controller_driver); > +} > +postcore_initcall(spmi_controller_init); > + > +static void __exit spmi_controller_exit(void) > +{ > + platform_driver_unregister(&spmi_controller_driver); > +} > +module_exit(spmi_controller_exit); > + > +MODULE_LICENSE("GPL v2"); > +MODULE_VERSION("1.0"); > +MODULE_ALIAS("platform:spmi_controller"); > diff --git a/drivers/staging/hikey9xx/Kconfig b/drivers/staging/hikey9xx/Kconfig > index 0e97b5b9a56a..69392e42cd0d 100644 > --- a/drivers/staging/hikey9xx/Kconfig > +++ b/drivers/staging/hikey9xx/Kconfig > @@ -1,16 +1,5 @@ > # SPDX-License-Identifier: GPL-2.0 > > -# to be placed at drivers/spmi > -config SPMI_HISI3670 > - tristate "Hisilicon 3670 SPMI Controller" > - select IRQ_DOMAIN_HIERARCHY > - depends on HAS_IOMEM > - depends on SPMI > - help > - If you say yes to this option, support will be included for the > - built-in SPMI PMIC Arbiter interface on Hisilicon 3670 > - processors. > - > # to be placed at drivers/mfd > config MFD_HI6421_SPMI > tristate "HiSilicon Hi6421v600 SPMI PMU/Codec IC" > diff --git a/drivers/staging/hikey9xx/Makefile b/drivers/staging/hikey9xx/Makefile > index 9371dcc3d35b..347880fd378f 100644 > --- a/drivers/staging/hikey9xx/Makefile > +++ b/drivers/staging/hikey9xx/Makefile > @@ -1,5 +1,4 @@ > # SPDX-License-Identifier: GPL-2.0 > > -obj-$(CONFIG_SPMI_HISI3670) += hisi-spmi-controller.o > obj-$(CONFIG_MFD_HI6421_SPMI) += hi6421-spmi-pmic.o > obj-$(CONFIG_REGULATOR_HI6421V600) += hi6421v600-regulator.o > diff --git a/drivers/staging/hikey9xx/hisi-spmi-controller.c b/drivers/staging/hikey9xx/hisi-spmi-controller.c > deleted file mode 100644 > index 4be2344ad7b5..000000000000 > --- a/drivers/staging/hikey9xx/hisi-spmi-controller.c > +++ /dev/null > @@ -1,358 +0,0 @@ > -// SPDX-License-Identifier: GPL-2.0 > - > -#include <linux/delay.h> > -#include <linux/err.h> > -#include <linux/interrupt.h> > -#include <linux/io.h> > -#include <linux/kernel.h> > -#include <linux/module.h> > -#include <linux/of.h> > -#include <linux/platform_device.h> > -#include <linux/seq_file.h> > -#include <linux/slab.h> > -#include <linux/spmi.h> > - > -/* > - * SPMI register addr > - */ > -#define SPMI_CHANNEL_OFFSET 0x0300 > -#define SPMI_SLAVE_OFFSET 0x20 > - > -#define SPMI_APB_SPMI_CMD_BASE_ADDR 0x0100 > - > -#define SPMI_APB_SPMI_WDATA0_BASE_ADDR 0x0104 > -#define SPMI_APB_SPMI_WDATA1_BASE_ADDR 0x0108 > -#define SPMI_APB_SPMI_WDATA2_BASE_ADDR 0x010c > -#define SPMI_APB_SPMI_WDATA3_BASE_ADDR 0x0110 > - > -#define SPMI_APB_SPMI_STATUS_BASE_ADDR 0x0200 > - > -#define SPMI_APB_SPMI_RDATA0_BASE_ADDR 0x0204 > -#define SPMI_APB_SPMI_RDATA1_BASE_ADDR 0x0208 > -#define SPMI_APB_SPMI_RDATA2_BASE_ADDR 0x020c > -#define SPMI_APB_SPMI_RDATA3_BASE_ADDR 0x0210 > - > -#define SPMI_PER_DATAREG_BYTE 4 > -/* > - * SPMI cmd register > - */ > -#define SPMI_APB_SPMI_CMD_EN BIT(31) > -#define SPMI_APB_SPMI_CMD_TYPE_OFFSET 24 > -#define SPMI_APB_SPMI_CMD_LENGTH_OFFSET 20 > -#define SPMI_APB_SPMI_CMD_SLAVEID_OFFSET 16 > -#define SPMI_APB_SPMI_CMD_ADDR_OFFSET 0 > - > -/* Command Opcodes */ > - > -enum spmi_controller_cmd_op_code { > - SPMI_CMD_REG_ZERO_WRITE = 0, > - SPMI_CMD_REG_WRITE = 1, > - SPMI_CMD_REG_READ = 2, > - SPMI_CMD_EXT_REG_WRITE = 3, > - SPMI_CMD_EXT_REG_READ = 4, > - SPMI_CMD_EXT_REG_WRITE_L = 5, > - SPMI_CMD_EXT_REG_READ_L = 6, > - SPMI_CMD_REG_RESET = 7, > - SPMI_CMD_REG_SLEEP = 8, > - SPMI_CMD_REG_SHUTDOWN = 9, > - SPMI_CMD_REG_WAKEUP = 10, > -}; > - > -/* > - * SPMI status register > - */ > -#define SPMI_APB_TRANS_DONE BIT(0) > -#define SPMI_APB_TRANS_FAIL BIT(2) > - > -/* Command register fields */ > -#define SPMI_CONTROLLER_CMD_MAX_BYTE_COUNT 16 > - > -/* Maximum number of support PMIC peripherals */ > -#define SPMI_CONTROLLER_TIMEOUT_US 1000 > -#define SPMI_CONTROLLER_MAX_TRANS_BYTES 16 > - > -struct spmi_controller_dev { > - struct spmi_controller *controller; > - struct device *dev; > - void __iomem *base; > - spinlock_t lock; > - u32 channel; > -}; > - > -static int spmi_controller_wait_for_done(struct device *dev, > - struct spmi_controller_dev *ctrl_dev, > - void __iomem *base, u8 sid, u16 addr) > -{ > - u32 timeout = SPMI_CONTROLLER_TIMEOUT_US; > - u32 status, offset; > - > - offset = SPMI_APB_SPMI_STATUS_BASE_ADDR; > - offset += SPMI_CHANNEL_OFFSET * ctrl_dev->channel + SPMI_SLAVE_OFFSET * sid; > - > - do { > - status = readl(base + offset); > - > - if (status & SPMI_APB_TRANS_DONE) { > - if (status & SPMI_APB_TRANS_FAIL) { > - dev_err(dev, "%s: transaction failed (0x%x)\n", > - __func__, status); > - return -EIO; > - } > - dev_dbg(dev, "%s: status 0x%x\n", __func__, status); > - return 0; > - } > - udelay(1); > - } while (timeout--); > - > - dev_err(dev, "%s: timeout, status 0x%x\n", __func__, status); > - return -ETIMEDOUT; > -} > - > -static int spmi_read_cmd(struct spmi_controller *ctrl, > - u8 opc, u8 slave_id, u16 slave_addr, u8 *__buf, size_t bc) > -{ > - struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev); > - u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel; > - unsigned long flags; > - u8 *buf = __buf; > - u32 cmd, data; > - int rc; > - u8 op_code, i; > - > - if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) { > - dev_err(&ctrl->dev, > - "spmi_controller supports 1..%d bytes per trans, but:%zu requested\n", > - SPMI_CONTROLLER_MAX_TRANS_BYTES, bc); > - return -EINVAL; > - } > - > - switch (opc) { > - case SPMI_CMD_READ: > - op_code = SPMI_CMD_REG_READ; > - break; > - case SPMI_CMD_EXT_READ: > - op_code = SPMI_CMD_EXT_REG_READ; > - break; > - case SPMI_CMD_EXT_READL: > - op_code = SPMI_CMD_EXT_REG_READ_L; > - break; > - default: > - dev_err(&ctrl->dev, "invalid read cmd 0x%x\n", opc); > - return -EINVAL; > - } > - > - cmd = SPMI_APB_SPMI_CMD_EN | > - (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) | > - ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) | > - ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) | /* slvid */ > - ((slave_addr & 0xffff) << SPMI_APB_SPMI_CMD_ADDR_OFFSET); /* slave_addr */ > - > - spin_lock_irqsave(&spmi_controller->lock, flags); > - > - writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR); > - > - rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller, > - spmi_controller->base, slave_id, slave_addr); > - if (rc) > - goto done; > - > - for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) { > - data = readl(spmi_controller->base + chnl_ofst + > - SPMI_SLAVE_OFFSET * slave_id + > - SPMI_APB_SPMI_RDATA0_BASE_ADDR + > - i * SPMI_PER_DATAREG_BYTE); > - data = be32_to_cpu((__force __be32)data); > - if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) { > - memcpy(buf, &data, sizeof(data)); > - buf += sizeof(data); > - } else { > - memcpy(buf, &data, bc % SPMI_PER_DATAREG_BYTE); > - buf += (bc % SPMI_PER_DATAREG_BYTE); > - } > - } > - > -done: > - spin_unlock_irqrestore(&spmi_controller->lock, flags); > - if (rc) > - dev_err(&ctrl->dev, > - "spmi read wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n", > - opc, slave_id, slave_addr, bc + 1); > - else > - dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, read value: %*ph\n", > - __func__, slave_id, slave_addr, (int)bc, __buf); > - > - return rc; > -} > - > -static int spmi_write_cmd(struct spmi_controller *ctrl, > - u8 opc, u8 slave_id, u16 slave_addr, const u8 *__buf, size_t bc) > -{ > - struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev); > - u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel; > - const u8 *buf = __buf; > - unsigned long flags; > - u32 cmd, data; > - int rc; > - u8 op_code, i; > - > - if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) { > - dev_err(&ctrl->dev, > - "spmi_controller supports 1..%d bytes per trans, but:%zu requested\n", > - SPMI_CONTROLLER_MAX_TRANS_BYTES, bc); > - return -EINVAL; > - } > - > - switch (opc) { > - case SPMI_CMD_WRITE: > - op_code = SPMI_CMD_REG_WRITE; > - break; > - case SPMI_CMD_EXT_WRITE: > - op_code = SPMI_CMD_EXT_REG_WRITE; > - break; > - case SPMI_CMD_EXT_WRITEL: > - op_code = SPMI_CMD_EXT_REG_WRITE_L; > - break; > - default: > - dev_err(&ctrl->dev, "invalid write cmd 0x%x\n", opc); > - return -EINVAL; > - } > - > - cmd = SPMI_APB_SPMI_CMD_EN | > - (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) | > - ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) | > - ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) | > - ((slave_addr & 0xffff) << SPMI_APB_SPMI_CMD_ADDR_OFFSET); > - > - /* Write data to FIFOs */ > - spin_lock_irqsave(&spmi_controller->lock, flags); > - > - for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) { > - data = 0; > - if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) { > - memcpy(&data, buf, sizeof(data)); > - buf += sizeof(data); > - } else { > - memcpy(&data, buf, bc % SPMI_PER_DATAREG_BYTE); > - buf += (bc % SPMI_PER_DATAREG_BYTE); > - } > - > - writel((__force u32)cpu_to_be32(data), > - spmi_controller->base + chnl_ofst + > - SPMI_APB_SPMI_WDATA0_BASE_ADDR + > - SPMI_PER_DATAREG_BYTE * i); > - } > - > - /* Start the transaction */ > - writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR); > - > - rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller, > - spmi_controller->base, slave_id, > - slave_addr); > - spin_unlock_irqrestore(&spmi_controller->lock, flags); > - > - if (rc) > - dev_err(&ctrl->dev, "spmi write wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n", > - opc, slave_id, slave_addr, bc); > - else > - dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, wrote value: %*ph\n", > - __func__, slave_id, slave_addr, (int)bc, __buf); > - > - return rc; > -} > - > -static int spmi_controller_probe(struct platform_device *pdev) > -{ > - struct spmi_controller_dev *spmi_controller; > - struct spmi_controller *ctrl; > - struct resource *iores; > - int ret; > - > - ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*spmi_controller)); > - if (!ctrl) { > - dev_err(&pdev->dev, "can not allocate spmi_controller data\n"); > - return -ENOMEM; > - } > - spmi_controller = spmi_controller_get_drvdata(ctrl); > - spmi_controller->controller = ctrl; > - > - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); > - if (!iores) { > - dev_err(&pdev->dev, "can not get resource!\n"); > - return -EINVAL; > - } > - > - spmi_controller->base = devm_ioremap(&pdev->dev, iores->start, > - resource_size(iores)); > - if (!spmi_controller->base) { > - dev_err(&pdev->dev, "can not remap base addr!\n"); > - return -EADDRNOTAVAIL; > - } > - > - ret = of_property_read_u32(pdev->dev.of_node, "spmi-channel", > - &spmi_controller->channel); > - if (ret) { > - dev_err(&pdev->dev, "can not get channel\n"); > - return -ENODEV; > - } > - > - platform_set_drvdata(pdev, spmi_controller); > - dev_set_drvdata(&ctrl->dev, spmi_controller); > - > - spin_lock_init(&spmi_controller->lock); > - > - ctrl->nr = spmi_controller->channel; > - ctrl->dev.parent = pdev->dev.parent; > - ctrl->dev.of_node = of_node_get(pdev->dev.of_node); > - > - /* Callbacks */ > - ctrl->read_cmd = spmi_read_cmd; > - ctrl->write_cmd = spmi_write_cmd; > - > - ret = spmi_controller_add(ctrl); > - if (ret) > - dev_err(&pdev->dev, "spmi_add_controller failed with error %d!\n", ret); > - > - return ret; > -} > - > -static int spmi_del_controller(struct platform_device *pdev) > -{ > - struct spmi_controller *ctrl = platform_get_drvdata(pdev); > - > - spmi_controller_remove(ctrl); > - kfree(ctrl); > - return 0; > -} > - > -static const struct of_device_id spmi_controller_match_table[] = { > - { > - .compatible = "hisilicon,kirin970-spmi-controller", > - }, > - {} > -}; > -MODULE_DEVICE_TABLE(of, spmi_controller_match_table); > - > -static struct platform_driver spmi_controller_driver = { > - .probe = spmi_controller_probe, > - .remove = spmi_del_controller, > - .driver = { > - .name = "hisi_spmi_controller", > - .of_match_table = spmi_controller_match_table, > - }, > -}; > - > -static int __init spmi_controller_init(void) > -{ > - return platform_driver_register(&spmi_controller_driver); > -} > -postcore_initcall(spmi_controller_init); > - > -static void __exit spmi_controller_exit(void) > -{ > - platform_driver_unregister(&spmi_controller_driver); > -} > -module_exit(spmi_controller_exit); > - > -MODULE_LICENSE("GPL v2"); > -MODULE_VERSION("1.0"); > -MODULE_ALIAS("platform:spmi_controller"); > diff --git a/drivers/staging/hikey9xx/hisilicon,hisi-spmi-controller.yaml b/drivers/staging/hikey9xx/hisilicon,hisi-spmi-controller.yaml > deleted file mode 100644 > index 21f68a9c2df1..000000000000 > --- a/drivers/staging/hikey9xx/hisilicon,hisi-spmi-controller.yaml > +++ /dev/null > @@ -1,75 +0,0 @@ > -# SPDX-License-Identifier: GPL-2.0 > -%YAML 1.2 > ---- > -$id: http://devicetree.org/schemas/spmi/hisilicon,hisi-spmi-controller.yaml# > -$schema: http://devicetree.org/meta-schemas/core.yaml# > - > -title: HiSilicon SPMI controller > - > -maintainers: > - - Mauro Carvalho Chehab <mchehab+huawei@kernel.org> > - > -description: | > - The HiSilicon SPMI BUS controller is found on some Kirin-based designs. > - It is a MIPI System Power Management (SPMI) controller. > - > - The PMIC part is provided by > - drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml. > - > -properties: > - $nodename: > - pattern: "spmi@[0-9a-f]" > - > - compatible: > - const: hisilicon,kirin970-spmi-controller > - > - reg: > - maxItems: 1 > - > - "#address-cells": > - const: 2 > - > - "#size-cells": > - const: 0 > - > - spmi-channel: > - description: | > - number of the Kirin 970 SPMI channel where the SPMI devices are connected. > - > -required: > - - compatible > - - reg > - - spmi-channel > - - "#address-cells" > - - "#size-cells" > - > -patternProperties: > - "^pmic@[0-9a-f]$": > - description: | > - PMIC properties, which are specific to the used SPMI PMIC device(s). > - When used in combination with HiSilicon 6421v600, the properties > - are documented at > - drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml. > - > -additionalProperties: false > - > -examples: > - - | > - bus { > - #address-cells = <2>; > - #size-cells = <2>; > - > - spmi: spmi@fff24000 { > - compatible = "hisilicon,kirin970-spmi-controller"; > - #address-cells = <2>; > - #size-cells = <0>; > - status = "ok"; > - reg = <0x0 0xfff24000 0x0 0x1000>; > - spmi-channel = <2>; > - > - pmic@0 { > - reg = <0 0>; > - /* pmic properties */ > - }; > - }; > - }; > -- > 2.29.2 > ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v4 17/21] spmi: hisi-spmi-controller: move driver from staging 2021-02-05 22:19 ` Rob Herring @ 2021-03-25 13:47 ` Mauro Carvalho Chehab 0 siblings, 0 replies; 4+ messages in thread From: Mauro Carvalho Chehab @ 2021-03-25 13:47 UTC (permalink / raw) To: Rob Herring Cc: Mark Brown, Lee Jones, Colin Ian King, Dan Carpenter, Greg Kroah-Hartman, Mayulong, Stephen Boyd, YueHaibing, devel, devicetree, linux-arm-msm, linux-kernel Em Fri, 5 Feb 2021 16:19:47 -0600 Rob Herring <robh@kernel.org> escreveu: > On Tue, Jan 19, 2021 at 05:10:43PM +0100, Mauro Carvalho Chehab wrote: > > The Hisilicon 6421v600 SPMI driver is ready for mainstream. > > > > So, move it from staging. > > > > Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> > > --- > > .../spmi/hisilicon,hisi-spmi-controller.yaml | 75 ++++ > > MAINTAINERS | 7 + > > drivers/spmi/Kconfig | 9 + > > drivers/spmi/Makefile | 1 + > > drivers/spmi/hisi-spmi-controller.c | 358 ++++++++++++++++++ > > drivers/staging/hikey9xx/Kconfig | 11 - > > drivers/staging/hikey9xx/Makefile | 1 - > > .../staging/hikey9xx/hisi-spmi-controller.c | 358 ------------------ > > .../hisilicon,hisi-spmi-controller.yaml | 75 ---- > > 9 files changed, 450 insertions(+), 445 deletions(-) > > create mode 100644 Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml > > create mode 100644 drivers/spmi/hisi-spmi-controller.c > > delete mode 100644 drivers/staging/hikey9xx/hisi-spmi-controller.c > > delete mode 100644 drivers/staging/hikey9xx/hisilicon,hisi-spmi-controller.yaml > > > > diff --git a/Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml b/Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml > > new file mode 100644 > > index 000000000000..21f68a9c2df1 > > --- /dev/null > > +++ b/Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml > > @@ -0,0 +1,75 @@ > > +# SPDX-License-Identifier: GPL-2.0 > > +%YAML 1.2 > > +--- > > +$id: http://devicetree.org/schemas/spmi/hisilicon,hisi-spmi-controller.yaml# > > +$schema: http://devicetree.org/meta-schemas/core.yaml# > > + > > +title: HiSilicon SPMI controller > > + > > +maintainers: > > + - Mauro Carvalho Chehab <mchehab+huawei@kernel.org> > > + > > +description: | > > + The HiSilicon SPMI BUS controller is found on some Kirin-based designs. > > + It is a MIPI System Power Management (SPMI) controller. > > + > > + The PMIC part is provided by > > + drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml. > > + > > +properties: > > + $nodename: > > + pattern: "spmi@[0-9a-f]" > > + > > + compatible: > > + const: hisilicon,kirin970-spmi-controller > > '-controller' is kind of redundant. Ok. Will drop it. > > > + > > + reg: > > + maxItems: 1 > > + > > > + "#address-cells": > > + const: 2 > > + > > + "#size-cells": > > + const: 0 > > These 2 are covered by spmi.yaml Ok. > > > + > > + spmi-channel: > > + description: | > > + number of the Kirin 970 SPMI channel where the SPMI devices are connected. > > Common to SPMI? If not, needs a vendor prefix. That's an interesting question. My understanding is that this is not vendor-specific, but maybe Stephen can give us more details. The spmi.h header calls it "nr", and documents it at include/linux/spmi.h as: /** * struct spmi_controller - interface to the SPMI master controller * @dev: Driver model representation of the device. * @nr: board-specific number identifier for this controller/bus * @cmd: sends a non-data command sequence on the SPMI bus. * @read_cmd: sends a register read command sequence on the SPMI bus. * @write_cmd: sends a register write command sequence on the SPMI bus. */ There, it says that this is "board-specific number identifier". Yet, as the SPMI is a serial bus with up to 4 masters (controller), I suspect that the idea is to associate it with the master ID. This is used on boards with multiple SoCs. See, for instance, slide 5 of: https://www.mipi.org/sites/default/files/Bangalore-Qualcomm-SPMI-1.0-Multi-master-Verification.pdf However, it is hard to know for sure, as no drivers use it, except by Hikey 970 controller: $ grep "\b\->nr\b" $(git grep -l spmi.h) drivers/spmi/spmi.c: ida_simple_remove(&ctrl_ida, ctrl->nr); drivers/spmi/spmi.c: dev_set_name(&sdev->dev, "%d-%02x", ctrl->nr, sdev->usid); drivers/spmi/spmi.c: ctrl->nr = id; drivers/spmi/spmi.c: ctrl->nr, &ctrl->dev); drivers/staging/hikey9xx/hisi-spmi-controller.c: ctrl->nr = spmi_controller->channel; > > Type? Range of values? The SPMI core defines it as "unsigned int". So, I would use: $ref: /schemas/types.yaml#/definitions/uint32 as a type. At the driver, this is used to calculate the channel offset with: static int spmi_write_cmd(...) { u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel; ... writel((u32 __force)cpu_to_be32(data), spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_WDATA0_BASE_ADDR + SPMI_PER_DATAREG_BYTE * i); ... } As on both spmi.h and the Hikey 970 SPMI controller defines it as uint32, it doesn't seem to be a good idea to put a range of values, specially since we don't have the datasheets for this SoC. > > > + > > +required: > > + - compatible > > + - reg > > + - spmi-channel > > > + - "#address-cells" > > + - "#size-cells" > > Covered by spmi.yaml. > > > + > > +patternProperties: > > + "^pmic@[0-9a-f]$": > > Presumably you could have something besides a PMIC. Hmm... SPMI means MIPI System Power Management Interface. The MIPI says that [1]: "The MIPI System Power Management Interface is a two-wire serial interface that uses CMOS I/Os for the physical layer. The interface connects the integrated power controller of a system-on-chip (SoC) processor system with one or more power management IC voltage regulation systems." [1] https://www.mipi.org/specifications/system-power-management-interface OK, as this is a serial bus, I guess one could abuse the interface and add non-PMIC devices on it. Also, some future version of SPMI might extend it to non-PMIC devices, but, IMO, if we ever add a non-PMIC device, another patternProperties would be needed in order to describe the other device types that could be connected to the PM bus. > > > + description: | > > + PMIC properties, which are specific to the used SPMI PMIC device(s). > > + When used in combination with HiSilicon 6421v600, the properties > > + are documented at > > + drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml. > > + > > +additionalProperties: false > > + > > +examples: > > + - | > > + bus { > > + #address-cells = <2>; > > + #size-cells = <2>; > > + > > + spmi: spmi@fff24000 { > > + compatible = "hisilicon,kirin970-spmi-controller"; > > + #address-cells = <2>; > > + #size-cells = <0>; > > + status = "ok"; > > Drop status. Ok. > > > + reg = <0x0 0xfff24000 0x0 0x1000>; > > + spmi-channel = <2>; > > + > > + pmic@0 { > > + reg = <0 0>; > > + /* pmic properties */ > > + }; > > + }; > > + }; Thanks, Mauro ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2021-03-25 13:48 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2021-01-19 16:10 [PATCH v4 00/21] Move Hisilicon 6421v600 SPMI driver set out of staging Mauro Carvalho Chehab 2021-01-19 16:10 ` [PATCH v4 17/21] spmi: hisi-spmi-controller: move driver from staging Mauro Carvalho Chehab 2021-02-05 22:19 ` Rob Herring 2021-03-25 13:47 ` Mauro Carvalho Chehab
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox