123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- // 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((__be32 __force)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((u32 __force)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");
- ret = -EINVAL;
- goto err_put_controller;
- }
- 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");
- ret = -EADDRNOTAVAIL;
- goto err_put_controller;
- }
- ret = of_property_read_u32(pdev->dev.of_node, "hisilicon,spmi-channel",
- &spmi_controller->channel);
- if (ret) {
- dev_err(&pdev->dev, "can not get channel\n");
- ret = -ENODEV;
- goto err_put_controller;
- }
- 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_controller_add failed with error %d!\n", ret);
- goto err_put_controller;
- }
- return 0;
- err_put_controller:
- spmi_controller_put(ctrl);
- return ret;
- }
- static int spmi_del_controller(struct platform_device *pdev)
- {
- struct spmi_controller *ctrl = platform_get_drvdata(pdev);
- spmi_controller_remove(ctrl);
- spmi_controller_put(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");
|