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

Message ID 20180808052519.14528-6-suzuki.katsuhiro@socionext.com (mailing list archive)
State Under Review, archived
Delegated to: Mauro Carvalho Chehab
Headers

Commit Message

Katsuhiro Suzuki Aug. 8, 2018, 5:25 a.m. UTC
  Adds code to load uCode and start the internal cores of HSC for
Socionext UniPhier SoCs.

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

---

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
  

Patch

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__ */