[PATCH v2 5/7] media: uniphier: add ucode load common file of HSC

From: Katsuhiro Suzuki
Date: Wed Aug 08 2018 - 01:26:04 EST


Adds code to load uCode and start the internal cores of HSC for
Socionext UniPhier SoCs.

Signed-off-by: Katsuhiro Suzuki <suzuki.katsuhiro@xxxxxxxxxxxxx>

---

Changes from v1:
- Split from large patches
- Fix include lines
---
drivers/media/platform/uniphier/Makefile | 2 +-
drivers/media/platform/uniphier/hsc-reg.h | 59 +++
drivers/media/platform/uniphier/hsc-ucode.c | 416 ++++++++++++++++++++
drivers/media/platform/uniphier/hsc.h | 4 +
4 files changed, 480 insertions(+), 1 deletion(-)
create mode 100644 drivers/media/platform/uniphier/hsc-ucode.c

diff --git a/drivers/media/platform/uniphier/Makefile b/drivers/media/platform/uniphier/Makefile
index 2ba03067644d..79b4dc44df94 100644
--- a/drivers/media/platform/uniphier/Makefile
+++ b/drivers/media/platform/uniphier/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
-uniphier-dvb-y += hsc-dma.o hsc-css.o hsc-ts.o
+uniphier-dvb-y += hsc-dma.o hsc-css.o hsc-ts.o hsc-ucode.o

obj-$(CONFIG_DVB_UNIPHIER) += uniphier-dvb.o
diff --git a/drivers/media/platform/uniphier/hsc-reg.h b/drivers/media/platform/uniphier/hsc-reg.h
index 26f04b79178b..1ca3ad55330f 100644
--- a/drivers/media/platform/uniphier/hsc-reg.h
+++ b/drivers/media/platform/uniphier/hsc-reg.h
@@ -8,6 +8,65 @@
#ifndef DVB_UNIPHIER_HSC_REG_H__
#define DVB_UNIPHIER_HSC_REG_H__

+/* IOB1, 2, 3 */
+#define IOB_PKTCNT 0x1740
+#define IOB_PKTCNTRST 0x1744
+#define IOB_PKTCNTST 0x1744
+#define IOB_DUMMY_ENABLE 0x1748
+#define IOB_FORMATCHANGE_EN 0x174c
+#define IOB_UASSIST0 0x1750
+#define IOB_UASSIST1 0x1754
+#define IOB_URESERVE(i) (0x1758 + (i) * 0x4)
+#define IOB_PCRRECEN IOB_URESERVE(2)
+#define IOB_UPARTIAL(i) (0x1768 + (i) * 0x4)
+#define IOB_SPUINTREN 0x1778
+
+#define IOB_HSCREV 0x1a00
+#define IOB_SECCLK(i) (0x1a08 + (i) * 0x6c)
+#define IOB_SECTIMEH(i) (0x1a0c + (i) * 0x6c)
+#define IOB_SECTIMEL(i) (0x1a10 + (i) * 0x6c)
+#define IOB_RESET0 0x1a14
+#define IOB_RESET0_APCORE BIT(20)
+#define IOB_RESET1 0x1a18
+#define IOB_CLKSTOP 0x1a1c
+#define IOB_DEBUG 0x1a20
+#define IOB_DEBUG_SPUHALT BIT(0)
+#define IOB_INTREN(i) (0x1a24 + (i) * 0x8)
+#define IOB_INTRST(i) (0x1a28 + (i) * 0x8)
+#define IOB_INTREN0 0x1a24
+#define IOB_INTRST0 0x1a28
+#define IOB_INTREN0_1 0x1a2c
+#define IOB_INTRST0_1 0x1a30
+#define IOB_INTREN0_2 0x1a34
+#define IOB_INTRST0_2 0x1a38
+#define IOB_INTREN1 0x1a3c
+#define IOB_INTRST1 0x1a40
+#define IOB_INTREN1_1 0x1a44
+#define IOB_INTRST1_1 0x1a48
+#define IOB_INTREN2 0x1a4c
+#define IOB_INTRST2 0x1a50
+#define INTR2_DRV BIT(31)
+#define INTR2_CIP_FRMT(i) BIT((i) + 16)
+#define INTR2_CIP_NORMAL BIT(16)
+#define INTR2_SEC_CLK_A BIT(15)
+#define INTR2_SEC_CLK_S BIT(14)
+#define INTR2_MBC_CIP_W(i) BIT((i) + 9)
+#define INTR2_MBC_CIP_R(i) BIT((i) + 4)
+#define INTR2_CIP_AUTH_A BIT(1)
+#define INTR2_CIP_AUTH_S BIT(0)
+#define IOB_INTREN3 0x1a54
+#define IOB_INTRST3 0x1a58
+#define INTR3_DRV BIT(31)
+#define INTR3_CIP_FRMT(i) BIT((i) + 16)
+#define INTR3_SEC_CLK_A BIT(15)
+#define INTR3_SEC_CLK_S BIT(14)
+#define INTR3_MBC_CIP_W(i) BIT((i) + 9)
+#define INTR3_MBC_CIP_R(i) BIT((i) + 4)
+#define INTR3_CIP_AUTH_A BIT(1)
+#define INTR3_CIP_AUTH_S BIT(0)
+#define IOB_INTREN4 0x1a5c
+#define IOB_INTRST4 0x1a60
+
/* MBC1-7 Common */
#define CDMBC_STRT(i) (0x2300 + ((i) - 1) * 0x4)
#define CDMBC_PERFCNFG 0x230c
diff --git a/drivers/media/platform/uniphier/hsc-ucode.c b/drivers/media/platform/uniphier/hsc-ucode.c
new file mode 100644
index 000000000000..9d9369914c48
--- /dev/null
+++ b/drivers/media/platform/uniphier/hsc-ucode.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Socionext UniPhier DVB driver for High-speed Stream Controller (HSC).
+// Core init and uCode loader.
+//
+// Copyright (c) 2018 Socionext Inc.
+
+#include <linux/bitfield.h>
+#include <linux/firmware.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+
+#include "hsc.h"
+#include "hsc-reg.h"
+
+/* CIP SPU File */
+#define CIP_F_ID 0x1540
+#define CIP_F_MODE 0x1544
+#define CIP_F_CTRL 0x1548
+#define CIP_F_SKIP 0x154c
+#define CIP_F_PAYLOAD 0x1560
+
+/* CIP file channel */
+#define CDMBC_CIPMODE(i) (0x24fc + (i) * 0x4)
+#define CDMBC_CIPMODE_PUSH BIT(0)
+#define CDMBC_CIPPRIORITY(i) (0x2510 + (i) * 0x4)
+#define CDMBC_CIPPRIORITY_PRIOR_MASK GENMASK(1, 0)
+#define CDMBC_CH18ATTRIBUTE (0x2524)
+
+/* UCODE DL */
+#define UCODE_REVISION_AM 0x10fd0
+#define CIP_UCODEADDR_AM1 0x10fd4
+#define CIP_UCODEADDR_AM0 0x10fd8
+#define CORRECTATS_CTRL 0x10fdc
+#define UCODE_REVISION 0x10fe0
+#define AM_UCODE_IGPGCTRL 0x10fe4
+#define REPDPLLCTRLEN 0x10fe8
+#define UCODE_DLADDR1 0x10fec
+#define UCODE_DLADDR0 0x10ff0
+#define UCODE_ERRLOGCTRL 0x10ff4
+
+struct hsc_cip_file_dma_param {
+ dma_addr_t cipr_start;
+ dma_addr_t cipw_start;
+ size_t inter_size;
+ size_t total_size;
+ u8 key_id1;
+ u8 key_id0;
+ u8 endian;
+ int id1_en;
+ int push;
+};
+
+static void core_start(struct hsc_chip *chip)
+{
+ const struct hsc_spec_init_ram *rams = chip->spec->init_rams;
+ struct regmap *r = chip->regmap;
+ size_t i, s;
+
+ regmap_write(r, IOB_RESET0, ~0);
+ regmap_write(r, IOB_RESET1, ~0);
+
+ regmap_write(r, IOB_CLKSTOP, 0);
+ /* Deassert all internal resets, but AP core is later for uCode */
+ regmap_write(r, IOB_RESET0, IOB_RESET0_APCORE);
+ regmap_write(r, IOB_RESET1, 0);
+
+ /* Halt SPU for uCode */
+ regmap_write(r, IOB_DEBUG, IOB_DEBUG_SPUHALT);
+
+ for (i = 0; i < chip->spec->num_init_rams; i++)
+ for (s = 0; s < rams[i].size; s += 4)
+ regmap_write(r, rams[i].addr + s, rams[i].pattern);
+}
+
+static void core_stop(struct hsc_chip *chip)
+{
+ struct regmap *r = chip->regmap;
+
+ regmap_write(r, IOB_RESET0, 0);
+ regmap_write(r, IOB_RESET1, 0);
+
+ regmap_write(r, IOB_CLKSTOP, ~0);
+}
+
+static int ucode_set_data_addr(struct hsc_chip *chip, int mode)
+{
+ struct regmap *r = chip->regmap;
+ dma_addr_t addr;
+
+ switch (mode) {
+ case HSC_UCODE_SPU_0:
+ case HSC_UCODE_SPU_1:
+ addr = chip->ucode_spu.phys_data;
+ regmap_write(r, UCODE_DLADDR0, addr);
+ regmap_write(r, UCODE_DLADDR1, addr >> 32);
+ break;
+ case HSC_UCODE_ACE:
+ addr = chip->ucode_am.phys_data;
+ regmap_write(r, CIP_UCODEADDR_AM0, addr);
+ regmap_write(r, CIP_UCODEADDR_AM1, addr >> 32);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void file_channel_dma_set(struct hsc_chip *chip,
+ const struct hsc_spec_dma *spec_r,
+ const struct hsc_spec_dma *spec_w,
+ struct hsc_cip_file_dma_param *p)
+{
+ struct regmap *r = chip->regmap;
+ dma_addr_t cipr_end, cipw_end;
+ u32 v;
+
+ /* For CIP Read */
+ v = FIELD_PREP(CDMBC_CHCTRL1_LINKCH1_MASK, 1) |
+ FIELD_PREP(CDMBC_CHCTRL1_STATSEL_MASK, 4) |
+ CDMBC_CHCTRL1_TYPE_INTERMIT;
+ regmap_write(r, CDMBC_CHCTRL1(spec_r->dma_ch), v);
+
+ regmap_write(r, CDMBC_CHCAUSECTRL(spec_r->dma_ch), 0);
+
+ v = FIELD_PREP(CDMBC_CHAMODE_ENDIAN_MASK, p->endian) |
+ FIELD_PREP(CDMBC_CHAMODE_AUPDT_MASK, 0) |
+ CDMBC_CHAMODE_TYPE_RB;
+ regmap_write(r, CDMBC_CHSRCAMODE(spec_r->dma_ch), v);
+
+ v = FIELD_PREP(CDMBC_CHAMODE_ENDIAN_MASK, 1) |
+ FIELD_PREP(CDMBC_CHAMODE_AUPDT_MASK, 2);
+ regmap_write(r, CDMBC_CHDSTAMODE(spec_r->dma_ch), v);
+
+ v = FIELD_PREP(CDMBC_CHDSTSTRTADRS_TID_MASK, 0xc) |
+ FIELD_PREP(CDMBC_CHDSTSTRTADRS_ID1_EN_MASK, p->id1_en) |
+ FIELD_PREP(CDMBC_CHDSTSTRTADRS_KEY_ID1_MASK, p->key_id1) |
+ FIELD_PREP(CDMBC_CHDSTSTRTADRS_KEY_ID0_MASK, p->key_id0);
+ regmap_write(r, CDMBC_CHDSTSTRTADRSD(spec_r->dma_ch), v);
+
+ regmap_write(r, CDMBC_CHSIZE(spec_r->dma_ch), p->inter_size);
+
+ cipr_end = p->cipr_start + p->total_size;
+ hsc_dma_rb_set_buffer(chip, spec_r->rb_ch, p->cipr_start, cipr_end);
+ hsc_dma_rb_set_rp(chip, spec_r->rb_ch, p->cipr_start);
+ hsc_dma_rb_set_wp(chip, spec_r->rb_ch, cipr_end);
+
+ /* For CIP Write */
+ v = FIELD_PREP(CDMBC_CHCTRL1_LINKCH1_MASK, 5) |
+ FIELD_PREP(CDMBC_CHCTRL1_STATSEL_MASK, 4) |
+ CDMBC_CHCTRL1_TYPE_INTERMIT |
+ CDMBC_CHCTRL1_IND_SIZE_UND;
+ regmap_write(r, CDMBC_CHCTRL1(spec_w->dma_ch), v);
+
+ v = FIELD_PREP(CDMBC_CHAMODE_ENDIAN_MASK, 1) |
+ FIELD_PREP(CDMBC_CHAMODE_AUPDT_MASK, 2);
+ regmap_write(r, CDMBC_CHSRCAMODE(spec_w->dma_ch), v);
+
+ v = FIELD_PREP(CDMBC_CHAMODE_ENDIAN_MASK, p->endian) |
+ FIELD_PREP(CDMBC_CHAMODE_AUPDT_MASK, 0) |
+ CDMBC_CHAMODE_TYPE_RB;
+ regmap_write(r, CDMBC_CHDSTAMODE(spec_w->dma_ch), v);
+
+ cipw_end = p->cipw_start + p->total_size;
+ hsc_dma_rb_set_buffer(chip, spec_w->rb_ch, p->cipw_start, cipw_end);
+ hsc_dma_rb_set_rp(chip, spec_w->rb_ch, cipw_end);
+ hsc_dma_rb_set_wp(chip, spec_w->rb_ch, p->cipw_start);
+
+ /* Transferring size */
+ regmap_write(r, CDMBC_ITSTEPS(spec_r->it_ch), p->total_size);
+
+ /* CIP settings */
+ regmap_write(r, CDMBC_CIPMODE(spec_r->cip_ch),
+ (p->push) ? CDMBC_CIPMODE_PUSH : 0);
+
+ regmap_write(r, CDMBC_CIPPRIORITY(spec_r->cip_ch),
+ FIELD_PREP(CDMBC_CIPPRIORITY_PRIOR_MASK, 3));
+}
+
+static void file_channel_start(struct hsc_chip *chip,
+ const struct hsc_spec_dma *spec_r,
+ const struct hsc_spec_dma *spec_w,
+ bool push, bool mmu_en)
+{
+ struct regmap *r = chip->regmap;
+ u32 v;
+
+ regmap_write(r, CDMBC_CIPMODE(spec_r->cip_ch),
+ (push) ? CDMBC_CIPMODE_PUSH : 0);
+
+ if (mmu_en) {
+ v = CDMBC_CHDDR_REG_LOAD_ON | CDMBC_CHDDR_AT_CHEN_ON;
+
+ /* Enable IOMMU for CIP-R and CIP-W */
+ regmap_write(r, CDMBC_CHDDR(spec_r->dma_ch),
+ v | CDMBC_CHDDR_SET_MCB_RD);
+ regmap_write(r, CDMBC_CHDDR(spec_w->dma_ch),
+ v | CDMBC_CHDDR_SET_MCB_WR);
+ }
+
+ v = 0x01000000 | (1 << spec_r->en.sft) | (1 << spec_w->en.sft);
+ regmap_write(r, CDMBC_STRT(1), v);
+}
+
+static void file_channel_wait(struct hsc_chip *chip,
+ const struct hsc_spec_dma *spec)
+{
+ struct regmap *r = chip->regmap;
+ u32 v;
+
+ regmap_read(r, CDMBC_CHIR(spec->dma_ch), &v);
+ while (!(v & INTR_MBC_CH_WDONE)) {
+ usleep_range(1000, 10000);
+ regmap_read(r, CDMBC_CHIR(spec->dma_ch), &v);
+ };
+ regmap_write(r, CDMBC_CHIR(spec->dma_ch), v);
+
+ regmap_read(r, CDMBC_RBIR(spec->dma_ch), &v);
+ regmap_write(r, CDMBC_RBIR(spec->dma_ch), v);
+}
+
+static int ucode_load_dma(struct hsc_chip *chip, int mode)
+{
+ const struct hsc_spec_dma *spec_r, *spec_w;
+ struct regmap *r = chip->regmap;
+ struct hsc_ucode_buf *ucode;
+ struct hsc_cip_file_dma_param dma_p = {0};
+ u32 cip_f_ctrl;
+
+ spec_r = &chip->spec->dma_in[HSC_DMA_CIP_IN0];
+ spec_w = &chip->spec->dma_out[HSC_DMA_CIP_OUT0];
+
+ switch (mode) {
+ case HSC_UCODE_SPU_0:
+ case HSC_UCODE_SPU_1:
+ ucode = &chip->ucode_spu;
+ cip_f_ctrl = 0x2f090001;
+ break;
+ case HSC_UCODE_ACE:
+ ucode = &chip->ucode_am;
+ cip_f_ctrl = 0x3f090001;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(r, CIP_F_CTRL, cip_f_ctrl);
+
+ dma_p.cipr_start = ucode->phys_code;
+ dma_p.cipw_start = 0;
+ dma_p.inter_size = ucode->size_code;
+ dma_p.total_size = ucode->size_code;
+ dma_p.key_id1 = 0;
+ dma_p.key_id0 = 0;
+ dma_p.endian = 1;
+ dma_p.id1_en = 0;
+ file_channel_dma_set(chip, spec_r, spec_w, &dma_p);
+ file_channel_start(chip, spec_r, spec_w, true, false);
+
+ file_channel_wait(chip, spec_r);
+ file_channel_wait(chip, spec_w);
+
+ return 0;
+}
+
+static int ucode_load(struct hsc_chip *chip, int mode)
+{
+ struct device *dev = &chip->pdev->dev;
+ const struct hsc_spec_ucode *spec;
+ struct hsc_ucode_buf *ucode;
+ const struct firmware *firm_code, *firm_data;
+ int ret;
+
+ switch (mode) {
+ case HSC_UCODE_SPU_0:
+ case HSC_UCODE_SPU_1:
+ spec = &chip->spec->ucode_spu;
+ ucode = &chip->ucode_spu;
+ break;
+ case HSC_UCODE_ACE:
+ spec = &chip->spec->ucode_ace;
+ ucode = &chip->ucode_am;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = request_firmware(&firm_code, spec->name_code, dev);
+ if (ret) {
+ dev_err(dev, "Failed to load firmware '%s'.\n",
+ spec->name_code);
+ return ret;
+ }
+
+ ret = request_firmware(&firm_data, spec->name_data, dev);
+ if (ret) {
+ dev_err(dev, "Failed to load firmware '%s'.\n",
+ spec->name_data);
+ goto err_firm_code;
+ }
+
+ ucode->buf_code = dma_alloc_coherent(dev, firm_code->size,
+ &ucode->phys_code, GFP_KERNEL);
+ if (!ucode->buf_code) {
+ ret = -ENOMEM;
+ goto err_firm_data;
+ }
+ ucode->size_code = firm_code->size;
+
+ ucode->buf_data = dma_alloc_coherent(dev, firm_data->size,
+ &ucode->phys_data, GFP_KERNEL);
+ if (!ucode->buf_data) {
+ ret = -ENOMEM;
+ goto err_buf_code;
+ }
+ ucode->size_data = firm_data->size;
+
+ memcpy(ucode->buf_code, firm_code->data, firm_code->size);
+ memcpy(ucode->buf_data, firm_data->data, firm_data->size);
+
+ ret = ucode_set_data_addr(chip, mode);
+ if (ret)
+ goto err_buf_data;
+
+ ret = ucode_load_dma(chip, mode);
+ if (ret)
+ goto err_buf_data;
+
+ release_firmware(firm_data);
+ release_firmware(firm_code);
+
+ return 0;
+
+err_buf_data:
+ dma_free_coherent(dev, ucode->size_data, ucode->buf_data,
+ ucode->phys_data);
+
+err_buf_code:
+ dma_free_coherent(dev, ucode->size_code, ucode->buf_code,
+ ucode->phys_code);
+
+err_firm_data:
+ release_firmware(firm_data);
+
+err_firm_code:
+ release_firmware(firm_code);
+
+ return ret;
+}
+
+static int ucode_unload(struct hsc_chip *chip, int mode)
+{
+ struct device *dev = &chip->pdev->dev;
+ struct hsc_ucode_buf *ucode;
+
+ switch (mode) {
+ case HSC_UCODE_SPU_0:
+ case HSC_UCODE_SPU_1:
+ ucode = &chip->ucode_spu;
+ break;
+ case HSC_UCODE_ACE:
+ ucode = &chip->ucode_am;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dma_free_coherent(dev, ucode->size_data, ucode->buf_data,
+ ucode->phys_data);
+ dma_free_coherent(dev, ucode->size_code, ucode->buf_code,
+ ucode->phys_code);
+
+ return 0;
+}
+
+int hsc_ucode_load_all(struct hsc_chip *chip)
+{
+ struct regmap *r = chip->regmap;
+ int ret;
+
+ core_start(chip);
+
+ ret = ucode_load(chip, HSC_UCODE_SPU_0);
+ if (ret)
+ return ret;
+
+ /* Start SPU core */
+ regmap_write(r, IOB_DEBUG, 0);
+
+ ret = ucode_load(chip, HSC_UCODE_ACE);
+ if (ret)
+ return ret;
+
+ /* Start AP core */
+ regmap_write(r, IOB_RESET0, 0);
+
+ return 0;
+}
+
+int hsc_ucode_unload_all(struct hsc_chip *chip)
+{
+ int ret;
+
+ core_stop(chip);
+
+ ret = ucode_unload(chip, HSC_UCODE_SPU_0);
+ if (ret)
+ return ret;
+
+ ret = ucode_unload(chip, HSC_UCODE_ACE);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/media/platform/uniphier/hsc.h b/drivers/media/platform/uniphier/hsc.h
index a10b7a480193..bbfd90ffaad5 100644
--- a/drivers/media/platform/uniphier/hsc.h
+++ b/drivers/media/platform/uniphier/hsc.h
@@ -372,4 +372,8 @@ void hsc_dma_out_sync(struct hsc_dma *dma_out);
int hsc_dma_out_get_intr(struct hsc_dma *dma_out, u32 *stat);
void hsc_dma_out_clear_intr(struct hsc_dma *dma_out, u32 clear);

+/* UCODE DL */
+int hsc_ucode_load_all(struct hsc_chip *chip);
+int hsc_ucode_unload_all(struct hsc_chip *chip);
+
#endif /* DVB_UNIPHIER_HSC_H__ */
--
2.18.0