| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * xHCI host controller toolkit driver |
| * |
| * Copyright (C) 2021 MediaTek Inc. |
| * |
| * Author: Zhanyong Wang <zhanyong.wang@mediatek.com> |
| */ |
| |
| #include <linux/platform_device.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include <linux/usb.h> |
| #include <linux/of.h> |
| #include <linux/of_address.h> |
| #include "xhci-mtk.h" |
| #include "xhci-mtk-test.h" |
| #include "xhci-mtk-unusual.h" |
| |
| |
| u32 binary_write_width1(u32 __iomem *addr, u32 shift, const char *buf) |
| { |
| u32 val = 0; |
| |
| if (!strncmp(buf, STRNG_0_WIDTH_1, BIT_WIDTH_1)) |
| val = 0; |
| else if (!strncmp(buf, STRNG_1_WIDTH_1, BIT_WIDTH_1)) |
| val = 1; |
| else |
| val = 0xFFFFFFFF; |
| |
| if (val <= 1) |
| val = usb20hqa_write(addr, shift, MSK_WIDTH_1, val); |
| |
| return val; |
| } |
| |
| u32 binary_write_width2(u32 __iomem *addr, u32 shift, const char *buf) |
| { |
| u32 val = 0; |
| |
| if (!strncmp(buf, STRNG_0_WIDTH_2, BIT_WIDTH_2)) |
| val = 0; |
| else if (!strncmp(buf, STRNG_1_WIDTH_2, BIT_WIDTH_2)) |
| val = 1; |
| else if (!strncmp(buf, STRNG_2_WIDTH_2, BIT_WIDTH_2)) |
| val = 2; |
| else if (!strncmp(buf, STRNG_3_WIDTH_2, BIT_WIDTH_2)) |
| val = 3; |
| else |
| val = 0xFFFFFFFF; |
| |
| if (val <= 3) |
| val = usb20hqa_write(addr, shift, MSK_WIDTH_2, val); |
| |
| return val; |
| } |
| |
| u32 binary_write_width3(u32 __iomem *addr, u32 shift, const char *buf) |
| { |
| u32 val = 0; |
| |
| if (!strncmp(buf, STRNG_0_WIDTH_3, BIT_WIDTH_3)) |
| val = 0; |
| else if (!strncmp(buf, STRNG_1_WIDTH_3, BIT_WIDTH_3)) |
| val = 1; |
| else if (!strncmp(buf, STRNG_2_WIDTH_3, BIT_WIDTH_3)) |
| val = 2; |
| else if (!strncmp(buf, STRNG_3_WIDTH_3, BIT_WIDTH_3)) |
| val = 3; |
| else if (!strncmp(buf, STRNG_4_WIDTH_3, BIT_WIDTH_3)) |
| val = 4; |
| else if (!strncmp(buf, STRNG_5_WIDTH_3, BIT_WIDTH_3)) |
| val = 5; |
| else if (!strncmp(buf, STRNG_6_WIDTH_3, BIT_WIDTH_3)) |
| val = 6; |
| else if (!strncmp(buf, STRNG_7_WIDTH_3, BIT_WIDTH_3)) |
| val = 7; |
| else |
| val = 0xFFFFFFFF; |
| |
| if (val <= 7) |
| val = usb20hqa_write(addr, shift, MSK_WIDTH_3, val); |
| |
| return val; |
| } |
| |
| u32 binary_write_width4(u32 __iomem *addr, u32 shift, const char *buf) |
| { |
| u32 val = 0; |
| |
| if (!strncmp(buf, STRNG_0_WIDTH_4, BIT_WIDTH_4)) |
| val = 0; |
| else if (!strncmp(buf, STRNG_1_WIDTH_4, BIT_WIDTH_4)) |
| val = 1; |
| else if (!strncmp(buf, STRNG_2_WIDTH_4, BIT_WIDTH_4)) |
| val = 2; |
| else if (!strncmp(buf, STRNG_3_WIDTH_4, BIT_WIDTH_4)) |
| val = 3; |
| else if (!strncmp(buf, STRNG_4_WIDTH_4, BIT_WIDTH_4)) |
| val = 4; |
| else if (!strncmp(buf, STRNG_5_WIDTH_4, BIT_WIDTH_4)) |
| val = 5; |
| else if (!strncmp(buf, STRNG_6_WIDTH_4, BIT_WIDTH_4)) |
| val = 6; |
| else if (!strncmp(buf, STRNG_7_WIDTH_4, BIT_WIDTH_4)) |
| val = 7; |
| else if (!strncmp(buf, STRNG_8_WIDTH_4, BIT_WIDTH_4)) |
| val = 8; |
| else if (!strncmp(buf, STRNG_9_WIDTH_4, BIT_WIDTH_4)) |
| val = 9; |
| else if (!strncmp(buf, STRNG_A_WIDTH_4, BIT_WIDTH_4)) |
| val = 10; |
| else if (!strncmp(buf, STRNG_B_WIDTH_4, BIT_WIDTH_4)) |
| val = 11; |
| else if (!strncmp(buf, STRNG_C_WIDTH_4, BIT_WIDTH_4)) |
| val = 12; |
| else if (!strncmp(buf, STRNG_D_WIDTH_4, BIT_WIDTH_4)) |
| val = 13; |
| else if (!strncmp(buf, STRNG_E_WIDTH_4, BIT_WIDTH_4)) |
| val = 14; |
| else if (!strncmp(buf, STRNG_F_WIDTH_4, BIT_WIDTH_4)) |
| val = 15; |
| else |
| val = 0xFFFFFFFF; |
| |
| if (val <= 15) |
| val = usb20hqa_write(addr, shift, MSK_WIDTH_4, val); |
| |
| return val; |
| } |
| |
| u32 bin2str(u32 value, u32 width, char *buffer) |
| { |
| int i, temp; |
| |
| temp = value; |
| buffer[width] = '\0'; |
| for (i = (width - 1); i >= 0; i--) { |
| buffer[i] = '0'; |
| if (value % 2) |
| buffer[i] = '1'; |
| |
| value /= 2; |
| } |
| |
| return value; |
| } |
| |
| int query_phy_addr(struct device_node *np, int *start, u32 *addr, u32 *length, int type) |
| { |
| int ret = -EPERM; |
| struct of_phandle_args args; |
| struct resource res; |
| struct device_node *node = np; |
| int numphys = 0; |
| int index; |
| |
| if (np == NULL || start == NULL || addr == NULL || length == NULL) |
| return -EINVAL; |
| |
| while (node) { |
| numphys = of_count_phandle_with_args(node, |
| "phys", "#phy-cells"); |
| for (index = *start; |
| (numphys > 0) && index < numphys; index++) { |
| ret = of_parse_phandle_with_args(node, |
| "phys", "#phy-cells", |
| index, &args); |
| if (ret < 0) |
| break; |
| |
| if (args.args[0] == type) { |
| ret = of_address_to_resource(args.np, |
| 0, &res); |
| if (ret < 0) { |
| of_node_put(args.np); |
| break; |
| } |
| |
| *addr = res.start; |
| *length = (u32)resource_size(&res); |
| *start = index; |
| if (!of_device_is_available(args.np)) |
| ret = -EACCES; |
| |
| of_node_put(args.np); |
| break; |
| } |
| } |
| if (index < numphys) |
| break; |
| |
| node = node->parent; |
| } |
| |
| ret = index < numphys ? ret : -EPERM; |
| return ret; |
| } |
| |
| int query_reg_addr(struct platform_device *pdev, u32 *addr, u32 *length, const char* name) |
| { |
| int ret = -EPERM; |
| struct resource *pres; |
| struct platform_device *device = pdev; |
| |
| if (pdev == NULL || addr == NULL || length == NULL) |
| return -EINVAL; |
| |
| while (device) { |
| pres = platform_get_resource_byname(device, IORESOURCE_MEM, name); |
| if (pres != NULL) { |
| *addr = pres->start; |
| *length = (u32)resource_size(pres); |
| ret = 0; |
| break; |
| } |
| |
| if (device->dev.parent == NULL) |
| break; |
| |
| device = to_platform_device(device->dev.parent); |
| } |
| |
| return ret; |
| } |
| |