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@xxxxxxxxxx> > --- > .../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@xxxxxxxxxx> > + > +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@xxxxxxxxxx> > +L: linux-kernel@xxxxxxxxxxxxxxx > +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@xxxxxxxxxx> > L: devel@xxxxxxxxxxxxxxxxxxxx > 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@xxxxxxxxxx> > - > -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 >