| /* |
| * Copyright (c) 2016, Intel Corporation. All rights reserved. |
| * |
| * Author: Archana Vohra <archana.vohra@intel.com> |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * Redistributions of source code must retain the above copyright notice, this |
| * list of conditions and the following disclaimer. |
| * |
| * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * Neither the name of Intel nor the names of its contributors may be used |
| * to endorse or promote products derived from this software without specific |
| * prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <linux/errno.h> |
| #include <linux/kernel.h> |
| #include <linux/types.h> |
| #include <linux/of_device.h> |
| #include <linux/sysfs.h> |
| #include <linux/string.h> |
| #include "mipicsi_debug.h" |
| #include "mipicsi_device.h" |
| #include "mipicsi_host.h" |
| #include "mipicsi_top.h" |
| #include "mipicsi_util.h" |
| #include "mipicsi_sysfs.h" |
| #include "mipicsi_util.h" |
| #include <soc/mnh/mnh-hwio-mipi-top.h> |
| #include <soc/mnh/mnh-hwio-mipi-rx.h> |
| #include <soc/mnh/mnh-hwio-mipi-tx.h> |
| |
| #define MAX_STR_COPY 100 |
| |
| |
| struct devs{ |
| char name[4]; |
| enum mipicsi_top_dev device; |
| }; |
| |
| struct devs dev_tbl[MIPI_MAX+1] = { |
| {"Rx0", MIPI_RX0}, |
| {"Rx1", MIPI_RX1}, |
| {"Rx2", MIPI_RX2}, |
| {"Tx0", MIPI_TX0}, |
| {"Tx1", MIPI_TX1}, |
| {"TOP", MIPI_TOP}, |
| {"IPU", MIPI_IPU} |
| }; |
| |
| static uint32_t read_data; |
| static char string_data[MAX_STR_COPY]; |
| |
| #define SHOW_FMT_NA(name) \ |
| static ssize_t name##_show(struct device *dev, \ |
| struct device_attribute *attr, \ |
| char *buf) \ |
| { \ |
| return snprintf((char *)buf, MAX_STR_COPY, "cat %s not supported\n", \ |
| #name); \ |
| } |
| |
| |
| int find_device(const char *str, enum mipicsi_top_dev *device) |
| { |
| uint8_t i; |
| bool found = false; |
| |
| for (i=0; i < sizeof (dev_tbl)/sizeof(struct devs); i++) { |
| if (!strncmp(str, dev_tbl[i].name, strlen(dev_tbl[i].name))) { |
| found = true; |
| break; |
| } |
| } |
| |
| if (!found) { |
| pr_err("Device not found\n"); |
| return -EINVAL; |
| } |
| |
| *device = dev_tbl[i].device; |
| return 0; |
| } |
| |
| SHOW_FMT_NA(start_dev); |
| |
| static ssize_t start_dev_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| struct mipicsi_top_cfg cfg; |
| enum mipicsi_top_dev device; |
| uint8_t *token; |
| const char *delim = ";"; |
| |
| token = strsep((char **)&buf, delim); |
| if ((token) && (find_device(token, &device) >= 0)) { |
| cfg.dev = device; |
| token = strsep((char **)&buf, delim); |
| if (!(token) || (kstrtou32(token, 0, &cfg.mbps))) |
| cfg.mbps = 640; |
| token = strsep((char **)&buf, delim); |
| if (!(token) || (kstrtou32(token, 0, &cfg.num_lanes))) |
| cfg.num_lanes = 4; |
| if (mipicsi_top_start(&cfg) == 0) |
| return count; |
| } |
| pr_err("Usage: echo\"<dev>;<bitrate>;<lanes>\">start_dev\n"); |
| pr_err("dev=Rx0,Rx1,Rx2,Tx0,Tx1\n"); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR(start_dev, S_IRUGO | S_IWUSR | S_IWGRP, |
| start_dev_show, start_dev_store); |
| |
| SHOW_FMT_NA(stop_dev); |
| |
| static ssize_t stop_dev_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| enum mipicsi_top_dev device; |
| |
| if (find_device(buf, &device) >= 0) { |
| mipicsi_top_stop(device); |
| return count; |
| } |
| pr_err("Usage: echo\"<dev>\">stop_dev\n"); |
| pr_err("dev=Rx0,Rx1,Rx2,Tx0,Tx1\n"); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR(stop_dev, S_IRUGO | S_IWUSR | S_IWGRP, |
| stop_dev_show, stop_dev_store); |
| |
| SHOW_FMT_NA(reset_dev); |
| |
| static ssize_t reset_dev_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| enum mipicsi_top_dev device; |
| |
| if (!strncmp(buf,"ALL",3)) { |
| mipicsi_top_reset_all(); |
| return count; |
| } else if (find_device(buf, &device) >= 0) { |
| mipicsi_top_reset(device); |
| return count; |
| } |
| pr_err("Usage: echo\"<dev>\">reset_dev\n"); |
| pr_err("dev=ALL,Rx0,Rx1,Rx2,Tx0,Tx1\n"); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR(reset_dev, S_IRUGO | S_IWUSR | S_IWGRP, |
| reset_dev_show, reset_dev_store); |
| |
| SHOW_FMT_NA(set_mux); |
| |
| static ssize_t set_mux_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| enum mipicsi_top_dev device; |
| uint8_t *token; |
| const char *delim = ";"; |
| struct mipicsi_top_mux mux; |
| |
| token = strsep((char **)&buf, delim); |
| if ((token) && (find_device(token, &device) >= 0)) { |
| mux.source = device; |
| token = strsep((char **)&buf, delim); |
| if ((token) && (find_device(token, &device) >= 0)) { |
| mux.sink = device; |
| mux.ss_vc_mask = 0x0F; |
| mux.ss_stream_off = true; |
| mipicsi_top_set_mux(&mux); |
| return count; |
| } |
| } |
| pr_err("Usage: echo\"<source>;<sink>\">set_mux\n"); |
| pr_err("source=Rx0,Rx1,Rx2,IPU sink=Tx0,Tx1,IPU\n"); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR(set_mux, S_IRUGO | S_IWUSR | S_IWGRP, |
| set_mux_show, set_mux_store); |
| |
| SHOW_FMT_NA(disable_mux); |
| |
| static ssize_t disable_mux_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| enum mipicsi_top_dev device; |
| uint8_t *token; |
| const char *delim = ";"; |
| struct mipicsi_top_mux mux; |
| |
| token = strsep((char **)&buf, delim); |
| if ((token) && (find_device(token, &device) >= 0)) { |
| mux.source = device; |
| token = strsep((char **)&buf, delim); |
| if ((token) && (find_device(token, &device) >= 0)) { |
| mux.sink = device; |
| mux.ss_stream_off = true; |
| mipicsi_top_disable_mux(&mux); |
| return count; |
| } |
| } |
| pr_err("Usage: echo\"<source>;<sink>\">disable_mux\n"); |
| pr_err("source=Rx0,Rx1,Rx2,IPU sink=Tx0,Tx1,IPU\n"); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR(disable_mux, S_IRUGO | S_IWUSR | S_IWGRP, |
| disable_mux_show, disable_mux_store); |
| |
| static ssize_t get_mux_status_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| ssize_t strlen = 0; |
| strlen = snprintf((char *)buf, MAX_STR_COPY, "%s\n", string_data); |
| string_data[0] = '\0'; |
| |
| return strlen; |
| } |
| |
| static ssize_t get_mux_status_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| const size_t count) |
| { |
| enum mipicsi_top_dev device; |
| uint8_t *token; |
| const char *delim = ";"; |
| struct mipicsi_top_mux mux; |
| |
| token = strsep((char **)&buf, delim); |
| if ((token) && (find_device(token, &device) >= 0)) { |
| mux.source = device; |
| token = strsep((char **)&buf, delim); |
| if ((token) && (find_device(token, &device) >= 0)) { |
| mux.sink = device; |
| mipicsi_top_get_mux_status(&mux); |
| |
| if (mux.active == true) |
| snprintf(string_data, MAX_STR_COPY, |
| "%s\n", "Mux path active"); |
| else if (mux.active == false) |
| snprintf(string_data, MAX_STR_COPY, |
| "%s\n", "Mux path inactive"); |
| return count; |
| } |
| } |
| pr_err("Usage: echo\"<source>;<sink>;\">get_mux_status\n"); |
| pr_err("source=Rx0,Rx1,Rx2,IPU sink=Tx0,Tx1,IPU\n"); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR(get_mux_status, S_IRUGO | S_IWUSR | S_IWGRP, |
| get_mux_status_show, get_mux_status_store); |
| |
| SHOW_FMT_NA(get_device_status); |
| |
| static ssize_t get_device_status_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| const size_t count) |
| { |
| enum mipicsi_top_dev device; |
| uint8_t *token; |
| const char *delim = ";"; |
| struct mipicsi_top_reg reg; |
| uint32_t value; |
| |
| token = strsep((char **)&buf, delim); |
| if ((token) && (find_device(token, &device) >= 0)) { |
| mipicsi_util_read_top(HWIO_MIPI_TOP_CSI_CLK_CTRL_REGOFF, |
| &value); |
| switch (device) { |
| case MIPI_TX0: |
| if (value & |
| HWIO_MIPI_TOP_CSI_CLK_CTRL_CSI2_TX0_CG_FLDMASK) { |
| pr_err("Clock is not enabled to the device\n"); |
| break; |
| } |
| mipicsi_util_read_top(HWIO_MIPI_TOP_TX0_BYPINT_REGOFF, |
| &value); |
| if (value & HWIO_MIPI_TOP_TX0_BYPINT_TX0_BYP_OF_FLDMASK) |
| pr_err("Bypass overflow ocurred\n"); |
| break; |
| |
| case MIPI_TX1: |
| if (value & |
| HWIO_MIPI_TOP_CSI_CLK_CTRL_CSI2_TX1_CG_FLDMASK) { |
| pr_err("Clock is not enabled to the device\n"); |
| break; |
| } |
| mipicsi_util_read_top(HWIO_MIPI_TOP_TX1_BYPINT_REGOFF, |
| &value); |
| if (value & HWIO_MIPI_TOP_TX1_BYPINT_TX1_BYP_OF_FLDMASK) |
| pr_err("Bypass overflow ocurred\n"); |
| break; |
| |
| case MIPI_RX0: |
| if (value & |
| HWIO_MIPI_TOP_CSI_CLK_CTRL_CSI2_RX0_CG_FLDMASK) |
| pr_err("Clock is not enabled to the device\n"); |
| break; |
| |
| case MIPI_RX1: |
| if (value & |
| HWIO_MIPI_TOP_CSI_CLK_CTRL_CSI2_RX1_CG_FLDMASK) |
| pr_err("Clock is not enabled to the device\n"); |
| break; |
| |
| case MIPI_RX2: |
| if (value & |
| HWIO_MIPI_TOP_CSI_CLK_CTRL_CSI2_RX2_CG_FLDMASK) |
| pr_err("Clock is not enabled to the device\v"); |
| break; |
| |
| default: |
| pr_err("Usage: echo\"<device>;\">get_device_status\n"); |
| pr_err("source=Rx0,Rx1,Rx2,Tx0,Tx1"); |
| return -EINVAL; |
| } |
| |
| if ((device == MIPI_TX0) || (device == MIPI_TX1)) { |
| reg.dev = device; |
| reg.offset = HWIO_MIPI_TX_PHY_STATUS_REGOFF; |
| mipicsi_top_read(®); |
| if (reg.value == 0x155B) |
| pr_err("Phy is in stop state\n"); |
| else if (reg.value & 0x000B) |
| pr_err("Data is going over Phy\n"); |
| |
| reg.offset = HWIO_MIPI_TX_INT_ST_VPG_REGOFF; |
| mipicsi_top_read(®); |
| if (reg.value) |
| pr_err("VPG errors %d\n", reg.value); |
| |
| reg.offset = HWIO_MIPI_TX_INT_ST_IDI_REGOFF; |
| mipicsi_top_read(®); |
| if (reg.value) |
| pr_err("IDI errors %d\n", reg.value); |
| |
| reg.offset = HWIO_MIPI_TX_INT_ST_PHY_REGOFF; |
| mipicsi_top_read(®); |
| if (reg.value) |
| pr_err("Phy errors %d\n", reg.value); |
| } else { |
| reg.dev = device; |
| reg.offset = HWIO_MIPI_RX_PHY_STOPSTATE_REGOFF; |
| mipicsi_top_read(®); |
| pr_err("Phy stopstate is 0x%x\n", reg.value); |
| |
| reg.offset = HWIO_MIPI_RX_PHY_RX_REGOFF; |
| mipicsi_top_read(®); |
| pr_err("Phy state is 0x%x\n", reg.value); |
| |
| reg.offset = HWIO_MIPI_RX_INT_ST_PHY_FATAL_REGOFF; |
| mipicsi_top_read(®); |
| if (reg.value & 0xF) |
| pr_err("SOT error on lanes[3:0] 0x%x\n", |
| reg.value & 0xF); |
| |
| reg.offset = HWIO_MIPI_RX_INT_ST_PKT_FATAL_REGOFF; |
| mipicsi_top_read(®); |
| if (reg.value & 0xF) |
| pr_err("Checksum error on VC[3:0] 0x%x\n", |
| reg.value & 0xF); |
| |
| reg.offset = HWIO_MIPI_RX_INT_ST_FRAME_FATAL_REGOFF; |
| mipicsi_top_read(®); |
| if (reg.value & 0xF0000) |
| pr_err("CRC error on VC[3:0] 0x%x\n", |
| (reg.value & 0xF0000)>>16); |
| if (reg.value & 0xF00) |
| pr_err("Incorrect frame seq on VC[3:0] 0x%x\n", |
| (reg.value & 0xF00)>>8); |
| if (reg.value & 0xF) |
| pr_err("Mistmatch frame start/end VC[3:0] 0x%x\n", |
| reg.value & 0xF); |
| |
| reg.offset = HWIO_MIPI_RX_INT_ST_PHY_REGOFF; |
| mipicsi_top_read(®); |
| if (reg.value & 0xF0000) |
| pr_err("SOT error no sync on Lane[3:0] 0x%x\n", |
| (reg.value & 0xF0000)>>16); |
| if (reg.value & 0xF) |
| pr_err("SOT error on Lane[3:0] 0x%x\n", |
| reg.value & 0xF); |
| |
| reg.offset = HWIO_MIPI_RX_INT_ST_PKT_REGOFF; |
| mipicsi_top_read(®); |
| if (reg.value & 0xF0000) |
| pr_err("Header error corrected on VC[3:0] 0x%x\n", |
| (reg.value & 0xF0000)>>16); |
| if (reg.value & 0xF) |
| pr_err("DT error on VC[3:0] 0x%x\n", |
| reg.value & 0xF); |
| |
| reg.offset = HWIO_MIPI_RX_INT_ST_LINE_REGOFF; |
| mipicsi_top_read(®); |
| if (reg.value & 0xF0000) |
| pr_err("Line sequence error VC[3:0] 0x%x\n", |
| (reg.value & 0xF0000)>>16); |
| if (reg.value & 0xF) |
| pr_err("Line boundary error DT/VC[3:0] 0x%x\n", |
| reg.value & 0xF); |
| } |
| return count; |
| } |
| pr_err("Usage: echo\"<device>;\">get_device_status\n"); |
| pr_err("source=Rx0,Rx1,Rx2,Tx0,Tx1"); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR(get_device_status, S_IRUGO | S_IWUSR | S_IWGRP, |
| get_device_status_show, get_device_status_store); |
| |
| SHOW_FMT_NA(enable_irq); |
| |
| static ssize_t enable_irq_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| enum mipicsi_top_dev device; |
| struct mipi_device_irq_mask dmask; |
| struct mipi_host_irq_mask hmask; |
| |
| if (find_device(buf, &device) >= 0) { |
| switch (device) { |
| case MIPI_RX0: |
| case MIPI_RX1: |
| case MIPI_RX2: |
| hmask.phy_fatal = 0xFFFFFFFF; |
| hmask.pkt_fatal = 0xFFFFFFFF; |
| hmask.frame_fatal = 0xFFFFFFFF; |
| hmask.phy = 0xFFFFFFFF; |
| hmask.pkt = 0xFFFFFFFF; |
| hmask.line = 0xFFFFFFFF; |
| mipicsi_host_set_interrupt_mask(device, &hmask); |
| return count; |
| |
| case MIPI_TX0: |
| case MIPI_TX1: |
| dmask.vpg = 0xFFFFFFFF; |
| dmask.idi = 0xFFFFFFFF; |
| dmask.phy = 0xFFFFFFFF; |
| mipicsi_device_set_interrupt_mask(device, &dmask); |
| mipicsi_util_write_top(HWIO_MIPI_TOP_TX0_BYPINT_REGOFF, |
| 0x2); |
| mipicsi_util_write_top(HWIO_MIPI_TOP_TX1_BYPINT_REGOFF, |
| 0x2); |
| return count; |
| |
| default: |
| break; |
| } |
| } |
| pr_err("Usage: echo\"<dev>\">enable_irq\n"); |
| pr_err("dev=Rx0,Rx1,Rx2,Tx0,Tx1\n"); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR(enable_irq, S_IRUGO | S_IWUSR | S_IWGRP, |
| enable_irq_show, enable_irq_store); |
| |
| SHOW_FMT_NA(vpg_preset); |
| |
| static ssize_t vpg_preset_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| const size_t count) |
| { |
| enum mipicsi_top_dev device; |
| uint8_t *token; |
| const char *delim = ";"; |
| struct mipicsi_top_vpg vpg; |
| |
| token = strsep((char **)&buf, delim); |
| if ((token) && (find_device(token, &device) >= 0)) { |
| vpg.dev = device; |
| token = strsep((char **)&buf, delim); |
| if (token) { |
| if (!strncmp (token, "VGA", 3)) { |
| mipicsi_debug_vpg_preset(&vpg, VPG_VGA); |
| return count; |
| } |
| else if (!strncmp (token, "1080P", 5)) { |
| mipicsi_debug_vpg_preset(&vpg, VPG_1080P); |
| return count; |
| } |
| else if (!strncmp (token, "12MP", 4)) { |
| mipicsi_debug_vpg_preset(&vpg, VPG_12MP); |
| return count; |
| } |
| } |
| } |
| pr_err("Usage: echo\"<dev>;<resolution>\">vpg_preset\n"); |
| pr_err("dev=Tx0,Tx1, resolution=VGA,1080P,12MP\n"); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR(vpg_preset, S_IRUGO | S_IWUSR | S_IWGRP, |
| vpg_preset_show, vpg_preset_store); |
| |
| SHOW_FMT_NA(bist_start); |
| |
| static ssize_t bist_start_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| enum mipicsi_top_dev device; |
| |
| if (find_device(buf, &device) >= 0) { |
| mipicsi_top_debug_bist_start(device); |
| return count; |
| } |
| pr_err("Usage: echo\"<dev>\">bist_start\n"); |
| pr_err("dev=Rx0,Rx1,Rx2,Tx0,Tx1\n"); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR(bist_start, S_IRUGO | S_IWUSR | S_IWGRP, |
| bist_start_show, bist_start_store); |
| |
| static ssize_t bist_status_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| ssize_t strlen = 0; |
| strlen = snprintf((char *)buf, MAX_STR_COPY, "%s\n", string_data); |
| string_data[0] = '\0'; |
| |
| return strlen; |
| } |
| |
| static ssize_t bist_status_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| const size_t count) |
| { |
| enum mipicsi_top_dev device; |
| struct mipicsi_top_bist bist; |
| |
| if (find_device(buf, &device) >= 0) { |
| mipicsi_top_debug_bist_status(&bist); |
| if (bist.done == true) { |
| if (bist.ok == true) |
| snprintf(string_data, MAX_STR_COPY, |
| "%s\n", "BIST OK"); |
| else |
| snprintf(string_data, MAX_STR_COPY, |
| "%s\n", "BIST Failed"); |
| } else { |
| snprintf(string_data, MAX_STR_COPY, |
| "%s\n", "BIST In Progress"); |
| } |
| return count; |
| } |
| pr_err("Usage: echo\"<dev>\">bist_status\n"); |
| pr_err("dev=Rx0,Rx1,Rx2,Tx0,Tx1\n"); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR(bist_status, S_IRUGO | S_IWUSR | S_IWGRP, |
| bist_status_show, bist_status_store); |
| |
| static ssize_t reg_read_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| ssize_t strlen = 0; |
| strlen = snprintf(buf, MAX_STR_COPY, "0x%lx\n", |
| (unsigned long) read_data); |
| read_data = 0; |
| |
| return strlen; |
| } |
| |
| static ssize_t reg_read_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| uint32_t val; |
| enum mipicsi_top_dev device; |
| uint8_t *token; |
| const char *delim = ";"; |
| struct mipicsi_top_reg reg; |
| |
| token = strsep((char **)&buf, delim); |
| if ((token) && (find_device(token, &device) >= 0)) { |
| reg.dev = device; |
| token = strsep((char **)&buf, delim); |
| if ((token) && (!(kstrtou32(token, 0, &val))) |
| && (val >= 0) && (val <= 0xFFFF)) { |
| reg.offset = val; |
| mipicsi_top_read(®); |
| read_data = reg.value; |
| pr_err("Reg Read: Offset 0x%x, Value 0x%x\n", |
| (unsigned int)reg.offset, (unsigned int)reg.value); |
| return count; |
| } |
| } |
| pr_err("Usage: echo\"<dev>;<offset>;\">reg_read\n"); |
| pr_err("dev=Rx0,Rx1,Rx2,Tx0,Tx1\n"); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR(reg_read, S_IRUGO | S_IWUSR | S_IWGRP, |
| reg_read_show, reg_read_store); |
| |
| SHOW_FMT_NA(reg_write); |
| |
| static ssize_t reg_write_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| uint32_t val; |
| enum mipicsi_top_dev device; |
| uint8_t *token; |
| const char *delim = ";"; |
| struct mipicsi_top_reg reg; |
| |
| token = strsep((char **)&buf, delim); |
| if ((token) && (find_device(token, &device) >= 0)) { |
| reg.dev = device; |
| token = strsep((char **)&buf, delim); |
| |
| if ((token) && (!(kstrtou32(token, 0, &val))) |
| && (val >= 0) && (val <= 0xFFFF)) { |
| reg.offset = val; |
| token = strsep((char **)&buf, delim); |
| if ((token) && (!(kstrtou32(token, 0, &val)))) { |
| reg.value = val; |
| mipicsi_top_write(®); |
| return count; |
| } |
| } |
| } |
| pr_err("Usage: echo\"<dev>;<offset>;<value>;\">reg_write\n"); |
| pr_err("dev=Rx0,Rx1,Rx2,Tx0,Tx1\n"); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR(reg_write, S_IRUGO | S_IWUSR | S_IWGRP, |
| reg_write_show, reg_write_store); |
| |
| static ssize_t dphy_read_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| ssize_t strlen = 0; |
| |
| strlen = snprintf(buf, MAX_STR_COPY, "0x%lx\n", |
| (unsigned long)read_data); |
| read_data = 0; |
| |
| return strlen; |
| } |
| |
| static ssize_t dphy_read_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| uint32_t val; |
| enum mipicsi_top_dev device; |
| uint8_t *token; |
| const char *delim = ";"; |
| struct mipicsi_top_reg reg; |
| |
| token = strsep((char **)&buf, delim); |
| if ((token) && (find_device(token, &device) >= 0)) { |
| reg.dev = device; |
| token = strsep((char **)&buf, delim); |
| if ((token) && (!(kstrtou32(token, 0, &val))) |
| && (val >= 0) && (val <= 0xFFFF)) { |
| reg.offset = val; |
| mipicsi_top_dphy_read(®); |
| read_data = reg.value; |
| pr_err("DPhy Read: Offset 0x%x, Val 0x%x\n", reg.offset, |
| reg.value); |
| /* Get the device out of shutdown */ |
| if ((reg.dev == MIPI_TX0) || |
| (reg.dev == MIPI_TX1)) { |
| reg.offset = HWIO_MIPI_TX_PHY_RSTZ_REGOFF; |
| reg.value = 0x07; |
| mipicsi_top_write(®); |
| } else { |
| reg.offset = HWIO_MIPI_RX_PHY_SHUTDOWNZ_REGOFF; |
| reg.value = 1; |
| mipicsi_top_write(®); |
| reg.offset = HWIO_MIPI_RX_DPHY_RSTZ_REGOFF; |
| reg.value = 1; |
| mipicsi_top_write(®); |
| } |
| return count; |
| } |
| } |
| pr_err("Usage: echo\"<dev>;<offset>;\">dphy_read\n"); |
| pr_err("dev=Rx0,Rx1,Rx2,Tx0,Tx1\n"); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR(dphy_read, S_IRUGO | S_IWUSR | S_IWGRP, |
| dphy_read_show, dphy_read_store); |
| |
| SHOW_FMT_NA(dphy_write); |
| |
| static ssize_t dphy_write_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| uint32_t val; |
| enum mipicsi_top_dev device; |
| uint8_t *token; |
| const char *delim = ";"; |
| struct mipicsi_top_reg reg; |
| |
| token = strsep((char **)&buf, delim); |
| if ((token) && (find_device(token, &device) >= 0)) { |
| reg.dev = device; |
| token = strsep((char **)&buf, delim); |
| |
| if ((token) && (!(kstrtou32(token, 0, &val))) |
| && (val >= 0) && (val <= 0xFFFF)) { |
| reg.offset = val; |
| token = strsep((char **)&buf, delim); |
| if ((token) && (!(kstrtou32(token, 0, &val)))) { |
| reg.value = val; |
| mipicsi_top_dphy_write(®); |
| /* Take the device out of shutdown */ |
| if ((reg.dev == MIPI_TX0) || (reg.dev == MIPI_TX1)) { |
| reg.offset = HWIO_MIPI_TX_PHY_RSTZ_REGOFF; |
| reg.value = 0x07; |
| mipicsi_top_write(®); |
| } else { |
| reg.offset = HWIO_MIPI_RX_PHY_SHUTDOWNZ_REGOFF; |
| reg.value = 1; |
| mipicsi_top_write(®); |
| reg.offset = HWIO_MIPI_RX_DPHY_RSTZ_REGOFF; |
| reg.value = 1; |
| mipicsi_top_write(®); |
| } |
| return count; |
| } |
| } |
| } |
| pr_err("Usage: echo\"<dev>;<offset>;<value>;\">dphy_write\n"); |
| pr_err("dev=Rx0,Rx1,Rx2,Tx0,Tx1\n"); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR(dphy_write, S_IRUGO | S_IWUSR | S_IWGRP, |
| dphy_write_show, dphy_write_store); |
| |
| |
| SHOW_FMT_NA(dump_regs); |
| |
| static ssize_t dump_regs_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| enum mipicsi_top_dev device; |
| |
| if (find_device(buf, &device) >= 0) { |
| mipicsi_debug_dump(device); |
| return count; |
| } |
| pr_err("Usage: echo\"<dev>\">dump_regs\n"); |
| pr_err("dev=Rx0,Rx1,Rx2,Tx0,Tx1\n"); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR(dump_regs, S_IRUGO | S_IWUSR | S_IWGRP, |
| dump_regs_show, dump_regs_store); |
| int mipicsi_sysfs_init(struct device *mipicsi_top_device) |
| { |
| int ret; |
| |
| pr_debug("MIPI TOP: init_sysfs\n"); |
| ret = device_create_file(mipicsi_top_device, |
| &dev_attr_start_dev); |
| if (ret) { |
| dev_err(mipicsi_top_device, |
| "Failed to create sysfs: start_dev\n"); |
| return -EINVAL; |
| } |
| |
| ret = device_create_file(mipicsi_top_device, |
| &dev_attr_stop_dev); |
| if (ret) { |
| dev_err(mipicsi_top_device, |
| "Failed to create sysfs: stop_dev\n"); |
| return -EINVAL; |
| } |
| |
| ret = device_create_file(mipicsi_top_device, |
| &dev_attr_reset_dev); |
| if (ret) { |
| dev_err(mipicsi_top_device, |
| "Failed to create sysfs: reset_dev\n"); |
| return -EINVAL; |
| } |
| |
| ret = device_create_file(mipicsi_top_device, |
| &dev_attr_set_mux); |
| if (ret) { |
| dev_err(mipicsi_top_device, |
| "Failed to create sysfs: set_mux\n"); |
| return -EINVAL; |
| } |
| |
| ret = device_create_file(mipicsi_top_device, |
| &dev_attr_disable_mux); |
| if (ret) { |
| dev_err(mipicsi_top_device, |
| "Failed to create sysfs: disable_mux\n"); |
| return -EINVAL; |
| } |
| |
| ret = device_create_file(mipicsi_top_device, |
| &dev_attr_get_mux_status); |
| if (ret) { |
| dev_err(mipicsi_top_device, |
| "Failed to create sysfs: get_mux_status\n"); |
| return -EINVAL; |
| } |
| |
| ret = device_create_file(mipicsi_top_device, |
| &dev_attr_get_device_status); |
| if (ret) { |
| dev_err(mipicsi_top_device, |
| "Failed to create sysfs: get_device_status\n"); |
| return -EINVAL; |
| } |
| |
| ret = device_create_file(mipicsi_top_device, |
| &dev_attr_enable_irq); |
| if (ret) { |
| dev_err(mipicsi_top_device, |
| "Failed to create sysfs: enable_irq\n"); |
| return -EINVAL; |
| } |
| |
| ret = device_create_file(mipicsi_top_device, |
| &dev_attr_vpg_preset); |
| if (ret) { |
| dev_err(mipicsi_top_device, |
| "Failed to create sysfs: vpg_preset\n"); |
| return -EINVAL; |
| } |
| |
| ret = device_create_file(mipicsi_top_device, |
| &dev_attr_bist_start); |
| if (ret) { |
| dev_err(mipicsi_top_device, |
| "Failed to create sysfs: bist_start\n"); |
| return -EINVAL; |
| } |
| |
| ret = device_create_file(mipicsi_top_device, |
| &dev_attr_bist_status); |
| if (ret) { |
| dev_err(mipicsi_top_device, |
| "Failed to create sysfs: bist_status\n"); |
| return -EINVAL; |
| } |
| |
| ret = device_create_file(mipicsi_top_device, |
| &dev_attr_reg_read); |
| if (ret) { |
| dev_err(mipicsi_top_device, |
| "Failed to create sysfs: reg_read\n"); |
| return -EINVAL; |
| } |
| |
| ret = device_create_file(mipicsi_top_device, |
| &dev_attr_reg_write); |
| if (ret) { |
| dev_err(mipicsi_top_device, |
| "Failed to create sysfs: reg_write\n"); |
| return -EINVAL; |
| } |
| |
| ret = device_create_file(mipicsi_top_device, |
| &dev_attr_dphy_read); |
| if (ret) { |
| dev_err(mipicsi_top_device, |
| "Failed to create sysfs: dphy_read\n"); |
| return -EINVAL; |
| } |
| |
| ret = device_create_file(mipicsi_top_device, |
| &dev_attr_dphy_write); |
| if (ret) { |
| dev_err(mipicsi_top_device, |
| "Failed to create sysfs: dphy_write\n"); |
| return -EINVAL; |
| } |
| |
| ret = device_create_file(mipicsi_top_device, |
| &dev_attr_dump_regs); |
| if (ret) { |
| dev_err(mipicsi_top_device, |
| "Failed to create sysfs: dump_regs\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| void mipicsi_sysfs_clean(struct device *mipicsi_top_device) |
| { |
| device_remove_file(mipicsi_top_device, |
| &dev_attr_start_dev); |
| |
| device_remove_file(mipicsi_top_device, |
| &dev_attr_stop_dev); |
| |
| device_remove_file(mipicsi_top_device, |
| &dev_attr_reset_dev); |
| |
| device_remove_file(mipicsi_top_device, |
| &dev_attr_set_mux); |
| |
| device_remove_file(mipicsi_top_device, |
| &dev_attr_disable_mux); |
| |
| device_remove_file(mipicsi_top_device, |
| &dev_attr_get_mux_status); |
| |
| device_remove_file(mipicsi_top_device, |
| &dev_attr_get_device_status); |
| |
| device_remove_file(mipicsi_top_device, |
| &dev_attr_enable_irq); |
| |
| device_remove_file(mipicsi_top_device, |
| &dev_attr_reg_read); |
| |
| device_remove_file(mipicsi_top_device, |
| &dev_attr_reg_write); |
| |
| device_remove_file(mipicsi_top_device, |
| &dev_attr_vpg_preset); |
| |
| device_remove_file(mipicsi_top_device, |
| &dev_attr_dump_regs); |
| } |