* [PATCH v3 3/3] drm: zte: add overlay plane support
From: Shawn Guo @ 2016-12-29 2:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482979048-32037-1-git-send-email-shawnguo@kernel.org>
From: Shawn Guo <shawn.guo@linaro.org>
It enables VOU VL (Video Layer) to support overlay plane with scaling
function. VL0 has some quirks on scaling support. We choose to skip it
and only adds VL1 and VL2 into DRM core for now.
Function zx_plane_atomic_disable() gets moved around with no changes to
save a forward declaration.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
drivers/gpu/drm/zte/zx_plane.c | 311 +++++++++++++++++++++++++++++++++---
drivers/gpu/drm/zte/zx_plane_regs.h | 51 ++++++
drivers/gpu/drm/zte/zx_vou.c | 80 +++++++++-
drivers/gpu/drm/zte/zx_vou_regs.h | 18 +++
4 files changed, 431 insertions(+), 29 deletions(-)
diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c
index 5445eebf830f..c5ac42647735 100644
--- a/drivers/gpu/drm/zte/zx_plane.c
+++ b/drivers/gpu/drm/zte/zx_plane.c
@@ -30,6 +30,275 @@
DRM_FORMAT_ARGB4444,
};
+static const uint32_t vl_formats[] = {
+ DRM_FORMAT_NV12, /* Semi-planar YUV420 */
+ DRM_FORMAT_YUV420, /* Planar YUV420 */
+ DRM_FORMAT_YUYV, /* Packed YUV422 */
+ DRM_FORMAT_YVYU,
+ DRM_FORMAT_UYVY,
+ DRM_FORMAT_VYUY,
+ DRM_FORMAT_YUV444, /* YUV444 8bit */
+ /*
+ * TODO: add formats below that HW supports:
+ * - YUV420 P010
+ * - YUV420 Hantro
+ * - YUV444 10bit
+ */
+};
+
+#define FRAC_16_16(mult, div) (((mult) << 16) / (div))
+
+static int zx_vl_plane_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *plane_state)
+{
+ struct drm_framebuffer *fb = plane_state->fb;
+ struct drm_crtc *crtc = plane_state->crtc;
+ struct drm_crtc_state *crtc_state;
+ struct drm_rect clip;
+ int min_scale = FRAC_16_16(1, 8);
+ int max_scale = FRAC_16_16(8, 1);
+
+ if (!crtc || !fb)
+ return 0;
+
+ crtc_state = drm_atomic_get_existing_crtc_state(plane_state->state,
+ crtc);
+ if (WARN_ON(!crtc_state))
+ return -EINVAL;
+
+ /* nothing to check when disabling or disabled */
+ if (!crtc_state->enable)
+ return 0;
+
+ /* plane must be enabled */
+ if (!plane_state->crtc)
+ return -EINVAL;
+
+ clip.x1 = 0;
+ clip.y1 = 0;
+ clip.x2 = crtc_state->adjusted_mode.hdisplay;
+ clip.y2 = crtc_state->adjusted_mode.vdisplay;
+
+ return drm_plane_helper_check_state(plane_state, &clip,
+ min_scale, max_scale,
+ true, true);
+}
+
+static u32 zx_vl_get_fmt(uint32_t format)
+{
+ u32 val = 0;
+
+ switch (format) {
+ case DRM_FORMAT_NV12:
+ val = VL_FMT_YUV420;
+ break;
+ case DRM_FORMAT_YUV420:
+ val = VL_YUV420_PLANAR | VL_FMT_YUV420;
+ break;
+ case DRM_FORMAT_YUYV:
+ val = VL_YUV422_YUYV | VL_FMT_YUV422;
+ break;
+ case DRM_FORMAT_YVYU:
+ val = VL_YUV422_YVYU | VL_FMT_YUV422;
+ break;
+ case DRM_FORMAT_UYVY:
+ val = VL_YUV422_UYVY | VL_FMT_YUV422;
+ break;
+ case DRM_FORMAT_VYUY:
+ val = VL_YUV422_VYUY | VL_FMT_YUV422;
+ break;
+ case DRM_FORMAT_YUV444:
+ val = VL_FMT_YUV444_8BIT;
+ break;
+ default:
+ WARN_ONCE(1, "invalid pixel format %d\n", format);
+ }
+
+ return val;
+}
+
+static inline void zx_vl_set_update(struct zx_plane *zplane)
+{
+ void __iomem *layer = zplane->layer;
+
+ zx_writel_mask(layer + VL_CTRL0, VL_UPDATE, VL_UPDATE);
+}
+
+static inline void zx_vl_rsz_set_update(struct zx_plane *zplane)
+{
+ zx_writel(zplane->rsz + RSZ_VL_ENABLE_CFG, 1);
+}
+
+static u32 zx_vl_rsz_get_fmt(uint32_t format)
+{
+ u32 val = 0;
+
+ switch (format) {
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_YUV420:
+ val = RSZ_VL_FMT_YCBCR420;
+ break;
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YVYU:
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_VYUY:
+ val = RSZ_VL_FMT_YCBCR422;
+ break;
+ case DRM_FORMAT_YUV444:
+ val = RSZ_VL_FMT_YCBCR444;
+ break;
+ default:
+ WARN_ONCE(1, "invalid pixel format %d\n", format);
+ }
+
+ return val;
+}
+
+static inline u32 rsz_step_value(u32 src, u32 dst)
+{
+ u32 val = 0;
+
+ if (src == dst)
+ val = 0;
+ else if (src < dst)
+ val = RSZ_PARA_STEP((src << 16) / dst);
+ else if (src > dst)
+ val = RSZ_DATA_STEP(src / dst) |
+ RSZ_PARA_STEP(((src << 16) / dst) & 0xffff);
+
+ return val;
+}
+
+static void zx_vl_rsz_setup(struct zx_plane *zplane, uint32_t format,
+ u32 src_w, u32 src_h, u32 dst_w, u32 dst_h)
+{
+ void __iomem *rsz = zplane->rsz;
+ u32 src_chroma_w = src_w;
+ u32 src_chroma_h = src_h;
+ u32 fmt;
+
+ /* Set up source and destination resolution */
+ zx_writel(rsz + RSZ_SRC_CFG, RSZ_VER(src_h - 1) | RSZ_HOR(src_w - 1));
+ zx_writel(rsz + RSZ_DEST_CFG, RSZ_VER(dst_h - 1) | RSZ_HOR(dst_w - 1));
+
+ /* Configure data format for VL RSZ */
+ fmt = zx_vl_rsz_get_fmt(format);
+ zx_writel_mask(rsz + RSZ_VL_CTRL_CFG, RSZ_VL_FMT_MASK, fmt);
+
+ /* Calculate Chroma heigth and width */
+ if (fmt == RSZ_VL_FMT_YCBCR420) {
+ src_chroma_w = src_w >> 1;
+ src_chroma_h = src_h >> 1;
+ } else if (fmt == RSZ_VL_FMT_YCBCR422) {
+ src_chroma_w = src_w >> 1;
+ }
+
+ /* Set up Luma and Chroma step registers */
+ zx_writel(rsz + RSZ_VL_LUMA_HOR, rsz_step_value(src_w, dst_w));
+ zx_writel(rsz + RSZ_VL_LUMA_VER, rsz_step_value(src_h, dst_h));
+ zx_writel(rsz + RSZ_VL_CHROMA_HOR, rsz_step_value(src_chroma_w, dst_w));
+ zx_writel(rsz + RSZ_VL_CHROMA_VER, rsz_step_value(src_chroma_h, dst_h));
+
+ zx_vl_rsz_set_update(zplane);
+}
+
+static void zx_vl_plane_atomic_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct zx_plane *zplane = to_zx_plane(plane);
+ struct drm_plane_state *state = plane->state;
+ struct drm_framebuffer *fb = state->fb;
+ struct drm_rect *src = &state->src;
+ struct drm_rect *dst = &state->dst;
+ struct drm_gem_cma_object *cma_obj;
+ void __iomem *layer = zplane->layer;
+ void __iomem *hbsc = zplane->hbsc;
+ void __iomem *paddr_reg;
+ dma_addr_t paddr;
+ u32 src_x, src_y, src_w, src_h;
+ u32 dst_x, dst_y, dst_w, dst_h;
+ uint32_t format;
+ u32 fmt;
+ int num_planes;
+ int i;
+
+ if (!fb)
+ return;
+
+ format = fb->pixel_format;
+
+ src_x = src->x1 >> 16;
+ src_y = src->y1 >> 16;
+ src_w = drm_rect_width(src) >> 16;
+ src_h = drm_rect_height(src) >> 16;
+
+ dst_x = dst->x1;
+ dst_y = dst->y1;
+ dst_w = drm_rect_width(dst);
+ dst_h = drm_rect_height(dst);
+
+ /* Set up data address registers for Y, Cb and Cr planes */
+ num_planes = drm_format_num_planes(format);
+ paddr_reg = layer + VL_Y;
+ for (i = 0; i < num_planes; i++) {
+ cma_obj = drm_fb_cma_get_gem_obj(fb, i);
+ paddr = cma_obj->paddr + fb->offsets[i];
+ paddr += src_y * fb->pitches[i];
+ paddr += src_x * drm_format_plane_cpp(format, i);
+ zx_writel(paddr_reg, paddr);
+ paddr_reg += 4;
+ }
+
+ /* Set up source height/width register */
+ zx_writel(layer + VL_SRC_SIZE, GL_SRC_W(src_w) | GL_SRC_H(src_h));
+
+ /* Set up start position register */
+ zx_writel(layer + VL_POS_START, GL_POS_X(dst_x) | GL_POS_Y(dst_y));
+
+ /* Set up end position register */
+ zx_writel(layer + VL_POS_END,
+ GL_POS_X(dst_x + dst_w) | GL_POS_Y(dst_y + dst_h));
+
+ /* Strides of Cb and Cr planes should be identical */
+ zx_writel(layer + VL_STRIDE, LUMA_STRIDE(fb->pitches[0]) |
+ CHROMA_STRIDE(fb->pitches[1]));
+
+ /* Set up video layer data format */
+ fmt = zx_vl_get_fmt(format);
+ zx_writel(layer + VL_CTRL1, fmt);
+
+ /* Always use scaler since it exists (set for not bypass) */
+ zx_writel_mask(layer + VL_CTRL2, VL_SCALER_BYPASS_MODE,
+ VL_SCALER_BYPASS_MODE);
+
+ zx_vl_rsz_setup(zplane, format, src_w, src_h, dst_w, dst_h);
+
+ /* Enable HBSC block */
+ zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, HBSC_CTRL_EN);
+
+ zx_vou_layer_enable(plane);
+
+ zx_vl_set_update(zplane);
+}
+
+static void zx_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct zx_plane *zplane = to_zx_plane(plane);
+ void __iomem *hbsc = zplane->hbsc;
+
+ zx_vou_layer_disable(plane);
+
+ /* Disable HBSC block */
+ zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, 0);
+}
+
+static const struct drm_plane_helper_funcs zx_vl_plane_helper_funcs = {
+ .atomic_check = zx_vl_plane_atomic_check,
+ .atomic_update = zx_vl_plane_atomic_update,
+ .atomic_disable = zx_plane_atomic_disable,
+};
+
static int zx_gl_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *plane_state)
{
@@ -97,14 +366,6 @@ static inline void zx_gl_rsz_set_update(struct zx_plane *zplane)
zx_writel(zplane->rsz + RSZ_ENABLE_CFG, 1);
}
-void zx_plane_set_update(struct drm_plane *plane)
-{
- struct zx_plane *zplane = to_zx_plane(plane);
-
- zx_gl_rsz_set_update(zplane);
- zx_gl_set_update(zplane);
-}
-
static void zx_gl_rsz_setup(struct zx_plane *zplane, u32 src_w, u32 src_h,
u32 dst_w, u32 dst_h)
{
@@ -202,18 +463,6 @@ static void zx_gl_plane_atomic_update(struct drm_plane *plane,
zx_gl_set_update(zplane);
}
-static void zx_plane_atomic_disable(struct drm_plane *plane,
- struct drm_plane_state *old_state)
-{
- struct zx_plane *zplane = to_zx_plane(plane);
- void __iomem *hbsc = zplane->hbsc;
-
- zx_vou_layer_disable(plane);
-
- /* Disable HBSC block */
- zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, 0);
-}
-
static const struct drm_plane_helper_funcs zx_gl_plane_helper_funcs = {
.atomic_check = zx_gl_plane_atomic_check,
.atomic_update = zx_gl_plane_atomic_update,
@@ -235,6 +484,24 @@ static void zx_plane_destroy(struct drm_plane *plane)
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
+void zx_plane_set_update(struct drm_plane *plane)
+{
+ struct zx_plane *zplane = to_zx_plane(plane);
+
+ switch (plane->type) {
+ case DRM_PLANE_TYPE_PRIMARY:
+ zx_gl_rsz_set_update(zplane);
+ zx_gl_set_update(zplane);
+ break;
+ case DRM_PLANE_TYPE_OVERLAY:
+ zx_vl_rsz_set_update(zplane);
+ zx_vl_set_update(zplane);
+ break;
+ default:
+ WARN_ONCE(1, "unsupported plane type %d\n", plane->type);
+ }
+}
+
static void zx_plane_hbsc_init(struct zx_plane *zplane)
{
void __iomem *hbsc = zplane->hbsc;
@@ -272,7 +539,9 @@ int zx_plane_init(struct drm_device *drm, struct zx_plane *zplane,
format_count = ARRAY_SIZE(gl_formats);
break;
case DRM_PLANE_TYPE_OVERLAY:
- /* TODO: add video layer (vl) support */
+ helper = &zx_vl_plane_helper_funcs;
+ formats = vl_formats;
+ format_count = ARRAY_SIZE(vl_formats);
break;
default:
return -ENODEV;
diff --git a/drivers/gpu/drm/zte/zx_plane_regs.h b/drivers/gpu/drm/zte/zx_plane_regs.h
index 3dde6716a558..65f271aeabed 100644
--- a/drivers/gpu/drm/zte/zx_plane_regs.h
+++ b/drivers/gpu/drm/zte/zx_plane_regs.h
@@ -46,6 +46,37 @@
#define GL_POS_X(x) (((x) << GL_POS_X_SHIFT) & GL_POS_X_MASK)
#define GL_POS_Y(x) (((x) << GL_POS_Y_SHIFT) & GL_POS_Y_MASK)
+/* VL registers */
+#define VL_CTRL0 0x00
+#define VL_UPDATE BIT(3)
+#define VL_CTRL1 0x04
+#define VL_YUV420_PLANAR BIT(5)
+#define VL_YUV422_SHIFT 3
+#define VL_YUV422_YUYV (0 << VL_YUV422_SHIFT)
+#define VL_YUV422_YVYU (1 << VL_YUV422_SHIFT)
+#define VL_YUV422_UYVY (2 << VL_YUV422_SHIFT)
+#define VL_YUV422_VYUY (3 << VL_YUV422_SHIFT)
+#define VL_FMT_YUV420 0
+#define VL_FMT_YUV422 1
+#define VL_FMT_YUV420_P010 2
+#define VL_FMT_YUV420_HANTRO 3
+#define VL_FMT_YUV444_8BIT 4
+#define VL_FMT_YUV444_10BIT 5
+#define VL_CTRL2 0x08
+#define VL_SCALER_BYPASS_MODE BIT(0)
+#define VL_STRIDE 0x0c
+#define LUMA_STRIDE_SHIFT 16
+#define LUMA_STRIDE_MASK (0xffff << LUMA_STRIDE_SHIFT)
+#define CHROMA_STRIDE_SHIFT 0
+#define CHROMA_STRIDE_MASK (0xffff << CHROMA_STRIDE_SHIFT)
+#define VL_SRC_SIZE 0x10
+#define VL_Y 0x14
+#define VL_POS_START 0x30
+#define VL_POS_END 0x34
+
+#define LUMA_STRIDE(x) (((x) << LUMA_STRIDE_SHIFT) & LUMA_STRIDE_MASK)
+#define CHROMA_STRIDE(x) (((x) << CHROMA_STRIDE_SHIFT) & CHROMA_STRIDE_MASK)
+
/* CSC registers */
#define CSC_CTRL0 0x30
#define CSC_COV_MODE_SHIFT 16
@@ -69,6 +100,18 @@
#define RSZ_DEST_CFG 0x04
#define RSZ_ENABLE_CFG 0x14
+#define RSZ_VL_LUMA_HOR 0x08
+#define RSZ_VL_LUMA_VER 0x0c
+#define RSZ_VL_CHROMA_HOR 0x10
+#define RSZ_VL_CHROMA_VER 0x14
+#define RSZ_VL_CTRL_CFG 0x18
+#define RSZ_VL_FMT_SHIFT 3
+#define RSZ_VL_FMT_MASK (0x3 << RSZ_VL_FMT_SHIFT)
+#define RSZ_VL_FMT_YCBCR420 (0x0 << RSZ_VL_FMT_SHIFT)
+#define RSZ_VL_FMT_YCBCR422 (0x1 << RSZ_VL_FMT_SHIFT)
+#define RSZ_VL_FMT_YCBCR444 (0x2 << RSZ_VL_FMT_SHIFT)
+#define RSZ_VL_ENABLE_CFG 0x1c
+
#define RSZ_VER_SHIFT 16
#define RSZ_VER_MASK (0xffff << RSZ_VER_SHIFT)
#define RSZ_HOR_SHIFT 0
@@ -77,6 +120,14 @@
#define RSZ_VER(x) (((x) << RSZ_VER_SHIFT) & RSZ_VER_MASK)
#define RSZ_HOR(x) (((x) << RSZ_HOR_SHIFT) & RSZ_HOR_MASK)
+#define RSZ_DATA_STEP_SHIFT 16
+#define RSZ_DATA_STEP_MASK (0xffff << RSZ_DATA_STEP_SHIFT)
+#define RSZ_PARA_STEP_SHIFT 0
+#define RSZ_PARA_STEP_MASK (0xffff << RSZ_PARA_STEP_SHIFT)
+
+#define RSZ_DATA_STEP(x) (((x) << RSZ_DATA_STEP_SHIFT) & RSZ_DATA_STEP_MASK)
+#define RSZ_PARA_STEP(x) (((x) << RSZ_PARA_STEP_SHIFT) & RSZ_PARA_STEP_MASK)
+
/* HBSC registers */
#define HBSC_SATURATION 0x00
#define HBSC_HUE 0x04
diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
index 3fb4fc04e693..e832c2ec3156 100644
--- a/drivers/gpu/drm/zte/zx_vou.c
+++ b/drivers/gpu/drm/zte/zx_vou.c
@@ -84,6 +84,8 @@ struct zx_crtc_bits {
struct zx_crtc {
struct drm_crtc crtc;
struct drm_plane *primary;
+ struct drm_plane *overlay_active[VL_NUM];
+ unsigned int overlay_active_num;
struct zx_vou_hw *vou;
void __iomem *chnreg;
const struct zx_crtc_regs *regs;
@@ -112,6 +114,22 @@ struct vou_layer_bits {
},
};
+static const struct vou_layer_bits zx_vl_bits[VL_NUM] = {
+ {
+ .enable = OSD_CTRL0_VL0_EN,
+ .chnsel = OSD_CTRL0_VL0_SEL,
+ .clksel = VOU_CLK_VL0_SEL,
+ }, {
+ .enable = OSD_CTRL0_VL1_EN,
+ .chnsel = OSD_CTRL0_VL1_SEL,
+ .clksel = VOU_CLK_VL1_SEL,
+ }, {
+ .enable = OSD_CTRL0_VL2_EN,
+ .chnsel = OSD_CTRL0_VL2_SEL,
+ .clksel = VOU_CLK_VL2_SEL,
+ },
+};
+
struct zx_vou_hw {
struct device *dev;
void __iomem *osd;
@@ -439,6 +457,9 @@ void zx_vou_layer_enable(struct drm_plane *plane)
}
zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, bits->enable);
+
+ if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+ zcrtc->overlay_active[zcrtc->overlay_active_num++] = plane;
}
void zx_vou_layer_disable(struct drm_plane *plane)
@@ -449,6 +470,51 @@ void zx_vou_layer_disable(struct drm_plane *plane)
const struct vou_layer_bits *bits = zplane->bits;
zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, 0);
+
+ if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+ zcrtc->overlay_active[zcrtc->overlay_active_num--] = NULL;
+}
+
+static void zx_overlay_init(struct drm_device *drm, struct zx_vou_hw *vou)
+{
+ struct device *dev = vou->dev;
+ struct zx_plane *zplane;
+ int i;
+ int ret;
+
+ /*
+ * VL0 has some quirks on scaling support which need special handling.
+ * Let's leave it out for now.
+ */
+ for (i = 1; i < VL_NUM; i++) {
+ zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL);
+ if (!zplane) {
+ DRM_DEV_ERROR(dev, "failed to allocate zplane %d\n", i);
+ return;
+ }
+
+ zplane->layer = vou->osd + OSD_VL_OFFSET(i);
+ zplane->hbsc = vou->osd + HBSC_VL_OFFSET(i);
+ zplane->rsz = vou->otfppu + RSZ_VL_OFFSET(i);
+ zplane->bits = &zx_vl_bits[i];
+
+ ret = zx_plane_init(drm, zplane, DRM_PLANE_TYPE_OVERLAY);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to init overlay %d\n", i);
+ continue;
+ }
+ }
+}
+
+static inline void zx_osd_int_update(struct zx_crtc *zcrtc)
+{
+ int i;
+
+ vou_chn_set_update(zcrtc);
+ zx_plane_set_update(zcrtc->primary);
+
+ for (i = 0; i < zcrtc->overlay_active_num; i++)
+ zx_plane_set_update(zcrtc->overlay_active[i]);
}
static irqreturn_t vou_irq_handler(int irq, void *dev_id)
@@ -470,15 +536,11 @@ static irqreturn_t vou_irq_handler(int irq, void *dev_id)
state = zx_readl(vou->osd + OSD_INT_STA);
zx_writel(vou->osd + OSD_INT_CLRSTA, state);
- if (state & OSD_INT_MAIN_UPT) {
- vou_chn_set_update(vou->main_crtc);
- zx_plane_set_update(vou->main_crtc->primary);
- }
+ if (state & OSD_INT_MAIN_UPT)
+ zx_osd_int_update(vou->main_crtc);
- if (state & OSD_INT_AUX_UPT) {
- vou_chn_set_update(vou->aux_crtc);
- zx_plane_set_update(vou->aux_crtc->primary);
- }
+ if (state & OSD_INT_AUX_UPT)
+ zx_osd_int_update(vou->aux_crtc);
if (state & OSD_INT_ERROR)
DRM_DEV_ERROR(vou->dev, "OSD ERROR: 0x%08x!\n", state);
@@ -648,6 +710,8 @@ static int zx_crtc_bind(struct device *dev, struct device *master, void *data)
goto disable_ppu_clk;
}
+ zx_overlay_init(drm, vou);
+
return 0;
disable_ppu_clk:
diff --git a/drivers/gpu/drm/zte/zx_vou_regs.h b/drivers/gpu/drm/zte/zx_vou_regs.h
index f44e7a4ae441..193c1ce01fe7 100644
--- a/drivers/gpu/drm/zte/zx_vou_regs.h
+++ b/drivers/gpu/drm/zte/zx_vou_regs.h
@@ -22,6 +22,15 @@
#define AUX_HBSC_OFFSET 0x860
#define AUX_RSZ_OFFSET 0x800
+#define OSD_VL0_OFFSET 0x040
+#define OSD_VL_OFFSET(i) (OSD_VL0_OFFSET + 0x050 * (i))
+
+#define HBSC_VL0_OFFSET 0x760
+#define HBSC_VL_OFFSET(i) (HBSC_VL0_OFFSET + 0x040 * (i))
+
+#define RSZ_VL1_U0 0xa00
+#define RSZ_VL_OFFSET(i) (RSZ_VL1_U0 + 0x200 * (i))
+
/* OSD (GPC_GLOBAL) registers */
#define OSD_INT_STA 0x04
#define OSD_INT_CLRSTA 0x08
@@ -42,6 +51,12 @@
)
#define OSD_INT_ENABLE (OSD_INT_ERROR | OSD_INT_AUX_UPT | OSD_INT_MAIN_UPT)
#define OSD_CTRL0 0x10
+#define OSD_CTRL0_VL0_EN BIT(13)
+#define OSD_CTRL0_VL0_SEL BIT(12)
+#define OSD_CTRL0_VL1_EN BIT(11)
+#define OSD_CTRL0_VL1_SEL BIT(10)
+#define OSD_CTRL0_VL2_EN BIT(9)
+#define OSD_CTRL0_VL2_SEL BIT(8)
#define OSD_CTRL0_GL0_EN BIT(7)
#define OSD_CTRL0_GL0_SEL BIT(6)
#define OSD_CTRL0_GL1_EN BIT(5)
@@ -146,6 +161,9 @@
#define VOU_INF_DATA_SEL 0x08
#define VOU_SOFT_RST 0x14
#define VOU_CLK_SEL 0x18
+#define VOU_CLK_VL2_SEL BIT(8)
+#define VOU_CLK_VL1_SEL BIT(7)
+#define VOU_CLK_VL0_SEL BIT(6)
#define VOU_CLK_GL1_SEL BIT(5)
#define VOU_CLK_GL0_SEL BIT(4)
#define VOU_CLK_REQEN 0x20
--
1.9.1
^ permalink raw reply related
* [PATCH v3 2/3] drm: zte: add .atomic_disable hook to disable graphic layer
From: Shawn Guo @ 2016-12-29 2:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482979048-32037-1-git-send-email-shawnguo@kernel.org>
From: Shawn Guo <shawn.guo@linaro.org>
There are a few hardware bits for each graphic layer to control main/aux
channel and clock selection, as well as the layer enabling. These bits
sit outside the layer block itself, but in VOU control glue block. We
currently set these bits up at CRTC initialization for once, and do not
support disabling the layer.
This patch creates a pair of functions zx_vou_layer_enable[disable] to
be invoked from plane hooks .atomic_update and .atomic_disable to set up
and tear down the layer. This is generic for both graphic and video
layers, so it will make the overlay plane support to be added later much
easier.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
drivers/gpu/drm/zte/zx_plane.c | 15 +++++++++
drivers/gpu/drm/zte/zx_plane.h | 1 +
drivers/gpu/drm/zte/zx_vou.c | 70 ++++++++++++++++++++++++++++++------------
drivers/gpu/drm/zte/zx_vou.h | 3 ++
4 files changed, 69 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c
index 78d29b1db91c..5445eebf830f 100644
--- a/drivers/gpu/drm/zte/zx_plane.c
+++ b/drivers/gpu/drm/zte/zx_plane.c
@@ -197,12 +197,27 @@ static void zx_gl_plane_atomic_update(struct drm_plane *plane,
/* Enable HBSC block */
zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, HBSC_CTRL_EN);
+ zx_vou_layer_enable(plane);
+
zx_gl_set_update(zplane);
}
+static void zx_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct zx_plane *zplane = to_zx_plane(plane);
+ void __iomem *hbsc = zplane->hbsc;
+
+ zx_vou_layer_disable(plane);
+
+ /* Disable HBSC block */
+ zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, 0);
+}
+
static const struct drm_plane_helper_funcs zx_gl_plane_helper_funcs = {
.atomic_check = zx_gl_plane_atomic_check,
.atomic_update = zx_gl_plane_atomic_update,
+ .atomic_disable = zx_plane_atomic_disable,
};
static void zx_plane_destroy(struct drm_plane *plane)
diff --git a/drivers/gpu/drm/zte/zx_plane.h b/drivers/gpu/drm/zte/zx_plane.h
index 264a92e0b532..933611ddffd0 100644
--- a/drivers/gpu/drm/zte/zx_plane.h
+++ b/drivers/gpu/drm/zte/zx_plane.h
@@ -18,6 +18,7 @@ struct zx_plane {
void __iomem *csc;
void __iomem *hbsc;
void __iomem *rsz;
+ const struct vou_layer_bits *bits;
};
#define to_zx_plane(plane) container_of(plane, struct zx_plane, plane)
diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
index d5c801f6f97b..3fb4fc04e693 100644
--- a/drivers/gpu/drm/zte/zx_vou.c
+++ b/drivers/gpu/drm/zte/zx_vou.c
@@ -65,7 +65,6 @@ struct zx_crtc_bits {
u32 polarity_shift;
u32 int_frame_mask;
u32 tc_enable;
- u32 gl_enable;
};
static const struct zx_crtc_bits main_crtc_bits = {
@@ -73,7 +72,6 @@ struct zx_crtc_bits {
.polarity_shift = MAIN_POL_SHIFT,
.int_frame_mask = TIMING_INT_MAIN_FRAME,
.tc_enable = MAIN_TC_EN,
- .gl_enable = OSD_CTRL0_GL0_EN,
};
static const struct zx_crtc_bits aux_crtc_bits = {
@@ -81,7 +79,6 @@ struct zx_crtc_bits {
.polarity_shift = AUX_POL_SHIFT,
.int_frame_mask = TIMING_INT_AUX_FRAME,
.tc_enable = AUX_TC_EN,
- .gl_enable = OSD_CTRL0_GL1_EN,
};
struct zx_crtc {
@@ -97,6 +94,24 @@ struct zx_crtc {
#define to_zx_crtc(x) container_of(x, struct zx_crtc, crtc)
+struct vou_layer_bits {
+ u32 enable;
+ u32 chnsel;
+ u32 clksel;
+};
+
+static const struct vou_layer_bits zx_gl_bits[GL_NUM] = {
+ {
+ .enable = OSD_CTRL0_GL0_EN,
+ .chnsel = OSD_CTRL0_GL0_SEL,
+ .clksel = VOU_CLK_GL0_SEL,
+ }, {
+ .enable = OSD_CTRL0_GL1_EN,
+ .chnsel = OSD_CTRL0_GL1_SEL,
+ .clksel = VOU_CLK_GL1_SEL,
+ },
+};
+
struct zx_vou_hw {
struct device *dev;
void __iomem *osd;
@@ -220,10 +235,6 @@ static void zx_crtc_enable(struct drm_crtc *crtc)
/* Enable channel */
zx_writel_mask(zcrtc->chnreg + CHN_CTRL0, CHN_ENABLE, CHN_ENABLE);
- /* Enable Graphic Layer */
- zx_writel_mask(vou->osd + OSD_CTRL0, bits->gl_enable,
- bits->gl_enable);
-
drm_crtc_vblank_on(crtc);
ret = clk_set_rate(zcrtc->pixclk, mode->clock * 1000);
@@ -247,9 +258,6 @@ static void zx_crtc_disable(struct drm_crtc *crtc)
drm_crtc_vblank_off(crtc);
- /* Disable Graphic Layer */
- zx_writel_mask(vou->osd + OSD_CTRL0, bits->gl_enable, 0);
-
/* Disable channel */
zx_writel_mask(zcrtc->chnreg + CHN_CTRL0, CHN_ENABLE, 0);
@@ -316,6 +324,7 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou,
zplane->csc = vou->osd + MAIN_CSC_OFFSET;
zplane->hbsc = vou->osd + MAIN_HBSC_OFFSET;
zplane->rsz = vou->otfppu + MAIN_RSZ_OFFSET;
+ zplane->bits = &zx_gl_bits[0];
zcrtc->chnreg = vou->osd + OSD_MAIN_CHN;
zcrtc->regs = &main_crtc_regs;
zcrtc->bits = &main_crtc_bits;
@@ -324,6 +333,7 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou,
zplane->csc = vou->osd + AUX_CSC_OFFSET;
zplane->hbsc = vou->osd + AUX_HBSC_OFFSET;
zplane->rsz = vou->otfppu + AUX_RSZ_OFFSET;
+ zplane->bits = &zx_gl_bits[1];
zcrtc->chnreg = vou->osd + OSD_AUX_CHN;
zcrtc->regs = &aux_crtc_regs;
zcrtc->bits = &aux_crtc_bits;
@@ -411,6 +421,36 @@ void zx_vou_disable_vblank(struct drm_device *drm, unsigned int pipe)
zcrtc->bits->int_frame_mask, 0);
}
+void zx_vou_layer_enable(struct drm_plane *plane)
+{
+ struct zx_crtc *zcrtc = to_zx_crtc(plane->state->crtc);
+ struct zx_vou_hw *vou = zcrtc->vou;
+ struct zx_plane *zplane = to_zx_plane(plane);
+ const struct vou_layer_bits *bits = zplane->bits;
+
+ if (zcrtc->chn_type == VOU_CHN_MAIN) {
+ zx_writel_mask(vou->osd + OSD_CTRL0, bits->chnsel, 0);
+ zx_writel_mask(vou->vouctl + VOU_CLK_SEL, bits->clksel, 0);
+ } else {
+ zx_writel_mask(vou->osd + OSD_CTRL0, bits->chnsel,
+ bits->chnsel);
+ zx_writel_mask(vou->vouctl + VOU_CLK_SEL, bits->clksel,
+ bits->clksel);
+ }
+
+ zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, bits->enable);
+}
+
+void zx_vou_layer_disable(struct drm_plane *plane)
+{
+ struct zx_crtc *zcrtc = to_zx_crtc(plane->crtc);
+ struct zx_vou_hw *vou = zcrtc->vou;
+ struct zx_plane *zplane = to_zx_plane(plane);
+ const struct vou_layer_bits *bits = zplane->bits;
+
+ zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, 0);
+}
+
static irqreturn_t vou_irq_handler(int irq, void *dev_id)
{
struct zx_vou_hw *vou = dev_id;
@@ -469,19 +509,9 @@ static void vou_dtrc_init(struct zx_vou_hw *vou)
static void vou_hw_init(struct zx_vou_hw *vou)
{
- /* Set GL0 to main channel and GL1 to aux channel */
- zx_writel_mask(vou->osd + OSD_CTRL0, OSD_CTRL0_GL0_SEL, 0);
- zx_writel_mask(vou->osd + OSD_CTRL0, OSD_CTRL0_GL1_SEL,
- OSD_CTRL0_GL1_SEL);
-
/* Release reset for all VOU modules */
zx_writel(vou->vouctl + VOU_SOFT_RST, ~0);
- /* Select main clock for GL0 and aux clock for GL1 module */
- zx_writel_mask(vou->vouctl + VOU_CLK_SEL, VOU_CLK_GL0_SEL, 0);
- zx_writel_mask(vou->vouctl + VOU_CLK_SEL, VOU_CLK_GL1_SEL,
- VOU_CLK_GL1_SEL);
-
/* Enable clock auto-gating for all VOU modules */
zx_writel(vou->vouctl + VOU_CLK_REQEN, ~0);
diff --git a/drivers/gpu/drm/zte/zx_vou.h b/drivers/gpu/drm/zte/zx_vou.h
index 349e06cd86f4..4b4339be641b 100644
--- a/drivers/gpu/drm/zte/zx_vou.h
+++ b/drivers/gpu/drm/zte/zx_vou.h
@@ -43,4 +43,7 @@ struct vou_inf {
int zx_vou_enable_vblank(struct drm_device *drm, unsigned int pipe);
void zx_vou_disable_vblank(struct drm_device *drm, unsigned int pipe);
+void zx_vou_layer_enable(struct drm_plane *plane);
+void zx_vou_layer_disable(struct drm_plane *plane);
+
#endif /* __ZX_VOU_H__ */
--
1.9.1
^ permalink raw reply related
* [PATCH v3 1/3] drm: zte: make zx_plane accessible from zx_vou driver
From: Shawn Guo @ 2016-12-29 2:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482979048-32037-1-git-send-email-shawnguo@kernel.org>
From: Shawn Guo <shawn.guo@linaro.org>
Move struct zx_plane from zx_plane.c to zx_plane.h, so that it can be
accessed from zx_vou driver, and we can save the use of struct
zx_layer_data completely. More importantly, those additional data used
by VOU controller to enable/disable graphic and video layers can later
be added and accessed much more easily from zx_vou driver.
While at it, we make two changes to zx_plane_init() interface:
- Encode struct device pointer in zx_plane, so that we do not need to
pass it as a parameter.
- Change return of zx_plane_init() from struct drm_plane pointer to
error code, since we can get the pointer from zx_plane in zx_vou
driver now.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
drivers/gpu/drm/zte/zx_plane.c | 36 +++++++-----------------------------
drivers/gpu/drm/zte/zx_plane.h | 11 +++++++----
drivers/gpu/drm/zte/zx_vou.c | 31 +++++++++++++++++++------------
3 files changed, 33 insertions(+), 45 deletions(-)
diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c
index 546eb92a94e8..78d29b1db91c 100644
--- a/drivers/gpu/drm/zte/zx_plane.c
+++ b/drivers/gpu/drm/zte/zx_plane.c
@@ -21,16 +21,6 @@
#include "zx_plane_regs.h"
#include "zx_vou.h"
-struct zx_plane {
- struct drm_plane plane;
- void __iomem *layer;
- void __iomem *csc;
- void __iomem *hbsc;
- void __iomem *rsz;
-};
-
-#define to_zx_plane(plane) container_of(plane, struct zx_plane, plane)
-
static const uint32_t gl_formats[] = {
DRM_FORMAT_ARGB8888,
DRM_FORMAT_XRGB8888,
@@ -248,28 +238,16 @@ static void zx_plane_hbsc_init(struct zx_plane *zplane)
zx_writel(hbsc + HBSC_THRESHOLD_COL3, (0x3c0 << 16) | 0x40);
}
-struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev,
- struct zx_layer_data *data,
- enum drm_plane_type type)
+int zx_plane_init(struct drm_device *drm, struct zx_plane *zplane,
+ enum drm_plane_type type)
{
const struct drm_plane_helper_funcs *helper;
- struct zx_plane *zplane;
- struct drm_plane *plane;
+ struct drm_plane *plane = &zplane->plane;
+ struct device *dev = zplane->dev;
const uint32_t *formats;
unsigned int format_count;
int ret;
- zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL);
- if (!zplane)
- return ERR_PTR(-ENOMEM);
-
- plane = &zplane->plane;
-
- zplane->layer = data->layer;
- zplane->hbsc = data->hbsc;
- zplane->csc = data->csc;
- zplane->rsz = data->rsz;
-
zx_plane_hbsc_init(zplane);
switch (type) {
@@ -282,7 +260,7 @@ struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev,
/* TODO: add video layer (vl) support */
break;
default:
- return ERR_PTR(-ENODEV);
+ return -ENODEV;
}
ret = drm_universal_plane_init(drm, plane, VOU_CRTC_MASK,
@@ -290,10 +268,10 @@ struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev,
type, NULL);
if (ret) {
DRM_DEV_ERROR(dev, "failed to init universal plane: %d\n", ret);
- return ERR_PTR(ret);
+ return ret;
}
drm_plane_helper_add(plane, helper);
- return plane;
+ return 0;
}
diff --git a/drivers/gpu/drm/zte/zx_plane.h b/drivers/gpu/drm/zte/zx_plane.h
index 2b82cd558d9d..264a92e0b532 100644
--- a/drivers/gpu/drm/zte/zx_plane.h
+++ b/drivers/gpu/drm/zte/zx_plane.h
@@ -11,16 +11,19 @@
#ifndef __ZX_PLANE_H__
#define __ZX_PLANE_H__
-struct zx_layer_data {
+struct zx_plane {
+ struct drm_plane plane;
+ struct device *dev;
void __iomem *layer;
void __iomem *csc;
void __iomem *hbsc;
void __iomem *rsz;
};
-struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev,
- struct zx_layer_data *data,
- enum drm_plane_type type);
+#define to_zx_plane(plane) container_of(plane, struct zx_plane, plane)
+
+int zx_plane_init(struct drm_device *drm, struct zx_plane *zplane,
+ enum drm_plane_type type);
void zx_plane_set_update(struct drm_plane *plane);
#endif /* __ZX_PLANE_H__ */
diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
index 73fe15c17c32..d5c801f6f97b 100644
--- a/drivers/gpu/drm/zte/zx_vou.c
+++ b/drivers/gpu/drm/zte/zx_vou.c
@@ -294,7 +294,7 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou,
enum vou_chn_type chn_type)
{
struct device *dev = vou->dev;
- struct zx_layer_data data;
+ struct zx_plane *zplane;
struct zx_crtc *zcrtc;
int ret;
@@ -305,19 +305,25 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou,
zcrtc->vou = vou;
zcrtc->chn_type = chn_type;
+ zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL);
+ if (!zplane)
+ return -ENOMEM;
+
+ zplane->dev = dev;
+
if (chn_type == VOU_CHN_MAIN) {
- data.layer = vou->osd + MAIN_GL_OFFSET;
- data.csc = vou->osd + MAIN_CSC_OFFSET;
- data.hbsc = vou->osd + MAIN_HBSC_OFFSET;
- data.rsz = vou->otfppu + MAIN_RSZ_OFFSET;
+ zplane->layer = vou->osd + MAIN_GL_OFFSET;
+ zplane->csc = vou->osd + MAIN_CSC_OFFSET;
+ zplane->hbsc = vou->osd + MAIN_HBSC_OFFSET;
+ zplane->rsz = vou->otfppu + MAIN_RSZ_OFFSET;
zcrtc->chnreg = vou->osd + OSD_MAIN_CHN;
zcrtc->regs = &main_crtc_regs;
zcrtc->bits = &main_crtc_bits;
} else {
- data.layer = vou->osd + AUX_GL_OFFSET;
- data.csc = vou->osd + AUX_CSC_OFFSET;
- data.hbsc = vou->osd + AUX_HBSC_OFFSET;
- data.rsz = vou->otfppu + AUX_RSZ_OFFSET;
+ zplane->layer = vou->osd + AUX_GL_OFFSET;
+ zplane->csc = vou->osd + AUX_CSC_OFFSET;
+ zplane->hbsc = vou->osd + AUX_HBSC_OFFSET;
+ zplane->rsz = vou->otfppu + AUX_RSZ_OFFSET;
zcrtc->chnreg = vou->osd + OSD_AUX_CHN;
zcrtc->regs = &aux_crtc_regs;
zcrtc->bits = &aux_crtc_bits;
@@ -331,13 +337,14 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou,
return ret;
}
- zcrtc->primary = zx_plane_init(drm, dev, &data, DRM_PLANE_TYPE_PRIMARY);
- if (IS_ERR(zcrtc->primary)) {
- ret = PTR_ERR(zcrtc->primary);
+ ret = zx_plane_init(drm, zplane, DRM_PLANE_TYPE_PRIMARY);
+ if (ret) {
DRM_DEV_ERROR(dev, "failed to init primary plane: %d\n", ret);
return ret;
}
+ zcrtc->primary = &zplane->plane;
+
ret = drm_crtc_init_with_planes(drm, &zcrtc->crtc, zcrtc->primary, NULL,
&zx_crtc_funcs, NULL);
if (ret) {
--
1.9.1
^ permalink raw reply related
* [PATCH v3 0/3] Add overlay plane support for ZTE drm driver
From: Shawn Guo @ 2016-12-29 2:37 UTC (permalink / raw)
To: linux-arm-kernel
From: Shawn Guo <shawn.guo@linaro.org>
Changes for v3:
- Let zx_plane be accessible from zx_vou driver, and so we can easily
access all the data encoded in zx_plane with a drm_plane pointer.
Thus, function zx_overlay_find_vl_idx() can be saved completely.
- Refine the existing zx_plane driver a bit to support disable graphic
layer, and make the support of overlay plane a bit easier, by sharing
VOU layer setup and teardown functions between graphic and video
layers.
Changes for v2:
- Use clipped coordinates for overlay position calculation
Shawn Guo (3):
drm: zte: make zx_plane accessible from zx_vou driver
drm: zte: add .atomic_disable hook to disable graphic layer
drm: zte: add overlay plane support
drivers/gpu/drm/zte/zx_plane.c | 338 ++++++++++++++++++++++++++++++++----
drivers/gpu/drm/zte/zx_plane.h | 12 +-
drivers/gpu/drm/zte/zx_plane_regs.h | 51 ++++++
drivers/gpu/drm/zte/zx_vou.c | 181 ++++++++++++++-----
drivers/gpu/drm/zte/zx_vou.h | 3 +
drivers/gpu/drm/zte/zx_vou_regs.h | 18 ++
6 files changed, 521 insertions(+), 82 deletions(-)
--
1.9.1
^ permalink raw reply
* [PATCH v2] drm: zte: add overlay plane support
From: Shawn Guo @ 2016-12-29 2:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAOw6vb+u7yb05nyMjgyW2hdUVHTPOVF=dkxSkpTC1qGk1j6N6w@mail.gmail.com>
Hi Sean,
On Thu, Dec 22, 2016 at 09:56:01AM -0500, Sean Paul wrote:
> On Tue, Dec 20, 2016 at 7:09 AM, Shawn Guo <shawnguo@kernel.org> wrote:
> > diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
> > index 73fe15c17c32..8ca9c4bdeeaf 100644
> > --- a/drivers/gpu/drm/zte/zx_vou.c
> > +++ b/drivers/gpu/drm/zte/zx_vou.c
> > @@ -93,10 +93,38 @@ struct zx_crtc {
> > const struct zx_crtc_bits *bits;
> > enum vou_chn_type chn_type;
> > struct clk *pixclk;
> > + u32 overlay_bitmap;
> > };
> >
> > #define to_zx_crtc(x) container_of(x, struct zx_crtc, crtc)
> >
> > +struct zx_vl_bits {
> > + u32 enable;
> > + u32 chnsel;
> > + u32 clksel;
> > +};
> > +
> > +static const struct zx_vl_bits zx_vl_bits[VL_NUM] = {
> > + {
> > + .enable = OSD_CTRL0_VL0_EN,
> > + .chnsel = OSD_CTRL0_VL0_SEL,
> > + .clksel = VOU_CLK_VL0_SEL,
> > + }, {
> > + .enable = OSD_CTRL0_VL1_EN,
> > + .chnsel = OSD_CTRL0_VL1_SEL,
> > + .clksel = VOU_CLK_VL1_SEL,
> > + }, {
> > + .enable = OSD_CTRL0_VL2_EN,
> > + .chnsel = OSD_CTRL0_VL2_SEL,
> > + .clksel = VOU_CLK_VL2_SEL,
> > + },
> > +};
> > +
> > +struct zx_overlay {
> > + struct drm_plane *plane;
>
> If you subclass plane instead of storing the pointer, you don't need
> to keep an array of overlays in vou_hw or the find_vl_idx function.
Thanks for the comment, which I found is quite useful. It reminds me
something in the existing code which could be optimized.
We already have a subclass of drm_plane. That's struct zx_plane in
zx_plane.c. Initially, I thought it might be good to keep the structure
local in zx_plane driver. It should make the most sense for the ideal
case, like all the data we have to encode in the structure will only be
accessed inside zx_plane driver. Unfortunately, I found it's not quite
true. There are a few layer specific hardware bits we need to configure
do not sit inside layer block itself, but in some VOU hardware glue
blocks like OSD_CTRL and VOU_CTRL. These glue blocks are only available
in zx_vou driver. If we can access struct zx_plane from zx_vou driver,
things will become much easier and functions like find_vl_idx can be
saved completely.
I have worked out v3 with your comment addressed. There are a couple
of new patches added, which moves struct zx_plane from zx_plane.c to
zx_plane.h and adds support of disabling layer. I will post it shortly.
Please take another look at your convenient time. Thanks for your time.
Shawn
^ permalink raw reply
* [PATCH v2 2/2] clk: hisilicon: Add clock driver for hi3660 SoC
From: Zhangfei Gao @ 2016-12-29 2:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482978805-6981-1-git-send-email-zhangfei.gao@linaro.org>
Add clock drivers for hi3660 SoC, this driver controls the SoC
registers to supply different clocks to different IPs in the SoC.
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
drivers/clk/hisilicon/Kconfig | 7 +
drivers/clk/hisilicon/Makefile | 1 +
drivers/clk/hisilicon/clk-hi3660.c | 592 +++++++++++++++++++++++++++++++
include/dt-bindings/clock/hi3660-clock.h | 194 ++++++++++
4 files changed, 794 insertions(+)
create mode 100644 drivers/clk/hisilicon/clk-hi3660.c
create mode 100644 include/dt-bindings/clock/hi3660-clock.h
diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig
index cbed660..7098bfd 100644
--- a/drivers/clk/hisilicon/Kconfig
+++ b/drivers/clk/hisilicon/Kconfig
@@ -14,6 +14,13 @@ config COMMON_CLK_HI3519
help
Build the clock driver for hi3519.
+config COMMON_CLK_HI3660
+ bool "Hi3660 Clock Driver"
+ depends on ARCH_HISI || COMPILE_TEST
+ default ARCH_HISI
+ help
+ Build the clock driver for hi3660.
+
config COMMON_CLK_HI3798CV200
tristate "Hi3798CV200 Clock Driver"
depends on ARCH_HISI || COMPILE_TEST
diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile
index 4eec5e5..1e4c3dd 100644
--- a/drivers/clk/hisilicon/Makefile
+++ b/drivers/clk/hisilicon/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o
obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o
obj-$(CONFIG_COMMON_CLK_HI3516CV300) += crg-hi3516cv300.o
obj-$(CONFIG_COMMON_CLK_HI3519) += clk-hi3519.o
+obj-$(CONFIG_COMMON_CLK_HI3660) += clk-hi3660.o
obj-$(CONFIG_COMMON_CLK_HI3798CV200) += crg-hi3798cv200.o
obj-$(CONFIG_COMMON_CLK_HI6220) += clk-hi6220.o
obj-$(CONFIG_RESET_HISI) += reset.o
diff --git a/drivers/clk/hisilicon/clk-hi3660.c b/drivers/clk/hisilicon/clk-hi3660.c
new file mode 100644
index 0000000..4e752c6
--- /dev/null
+++ b/drivers/clk/hisilicon/clk-hi3660.c
@@ -0,0 +1,592 @@
+/*
+ * Copyright (c) 2016-2017 Linaro Ltd.
+ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <dt-bindings/clock/hi3660-clock.h>
+#include <linux/clk-provider.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include "clk.h"
+
+enum hi3660_clk_type {
+ HI3660_CRGCTRL = 1,
+ HI3660_PCTRL,
+ HI3660_PMUCTRL,
+ HI3660_SCTRL,
+ HI3660_IOMCU,
+};
+
+static const struct hisi_fixed_rate_clock hi3660_fixed_rate_clks[] = {
+ { HI3660_CLKIN_SYS, "clkin_sys", NULL, 0, 19200000, },
+ { HI3660_CLKIN_REF, "clkin_ref", NULL, 0, 32764, },
+ { HI3660_CLK_FLL_SRC, "clk_fll_src", NULL, 0, 128000000, },
+ { HI3660_CLK_PPLL0, "clk_ppll0", NULL, 0, 1600000000, },
+ { HI3660_CLK_PPLL1, "clk_ppll1", NULL, 0, 1866000000, },
+ { HI3660_CLK_PPLL2, "clk_ppll2", NULL, 0, 960000000, },
+ { HI3660_CLK_PPLL3, "clk_ppll3", NULL, 0, 1290000000, },
+ { HI3660_CLK_SCPLL, "clk_scpll", NULL, 0, 245760000, },
+ { HI3660_PCLK, "pclk", NULL, 0, 20000000, },
+ { HI3660_CLK_UART0_DBG, "clk_uart0_dbg", NULL, 0, 19200000, },
+ { HI3660_CLK_UART6, "clk_uart6", NULL, 0, 19200000, },
+ { HI3660_OSC32K, "osc32k", NULL, 0, 32764, },
+ { HI3660_OSC19M, "osc19m", NULL, 0, 19200000, },
+ { HI3660_CLK_480M, "clk_480m", NULL, 0, 480000000, },
+ { HI3660_CLK_INV, "clk_inv", NULL, 0, 10000000, },
+};
+
+/* crgctrl */
+static const struct hisi_fixed_factor_clock hi3660_crg_fixed_factor_clks[] = {
+ { HI3660_FACTOR_UART3, "clk_factor_uart3", "iomcu_peri0", 1, 8, 0, },
+ { HI3660_CLK_FACTOR_MMC, "clk_factor_mmc", "clkin_sys", 1, 6, 0, },
+ { HI3660_CLK_GATE_I2C0, "clk_gate_i2c0", "clk_i2c0_iomcu", 1, 4, 0, },
+ { HI3660_CLK_GATE_I2C1, "clk_gate_i2c1", "clk_i2c1_iomcu", 1, 4, 0, },
+ { HI3660_CLK_GATE_I2C2, "clk_gate_i2c2", "clk_i2c2_iomcu", 1, 4, 0, },
+ { HI3660_CLK_GATE_I2C6, "clk_gate_i2c6", "clk_i2c6_iomcu", 1, 4, 0, },
+ { HI3660_CLK_DIV_SYSBUS, "clk_div_sysbus", "clk_mux_sysbus", 1, 7, 0, },
+ { HI3660_CLK_DIV_320M, "clk_div_320m", "clk_320m_pll_gt", 1, 5, 0, },
+ { HI3660_CLK_DIV_A53, "clk_div_a53hpm", "clk_a53hpm_andgt", 1, 2, 0, },
+ { HI3660_CLK_GATE_SPI0, "clk_gate_spi0", "clk_ppll0", 1, 8, 0, },
+ { HI3660_CLK_GATE_SPI2, "clk_gate_spi2", "clk_ppll0", 1, 8, 0, },
+ { HI3660_PCIEPHY_REF, "clk_pciephy_ref", "clk_div_pciephy", 1, 1, 0, },
+ { HI3660_CLK_ABB_USB, "clk_abb_usb", "clk_gate_usb_tcxo_en", 1, 1, 0 },
+};
+
+static const struct hisi_gate_clock hi3660_crgctrl_gate_sep_clks[] = {
+ { HI3660_HCLK_GATE_SDIO0, "hclk_gate_sdio0", "clk_div_sysbus",
+ CLK_SET_RATE_PARENT, 0x0, 21, 0, },
+ { HI3660_HCLK_GATE_SD, "hclk_gate_sd", "clk_div_sysbus",
+ CLK_SET_RATE_PARENT, 0x0, 30, 0, },
+ { HI3660_CLK_GATE_AOMM, "clk_gate_aomm", "clk_div_aomm",
+ CLK_SET_RATE_PARENT, 0x0, 31, 0, },
+ { HI3660_PCLK_GPIO0, "pclk_gpio0", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 0, 0, },
+ { HI3660_PCLK_GPIO1, "pclk_gpio1", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 1, 0, },
+ { HI3660_PCLK_GPIO2, "pclk_gpio2", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 2, 0, },
+ { HI3660_PCLK_GPIO3, "pclk_gpio3", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 3, 0, },
+ { HI3660_PCLK_GPIO4, "pclk_gpio4", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 4, 0, },
+ { HI3660_PCLK_GPIO5, "pclk_gpio5", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 5, 0, },
+ { HI3660_PCLK_GPIO6, "pclk_gpio6", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 6, 0, },
+ { HI3660_PCLK_GPIO7, "pclk_gpio7", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 7, 0, },
+ { HI3660_PCLK_GPIO8, "pclk_gpio8", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 8, 0, },
+ { HI3660_PCLK_GPIO9, "pclk_gpio9", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 9, 0, },
+ { HI3660_PCLK_GPIO10, "pclk_gpio10", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 10, 0, },
+ { HI3660_PCLK_GPIO11, "pclk_gpio11", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 11, 0, },
+ { HI3660_PCLK_GPIO12, "pclk_gpio12", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 12, 0, },
+ { HI3660_PCLK_GPIO13, "pclk_gpio13", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 13, 0, },
+ { HI3660_PCLK_GPIO14, "pclk_gpio14", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 14, 0, },
+ { HI3660_PCLK_GPIO15, "pclk_gpio15", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 15, 0, },
+ { HI3660_PCLK_GPIO16, "pclk_gpio16", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 16, 0, },
+ { HI3660_PCLK_GPIO17, "pclk_gpio17", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 17, 0, },
+ { HI3660_PCLK_GPIO18, "pclk_gpio18", "clk_div_ioperi",
+ CLK_SET_RATE_PARENT, 0x10, 18, 0, },
+ { HI3660_PCLK_GPIO19, "pclk_gpio19", "clk_div_ioperi",
+ CLK_SET_RATE_PARENT, 0x10, 19, 0, },
+ { HI3660_PCLK_GPIO20, "pclk_gpio20", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 20, 0, },
+ { HI3660_PCLK_GPIO21, "pclk_gpio21", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 21, 0, },
+ { HI3660_CLK_GATE_SPI3, "clk_gate_spi3", "clk_div_ioperi",
+ CLK_SET_RATE_PARENT, 0x10, 30, 0, },
+ { HI3660_CLK_GATE_I2C7, "clk_gate_i2c7", "clk_mux_i2c",
+ CLK_SET_RATE_PARENT, 0x10, 31, 0, },
+ { HI3660_CLK_GATE_I2C3, "clk_gate_i2c3", "clk_mux_i2c",
+ CLK_SET_RATE_PARENT, 0x20, 7, 0, },
+ { HI3660_CLK_GATE_SPI1, "clk_gate_spi1", "clk_mux_spi",
+ CLK_SET_RATE_PARENT, 0x20, 9, 0, },
+ { HI3660_CLK_GATE_UART1, "clk_gate_uart1", "clk_mux_uarth",
+ CLK_SET_RATE_PARENT, 0x20, 11, 0, },
+ { HI3660_CLK_GATE_UART2, "clk_gate_uart2", "clk_mux_uart1",
+ CLK_SET_RATE_PARENT, 0x20, 12, 0, },
+ { HI3660_CLK_GATE_UART4, "clk_gate_uart4", "clk_mux_uarth",
+ CLK_SET_RATE_PARENT, 0x20, 14, 0, },
+ { HI3660_CLK_GATE_UART5, "clk_gate_uart5", "clk_mux_uart1",
+ CLK_SET_RATE_PARENT, 0x20, 15, 0, },
+ { HI3660_CLK_GATE_I2C4, "clk_gate_i2c4", "clk_mux_i2c",
+ CLK_SET_RATE_PARENT, 0x20, 27, 0, },
+ { HI3660_CLK_GATE_DMAC, "clk_gate_dmac", "clk_div_sysbus",
+ CLK_SET_RATE_PARENT, 0x30, 1, 0, },
+ { HI3660_PCLK_GATE_DSS, "pclk_gate_dss", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x30, 12, 0, },
+ { HI3660_ACLK_GATE_DSS, "aclk_gate_dss", "clk_gate_vivobus",
+ CLK_SET_RATE_PARENT, 0x30, 13, 0, },
+ { HI3660_CLK_GATE_LDI1, "clk_gate_ldi1", "clk_div_ldi1",
+ CLK_SET_RATE_PARENT, 0x30, 14, 0, },
+ { HI3660_CLK_GATE_LDI0, "clk_gate_ldi0", "clk_div_ldi0",
+ CLK_SET_RATE_PARENT, 0x30, 15, 0, },
+ { HI3660_CLK_GATE_VIVOBUS, "clk_gate_vivobus", "clk_div_vivobus",
+ CLK_SET_RATE_PARENT, 0x30, 16, 0, },
+ { HI3660_CLK_GATE_EDC0, "clk_gate_edc0", "clk_div_edc0",
+ CLK_SET_RATE_PARENT, 0x30, 17, 0, },
+ { HI3660_CLK_GATE_TXDPHY0_CFG, "clk_gate_txdphy0_cfg", "clkin_sys",
+ CLK_SET_RATE_PARENT, 0x30, 28, 0, },
+ { HI3660_CLK_GATE_TXDPHY0_REF, "clk_gate_txdphy0_ref", "clkin_sys",
+ CLK_SET_RATE_PARENT, 0x30, 29, 0, },
+ { HI3660_CLK_GATE_TXDPHY1_CFG, "clk_gate_txdphy1_cfg", "clkin_sys",
+ CLK_SET_RATE_PARENT, 0x30, 30, 0, },
+ { HI3660_CLK_GATE_TXDPHY1_REF, "clk_gate_txdphy1_ref", "clkin_sys",
+ CLK_SET_RATE_PARENT, 0x30, 31, 0, },
+ { HI3660_ACLK_GATE_USB3OTG, "aclk_gate_usb3otg", "clk_div_mmc0bus",
+ CLK_SET_RATE_PARENT, 0x40, 1, 0, },
+ { HI3660_CLK_GATE_SPI4, "clk_gate_spi4", "clk_mux_spi",
+ CLK_SET_RATE_PARENT, 0x40, 4, 0, },
+ { HI3660_CLK_GATE_SD, "clk_gate_sd", "clk_mux_sd_sys",
+ CLK_SET_RATE_PARENT, 0x40, 17, 0, },
+ { HI3660_CLK_GATE_SDIO0, "clk_gate_sdio0", "clk_mux_sdio_sys",
+ CLK_SET_RATE_PARENT, 0x40, 19, 0, },
+ { HI3660_CLK_GATE_UFS_SUBSYS, "clk_gate_ufs_subsys", "clk_div_sysbus",
+ CLK_SET_RATE_PARENT, 0x50, 21, 0, },
+ { HI3660_PCLK_GATE_DSI0, "pclk_gate_dsi0", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x50, 28, 0, },
+ { HI3660_PCLK_GATE_DSI1, "pclk_gate_dsi1", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x50, 29, 0, },
+ { HI3660_ACLK_GATE_PCIE, "aclk_gate_pcie", "clk_div_mmc1bus",
+ CLK_SET_RATE_PARENT, 0x420, 5, 0, },
+ { HI3660_PCLK_GATE_PCIE_SYS, "pclk_gate_pcie_sys", "clk_div_mmc1bus",
+ CLK_SET_RATE_PARENT, 0x420, 7, 0, },
+ { HI3660_CLK_GATE_PCIEAUX, "clk_gate_pcieaux", "clkin_sys",
+ CLK_SET_RATE_PARENT, 0x420, 8, 0, },
+ { HI3660_PCLK_GATE_PCIE_PHY, "pclk_gate_pcie_phy", "clk_div_mmc1bus",
+ CLK_SET_RATE_PARENT, 0x420, 9, 0, },
+};
+
+static const struct hisi_gate_clock hi3660_crgctrl_gate_clks[] = {
+ { HI3660_CLK_ANDGT_LDI0, "clk_andgt_ldi0", "clk_mux_ldi0",
+ CLK_SET_RATE_PARENT, 0xf0, 6, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_LDI1, "clk_andgt_ldi1", "clk_mux_ldi1",
+ CLK_SET_RATE_PARENT, 0xf0, 7, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_EDC0, "clk_andgt_edc0", "clk_mux_edc0",
+ CLK_SET_RATE_PARENT, 0xf0, 8, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_GATE_UFSPHY_GT, "clk_gate_ufsphy_gt", "clk_div_ufsperi",
+ CLK_SET_RATE_PARENT, 0xf4, 1, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_MMC, "clk_andgt_mmc", "clk_mux_mmc_pll",
+ CLK_SET_RATE_PARENT, 0xf4, 2, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_SD, "clk_andgt_sd", "clk_mux_sd_pll",
+ CLK_SET_RATE_PARENT, 0xf4, 3, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_A53HPM_ANDGT, "clk_a53hpm_andgt", "clk_mux_a53hpm",
+ CLK_SET_RATE_PARENT, 0xf4, 7, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_SDIO, "clk_andgt_sdio", "clk_mux_sdio_pll",
+ CLK_SET_RATE_PARENT, 0xf4, 8, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_UART0, "clk_andgt_uart0", "clk_div_320m",
+ CLK_SET_RATE_PARENT, 0xf4, 9, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_UART1, "clk_andgt_uart1", "clk_div_320m",
+ CLK_SET_RATE_PARENT, 0xf4, 10, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_UARTH, "clk_andgt_uarth", "clk_div_320m",
+ CLK_SET_RATE_PARENT, 0xf4, 11, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_SPI, "clk_andgt_spi", "clk_div_320m",
+ CLK_SET_RATE_PARENT, 0xf4, 13, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_VIVOBUS_ANDGT, "clk_vivobus_andgt", "clk_mux_vivobus",
+ CLK_SET_RATE_PARENT, 0xf8, 1, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_AOMM_ANDGT, "clk_aomm_andgt", "clk_ppll2",
+ CLK_SET_RATE_PARENT, 0xf8, 3, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_320M_PLL_GT, "clk_320m_pll_gt", "clk_mux_320m",
+ CLK_SET_RATE_PARENT, 0xf8, 10, 0, },
+ { HI3660_AUTODIV_EMMC0BUS, "autodiv_emmc0bus", "autodiv_sysbus",
+ CLK_SET_RATE_PARENT, 0x404, 1, CLK_GATE_HIWORD_MASK, },
+ { HI3660_AUTODIV_SYSBUS, "autodiv_sysbus", "clk_div_sysbus",
+ CLK_SET_RATE_PARENT, 0x404, 5, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_GATE_UFSPHY_CFG, "clk_gate_ufsphy_cfg",
+ "clk_div_ufsphy_cfg", CLK_SET_RATE_PARENT, 0x420, 12, 0, },
+ { HI3660_CLK_GATE_UFSIO_REF, "clk_gate_ufsio_ref",
+ "clk_gate_ufs_tcxo_en", CLK_SET_RATE_PARENT, 0x420, 14, 0, },
+};
+
+static const char *const
+clk_mux_sdio_sys_p[] = {"clk_factor_mmc", "clk_div_sdio",};
+static const char *const
+clk_mux_sd_sys_p[] = {"clk_factor_mmc", "clk_div_sd",};
+static const char *const
+clk_mux_pll_p[] = {"clk_ppll0", "clk_ppll1", "clk_ppll2", "clk_ppll2",};
+static const char *const
+clk_mux_pll0123_p[] = {"clk_ppll0", "clk_ppll1", "clk_ppll2", "clk_ppll3",};
+static const char *const
+clk_mux_edc0_p[] = {"clk_inv", "clk_ppll0", "clk_ppll1", "clk_inv",
+ "clk_ppll2", "clk_inv", "clk_inv", "clk_inv",
+ "clk_ppll3", "clk_inv", "clk_inv", "clk_inv",
+ "clk_inv", "clk_inv", "clk_inv", "clk_inv",};
+static const char *const
+clk_mux_ldi0_p[] = {"clk_inv", "clk_ppll0", "clk_ppll2", "clk_inv",
+ "clk_ppll1", "clk_inv", "clk_inv", "clk_inv",
+ "clk_ppll3", "clk_inv", "clk_inv", "clk_inv",
+ "clk_inv", "clk_inv", "clk_inv", "clk_inv",};
+static const char *const
+clk_mux_uart0_p[] = {"clkin_sys", "clk_div_uart0",};
+static const char *const
+clk_mux_uart1_p[] = {"clkin_sys", "clk_div_uart1",};
+static const char *const
+clk_mux_uarth_p[] = {"clkin_sys", "clk_div_uarth",};
+static const char *const
+clk_mux_pll02p[] = {"clk_ppll0", "clk_ppll2",};
+static const char *const
+clk_mux_ioperi_p[] = {"clk_div_320m", "clk_div_a53hpm",};
+static const char *const
+clk_mux_spi_p[] = {"clkin_sys", "clk_div_spi",};
+static const char *const
+clk_mux_i2c_p[] = {"clkin_sys", "clk_div_i2c",};
+
+static const struct hisi_mux_clock hi3660_crgctrl_mux_clks[] = {
+ { HI3660_CLK_MUX_SYSBUS, "clk_mux_sysbus", clk_mux_sdio_sys_p,
+ ARRAY_SIZE(clk_mux_sdio_sys_p), CLK_SET_RATE_PARENT, 0xac, 0, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_UART0, "clk_mux_uart0", clk_mux_uart0_p,
+ ARRAY_SIZE(clk_mux_uart0_p), CLK_SET_RATE_PARENT, 0xac, 2, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_UART1, "clk_mux_uart1", clk_mux_uart1_p,
+ ARRAY_SIZE(clk_mux_uart1_p), CLK_SET_RATE_PARENT, 0xac, 3, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_UARTH, "clk_mux_uarth", clk_mux_uarth_p,
+ ARRAY_SIZE(clk_mux_uarth_p), CLK_SET_RATE_PARENT, 0xac, 4, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_SPI, "clk_mux_spi", clk_mux_spi_p,
+ ARRAY_SIZE(clk_mux_spi_p), CLK_SET_RATE_PARENT, 0xac, 8, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_I2C, "clk_mux_i2c", clk_mux_i2c_p,
+ ARRAY_SIZE(clk_mux_i2c_p), CLK_SET_RATE_PARENT, 0xac, 13, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_MMC_PLL, "clk_mux_mmc_pll", clk_mux_pll02p,
+ ARRAY_SIZE(clk_mux_pll02p), CLK_SET_RATE_PARENT, 0xb4, 0, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_LDI1, "clk_mux_ldi1", clk_mux_ldi0_p,
+ ARRAY_SIZE(clk_mux_ldi0_p), CLK_SET_RATE_PARENT, 0xb4, 8, 4,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_LDI0, "clk_mux_ldi0", clk_mux_ldi0_p,
+ ARRAY_SIZE(clk_mux_ldi0_p), CLK_SET_RATE_PARENT, 0xb4, 12, 4,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_SD_PLL, "clk_mux_sd_pll", clk_mux_pll_p,
+ ARRAY_SIZE(clk_mux_pll_p), CLK_SET_RATE_PARENT, 0xb8, 4, 2,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_SD_SYS, "clk_mux_sd_sys", clk_mux_sd_sys_p,
+ ARRAY_SIZE(clk_mux_sd_sys_p), CLK_SET_RATE_PARENT, 0xb8, 6, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_EDC0, "clk_mux_edc0", clk_mux_edc0_p,
+ ARRAY_SIZE(clk_mux_edc0_p), CLK_SET_RATE_PARENT, 0xbc, 6, 4,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_SDIO_SYS, "clk_mux_sdio_sys", clk_mux_sdio_sys_p,
+ ARRAY_SIZE(clk_mux_sdio_sys_p), CLK_SET_RATE_PARENT, 0xc0, 6, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_SDIO_PLL, "clk_mux_sdio_pll", clk_mux_pll_p,
+ ARRAY_SIZE(clk_mux_pll_p), CLK_SET_RATE_PARENT, 0xc0, 4, 2,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_VIVOBUS, "clk_mux_vivobus", clk_mux_pll0123_p,
+ ARRAY_SIZE(clk_mux_pll0123_p), CLK_SET_RATE_PARENT, 0xd0, 12, 2,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_A53HPM, "clk_mux_a53hpm", clk_mux_pll02p,
+ ARRAY_SIZE(clk_mux_pll02p), CLK_SET_RATE_PARENT, 0xd4, 9, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_320M, "clk_mux_320m", clk_mux_pll02p,
+ ARRAY_SIZE(clk_mux_pll02p), CLK_SET_RATE_PARENT, 0x100, 0, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_IOPERI, "clk_mux_ioperi", clk_mux_ioperi_p,
+ ARRAY_SIZE(clk_mux_ioperi_p), CLK_SET_RATE_PARENT, 0x108, 10, 1,
+ CLK_MUX_HIWORD_MASK, },
+};
+
+static const struct hisi_divider_clock hi3660_crgctrl_divider_clks[] = {
+ { HI3660_CLK_DIV_UART0, "clk_div_uart0", "clk_andgt_uart0",
+ CLK_SET_RATE_PARENT, 0xb0, 4, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_UART1, "clk_div_uart1", "clk_andgt_uart1",
+ CLK_SET_RATE_PARENT, 0xb0, 8, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_UARTH, "clk_div_uarth", "clk_andgt_uarth",
+ CLK_SET_RATE_PARENT, 0xb0, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_MMC, "clk_div_mmc", "clk_andgt_mmc",
+ CLK_SET_RATE_PARENT, 0xb4, 3, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_SD, "clk_div_sd", "clk_andgt_sd",
+ CLK_SET_RATE_PARENT, 0xb8, 0, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_EDC0, "clk_div_edc0", "clk_andgt_edc0",
+ CLK_SET_RATE_PARENT, 0xbc, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_LDI0, "clk_div_ldi0", "clk_andgt_ldi0",
+ CLK_SET_RATE_PARENT, 0xbc, 10, 6, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_SDIO, "clk_div_sdio", "clk_andgt_sdio",
+ CLK_SET_RATE_PARENT, 0xc0, 0, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_LDI1, "clk_div_ldi1", "clk_andgt_ldi1",
+ CLK_SET_RATE_PARENT, 0xc0, 8, 6, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_SPI, "clk_div_spi", "clk_andgt_spi",
+ CLK_SET_RATE_PARENT, 0xc4, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_VIVOBUS, "clk_div_vivobus", "clk_vivobus_andgt",
+ CLK_SET_RATE_PARENT, 0xd0, 7, 5, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_I2C, "clk_div_i2c", "clk_div_320m",
+ CLK_SET_RATE_PARENT, 0xe8, 4, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_UFSPHY, "clk_div_ufsphy_cfg", "clk_gate_ufsphy_gt",
+ CLK_SET_RATE_PARENT, 0xe8, 9, 2, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_CFGBUS, "clk_div_cfgbus", "clk_div_sysbus",
+ CLK_SET_RATE_PARENT, 0xec, 0, 2, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_MMC0BUS, "clk_div_mmc0bus", "autodiv_emmc0bus",
+ CLK_SET_RATE_PARENT, 0xec, 2, 1, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_MMC1BUS, "clk_div_mmc1bus", "clk_div_sysbus",
+ CLK_SET_RATE_PARENT, 0xec, 3, 1, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_UFSPERI, "clk_div_ufsperi", "clk_gate_ufs_subsys",
+ CLK_SET_RATE_PARENT, 0xec, 14, 1, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_AOMM, "clk_div_aomm", "clk_aomm_andgt",
+ CLK_SET_RATE_PARENT, 0x100, 7, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_IOPERI, "clk_div_ioperi", "clk_mux_ioperi",
+ CLK_SET_RATE_PARENT, 0x108, 11, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+};
+
+/* clk_pmuctrl */
+/* pmu register need shift 2 bits */
+static const struct hisi_gate_clock hi3660_pmu_gate_clks[] = {
+ { HI3660_GATE_ABB_192, "clk_gate_abb_192", "clkin_sys",
+ CLK_SET_RATE_PARENT, (0x10a << 2), 3, 0, },
+};
+
+/* clk_pctrl */
+static const struct hisi_gate_clock hi3660_pctrl_gate_clks[] = {
+ { HI3660_GATE_UFS_TCXO_EN, "clk_gate_ufs_tcxo_en",
+ "clk_gate_abb_192", CLK_SET_RATE_PARENT, 0x10, 0,
+ CLK_GATE_HIWORD_MASK, },
+ { HI3660_GATE_USB_TCXO_EN, "clk_gate_usb_tcxo_en", "clk_gate_abb_192",
+ CLK_SET_RATE_PARENT, 0x10, 1, CLK_GATE_HIWORD_MASK, },
+};
+
+/* clk_sctrl */
+static const struct hisi_gate_clock hi3660_sctrl_gate_sep_clks[] = {
+ { HI3660_PCLK_AO_GPIO0, "pclk_ao_gpio0", "clk_div_aobus",
+ CLK_SET_RATE_PARENT, 0x160, 11, 0, },
+ { HI3660_PCLK_AO_GPIO1, "pclk_ao_gpio1", "clk_div_aobus",
+ CLK_SET_RATE_PARENT, 0x160, 12, 0, },
+ { HI3660_PCLK_AO_GPIO2, "pclk_ao_gpio2", "clk_div_aobus",
+ CLK_SET_RATE_PARENT, 0x160, 13, 0, },
+ { HI3660_PCLK_AO_GPIO3, "pclk_ao_gpio3", "clk_div_aobus",
+ CLK_SET_RATE_PARENT, 0x160, 14, 0, },
+ { HI3660_PCLK_AO_GPIO4, "pclk_ao_gpio4", "clk_div_aobus",
+ CLK_SET_RATE_PARENT, 0x160, 21, 0, },
+ { HI3660_PCLK_AO_GPIO5, "pclk_ao_gpio5", "clk_div_aobus",
+ CLK_SET_RATE_PARENT, 0x160, 22, 0, },
+ { HI3660_PCLK_AO_GPIO6, "pclk_ao_gpio6", "clk_div_aobus",
+ CLK_SET_RATE_PARENT, 0x160, 25, 0, },
+ { HI3660_PCLK_GATE_MMBUF, "pclk_gate_mmbuf", "pclk_div_mmbuf",
+ CLK_SET_RATE_PARENT, 0x170, 23, 0, },
+ { HI3660_CLK_GATE_DSS_AXI_MM, "clk_gate_dss_axi_mm", "aclk_mux_mmbuf",
+ CLK_SET_RATE_PARENT, 0x170, 24, 0, },
+};
+
+static const struct hisi_gate_clock hi3660_sctrl_gate_clks[] = {
+ { HI3660_PCLK_MMBUF_ANDGT, "pclk_mmbuf_andgt", "clk_sw_mmbuf",
+ CLK_SET_RATE_PARENT, 0x258, 7, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_MMBUF_PLL_ANDGT, "clk_mmbuf_pll_andgt", "clk_ppll0",
+ CLK_SET_RATE_PARENT, 0x260, 11, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_FLL_MMBUF_ANDGT, "clk_fll_mmbuf_andgt", "clk_fll_src",
+ CLK_SET_RATE_PARENT, 0x260, 12, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_SYS_MMBUF_ANDGT, "clk_sys_mmbuf_andgt", "clkin_sys",
+ CLK_SET_RATE_PARENT, 0x260, 13, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_GATE_PCIEPHY_GT, "clk_gate_pciephy_gt", "clk_ppll0",
+ CLK_SET_RATE_PARENT, 0x268, 11, CLK_DIVIDER_HIWORD_MASK, 0, },
+};
+
+static const char *const
+aclk_mux_mmbuf_p[] = {"aclk_div_mmbuf", "clk_gate_aomm",};
+static const char *const
+clk_sw_mmbuf_p[] = {"clk_sys_mmbuf_andgt", "clk_fll_mmbuf_andgt",
+ "aclk_mux_mmbuf", "aclk_mux_mmbuf"};
+
+static const struct hisi_mux_clock hi3660_sctrl_mux_clks[] = {
+ { HI3660_ACLK_MUX_MMBUF, "aclk_mux_mmbuf", aclk_mux_mmbuf_p,
+ ARRAY_SIZE(aclk_mux_mmbuf_p), CLK_SET_RATE_PARENT, 0x250, 12, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_SW_MMBUF, "clk_sw_mmbuf", clk_sw_mmbuf_p,
+ ARRAY_SIZE(clk_sw_mmbuf_p), CLK_SET_RATE_PARENT, 0x258, 8, 2,
+ CLK_MUX_HIWORD_MASK, },
+};
+
+static const struct hisi_divider_clock hi3660_sctrl_divider_clks[] = {
+ { HI3660_CLK_DIV_AOBUS, "clk_div_aobus", "clk_ppll0",
+ CLK_SET_RATE_PARENT, 0x254, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_PCLK_DIV_MMBUF, "pclk_div_mmbuf", "pclk_mmbuf_andgt",
+ CLK_SET_RATE_PARENT, 0x258, 10, 2, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_ACLK_DIV_MMBUF, "aclk_div_mmbuf", "clk_mmbuf_pll_andgt",
+ CLK_SET_RATE_PARENT, 0x258, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_PCIEPHY, "clk_div_pciephy", "clk_gate_pciephy_gt",
+ CLK_SET_RATE_PARENT, 0x268, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+};
+
+/* clk_iomcu */
+static const struct hisi_gate_clock hi3660_iomcu_gate_sep_clks[] = {
+ { HI3660_CLK_I2C0_IOMCU, "clk_i2c0_iomcu", "clk_fll_src",
+ CLK_SET_RATE_PARENT, 0x10, 3, 0, },
+ { HI3660_CLK_I2C1_IOMCU, "clk_i2c1_iomcu", "clk_fll_src",
+ CLK_SET_RATE_PARENT, 0x10, 4, 0, },
+ { HI3660_CLK_I2C2_IOMCU, "clk_i2c2_iomcu", "clk_fll_src",
+ CLK_SET_RATE_PARENT, 0x10, 5, 0, },
+ { HI3660_CLK_I2C6_IOMCU, "clk_i2c6_iomcu", "clk_fll_src",
+ CLK_SET_RATE_PARENT, 0x10, 27, 0, },
+ { HI3660_CLK_IOMCU_PERI0, "iomcu_peri0", "clk_ppll0",
+ CLK_SET_RATE_PARENT, 0x90, 0, 0, },
+};
+
+static void hi3660_clk_iomcu_init(struct device_node *np)
+{
+ struct hisi_clock_data *clk_data;
+ int nr = ARRAY_SIZE(hi3660_iomcu_gate_sep_clks);
+
+ clk_data = hisi_clk_init(np, nr);
+ if (!clk_data)
+ return;
+
+ hisi_clk_register_gate_sep(hi3660_iomcu_gate_sep_clks,
+ ARRAY_SIZE(hi3660_iomcu_gate_sep_clks),
+ clk_data);
+}
+
+static void hi3660_clk_pmuctrl_init(struct device_node *np)
+{
+ struct hisi_clock_data *clk_data;
+ int nr = ARRAY_SIZE(hi3660_pmu_gate_clks);
+
+ clk_data = hisi_clk_init(np, nr);
+ if (!clk_data)
+ return;
+
+ hisi_clk_register_gate(hi3660_pmu_gate_clks,
+ ARRAY_SIZE(hi3660_pmu_gate_clks), clk_data);
+}
+
+static void hi3660_clk_pctrl_init(struct device_node *np)
+{
+ struct hisi_clock_data *clk_data;
+ int nr = ARRAY_SIZE(hi3660_pctrl_gate_clks);
+
+ clk_data = hisi_clk_init(np, nr);
+ if (!clk_data)
+ return;
+ hisi_clk_register_gate(hi3660_pctrl_gate_clks,
+ ARRAY_SIZE(hi3660_pctrl_gate_clks), clk_data);
+}
+
+static void hi3660_clk_sctrl_init(struct device_node *np)
+{
+ struct hisi_clock_data *clk_data;
+ int nr = ARRAY_SIZE(hi3660_sctrl_gate_clks) +
+ ARRAY_SIZE(hi3660_sctrl_gate_sep_clks) +
+ ARRAY_SIZE(hi3660_sctrl_mux_clks) +
+ ARRAY_SIZE(hi3660_sctrl_divider_clks);
+
+ clk_data = hisi_clk_init(np, nr);
+ if (!clk_data)
+ return;
+ hisi_clk_register_gate(hi3660_sctrl_gate_clks,
+ ARRAY_SIZE(hi3660_sctrl_gate_clks), clk_data);
+ hisi_clk_register_gate_sep(hi3660_sctrl_gate_sep_clks,
+ ARRAY_SIZE(hi3660_sctrl_gate_sep_clks),
+ clk_data);
+ hisi_clk_register_mux(hi3660_sctrl_mux_clks,
+ ARRAY_SIZE(hi3660_sctrl_mux_clks), clk_data);
+ hisi_clk_register_divider(hi3660_sctrl_divider_clks,
+ ARRAY_SIZE(hi3660_sctrl_divider_clks),
+ clk_data);
+}
+
+static void hi3660_clk_crgctrl_init(struct device_node *np)
+{
+ struct hisi_clock_data *clk_data;
+ int nr = ARRAY_SIZE(hi3660_fixed_rate_clks) +
+ ARRAY_SIZE(hi3660_crgctrl_gate_sep_clks) +
+ ARRAY_SIZE(hi3660_crgctrl_gate_clks) +
+ ARRAY_SIZE(hi3660_crgctrl_mux_clks) +
+ ARRAY_SIZE(hi3660_crg_fixed_factor_clks) +
+ ARRAY_SIZE(hi3660_crgctrl_divider_clks);
+
+ clk_data = hisi_clk_init(np, nr);
+ if (!clk_data)
+ return;
+
+ hisi_clk_register_fixed_rate(hi3660_fixed_rate_clks,
+ ARRAY_SIZE(hi3660_fixed_rate_clks),
+ clk_data);
+ hisi_clk_register_gate_sep(hi3660_crgctrl_gate_sep_clks,
+ ARRAY_SIZE(hi3660_crgctrl_gate_sep_clks),
+ clk_data);
+ hisi_clk_register_gate(hi3660_crgctrl_gate_clks,
+ ARRAY_SIZE(hi3660_crgctrl_gate_clks),
+ clk_data);
+ hisi_clk_register_mux(hi3660_crgctrl_mux_clks,
+ ARRAY_SIZE(hi3660_crgctrl_mux_clks),
+ clk_data);
+ hisi_clk_register_fixed_factor(hi3660_crg_fixed_factor_clks,
+ ARRAY_SIZE(hi3660_crg_fixed_factor_clks),
+ clk_data);
+ hisi_clk_register_divider(hi3660_crgctrl_divider_clks,
+ ARRAY_SIZE(hi3660_crgctrl_divider_clks),
+ clk_data);
+}
+
+static const struct of_device_id hi3660_clk_match_table[] = {
+ { .compatible = "hisilicon,hi3660-crgctrl",
+ .data = (void *)HI3660_CRGCTRL },
+ { .compatible = "hisilicon,hi3660-pctrl",
+ .data = (void *)HI3660_PCTRL },
+ { .compatible = "hisilicon,hi3660-pmuctrl",
+ .data = (void *)HI3660_PMUCTRL },
+ { .compatible = "hisilicon,hi3660-sctrl",
+ .data = (void *)HI3660_SCTRL },
+ { .compatible = "hisilicon,hi3660-iomcu",
+ .data = (void *)HI3660_IOMCU },
+ { }
+};
+
+static int hi3660_clk_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ enum hi3660_clk_type type;
+
+ type = (enum hi3660_clk_type)of_device_get_match_data(dev);
+ if (!type)
+ return -ENODEV;
+
+ switch (type) {
+ case HI3660_CRGCTRL:
+ hi3660_clk_crgctrl_init(np);
+ break;
+ case HI3660_PCTRL:
+ hi3660_clk_pctrl_init(np);
+ break;
+ case HI3660_PMUCTRL:
+ hi3660_clk_pmuctrl_init(np);
+ break;
+ case HI3660_SCTRL:
+ hi3660_clk_sctrl_init(np);
+ break;
+ case HI3660_IOMCU:
+ hi3660_clk_iomcu_init(np);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static struct platform_driver hi3660_clk_driver = {
+ .probe = hi3660_clk_probe,
+ .driver = {
+ .name = "hi3660-clk",
+ .of_match_table = hi3660_clk_match_table,
+ },
+};
+
+static int __init hi3660_clk_init(void)
+{
+ return platform_driver_register(&hi3660_clk_driver);
+}
+core_initcall(hi3660_clk_init);
diff --git a/include/dt-bindings/clock/hi3660-clock.h b/include/dt-bindings/clock/hi3660-clock.h
new file mode 100644
index 0000000..1c00b7f
--- /dev/null
+++ b/include/dt-bindings/clock/hi3660-clock.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2016-2017 Linaro Ltd.
+ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __DTS_HI3660_CLOCK_H
+#define __DTS_HI3660_CLOCK_H
+
+/* fixed rate clocks */
+#define HI3660_CLKIN_SYS 0
+#define HI3660_CLKIN_REF 1
+#define HI3660_CLK_FLL_SRC 2
+#define HI3660_CLK_PPLL0 3
+#define HI3660_CLK_PPLL1 4
+#define HI3660_CLK_PPLL2 5
+#define HI3660_CLK_PPLL3 6
+#define HI3660_CLK_SCPLL 7
+#define HI3660_PCLK 8
+#define HI3660_CLK_UART0_DBG 9
+#define HI3660_CLK_UART6 10
+#define HI3660_OSC32K 11
+#define HI3660_OSC19M 12
+#define HI3660_CLK_480M 13
+#define HI3660_CLK_INV 14
+
+/* clk in crgctrl */
+#define HI3660_FACTOR_UART3 15
+#define HI3660_CLK_FACTOR_MMC 16
+#define HI3660_CLK_GATE_I2C0 17
+#define HI3660_CLK_GATE_I2C1 18
+#define HI3660_CLK_GATE_I2C2 19
+#define HI3660_CLK_GATE_I2C6 20
+#define HI3660_CLK_DIV_SYSBUS 21
+#define HI3660_CLK_DIV_320M 22
+#define HI3660_CLK_DIV_A53 23
+#define HI3660_CLK_GATE_SPI0 24
+#define HI3660_CLK_GATE_SPI2 25
+#define HI3660_PCIEPHY_REF 26
+#define HI3660_CLK_ABB_USB 27
+#define HI3660_HCLK_GATE_SDIO0 28
+#define HI3660_HCLK_GATE_SD 29
+#define HI3660_CLK_GATE_AOMM 30
+#define HI3660_PCLK_GPIO0 31
+#define HI3660_PCLK_GPIO1 32
+#define HI3660_PCLK_GPIO2 33
+#define HI3660_PCLK_GPIO3 34
+#define HI3660_PCLK_GPIO4 35
+#define HI3660_PCLK_GPIO5 36
+#define HI3660_PCLK_GPIO6 37
+#define HI3660_PCLK_GPIO7 38
+#define HI3660_PCLK_GPIO8 39
+#define HI3660_PCLK_GPIO9 40
+#define HI3660_PCLK_GPIO10 41
+#define HI3660_PCLK_GPIO11 42
+#define HI3660_PCLK_GPIO12 43
+#define HI3660_PCLK_GPIO13 44
+#define HI3660_PCLK_GPIO14 45
+#define HI3660_PCLK_GPIO15 46
+#define HI3660_PCLK_GPIO16 47
+#define HI3660_PCLK_GPIO17 48
+#define HI3660_PCLK_GPIO18 49
+#define HI3660_PCLK_GPIO19 50
+#define HI3660_PCLK_GPIO20 51
+#define HI3660_PCLK_GPIO21 52
+#define HI3660_CLK_GATE_SPI3 53
+#define HI3660_CLK_GATE_I2C7 54
+#define HI3660_CLK_GATE_I2C3 55
+#define HI3660_CLK_GATE_SPI1 56
+#define HI3660_CLK_GATE_UART1 57
+#define HI3660_CLK_GATE_UART2 58
+#define HI3660_CLK_GATE_UART4 59
+#define HI3660_CLK_GATE_UART5 60
+#define HI3660_CLK_GATE_I2C4 61
+#define HI3660_CLK_GATE_DMAC 62
+#define HI3660_PCLK_GATE_DSS 63
+#define HI3660_ACLK_GATE_DSS 64
+#define HI3660_CLK_GATE_LDI1 65
+#define HI3660_CLK_GATE_LDI0 66
+#define HI3660_CLK_GATE_VIVOBUS 67
+#define HI3660_CLK_GATE_EDC0 68
+#define HI3660_CLK_GATE_TXDPHY0_CFG 69
+#define HI3660_CLK_GATE_TXDPHY0_REF 70
+#define HI3660_CLK_GATE_TXDPHY1_CFG 71
+#define HI3660_CLK_GATE_TXDPHY1_REF 72
+#define HI3660_ACLK_GATE_USB3OTG 73
+#define HI3660_CLK_GATE_SPI4 74
+#define HI3660_CLK_GATE_SD 75
+#define HI3660_CLK_GATE_SDIO0 76
+#define HI3660_CLK_GATE_UFS_SUBSYS 77
+#define HI3660_PCLK_GATE_DSI0 78
+#define HI3660_PCLK_GATE_DSI1 79
+#define HI3660_ACLK_GATE_PCIE 80
+#define HI3660_PCLK_GATE_PCIE_SYS 81
+#define HI3660_CLK_GATE_PCIEAUX 82
+#define HI3660_PCLK_GATE_PCIE_PHY 83
+#define HI3660_CLK_ANDGT_LDI0 84
+#define HI3660_CLK_ANDGT_LDI1 85
+#define HI3660_CLK_ANDGT_EDC0 86
+#define HI3660_CLK_GATE_UFSPHY_GT 87
+#define HI3660_CLK_ANDGT_MMC 88
+#define HI3660_CLK_ANDGT_SD 89
+#define HI3660_CLK_A53HPM_ANDGT 90
+#define HI3660_CLK_ANDGT_SDIO 91
+#define HI3660_CLK_ANDGT_UART0 92
+#define HI3660_CLK_ANDGT_UART1 93
+#define HI3660_CLK_ANDGT_UARTH 94
+#define HI3660_CLK_ANDGT_SPI 95
+#define HI3660_CLK_VIVOBUS_ANDGT 96
+#define HI3660_CLK_AOMM_ANDGT 97
+#define HI3660_CLK_320M_PLL_GT 98
+#define HI3660_AUTODIV_EMMC0BUS 99
+#define HI3660_AUTODIV_SYSBUS 100
+#define HI3660_CLK_GATE_UFSPHY_CFG 101
+#define HI3660_CLK_GATE_UFSIO_REF 102
+#define HI3660_CLK_MUX_SYSBUS 103
+#define HI3660_CLK_MUX_UART0 104
+#define HI3660_CLK_MUX_UART1 105
+#define HI3660_CLK_MUX_UARTH 106
+#define HI3660_CLK_MUX_SPI 107
+#define HI3660_CLK_MUX_I2C 108
+#define HI3660_CLK_MUX_MMC_PLL 109
+#define HI3660_CLK_MUX_LDI1 110
+#define HI3660_CLK_MUX_LDI0 111
+#define HI3660_CLK_MUX_SD_PLL 112
+#define HI3660_CLK_MUX_SD_SYS 113
+#define HI3660_CLK_MUX_EDC0 114
+#define HI3660_CLK_MUX_SDIO_SYS 115
+#define HI3660_CLK_MUX_SDIO_PLL 116
+#define HI3660_CLK_MUX_VIVOBUS 117
+#define HI3660_CLK_MUX_A53HPM 118
+#define HI3660_CLK_MUX_320M 119
+#define HI3660_CLK_MUX_IOPERI 120
+#define HI3660_CLK_DIV_UART0 121
+#define HI3660_CLK_DIV_UART1 122
+#define HI3660_CLK_DIV_UARTH 123
+#define HI3660_CLK_DIV_MMC 124
+#define HI3660_CLK_DIV_SD 125
+#define HI3660_CLK_DIV_EDC0 126
+#define HI3660_CLK_DIV_LDI0 127
+#define HI3660_CLK_DIV_SDIO 128
+#define HI3660_CLK_DIV_LDI1 129
+#define HI3660_CLK_DIV_SPI 130
+#define HI3660_CLK_DIV_VIVOBUS 131
+#define HI3660_CLK_DIV_I2C 132
+#define HI3660_CLK_DIV_UFSPHY 133
+#define HI3660_CLK_DIV_CFGBUS 134
+#define HI3660_CLK_DIV_MMC0BUS 135
+#define HI3660_CLK_DIV_MMC1BUS 136
+#define HI3660_CLK_DIV_UFSPERI 137
+#define HI3660_CLK_DIV_AOMM 138
+#define HI3660_CLK_DIV_IOPERI 139
+
+/* clk in pmuctrl */
+#define HI3660_GATE_ABB_192 0
+
+/* clk in pctrl */
+#define HI3660_GATE_UFS_TCXO_EN 0
+#define HI3660_GATE_USB_TCXO_EN 1
+
+/* clk in sctrl */
+#define HI3660_PCLK_AO_GPIO0 0
+#define HI3660_PCLK_AO_GPIO1 1
+#define HI3660_PCLK_AO_GPIO2 2
+#define HI3660_PCLK_AO_GPIO3 3
+#define HI3660_PCLK_AO_GPIO4 4
+#define HI3660_PCLK_AO_GPIO5 5
+#define HI3660_PCLK_AO_GPIO6 6
+#define HI3660_PCLK_GATE_MMBUF 7
+#define HI3660_CLK_GATE_DSS_AXI_MM 8
+#define HI3660_PCLK_MMBUF_ANDGT 9
+#define HI3660_CLK_MMBUF_PLL_ANDGT 10
+#define HI3660_CLK_FLL_MMBUF_ANDGT 11
+#define HI3660_CLK_SYS_MMBUF_ANDGT 12
+#define HI3660_CLK_GATE_PCIEPHY_GT 13
+#define HI3660_ACLK_MUX_MMBUF 14
+#define HI3660_CLK_SW_MMBUF 15
+#define HI3660_CLK_DIV_AOBUS 16
+#define HI3660_PCLK_DIV_MMBUF 17
+#define HI3660_ACLK_DIV_MMBUF 18
+#define HI3660_CLK_DIV_PCIEPHY 19
+
+/* clk in iomcu */
+#define HI3660_CLK_I2C0_IOMCU 0
+#define HI3660_CLK_I2C1_IOMCU 1
+#define HI3660_CLK_I2C2_IOMCU 2
+#define HI3660_CLK_I2C6_IOMCU 3
+#define HI3660_CLK_IOMCU_PERI0 4
+
+#endif /* __DTS_HI3660_CLOCK_H */
--
2.7.4
^ permalink raw reply related
* [PATCH v2 1/2] dt-bindings: Document the hi3660 clock bindings
From: Zhangfei Gao @ 2016-12-29 2:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482978805-6981-1-git-send-email-zhangfei.gao@linaro.org>
Add DT bindings documentation for hi3660 SoC clock.
Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
---
.../devicetree/bindings/clock/hi3660-clock.txt | 42 ++++++++++++++++++++++
1 file changed, 42 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/hi3660-clock.txt
diff --git a/Documentation/devicetree/bindings/clock/hi3660-clock.txt b/Documentation/devicetree/bindings/clock/hi3660-clock.txt
new file mode 100644
index 0000000..cc9b86c
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/hi3660-clock.txt
@@ -0,0 +1,42 @@
+* Hisilicon Hi3660 Clock Controller
+
+The Hi3660 clock controller generates and supplies clock to various
+controllers within the Hi3660 SoC.
+
+Required Properties:
+
+- compatible: the compatible should be one of the following strings to
+ indicate the clock controller functionality.
+
+ - "hisilicon,hi3660-crgctrl"
+ - "hisilicon,hi3660-pctrl"
+ - "hisilicon,hi3660-pmuctrl"
+ - "hisilicon,hi3660-sctrl"
+ - "hisilicon,hi3660-iomcu"
+
+- reg: physical base address of the controller and length of memory mapped
+ region.
+
+- #clock-cells: should be 1.
+
+Each clock is assigned an identifier and client nodes use this identifier
+to specify the clock which they consume.
+
+All these identifier could be found in <dt-bindings/clock/hi3660-clock.h>.
+
+Examples:
+ crg_ctrl: clock-controller at fff35000 {
+ compatible = "hisilicon,hi3660-crgctrl", "syscon";
+ reg = <0x0 0xfff35000 0x0 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ uart0: serial at fdf02000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x0 0xfdf02000 0x0 0x1000>;
+ interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&crg_ctrl HI3660_CLK_MUX_UART0>,
+ <&crg_ctrl HI3660_PCLK>;
+ clock-names = "uartclk", "apb_pclk";
+ status = "disabled";
+ };
--
2.7.4
^ permalink raw reply related
* [PATCH v2 0/2] add clk-hi3660
From: Zhangfei Gao @ 2016-12-29 2:33 UTC (permalink / raw)
To: linux-arm-kernel
Address comments from Rob and Stephen.
Rebase to 4.10-rc1
Zhangfei Gao (2):
dt-bindings: Document the hi3660 clock bindings
clk: hisilicon: Add clock driver for hi3660 SoC
.../devicetree/bindings/clock/hi3660-clock.txt | 42 ++
drivers/clk/hisilicon/Kconfig | 7 +
drivers/clk/hisilicon/Makefile | 1 +
drivers/clk/hisilicon/clk-hi3660.c | 592 +++++++++++++++++++++
include/dt-bindings/clock/hi3660-clock.h | 194 +++++++
5 files changed, 836 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/hi3660-clock.txt
create mode 100644 drivers/clk/hisilicon/clk-hi3660.c
create mode 100644 include/dt-bindings/clock/hi3660-clock.h
--
2.7.4
^ permalink raw reply
* [PATCH] crypto: arm/aes-neonbs - process 8 blocks in parallel if we can
From: Herbert Xu @ 2016-12-29 2:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKv+Gu__722s0JkpTzqDu7MmrzSMQ9jJQn=RYnxgSyV+P6MZRQ@mail.gmail.com>
On Wed, Dec 28, 2016 at 07:50:44PM +0000, Ard Biesheuvel wrote:
>
> So about this chunksize, is it ever expected to assume other values
> than 1 (for stream ciphers) or the block size (for block ciphers)?
> Having block size, IV size *and* chunk size fields may be confusing to
> some already, so if the purpose of chunk size can be fulfilled by a
> single 'stream cipher' flag, perhaps we should change that first.
For users (such as algif) it's much more convenient to have a size
rather than a flag because that's what they need to determine the
minimum size for partial updates.
For implementors you don't need to specify the chunksize at all
unless you're a stream cipher (or some other case in future where
the minimum partial update size is not equal to your block size).
Cheers,
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* [PATCH] mtd: nand: Update dependency of IFC for LS1021A
From: Alison Wang @ 2016-12-29 0:58 UTC (permalink / raw)
To: linux-arm-kernel
As NAND support for Freescale/NXP IFC controller is available on
LS1021A, the dependency for LS1021A is added.
Signed-off-by: Alison Wang <alison.wang@nxp.com>
---
drivers/mtd/nand/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 353a9dd..85e3860 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -441,7 +441,7 @@ config MTD_NAND_FSL_ELBC
config MTD_NAND_FSL_IFC
tristate "NAND support for Freescale IFC controller"
- depends on FSL_SOC || ARCH_LAYERSCAPE
+ depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A
select FSL_IFC
select MEMORY
help
--
2.1.0.27.g96db324
^ permalink raw reply related
* [PATCH 2/2] ARM: orion5x: Utilize micon MFD driver
From: Florian Fainelli @ 2016-12-29 0:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161229000137.5553-1-f.fainelli@gmail.com>
Convert the Kurobox Pro and Terastation Pro II/Live machines to utilize
the MFD_MICON driver which currently provides reboot support, but will
later be extended to support LEDs, buttons, FAN control and temperature.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
arch/arm/mach-orion5x/Kconfig | 4 +
arch/arm/mach-orion5x/kurobox_pro-setup.c | 149 +++----------------------
arch/arm/mach-orion5x/terastation_pro2-setup.c | 149 +++----------------------
3 files changed, 40 insertions(+), 262 deletions(-)
diff --git a/arch/arm/mach-orion5x/Kconfig b/arch/arm/mach-orion5x/Kconfig
index 633442ad4e4c..1146181280d0 100644
--- a/arch/arm/mach-orion5x/Kconfig
+++ b/arch/arm/mach-orion5x/Kconfig
@@ -51,6 +51,8 @@ config MACH_RD88F5182_DT
config MACH_KUROBOX_PRO
bool "KuroBox Pro"
select I2C_BOARDINFO if I2C
+ select MFD_CORE
+ select MFD_MICON
help
Say 'Y' here if you want your kernel to support the
KuroBox Pro platform.
@@ -71,6 +73,8 @@ config MACH_TS209
QNAP TS-109/TS-209 platform.
config MACH_TERASTATION_PRO2
+ select MFD_CORE
+ select MFD_MICON
bool "Buffalo Terastation Pro II/Live"
help
Say 'Y' here if you want your kernel to support the
diff --git a/arch/arm/mach-orion5x/kurobox_pro-setup.c b/arch/arm/mach-orion5x/kurobox_pro-setup.c
index 9dc3f59bed9c..b1c27c93b4c5 100644
--- a/arch/arm/mach-orion5x/kurobox_pro-setup.c
+++ b/arch/arm/mach-orion5x/kurobox_pro-setup.c
@@ -24,6 +24,7 @@
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include <linux/platform_data/mtd-orion_nand.h>
+#include <linux/platform_data/micon.h>
#include "common.h"
#include "mpp.h"
#include "orion5x.h"
@@ -180,136 +181,23 @@ static struct mv_sata_platform_data kurobox_pro_sata_data = {
/*****************************************************************************
* Kurobox Pro specific power off method via UART1-attached microcontroller
****************************************************************************/
+struct resource kurobox_pro_micon_res = {
+ .start = UART1_PHYS_BASE,
+ .end = UART1_PHYS_BASE + 0x100,
+ .flags = IORESOURCE_MEM,
+};
-#define UART1_REG(x) (UART1_VIRT_BASE + ((UART_##x) << 2))
-
-static int kurobox_pro_miconread(unsigned char *buf, int count)
-{
- int i;
- int timeout;
-
- for (i = 0; i < count; i++) {
- timeout = 10;
-
- while (!(readl(UART1_REG(LSR)) & UART_LSR_DR)) {
- if (--timeout == 0)
- break;
- udelay(1000);
- }
-
- if (timeout == 0)
- break;
- buf[i] = readl(UART1_REG(RX));
- }
-
- /* return read bytes */
- return i;
-}
-
-static int kurobox_pro_miconwrite(const unsigned char *buf, int count)
-{
- int i = 0;
-
- while (count--) {
- while (!(readl(UART1_REG(LSR)) & UART_LSR_THRE))
- barrier();
- writel(buf[i++], UART1_REG(TX));
- }
-
- return 0;
-}
-
-static int kurobox_pro_miconsend(const unsigned char *data, int count)
-{
- int i;
- unsigned char checksum = 0;
- unsigned char recv_buf[40];
- unsigned char send_buf[40];
- unsigned char correct_ack[3];
- int retry = 2;
-
- /* Generate checksum */
- for (i = 0; i < count; i++)
- checksum -= data[i];
-
- do {
- /* Send data */
- kurobox_pro_miconwrite(data, count);
-
- /* send checksum */
- kurobox_pro_miconwrite(&checksum, 1);
-
- if (kurobox_pro_miconread(recv_buf, sizeof(recv_buf)) <= 3) {
- printk(KERN_ERR ">%s: receive failed.\n", __func__);
-
- /* send preamble to clear the receive buffer */
- memset(&send_buf, 0xff, sizeof(send_buf));
- kurobox_pro_miconwrite(send_buf, sizeof(send_buf));
-
- /* make dummy reads */
- mdelay(100);
- kurobox_pro_miconread(recv_buf, sizeof(recv_buf));
- } else {
- /* Generate expected ack */
- correct_ack[0] = 0x01;
- correct_ack[1] = data[1];
- correct_ack[2] = 0x00;
-
- /* checksum Check */
- if ((recv_buf[0] + recv_buf[1] + recv_buf[2] +
- recv_buf[3]) & 0xFF) {
- printk(KERN_ERR ">%s: Checksum Error : "
- "Received data[%02x, %02x, %02x, %02x]"
- "\n", __func__, recv_buf[0],
- recv_buf[1], recv_buf[2], recv_buf[3]);
- } else {
- /* Check Received Data */
- if (correct_ack[0] == recv_buf[0] &&
- correct_ack[1] == recv_buf[1] &&
- correct_ack[2] == recv_buf[2]) {
- /* Interval for next command */
- mdelay(10);
-
- /* Receive ACK */
- return 0;
- }
- }
- /* Received NAK or illegal Data */
- printk(KERN_ERR ">%s: Error : NAK or Illegal Data "
- "Received\n", __func__);
- }
- } while (retry--);
-
- /* Interval for next command */
- mdelay(10);
+static struct micon_platform_data kurobox_pro_micon_pdata;
- return -1;
-}
-
-static void kurobox_pro_power_off(void)
-{
- const unsigned char watchdogkill[] = {0x01, 0x35, 0x00};
- const unsigned char shutdownwait[] = {0x00, 0x0c};
- const unsigned char poweroff[] = {0x00, 0x06};
- /* 38400 baud divisor */
- const unsigned divisor = ((orion5x_tclk + (8 * 38400)) / (16 * 38400));
-
- pr_info("%s: triggering power-off...\n", __func__);
-
- /* hijack uart1 and reset into sane state (38400,8n1,even parity) */
- writel(0x83, UART1_REG(LCR));
- writel(divisor & 0xff, UART1_REG(DLL));
- writel((divisor >> 8) & 0xff, UART1_REG(DLM));
- writel(0x1b, UART1_REG(LCR));
- writel(0x00, UART1_REG(IER));
- writel(0x07, UART1_REG(FCR));
- writel(0x00, UART1_REG(MCR));
-
- /* Send the commands to shutdown the Kurobox Pro */
- kurobox_pro_miconsend(watchdogkill, sizeof(watchdogkill)) ;
- kurobox_pro_miconsend(shutdownwait, sizeof(shutdownwait)) ;
- kurobox_pro_miconsend(poweroff, sizeof(poweroff));
-}
+static struct platform_device kurobox_pro_micon = {
+ .name = MICON_NAME,
+ .id = -1,
+ .dev = {
+ .platform_data = &kurobox_pro_micon_pdata,
+ },
+ .resource = &kurobox_pro_micon_res,
+ .num_resources = 1,
+};
/*****************************************************************************
* General Setup
@@ -364,6 +252,8 @@ static void __init kurobox_pro_init(void)
KUROBOX_PRO_NOR_BOOT_BASE,
KUROBOX_PRO_NOR_BOOT_SIZE);
platform_device_register(&kurobox_pro_nor_flash);
+ kurobox_pro_micon_pdata.tclk = orion5x_tclk;
+ platform_device_register(&kurobox_pro_micon);
if (machine_is_kurobox_pro()) {
mvebu_mbus_add_window_by_id(ORION_MBUS_DEVBUS_TARGET(0),
@@ -374,9 +264,6 @@ static void __init kurobox_pro_init(void)
}
i2c_register_board_info(0, &kurobox_pro_i2c_rtc, 1);
-
- /* register Kurobox Pro specific power-off method */
- pm_power_off = kurobox_pro_power_off;
}
#ifdef CONFIG_MACH_KUROBOX_PRO
diff --git a/arch/arm/mach-orion5x/terastation_pro2-setup.c b/arch/arm/mach-orion5x/terastation_pro2-setup.c
index deb5e29ac669..7493f64388d6 100644
--- a/arch/arm/mach-orion5x/terastation_pro2-setup.c
+++ b/arch/arm/mach-orion5x/terastation_pro2-setup.c
@@ -19,6 +19,7 @@
#include <linux/mv643xx_eth.h>
#include <linux/i2c.h>
#include <linux/serial_reg.h>
+#include <linux/platform_data/micon.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
@@ -159,136 +160,23 @@ static struct i2c_board_info __initdata tsp2_i2c_rtc = {
* Terastation Pro II specific power off method via UART1-attached
* microcontroller
****************************************************************************/
+struct resource tsp2_micon_res = {
+ .start = UART1_PHYS_BASE,
+ .end = UART1_PHYS_BASE + 0x100,
+ .flags = IORESOURCE_MEM,
+};
-#define UART1_REG(x) (UART1_VIRT_BASE + ((UART_##x) << 2))
-
-static int tsp2_miconread(unsigned char *buf, int count)
-{
- int i;
- int timeout;
-
- for (i = 0; i < count; i++) {
- timeout = 10;
-
- while (!(readl(UART1_REG(LSR)) & UART_LSR_DR)) {
- if (--timeout == 0)
- break;
- udelay(1000);
- }
-
- if (timeout == 0)
- break;
- buf[i] = readl(UART1_REG(RX));
- }
-
- /* return read bytes */
- return i;
-}
-
-static int tsp2_miconwrite(const unsigned char *buf, int count)
-{
- int i = 0;
-
- while (count--) {
- while (!(readl(UART1_REG(LSR)) & UART_LSR_THRE))
- barrier();
- writel(buf[i++], UART1_REG(TX));
- }
-
- return 0;
-}
-
-static int tsp2_miconsend(const unsigned char *data, int count)
-{
- int i;
- unsigned char checksum = 0;
- unsigned char recv_buf[40];
- unsigned char send_buf[40];
- unsigned char correct_ack[3];
- int retry = 2;
-
- /* Generate checksum */
- for (i = 0; i < count; i++)
- checksum -= data[i];
-
- do {
- /* Send data */
- tsp2_miconwrite(data, count);
-
- /* send checksum */
- tsp2_miconwrite(&checksum, 1);
-
- if (tsp2_miconread(recv_buf, sizeof(recv_buf)) <= 3) {
- printk(KERN_ERR ">%s: receive failed.\n", __func__);
-
- /* send preamble to clear the receive buffer */
- memset(&send_buf, 0xff, sizeof(send_buf));
- tsp2_miconwrite(send_buf, sizeof(send_buf));
-
- /* make dummy reads */
- mdelay(100);
- tsp2_miconread(recv_buf, sizeof(recv_buf));
- } else {
- /* Generate expected ack */
- correct_ack[0] = 0x01;
- correct_ack[1] = data[1];
- correct_ack[2] = 0x00;
-
- /* checksum Check */
- if ((recv_buf[0] + recv_buf[1] + recv_buf[2] +
- recv_buf[3]) & 0xFF) {
- printk(KERN_ERR ">%s: Checksum Error : "
- "Received data[%02x, %02x, %02x, %02x]"
- "\n", __func__, recv_buf[0],
- recv_buf[1], recv_buf[2], recv_buf[3]);
- } else {
- /* Check Received Data */
- if (correct_ack[0] == recv_buf[0] &&
- correct_ack[1] == recv_buf[1] &&
- correct_ack[2] == recv_buf[2]) {
- /* Interval for next command */
- mdelay(10);
-
- /* Receive ACK */
- return 0;
- }
- }
- /* Received NAK or illegal Data */
- printk(KERN_ERR ">%s: Error : NAK or Illegal Data "
- "Received\n", __func__);
- }
- } while (retry--);
-
- /* Interval for next command */
- mdelay(10);
+static struct micon_platform_data tsp2_micon_pdata;
- return -1;
-}
-
-static void tsp2_power_off(void)
-{
- const unsigned char watchdogkill[] = {0x01, 0x35, 0x00};
- const unsigned char shutdownwait[] = {0x00, 0x0c};
- const unsigned char poweroff[] = {0x00, 0x06};
- /* 38400 baud divisor */
- const unsigned divisor = ((orion5x_tclk + (8 * 38400)) / (16 * 38400));
-
- pr_info("%s: triggering power-off...\n", __func__);
-
- /* hijack uart1 and reset into sane state (38400,8n1,even parity) */
- writel(0x83, UART1_REG(LCR));
- writel(divisor & 0xff, UART1_REG(DLL));
- writel((divisor >> 8) & 0xff, UART1_REG(DLM));
- writel(0x1b, UART1_REG(LCR));
- writel(0x00, UART1_REG(IER));
- writel(0x07, UART1_REG(FCR));
- writel(0x00, UART1_REG(MCR));
-
- /* Send the commands to shutdown the Terastation Pro II */
- tsp2_miconsend(watchdogkill, sizeof(watchdogkill)) ;
- tsp2_miconsend(shutdownwait, sizeof(shutdownwait)) ;
- tsp2_miconsend(poweroff, sizeof(poweroff));
-}
+static struct platform_device tsp2_micon = {
+ .name = MICON_NAME,
+ .id = -1,
+ .dev = {
+ .platform_data = &tsp2_micon_pdata,
+ },
+ .resource = &tsp2_micon_res,
+ .num_resources = 1,
+};
/*****************************************************************************
* General Setup
@@ -334,6 +222,8 @@ static void __init tsp2_init(void)
TSP2_NOR_BOOT_BASE,
TSP2_NOR_BOOT_SIZE);
platform_device_register(&tsp2_nor_flash);
+ tsp2_micon_pdata.tclk = orion5x_tclk;
+ platform_device_register(&tsp2_micon);
orion5x_ehci0_init();
orion5x_eth_init(&tsp2_eth_data);
@@ -351,9 +241,6 @@ static void __init tsp2_init(void)
if (tsp2_i2c_rtc.irq == 0)
pr_warn("tsp2_init: failed to get RTC IRQ\n");
i2c_register_board_info(0, &tsp2_i2c_rtc, 1);
-
- /* register Terastation Pro II specific power-off method */
- pm_power_off = tsp2_power_off;
}
MACHINE_START(TERASTATION_PRO2, "Buffalo Terastation Pro II/Live")
--
2.9.3
^ permalink raw reply related
* [PATCH 1/2] mfd: micon: Add Buffalo Kurobox Pro, Terastation II Pro/Live driver
From: Florian Fainelli @ 2016-12-29 0:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161229000137.5553-1-f.fainelli@gmail.com>
This driver is currently only used to reboot the devices, but the
microcontroller hanging off UART1 on the Buffalo Kurobox Pro and
Terastation II Pro/Live is capable of a lot more than that, which we
will support in subsequent patches. For now, just add the reboot
functionality to make the kernel reboot correctly.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/mfd/Kconfig | 8 ++
drivers/mfd/Makefile | 2 +
drivers/mfd/micon.c | 204 ++++++++++++++++++++++++++++++++++++
include/linux/platform_data/micon.h | 10 ++
4 files changed, 224 insertions(+)
create mode 100644 drivers/mfd/micon.c
create mode 100644 include/linux/platform_data/micon.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 4ce3b6f11830..e7df3d29351c 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -352,6 +352,14 @@ config MFD_MX25_TSADC
i.MX25 processors. They consist of a conversion queue for general
purpose ADC and a queue for Touchscreens.
+config MFD_MICON
+ bool "Buffalo Kurobox Pro/Terastation II Pro/Live MCU interface"
+ depends on PLAT_ORION
+ help
+ Enables support for the UART1 attached micro controller on Buffalo
+ Kurobox Pro and Terastation II Pro/Live NAS devices. Used for
+ machine restart.
+
config MFD_HI6421_PMIC
tristate "HiSilicon Hi6421 PMU/Codec IC"
depends on OF
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index dda4d4f73ad7..61a226878129 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -94,6 +94,8 @@ obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
+obj-$(CONFIG_MFD_MICON) += micon.o
+
obj-$(CONFIG_MFD_CORE) += mfd-core.o
obj-$(CONFIG_EZX_PCAP) += ezx-pcap.o
diff --git a/drivers/mfd/micon.c b/drivers/mfd/micon.c
new file mode 100644
index 000000000000..fe56abdc0160
--- /dev/null
+++ b/drivers/mfd/micon.c
@@ -0,0 +1,204 @@
+/*
+ * Buffalo Kurobox/Terastation Pro II specific power off method via
+ * UART1-attached microcontroller
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/micon.h>
+#include <linux/serial_reg.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+
+struct micon_priv {
+ struct device *dev;
+ void __iomem *base;
+ int tclk;
+ struct notifier_block nb;
+};
+
+#define UART1_REG(x) ((UART_##x) << 2)
+
+static int miconread(struct micon_priv *priv, unsigned char *buf, int count)
+{
+ int i;
+ int timeout;
+
+ for (i = 0; i < count; i++) {
+ timeout = 10;
+
+ while (!(readl(priv->base + UART1_REG(LSR)) & UART_LSR_DR)) {
+ if (--timeout == 0)
+ break;
+ udelay(1000);
+ }
+
+ if (timeout == 0)
+ break;
+ buf[i] = readl(priv->base + UART1_REG(RX));
+ }
+
+ /* return read bytes */
+ return i;
+}
+
+static int miconwrite(struct micon_priv *priv, const unsigned char *buf,
+ int count)
+{
+ int i = 0;
+
+ while (count--) {
+ while (!(readl(priv->base + UART1_REG(LSR)) & UART_LSR_THRE))
+ barrier();
+ writel(buf[i++], priv->base + UART1_REG(TX));
+ }
+
+ return 0;
+}
+
+static int miconsend(struct micon_priv *priv, const unsigned char *data,
+ int count)
+{
+ int i;
+ unsigned char checksum = 0;
+ unsigned char recv_buf[40];
+ unsigned char send_buf[40];
+ unsigned char correct_ack[3];
+ int retry = 2;
+
+ /* Generate checksum */
+ for (i = 0; i < count; i++)
+ checksum -= data[i];
+
+ do {
+ /* Send data */
+ miconwrite(priv, data, count);
+
+ /* send checksum */
+ miconwrite(priv, &checksum, 1);
+
+ if (miconread(priv, recv_buf, sizeof(recv_buf)) <= 3) {
+ dev_err(priv->dev, ">%s: receive failed.\n", __func__);
+
+ /* send preamble to clear the receive buffer */
+ memset(&send_buf, 0xff, sizeof(send_buf));
+ miconwrite(priv, send_buf, sizeof(send_buf));
+
+ /* make dummy reads */
+ mdelay(100);
+ miconread(priv, recv_buf, sizeof(recv_buf));
+ } else {
+ /* Generate expected ack */
+ correct_ack[0] = 0x01;
+ correct_ack[1] = data[1];
+ correct_ack[2] = 0x00;
+
+ /* checksum Check */
+ if ((recv_buf[0] + recv_buf[1] + recv_buf[2] +
+ recv_buf[3]) & 0xFF) {
+ dev_err(priv->dev, ">%s: Checksum Error : "
+ "Received data[%02x, %02x, %02x, %02x]"
+ "\n", __func__, recv_buf[0],
+ recv_buf[1], recv_buf[2], recv_buf[3]);
+ } else {
+ /* Check Received Data */
+ if (correct_ack[0] == recv_buf[0] &&
+ correct_ack[1] == recv_buf[1] &&
+ correct_ack[2] == recv_buf[2]) {
+ /* Interval for next command */
+ mdelay(10);
+
+ /* Receive ACK */
+ return 0;
+ }
+ }
+ /* Received NAK or illegal Data */
+ dev_err(priv->dev, ">%s: Error : NAK or Illegal Data "
+ "Received\n", __func__);
+ }
+ } while (retry--);
+
+ /* Interval for next command */
+ mdelay(10);
+
+ return -1;
+}
+
+static int restart_handler(struct notifier_block *this,
+ unsigned long code, void *cmd)
+{
+ struct micon_priv *priv = container_of(this, struct micon_priv, nb);
+
+ const unsigned char watchdogkill[] = {0x01, 0x35, 0x00};
+ const unsigned char shutdownwait[] = {0x00, 0x0c};
+ const unsigned char poweroff[] = {0x00, 0x06};
+ /* 38400 baud divisor */
+ unsigned int divisor = ((priv->tclk + (8 * 38400)) / (16 * 38400));
+
+ if (code != SYS_DOWN && code != SYS_HALT)
+ return NOTIFY_DONE;
+
+ dev_info(priv->dev, "%s: triggering power-off...\n", __func__);
+
+ /* hijack uart1 and reset into sane state (38400,8n1,even parity) */
+ writel(0x83, priv->base + UART1_REG(LCR));
+ writel(divisor & 0xff, priv->base + UART1_REG(DLL));
+ writel((divisor >> 8) & 0xff, priv->base + UART1_REG(DLM));
+ writel(0x1b, priv->base + UART1_REG(LCR));
+ writel(0x00, priv->base + UART1_REG(IER));
+ writel(0x07, priv->base + UART1_REG(FCR));
+ writel(0x00, priv->base + UART1_REG(MCR));
+
+ /* Send the commands to shutdown the Terastation Pro II */
+ miconsend(priv, watchdogkill, sizeof(watchdogkill));
+ miconsend(priv, shutdownwait, sizeof(shutdownwait));
+ miconsend(priv, poweroff, sizeof(poweroff));
+
+ return NOTIFY_DONE;
+}
+
+static int micon_probe(struct platform_device *pdev)
+{
+ struct micon_platform_data *pdata = pdev->dev.platform_data;
+ struct micon_priv *priv;
+ struct resource *r;
+ int ret;
+
+ if (!pdata)
+ return -ENODEV;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = &pdev->dev;
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ /* Don't request exclusive access since 8250 may also utilize this
+ * resource
+ */
+ priv->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
+ if (IS_ERR(priv->base))
+ return -ENOMEM;
+
+ priv->tclk = pdata->tclk;
+
+ priv->nb.notifier_call = restart_handler;
+
+ return register_reboot_notifier(&priv->nb);
+}
+
+static struct platform_driver micon_driver = {
+ .probe = micon_probe,
+ .driver = {
+ .name = MICON_NAME,
+ },
+};
+builtin_platform_driver(micon_driver);
diff --git a/include/linux/platform_data/micon.h b/include/linux/platform_data/micon.h
new file mode 100644
index 000000000000..67dbaad5dfaa
--- /dev/null
+++ b/include/linux/platform_data/micon.h
@@ -0,0 +1,10 @@
+#ifndef __MICON_PDATA_H
+#define __MICON_PDATA_H
+
+#define MICON_NAME "micon"
+
+struct micon_platform_data {
+ int tclk;
+};
+
+#endif /* __MICON_PDATA_H */
--
2.9.3
^ permalink raw reply related
* [PATCH 0/2] ARM: orion5x: Move micon code to a MFD driver
From: Florian Fainelli @ 2016-12-29 0:01 UTC (permalink / raw)
To: linux-arm-kernel
Hi all,
This patch series removes some duplicate code between the Kurobox and the
Terastation Pro 2 since they both use the same on-board microcontroller (micon)
attached to their UART1 for system restart.
Future patches will add support for the LEDs, temperature, FAN that the micro
controller provides.
Florian Fainelli (2):
mfd: micon: Add Buffalo Kurobox Pro, Terastation II Pro/Live driver
ARM: orion5x: Utilize micon MFD driver
arch/arm/mach-orion5x/Kconfig | 4 +
arch/arm/mach-orion5x/kurobox_pro-setup.c | 149 +++---------------
arch/arm/mach-orion5x/terastation_pro2-setup.c | 149 +++---------------
drivers/mfd/Kconfig | 8 +
drivers/mfd/Makefile | 2 +
drivers/mfd/micon.c | 204 +++++++++++++++++++++++++
include/linux/platform_data/micon.h | 10 ++
7 files changed, 264 insertions(+), 262 deletions(-)
create mode 100644 drivers/mfd/micon.c
create mode 100644 include/linux/platform_data/micon.h
--
2.9.3
^ permalink raw reply
* [PATCH v1] watchdog: imx2: fix hang-up on boot for i.MX21, i.MX27 and i.MX31 SoCs
From: Fabio Estevam @ 2016-12-28 23:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161228214651.oncshkvksqep3krk@pengutronix.de>
On Wed, Dec 28, 2016 at 7:46 PM, Uwe Kleine-K?nig
<u.kleine-koenig@pengutronix.de> wrote:
> Right, so they would fall back to the i.MX21 type and not profit from
> Markus' change until either the dtb is updated or the third commit is
> added. I'll try to contact Markus to ask if he remembers more details.
There is no profit from Markus' change for mx21/mx27/mx31.
As Vladimir explained in his commit log these SoCs do not have the
WMCR register, which causes a hang on these SoCs when we try to write
to it.
^ permalink raw reply
* [PATCH v6 25/25] phy: Add support for Qualcomm's USB HS phy
From: Stephen Boyd @ 2016-12-28 22:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161228225711.698-1-stephen.boyd@linaro.org>
The high-speed phy on qcom SoCs is controlled via the ULPI
viewport.
Cc: Kishon Vijay Abraham I <kishon@ti.com>
Cc: <devicetree@vger.kernel.org>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
.../devicetree/bindings/phy/qcom,usb-hs-phy.txt | 78 +++++++
drivers/phy/Kconfig | 8 +
drivers/phy/Makefile | 1 +
drivers/phy/phy-qcom-usb-hs.c | 243 +++++++++++++++++++++
4 files changed, 330 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.txt
create mode 100644 drivers/phy/phy-qcom-usb-hs.c
diff --git a/Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.txt b/Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.txt
new file mode 100644
index 000000000000..bec77a74bd39
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.txt
@@ -0,0 +1,78 @@
+Qualcomm's USB HS PHY
+
+PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Should contain "qcom,usb-hs-phy" and more specifically one of the
+ following:
+
+ "qcom,usb-hs-phy-apq8064"
+ "qcom,usb-hs-phy-msm8916"
+ "qcom,usb-hs-phy-msm8974"
+
+- #phy-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: Should contain 0
+
+- clocks:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Should contain clock specifier for the reference and sleep
+ clocks
+
+- clock-names:
+ Usage: required
+ Value type: <stringlist>
+ Definition: Should contain "ref" and "sleep" for the reference and sleep
+ clocks respectively
+
+- resets:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Should contain the phy and POR resets
+
+- reset-names:
+ Usage: required
+ Value type: <stringlist>
+ Definition: Should contain "phy" and "por" for the phy and POR resets
+ respectively
+
+- v3p3-supply:
+ Usage: required
+ Value type: <phandle>
+ Definition: Should contain a reference to the 3.3V supply
+
+- v1p8-supply:
+ Usage: required
+ Value type: <phandle>
+ Definition: Should contain a reference to the 1.8V supply
+
+- qcom,init-seq:
+ Usage: optional
+ Value type: <u8 array>
+ Definition: Should contain a sequence of ULPI address and value pairs to
+ program into the ULPI_EXT_VENDOR_SPECIFIC area. This is related
+ to Device Mode Eye Diagram test. The addresses are offsets
+ from the ULPI_EXT_VENDOR_SPECIFIC address, for example,
+ <0x1 0x53> would mean "write the value 0x53 to address 0x81".
+
+EXAMPLE
+
+otg: usb-controller {
+ ulpi {
+ phy {
+ compatible = "qcom,usb-hs-phy-msm8974", "qcom,usb-hs-phy";
+ #phy-cells = <0>;
+ clocks = <&xo_board>, <&gcc GCC_USB2A_PHY_SLEEP_CLK>;
+ clock-names = "ref", "sleep";
+ resets = <&gcc GCC_USB2A_PHY_BCR>, <&otg 0>;
+ reset-names = "phy", "por";
+ v3p3-supply = <&pm8941_l24>;
+ v1p8-supply = <&pm8941_l6>;
+ qcom,init-seq = /bits/ 8 <0x1 0x63>;
+ };
+ };
+};
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index a430a64981d5..61a22e985831 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -437,6 +437,14 @@ config PHY_QCOM_UFS
help
Support for UFS PHY on QCOM chipsets.
+config PHY_QCOM_USB_HS
+ tristate "Qualcomm USB HS PHY module"
+ depends on USB_ULPI_BUS
+ select GENERIC_PHY
+ help
+ Support for the USB high-speed ULPI compliant phy on Qualcomm
+ chipsets.
+
config PHY_QCOM_USB_HSIC
tristate "Qualcomm USB HSIC ULPI PHY module"
depends on USB_ULPI_BUS
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index c43c9df5d301..0e4259473d28 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o
+obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o
obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o
obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o
diff --git a/drivers/phy/phy-qcom-usb-hs.c b/drivers/phy/phy-qcom-usb-hs.c
new file mode 100644
index 000000000000..bd794cdcbadf
--- /dev/null
+++ b/drivers/phy/phy-qcom-usb-hs.c
@@ -0,0 +1,243 @@
+/**
+ * Copyright (C) 2016 Linaro Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/ulpi/driver.h>
+#include <linux/ulpi/regs.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_device.h>
+#include <linux/reset.h>
+#include <linux/usb/of.h>
+
+#include "ulpi_phy.h"
+
+#define ULPI_PWR_CLK_MNG_REG 0x88
+# define ULPI_PWR_OTG_COMP_DISABLE BIT(0)
+
+#define ULPI_MISC_A 0x96
+# define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1)
+# define ULPI_MISC_A_VBUSVLDEXT BIT(0)
+
+
+struct ulpi_seq {
+ u8 addr;
+ u8 val;
+};
+
+struct qcom_usb_hs_phy {
+ struct ulpi *ulpi;
+ struct phy *phy;
+ struct clk *ref_clk;
+ struct clk *sleep_clk;
+ struct regulator *v1p8;
+ struct regulator *v3p3;
+ struct reset_control *reset;
+ struct ulpi_seq *init_seq;
+ enum usb_dr_mode dr_mode;
+};
+
+static int qcom_usb_hs_phy_set_mode(struct phy *phy, enum phy_mode mode)
+{
+ struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
+ u8 addr, val = 0;
+ int ret;
+
+
+ switch (mode) {
+ case PHY_MODE_USB_OTG:
+ switch (uphy->dr_mode) {
+ case USB_DR_MODE_OTG:
+ val |= ULPI_INT_IDGRD;
+ case USB_DR_MODE_PERIPHERAL:
+ val |= ULPI_INT_SESS_VALID;
+ default:
+ break;
+ }
+
+ ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_RISE, val);
+ if (ret)
+ return ret;
+ return ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_FALL, val);
+ case PHY_MODE_USB_DEVICE: /* Pull up D+ */
+ addr = ULPI_SET(ULPI_MISC_A);
+ break;
+ case PHY_MODE_USB_HOST:
+ addr = ULPI_CLR(ULPI_MISC_A);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ulpi_write(uphy->ulpi, ULPI_SET(ULPI_PWR_CLK_MNG_REG),
+ ULPI_PWR_OTG_COMP_DISABLE);
+ return ulpi_write(uphy->ulpi, addr,
+ ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT);
+}
+
+static int qcom_usb_hs_phy_power_on(struct phy *phy)
+{
+ struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
+ struct ulpi *ulpi = uphy->ulpi;
+ const struct ulpi_seq *seq;
+ int ret;
+
+ ret = clk_prepare_enable(uphy->ref_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(uphy->sleep_clk);
+ if (ret)
+ goto err_sleep;
+
+ ret = regulator_set_load(uphy->v1p8, 50000);
+ if (ret < 0)
+ goto err_1p8;
+
+ ret = regulator_enable(uphy->v1p8);
+ if (ret)
+ goto err_1p8;
+
+ ret = regulator_set_voltage_triplet(uphy->v3p3, 3050000, 3300000,
+ 3300000);
+ if (ret)
+ goto err_3p3;
+
+ ret = regulator_set_load(uphy->v3p3, 50000);
+ if (ret < 0)
+ goto err_3p3;
+
+ ret = regulator_enable(uphy->v3p3);
+ if (ret)
+ goto err_3p3;
+
+ for (seq = uphy->init_seq; seq->addr; seq++) {
+ ret = ulpi_write(ulpi, ULPI_EXT_VENDOR_SPECIFIC + seq->addr,
+ seq->val);
+ if (ret)
+ goto err_ulpi;
+ }
+
+ if (uphy->reset) {
+ ret = reset_control_reset(uphy->reset);
+ if (ret)
+ goto err_ulpi;
+ }
+
+ return 0;
+err_ulpi:
+ regulator_disable(uphy->v3p3);
+err_3p3:
+ regulator_disable(uphy->v1p8);
+err_1p8:
+ clk_disable_unprepare(uphy->sleep_clk);
+err_sleep:
+ clk_disable_unprepare(uphy->ref_clk);
+ return ret;
+}
+
+static int qcom_usb_hs_phy_power_off(struct phy *phy)
+{
+ struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
+
+ regulator_disable(uphy->v3p3);
+ regulator_disable(uphy->v1p8);
+ clk_disable_unprepare(uphy->sleep_clk);
+ clk_disable_unprepare(uphy->ref_clk);
+
+ return 0;
+}
+
+static const struct phy_ops qcom_usb_hs_phy_ops = {
+ .power_on = qcom_usb_hs_phy_power_on,
+ .power_off = qcom_usb_hs_phy_power_off,
+ .set_mode = qcom_usb_hs_phy_set_mode,
+ .owner = THIS_MODULE,
+};
+
+static int qcom_usb_hs_phy_probe(struct ulpi *ulpi)
+{
+ struct qcom_usb_hs_phy *uphy;
+ struct phy_provider *p;
+ struct clk *clk;
+ struct regulator *reg;
+ struct reset_control *reset;
+ int size;
+ int ret;
+
+ uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL);
+ if (!uphy)
+ return -ENOMEM;
+ ulpi_set_drvdata(ulpi, uphy);
+ uphy->ulpi = ulpi;
+ uphy->dr_mode = of_usb_get_dr_mode_by_phy(ulpi->dev.of_node, -1);
+
+ size = of_property_count_u8_elems(ulpi->dev.of_node, "qcom,init-seq");
+ if (size < 0)
+ size = 0;
+ uphy->init_seq = devm_kmalloc_array(&ulpi->dev, (size / 2) + 1,
+ sizeof(*uphy->init_seq), GFP_KERNEL);
+ if (!uphy->init_seq)
+ return -ENOMEM;
+ ret = of_property_read_u8_array(ulpi->dev.of_node, "qcom,init-seq",
+ (u8 *)uphy->init_seq, size);
+ if (ret && size)
+ return ret;
+ /* NUL terminate */
+ uphy->init_seq[size / 2].addr = uphy->init_seq[size / 2].val = 0;
+
+ uphy->ref_clk = clk = devm_clk_get(&ulpi->dev, "ref");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ uphy->sleep_clk = clk = devm_clk_get(&ulpi->dev, "sleep");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ uphy->v1p8 = reg = devm_regulator_get(&ulpi->dev, "v1p8");
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ uphy->v3p3 = reg = devm_regulator_get(&ulpi->dev, "v3p3");
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ uphy->reset = reset = devm_reset_control_get(&ulpi->dev, "por");
+ if (IS_ERR(reset)) {
+ if (PTR_ERR(reset) == -EPROBE_DEFER)
+ return PTR_ERR(reset);
+ uphy->reset = NULL;
+ }
+
+ uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node,
+ &qcom_usb_hs_phy_ops);
+ if (IS_ERR(uphy->phy))
+ return PTR_ERR(uphy->phy);
+
+ phy_set_drvdata(uphy->phy, uphy);
+
+ p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate);
+ return PTR_ERR_OR_ZERO(p);
+}
+
+static const struct of_device_id qcom_usb_hs_phy_match[] = {
+ { .compatible = "qcom,usb-hs-phy", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qcom_usb_hs_phy_match);
+
+static struct ulpi_driver qcom_usb_hs_phy_driver = {
+ .probe = qcom_usb_hs_phy_probe,
+ .driver = {
+ .name = "qcom_usb_hs_phy",
+ .of_match_table = qcom_usb_hs_phy_match,
+ },
+};
+module_ulpi_driver(qcom_usb_hs_phy_driver);
+
+MODULE_DESCRIPTION("Qualcomm USB HS phy");
+MODULE_LICENSE("GPL v2");
--
2.10.0.297.gf6727b0
^ permalink raw reply related
* [PATCH v6 24/25] phy: Add support for Qualcomm's USB HSIC phy
From: Stephen Boyd @ 2016-12-28 22:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161228225711.698-1-stephen.boyd@linaro.org>
The HSIC USB controller on qcom SoCs has an integrated all
digital phy controlled via the ULPI viewport.
Cc: Kishon Vijay Abraham I <kishon@ti.com>
Acked-by: Rob Herring <robh@kernel.org>
Cc: <devicetree@vger.kernel.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
.../devicetree/bindings/phy/qcom,usb-hsic-phy.txt | 65 +++++++++
drivers/phy/Kconfig | 7 +
drivers/phy/Makefile | 1 +
drivers/phy/phy-qcom-usb-hsic.c | 160 +++++++++++++++++++++
4 files changed, 233 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/qcom,usb-hsic-phy.txt
create mode 100644 drivers/phy/phy-qcom-usb-hsic.c
diff --git a/Documentation/devicetree/bindings/phy/qcom,usb-hsic-phy.txt b/Documentation/devicetree/bindings/phy/qcom,usb-hsic-phy.txt
new file mode 100644
index 000000000000..3c7cb2be4b12
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/qcom,usb-hsic-phy.txt
@@ -0,0 +1,65 @@
+Qualcomm's USB HSIC PHY
+
+PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Should contain "qcom,usb-hsic-phy" and more specifically one of the
+ following:
+
+ "qcom,usb-hsic-phy-mdm9615"
+ "qcom,usb-hsic-phy-msm8974"
+
+- #phy-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: Should contain 0
+
+- clocks:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Should contain clock specifier for phy, calibration and
+ a calibration sleep clock
+
+- clock-names:
+ Usage: required
+ Value type: <stringlist>
+ Definition: Should contain "phy, "cal" and "cal_sleep"
+
+- pinctrl-names:
+ Usage: required
+ Value type: <stringlist>
+ Definition: Should contain "init" and "default" in that order
+
+- pinctrl-0:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: List of pinctrl settings to apply to keep HSIC pins in a glitch
+ free state
+
+- pinctrl-1:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: List of pinctrl settings to apply to mux out the HSIC pins
+
+EXAMPLE
+
+usb-controller {
+ ulpi {
+ phy {
+ compatible = "qcom,usb-hsic-phy-msm8974",
+ "qcom,usb-hsic-phy";
+ #phy-cells = <0>;
+ pinctrl-names = "init", "default";
+ pinctrl-0 = <&hsic_sleep>;
+ pinctrl-1 = <&hsic_default>;
+ clocks = <&gcc GCC_USB_HSIC_CLK>,
+ <&gcc GCC_USB_HSIC_IO_CAL_CLK>,
+ <&gcc GCC_USB_HSIC_IO_CAL_SLEEP_CLK>;
+ clock-names = "phy", "cal", "cal_sleep";
+ assigned-clocks = <&gcc GCC_USB_HSIC_IO_CAL_CLK>;
+ assigned-clock-rates = <960000>;
+ };
+ };
+};
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index e8eb7f225a88..a430a64981d5 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -437,6 +437,13 @@ config PHY_QCOM_UFS
help
Support for UFS PHY on QCOM chipsets.
+config PHY_QCOM_USB_HSIC
+ tristate "Qualcomm USB HSIC ULPI PHY module"
+ depends on USB_ULPI_BUS
+ select GENERIC_PHY
+ help
+ Support for the USB HSIC ULPI compliant PHY on QCOM chipsets.
+
config PHY_TUSB1210
tristate "TI TUSB1210 ULPI PHY module"
depends on USB_ULPI_BUS
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 65eb2f436a41..c43c9df5d301 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o
+obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o
obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o
obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
diff --git a/drivers/phy/phy-qcom-usb-hsic.c b/drivers/phy/phy-qcom-usb-hsic.c
new file mode 100644
index 000000000000..47690f9945b9
--- /dev/null
+++ b/drivers/phy/phy-qcom-usb-hsic.c
@@ -0,0 +1,160 @@
+/**
+ * Copyright (C) 2016 Linaro Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/ulpi/driver.h>
+#include <linux/ulpi/regs.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinctrl-state.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include "ulpi_phy.h"
+
+#define ULPI_HSIC_CFG 0x30
+#define ULPI_HSIC_IO_CAL 0x33
+
+struct qcom_usb_hsic_phy {
+ struct ulpi *ulpi;
+ struct phy *phy;
+ struct pinctrl *pctl;
+ struct clk *phy_clk;
+ struct clk *cal_clk;
+ struct clk *cal_sleep_clk;
+};
+
+static int qcom_usb_hsic_phy_power_on(struct phy *phy)
+{
+ struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy);
+ struct ulpi *ulpi = uphy->ulpi;
+ struct pinctrl_state *pins_default;
+ int ret;
+
+ ret = clk_prepare_enable(uphy->phy_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(uphy->cal_clk);
+ if (ret)
+ goto err_cal;
+
+ ret = clk_prepare_enable(uphy->cal_sleep_clk);
+ if (ret)
+ goto err_sleep;
+
+ /* Set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */
+ ret = ulpi_write(ulpi, ULPI_HSIC_IO_CAL, 0xff);
+ if (ret)
+ goto err_ulpi;
+
+ /* Enable periodic IO calibration in HSIC_CFG register */
+ ret = ulpi_write(ulpi, ULPI_HSIC_CFG, 0xa8);
+ if (ret)
+ goto err_ulpi;
+
+ /* Configure pins for HSIC functionality */
+ pins_default = pinctrl_lookup_state(uphy->pctl, PINCTRL_STATE_DEFAULT);
+ if (IS_ERR(pins_default))
+ return PTR_ERR(pins_default);
+
+ ret = pinctrl_select_state(uphy->pctl, pins_default);
+ if (ret)
+ goto err_ulpi;
+
+ /* Enable HSIC mode in HSIC_CFG register */
+ ret = ulpi_write(ulpi, ULPI_SET(ULPI_HSIC_CFG), 0x01);
+ if (ret)
+ goto err_ulpi;
+
+ /* Disable auto-resume */
+ ret = ulpi_write(ulpi, ULPI_CLR(ULPI_IFC_CTRL),
+ ULPI_IFC_CTRL_AUTORESUME);
+ if (ret)
+ goto err_ulpi;
+
+ return ret;
+err_ulpi:
+ clk_disable_unprepare(uphy->cal_sleep_clk);
+err_sleep:
+ clk_disable_unprepare(uphy->cal_clk);
+err_cal:
+ clk_disable_unprepare(uphy->phy_clk);
+ return ret;
+}
+
+static int qcom_usb_hsic_phy_power_off(struct phy *phy)
+{
+ struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy);
+
+ clk_disable_unprepare(uphy->cal_sleep_clk);
+ clk_disable_unprepare(uphy->cal_clk);
+ clk_disable_unprepare(uphy->phy_clk);
+
+ return 0;
+}
+
+static const struct phy_ops qcom_usb_hsic_phy_ops = {
+ .power_on = qcom_usb_hsic_phy_power_on,
+ .power_off = qcom_usb_hsic_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int qcom_usb_hsic_phy_probe(struct ulpi *ulpi)
+{
+ struct qcom_usb_hsic_phy *uphy;
+ struct phy_provider *p;
+ struct clk *clk;
+
+ uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL);
+ if (!uphy)
+ return -ENOMEM;
+ ulpi_set_drvdata(ulpi, uphy);
+
+ uphy->ulpi = ulpi;
+ uphy->pctl = devm_pinctrl_get(&ulpi->dev);
+ if (IS_ERR(uphy->pctl))
+ return PTR_ERR(uphy->pctl);
+
+ uphy->phy_clk = clk = devm_clk_get(&ulpi->dev, "phy");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ uphy->cal_clk = clk = devm_clk_get(&ulpi->dev, "cal");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ uphy->cal_sleep_clk = clk = devm_clk_get(&ulpi->dev, "cal_sleep");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node,
+ &qcom_usb_hsic_phy_ops);
+ if (IS_ERR(uphy->phy))
+ return PTR_ERR(uphy->phy);
+ phy_set_drvdata(uphy->phy, uphy);
+
+ p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate);
+ return PTR_ERR_OR_ZERO(p);
+}
+
+static const struct of_device_id qcom_usb_hsic_phy_match[] = {
+ { .compatible = "qcom,usb-hsic-phy", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qcom_usb_hsic_phy_match);
+
+static struct ulpi_driver qcom_usb_hsic_phy_driver = {
+ .probe = qcom_usb_hsic_phy_probe,
+ .driver = {
+ .name = "qcom_usb_hsic_phy",
+ .of_match_table = qcom_usb_hsic_phy_match,
+ },
+};
+module_ulpi_driver(qcom_usb_hsic_phy_driver);
+
+MODULE_DESCRIPTION("Qualcomm USB HSIC phy");
+MODULE_LICENSE("GPL v2");
--
2.10.0.297.gf6727b0
^ permalink raw reply related
* [PATCH v6 23/25] usb: chipidea: Pullup D+ in device mode via phy APIs
From: Stephen Boyd @ 2016-12-28 22:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161228225711.698-1-stephen.boyd@linaro.org>
If the phy supports it, call phy_set_mode() to pull up D+ when
required by setting the mode to PHY_MODE_USB_DEVICE. If we want
to remove the pullup, set the mode to PHY_MODE_USB_HOST.
Cc: Peter Chen <peter.chen@nxp.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
drivers/usb/chipidea/udc.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 0d532a724d48..6d61fa0689b0 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -18,6 +18,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
+#include <linux/phy/phy.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg-fsm.h>
@@ -1609,10 +1610,15 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on)
return 0;
pm_runtime_get_sync(&ci->gadget.dev);
- if (is_on)
+ if (is_on) {
+ if (ci->phy)
+ phy_set_mode(ci->phy, PHY_MODE_USB_DEVICE);
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
- else
+ } else {
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
+ if (ci->phy)
+ phy_set_mode(ci->phy, PHY_MODE_USB_HOST);
+ }
pm_runtime_put_sync(&ci->gadget.dev);
return 0;
--
2.10.0.297.gf6727b0
^ permalink raw reply related
* [PATCH v6 22/25] usb: chipidea: Drop lock across event_notify during gadget stop
From: Stephen Boyd @ 2016-12-28 22:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161228225711.698-1-stephen.boyd@linaro.org>
The CI_HDRC_CONTROLLER_STOPPED_EVENT may want to call sleeping
APIs similar to how _gadget_stop_activity() may. Let's drop the
lock across the event so that glue drivers can make sleeping
calls.
Cc: Peter Chen <peter.chen@nxp.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
drivers/usb/chipidea/udc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 0db56fb7e9e9..0d532a724d48 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -1793,10 +1793,10 @@ static int ci_udc_stop(struct usb_gadget *gadget)
if (ci->vbus_active) {
hw_device_state(ci, 0);
+ spin_unlock_irqrestore(&ci->lock, flags);
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_STOPPED_EVENT);
- spin_unlock_irqrestore(&ci->lock, flags);
_gadget_stop_activity(&ci->gadget);
spin_lock_irqsave(&ci->lock, flags);
pm_runtime_put(&ci->gadget.dev);
--
2.10.0.297.gf6727b0
^ permalink raw reply related
* [PATCH v6 21/25] usb: chipidea: msm: Be silent on probe defer errors
From: Stephen Boyd @ 2016-12-28 22:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161228225711.698-1-stephen.boyd@linaro.org>
If something fails in ci_hdrc_add_device() due to probe defer, we
shouldn't print an error message. Be silent in this case as we'll
try probe again later.
Acked-by: Peter Chen <peter.chen@nxp.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
drivers/usb/chipidea/ci_hdrc_msm.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index 316044dba0ac..f1ede7909f54 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -262,8 +262,9 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
plat_ci = ci_hdrc_add_device(&pdev->dev, pdev->resource,
pdev->num_resources, &ci->pdata);
if (IS_ERR(plat_ci)) {
- dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
ret = PTR_ERR(plat_ci);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
goto err_mux;
}
--
2.10.0.297.gf6727b0
^ permalink raw reply related
* [PATCH v6 20/25] usb: chipidea: msm: Handle phy power states
From: Stephen Boyd @ 2016-12-28 22:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161228225711.698-1-stephen.boyd@linaro.org>
The ULPI phy on qcom platforms needs to be initialized and
powered on after a USB reset and before we toggle the run/stop
bit. Otherwise, the phy locks up and doesn't work properly. Hook
the phy initialization into the RESET event and the phy power off
into the STOPPED event.
Acked-by: Peter Chen <peter.chen@nxp.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
drivers/usb/chipidea/ci_hdrc_msm.c | 40 +++++++++++++++++++-------------------
drivers/usb/chipidea/core.c | 8 ++++++--
drivers/usb/chipidea/host.c | 8 ++++++--
include/linux/usb/chipidea.h | 2 +-
4 files changed, 33 insertions(+), 25 deletions(-)
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index fe96df7b530c..316044dba0ac 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -80,20 +80,33 @@ static const struct reset_control_ops ci_hdrc_msm_reset_ops = {
.reset = ci_hdrc_msm_por_reset,
};
-static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
+static int ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
{
struct device *dev = ci->dev->parent;
struct ci_hdrc_msm *msm_ci = dev_get_drvdata(dev);
+ int ret;
switch (event) {
case CI_HDRC_CONTROLLER_RESET_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
+
+ hw_phymode_configure(ci);
if (msm_ci->secondary_phy) {
u32 val = readl_relaxed(msm_ci->base + HS_PHY_SEC_CTRL);
val |= HS_PHY_DIG_CLAMP_N;
writel_relaxed(val, msm_ci->base + HS_PHY_SEC_CTRL);
}
+ ret = phy_init(ci->phy);
+ if (ret)
+ return ret;
+
+ ret = phy_power_on(ci->phy);
+ if (ret) {
+ phy_exit(ci->phy);
+ return ret;
+ }
+
/* use AHB transactor, allow posted data writes */
hw_write_id_reg(ci, HS_PHY_AHB_MODE, 0xffffffff, 0x8);
@@ -113,21 +126,18 @@ static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
HSPHY_SESS_VLD_CTRL);
}
-
- usb_phy_init(ci->usb_phy);
break;
case CI_HDRC_CONTROLLER_STOPPED_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n");
- /*
- * Put the phy in non-driving mode. Otherwise host
- * may not detect soft-disconnection.
- */
- usb_phy_notify_disconnect(ci->usb_phy, USB_SPEED_UNKNOWN);
+ phy_power_off(ci->phy);
+ phy_exit(ci->phy);
break;
default:
dev_dbg(dev, "unknown ci_hdrc event\n");
break;
}
+
+ return 0;
}
static int ci_hdrc_msm_mux_phy(struct ci_hdrc_msm *ci,
@@ -167,7 +177,6 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
{
struct ci_hdrc_msm *ci;
struct platform_device *plat_ci;
- struct usb_phy *phy;
struct clk *clk;
struct reset_control *reset;
struct resource *res;
@@ -181,21 +190,12 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, ci);
- /*
- * OTG(PHY) driver takes care of PHY initialization, clock management,
- * powering up VBUS, mapping of registers address space and power
- * management.
- */
- phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
- if (IS_ERR(phy))
- return PTR_ERR(phy);
-
ci->pdata.name = "ci_hdrc_msm";
ci->pdata.capoffset = DEF_CAPOFFSET;
ci->pdata.flags = CI_HDRC_REGS_SHARED | CI_HDRC_DISABLE_STREAMING |
- CI_HDRC_OVERRIDE_AHB_BURST;
+ CI_HDRC_OVERRIDE_AHB_BURST |
+ CI_HDRC_OVERRIDE_PHY_CONTROL;
ci->pdata.notify_event = ci_hdrc_msm_notify_event;
- ci->pdata.usb_phy = phy;
reset = devm_reset_control_get(&pdev->dev, "core");
if (IS_ERR(reset))
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 37f888e31f10..565f8799d540 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -327,6 +327,7 @@ void hw_phymode_configure(struct ci_hdrc *ci)
hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS);
}
}
+EXPORT_SYMBOL_GPL(hw_phymode_configure);
/**
* _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy
@@ -503,9 +504,12 @@ int hw_device_reset(struct ci_hdrc *ci)
return ret;
}
- if (ci->platdata->notify_event)
- ci->platdata->notify_event(ci,
+ if (ci->platdata->notify_event) {
+ ret = ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_RESET_EVENT);
+ if (ret)
+ return ret;
+ }
/* USBMODE should be configured step by step */
hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c
index f884c0877bca..915f3e91586e 100644
--- a/drivers/usb/chipidea/host.c
+++ b/drivers/usb/chipidea/host.c
@@ -90,8 +90,12 @@ static int ehci_ci_reset(struct usb_hcd *hcd)
ehci->need_io_watchdog = 0;
- if (ci->platdata->notify_event)
- ci->platdata->notify_event(ci, CI_HDRC_CONTROLLER_RESET_EVENT);
+ if (ci->platdata->notify_event) {
+ ret = ci->platdata->notify_event(ci,
+ CI_HDRC_CONTROLLER_RESET_EVENT);
+ if (ret)
+ return ret;
+ }
ci_platform_configure(ci);
diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h
index 7e3daa37cf60..c5fdfcf99828 100644
--- a/include/linux/usb/chipidea.h
+++ b/include/linux/usb/chipidea.h
@@ -61,7 +61,7 @@ struct ci_hdrc_platform_data {
enum usb_dr_mode dr_mode;
#define CI_HDRC_CONTROLLER_RESET_EVENT 0
#define CI_HDRC_CONTROLLER_STOPPED_EVENT 1
- void (*notify_event) (struct ci_hdrc *ci, unsigned event);
+ int (*notify_event) (struct ci_hdrc *ci, unsigned event);
struct regulator *reg_vbus;
struct usb_otg_caps ci_otg_caps;
bool tpl_support;
--
2.10.0.297.gf6727b0
^ permalink raw reply related
* [PATCH v6 19/25] usb: chipidea: msm: Add reset controller for PHY POR bit
From: Stephen Boyd @ 2016-12-28 22:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161228225711.698-1-stephen.boyd@linaro.org>
The MSM chipidea wrapper has two bits that are used to reset the
first or second phy. Add support for these bits via the reset
controller framework, so that phy drivers can reset their
hardware at the right time during initialization.
Acked-by: Peter Chen <peter.chen@nxp.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
drivers/usb/chipidea/Kconfig | 1 +
drivers/usb/chipidea/ci_hdrc_msm.c | 50 ++++++++++++++++++++++++++++++++++++--
2 files changed, 49 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig
index 19c20eaa23f2..fc96f5cdcb5c 100644
--- a/drivers/usb/chipidea/Kconfig
+++ b/drivers/usb/chipidea/Kconfig
@@ -2,6 +2,7 @@ config USB_CHIPIDEA
tristate "ChipIdea Highspeed Dual Role Controller"
depends on ((USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)) && HAS_DMA
select EXTCON
+ select RESET_CONTROLLER
help
Say Y here if your system has a dual role high speed USB
controller based on ChipIdea silicon IP. It supports:
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index 2489a63d3e75..fe96df7b530c 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -14,6 +14,7 @@
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/io.h>
+#include <linux/reset-controller.h>
#include <linux/extcon.h>
#include <linux/of.h>
@@ -31,8 +32,10 @@
#define HSPHY_SESS_VLD_CTRL BIT(25)
/* Vendor base starts at 0x200 beyond CI base */
+#define HS_PHY_CTRL 0x0040
#define HS_PHY_SEC_CTRL 0x0078
#define HS_PHY_DIG_CLAMP_N BIT(16)
+#define HS_PHY_POR_ASSERT BIT(0)
struct ci_hdrc_msm {
struct platform_device *ci;
@@ -40,11 +43,43 @@ struct ci_hdrc_msm {
struct clk *iface_clk;
struct clk *fs_clk;
struct ci_hdrc_platform_data pdata;
+ struct reset_controller_dev rcdev;
bool secondary_phy;
bool hsic;
void __iomem *base;
};
+static int
+ci_hdrc_msm_por_reset(struct reset_controller_dev *r, unsigned long id)
+{
+ struct ci_hdrc_msm *ci_msm = container_of(r, struct ci_hdrc_msm, rcdev);
+ void __iomem *addr = ci_msm->base;
+ u32 val;
+
+ if (id)
+ addr += HS_PHY_SEC_CTRL;
+ else
+ addr += HS_PHY_CTRL;
+
+ val = readl_relaxed(addr);
+ val |= HS_PHY_POR_ASSERT;
+ writel(val, addr);
+ /*
+ * wait for minimum 10 microseconds as suggested by manual.
+ * Use a slightly larger value since the exact value didn't
+ * work 100% of the time.
+ */
+ udelay(12);
+ val &= ~HS_PHY_POR_ASSERT;
+ writel(val, addr);
+
+ return 0;
+}
+
+static const struct reset_control_ops ci_hdrc_msm_reset_ops = {
+ .reset = ci_hdrc_msm_por_reset,
+};
+
static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
{
struct device *dev = ci->dev->parent;
@@ -186,10 +221,18 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
if (!ci->base)
return -ENOMEM;
- ret = clk_prepare_enable(ci->fs_clk);
+ ci->rcdev.owner = THIS_MODULE;
+ ci->rcdev.ops = &ci_hdrc_msm_reset_ops;
+ ci->rcdev.of_node = pdev->dev.of_node;
+ ci->rcdev.nr_resets = 2;
+ ret = reset_controller_register(&ci->rcdev);
if (ret)
return ret;
+ ret = clk_prepare_enable(ci->fs_clk);
+ if (ret)
+ goto err_fs;
+
reset_control_assert(reset);
usleep_range(10000, 12000);
reset_control_deassert(reset);
@@ -198,7 +241,7 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
ret = clk_prepare_enable(ci->core_clk);
if (ret)
- return ret;
+ goto err_fs;
ret = clk_prepare_enable(ci->iface_clk);
if (ret)
@@ -236,6 +279,8 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
clk_disable_unprepare(ci->iface_clk);
err_iface:
clk_disable_unprepare(ci->core_clk);
+err_fs:
+ reset_controller_unregister(&ci->rcdev);
return ret;
}
@@ -247,6 +292,7 @@ static int ci_hdrc_msm_remove(struct platform_device *pdev)
ci_hdrc_remove_device(ci->ci);
clk_disable_unprepare(ci->iface_clk);
clk_disable_unprepare(ci->core_clk);
+ reset_controller_unregister(&ci->rcdev);
return 0;
}
--
2.10.0.297.gf6727b0
^ permalink raw reply related
* [PATCH v6 18/25] usb: chipidea: msm: Make platform data driver local instead of global
From: Stephen Boyd @ 2016-12-28 22:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161228225711.698-1-stephen.boyd@linaro.org>
If two devices are probed with this same driver, they'll share
the same platform data structure, while the chipidea core layer
writes and modifies it. This can lead to interesting results
especially if one device is an OTG type chipidea controller and
another is a host. Let's create a copy of this structure per each
device instance so that odd things don't happen.
Acked-by: Peter Chen <peter.chen@nxp.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
drivers/usb/chipidea/ci_hdrc_msm.c | 23 +++++++++--------------
1 file changed, 9 insertions(+), 14 deletions(-)
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index 333817291798..2489a63d3e75 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -39,6 +39,7 @@ struct ci_hdrc_msm {
struct clk *core_clk;
struct clk *iface_clk;
struct clk *fs_clk;
+ struct ci_hdrc_platform_data pdata;
bool secondary_phy;
bool hsic;
void __iomem *base;
@@ -94,16 +95,6 @@ static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
}
}
-static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = {
- .name = "ci_hdrc_msm",
- .capoffset = DEF_CAPOFFSET,
- .flags = CI_HDRC_REGS_SHARED |
- CI_HDRC_DISABLE_STREAMING |
- CI_HDRC_OVERRIDE_AHB_BURST,
-
- .notify_event = ci_hdrc_msm_notify_event,
-};
-
static int ci_hdrc_msm_mux_phy(struct ci_hdrc_msm *ci,
struct platform_device *pdev)
{
@@ -164,7 +155,12 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
if (IS_ERR(phy))
return PTR_ERR(phy);
- ci_hdrc_msm_platdata.usb_phy = phy;
+ ci->pdata.name = "ci_hdrc_msm";
+ ci->pdata.capoffset = DEF_CAPOFFSET;
+ ci->pdata.flags = CI_HDRC_REGS_SHARED | CI_HDRC_DISABLE_STREAMING |
+ CI_HDRC_OVERRIDE_AHB_BURST;
+ ci->pdata.notify_event = ci_hdrc_msm_notify_event;
+ ci->pdata.usb_phy = phy;
reset = devm_reset_control_get(&pdev->dev, "core");
if (IS_ERR(reset))
@@ -220,9 +216,8 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
}
of_node_put(ulpi_node);
- plat_ci = ci_hdrc_add_device(&pdev->dev,
- pdev->resource, pdev->num_resources,
- &ci_hdrc_msm_platdata);
+ plat_ci = ci_hdrc_add_device(&pdev->dev, pdev->resource,
+ pdev->num_resources, &ci->pdata);
if (IS_ERR(plat_ci)) {
dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
ret = PTR_ERR(plat_ci);
--
2.10.0.297.gf6727b0
^ permalink raw reply related
* [PATCH v6 17/25] usb: chipidea: msm: Restore wrapper settings after reset
From: Stephen Boyd @ 2016-12-28 22:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161228225711.698-1-stephen.boyd@linaro.org>
When the RESET bit is set in the USBCMD register it resets quite
a few of the wrapper's registers to their reset state. This
includes the GENCONFIG and GENCONFIG2 registers. Currently this
is done by the usb phy and ehci-msm drivers writing into the
controller wrapper's MMIO address space. Let's consolidate the
register writes into the wrapper driver instead so that we
clearly split the wrapper from the phys.
Acked-by: Peter Chen <peter.chen@nxp.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
drivers/usb/chipidea/ci_hdrc_msm.c | 39 ++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index 4b0aadc2be2f..333817291798 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -14,11 +14,22 @@
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/io.h>
+#include <linux/extcon.h>
+#include <linux/of.h>
#include "ci.h"
#define HS_PHY_AHB_MODE 0x0098
+#define HS_PHY_GENCONFIG 0x009c
+#define HS_PHY_TXFIFO_IDLE_FORCE_DIS BIT(4)
+
+#define HS_PHY_GENCONFIG_2 0x00a0
+#define HS_PHY_SESS_VLD_CTRL_EN BIT(7)
+#define HS_PHY_ULPI_TX_PKT_EN_CLR_FIX BIT(19)
+
+#define HSPHY_SESS_VLD_CTRL BIT(25)
+
/* Vendor base starts at 0x200 beyond CI base */
#define HS_PHY_SEC_CTRL 0x0078
#define HS_PHY_DIG_CLAMP_N BIT(16)
@@ -29,6 +40,7 @@ struct ci_hdrc_msm {
struct clk *iface_clk;
struct clk *fs_clk;
bool secondary_phy;
+ bool hsic;
void __iomem *base;
};
@@ -48,6 +60,24 @@ static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
/* use AHB transactor, allow posted data writes */
hw_write_id_reg(ci, HS_PHY_AHB_MODE, 0xffffffff, 0x8);
+
+ /* workaround for rx buffer collision issue */
+ hw_write_id_reg(ci, HS_PHY_GENCONFIG,
+ HS_PHY_TXFIFO_IDLE_FORCE_DIS, 0);
+
+ if (!msm_ci->hsic)
+ hw_write_id_reg(ci, HS_PHY_GENCONFIG_2,
+ HS_PHY_ULPI_TX_PKT_EN_CLR_FIX, 0);
+
+ if (!IS_ERR(ci->platdata->vbus_extcon.edev)) {
+ hw_write_id_reg(ci, HS_PHY_GENCONFIG_2,
+ HS_PHY_SESS_VLD_CTRL_EN,
+ HS_PHY_SESS_VLD_CTRL_EN);
+ hw_write(ci, OP_USBCMD, HSPHY_SESS_VLD_CTRL,
+ HSPHY_SESS_VLD_CTRL);
+
+ }
+
usb_phy_init(ci->usb_phy);
break;
case CI_HDRC_CONTROLLER_STOPPED_EVENT:
@@ -116,6 +146,7 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
struct reset_control *reset;
struct resource *res;
int ret;
+ struct device_node *ulpi_node, *phy_node;
dev_dbg(&pdev->dev, "ci_hdrc_msm_probe\n");
@@ -181,6 +212,14 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
if (ret)
goto err_mux;
+ ulpi_node = of_find_node_by_name(pdev->dev.of_node, "ulpi");
+ if (ulpi_node) {
+ phy_node = of_get_next_available_child(ulpi_node, NULL);
+ ci->hsic = of_device_is_compatible(phy_node, "qcom,usb-hsic-phy");
+ of_node_put(phy_node);
+ }
+ of_node_put(ulpi_node);
+
plat_ci = ci_hdrc_add_device(&pdev->dev,
pdev->resource, pdev->num_resources,
&ci_hdrc_msm_platdata);
--
2.10.0.297.gf6727b0
^ permalink raw reply related
* [PATCH v6 16/25] usb: chipidea: msm: Mux over secondary phy at the right time
From: Stephen Boyd @ 2016-12-28 22:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161228225711.698-1-stephen.boyd@linaro.org>
We need to pick the correct phy at runtime based on how the SoC
has been wired onto the board. If the secondary phy is used, take
it out of reset and mux over to it by writing into the TCSR
register. Make sure to do this on reset too, because this
register is reset to the default value (primary phy) after the
RESET bit is set in USBCMD.
Acked-by: Peter Chen <peter.chen@nxp.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
drivers/usb/chipidea/ci_hdrc_msm.c | 62 ++++++++++++++++++++++++++++++++++++--
1 file changed, 60 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index 7e870a253f55..4b0aadc2be2f 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -8,29 +8,44 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/usb/gadget.h>
#include <linux/usb/chipidea.h>
#include <linux/clk.h>
#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/io.h>
#include "ci.h"
#define HS_PHY_AHB_MODE 0x0098
+/* Vendor base starts at 0x200 beyond CI base */
+#define HS_PHY_SEC_CTRL 0x0078
+#define HS_PHY_DIG_CLAMP_N BIT(16)
+
struct ci_hdrc_msm {
struct platform_device *ci;
struct clk *core_clk;
struct clk *iface_clk;
struct clk *fs_clk;
+ bool secondary_phy;
+ void __iomem *base;
};
static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
{
- struct device *dev = ci->gadget.dev.parent;
+ struct device *dev = ci->dev->parent;
+ struct ci_hdrc_msm *msm_ci = dev_get_drvdata(dev);
switch (event) {
case CI_HDRC_CONTROLLER_RESET_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
+ if (msm_ci->secondary_phy) {
+ u32 val = readl_relaxed(msm_ci->base + HS_PHY_SEC_CTRL);
+ val |= HS_PHY_DIG_CLAMP_N;
+ writel_relaxed(val, msm_ci->base + HS_PHY_SEC_CTRL);
+ }
+
/* use AHB transactor, allow posted data writes */
hw_write_id_reg(ci, HS_PHY_AHB_MODE, 0xffffffff, 0x8);
usb_phy_init(ci->usb_phy);
@@ -59,6 +74,39 @@ static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = {
.notify_event = ci_hdrc_msm_notify_event,
};
+static int ci_hdrc_msm_mux_phy(struct ci_hdrc_msm *ci,
+ struct platform_device *pdev)
+{
+ struct regmap *regmap;
+ struct device *dev = &pdev->dev;
+ struct of_phandle_args args;
+ u32 val;
+ int ret;
+
+ ret = of_parse_phandle_with_fixed_args(dev->of_node, "phy-select", 2, 0,
+ &args);
+ if (ret)
+ return 0;
+
+ regmap = syscon_node_to_regmap(args.np);
+ of_node_put(args.np);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ret = regmap_write(regmap, args.args[0], args.args[1]);
+ if (ret)
+ return ret;
+
+ ci->secondary_phy = !!args.args[1];
+ if (ci->secondary_phy) {
+ val = readl_relaxed(ci->base + HS_PHY_SEC_CTRL);
+ val |= HS_PHY_DIG_CLAMP_N;
+ writel_relaxed(val, ci->base + HS_PHY_SEC_CTRL);
+ }
+
+ return 0;
+}
+
static int ci_hdrc_msm_probe(struct platform_device *pdev)
{
struct ci_hdrc_msm *ci;
@@ -66,6 +114,7 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
struct usb_phy *phy;
struct clk *clk;
struct reset_control *reset;
+ struct resource *res;
int ret;
dev_dbg(&pdev->dev, "ci_hdrc_msm_probe\n");
@@ -105,6 +154,11 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
ci->fs_clk = NULL;
}
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ ci->base = devm_ioremap_resource(&pdev->dev, res);
+ if (!ci->base)
+ return -ENOMEM;
+
ret = clk_prepare_enable(ci->fs_clk);
if (ret)
return ret;
@@ -123,6 +177,10 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
if (ret)
goto err_iface;
+ ret = ci_hdrc_msm_mux_phy(ci, pdev);
+ if (ret)
+ goto err_mux;
+
plat_ci = ci_hdrc_add_device(&pdev->dev,
pdev->resource, pdev->num_resources,
&ci_hdrc_msm_platdata);
--
2.10.0.297.gf6727b0
^ permalink raw reply related
* [PATCH v6 15/25] usb: chipidea: msm: Add proper clk and reset support
From: Stephen Boyd @ 2016-12-28 22:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161228225711.698-1-stephen.boyd@linaro.org>
The msm chipidea controller uses two main clks, an AHB clk to
read/write the MMIO registers and a core clk called the system
clk that drives the controller itself. Add support for these clks
as they're required in all designs.
Also add support for an optional third clk that we need to turn
on to reset the controller and wrapper logic and other
"housekeeping" things. This clk was removed in later revisions of
the hardware because the reset methodology no longer required
clks to be enabled to propagate resets.
Acked-by: Peter Chen <peter.chen@nxp.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
drivers/usb/chipidea/ci_hdrc_msm.c | 72 +++++++++++++++++++++++++++++++++++---
1 file changed, 68 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index b282a717bf8c..7e870a253f55 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -10,11 +10,20 @@
#include <linux/pm_runtime.h>
#include <linux/usb/gadget.h>
#include <linux/usb/chipidea.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
#include "ci.h"
#define HS_PHY_AHB_MODE 0x0098
+struct ci_hdrc_msm {
+ struct platform_device *ci;
+ struct clk *core_clk;
+ struct clk *iface_clk;
+ struct clk *fs_clk;
+};
+
static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
{
struct device *dev = ci->gadget.dev.parent;
@@ -52,11 +61,20 @@ static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = {
static int ci_hdrc_msm_probe(struct platform_device *pdev)
{
+ struct ci_hdrc_msm *ci;
struct platform_device *plat_ci;
struct usb_phy *phy;
+ struct clk *clk;
+ struct reset_control *reset;
+ int ret;
dev_dbg(&pdev->dev, "ci_hdrc_msm_probe\n");
+ ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL);
+ if (!ci)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, ci);
+
/*
* OTG(PHY) driver takes care of PHY initialization, clock management,
* powering up VBUS, mapping of registers address space and power
@@ -68,29 +86,75 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
ci_hdrc_msm_platdata.usb_phy = phy;
+ reset = devm_reset_control_get(&pdev->dev, "core");
+ if (IS_ERR(reset))
+ return PTR_ERR(reset);
+
+ ci->core_clk = clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ ci->iface_clk = clk = devm_clk_get(&pdev->dev, "iface");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ ci->fs_clk = clk = devm_clk_get(&pdev->dev, "fs");
+ if (IS_ERR(clk)) {
+ if (PTR_ERR(clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ ci->fs_clk = NULL;
+ }
+
+ ret = clk_prepare_enable(ci->fs_clk);
+ if (ret)
+ return ret;
+
+ reset_control_assert(reset);
+ usleep_range(10000, 12000);
+ reset_control_deassert(reset);
+
+ clk_disable_unprepare(ci->fs_clk);
+
+ ret = clk_prepare_enable(ci->core_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(ci->iface_clk);
+ if (ret)
+ goto err_iface;
+
plat_ci = ci_hdrc_add_device(&pdev->dev,
pdev->resource, pdev->num_resources,
&ci_hdrc_msm_platdata);
if (IS_ERR(plat_ci)) {
dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
- return PTR_ERR(plat_ci);
+ ret = PTR_ERR(plat_ci);
+ goto err_mux;
}
- platform_set_drvdata(pdev, plat_ci);
+ ci->ci = plat_ci;
pm_runtime_set_active(&pdev->dev);
pm_runtime_no_callbacks(&pdev->dev);
pm_runtime_enable(&pdev->dev);
return 0;
+
+err_mux:
+ clk_disable_unprepare(ci->iface_clk);
+err_iface:
+ clk_disable_unprepare(ci->core_clk);
+ return ret;
}
static int ci_hdrc_msm_remove(struct platform_device *pdev)
{
- struct platform_device *plat_ci = platform_get_drvdata(pdev);
+ struct ci_hdrc_msm *ci = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
- ci_hdrc_remove_device(plat_ci);
+ ci_hdrc_remove_device(ci->ci);
+ clk_disable_unprepare(ci->iface_clk);
+ clk_disable_unprepare(ci->core_clk);
return 0;
}
--
2.10.0.297.gf6727b0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox