|
@@ -10,6 +10,7 @@
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
* GNU General Public License for more details.
|
|
|
*/
|
|
|
+#include <linux/bitmap.h>
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/err.h>
|
|
|
#include <linux/interrupt.h>
|
|
@@ -47,9 +48,9 @@
|
|
|
#define SPMI_MAPPING_BIT_IS_1_FLAG(X) (((X) >> 8) & 0x1)
|
|
|
#define SPMI_MAPPING_BIT_IS_1_RESULT(X) (((X) >> 0) & 0xFF)
|
|
|
|
|
|
-#define SPMI_MAPPING_TABLE_LEN 255
|
|
|
#define SPMI_MAPPING_TABLE_TREE_DEPTH 16 /* Maximum of 16-bits */
|
|
|
-#define PPID_TO_CHAN_TABLE_SZ BIT(12) /* PPID is 12bit chan is 1byte*/
|
|
|
+#define PMIC_ARB_MAX_PPID BIT(12) /* PPID is 12bit */
|
|
|
+#define PMIC_ARB_CHAN_VALID BIT(15)
|
|
|
|
|
|
/* Ownership Table */
|
|
|
#define SPMI_OWNERSHIP_TABLE_REG(N) (0x0700 + (4 * (N)))
|
|
@@ -85,9 +86,7 @@ enum pmic_arb_cmd_op_code {
|
|
|
};
|
|
|
|
|
|
/* Maximum number of support PMIC peripherals */
|
|
|
-#define PMIC_ARB_MAX_PERIPHS 256
|
|
|
-#define PMIC_ARB_MAX_CHNL 128
|
|
|
-#define PMIC_ARB_PERIPH_ID_VALID (1 << 15)
|
|
|
+#define PMIC_ARB_MAX_PERIPHS 512
|
|
|
#define PMIC_ARB_TIMEOUT_US 100
|
|
|
#define PMIC_ARB_MAX_TRANS_BYTES (8)
|
|
|
|
|
@@ -125,18 +124,22 @@ struct spmi_pmic_arb_dev {
|
|
|
void __iomem *wr_base;
|
|
|
void __iomem *intr;
|
|
|
void __iomem *cnfg;
|
|
|
+ void __iomem *core;
|
|
|
+ resource_size_t core_size;
|
|
|
raw_spinlock_t lock;
|
|
|
u8 channel;
|
|
|
int irq;
|
|
|
u8 ee;
|
|
|
- u8 min_apid;
|
|
|
- u8 max_apid;
|
|
|
- u32 mapping_table[SPMI_MAPPING_TABLE_LEN];
|
|
|
+ u16 min_apid;
|
|
|
+ u16 max_apid;
|
|
|
+ u32 *mapping_table;
|
|
|
+ DECLARE_BITMAP(mapping_table_valid, PMIC_ARB_MAX_PERIPHS);
|
|
|
struct irq_domain *domain;
|
|
|
struct spmi_controller *spmic;
|
|
|
- u16 apid_to_ppid[256];
|
|
|
+ u16 *apid_to_ppid;
|
|
|
const struct pmic_arb_ver_ops *ver_ops;
|
|
|
- u8 *ppid_to_chan;
|
|
|
+ u16 *ppid_to_chan;
|
|
|
+ u16 last_channel;
|
|
|
};
|
|
|
|
|
|
/**
|
|
@@ -158,7 +161,8 @@ struct spmi_pmic_arb_dev {
|
|
|
*/
|
|
|
struct pmic_arb_ver_ops {
|
|
|
/* spmi commands (read_cmd, write_cmd, cmd) functionality */
|
|
|
- u32 (*offset)(struct spmi_pmic_arb_dev *dev, u8 sid, u16 addr);
|
|
|
+ int (*offset)(struct spmi_pmic_arb_dev *dev, u8 sid, u16 addr,
|
|
|
+ u32 *offset);
|
|
|
u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc);
|
|
|
int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid);
|
|
|
/* Interrupts controller functionality (offset of PIC registers) */
|
|
@@ -212,7 +216,14 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl,
|
|
|
struct spmi_pmic_arb_dev *dev = spmi_controller_get_drvdata(ctrl);
|
|
|
u32 status = 0;
|
|
|
u32 timeout = PMIC_ARB_TIMEOUT_US;
|
|
|
- u32 offset = dev->ver_ops->offset(dev, sid, addr) + PMIC_ARB_STATUS;
|
|
|
+ u32 offset;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ rc = dev->ver_ops->offset(dev, sid, addr, &offset);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ offset += PMIC_ARB_STATUS;
|
|
|
|
|
|
while (timeout--) {
|
|
|
status = readl_relaxed(base + offset);
|
|
@@ -257,7 +268,11 @@ pmic_arb_non_data_cmd_v1(struct spmi_controller *ctrl, u8 opc, u8 sid)
|
|
|
unsigned long flags;
|
|
|
u32 cmd;
|
|
|
int rc;
|
|
|
- u32 offset = pmic_arb->ver_ops->offset(pmic_arb, sid, 0);
|
|
|
+ u32 offset;
|
|
|
+
|
|
|
+ rc = pmic_arb->ver_ops->offset(pmic_arb, sid, 0, &offset);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
|
|
|
cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20);
|
|
|
|
|
@@ -297,7 +312,11 @@ static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
|
|
|
u8 bc = len - 1;
|
|
|
u32 cmd;
|
|
|
int rc;
|
|
|
- u32 offset = pmic_arb->ver_ops->offset(pmic_arb, sid, addr);
|
|
|
+ u32 offset;
|
|
|
+
|
|
|
+ rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, &offset);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
|
|
|
if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
|
|
|
dev_err(&ctrl->dev,
|
|
@@ -344,7 +363,11 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
|
|
|
u8 bc = len - 1;
|
|
|
u32 cmd;
|
|
|
int rc;
|
|
|
- u32 offset = pmic_arb->ver_ops->offset(pmic_arb, sid, addr);
|
|
|
+ u32 offset;
|
|
|
+
|
|
|
+ rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, &offset);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
|
|
|
if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
|
|
|
dev_err(&ctrl->dev,
|
|
@@ -614,6 +637,10 @@ static int search_mapping_table(struct spmi_pmic_arb_dev *pa,
|
|
|
u32 data;
|
|
|
|
|
|
for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) {
|
|
|
+ if (!test_and_set_bit(index, pa->mapping_table_valid))
|
|
|
+ mapping_table[index] = readl_relaxed(pa->cnfg +
|
|
|
+ SPMI_MAPPING_TABLE_REG(index));
|
|
|
+
|
|
|
data = mapping_table[index];
|
|
|
|
|
|
if (ppid & (1 << SPMI_MAPPING_BIT_INDEX(data))) {
|
|
@@ -701,18 +728,61 @@ static int qpnpint_irq_domain_map(struct irq_domain *d,
|
|
|
}
|
|
|
|
|
|
/* v1 offset per ee */
|
|
|
-static u32 pmic_arb_offset_v1(struct spmi_pmic_arb_dev *pa, u8 sid, u16 addr)
|
|
|
+static int
|
|
|
+pmic_arb_offset_v1(struct spmi_pmic_arb_dev *pa, u8 sid, u16 addr, u32 *offset)
|
|
|
{
|
|
|
- return 0x800 + 0x80 * pa->channel;
|
|
|
+ *offset = 0x800 + 0x80 * pa->channel;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
+static u16 pmic_arb_find_chan(struct spmi_pmic_arb_dev *pa, u16 ppid)
|
|
|
+{
|
|
|
+ u32 regval, offset;
|
|
|
+ u16 chan;
|
|
|
+ u16 id;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * PMIC_ARB_REG_CHNL is a table in HW mapping channel to ppid.
|
|
|
+ * ppid_to_chan is an in-memory invert of that table.
|
|
|
+ */
|
|
|
+ for (chan = pa->last_channel; ; chan++) {
|
|
|
+ offset = PMIC_ARB_REG_CHNL(chan);
|
|
|
+ if (offset >= pa->core_size)
|
|
|
+ break;
|
|
|
+
|
|
|
+ regval = readl_relaxed(pa->core + offset);
|
|
|
+ if (!regval)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ id = (regval >> 8) & PMIC_ARB_PPID_MASK;
|
|
|
+ pa->ppid_to_chan[id] = chan | PMIC_ARB_CHAN_VALID;
|
|
|
+ if (id == ppid) {
|
|
|
+ chan |= PMIC_ARB_CHAN_VALID;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ pa->last_channel = chan & ~PMIC_ARB_CHAN_VALID;
|
|
|
+
|
|
|
+ return chan;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/* v2 offset per ppid (chan) and per ee */
|
|
|
-static u32 pmic_arb_offset_v2(struct spmi_pmic_arb_dev *pa, u8 sid, u16 addr)
|
|
|
+static int
|
|
|
+pmic_arb_offset_v2(struct spmi_pmic_arb_dev *pa, u8 sid, u16 addr, u32 *offset)
|
|
|
{
|
|
|
u16 ppid = (sid << 8) | (addr >> 8);
|
|
|
- u8 chan = pa->ppid_to_chan[ppid];
|
|
|
+ u16 chan;
|
|
|
|
|
|
- return 0x1000 * pa->ee + 0x8000 * chan;
|
|
|
+ chan = pa->ppid_to_chan[ppid];
|
|
|
+ if (!(chan & PMIC_ARB_CHAN_VALID))
|
|
|
+ chan = pmic_arb_find_chan(pa, ppid);
|
|
|
+ if (!(chan & PMIC_ARB_CHAN_VALID))
|
|
|
+ return -ENODEV;
|
|
|
+ chan &= ~PMIC_ARB_CHAN_VALID;
|
|
|
+
|
|
|
+ *offset = 0x1000 * pa->ee + 0x8000 * chan;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc)
|
|
@@ -797,7 +867,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
|
|
|
struct resource *res;
|
|
|
void __iomem *core;
|
|
|
u32 channel, ee, hw_ver;
|
|
|
- int err, i;
|
|
|
+ int err;
|
|
|
bool is_v1;
|
|
|
|
|
|
ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa));
|
|
@@ -808,6 +878,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
|
|
|
pa->spmic = ctrl;
|
|
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
|
|
|
+ pa->core_size = resource_size(res);
|
|
|
core = devm_ioremap_resource(&ctrl->dev, res);
|
|
|
if (IS_ERR(core)) {
|
|
|
err = PTR_ERR(core);
|
|
@@ -825,10 +896,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
|
|
|
pa->wr_base = core;
|
|
|
pa->rd_base = core;
|
|
|
} else {
|
|
|
- u8 chan;
|
|
|
- u16 ppid;
|
|
|
- u32 regval;
|
|
|
-
|
|
|
+ pa->core = core;
|
|
|
pa->ver_ops = &pmic_arb_v2;
|
|
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
|
@@ -847,24 +915,14 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
|
|
|
goto err_put_ctrl;
|
|
|
}
|
|
|
|
|
|
- pa->ppid_to_chan = devm_kzalloc(&ctrl->dev,
|
|
|
- PPID_TO_CHAN_TABLE_SZ, GFP_KERNEL);
|
|
|
+ pa->ppid_to_chan = devm_kcalloc(&ctrl->dev,
|
|
|
+ PMIC_ARB_MAX_PPID,
|
|
|
+ sizeof(*pa->ppid_to_chan),
|
|
|
+ GFP_KERNEL);
|
|
|
if (!pa->ppid_to_chan) {
|
|
|
err = -ENOMEM;
|
|
|
goto err_put_ctrl;
|
|
|
}
|
|
|
- /*
|
|
|
- * PMIC_ARB_REG_CHNL is a table in HW mapping channel to ppid.
|
|
|
- * ppid_to_chan is an in-memory invert of that table.
|
|
|
- */
|
|
|
- for (chan = 0; chan < PMIC_ARB_MAX_CHNL; ++chan) {
|
|
|
- regval = readl_relaxed(core + PMIC_ARB_REG_CHNL(chan));
|
|
|
- if (!regval)
|
|
|
- continue;
|
|
|
-
|
|
|
- ppid = (regval >> 8) & 0xFFF;
|
|
|
- pa->ppid_to_chan[ppid] = chan;
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr");
|
|
@@ -915,9 +973,20 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
|
|
|
|
|
|
pa->ee = ee;
|
|
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(pa->mapping_table); ++i)
|
|
|
- pa->mapping_table[i] = readl_relaxed(
|
|
|
- pa->cnfg + SPMI_MAPPING_TABLE_REG(i));
|
|
|
+ pa->apid_to_ppid = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS,
|
|
|
+ sizeof(*pa->apid_to_ppid),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!pa->apid_to_ppid) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto err_put_ctrl;
|
|
|
+ }
|
|
|
+
|
|
|
+ pa->mapping_table = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS - 1,
|
|
|
+ sizeof(*pa->mapping_table), GFP_KERNEL);
|
|
|
+ if (!pa->mapping_table) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto err_put_ctrl;
|
|
|
+ }
|
|
|
|
|
|
/* Initialize max_apid/min_apid to the opposite bounds, during
|
|
|
* the irq domain translation, we are sure to update these */
|