From 166fb177e4706b9eace88f06687ae1fedf79c143 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 13 Oct 2022 15:42:40 -0700 Subject: MAINTAINERS: update the 'T:' entry for xtensa All development activity for xtensa architecture have been routed through the github.com/jcmvbkbc/linux-xtensa tree for the last few years. Update the 'T:' entry in the MAINTAINERS file to reflect that. Github deprecated the git:// links about a year ago, so let's move to the https:// URLs instead. Reported-by: Conor Dooley Link: https://github.blog/2021-09-01-improving-git-protocol-security-github/ Signed-off-by: Palmer Dabbelt Signed-off-by: Max Filippov --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 72b9654f764c..98cf4476d135 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20097,7 +20097,7 @@ M: Chris Zankel M: Max Filippov L: linux-xtensa@linux-xtensa.org S: Maintained -T: git git://github.com/czankel/xtensa-linux.git +T: git https://github.com/jcmvbkbc/linux-xtensa.git F: arch/xtensa/ F: drivers/irqchip/irq-xtensa-* -- cgit v1.2.3 From c8a17756c42581ba1a567d1dd3b69e8f5619a7d8 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 11 Oct 2022 17:07:08 +0200 Subject: drm/ofdrm: Add ofdrm for Open Firmware framebuffers Open Firmware provides basic display output via the 'display' node. DT platform code already provides a device that represents the node's framebuffer. Add a DRM driver for the device. The display mode and color format is pre-initialized by the system's firmware. Runtime modesetting via DRM is not possible. The display is useful during early boot stages or as error fallback. Similar functionality is already provided by fbdev's offb driver, which is insufficient for modern userspace. The old driver includes support for BootX device tree, which can be found on old 32-bit PowerPC Macintosh systems. If these are still in use, the functionality can be added to ofdrm or implemented in a new driver. As with simpledrm, the fbdev driver cannot be selected if ofdrm is already enabled. Two notable points about the driver: * Reading the framebuffer aperture from the device tree is not reliable on all systems. Ofdrm takes the heuristics and a comment from offb to pick the correct range. * No resource management may be tied to the underlying PCI device. Otherwise the handover to the native driver will fail with a resource conflict. PCI management is therefore done as part of the platform device's cleanup. The driver has been tested on qemu's ppc64le emulation. The device hand-over has been tested with bochs. v5: * use drm_atomic_helper_check_crtc_primary_plane() v4: * set preferred depth to the correct value * set bpp value for console emulation * output scanout-buffer parameters with drm_dbg() v3: * reintegrate FWFB helpers into ofdrm * use damage iterator * sync GEM BOs with drm_gem_fb_{begin,end}_cpu_access() * fix various atomic_check helpers * remove CRTC atomic_{enable,disable} (Javier) * compute stride with drm_format_info_min_pitch() (Daniel) v2: * removed simple-pipe helpers * built driver on top of FWFB helpers * merged all init code into single function * make PCI support optional (Michal) * support COMPILE_TEST (Javier) Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas convert Link: https://patchwork.freedesktop.org/patch/msgid/20221011150712.3928-2-tzimmermann@suse.de --- MAINTAINERS | 1 + drivers/gpu/drm/tiny/Kconfig | 13 + drivers/gpu/drm/tiny/Makefile | 1 + drivers/gpu/drm/tiny/ofdrm.c | 763 ++++++++++++++++++++++++++++++++++++++++++ drivers/video/fbdev/Kconfig | 1 + 5 files changed, 779 insertions(+) create mode 100644 drivers/gpu/drm/tiny/ofdrm.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 4f2fa9925116..78f670955434 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6653,6 +6653,7 @@ L: dri-devel@lists.freedesktop.org S: Maintained T: git git://anongit.freedesktop.org/drm/drm-misc F: drivers/gpu/drm/drm_aperture.c +F: drivers/gpu/drm/tiny/ofdrm.c F: drivers/gpu/drm/tiny/simpledrm.c F: drivers/video/aperture.c F: include/drm/drm_aperture.h diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig index 565957264875..a300b03a3c7a 100644 --- a/drivers/gpu/drm/tiny/Kconfig +++ b/drivers/gpu/drm/tiny/Kconfig @@ -51,6 +51,19 @@ config DRM_GM12U320 This is a KMS driver for projectors which use the GM12U320 chipset for video transfer over USB2/3, such as the Acer C120 mini projector. +config DRM_OFDRM + tristate "Open Firmware display driver" + depends on DRM && OF && (PPC || COMPILE_TEST) + select APERTURE_HELPERS + select DRM_GEM_SHMEM_HELPER + select DRM_KMS_HELPER + help + DRM driver for Open Firmware framebuffers. + + This driver assumes that the display hardware has been initialized + by the Open Firmware before the kernel boots. Scanout buffer, size, + and display format must be provided via device tree. + config DRM_PANEL_MIPI_DBI tristate "DRM support for MIPI DBI compatible panels" depends on DRM && SPI diff --git a/drivers/gpu/drm/tiny/Makefile b/drivers/gpu/drm/tiny/Makefile index 1d9d6227e7ab..76dde89a044b 100644 --- a/drivers/gpu/drm/tiny/Makefile +++ b/drivers/gpu/drm/tiny/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_ARCPGU) += arcpgu.o obj-$(CONFIG_DRM_BOCHS) += bochs.o obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus.o obj-$(CONFIG_DRM_GM12U320) += gm12u320.o +obj-$(CONFIG_DRM_OFDRM) += ofdrm.o obj-$(CONFIG_DRM_PANEL_MIPI_DBI) += panel-mipi-dbi.o obj-$(CONFIG_DRM_SIMPLEDRM) += simpledrm.o obj-$(CONFIG_TINYDRM_HX8357D) += hx8357d.o diff --git a/drivers/gpu/drm/tiny/ofdrm.c b/drivers/gpu/drm/tiny/ofdrm.c new file mode 100644 index 000000000000..96a46078ade8 --- /dev/null +++ b/drivers/gpu/drm/tiny/ofdrm.c @@ -0,0 +1,763 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "ofdrm" +#define DRIVER_DESC "DRM driver for OF platform devices" +#define DRIVER_DATE "20220501" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 + +/* + * Helpers for display nodes + */ + +static int display_get_validated_int(struct drm_device *dev, const char *name, uint32_t value) +{ + if (value > INT_MAX) { + drm_err(dev, "invalid framebuffer %s of %u\n", name, value); + return -EINVAL; + } + return (int)value; +} + +static int display_get_validated_int0(struct drm_device *dev, const char *name, uint32_t value) +{ + if (!value) { + drm_err(dev, "invalid framebuffer %s of %u\n", name, value); + return -EINVAL; + } + return display_get_validated_int(dev, name, value); +} + +static const struct drm_format_info *display_get_validated_format(struct drm_device *dev, + u32 depth) +{ + const struct drm_format_info *info; + u32 format; + + switch (depth) { + case 8: + format = drm_mode_legacy_fb_format(8, 8); + break; + case 15: + case 16: + format = drm_mode_legacy_fb_format(16, depth); + break; + case 32: + format = drm_mode_legacy_fb_format(32, 24); + break; + default: + drm_err(dev, "unsupported framebuffer depth %u\n", depth); + return ERR_PTR(-EINVAL); + } + + info = drm_format_info(format); + if (!info) { + drm_err(dev, "cannot find framebuffer format for depth %u\n", depth); + return ERR_PTR(-EINVAL); + } + + return info; +} + +static int display_read_u32_of(struct drm_device *dev, struct device_node *of_node, + const char *name, u32 *value) +{ + int ret = of_property_read_u32(of_node, name, value); + + if (ret) + drm_err(dev, "cannot parse framebuffer %s: error %d\n", name, ret); + return ret; +} + +static int display_get_width_of(struct drm_device *dev, struct device_node *of_node) +{ + u32 width; + int ret = display_read_u32_of(dev, of_node, "width", &width); + + if (ret) + return ret; + return display_get_validated_int0(dev, "width", width); +} + +static int display_get_height_of(struct drm_device *dev, struct device_node *of_node) +{ + u32 height; + int ret = display_read_u32_of(dev, of_node, "height", &height); + + if (ret) + return ret; + return display_get_validated_int0(dev, "height", height); +} + +static int display_get_depth_of(struct drm_device *dev, struct device_node *of_node) +{ + u32 depth; + int ret = display_read_u32_of(dev, of_node, "depth", &depth); + + if (ret) + return ret; + return display_get_validated_int0(dev, "depth", depth); +} + +static int display_get_linebytes_of(struct drm_device *dev, struct device_node *of_node) +{ + u32 linebytes; + int ret = display_read_u32_of(dev, of_node, "linebytes", &linebytes); + + if (ret) + return ret; + return display_get_validated_int(dev, "linebytes", linebytes); +} + +static u64 display_get_address_of(struct drm_device *dev, struct device_node *of_node) +{ + u32 address; + int ret; + + /* + * Not all devices provide an address property, it's not + * a bug if this fails. The driver will try to find the + * framebuffer base address from the device's memory regions. + */ + ret = of_property_read_u32(of_node, "address", &address); + if (ret) + return OF_BAD_ADDR; + + return address; +} + +/* + * Open Firmware display device + */ + +struct ofdrm_device { + struct drm_device dev; + struct platform_device *pdev; + + /* firmware-buffer settings */ + struct iosys_map screen_base; + struct drm_display_mode mode; + const struct drm_format_info *format; + unsigned int pitch; + + /* modesetting */ + uint32_t formats[8]; + struct drm_plane primary_plane; + struct drm_crtc crtc; + struct drm_encoder encoder; + struct drm_connector connector; +}; + +static struct ofdrm_device *ofdrm_device_of_dev(struct drm_device *dev) +{ + return container_of(dev, struct ofdrm_device, dev); +} + +/* + * Hardware + */ + +#if defined(CONFIG_PCI) +static struct pci_dev *display_get_pci_dev_of(struct drm_device *dev, struct device_node *of_node) +{ + const __be32 *vendor_p, *device_p; + u32 vendor, device; + struct pci_dev *pcidev; + + vendor_p = of_get_property(of_node, "vendor-id", NULL); + if (!vendor_p) + return ERR_PTR(-ENODEV); + vendor = be32_to_cpup(vendor_p); + + device_p = of_get_property(of_node, "device-id", NULL); + if (!device_p) + return ERR_PTR(-ENODEV); + device = be32_to_cpup(device_p); + + pcidev = pci_get_device(vendor, device, NULL); + if (!pcidev) + return ERR_PTR(-ENODEV); + + return pcidev; +} + +static void ofdrm_pci_release(void *data) +{ + struct pci_dev *pcidev = data; + + pci_disable_device(pcidev); +} + +static int ofdrm_device_init_pci(struct ofdrm_device *odev) +{ + struct drm_device *dev = &odev->dev; + struct platform_device *pdev = to_platform_device(dev->dev); + struct device_node *of_node = pdev->dev.of_node; + struct pci_dev *pcidev; + int ret; + + /* + * Never use pcim_ or other managed helpers on the returned PCI + * device. Otherwise, probing the native driver will fail for + * resource conflicts. PCI-device management has to be tied to + * the lifetime of the platform device until the native driver + * takes over. + */ + pcidev = display_get_pci_dev_of(dev, of_node); + if (IS_ERR(pcidev)) + return 0; /* no PCI device found; ignore the error */ + + ret = pci_enable_device(pcidev); + if (ret) { + drm_err(dev, "pci_enable_device(%s) failed: %d\n", + dev_name(&pcidev->dev), ret); + return ret; + } + ret = devm_add_action_or_reset(&pdev->dev, ofdrm_pci_release, pcidev); + if (ret) + return ret; + + return 0; +} +#else +static int ofdrm_device_init_pci(struct ofdrm_device *odev) +{ + return 0; +} +#endif + +/* + * OF display settings + */ + +static struct resource *ofdrm_find_fb_resource(struct ofdrm_device *odev, + struct resource *fb_res) +{ + struct platform_device *pdev = to_platform_device(odev->dev.dev); + struct resource *res, *max_res = NULL; + u32 i; + + for (i = 0; pdev->num_resources; ++i) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) + break; /* all resources processed */ + if (resource_size(res) < resource_size(fb_res)) + continue; /* resource too small */ + if (fb_res->start && resource_contains(res, fb_res)) + return res; /* resource contains framebuffer */ + if (!max_res || resource_size(res) > resource_size(max_res)) + max_res = res; /* store largest resource as fallback */ + } + + return max_res; +} + +/* + * Modesetting + */ + +/* + * Support all formats of OF display and maybe more; in order + * of preference. The display's update function will do any + * conversion necessary. + * + * TODO: Add blit helpers for remaining formats and uncomment + * constants. + */ +static const uint32_t ofdrm_primary_plane_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565, + //DRM_FORMAT_XRGB1555, + //DRM_FORMAT_C8, +}; + +static const uint64_t ofdrm_primary_plane_format_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static int ofdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *new_state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane); + struct drm_crtc *new_crtc = new_plane_state->crtc; + struct drm_crtc_state *new_crtc_state = NULL; + + if (new_crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc); + + return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, false); +} + +static void ofdrm_primary_plane_helper_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_device *dev = plane->dev; + struct ofdrm_device *odev = ofdrm_device_of_dev(dev); + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + struct drm_framebuffer *fb = plane_state->fb; + unsigned int dst_pitch = odev->pitch; + const struct drm_format_info *dst_format = odev->format; + struct drm_atomic_helper_damage_iter iter; + struct drm_rect damage; + int ret, idx; + + ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); + if (ret) + return; + + if (!drm_dev_enter(dev, &idx)) + goto out_drm_gem_fb_end_cpu_access; + + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); + drm_atomic_for_each_plane_damage(&iter, &damage) { + struct iosys_map dst = odev->screen_base; + struct drm_rect dst_clip = plane_state->dst; + + if (!drm_rect_intersect(&dst_clip, &damage)) + continue; + + iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip)); + drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb, + &damage); + } + + drm_dev_exit(idx); +out_drm_gem_fb_end_cpu_access: + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); +} + +static void ofdrm_primary_plane_helper_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_device *dev = plane->dev; + struct ofdrm_device *odev = ofdrm_device_of_dev(dev); + struct iosys_map dst = odev->screen_base; + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); + void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */ + unsigned int dst_pitch = odev->pitch; + const struct drm_format_info *dst_format = odev->format; + struct drm_rect dst_clip; + unsigned long lines, linepixels, i; + int idx; + + drm_rect_init(&dst_clip, + plane_state->src_x >> 16, plane_state->src_y >> 16, + plane_state->src_w >> 16, plane_state->src_h >> 16); + + lines = drm_rect_height(&dst_clip); + linepixels = drm_rect_width(&dst_clip); + + if (!drm_dev_enter(dev, &idx)) + return; + + /* Clear buffer to black if disabled */ + dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip); + for (i = 0; i < lines; ++i) { + memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]); + dst_vmap += dst_pitch; + } + + drm_dev_exit(idx); +} + +static const struct drm_plane_helper_funcs ofdrm_primary_plane_helper_funcs = { + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .atomic_check = ofdrm_primary_plane_helper_atomic_check, + .atomic_update = ofdrm_primary_plane_helper_atomic_update, + .atomic_disable = ofdrm_primary_plane_helper_atomic_disable, +}; + +static const struct drm_plane_funcs ofdrm_primary_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + DRM_GEM_SHADOW_PLANE_FUNCS, +}; + +static enum drm_mode_status ofdrm_crtc_helper_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct ofdrm_device *odev = ofdrm_device_of_dev(crtc->dev); + + return drm_crtc_helper_mode_valid_fixed(crtc, mode, &odev->mode); +} + +static int ofdrm_crtc_helper_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_state *new_state) +{ + struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc); + + if (!new_crtc_state->enable) + return 0; + + return drm_atomic_helper_check_crtc_primary_plane(new_crtc_state); +} + +/* + * The CRTC is always enabled. Screen updates are performed by + * the primary plane's atomic_update function. Disabling clears + * the screen in the primary plane's atomic_disable function. + */ +static const struct drm_crtc_helper_funcs ofdrm_crtc_helper_funcs = { + .mode_valid = ofdrm_crtc_helper_mode_valid, + .atomic_check = ofdrm_crtc_helper_atomic_check, +}; + +static const struct drm_crtc_funcs ofdrm_crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; + +static int ofdrm_connector_helper_get_modes(struct drm_connector *connector) +{ + struct ofdrm_device *odev = ofdrm_device_of_dev(connector->dev); + + return drm_connector_helper_get_modes_fixed(connector, &odev->mode); +} + +static const struct drm_connector_helper_funcs ofdrm_connector_helper_funcs = { + .get_modes = ofdrm_connector_helper_get_modes, +}; + +static const struct drm_connector_funcs ofdrm_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static const struct drm_mode_config_funcs ofdrm_mode_config_funcs = { + .fb_create = drm_gem_fb_create_with_dirty, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +/* + * Init / Cleanup + */ + +static struct drm_display_mode ofdrm_mode(unsigned int width, unsigned int height) +{ + /* + * Assume a monitor resolution of 96 dpi to + * get a somewhat reasonable screen size. + */ + const struct drm_display_mode mode = { + DRM_MODE_INIT(60, width, height, + DRM_MODE_RES_MM(width, 96ul), + DRM_MODE_RES_MM(height, 96ul)) + }; + + return mode; +} + +static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv, + struct platform_device *pdev) +{ + struct device_node *of_node = pdev->dev.of_node; + struct ofdrm_device *odev; + struct drm_device *dev; + int width, height, depth, linebytes; + const struct drm_format_info *format; + u64 address; + resource_size_t fb_size, fb_base, fb_pgbase, fb_pgsize; + struct resource *res, *mem; + void __iomem *screen_base; + struct drm_plane *primary_plane; + struct drm_crtc *crtc; + struct drm_encoder *encoder; + struct drm_connector *connector; + unsigned long max_width, max_height; + size_t nformats; + int ret; + + odev = devm_drm_dev_alloc(&pdev->dev, drv, struct ofdrm_device, dev); + if (IS_ERR(odev)) + return ERR_CAST(odev); + dev = &odev->dev; + platform_set_drvdata(pdev, dev); + + ret = ofdrm_device_init_pci(odev); + if (ret) + return ERR_PTR(ret); + + /* + * OF display-node settings + */ + + width = display_get_width_of(dev, of_node); + if (width < 0) + return ERR_PTR(width); + height = display_get_height_of(dev, of_node); + if (height < 0) + return ERR_PTR(height); + depth = display_get_depth_of(dev, of_node); + if (depth < 0) + return ERR_PTR(depth); + linebytes = display_get_linebytes_of(dev, of_node); + if (linebytes < 0) + return ERR_PTR(linebytes); + + format = display_get_validated_format(dev, depth); + if (IS_ERR(format)) + return ERR_CAST(format); + if (!linebytes) { + linebytes = drm_format_info_min_pitch(format, 0, width); + if (drm_WARN_ON(dev, !linebytes)) + return ERR_PTR(-EINVAL); + } + + fb_size = linebytes * height; + + /* + * Try to figure out the address of the framebuffer. Unfortunately, Open + * Firmware doesn't provide a standard way to do so. All we can do is a + * dodgy heuristic that happens to work in practice. + * + * On most machines, the "address" property contains what we need, though + * not on Matrox cards found in IBM machines. What appears to give good + * results is to go through the PCI ranges and pick one that encloses the + * "address" property. If none match, we pick the largest. + */ + address = display_get_address_of(dev, of_node); + if (address != OF_BAD_ADDR) { + struct resource fb_res = DEFINE_RES_MEM(address, fb_size); + + res = ofdrm_find_fb_resource(odev, &fb_res); + if (!res) + return ERR_PTR(-EINVAL); + if (resource_contains(res, &fb_res)) + fb_base = address; + else + fb_base = res->start; + } else { + struct resource fb_res = DEFINE_RES_MEM(0u, fb_size); + + res = ofdrm_find_fb_resource(odev, &fb_res); + if (!res) + return ERR_PTR(-EINVAL); + fb_base = res->start; + } + + /* + * I/O resources + */ + + fb_pgbase = round_down(fb_base, PAGE_SIZE); + fb_pgsize = fb_base - fb_pgbase + round_up(fb_size, PAGE_SIZE); + + ret = devm_aperture_acquire_from_firmware(dev, fb_pgbase, fb_pgsize); + if (ret) { + drm_err(dev, "could not acquire memory range %pr: error %d\n", &res, ret); + return ERR_PTR(ret); + } + + mem = devm_request_mem_region(&pdev->dev, fb_pgbase, fb_pgsize, drv->name); + if (!mem) { + drm_warn(dev, "could not acquire memory region %pr\n", &res); + return ERR_PTR(-ENOMEM); + } + + screen_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); + if (!screen_base) + return ERR_PTR(-ENOMEM); + + /* + * Firmware framebuffer + */ + + iosys_map_set_vaddr_iomem(&odev->screen_base, screen_base); + odev->mode = ofdrm_mode(width, height); + odev->format = format; + odev->pitch = linebytes; + + drm_dbg(dev, "display mode={" DRM_MODE_FMT "}\n", DRM_MODE_ARG(&odev->mode)); + drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, linebytes=%d byte\n", + &format->format, width, height, linebytes); + + /* + * Mode-setting pipeline + */ + + ret = drmm_mode_config_init(dev); + if (ret) + return ERR_PTR(ret); + + max_width = max_t(unsigned long, width, DRM_SHADOW_PLANE_MAX_WIDTH); + max_height = max_t(unsigned long, height, DRM_SHADOW_PLANE_MAX_HEIGHT); + + dev->mode_config.min_width = width; + dev->mode_config.max_width = max_width; + dev->mode_config.min_height = height; + dev->mode_config.max_height = max_height; + dev->mode_config.funcs = &ofdrm_mode_config_funcs; + switch (depth) { + case 32: + dev->mode_config.preferred_depth = 24; + break; + default: + dev->mode_config.preferred_depth = depth; + break; + } + + /* Primary plane */ + + nformats = drm_fb_build_fourcc_list(dev, &format->format, 1, + ofdrm_primary_plane_formats, + ARRAY_SIZE(ofdrm_primary_plane_formats), + odev->formats, ARRAY_SIZE(odev->formats)); + + primary_plane = &odev->primary_plane; + ret = drm_universal_plane_init(dev, primary_plane, 0, &ofdrm_primary_plane_funcs, + odev->formats, nformats, + ofdrm_primary_plane_format_modifiers, + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) + return ERR_PTR(ret); + drm_plane_helper_add(primary_plane, &ofdrm_primary_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(primary_plane); + + /* CRTC */ + + crtc = &odev->crtc; + ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL, + &ofdrm_crtc_funcs, NULL); + if (ret) + return ERR_PTR(ret); + drm_crtc_helper_add(crtc, &ofdrm_crtc_helper_funcs); + + /* Encoder */ + + encoder = &odev->encoder; + ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_NONE); + if (ret) + return ERR_PTR(ret); + encoder->possible_crtcs = drm_crtc_mask(crtc); + + /* Connector */ + + connector = &odev->connector; + ret = drm_connector_init(dev, connector, &ofdrm_connector_funcs, + DRM_MODE_CONNECTOR_Unknown); + if (ret) + return ERR_PTR(ret); + drm_connector_helper_add(connector, &ofdrm_connector_helper_funcs); + drm_connector_set_panel_orientation_with_quirk(connector, + DRM_MODE_PANEL_ORIENTATION_UNKNOWN, + width, height); + + ret = drm_connector_attach_encoder(connector, encoder); + if (ret) + return ERR_PTR(ret); + + drm_mode_config_reset(dev); + + return odev; +} + +/* + * DRM driver + */ + +DEFINE_DRM_GEM_FOPS(ofdrm_fops); + +static struct drm_driver ofdrm_driver = { + DRM_GEM_SHMEM_DRIVER_OPS, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, + .fops = &ofdrm_fops, +}; + +/* + * Platform driver + */ + +static int ofdrm_probe(struct platform_device *pdev) +{ + struct ofdrm_device *odev; + struct drm_device *dev; + int ret; + + odev = ofdrm_device_create(&ofdrm_driver, pdev); + if (IS_ERR(odev)) + return PTR_ERR(odev); + dev = &odev->dev; + + ret = drm_dev_register(dev, 0); + if (ret) + return ret; + + /* + * FIXME: 24-bit color depth does not work reliably with a 32-bpp + * value. Force the bpp value of the scanout buffer's format. + */ + drm_fbdev_generic_setup(dev, drm_format_info_bpp(odev->format, 0)); + + return 0; +} + +static int ofdrm_remove(struct platform_device *pdev) +{ + struct drm_device *dev = platform_get_drvdata(pdev); + + drm_dev_unplug(dev); + + return 0; +} + +static const struct of_device_id ofdrm_of_match_display[] = { + { .compatible = "display", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ofdrm_of_match_display); + +static struct platform_driver ofdrm_platform_driver = { + .driver = { + .name = "of-display", + .of_match_table = ofdrm_of_match_display, + }, + .probe = ofdrm_probe, + .remove = ofdrm_remove, +}; + +module_platform_driver(ofdrm_platform_driver); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index cfc55273dc5d..a98987aa2784 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -455,6 +455,7 @@ config FB_ATARI config FB_OF bool "Open Firmware frame buffer device support" depends on (FB = y) && PPC && (!PPC_PSERIES || PCI) + depends on !DRM_OFDRM select APERTURE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA -- cgit v1.2.3 From f8572bb675250ee527d9ba35fa1ce17480407399 Mon Sep 17 00:00:00 2001 From: Riana Tauro Date: Thu, 13 Oct 2022 08:45:21 -0700 Subject: drm/i915/hwmon: Add HWMON current voltage support Use i915 HWMON subsystem to display current input voltage. v2: - Updated date and kernel version in feature description - Fixed review comments (Ashutosh) v3: Use macro HWMON_CHANNEL_INFO to define hwmon channel (Guenter) v4: - Fixed review comments (Ashutosh) - Use hwm_ prefix for static functions (Ashutosh) v5: Added unit of voltage as millivolts (Ashutosh) v6: KernelVersion: 6.2, Date: February 2023 in doc (Tvrtko) v7: Change contact to intel-gfx (Rodrigo) GEN12_RPSTAT1 is available for all Gen12+ (Andi) Added Documentation/ABI/testing/sysfs-driver-intel-i915-hwmon to MAINTAINERS Cc: Guenter Roeck Cc: Anshuman Gupta Signed-off-by: Riana Tauro Signed-off-by: Badal Nilawar Signed-off-by: Ashutosh Dixit Acked-by: Guenter Roeck Reviewed-by: Ashutosh Dixit Reviewed-by: Anshuman Gupta Signed-off-by: Anshuman Gupta Link: https://patchwork.freedesktop.org/patch/msgid/20221013154526.2105579-3-ashutosh.dixit@intel.com --- .../ABI/testing/sysfs-driver-intel-i915-hwmon | 7 +++ MAINTAINERS | 1 + drivers/gpu/drm/i915/gt/intel_gt_regs.h | 3 ++ drivers/gpu/drm/i915/i915_hwmon.c | 53 ++++++++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-intel-i915-hwmon (limited to 'MAINTAINERS') diff --git a/Documentation/ABI/testing/sysfs-driver-intel-i915-hwmon b/Documentation/ABI/testing/sysfs-driver-intel-i915-hwmon new file mode 100644 index 000000000000..5f4b136f0850 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-intel-i915-hwmon @@ -0,0 +1,7 @@ +What: /sys/devices/.../hwmon/hwmon/in0_input +Date: February 2023 +KernelVersion: 6.2 +Contact: intel-gfx@lists.freedesktop.org +Description: RO. Current Voltage in millivolt. + + Only supported for particular Intel i915 graphics platforms. diff --git a/MAINTAINERS b/MAINTAINERS index 4f2fa9925116..c03539c86e9e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10140,6 +10140,7 @@ Q: http://patchwork.freedesktop.org/project/intel-gfx/ B: https://gitlab.freedesktop.org/drm/intel/-/wikis/How-to-file-i915-bugs C: irc://irc.oftc.net/intel-gfx T: git git://anongit.freedesktop.org/drm-intel +F: Documentation/ABI/testing/sysfs-driver-intel-i915-hwmon F: Documentation/gpu/i915.rst F: drivers/gpu/drm/i915/ F: include/drm/i915* diff --git a/drivers/gpu/drm/i915/gt/intel_gt_regs.h b/drivers/gpu/drm/i915/gt/intel_gt_regs.h index 755a2c101269..c7639ae79a8f 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_regs.h +++ b/drivers/gpu/drm/i915/gt/intel_gt_regs.h @@ -1516,6 +1516,9 @@ #define VLV_RENDER_C0_COUNT _MMIO(0x138118) #define VLV_MEDIA_C0_COUNT _MMIO(0x13811c) +#define GEN12_RPSTAT1 _MMIO(0x1381b4) +#define GEN12_VOLTAGE_MASK REG_GENMASK(10, 0) + #define GEN11_GT_INTR_DW(x) _MMIO(0x190018 + ((x) * 4)) #define GEN11_CSME (31) #define GEN11_GUNIT (28) diff --git a/drivers/gpu/drm/i915/i915_hwmon.c b/drivers/gpu/drm/i915/i915_hwmon.c index 231552fda374..025399391ddc 100644 --- a/drivers/gpu/drm/i915/i915_hwmon.c +++ b/drivers/gpu/drm/i915/i915_hwmon.c @@ -11,8 +11,16 @@ #include "i915_hwmon.h" #include "i915_reg.h" #include "intel_mchbar_regs.h" +#include "gt/intel_gt_regs.h" + +/* + * SF_* - scale factors for particular quantities according to hwmon spec. + * - voltage - millivolts + */ +#define SF_VOLTAGE 1000 struct hwm_reg { + i915_reg_t gt_perf_status; }; struct hwm_drvdata { @@ -29,14 +37,51 @@ struct i915_hwmon { }; static const struct hwmon_channel_info *hwm_info[] = { + HWMON_CHANNEL_INFO(in, HWMON_I_INPUT), NULL }; +static umode_t +hwm_in_is_visible(const struct hwm_drvdata *ddat, u32 attr) +{ + struct drm_i915_private *i915 = ddat->uncore->i915; + + switch (attr) { + case hwmon_in_input: + return IS_DG1(i915) || IS_DG2(i915) ? 0444 : 0; + default: + return 0; + } +} + +static int +hwm_in_read(struct hwm_drvdata *ddat, u32 attr, long *val) +{ + struct i915_hwmon *hwmon = ddat->hwmon; + intel_wakeref_t wakeref; + u32 reg_value; + + switch (attr) { + case hwmon_in_input: + with_intel_runtime_pm(ddat->uncore->rpm, wakeref) + reg_value = intel_uncore_read(ddat->uncore, hwmon->rg.gt_perf_status); + /* HW register value in units of 2.5 millivolt */ + *val = DIV_ROUND_CLOSEST(REG_FIELD_GET(GEN12_VOLTAGE_MASK, reg_value) * 25, 10); + return 0; + default: + return -EOPNOTSUPP; + } +} + static umode_t hwm_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, int channel) { + struct hwm_drvdata *ddat = (struct hwm_drvdata *)drvdata; + switch (type) { + case hwmon_in: + return hwm_in_is_visible(ddat, attr); default: return 0; } @@ -46,7 +91,11 @@ static int hwm_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { + struct hwm_drvdata *ddat = dev_get_drvdata(dev); + switch (type) { + case hwmon_in: + return hwm_in_read(ddat, attr, val); default: return -EOPNOTSUPP; } @@ -76,6 +125,10 @@ static const struct hwmon_chip_info hwm_chip_info = { static void hwm_get_preregistration_info(struct drm_i915_private *i915) { + struct i915_hwmon *hwmon = i915->hwmon; + + /* Available for all Gen12+/dGfx */ + hwmon->rg.gt_perf_status = GEN12_RPSTAT1; } void i915_hwmon_register(struct drm_i915_private *i915) -- cgit v1.2.3 From 5f9952548d91263eaf70a2ca71f8897c2a638cf1 Mon Sep 17 00:00:00 2001 From: Dan Callaghan Date: Tue, 18 Oct 2022 15:06:23 +1100 Subject: platform/chrome: add a driver for HPS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces a driver for the ChromeOS human presence sensor (aka. HPS). The driver supports a sensor connected to the I2C bus and identified as "GOOG0020" in the ACPI tables. When loaded, the driver exports the sensor to userspace through a character device. This device only supports power management, i.e., communication with the sensor must be done through regular I2C transmissions from userspace. Power management is implemented by enabling the respective power GPIO while at least one userspace process holds an open fd on the character device. By default, the device is powered down if there are no active clients. Note that the driver makes no effort to preserve the state of the sensor between power down and power up events. Userspace is responsible for reinitializing any needed state once power has been restored. The device firmware, I2C protocol and other documentation is available at https://chromium.googlesource.com/chromiumos/platform/hps-firmware. Co-developed-by: Sami Kyöstilä Signed-off-by: Sami Kyöstilä Signed-off-by: Dan Callaghan Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20221018040623.2173441-1-dcallagh@chromium.org --- MAINTAINERS | 6 ++ drivers/platform/chrome/Kconfig | 10 ++ drivers/platform/chrome/Makefile | 1 + drivers/platform/chrome/cros_hps_i2c.c | 162 +++++++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+) create mode 100644 drivers/platform/chrome/cros_hps_i2c.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..4002d61291ca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4936,6 +4936,12 @@ S: Maintained F: drivers/platform/chrome/cros_usbpd_notify.c F: include/linux/platform_data/cros_usbpd_notify.h +CHROMEOS HPS DRIVER +M: Dan Callaghan +R: Sami Kyöstilä +S: Maintained +F: drivers/platform/chrome/cros_hps_i2c.c + CHRONTEL CH7322 CEC DRIVER M: Joe Tessler L: linux-media@vger.kernel.org diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 6b954c5acadb..c1ca247987d2 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -228,6 +228,16 @@ config CROS_EC_TYPEC To compile this driver as a module, choose M here: the module will be called cros_ec_typec. +config CROS_HPS_I2C + tristate "ChromeOS HPS device" + depends on HID && I2C && PM + help + Say Y here if you want to enable support for the ChromeOS + human presence sensor (HPS), attached via I2C. The driver supports a + sensor connected to the I2C bus and exposes it as a character device. + To save power, the sensor is automatically powered down when no + clients are accessing it. + config CROS_USBPD_LOGGER tristate "Logging driver for USB PD charger" depends on CHARGER_CROS_USBPD diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index 2950610101f1..f6068d077a40 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_CROS_EC_DEBUGFS) += cros_ec_debugfs.o cros-ec-sensorhub-objs := cros_ec_sensorhub.o cros_ec_sensorhub_ring.o obj-$(CONFIG_CROS_EC_SENSORHUB) += cros-ec-sensorhub.o obj-$(CONFIG_CROS_EC_SYSFS) += cros_ec_sysfs.o +obj-$(CONFIG_CROS_HPS_I2C) += cros_hps_i2c.o obj-$(CONFIG_CROS_USBPD_LOGGER) += cros_usbpd_logger.o obj-$(CONFIG_CROS_USBPD_NOTIFY) += cros_usbpd_notify.o diff --git a/drivers/platform/chrome/cros_hps_i2c.c b/drivers/platform/chrome/cros_hps_i2c.c new file mode 100644 index 000000000000..92da59d94745 --- /dev/null +++ b/drivers/platform/chrome/cros_hps_i2c.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the ChromeOS human presence sensor (HPS), attached via I2C. + * + * The driver exposes HPS as a character device, although currently no read or + * write operations are supported. Instead, the driver only controls the power + * state of the sensor, keeping it on only while userspace holds an open file + * descriptor to the HPS device. + * + * Copyright 2022 Google LLC. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define HPS_ACPI_ID "GOOG0020" + +struct hps_drvdata { + struct i2c_client *client; + struct miscdevice misc_device; + struct gpio_desc *enable_gpio; +}; + +static void hps_set_power(struct hps_drvdata *hps, bool state) +{ + gpiod_set_value_cansleep(hps->enable_gpio, state); +} + +static int hps_open(struct inode *inode, struct file *file) +{ + struct hps_drvdata *hps = container_of(file->private_data, + struct hps_drvdata, misc_device); + struct device *dev = &hps->client->dev; + + return pm_runtime_resume_and_get(dev); +} + +static int hps_release(struct inode *inode, struct file *file) +{ + struct hps_drvdata *hps = container_of(file->private_data, + struct hps_drvdata, misc_device); + struct device *dev = &hps->client->dev; + + return pm_runtime_put(dev); +} + +static const struct file_operations hps_fops = { + .owner = THIS_MODULE, + .open = hps_open, + .release = hps_release, +}; + +static int hps_i2c_probe(struct i2c_client *client) +{ + struct hps_drvdata *hps; + int ret; + + hps = devm_kzalloc(&client->dev, sizeof(*hps), GFP_KERNEL); + if (!hps) + return -ENOMEM; + + hps->misc_device.parent = &client->dev; + hps->misc_device.minor = MISC_DYNAMIC_MINOR; + hps->misc_device.name = "cros-hps"; + hps->misc_device.fops = &hps_fops; + + i2c_set_clientdata(client, hps); + hps->client = client; + + /* + * HPS is powered on from firmware before entering the kernel, so we + * acquire the line with GPIOD_OUT_HIGH here to preserve the existing + * state. The peripheral is powered off after successful probe below. + */ + hps->enable_gpio = devm_gpiod_get(&client->dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(hps->enable_gpio)) { + ret = PTR_ERR(hps->enable_gpio); + dev_err(&client->dev, "failed to get enable gpio: %d\n", ret); + return ret; + } + + ret = misc_register(&hps->misc_device); + if (ret) { + dev_err(&client->dev, "failed to initialize misc device: %d\n", ret); + return ret; + } + + hps_set_power(hps, false); + pm_runtime_enable(&client->dev); + return 0; +} + +static int hps_i2c_remove(struct i2c_client *client) +{ + struct hps_drvdata *hps = i2c_get_clientdata(client); + + pm_runtime_disable(&client->dev); + misc_deregister(&hps->misc_device); + + /* + * Re-enable HPS, in order to return it to its default state + * (i.e. powered on). + */ + hps_set_power(hps, true); + + return 0; +} + +static int hps_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct hps_drvdata *hps = i2c_get_clientdata(client); + + hps_set_power(hps, false); + return 0; +} + +static int hps_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct hps_drvdata *hps = i2c_get_clientdata(client); + + hps_set_power(hps, true); + return 0; +} +static UNIVERSAL_DEV_PM_OPS(hps_pm_ops, hps_suspend, hps_resume, NULL); + +static const struct i2c_device_id hps_i2c_id[] = { + { "cros-hps", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, hps_i2c_id); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id hps_acpi_id[] = { + { HPS_ACPI_ID, 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, hps_acpi_id); +#endif /* CONFIG_ACPI */ + +static struct i2c_driver hps_i2c_driver = { + .probe_new = hps_i2c_probe, + .remove = hps_i2c_remove, + .id_table = hps_i2c_id, + .driver = { + .name = "cros-hps", + .pm = &hps_pm_ops, + .acpi_match_table = ACPI_PTR(hps_acpi_id), + }, +}; +module_i2c_driver(hps_i2c_driver); + +MODULE_ALIAS("acpi:" HPS_ACPI_ID); +MODULE_AUTHOR("Sami Kyöstilä "); +MODULE_DESCRIPTION("Driver for ChromeOS HPS"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From d4353decd4fdec71b9f9d1ba8fa85b7e595e8924 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Thu, 29 Sep 2022 14:34:31 +0200 Subject: MAINTAINERS: rectify entry for MESON NAND controller bindings Commit fbc00b5e746f ("dt-bindings: nand: meson: convert txt to yaml") converts amlogic,meson-nand.txt to yaml, but misses to adjust its reference in MAINTAINERS. Hence, ./scripts/get_maintainer.pl --self-test=patterns complains about a broken reference. Repair this file reference in MESON NAND CONTROLLER DRIVER FOR AMLOGIC SOCS. Signed-off-by: Lukas Bulwahn Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220929123431.23180-1-lukas.bulwahn@gmail.com --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..358d7383d228 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13376,7 +13376,7 @@ MESON NAND CONTROLLER DRIVER FOR AMLOGIC SOCS M: Liang Yang L: linux-mtd@lists.infradead.org S: Maintained -F: Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt +F: Documentation/devicetree/bindings/mtd/amlogic,meson-nand.yaml F: drivers/mtd/nand/raw/meson_* MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS -- cgit v1.2.3 From b65c1735c73019549255e3a59ec80e9b9201c6e5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 9 Aug 2022 08:57:29 +0300 Subject: MAINTAINERS: ARM: marvell: include bindings Include top-level Marvell bindings in Marvell maintainer entries. Acked-by: Gregory CLEMENT Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/20220809055729.19242-1-krzysztof.kozlowski@linaro.org Signed-off-by: Krzysztof Kozlowski --- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..f118c09e0611 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2341,6 +2341,8 @@ M: Gregory Clement L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/gclement/mvebu.git +F: Documentation/devicetree/bindings/arm/marvell/marvell,dove.txt +F: Documentation/devicetree/bindings/arm/marvell/marvell,orion5x.txt F: Documentation/devicetree/bindings/soc/dove/ F: arch/arm/boot/dts/dove* F: arch/arm/boot/dts/orion5x* @@ -2357,6 +2359,7 @@ M: Sebastian Hesselbarth L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/gclement/mvebu.git +F: Documentation/devicetree/bindings/arm/marvell/ F: arch/arm/boot/dts/armada* F: arch/arm/boot/dts/kirkwood* F: arch/arm/configs/mvebu_*_defconfig -- cgit v1.2.3 From 4ef3405e82d25f2752fabe3c5443c3e703739172 Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Thu, 13 Oct 2022 14:46:38 -0700 Subject: MAINTAINERS: git://github -> https://github.com for kleikamp Github deprecated the git:// links about a year ago, so let's move to the https:// URLs instead. Reported-by: Conor Dooley Link: https://github.blog/2021-09-01-improving-git-protocol-security-github/ Signed-off-by: Palmer Dabbelt Also changed status from Maintained to Odd Fixes Signed-off-by: Dave Kleikamp --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..6f2badab5485 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10950,9 +10950,9 @@ F: drivers/hwmon/jc42.c JFS FILESYSTEM M: Dave Kleikamp L: jfs-discussion@lists.sourceforge.net -S: Maintained +S: Odd Fixes W: http://jfs.sourceforge.net/ -T: git git://github.com/kleikamp/linux-shaggy.git +T: git https://github.com/kleikamp/linux-shaggy.git F: Documentation/admin-guide/jfs.rst F: fs/jfs/ -- cgit v1.2.3 From c4d175c323e3751badf77766c79cb130b4a3eb5f Mon Sep 17 00:00:00 2001 From: Michał Grzelak Date: Fri, 14 Oct 2022 23:32:52 +0200 Subject: dt-bindings: net: marvell,pp2: convert to json-schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the marvell,pp2 bindings from text to proper schema. Move 'marvell,system-controller' and 'dma-coherent' properties from port up to the controller node, to match what is actually done in DT. Rename all subnodes to match "^(ethernet-)?port@[0-2]$" and deprecate port-id in favour of 'reg'. Signed-off-by: Michał Grzelak Reviewed-by: Rob Herring Signed-off-by: David S. Miller --- .../devicetree/bindings/net/marvell,pp2.yaml | 305 +++++++++++++++++++++ .../devicetree/bindings/net/marvell-pp2.txt | 141 ---------- MAINTAINERS | 2 +- 3 files changed, 306 insertions(+), 142 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/marvell,pp2.yaml delete mode 100644 Documentation/devicetree/bindings/net/marvell-pp2.txt (limited to 'MAINTAINERS') diff --git a/Documentation/devicetree/bindings/net/marvell,pp2.yaml b/Documentation/devicetree/bindings/net/marvell,pp2.yaml new file mode 100644 index 000000000000..4eadafc43d4f --- /dev/null +++ b/Documentation/devicetree/bindings/net/marvell,pp2.yaml @@ -0,0 +1,305 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/marvell,pp2.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell CN913X / Marvell Armada 375, 7K, 8K Ethernet Controller + +maintainers: + - Marcin Wojtas + - Russell King + +description: | + Marvell Armada 375 Ethernet Controller (PPv2.1) + Marvell Armada 7K/8K Ethernet Controller (PPv2.2) + Marvell CN913X Ethernet Controller (PPv2.3) + +properties: + compatible: + enum: + - marvell,armada-375-pp2 + - marvell,armada-7k-pp22 + + reg: + minItems: 3 + maxItems: 4 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + clocks: + minItems: 2 + items: + - description: main controller clock + - description: GOP clock + - description: MG clock + - description: MG Core clock + - description: AXI clock + + clock-names: + minItems: 2 + items: + - const: pp_clk + - const: gop_clk + - const: mg_clk + - const: mg_core_clk + - const: axi_clk + + dma-coherent: true + + marvell,system-controller: + $ref: /schemas/types.yaml#/definitions/phandle + description: a phandle to the system controller. + +patternProperties: + '^(ethernet-)?port@[0-2]$': + type: object + description: subnode for each ethernet port. + $ref: ethernet-controller.yaml# + unevaluatedProperties: false + + properties: + reg: + description: ID of the port from the MAC point of view. + maximum: 2 + + interrupts: + minItems: 1 + maxItems: 10 + description: interrupt(s) for the port + + interrupt-names: + minItems: 1 + items: + - const: hif0 + - const: hif1 + - const: hif2 + - const: hif3 + - const: hif4 + - const: hif5 + - const: hif6 + - const: hif7 + - const: hif8 + - const: link + + description: > + if more than a single interrupt for is given, must be the + name associated to the interrupts listed. Valid names are: + "hifX", with X in [0..8], and "link". The names "tx-cpu0", + "tx-cpu1", "tx-cpu2", "tx-cpu3" and "rx-shared" are supported + for backward compatibility but shouldn't be used for new + additions. + + phys: + minItems: 1 + maxItems: 2 + description: > + Generic PHY, providing SerDes connectivity. For most modes, + one lane is sufficient, but some (e.g. RXAUI) may require two. + + phy-mode: + enum: + - gmii + - sgmii + - rgmii-id + - 1000base-x + - 2500base-x + - 5gbase-r + - rxaui + - 10gbase-r + + port-id: + $ref: /schemas/types.yaml#/definitions/uint32 + deprecated: true + description: > + ID of the port from the MAC point of view. + Legacy binding for backward compatibility. + + marvell,loopback: + $ref: /schemas/types.yaml#/definitions/flag + description: port is loopback mode. + + gop-port-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: > + only for marvell,armada-7k-pp22, ID of the port from the + GOP (Group Of Ports) point of view. This ID is used to index the + per-port registers in the second register area. + + required: + - reg + - interrupts + - phy-mode + - port-id + +required: + - compatible + - reg + - clocks + - clock-names + +allOf: + - if: + properties: + compatible: + const: marvell,armada-7k-pp22 + then: + properties: + reg: + items: + - description: Packet Processor registers + - description: Networking interfaces registers + - description: CM3 address space used for TX Flow Control + + clocks: + minItems: 5 + + clock-names: + minItems: 5 + + patternProperties: + '^(ethernet-)?port@[0-2]$': + required: + - gop-port-id + + required: + - marvell,system-controller + else: + properties: + reg: + items: + - description: Packet Processor registers + - description: LMS registers + - description: Register area per eth0 + - description: Register area per eth1 + + clocks: + maxItems: 2 + + clock-names: + maxItems: 2 + + patternProperties: + '^(ethernet-)?port@[0-1]$': + properties: + reg: + maximum: 1 + + gop-port-id: false + +additionalProperties: false + +examples: + - | + // For Armada 375 variant + #include + #include + + ethernet@f0000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "marvell,armada-375-pp2"; + reg = <0xf0000 0xa000>, + <0xc0000 0x3060>, + <0xc4000 0x100>, + <0xc5000 0x100>; + clocks = <&gateclk 3>, <&gateclk 19>; + clock-names = "pp_clk", "gop_clk"; + + ethernet-port@0 { + interrupts = ; + reg = <0>; + port-id = <0>; /* For backward compatibility. */ + phy = <&phy0>; + phy-mode = "rgmii-id"; + }; + + ethernet-port@1 { + interrupts = ; + reg = <1>; + port-id = <1>; /* For backward compatibility. */ + phy = <&phy3>; + phy-mode = "gmii"; + }; + }; + + - | + // For Armada 7k/8k and Cn913x variants + #include + #include + + ethernet@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "marvell,armada-7k-pp22"; + reg = <0x0 0x100000>, <0x129000 0xb000>, <0x220000 0x800>; + clocks = <&cp0_clk 1 3>, <&cp0_clk 1 9>, + <&cp0_clk 1 5>, <&cp0_clk 1 6>, <&cp0_clk 1 18>; + clock-names = "pp_clk", "gop_clk", "mg_clk", "mg_core_clk", "axi_clk"; + marvell,system-controller = <&cp0_syscon0>; + + ethernet-port@0 { + interrupts = , + , + , + , + , + , + , + , + , + ; + interrupt-names = "hif0", "hif1", "hif2", "hif3", "hif4", + "hif5", "hif6", "hif7", "hif8", "link"; + phy-mode = "10gbase-r"; + phys = <&cp0_comphy4 0>; + reg = <0>; + port-id = <0>; /* For backward compatibility. */ + gop-port-id = <0>; + }; + + ethernet-port@1 { + interrupts = , + , + , + , + , + , + , + , + , + ; + interrupt-names = "hif0", "hif1", "hif2", "hif3", "hif4", + "hif5", "hif6", "hif7", "hif8", "link"; + phy-mode = "rgmii-id"; + reg = <1>; + port-id = <1>; /* For backward compatibility. */ + gop-port-id = <2>; + }; + + ethernet-port@2 { + interrupts = , + , + , + , + , + , + , + , + , + ; + interrupt-names = "hif0", "hif1", "hif2", "hif3", "hif4", + "hif5", "hif6", "hif7", "hif8", "link"; + phy-mode = "2500base-x"; + managed = "in-band-status"; + phys = <&cp0_comphy5 2>; + sfp = <&sfp_eth3>; + reg = <2>; + port-id = <2>; /* For backward compatibility. */ + gop-port-id = <3>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/marvell-pp2.txt b/Documentation/devicetree/bindings/net/marvell-pp2.txt deleted file mode 100644 index ce15c173f43f..000000000000 --- a/Documentation/devicetree/bindings/net/marvell-pp2.txt +++ /dev/null @@ -1,141 +0,0 @@ -* Marvell Armada 375 Ethernet Controller (PPv2.1) - Marvell Armada 7K/8K Ethernet Controller (PPv2.2) - Marvell CN913X Ethernet Controller (PPv2.3) - -Required properties: - -- compatible: should be one of: - "marvell,armada-375-pp2" - "marvell,armada-7k-pp2" -- reg: addresses and length of the register sets for the device. - For "marvell,armada-375-pp2", must contain the following register - sets: - - common controller registers - - LMS registers - - one register area per Ethernet port - For "marvell,armada-7k-pp2" used by 7K/8K and CN913X, must contain the following register - sets: - - packet processor registers - - networking interfaces registers - - CM3 address space used for TX Flow Control - -- clocks: pointers to the reference clocks for this device, consequently: - - main controller clock (for both armada-375-pp2 and armada-7k-pp2) - - GOP clock (for both armada-375-pp2 and armada-7k-pp2) - - MG clock (only for armada-7k-pp2) - - MG Core clock (only for armada-7k-pp2) - - AXI clock (only for armada-7k-pp2) -- clock-names: names of used clocks, must be "pp_clk", "gop_clk", "mg_clk", - "mg_core_clk" and "axi_clk" (the 3 latter only for armada-7k-pp2). - -The ethernet ports are represented by subnodes. At least one port is -required. - -Required properties (port): - -- interrupts: interrupt(s) for the port -- port-id: ID of the port from the MAC point of view -- gop-port-id: only for marvell,armada-7k-pp2, ID of the port from the - GOP (Group Of Ports) point of view. This ID is used to index the - per-port registers in the second register area. -- phy-mode: See ethernet.txt file in the same directory - -Optional properties (port): - -- marvell,loopback: port is loopback mode -- phy: a phandle to a phy node defining the PHY address (as the reg - property, a single integer). -- interrupt-names: if more than a single interrupt for is given, must be the - name associated to the interrupts listed. Valid names are: - "hifX", with X in [0..8], and "link". The names "tx-cpu0", - "tx-cpu1", "tx-cpu2", "tx-cpu3" and "rx-shared" are supported - for backward compatibility but shouldn't be used for new - additions. -- marvell,system-controller: a phandle to the system controller. - -Example for marvell,armada-375-pp2: - -ethernet@f0000 { - compatible = "marvell,armada-375-pp2"; - reg = <0xf0000 0xa000>, - <0xc0000 0x3060>, - <0xc4000 0x100>, - <0xc5000 0x100>; - clocks = <&gateclk 3>, <&gateclk 19>; - clock-names = "pp_clk", "gop_clk"; - - eth0: eth0@c4000 { - interrupts = ; - port-id = <0>; - phy = <&phy0>; - phy-mode = "gmii"; - }; - - eth1: eth1@c5000 { - interrupts = ; - port-id = <1>; - phy = <&phy3>; - phy-mode = "gmii"; - }; -}; - -Example for marvell,armada-7k-pp2: - -cpm_ethernet: ethernet@0 { - compatible = "marvell,armada-7k-pp22"; - reg = <0x0 0x100000>, <0x129000 0xb000>, <0x220000 0x800>; - clocks = <&cpm_syscon0 1 3>, <&cpm_syscon0 1 9>, - <&cpm_syscon0 1 5>, <&cpm_syscon0 1 6>, <&cpm_syscon0 1 18>; - clock-names = "pp_clk", "gop_clk", "mg_clk", "mg_core_clk", "axi_clk"; - - eth0: eth0 { - interrupts = , - , - , - , - , - , - , - , - , - ; - interrupt-names = "hif0", "hif1", "hif2", "hif3", "hif4", - "hif5", "hif6", "hif7", "hif8", "link"; - port-id = <0>; - gop-port-id = <0>; - }; - - eth1: eth1 { - interrupts = , - , - , - , - , - , - , - , - , - ; - interrupt-names = "hif0", "hif1", "hif2", "hif3", "hif4", - "hif5", "hif6", "hif7", "hif8", "link"; - port-id = <1>; - gop-port-id = <2>; - }; - - eth2: eth2 { - interrupts = , - , - , - , - , - , - , - , - , - ; - interrupt-names = "hif0", "hif1", "hif2", "hif3", "hif4", - "hif5", "hif6", "hif7", "hif8", "link"; - port-id = <2>; - gop-port-id = <3>; - }; -}; diff --git a/MAINTAINERS b/MAINTAINERS index a96c60c787af..5c6ce094e55e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12307,7 +12307,7 @@ M: Marcin Wojtas M: Russell King L: netdev@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/net/marvell-pp2.txt +F: Documentation/devicetree/bindings/net/marvell,pp2.yaml F: drivers/net/ethernet/marvell/mvpp2/ MARVELL MWIFIEX WIRELESS DRIVER -- cgit v1.2.3 From 7d7eca411fa0c662693613805107494a9dc7477f Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 11 Oct 2022 13:01:12 -0700 Subject: MAINTAINERS: Update pstore maintainers Update pstore to better reflect reality of active contributors: - Remove Anton and Colin (thank you for your help through the years!) - Move Tony to Reviewer - Add Guilherme as Reviewer - Add mailing list - Upgrade to Supported Signed-off-by: Kees Cook Acked-by: Guilherme G. Piccoli Acked-by: Colin Cross Link: https://lore.kernel.org/r/20221011200112.731334-6-keescook@chromium.org --- MAINTAINERS | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..41ae6877dcdf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16641,10 +16641,10 @@ F: net/psample PSTORE FILESYSTEM M: Kees Cook -M: Anton Vorontsov -M: Colin Cross -M: Tony Luck -S: Maintained +R: Tony Luck +R: Guilherme G. Piccoli +L: linux-hardening@vger.kernel.org +S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/pstore F: Documentation/admin-guide/ramoops.rst F: Documentation/admin-guide/pstore-blk.rst -- cgit v1.2.3 From 48276c08cf5d2039aad5ef92ae7057ae0946d51e Mon Sep 17 00:00:00 2001 From: Robert-Ionut Alexa Date: Tue, 18 Oct 2022 17:18:59 +0300 Subject: net: dpaa2-eth: AF_XDP RX zero copy support This patch adds the support for receiving packets via the AF_XDP zero-copy mechanism in the dpaa2-eth driver. The support is available only on the LX2160A SoC and variants because we are relying on the HW capability to associate a buffer pool to a specific queue (QDBIN), only available on newer WRIOP versions. On the control path, the dpaa2_xsk_enable_pool() function is responsible to allocate a buffer pool (BP), setup this new BP to be used only on the requested queue and change the consume function to point to the XSK ZC one. We are forced to call dev_close() in order to change the queue to buffer pool association (dpaa2_xsk_set_bp_per_qdbin) . This also works in our favor since at dev_close() the buffer pools will be drained and at the later dev_open() call they will be again seeded, this time with buffers allocated from the XSK pool if needed. On the data path, a new software annotation type is defined to be used only for the XSK scenarios. This will enable us to pass keep necessary information about a packet buffer between the moment in which it was seeded and when it's received by the driver. In the XSK case, we are keeping the associated xdp_buff. Depending on the action returned by the BPF program, we will do the following: - XDP_PASS: copy the contents of the packet into a brand new skb, recycle the initial buffer. - XDP_TX: just enqueue the same frame descriptor back into the Tx path, the buffer will get automatically released into the initial BP. - XDP_REDIRECT: call xdp_do_redirect() and exit. Signed-off-by: Robert-Ionut Alexa Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- MAINTAINERS | 1 + drivers/net/ethernet/freescale/dpaa2/Makefile | 2 +- drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 131 ++++++--- drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h | 36 ++- drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c | 327 +++++++++++++++++++++++ 5 files changed, 452 insertions(+), 45 deletions(-) create mode 100644 drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 92708912fef8..479a15d86a91 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6326,6 +6326,7 @@ F: drivers/net/ethernet/freescale/dpaa2/Kconfig F: drivers/net/ethernet/freescale/dpaa2/Makefile F: drivers/net/ethernet/freescale/dpaa2/dpaa2-eth* F: drivers/net/ethernet/freescale/dpaa2/dpaa2-mac* +F: drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk* F: drivers/net/ethernet/freescale/dpaa2/dpkg.h F: drivers/net/ethernet/freescale/dpaa2/dpmac* F: drivers/net/ethernet/freescale/dpaa2/dpni* diff --git a/drivers/net/ethernet/freescale/dpaa2/Makefile b/drivers/net/ethernet/freescale/dpaa2/Makefile index 3d9842af7f10..1b05ba8d1cbf 100644 --- a/drivers/net/ethernet/freescale/dpaa2/Makefile +++ b/drivers/net/ethernet/freescale/dpaa2/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_FSL_DPAA2_ETH) += fsl-dpaa2-eth.o obj-$(CONFIG_FSL_DPAA2_PTP_CLOCK) += fsl-dpaa2-ptp.o obj-$(CONFIG_FSL_DPAA2_SWITCH) += fsl-dpaa2-switch.o -fsl-dpaa2-eth-objs := dpaa2-eth.o dpaa2-ethtool.o dpni.o dpaa2-mac.o dpmac.o dpaa2-eth-devlink.o +fsl-dpaa2-eth-objs := dpaa2-eth.o dpaa2-ethtool.o dpni.o dpaa2-mac.o dpmac.o dpaa2-eth-devlink.o dpaa2-xsk.o fsl-dpaa2-eth-${CONFIG_FSL_DPAA2_ETH_DCB} += dpaa2-eth-dcb.o fsl-dpaa2-eth-${CONFIG_DEBUG_FS} += dpaa2-eth-debugfs.o fsl-dpaa2-ptp-objs := dpaa2-ptp.o dprtc.o diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 0b21a3ee1bf1..4c340f70f50e 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "dpaa2-eth.h" @@ -104,8 +105,8 @@ static void dpaa2_ptp_onestep_reg_update_method(struct dpaa2_eth_priv *priv) priv->dpaa2_set_onestep_params_cb = dpaa2_update_ptp_onestep_direct; } -static void *dpaa2_iova_to_virt(struct iommu_domain *domain, - dma_addr_t iova_addr) +void *dpaa2_iova_to_virt(struct iommu_domain *domain, + dma_addr_t iova_addr) { phys_addr_t phys_addr; @@ -279,23 +280,33 @@ static struct sk_buff *dpaa2_eth_build_frag_skb(struct dpaa2_eth_priv *priv, * be released in the pool */ static void dpaa2_eth_free_bufs(struct dpaa2_eth_priv *priv, u64 *buf_array, - int count) + int count, bool xsk_zc) { struct device *dev = priv->net_dev->dev.parent; + struct dpaa2_eth_swa *swa; + struct xdp_buff *xdp_buff; void *vaddr; int i; for (i = 0; i < count; i++) { vaddr = dpaa2_iova_to_virt(priv->iommu_domain, buf_array[i]); - dma_unmap_page(dev, buf_array[i], priv->rx_buf_size, - DMA_BIDIRECTIONAL); - free_pages((unsigned long)vaddr, 0); + + if (!xsk_zc) { + dma_unmap_page(dev, buf_array[i], priv->rx_buf_size, + DMA_BIDIRECTIONAL); + free_pages((unsigned long)vaddr, 0); + } else { + swa = (struct dpaa2_eth_swa *) + (vaddr + DPAA2_ETH_RX_HWA_SIZE); + xdp_buff = swa->xsk.xdp_buff; + xsk_buff_free(xdp_buff); + } } } -static void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv, - struct dpaa2_eth_channel *ch, - dma_addr_t addr) +void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + dma_addr_t addr) { int retries = 0; int err; @@ -313,7 +324,8 @@ static void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv, } if (err) { - dpaa2_eth_free_bufs(priv, ch->recycled_bufs, ch->recycled_bufs_cnt); + dpaa2_eth_free_bufs(priv, ch->recycled_bufs, + ch->recycled_bufs_cnt, ch->xsk_zc); ch->buf_count -= ch->recycled_bufs_cnt; } @@ -377,10 +389,10 @@ static void dpaa2_eth_xdp_tx_flush(struct dpaa2_eth_priv *priv, fq->xdp_tx_fds.num = 0; } -static void dpaa2_eth_xdp_enqueue(struct dpaa2_eth_priv *priv, - struct dpaa2_eth_channel *ch, - struct dpaa2_fd *fd, - void *buf_start, u16 queue_id) +void dpaa2_eth_xdp_enqueue(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + struct dpaa2_fd *fd, + void *buf_start, u16 queue_id) { struct dpaa2_faead *faead; struct dpaa2_fd *dest_fd; @@ -1652,37 +1664,63 @@ static int dpaa2_eth_set_tx_csum(struct dpaa2_eth_priv *priv, bool enable) static int dpaa2_eth_add_bufs(struct dpaa2_eth_priv *priv, struct dpaa2_eth_channel *ch) { + struct xdp_buff *xdp_buffs[DPAA2_ETH_BUFS_PER_CMD]; struct device *dev = priv->net_dev->dev.parent; u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; + struct dpaa2_eth_swa *swa; struct page *page; dma_addr_t addr; int retries = 0; - int i, err; - - for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) { - /* Allocate buffer visible to WRIOP + skb shared info + - * alignment padding - */ - /* allocate one page for each Rx buffer. WRIOP sees - * the entire page except for a tailroom reserved for - * skb shared info + int i = 0, err; + u32 batch; + + /* Allocate buffers visible to WRIOP */ + if (!ch->xsk_zc) { + for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) { + /* Also allocate skb shared info and alignment padding. + * There is one page for each Rx buffer. WRIOP sees + * the entire page except for a tailroom reserved for + * skb shared info + */ + page = dev_alloc_pages(0); + if (!page) + goto err_alloc; + + addr = dma_map_page(dev, page, 0, priv->rx_buf_size, + DMA_BIDIRECTIONAL); + if (unlikely(dma_mapping_error(dev, addr))) + goto err_map; + + buf_array[i] = addr; + + /* tracing point */ + trace_dpaa2_eth_buf_seed(priv->net_dev, + page_address(page), + DPAA2_ETH_RX_BUF_RAW_SIZE, + addr, priv->rx_buf_size, + ch->bp->bpid); + } + } else if (xsk_buff_can_alloc(ch->xsk_pool, DPAA2_ETH_BUFS_PER_CMD)) { + /* Allocate XSK buffers for AF_XDP fast path in batches + * of DPAA2_ETH_BUFS_PER_CMD. Bail out if the UMEM cannot + * provide enough buffers at the moment */ - page = dev_alloc_pages(0); - if (!page) + batch = xsk_buff_alloc_batch(ch->xsk_pool, xdp_buffs, + DPAA2_ETH_BUFS_PER_CMD); + if (!batch) goto err_alloc; - addr = dma_map_page(dev, page, 0, priv->rx_buf_size, - DMA_BIDIRECTIONAL); - if (unlikely(dma_mapping_error(dev, addr))) - goto err_map; + for (i = 0; i < batch; i++) { + swa = (struct dpaa2_eth_swa *)(xdp_buffs[i]->data_hard_start + + DPAA2_ETH_RX_HWA_SIZE); + swa->xsk.xdp_buff = xdp_buffs[i]; - buf_array[i] = addr; + addr = xsk_buff_xdp_get_frame_dma(xdp_buffs[i]); + if (unlikely(dma_mapping_error(dev, addr))) + goto err_map; - /* tracing point */ - trace_dpaa2_eth_buf_seed(priv->net_dev, page_address(page), - DPAA2_ETH_RX_BUF_RAW_SIZE, - addr, priv->rx_buf_size, - ch->bp->bpid); + buf_array[i] = addr; + } } release_bufs: @@ -1698,14 +1736,19 @@ release_bufs: * not much else we can do about it */ if (err) { - dpaa2_eth_free_bufs(priv, buf_array, i); + dpaa2_eth_free_bufs(priv, buf_array, i, ch->xsk_zc); return 0; } return i; err_map: - __free_pages(page, 0); + if (!ch->xsk_zc) { + __free_pages(page, 0); + } else { + for (; i < batch; i++) + xsk_buff_free(xdp_buffs[i]); + } err_alloc: /* If we managed to allocate at least some buffers, * release them to hardware @@ -1764,8 +1807,13 @@ static void dpaa2_eth_drain_bufs(struct dpaa2_eth_priv *priv, int bpid, int count) { u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; + bool xsk_zc = false; int retries = 0; - int ret; + int i, ret; + + for (i = 0; i < priv->num_channels; i++) + if (priv->channel[i]->bp->bpid == bpid) + xsk_zc = priv->channel[i]->xsk_zc; do { ret = dpaa2_io_service_acquire(NULL, bpid, buf_array, count); @@ -1776,7 +1824,7 @@ static void dpaa2_eth_drain_bufs(struct dpaa2_eth_priv *priv, int bpid, netdev_err(priv->net_dev, "dpaa2_io_service_acquire() failed\n"); return; } - dpaa2_eth_free_bufs(priv, buf_array, ret); + dpaa2_eth_free_bufs(priv, buf_array, ret, xsk_zc); retries = 0; } while (ret); } @@ -2694,6 +2742,8 @@ static int dpaa2_eth_xdp(struct net_device *dev, struct netdev_bpf *xdp) switch (xdp->command) { case XDP_SETUP_PROG: return dpaa2_eth_setup_xdp(dev, xdp->prog); + case XDP_SETUP_XSK_POOL: + return dpaa2_xsk_setup_pool(dev, xdp->xsk.pool, xdp->xsk.queue_id); default: return -EINVAL; } @@ -2924,6 +2974,7 @@ static const struct net_device_ops dpaa2_eth_ops = { .ndo_change_mtu = dpaa2_eth_change_mtu, .ndo_bpf = dpaa2_eth_xdp, .ndo_xdp_xmit = dpaa2_eth_xdp_xmit, + .ndo_xsk_wakeup = dpaa2_xsk_wakeup, .ndo_setup_tc = dpaa2_eth_setup_tc, .ndo_vlan_rx_add_vid = dpaa2_eth_rx_add_vid, .ndo_vlan_rx_kill_vid = dpaa2_eth_rx_kill_vid @@ -4247,8 +4298,8 @@ static int dpaa2_eth_bind_dpni(struct dpaa2_eth_priv *priv) { struct dpaa2_eth_bp *bp = priv->bp[DPAA2_ETH_DEFAULT_BP_IDX]; struct net_device *net_dev = priv->net_dev; + struct dpni_pools_cfg pools_params = { 0 }; struct device *dev = net_dev->dev.parent; - struct dpni_pools_cfg pools_params; struct dpni_error_cfg err_cfg; int err = 0; int i; diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h index 3c4fc46b1324..38f67b98865f 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h @@ -130,6 +130,7 @@ enum dpaa2_eth_swa_type { DPAA2_ETH_SWA_SINGLE, DPAA2_ETH_SWA_SG, DPAA2_ETH_SWA_XDP, + DPAA2_ETH_SWA_XSK, DPAA2_ETH_SWA_SW_TSO, }; @@ -151,6 +152,9 @@ struct dpaa2_eth_swa { int dma_size; struct xdp_frame *xdpf; } xdp; + struct { + struct xdp_buff *xdp_buff; + } xsk; struct { struct sk_buff *skb; int num_sg; @@ -429,12 +433,19 @@ enum dpaa2_eth_fq_type { }; struct dpaa2_eth_priv; +struct dpaa2_eth_channel; +struct dpaa2_eth_fq; struct dpaa2_eth_xdp_fds { struct dpaa2_fd fds[DEV_MAP_BULK_SIZE]; ssize_t num; }; +typedef void dpaa2_eth_consume_cb_t(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + const struct dpaa2_fd *fd, + struct dpaa2_eth_fq *fq); + struct dpaa2_eth_fq { u32 fqid; u32 tx_qdbin; @@ -447,10 +458,7 @@ struct dpaa2_eth_fq { struct dpaa2_eth_channel *channel; enum dpaa2_eth_fq_type type; - void (*consume)(struct dpaa2_eth_priv *priv, - struct dpaa2_eth_channel *ch, - const struct dpaa2_fd *fd, - struct dpaa2_eth_fq *fq); + dpaa2_eth_consume_cb_t *consume; struct dpaa2_eth_fq_stats stats; struct dpaa2_eth_xdp_fds xdp_redirect_fds; @@ -486,6 +494,8 @@ struct dpaa2_eth_channel { u64 recycled_bufs[DPAA2_ETH_BUFS_PER_CMD]; int recycled_bufs_cnt; + bool xsk_zc; + struct xsk_buff_pool *xsk_pool; struct dpaa2_eth_bp *bp; }; @@ -808,4 +818,22 @@ void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, struct dpaa2_eth_channel *ch, const struct dpaa2_fd *fd, struct dpaa2_eth_fq *fq); + +struct dpaa2_eth_bp *dpaa2_eth_allocate_dpbp(struct dpaa2_eth_priv *priv); +void dpaa2_eth_free_dpbp(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_bp *bp); + +void *dpaa2_iova_to_virt(struct iommu_domain *domain, dma_addr_t iova_addr); +void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + dma_addr_t addr); + +void dpaa2_eth_xdp_enqueue(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + struct dpaa2_fd *fd, + void *buf_start, u16 queue_id); + +int dpaa2_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags); +int dpaa2_xsk_setup_pool(struct net_device *dev, struct xsk_buff_pool *pool, u16 qid); + #endif /* __DPAA2_H */ diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c new file mode 100644 index 000000000000..2df7bffec5a7 --- /dev/null +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* Copyright 2022 NXP + */ +#include +#include +#include +#include +#include + +#include "dpaa2-eth.h" + +static void dpaa2_eth_setup_consume_func(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + enum dpaa2_eth_fq_type type, + dpaa2_eth_consume_cb_t *consume) +{ + struct dpaa2_eth_fq *fq; + int i; + + for (i = 0; i < priv->num_fqs; i++) { + fq = &priv->fq[i]; + + if (fq->type != type) + continue; + if (fq->channel != ch) + continue; + + fq->consume = consume; + } +} + +static u32 dpaa2_xsk_run_xdp(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + struct dpaa2_eth_fq *rx_fq, + struct dpaa2_fd *fd, void *vaddr) +{ + dma_addr_t addr = dpaa2_fd_get_addr(fd); + struct bpf_prog *xdp_prog; + struct xdp_buff *xdp_buff; + struct dpaa2_eth_swa *swa; + u32 xdp_act = XDP_PASS; + int err; + + xdp_prog = READ_ONCE(ch->xdp.prog); + if (!xdp_prog) + goto out; + + swa = (struct dpaa2_eth_swa *)(vaddr + DPAA2_ETH_RX_HWA_SIZE + + ch->xsk_pool->umem->headroom); + xdp_buff = swa->xsk.xdp_buff; + + xdp_buff->data_hard_start = vaddr; + xdp_buff->data = vaddr + dpaa2_fd_get_offset(fd); + xdp_buff->data_end = xdp_buff->data + dpaa2_fd_get_len(fd); + xdp_set_data_meta_invalid(xdp_buff); + xdp_buff->rxq = &ch->xdp_rxq; + + xsk_buff_dma_sync_for_cpu(xdp_buff, ch->xsk_pool); + xdp_act = bpf_prog_run_xdp(xdp_prog, xdp_buff); + + /* xdp.data pointer may have changed */ + dpaa2_fd_set_offset(fd, xdp_buff->data - vaddr); + dpaa2_fd_set_len(fd, xdp_buff->data_end - xdp_buff->data); + + if (likely(xdp_act == XDP_REDIRECT)) { + err = xdp_do_redirect(priv->net_dev, xdp_buff, xdp_prog); + if (unlikely(err)) { + ch->stats.xdp_drop++; + dpaa2_eth_recycle_buf(priv, ch, addr); + } else { + ch->buf_count--; + ch->stats.xdp_redirect++; + } + + goto xdp_redir; + } + + switch (xdp_act) { + case XDP_PASS: + break; + case XDP_TX: + dpaa2_eth_xdp_enqueue(priv, ch, fd, vaddr, rx_fq->flowid); + break; + default: + bpf_warn_invalid_xdp_action(priv->net_dev, xdp_prog, xdp_act); + fallthrough; + case XDP_ABORTED: + trace_xdp_exception(priv->net_dev, xdp_prog, xdp_act); + fallthrough; + case XDP_DROP: + dpaa2_eth_recycle_buf(priv, ch, addr); + ch->stats.xdp_drop++; + break; + } + +xdp_redir: + ch->xdp.res |= xdp_act; +out: + return xdp_act; +} + +/* Rx frame processing routine for the AF_XDP fast path */ +static void dpaa2_xsk_rx(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + const struct dpaa2_fd *fd, + struct dpaa2_eth_fq *fq) +{ + dma_addr_t addr = dpaa2_fd_get_addr(fd); + u8 fd_format = dpaa2_fd_get_format(fd); + struct rtnl_link_stats64 *percpu_stats; + u32 fd_length = dpaa2_fd_get_len(fd); + struct sk_buff *skb; + void *vaddr; + u32 xdp_act; + + vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr); + percpu_stats = this_cpu_ptr(priv->percpu_stats); + + if (fd_format != dpaa2_fd_single) { + WARN_ON(priv->xdp_prog); + /* AF_XDP doesn't support any other formats */ + goto err_frame_format; + } + + xdp_act = dpaa2_xsk_run_xdp(priv, ch, fq, (struct dpaa2_fd *)fd, vaddr); + if (xdp_act != XDP_PASS) { + percpu_stats->rx_packets++; + percpu_stats->rx_bytes += dpaa2_fd_get_len(fd); + return; + } + + /* Build skb */ + skb = dpaa2_eth_alloc_skb(priv, ch, fd, fd_length, vaddr); + if (!skb) + /* Nothing else we can do, recycle the buffer and + * drop the frame. + */ + goto err_alloc_skb; + + /* Send the skb to the Linux networking stack */ + dpaa2_eth_receive_skb(priv, ch, fd, vaddr, fq, percpu_stats, skb); + + return; + +err_alloc_skb: + dpaa2_eth_recycle_buf(priv, ch, addr); +err_frame_format: + percpu_stats->rx_dropped++; +} + +static void dpaa2_xsk_set_bp_per_qdbin(struct dpaa2_eth_priv *priv, + struct dpni_pools_cfg *pools_params) +{ + int curr_bp = 0, i, j; + + pools_params->pool_options = DPNI_POOL_ASSOC_QDBIN; + for (i = 0; i < priv->num_bps; i++) { + for (j = 0; j < priv->num_channels; j++) + if (priv->bp[i] == priv->channel[j]->bp) + pools_params->pools[curr_bp].priority_mask |= (1 << j); + if (!pools_params->pools[curr_bp].priority_mask) + continue; + + pools_params->pools[curr_bp].dpbp_id = priv->bp[i]->bpid; + pools_params->pools[curr_bp].buffer_size = priv->rx_buf_size; + pools_params->pools[curr_bp++].backup_pool = 0; + } + pools_params->num_dpbp = curr_bp; +} + +static int dpaa2_xsk_disable_pool(struct net_device *dev, u16 qid) +{ + struct xsk_buff_pool *pool = xsk_get_pool_from_qid(dev, qid); + struct dpaa2_eth_priv *priv = netdev_priv(dev); + struct dpni_pools_cfg pools_params = { 0 }; + struct dpaa2_eth_channel *ch; + int err; + bool up; + + ch = priv->channel[qid]; + if (!ch->xsk_pool) + return -EINVAL; + + up = netif_running(dev); + if (up) + dev_close(dev); + + xsk_pool_dma_unmap(pool, 0); + err = xdp_rxq_info_reg_mem_model(&ch->xdp_rxq, + MEM_TYPE_PAGE_ORDER0, NULL); + if (err) + netdev_err(dev, "xsk_rxq_info_reg_mem_model() failed (err = %d)\n", + err); + + dpaa2_eth_free_dpbp(priv, ch->bp); + + ch->xsk_zc = false; + ch->xsk_pool = NULL; + ch->bp = priv->bp[DPAA2_ETH_DEFAULT_BP_IDX]; + + dpaa2_eth_setup_consume_func(priv, ch, DPAA2_RX_FQ, dpaa2_eth_rx); + + dpaa2_xsk_set_bp_per_qdbin(priv, &pools_params); + err = dpni_set_pools(priv->mc_io, 0, priv->mc_token, &pools_params); + if (err) + netdev_err(dev, "dpni_set_pools() failed\n"); + + if (up) { + err = dev_open(dev, NULL); + if (err) + return err; + } + + return 0; +} + +static int dpaa2_xsk_enable_pool(struct net_device *dev, + struct xsk_buff_pool *pool, + u16 qid) +{ + struct dpaa2_eth_priv *priv = netdev_priv(dev); + struct dpni_pools_cfg pools_params = { 0 }; + struct dpaa2_eth_channel *ch; + int err, err2; + bool up; + + if (priv->dpni_attrs.wriop_version < DPAA2_WRIOP_VERSION(3, 0, 0)) { + netdev_err(dev, "AF_XDP zero-copy not supported on devices <= WRIOP(3, 0, 0)\n"); + return -EOPNOTSUPP; + } + + if (priv->dpni_attrs.num_queues > 8) { + netdev_err(dev, "AF_XDP zero-copy not supported on DPNI with more then 8 queues\n"); + return -EOPNOTSUPP; + } + + up = netif_running(dev); + if (up) + dev_close(dev); + + err = xsk_pool_dma_map(pool, priv->net_dev->dev.parent, 0); + if (err) { + netdev_err(dev, "xsk_pool_dma_map() failed (err = %d)\n", + err); + goto err_dma_unmap; + } + + ch = priv->channel[qid]; + err = xdp_rxq_info_reg_mem_model(&ch->xdp_rxq, MEM_TYPE_XSK_BUFF_POOL, NULL); + if (err) { + netdev_err(dev, "xdp_rxq_info_reg_mem_model() failed (err = %d)\n", err); + goto err_mem_model; + } + xsk_pool_set_rxq_info(pool, &ch->xdp_rxq); + + priv->bp[priv->num_bps] = dpaa2_eth_allocate_dpbp(priv); + if (IS_ERR(priv->bp[priv->num_bps])) { + err = PTR_ERR(priv->bp[priv->num_bps]); + goto err_bp_alloc; + } + ch->xsk_zc = true; + ch->xsk_pool = pool; + ch->bp = priv->bp[priv->num_bps++]; + + dpaa2_eth_setup_consume_func(priv, ch, DPAA2_RX_FQ, dpaa2_xsk_rx); + + dpaa2_xsk_set_bp_per_qdbin(priv, &pools_params); + err = dpni_set_pools(priv->mc_io, 0, priv->mc_token, &pools_params); + if (err) { + netdev_err(dev, "dpni_set_pools() failed\n"); + goto err_set_pools; + } + + if (up) { + err = dev_open(dev, NULL); + if (err) + return err; + } + + return 0; + +err_set_pools: + err2 = dpaa2_xsk_disable_pool(dev, qid); + if (err2) + netdev_err(dev, "dpaa2_xsk_disable_pool() failed %d\n", err2); +err_bp_alloc: + err2 = xdp_rxq_info_reg_mem_model(&priv->channel[qid]->xdp_rxq, + MEM_TYPE_PAGE_ORDER0, NULL); + if (err2) + netdev_err(dev, "xsk_rxq_info_reg_mem_model() failed with %d)\n", err2); +err_mem_model: + xsk_pool_dma_unmap(pool, 0); +err_dma_unmap: + if (up) + dev_open(dev, NULL); + + return err; +} + +int dpaa2_xsk_setup_pool(struct net_device *dev, struct xsk_buff_pool *pool, u16 qid) +{ + return pool ? dpaa2_xsk_enable_pool(dev, pool, qid) : + dpaa2_xsk_disable_pool(dev, qid); +} + +int dpaa2_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags) +{ + struct dpaa2_eth_priv *priv = netdev_priv(dev); + struct dpaa2_eth_channel *ch = priv->channel[qid]; + + if (!priv->link_state.up) + return -ENETDOWN; + + if (!priv->xdp_prog) + return -EINVAL; + + if (!ch->xsk_zc) + return -EINVAL; + + /* We do not have access to a per channel SW interrupt, so instead we + * schedule a NAPI instance. + */ + if (!napi_if_scheduled_mark_missed(&ch->napi)) + napi_schedule(&ch->napi); + + return 0; +} -- cgit v1.2.3 From 8beef08f4618c9bfab9374fc72930fc6ed70fdc1 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 20 Oct 2022 15:08:56 +0200 Subject: net: microchip: sparx5: Adding initial VCAP API support This provides the initial VCAP API framework and Sparx5 specific VCAP implementation. When the Sparx5 Switchdev driver is initialized it will also initialize its VCAP module, and this hooks up the concrete Sparx5 VCAP model to the VCAP API, so that the VCAP API knows what VCAP instances are available. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- MAINTAINERS | 1 + drivers/net/ethernet/microchip/Kconfig | 1 + drivers/net/ethernet/microchip/sparx5/Kconfig | 1 + drivers/net/ethernet/microchip/sparx5/Makefile | 8 +- .../net/ethernet/microchip/sparx5/sparx5_main.c | 9 + .../net/ethernet/microchip/sparx5/sparx5_main.h | 6 + .../ethernet/microchip/sparx5/sparx5_vcap_impl.c | 41 +++ drivers/net/ethernet/microchip/vcap/Kconfig | 39 +++ drivers/net/ethernet/microchip/vcap/vcap_ag_api.h | 326 +++++++++++++++++++++ drivers/net/ethernet/microchip/vcap/vcap_api.h | 269 +++++++++++++++++ 10 files changed, 699 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c create mode 100644 drivers/net/ethernet/microchip/vcap/Kconfig create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_ag_api.h create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_api.h (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 479a15d86a91..45c79261e355 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2439,6 +2439,7 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported T: git git://github.com/microchip-ung/linux-upstream.git F: arch/arm64/boot/dts/microchip/ +F: drivers/net/ethernet/microchip/vcap/ F: drivers/pinctrl/pinctrl-microchip-sgpio.c N: sparx5 diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig index ed7a35c3ceac..24c994baad13 100644 --- a/drivers/net/ethernet/microchip/Kconfig +++ b/drivers/net/ethernet/microchip/Kconfig @@ -57,5 +57,6 @@ config LAN743X source "drivers/net/ethernet/microchip/lan966x/Kconfig" source "drivers/net/ethernet/microchip/sparx5/Kconfig" +source "drivers/net/ethernet/microchip/vcap/Kconfig" endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/sparx5/Kconfig b/drivers/net/ethernet/microchip/sparx5/Kconfig index cc5e48e1bb4c..98e27530a91f 100644 --- a/drivers/net/ethernet/microchip/sparx5/Kconfig +++ b/drivers/net/ethernet/microchip/sparx5/Kconfig @@ -9,5 +9,6 @@ config SPARX5_SWITCH select PHYLINK select PHY_SPARX5_SERDES select RESET_CONTROLLER + select VCAP help This driver supports the Sparx5 network switch device. diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile index d1c6ad966747..aa4dadb8a25d 100644 --- a/drivers/net/ethernet/microchip/sparx5/Makefile +++ b/drivers/net/ethernet/microchip/sparx5/Makefile @@ -5,7 +5,11 @@ obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o -sparx5-switch-objs := sparx5_main.o sparx5_packet.o \ +sparx5-switch-y := sparx5_main.o sparx5_packet.o \ sparx5_netdev.o sparx5_phylink.o sparx5_port.o sparx5_mactable.o sparx5_vlan.o \ sparx5_switchdev.o sparx5_calendar.o sparx5_ethtool.o sparx5_fdma.o \ - sparx5_ptp.o sparx5_pgid.o sparx5_tc.o sparx5_qos.o + sparx5_ptp.o sparx5_pgid.o sparx5_tc.o sparx5_qos.o \ + sparx5_vcap_impl.o + +# Provide include files +ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c index 62a325e96345..0b70c00c6eaa 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c @@ -672,6 +672,14 @@ static int sparx5_start(struct sparx5 *sparx5) sparx5_board_init(sparx5); err = sparx5_register_notifier_blocks(sparx5); + if (err) + return err; + + err = sparx5_vcap_init(sparx5); + if (err) { + sparx5_unregister_notifier_blocks(sparx5); + return err; + } /* Start Frame DMA with fallback to register based INJ/XTR */ err = -ENXIO; @@ -906,6 +914,7 @@ static int mchp_sparx5_remove(struct platform_device *pdev) sparx5_ptp_deinit(sparx5); sparx5_fdma_stop(sparx5); sparx5_cleanup_ports(sparx5); + sparx5_vcap_destroy(sparx5); /* Unregister netdevs */ sparx5_unregister_notifier_blocks(sparx5); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h index 7a83222caa73..2ab22a7b799e 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h @@ -288,6 +288,8 @@ struct sparx5 { struct mutex ptp_lock; /* lock for ptp interface state */ u16 ptp_skbs; int ptp_irq; + /* VCAP */ + struct vcap_control *vcap_ctrl; /* PGID allocation map */ u8 pgid_map[PGID_TABLE_SIZE]; }; @@ -382,6 +384,10 @@ void sparx5_ptp_txtstamp_release(struct sparx5_port *port, struct sk_buff *skb); irqreturn_t sparx5_ptp_irq_handler(int irq, void *args); +/* sparx5_vcap_impl.c */ +int sparx5_vcap_init(struct sparx5 *sparx5); +void sparx5_vcap_destroy(struct sparx5 *sparx5); + /* sparx5_pgid.c */ enum sparx5_pgid_type { SPX5_PGID_FREE, diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c new file mode 100644 index 000000000000..8df7cba77a28 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip Sparx5 Switch driver VCAP implementation + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + * + * The Sparx5 Chip Register Model can be browsed at this location: + * https://github.com/microchip-ung/sparx-5_reginfo + */ + +#include +#include + +#include "vcap_api.h" +#include "sparx5_main_regs.h" +#include "sparx5_main.h" + +/* Allocate a vcap control and vcap instances and configure the system */ +int sparx5_vcap_init(struct sparx5 *sparx5) +{ + struct vcap_control *ctrl; + + /* Create a VCAP control instance that owns the platform specific VCAP + * model with VCAP instances and information about keysets, keys, + * actionsets and actions + */ + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + sparx5->vcap_ctrl = ctrl; + + return 0; +} + +void sparx5_vcap_destroy(struct sparx5 *sparx5) +{ + if (!sparx5->vcap_ctrl) + return; + + kfree(sparx5->vcap_ctrl); +} diff --git a/drivers/net/ethernet/microchip/vcap/Kconfig b/drivers/net/ethernet/microchip/vcap/Kconfig new file mode 100644 index 000000000000..a78cbc6ce6bb --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/Kconfig @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Microchip VCAP API configuration +# + +if NET_VENDOR_MICROCHIP + +config VCAP + bool "VCAP (Versatile Content-Aware Processor) library" + help + Provides the basic VCAP functionality for multiple Microchip switchcores + + A VCAP is essentially a TCAM with rules consisting of + + - Programmable key fields + - Programmable action fields + - A counter (which may be only one bit wide) + + Besides this each VCAP has: + + - A number of lookups + - A keyset configuration per port per lookup + + The VCAP implementation provides switchcore independent handling of rules + and supports: + + - Creating and deleting rules + - Updating and getting rules + + The platform specific configuration as well as the platform specific model + of the VCAP instances are attached to the VCAP API and a client can then + access rules via the API in a platform independent way, with the + limitations that each VCAP has in terms of its supported keys and actions. + + Different switchcores will have different VCAP instances with different + characteristics. Look in the datasheet for the VCAP specifications for the + specific switchcore. + +endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h b/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h new file mode 100644 index 000000000000..804d57b9b60a --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h @@ -0,0 +1,326 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API + */ + +/* This file is autogenerated by cml-utils 2022-10-13 10:04:41 +0200. + * Commit ID: fd7cafd175899f0672c73afb3a30fc872500ae86 + */ + +#ifndef __VCAP_AG_API__ +#define __VCAP_AG_API__ + +enum vcap_type { + VCAP_TYPE_IS2, + VCAP_TYPE_MAX +}; + +/* Keyfieldset names with origin information */ +enum vcap_keyfield_set { + VCAP_KFS_NO_VALUE, /* initial value */ + VCAP_KFS_ARP, /* sparx5 is2 X6 */ + VCAP_KFS_IP4_OTHER, /* sparx5 is2 X6 */ + VCAP_KFS_IP4_TCP_UDP, /* sparx5 is2 X6 */ + VCAP_KFS_IP6_STD, /* sparx5 is2 X6 */ + VCAP_KFS_IP_7TUPLE, /* sparx5 is2 X12 */ + VCAP_KFS_MAC_ETYPE, /* sparx5 is2 X6 */ +}; + +/* List of keyfields with description + * + * Keys ending in _IS are booleans derived from frame data + * Keys ending in _CLS are classified frame data + * + * VCAP_KF_8021Q_DEI_CLS: W1, sparx5: is2 + * Classified DEI + * VCAP_KF_8021Q_PCP_CLS: W3, sparx5: is2 + * Classified PCP + * VCAP_KF_8021Q_VID_CLS: W13, sparx5: is2 + * Classified VID + * VCAP_KF_8021Q_VLAN_TAGGED_IS: W1, sparx5: is2 + * Sparx5: Set if frame was received with a VLAN tag, LAN966x: Set if frame has + * one or more Q-tags. Independent of port VLAN awareness + * VCAP_KF_ARP_ADDR_SPACE_OK_IS: W1, sparx5: is2 + * Set if hardware address is Ethernet + * VCAP_KF_ARP_LEN_OK_IS: W1, sparx5: is2 + * Set if hardware address length = 6 (Ethernet) and IP address length = 4 (IP). + * VCAP_KF_ARP_OPCODE: W2, sparx5: is2 + * ARP opcode + * VCAP_KF_ARP_OPCODE_UNKNOWN_IS: W1, sparx5: is2 + * Set if not one of the codes defined in VCAP_KF_ARP_OPCODE + * VCAP_KF_ARP_PROTO_SPACE_OK_IS: W1, sparx5: is2 + * Set if protocol address space is 0x0800 + * VCAP_KF_ARP_SENDER_MATCH_IS: W1, sparx5: is2 + * Sender Hardware Address = SMAC (ARP) + * VCAP_KF_ARP_TGT_MATCH_IS: W1, sparx5: is2 + * Target Hardware Address = SMAC (RARP) + * VCAP_KF_ETYPE: W16, sparx5: is2 + * Ethernet type + * VCAP_KF_ETYPE_LEN_IS: W1, sparx5: is2 + * Set if frame has EtherType >= 0x600 + * VCAP_KF_IF_IGR_PORT_MASK: sparx5 is2 W32, sparx5 is2 W65 + * Ingress port mask, one bit per port/erleg + * VCAP_KF_IF_IGR_PORT_MASK_L3: W1, sparx5: is2 + * If set, IF_IGR_PORT_MASK, IF_IGR_PORT_MASK_RNG, and IF_IGR_PORT_MASK_SEL are + * used to specify L3 interfaces + * VCAP_KF_IF_IGR_PORT_MASK_RNG: W4, sparx5: is2 + * Range selector for IF_IGR_PORT_MASK. Specifies which group of 32 ports are + * available in IF_IGR_PORT_MASK + * VCAP_KF_IF_IGR_PORT_MASK_SEL: W2, sparx5: is2 + * Mode selector for IF_IGR_PORT_MASK, applicable when IF_IGR_PORT_MASK_L3 == 0. + * Mapping: 0: DEFAULT 1: LOOPBACK 2: MASQUERADE 3: CPU_VD + * VCAP_KF_IP4_IS: W1, sparx5: is2 + * Set if frame has EtherType = 0x800 and IP version = 4 + * VCAP_KF_ISDX_CLS: W12, sparx5: is2 + * Classified ISDX + * VCAP_KF_ISDX_GT0_IS: W1, sparx5: is2 + * Set if classified ISDX > 0 + * VCAP_KF_L2_BC_IS: W1, sparx5: is2 + * Set if frame’s destination MAC address is the broadcast address + * (FF-FF-FF-FF-FF-FF). + * VCAP_KF_L2_DMAC: W48, sparx5: is2 + * Destination MAC address + * VCAP_KF_L2_FWD_IS: W1, sparx5: is2 + * Set if the frame is allowed to be forwarded to front ports + * VCAP_KF_L2_MC_IS: W1, sparx5: is2 + * Set if frame’s destination MAC address is a multicast address (bit 40 = 1). + * VCAP_KF_L2_PAYLOAD_ETYPE: W64, sparx5: is2 + * Byte 0-7 of L2 payload after Type/Len field and overloading for OAM + * VCAP_KF_L2_SMAC: W48, sparx5: is2 + * Source MAC address + * VCAP_KF_L3_DIP_EQ_SIP_IS: W1, sparx5: is2 + * Set if Src IP matches Dst IP address + * VCAP_KF_L3_DST_IS: W1, sparx5: is2 + * Set if lookup is done for egress router leg + * VCAP_KF_L3_FRAGMENT_TYPE: W2, sparx5: is2 + * L3 Fragmentation type (none, initial, suspicious, valid follow up) + * VCAP_KF_L3_FRAG_INVLD_L4_LEN: W1, sparx5: is2 + * Set if frame's L4 length is less than ANA_CL:COMMON:CLM_FRAGMENT_CFG.L4_MIN_L + * EN + * VCAP_KF_L3_IP4_DIP: W32, sparx5: is2 + * Destination IPv4 Address + * VCAP_KF_L3_IP4_SIP: W32, sparx5: is2 + * Source IPv4 Address + * VCAP_KF_L3_IP6_DIP: W128, sparx5: is2 + * Sparx5: Full IPv6 DIP, LAN966x: Either Full IPv6 DIP or a subset depending on + * frame type + * VCAP_KF_L3_IP6_SIP: W128, sparx5: is2 + * Sparx5: Full IPv6 SIP, LAN966x: Either Full IPv6 SIP or a subset depending on + * frame type + * VCAP_KF_L3_IP_PROTO: W8, sparx5: is2 + * IPv4 frames: IP protocol. IPv6 frames: Next header, same as for IPV4 + * VCAP_KF_L3_OPTIONS_IS: W1, sparx5: is2 + * Set if IPv4 frame contains options (IP len > 5) + * VCAP_KF_L3_PAYLOAD: sparx5 is2 W96, sparx5 is2 W40 + * Sparx5: Payload bytes after IP header. IPv4: IPv4 options are not parsed so + * payload is always taken 20 bytes after the start of the IPv4 header, LAN966x: + * Bytes 0-6 after IP header + * VCAP_KF_L3_RT_IS: W1, sparx5: is2 + * Set if frame has hit a router leg + * VCAP_KF_L3_TOS: W8, sparx5: is2 + * Sparx5: Frame's IPv4/IPv6 DSCP and ECN fields, LAN966x: IP TOS field + * VCAP_KF_L3_TTL_GT0: W1, sparx5: is2 + * Set if IPv4 TTL / IPv6 hop limit is greater than 0 + * VCAP_KF_L4_ACK: W1, sparx5: is2 + * Sparx5 and LAN966x: TCP flag ACK, LAN966x only: PTP over UDP: flagField bit 2 + * (unicastFlag) + * VCAP_KF_L4_DPORT: W16, sparx5: is2 + * Sparx5: TCP/UDP destination port. Overloading for IP_7TUPLE: Non-TCP/UDP IP + * frames: L4_DPORT = L3_IP_PROTO, LAN966x: TCP/UDP destination port + * VCAP_KF_L4_FIN: W1, sparx5: is2 + * TCP flag FIN, LAN966x: TCP flag FIN, and for PTP over UDP: messageType bit 1 + * VCAP_KF_L4_PAYLOAD: W64, sparx5: is2 + * Payload bytes after TCP/UDP header Overloading for IP_7TUPLE: Non TCP/UDP + * frames: Payload bytes 0–7 after IP header. IPv4 options are not parsed so + * payload is always taken 20 bytes after the start of the IPv4 header for non + * TCP/UDP IPv4 frames + * VCAP_KF_L4_PSH: W1, sparx5: is2 + * Sparx5: TCP flag PSH, LAN966x: TCP: TCP flag PSH. PTP over UDP: flagField bit + * 1 (twoStepFlag) + * VCAP_KF_L4_RNG: W16, sparx5: is2 + * Range checker bitmask (one for each range checker). Input into range checkers + * is taken from classified results (VID, DSCP) and frame (SPORT, DPORT, ETYPE, + * outer VID, inner VID) + * VCAP_KF_L4_RST: W1, sparx5: is2 + * Sparx5: TCP flag RST , LAN966x: TCP: TCP flag RST. PTP over UDP: messageType + * bit 3 + * VCAP_KF_L4_SEQUENCE_EQ0_IS: W1, sparx5: is2 + * Set if TCP sequence number is 0, LAN966x: Overlayed with PTP over UDP: + * messageType bit 0 + * VCAP_KF_L4_SPORT: W16, sparx5: is2 + * TCP/UDP source port + * VCAP_KF_L4_SPORT_EQ_DPORT_IS: W1, sparx5: is2 + * Set if UDP or TCP source port equals UDP or TCP destination port + * VCAP_KF_L4_SYN: W1, sparx5: is2 + * Sparx5: TCP flag SYN, LAN966x: TCP: TCP flag SYN. PTP over UDP: messageType + * bit 2 + * VCAP_KF_L4_URG: W1, sparx5: is2 + * Sparx5: TCP flag URG, LAN966x: TCP: TCP flag URG. PTP over UDP: flagField bit + * 7 (reserved) + * VCAP_KF_LOOKUP_FIRST_IS: W1, sparx5: is2 + * Selects between entries relevant for first and second lookup. Set for first + * lookup, cleared for second lookup. + * VCAP_KF_LOOKUP_PAG: W8, sparx5: is2 + * Classified Policy Association Group: chains rules from IS1/CLM to IS2 + * VCAP_KF_OAM_CCM_CNTS_EQ0: W1, sparx5: is2 + * Dual-ended loss measurement counters in CCM frames are all zero + * VCAP_KF_OAM_Y1731_IS: W1, sparx5: is2 + * Set if frame’s EtherType = 0x8902 + * VCAP_KF_TCP_IS: W1, sparx5: is2 + * Set if frame is IPv4 TCP frame (IP protocol = 6) or IPv6 TCP frames (Next + * header = 6) + * VCAP_KF_TCP_UDP_IS: W1, sparx5: is2 + * Set if frame is IPv4/IPv6 TCP or UDP frame (IP protocol/next header equals 6 + * or 17) + * VCAP_KF_TYPE: sparx5 is2 W4, sparx5 is2 W2 + * Keyset type id - set by the API + */ + +/* Keyfield names */ +enum vcap_key_field { + VCAP_KF_NO_VALUE, /* initial value */ + VCAP_KF_8021Q_DEI_CLS, + VCAP_KF_8021Q_PCP_CLS, + VCAP_KF_8021Q_VID_CLS, + VCAP_KF_8021Q_VLAN_TAGGED_IS, + VCAP_KF_ARP_ADDR_SPACE_OK_IS, + VCAP_KF_ARP_LEN_OK_IS, + VCAP_KF_ARP_OPCODE, + VCAP_KF_ARP_OPCODE_UNKNOWN_IS, + VCAP_KF_ARP_PROTO_SPACE_OK_IS, + VCAP_KF_ARP_SENDER_MATCH_IS, + VCAP_KF_ARP_TGT_MATCH_IS, + VCAP_KF_ETYPE, + VCAP_KF_ETYPE_LEN_IS, + VCAP_KF_IF_IGR_PORT_MASK, + VCAP_KF_IF_IGR_PORT_MASK_L3, + VCAP_KF_IF_IGR_PORT_MASK_RNG, + VCAP_KF_IF_IGR_PORT_MASK_SEL, + VCAP_KF_IP4_IS, + VCAP_KF_ISDX_CLS, + VCAP_KF_ISDX_GT0_IS, + VCAP_KF_L2_BC_IS, + VCAP_KF_L2_DMAC, + VCAP_KF_L2_FWD_IS, + VCAP_KF_L2_MC_IS, + VCAP_KF_L2_PAYLOAD_ETYPE, + VCAP_KF_L2_SMAC, + VCAP_KF_L3_DIP_EQ_SIP_IS, + VCAP_KF_L3_DST_IS, + VCAP_KF_L3_FRAGMENT_TYPE, + VCAP_KF_L3_FRAG_INVLD_L4_LEN, + VCAP_KF_L3_IP4_DIP, + VCAP_KF_L3_IP4_SIP, + VCAP_KF_L3_IP6_DIP, + VCAP_KF_L3_IP6_SIP, + VCAP_KF_L3_IP_PROTO, + VCAP_KF_L3_OPTIONS_IS, + VCAP_KF_L3_PAYLOAD, + VCAP_KF_L3_RT_IS, + VCAP_KF_L3_TOS, + VCAP_KF_L3_TTL_GT0, + VCAP_KF_L4_ACK, + VCAP_KF_L4_DPORT, + VCAP_KF_L4_FIN, + VCAP_KF_L4_PAYLOAD, + VCAP_KF_L4_PSH, + VCAP_KF_L4_RNG, + VCAP_KF_L4_RST, + VCAP_KF_L4_SEQUENCE_EQ0_IS, + VCAP_KF_L4_SPORT, + VCAP_KF_L4_SPORT_EQ_DPORT_IS, + VCAP_KF_L4_SYN, + VCAP_KF_L4_URG, + VCAP_KF_LOOKUP_FIRST_IS, + VCAP_KF_LOOKUP_PAG, + VCAP_KF_OAM_CCM_CNTS_EQ0, + VCAP_KF_OAM_Y1731_IS, + VCAP_KF_TCP_IS, + VCAP_KF_TCP_UDP_IS, + VCAP_KF_TYPE, +}; + +/* Actionset names with origin information */ +enum vcap_actionfield_set { + VCAP_AFS_NO_VALUE, /* initial value */ + VCAP_AFS_BASE_TYPE, /* sparx5 is2 X3 */ +}; + +/* List of actionfields with description + * + * VCAP_AF_CNT_ID: W12, sparx5: is2 + * Counter ID, used per lookup to index the 4K frame counters (ANA_ACL:CNT_TBL). + * Multiple VCAP IS2 entries can use the same counter. + * VCAP_AF_CPU_COPY_ENA: W1, sparx5: is2 + * Setting this bit to 1 causes all frames that hit this action to be copied to + * the CPU extraction queue specified in CPU_QUEUE_NUM. + * VCAP_AF_CPU_QUEUE_NUM: W3, sparx5: is2 + * CPU queue number. Used when CPU_COPY_ENA is set. + * VCAP_AF_HIT_ME_ONCE: W1, sparx5: is2 + * Setting this bit to 1 causes the first frame that hits this action where the + * HIT_CNT counter is zero to be copied to the CPU extraction queue specified in + * CPU_QUEUE_NUM. The HIT_CNT counter is then incremented and any frames that + * hit this action later are not copied to the CPU. To re-enable the HIT_ME_ONCE + * functionality, the HIT_CNT counter must be cleared. + * VCAP_AF_IGNORE_PIPELINE_CTRL: W1, sparx5: is2 + * Ignore ingress pipeline control. This enforces the use of the VCAP IS2 action + * even when the pipeline control has terminated the frame before VCAP IS2. + * VCAP_AF_INTR_ENA: W1, sparx5: is2 + * If set, an interrupt is triggered when this rule is hit + * VCAP_AF_LRN_DIS: W1, sparx5: is2 + * Setting this bit to 1 disables learning of frames hitting this action. + * VCAP_AF_MASK_MODE: W3, sparx5: is2 + * Controls the PORT_MASK use. Sparx5: 0: OR_DSTMASK, 1: AND_VLANMASK, 2: + * REPLACE_PGID, 3: REPLACE_ALL, 4: REDIR_PGID, 5: OR_PGID_MASK, 6: VSTAX, 7: + * Not applicable. LAN966X: 0: No action, 1: Permit/deny (AND), 2: Policy + * forwarding (DMAC lookup), 3: Redirect. The CPU port is untouched by + * MASK_MODE. + * VCAP_AF_MATCH_ID: W16, sparx5: is2 + * Logical ID for the entry. The MATCH_ID is extracted together with the frame + * if the frame is forwarded to the CPU (CPU_COPY_ENA). The result is placed in + * IFH.CL_RSLT. + * VCAP_AF_MATCH_ID_MASK: W16, sparx5: is2 + * Mask used by MATCH_ID. + * VCAP_AF_MIRROR_PROBE: W2, sparx5: is2 + * Mirroring performed according to configuration of a mirror probe. 0: No + * mirroring. 1: Mirror probe 0. 2: Mirror probe 1. 3: Mirror probe 2 + * VCAP_AF_PIPELINE_FORCE_ENA: W1, sparx5: is2 + * If set, use PIPELINE_PT unconditionally and set PIPELINE_ACT = NONE if + * PIPELINE_PT == NONE. Overrules previous settings of pipeline point. + * VCAP_AF_PIPELINE_PT: W5, sparx5: is2 + * Pipeline point used if PIPELINE_FORCE_ENA is set + * VCAP_AF_POLICE_ENA: W1, sparx5: is2 + * Setting this bit to 1 causes frames that hit this action to be policed by the + * ACL policer specified in POLICE_IDX. Only applies to the first lookup. + * VCAP_AF_POLICE_IDX: W6, sparx5: is2 + * Selects VCAP policer used when policing frames (POLICE_ENA) + * VCAP_AF_PORT_MASK: W68, sparx5: is2 + * Port mask applied to the forwarding decision based on MASK_MODE. + * VCAP_AF_RT_DIS: W1, sparx5: is2 + * If set, routing is disallowed. Only applies when IS_INNER_ACL is 0. See also + * IGR_ACL_ENA, EGR_ACL_ENA, and RLEG_STAT_IDX. + */ + +/* Actionfield names */ +enum vcap_action_field { + VCAP_AF_NO_VALUE, /* initial value */ + VCAP_AF_CNT_ID, + VCAP_AF_CPU_COPY_ENA, + VCAP_AF_CPU_QUEUE_NUM, + VCAP_AF_HIT_ME_ONCE, + VCAP_AF_IGNORE_PIPELINE_CTRL, + VCAP_AF_INTR_ENA, + VCAP_AF_LRN_DIS, + VCAP_AF_MASK_MODE, + VCAP_AF_MATCH_ID, + VCAP_AF_MATCH_ID_MASK, + VCAP_AF_MIRROR_PROBE, + VCAP_AF_PIPELINE_FORCE_ENA, + VCAP_AF_PIPELINE_PT, + VCAP_AF_POLICE_ENA, + VCAP_AF_POLICE_IDX, + VCAP_AF_PORT_MASK, + VCAP_AF_RT_DIS, +}; + +#endif /* __VCAP_AG_API__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.h b/drivers/net/ethernet/microchip/vcap/vcap_api.h new file mode 100644 index 000000000000..4444bf67ebec --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.h @@ -0,0 +1,269 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API + */ + +#ifndef __VCAP_API__ +#define __VCAP_API__ + +#include +#include +#include + +/* Use the generated API model */ +#include "vcap_ag_api.h" + +#define VCAP_CID_LOOKUP_SIZE 100000 /* Chains in a lookup */ +#define VCAP_CID_INGRESS_L0 1000000 /* Ingress Stage 1 Lookup 0 */ +#define VCAP_CID_INGRESS_L1 1100000 /* Ingress Stage 1 Lookup 1 */ +#define VCAP_CID_INGRESS_L2 1200000 /* Ingress Stage 1 Lookup 2 */ +#define VCAP_CID_INGRESS_L3 1300000 /* Ingress Stage 1 Lookup 3 */ +#define VCAP_CID_INGRESS_L4 1400000 /* Ingress Stage 1 Lookup 4 */ +#define VCAP_CID_INGRESS_L5 1500000 /* Ingress Stage 1 Lookup 5 */ + +#define VCAP_CID_PREROUTING_IPV6 3000000 /* Prerouting Stage */ +#define VCAP_CID_PREROUTING 6000000 /* Prerouting Stage */ + +#define VCAP_CID_INGRESS_STAGE2_L0 8000000 /* Ingress Stage 2 Lookup 0 */ +#define VCAP_CID_INGRESS_STAGE2_L1 8100000 /* Ingress Stage 2 Lookup 1 */ +#define VCAP_CID_INGRESS_STAGE2_L2 8200000 /* Ingress Stage 2 Lookup 2 */ +#define VCAP_CID_INGRESS_STAGE2_L3 8300000 /* Ingress Stage 2 Lookup 3 */ + +#define VCAP_CID_EGRESS_L0 10000000 /* Egress Lookup 0 */ +#define VCAP_CID_EGRESS_L1 10100000 /* Egress Lookup 1 */ + +#define VCAP_CID_EGRESS_STAGE2_L0 20000000 /* Egress Stage 2 Lookup 0 */ +#define VCAP_CID_EGRESS_STAGE2_L1 20100000 /* Egress Stage 2 Lookup 1 */ + +/* Known users of the VCAP API */ +enum vcap_user { + VCAP_USER_PTP, + VCAP_USER_MRP, + VCAP_USER_CFM, + VCAP_USER_VLAN, + VCAP_USER_QOS, + VCAP_USER_VCAP_UTIL, + VCAP_USER_TC, + VCAP_USER_TC_EXTRA, + + /* add new users above here */ + + /* used to define VCAP_USER_MAX below */ + __VCAP_USER_AFTER_LAST, + VCAP_USER_MAX = __VCAP_USER_AFTER_LAST - 1, +}; + +/* VCAP information used for displaying data */ +struct vcap_statistics { + char *name; + int count; + const char * const *keyfield_set_names; + const char * const *actionfield_set_names; + const char * const *keyfield_names; + const char * const *actionfield_names; +}; + +/* VCAP key/action field type, position and width */ +struct vcap_field { + u16 type; + u16 width; + u16 offset; +}; + +/* VCAP keyset or actionset type and width */ +struct vcap_set { + u8 type_id; + u8 sw_per_item; + u8 sw_cnt; +}; + +/* VCAP typegroup position and bitvalue */ +struct vcap_typegroup { + u16 offset; + u16 width; + u16 value; +}; + +/* VCAP model data */ +struct vcap_info { + char *name; /* user-friendly name */ + u16 rows; /* number of row in instance */ + u16 sw_count; /* maximum subwords used per rule */ + u16 sw_width; /* bits per subword in a keyset */ + u16 sticky_width; /* sticky bits per rule */ + u16 act_width; /* bits per subword in an actionset */ + u16 default_cnt; /* number of default rules */ + u16 require_cnt_dis; /* not used */ + u16 version; /* vcap rtl version */ + const struct vcap_set *keyfield_set; /* keysets */ + int keyfield_set_size; /* number of keysets */ + const struct vcap_set *actionfield_set; /* actionsets */ + int actionfield_set_size; /* number of actionsets */ + /* map of keys per keyset */ + const struct vcap_field **keyfield_set_map; + /* number of entries in the above map */ + int *keyfield_set_map_size; + /* map of actions per actionset */ + const struct vcap_field **actionfield_set_map; + /* number of entries in the above map */ + int *actionfield_set_map_size; + /* map of keyset typegroups per subword size */ + const struct vcap_typegroup **keyfield_set_typegroups; + /* map of actionset typegroups per subword size */ + const struct vcap_typegroup **actionfield_set_typegroups; +}; + +enum vcap_field_type { + VCAP_FIELD_BIT, + VCAP_FIELD_U32, + VCAP_FIELD_U48, + VCAP_FIELD_U56, + VCAP_FIELD_U64, + VCAP_FIELD_U72, + VCAP_FIELD_U112, + VCAP_FIELD_U128, +}; + +/* VCAP rule data towards the VCAP cache */ +struct vcap_cache_data { + u32 *keystream; + u32 *maskstream; + u32 *actionstream; + u32 counter; + bool sticky; +}; + +/* Selects which part of the rule must be updated */ +enum vcap_selection { + VCAP_SEL_ENTRY = 0x01, + VCAP_SEL_ACTION = 0x02, + VCAP_SEL_COUNTER = 0x04, + VCAP_SEL_ALL = 0xff, +}; + +/* Commands towards the VCAP cache */ +enum vcap_command { + VCAP_CMD_WRITE = 0, + VCAP_CMD_READ = 1, + VCAP_CMD_MOVE_DOWN = 2, + VCAP_CMD_MOVE_UP = 3, + VCAP_CMD_INITIALIZE = 4, +}; + +enum vcap_rule_error { + VCAP_ERR_NONE = 0, /* No known error */ + VCAP_ERR_NO_ADMIN, /* No admin instance */ + VCAP_ERR_NO_NETDEV, /* No netdev instance */ + VCAP_ERR_NO_KEYSET_MATCH, /* No keyset matched the rule keys */ + VCAP_ERR_NO_ACTIONSET_MATCH, /* No actionset matched the rule actions */ + VCAP_ERR_NO_PORT_KEYSET_MATCH, /* No port keyset matched the rule keys */ +}; + +/* Administration of each VCAP instance */ +struct vcap_admin { + struct list_head list; /* for insertion in vcap_control */ + struct list_head rules; /* list of rules */ + enum vcap_type vtype; /* type of vcap */ + int vinst; /* instance number within the same type */ + int first_cid; /* first chain id in this vcap */ + int last_cid; /* last chain id in this vcap */ + int tgt_inst; /* hardware instance number */ + int lookups; /* number of lookups in this vcap type */ + int lookups_per_instance; /* number of lookups in this instance */ + int last_valid_addr; /* top of address range to be used */ + int first_valid_addr; /* bottom of address range to be used */ + int last_used_addr; /* address of lowest added rule */ + bool w32be; /* vcap uses "32bit-word big-endian" encoding */ + struct vcap_cache_data cache; /* encoded rule data */ +}; + +/* Client supplied VCAP rule data */ +struct vcap_rule { + int vcap_chain_id; /* chain used for this rule */ + enum vcap_user user; /* rule owner */ + u16 priority; + u32 id; /* vcap rule id, must be unique, 0 will auto-generate a value */ + u64 cookie; /* used by the client to identify the rule */ + struct list_head keyfields; /* list of vcap_client_keyfield */ + struct list_head actionfields; /* list of vcap_client_actionfield */ + enum vcap_keyfield_set keyset; /* keyset used: may be derived from fields */ + enum vcap_actionfield_set actionset; /* actionset used: may be derived from fields */ + enum vcap_rule_error exterr; /* extended error - used by TC */ + u64 client; /* space for client defined data */ +}; + +/* List of keysets */ +struct vcap_keyset_list { + int max; /* size of the keyset list */ + int cnt; /* count of keysets actually in the list */ + enum vcap_keyfield_set *keysets; /* the list of keysets */ +}; + +/* Client supplied VCAP callback operations */ +struct vcap_operations { + /* validate port keyset operation */ + enum vcap_keyfield_set (*validate_keyset) + (struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule, + struct vcap_keyset_list *kslist, + u16 l3_proto); + /* add default rule fields for the selected keyset operations */ + void (*add_default_fields) + (struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule); + /* cache operations */ + void (*cache_erase) + (struct vcap_admin *admin); + void (*cache_write) + (struct net_device *ndev, + struct vcap_admin *admin, + enum vcap_selection sel, + u32 idx, u32 count); + void (*cache_read) + (struct net_device *ndev, + struct vcap_admin *admin, + enum vcap_selection sel, + u32 idx, + u32 count); + /* block operations */ + void (*init) + (struct net_device *ndev, + struct vcap_admin *admin, + u32 addr, + u32 count); + void (*update) + (struct net_device *ndev, + struct vcap_admin *admin, + enum vcap_command cmd, + enum vcap_selection sel, + u32 addr); + void (*move) + (struct net_device *ndev, + struct vcap_admin *admin, + u32 addr, + int offset, + int count); + /* informational */ + int (*port_info) + (struct net_device *ndev, + enum vcap_type vtype, + int (*pf)(void *out, int arg, const char *fmt, ...), + void *out, + int arg); +}; + +/* VCAP API Client control interface */ +struct vcap_control { + u32 rule_id; /* last used rule id (unique across VCAP instances) */ + struct vcap_operations *ops; /* client supplied operations */ + const struct vcap_info *vcaps; /* client supplied vcap models */ + const struct vcap_statistics *stats; /* client supplied vcap stats */ + struct list_head list; /* list of vcap instances */ +}; + +/* Set client control interface on the API */ +int vcap_api_set_client(struct vcap_control *vctrl); + +#endif /* __VCAP_API__ */ -- cgit v1.2.3 From ee357294a85b1b0ae4dbd87ec68551c452812a1f Mon Sep 17 00:00:00 2001 From: Vikash Garodia Date: Tue, 13 Sep 2022 17:39:00 +0530 Subject: MAINTAINERS: Add Vikash as VENUS video driver co-maintainer For the past several amendments in video driver, I have been working with Stanimir in multiple design discussions or handling a given issue. With this, adding myself as a co-maintainer. Signed-off-by: Vikash Garodia Acked-by: Stanimir Varbanov Acked-by: Hans Verkuil --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index e04d944005ba..df5969d88707 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17154,6 +17154,7 @@ F: drivers/thermal/qcom/ QUALCOMM VENUS VIDEO ACCELERATOR DRIVER M: Stanimir Varbanov +M: Vikash Garodia L: linux-media@vger.kernel.org L: linux-arm-msm@vger.kernel.org S: Maintained -- cgit v1.2.3 From 15886e59cb3c04fd7705967e2905335f68446c17 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Tue, 25 Oct 2022 10:06:07 +0300 Subject: MAINTAINERS: Change email for Venus driver My email at linaro.org will not be active soon, so change with my private email. Signed-off-by: Stanimir Varbanov Acked-by: Hans Verkuil --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index df5969d88707..516b2dc69db1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17153,7 +17153,7 @@ F: Documentation/devicetree/bindings/thermal/qcom-tsens.yaml F: drivers/thermal/qcom/ QUALCOMM VENUS VIDEO ACCELERATOR DRIVER -M: Stanimir Varbanov +M: Stanimir Varbanov M: Vikash Garodia L: linux-media@vger.kernel.org L: linux-arm-msm@vger.kernel.org -- cgit v1.2.3 From b9b1fc1ae1191243d3956888c65a280a9b2c847f Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Sun, 18 Sep 2022 12:50:43 -0400 Subject: gpio: idio-16: Introduce the ACCES IDIO-16 GPIO library module Exposes consumer library functions to facilitate communication with devices within the ACCES IDIO-16 family such as the 104-IDIO-16 and the PCI-IDIO-16. A CONFIG_GPIO_IDIO_16 Kconfig option is introduced by this patch. Modules wanting access to these idio-16 library functions should select this Kconfig option and import the GPIO_IDIO_16 symbol namespace. Cc: Andy Shevchenko Signed-off-by: William Breathitt Gray Signed-off-by: Bartosz Golaszewski --- MAINTAINERS | 7 +++ drivers/gpio/Kconfig | 9 +++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-idio-16.c | 146 ++++++++++++++++++++++++++++++++++++++++++++ drivers/gpio/gpio-idio-16.h | 71 +++++++++++++++++++++ 5 files changed, 234 insertions(+) create mode 100644 drivers/gpio/gpio-idio-16.c create mode 100644 drivers/gpio/gpio-idio-16.h (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..74efa0492c43 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -312,6 +312,13 @@ L: linux-iio@vger.kernel.org S: Maintained F: drivers/counter/104-quad-8.c +ACCES IDIO-16 GPIO LIBRARY +M: William Breathitt Gray +L: linux-gpio@vger.kernel.org +S: Maintained +F: drivers/gpio/gpio-idio-16.c +F: drivers/gpio/gpio-idio-16.h + ACCES PCI-IDIO-16 GPIO DRIVER M: William Breathitt Gray L: linux-gpio@vger.kernel.org diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index e034f752e7ce..3f8cf6e2165e 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -109,6 +109,15 @@ config GPIO_REGMAP config GPIO_MAX730X tristate +config GPIO_IDIO_16 + tristate + help + Enables support for the idio-16 library functions. The idio-16 library + provides functions to facilitate communication with devices within the + ACCES IDIO-16 family such as the 104-IDIO-16 and the PCI-IDIO-16. + + If built as a module its name will be gpio-idio-16. + menu "Memory mapped GPIO drivers" depends on HAS_IOMEM diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 84fae267e8eb..37a0b7ebda43 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o obj-$(CONFIG_GPIO_I8255) += gpio-i8255.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o +obj-$(CONFIG_GPIO_IDIO_16) += gpio-idio-16.o obj-$(CONFIG_GPIO_IDT3243X) += gpio-idt3243x.o obj-$(CONFIG_GPIO_IMX_SCU) += gpio-imx-scu.o obj-$(CONFIG_GPIO_IOP) += gpio-iop.o diff --git a/drivers/gpio/gpio-idio-16.c b/drivers/gpio/gpio-idio-16.c new file mode 100644 index 000000000000..13315242d220 --- /dev/null +++ b/drivers/gpio/gpio-idio-16.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * GPIO library for the ACCES IDIO-16 family + * Copyright (C) 2022 William Breathitt Gray + */ +#include +#include +#include +#include +#include +#include + +#include "gpio-idio-16.h" + +#define DEFAULT_SYMBOL_NAMESPACE GPIO_IDIO_16 + +/** + * idio_16_get - get signal value at signal offset + * @reg: ACCES IDIO-16 device registers + * @state: ACCES IDIO-16 device state + * @offset: offset of signal to get + * + * Returns the signal value (0=low, 1=high) for the signal at @offset. + */ +int idio_16_get(struct idio_16 __iomem *const reg, + struct idio_16_state *const state, const unsigned long offset) +{ + const unsigned long mask = BIT(offset); + + if (offset < IDIO_16_NOUT) + return test_bit(offset, state->out_state); + + if (offset < 24) + return !!(ioread8(®->in0_7) & (mask >> IDIO_16_NOUT)); + + if (offset < 32) + return !!(ioread8(®->in8_15) & (mask >> 24)); + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(idio_16_get); + +/** + * idio_16_get_multiple - get multiple signal values at multiple signal offsets + * @reg: ACCES IDIO-16 device registers + * @state: ACCES IDIO-16 device state + * @mask: mask of signals to get + * @bits: bitmap to store signal values + * + * Stores in @bits the values (0=low, 1=high) for the signals defined by @mask. + */ +void idio_16_get_multiple(struct idio_16 __iomem *const reg, + struct idio_16_state *const state, + const unsigned long *const mask, + unsigned long *const bits) +{ + unsigned long flags; + const unsigned long out_mask = GENMASK(IDIO_16_NOUT - 1, 0); + + spin_lock_irqsave(&state->lock, flags); + + bitmap_replace(bits, bits, state->out_state, &out_mask, IDIO_16_NOUT); + if (*mask & GENMASK(23, 16)) + bitmap_set_value8(bits, ioread8(®->in0_7), 16); + if (*mask & GENMASK(31, 24)) + bitmap_set_value8(bits, ioread8(®->in8_15), 24); + + spin_unlock_irqrestore(&state->lock, flags); +} +EXPORT_SYMBOL_GPL(idio_16_get_multiple); + +/** + * idio_16_set - set signal value at signal offset + * @reg: ACCES IDIO-16 device registers + * @state: ACCES IDIO-16 device state + * @offset: offset of signal to set + * @value: value of signal to set + * + * Assigns output @value for the signal at @offset. + */ +void idio_16_set(struct idio_16 __iomem *const reg, + struct idio_16_state *const state, const unsigned long offset, + const unsigned long value) +{ + unsigned long flags; + + if (offset >= IDIO_16_NOUT) + return; + + spin_lock_irqsave(&state->lock, flags); + + __assign_bit(offset, state->out_state, value); + if (offset < 8) + iowrite8(bitmap_get_value8(state->out_state, 0), ®->out0_7); + else + iowrite8(bitmap_get_value8(state->out_state, 8), ®->out8_15); + + spin_unlock_irqrestore(&state->lock, flags); +} +EXPORT_SYMBOL_GPL(idio_16_set); + +/** + * idio_16_set_multiple - set signal values at multiple signal offsets + * @reg: ACCES IDIO-16 device registers + * @state: ACCES IDIO-16 device state + * @mask: mask of signals to set + * @bits: bitmap of signal output values + * + * Assigns output values defined by @bits for the signals defined by @mask. + */ +void idio_16_set_multiple(struct idio_16 __iomem *const reg, + struct idio_16_state *const state, + const unsigned long *const mask, + const unsigned long *const bits) +{ + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + + bitmap_replace(state->out_state, state->out_state, bits, mask, + IDIO_16_NOUT); + if (*mask & GENMASK(7, 0)) + iowrite8(bitmap_get_value8(state->out_state, 0), ®->out0_7); + if (*mask & GENMASK(15, 8)) + iowrite8(bitmap_get_value8(state->out_state, 8), ®->out8_15); + + spin_unlock_irqrestore(&state->lock, flags); +} +EXPORT_SYMBOL_GPL(idio_16_set_multiple); + +/** + * idio_16_state_init - initialize idio_16_state structure + * @state: ACCES IDIO-16 device state + * + * Initializes the ACCES IDIO-16 device @state for use in idio-16 library + * functions. + */ +void idio_16_state_init(struct idio_16_state *const state) +{ + spin_lock_init(&state->lock); +} +EXPORT_SYMBOL_GPL(idio_16_state_init); + +MODULE_AUTHOR("William Breathitt Gray"); +MODULE_DESCRIPTION("ACCES IDIO-16 GPIO Library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-idio-16.h b/drivers/gpio/gpio-idio-16.h new file mode 100644 index 000000000000..928f8251a2bd --- /dev/null +++ b/drivers/gpio/gpio-idio-16.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright 2022 William Breathitt Gray */ +#ifndef _IDIO_16_H_ +#define _IDIO_16_H_ + +#include +#include + +/** + * struct idio_16 - IDIO-16 registers structure + * @out0_7: Read: FET Drive Outputs 0-7 + * Write: FET Drive Outputs 0-7 + * @in0_7: Read: Isolated Inputs 0-7 + * Write: Clear Interrupt + * @irq_ctl: Read: Enable IRQ + * Write: Disable IRQ + * @filter_ctl: Read: Activate Input Filters 0-15 + * Write: Deactivate Input Filters 0-15 + * @out8_15: Read: FET Drive Outputs 8-15 + * Write: FET Drive Outputs 8-15 + * @in8_15: Read: Isolated Inputs 8-15 + * Write: Unused + * @irq_status: Read: Interrupt status + * Write: Unused + */ +struct idio_16 { + u8 out0_7; + u8 in0_7; + u8 irq_ctl; + u8 filter_ctl; + u8 out8_15; + u8 in8_15; + u8 irq_status; +}; + +#define IDIO_16_NOUT 16 + +/** + * struct idio_16_state - IDIO-16 state structure + * @lock: synchronization lock for accessing device state + * @out_state: output signals state + */ +struct idio_16_state { + spinlock_t lock; + DECLARE_BITMAP(out_state, IDIO_16_NOUT); +}; + +/** + * idio_16_get_direction - get the I/O direction for a signal offset + * @offset: offset of signal to get direction + * + * Returns the signal direction (0=output, 1=input) for the signal at @offset. + */ +static inline int idio_16_get_direction(const unsigned long offset) +{ + return (offset >= IDIO_16_NOUT) ? 1 : 0; +} + +int idio_16_get(struct idio_16 __iomem *reg, struct idio_16_state *state, + unsigned long offset); +void idio_16_get_multiple(struct idio_16 __iomem *reg, + struct idio_16_state *state, + const unsigned long *mask, unsigned long *bits); +void idio_16_set(struct idio_16 __iomem *reg, struct idio_16_state *state, + unsigned long offset, unsigned long value); +void idio_16_set_multiple(struct idio_16 __iomem *reg, + struct idio_16_state *state, + const unsigned long *mask, const unsigned long *bits); +void idio_16_state_init(struct idio_16_state *state); + +#endif /* _IDIO_16_H_ */ -- cgit v1.2.3 From ab7720a2b1175b21cace83757a9fd70408156bae Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 25 Oct 2022 11:30:28 +0200 Subject: maintainers: Add ISOFS entry We miss ISOFS entry in MAINTAINERS file. Add it and write me as the maintainer there since ISOFS is pretty low effort these days. Less random patches for Andrew to merge ;-). Signed-off-by: Jan Kara --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index e04d944005ba..20667c64dad3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10893,6 +10893,13 @@ F: drivers/isdn/Makefile F: drivers/isdn/hardware/ F: drivers/isdn/mISDN/ +ISOFS FILESYSTEM +M: Jan Kara +L: linux-fsdevel@vger.kernel.org +S: Maintained +F: Documentation/filesystems/isofs.rst +F: fs/isofs/ + IT87 HARDWARE MONITORING DRIVER M: Jean Delvare L: linux-hwmon@vger.kernel.org -- cgit v1.2.3 From 38fc5136ac16fe395577996d860ee55abb963922 Mon Sep 17 00:00:00 2001 From: Shawn Tu Date: Tue, 25 Oct 2022 10:27:38 +0800 Subject: media: i2c: Add ov08x40 image sensor driver Add a V4L2 sub-device driver for Omnivision ov08X40 image sensor. This is a camera sensor using the I2C bus for control and the CSI-2 bus for data. This driver supports following features: - manual exposure and analog/digital gain control support - vblank/hblank control support - test pattern support - media controller support - runtime PM support - support following resolutions: + 3856x2464 at 30FPS + 1928x1208 at 30FPS Signed-off-by: Jason Chen Signed-off-by: Shawn Tu Signed-off-by: Sakari Ailus --- MAINTAINERS | 7 + drivers/media/i2c/Kconfig | 13 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov08x40.c | 3327 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 3348 insertions(+) create mode 100644 drivers/media/i2c/ov08x40.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index e04d944005ba..f3ac270d9fe8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15176,6 +15176,13 @@ S: Maintained T: git git://linuxtv.org/media_tree.git F: drivers/media/i2c/ov08d10.c +OMNIVISION OV08X40 SENSOR DRIVER +M: Jason Chen +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: drivers/media/i2c/ov08x40.c + OMNIVISION OV13858 SENSOR DRIVER M: Sakari Ailus L: linux-media@vger.kernel.org diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 7806d4b81716..8d749c01df70 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -364,6 +364,19 @@ config VIDEO_OV08D10 To compile this driver as a module, choose M here: the module will be called ov08d10. +config VIDEO_OV08X40 + tristate "OmniVision OV08X40 sensor support" + depends on VIDEO_V4L2 && I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV08X40 camera. + + To compile this driver as a module, choose M here: the + module will be called ov08x40. + config VIDEO_OV13858 tristate "OmniVision OV13858 sensor support" depends on I2C && VIDEO_DEV diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 0a2933103dd9..83be451ef326 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o obj-$(CONFIG_VIDEO_OV08D10) += ov08d10.o +obj-$(CONFIG_VIDEO_OV08X40) += ov08x40.o obj-$(CONFIG_VIDEO_OV13858) += ov13858.o obj-$(CONFIG_VIDEO_OV13B10) += ov13b10.o obj-$(CONFIG_VIDEO_OV2640) += ov2640.o diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c new file mode 100644 index 000000000000..b4ade17a83f5 --- /dev/null +++ b/drivers/media/i2c/ov08x40.c @@ -0,0 +1,3327 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include + +#define OV08X40_REG_VALUE_08BIT 1 +#define OV08X40_REG_VALUE_16BIT 2 +#define OV08X40_REG_VALUE_24BIT 3 + +#define OV08X40_REG_MODE_SELECT 0x0100 +#define OV08X40_MODE_STANDBY 0x00 +#define OV08X40_MODE_STREAMING 0x01 + +#define OV08X40_REG_AO_STANDBY 0x1000 +#define OV08X40_AO_STREAMING 0x04 + +#define OV08X40_REG_MS_SELECT 0x1001 +#define OV08X40_MS_STANDBY 0x00 +#define OV08X40_MS_STREAMING 0x04 + +#define OV08X40_REG_SOFTWARE_RST 0x0103 +#define OV08X40_SOFTWARE_RST 0x01 + +/* Chip ID */ +#define OV08X40_REG_CHIP_ID 0x300a +#define OV08X40_CHIP_ID 0x560858 + +/* V_TIMING internal */ +#define OV08X40_REG_VTS 0x380e +#define OV08X40_VTS_30FPS 0x1388 +#define OV08X40_VTS_BIN_30FPS 0x115c +#define OV08X40_VTS_MAX 0x7fff + +/* H TIMING internal */ +#define OV08X40_REG_HTS 0x380c +#define OV08X40_HTS_30FPS 0x0280 + +/* Exposure control */ +#define OV08X40_REG_EXPOSURE 0x3500 +#define OV08X40_EXPOSURE_MAX_MARGIN 31 +#define OV08X40_EXPOSURE_MIN 1 +#define OV08X40_EXPOSURE_STEP 1 +#define OV08X40_EXPOSURE_DEFAULT 0x40 + +/* Short Exposure control */ +#define OV08X40_REG_SHORT_EXPOSURE 0x3540 + +/* Analog gain control */ +#define OV08X40_REG_ANALOG_GAIN 0x3508 +#define OV08X40_ANA_GAIN_MIN 0x80 +#define OV08X40_ANA_GAIN_MAX 0x07c0 +#define OV08X40_ANA_GAIN_STEP 1 +#define OV08X40_ANA_GAIN_DEFAULT 0x80 + +/* Digital gain control */ +#define OV08X40_REG_DGTL_GAIN_H 0x350a +#define OV08X40_REG_DGTL_GAIN_M 0x350b +#define OV08X40_REG_DGTL_GAIN_L 0x350c + +#define OV08X40_DGTL_GAIN_MIN 1024 /* Min = 1 X */ +#define OV08X40_DGTL_GAIN_MAX (4096 - 1) /* Max = 4 X */ +#define OV08X40_DGTL_GAIN_DEFAULT 2560 /* Default gain = 2.5 X */ +#define OV08X40_DGTL_GAIN_STEP 1 /* Each step = 1/1024 */ + +#define OV08X40_DGTL_GAIN_L_SHIFT 6 +#define OV08X40_DGTL_GAIN_L_MASK 0x3 +#define OV08X40_DGTL_GAIN_M_SHIFT 2 +#define OV08X40_DGTL_GAIN_M_MASK 0xff +#define OV08X40_DGTL_GAIN_H_SHIFT 10 +#define OV08X40_DGTL_GAIN_H_MASK 0x1F + +/* Test Pattern Control */ +#define OV08X40_REG_TEST_PATTERN 0x50C1 +#define OV08X40_REG_ISP 0x5000 +#define OV08X40_REG_SHORT_TEST_PATTERN 0x53C1 +#define OV08X40_TEST_PATTERN_ENABLE BIT(0) +#define OV08X40_TEST_PATTERN_MASK 0xcf +#define OV08X40_TEST_PATTERN_BAR_SHIFT 4 + +/* Flip Control */ +#define OV08X40_REG_VFLIP 0x3820 +#define OV08X40_REG_MIRROR 0x3821 + +/* Horizontal Window Offset */ +#define OV08X40_REG_H_WIN_OFFSET 0x3811 + +/* Vertical Window Offset */ +#define OV08X40_REG_V_WIN_OFFSET 0x3813 + +enum { + OV08X40_LINK_FREQ_400MHZ_INDEX, +}; + +struct ov08x40_reg { + u16 address; + u8 val; +}; + +struct ov08x40_reg_list { + u32 num_of_regs; + const struct ov08x40_reg *regs; +}; + +/* Link frequency config */ +struct ov08x40_link_freq_config { + u32 pixels_per_line; + + /* registers for this link frequency */ + struct ov08x40_reg_list reg_list; +}; + +/* Mode : resolution and related config&values */ +struct ov08x40_mode { + /* Frame width */ + u32 width; + /* Frame height */ + u32 height; + + u32 lanes; + /* V-timing */ + u32 vts_def; + u32 vts_min; + + /* Index of Link frequency config to be used */ + u32 link_freq_index; + /* Default register values */ + struct ov08x40_reg_list reg_list; +}; + +static const struct ov08x40_reg mipi_data_rate_800mbps[] = { + {0x0103, 0x01}, + {0x1000, 0x00}, + {0x1601, 0xd0}, + {0x1001, 0x04}, + {0x5004, 0x53}, + {0x5110, 0x00}, + {0x5111, 0x14}, + {0x5112, 0x01}, + {0x5113, 0x7b}, + {0x5114, 0x00}, + {0x5152, 0xa3}, + {0x5a52, 0x1f}, + {0x5a1a, 0x0e}, + {0x5a1b, 0x10}, + {0x5a1f, 0x0e}, + {0x5a27, 0x0e}, + {0x6002, 0x2e}, +}; + +static const struct ov08x40_reg mode_3856x2416_regs[] = { + {0x5000, 0x5d}, + {0x5001, 0x20}, + {0x5008, 0xb0}, + {0x50c1, 0x00}, + {0x53c1, 0x00}, + {0x5f40, 0x00}, + {0x5f41, 0x40}, + {0x0300, 0x3a}, + {0x0301, 0xc8}, + {0x0302, 0x31}, + {0x0303, 0x03}, + {0x0304, 0x01}, + {0x0305, 0xa1}, + {0x0306, 0x04}, + {0x0307, 0x01}, + {0x0308, 0x03}, + {0x0309, 0x03}, + {0x0310, 0x0a}, + {0x0311, 0x02}, + {0x0312, 0x01}, + {0x0313, 0x08}, + {0x0314, 0x66}, + {0x0315, 0x00}, + {0x0316, 0x34}, + {0x0320, 0x02}, + {0x0321, 0x03}, + {0x0323, 0x05}, + {0x0324, 0x01}, + {0x0325, 0xb8}, + {0x0326, 0x4a}, + {0x0327, 0x04}, + {0x0329, 0x00}, + {0x032a, 0x05}, + {0x032b, 0x00}, + {0x032c, 0x00}, + {0x032d, 0x00}, + {0x032e, 0x02}, + {0x032f, 0xa0}, + {0x0350, 0x00}, + {0x0360, 0x01}, + {0x1216, 0x60}, + {0x1217, 0x5b}, + {0x1218, 0x00}, + {0x1220, 0x24}, + {0x198a, 0x00}, + {0x198b, 0x01}, + {0x198e, 0x00}, + {0x198f, 0x01}, + {0x3009, 0x04}, + {0x3012, 0x41}, + {0x3015, 0x00}, + {0x3016, 0xb0}, + {0x3017, 0xf0}, + {0x3018, 0xf0}, + {0x3019, 0xd2}, + {0x301a, 0xb0}, + {0x301c, 0x81}, + {0x301d, 0x02}, + {0x301e, 0x80}, + {0x3022, 0xf0}, + {0x3025, 0x89}, + {0x3030, 0x03}, + {0x3044, 0xc2}, + {0x3050, 0x35}, + {0x3051, 0x60}, + {0x3052, 0x25}, + {0x3053, 0x00}, + {0x3054, 0x00}, + {0x3055, 0x02}, + {0x3056, 0x80}, + {0x3057, 0x80}, + {0x3058, 0x80}, + {0x3059, 0x00}, + {0x3107, 0x86}, + {0x3400, 0x1c}, + {0x3401, 0x80}, + {0x3402, 0x8c}, + {0x3419, 0x13}, + {0x341a, 0x89}, + {0x341b, 0x30}, + {0x3420, 0x00}, + {0x3421, 0x00}, + {0x3422, 0x00}, + {0x3423, 0x00}, + {0x3424, 0x00}, + {0x3425, 0x00}, + {0x3426, 0x00}, + {0x3427, 0x00}, + {0x3428, 0x0f}, + {0x3429, 0x00}, + {0x342a, 0x00}, + {0x342b, 0x00}, + {0x342c, 0x00}, + {0x342d, 0x00}, + {0x342e, 0x00}, + {0x342f, 0x11}, + {0x3430, 0x11}, + {0x3431, 0x10}, + {0x3432, 0x00}, + {0x3433, 0x00}, + {0x3434, 0x00}, + {0x3435, 0x00}, + {0x3436, 0x00}, + {0x3437, 0x00}, + {0x3442, 0x02}, + {0x3443, 0x02}, + {0x3444, 0x07}, + {0x3450, 0x00}, + {0x3451, 0x00}, + {0x3452, 0x18}, + {0x3453, 0x18}, + {0x3454, 0x00}, + {0x3455, 0x80}, + {0x3456, 0x08}, + {0x3500, 0x00}, + {0x3501, 0x02}, + {0x3502, 0x00}, + {0x3504, 0x4c}, + {0x3506, 0x30}, + {0x3507, 0x00}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {0x350a, 0x01}, + {0x350b, 0x00}, + {0x350c, 0x00}, + {0x3540, 0x00}, + {0x3541, 0x01}, + {0x3542, 0x00}, + {0x3544, 0x4c}, + {0x3546, 0x30}, + {0x3547, 0x00}, + {0x3548, 0x01}, + {0x3549, 0x00}, + {0x354a, 0x01}, + {0x354b, 0x00}, + {0x354c, 0x00}, + {0x3688, 0x02}, + {0x368a, 0x2e}, + {0x368e, 0x71}, + {0x3696, 0xd1}, + {0x3699, 0x00}, + {0x369a, 0x00}, + {0x36a4, 0x00}, + {0x36a6, 0x00}, + {0x3711, 0x00}, + {0x3712, 0x51}, + {0x3713, 0x00}, + {0x3714, 0x24}, + {0x3716, 0x00}, + {0x3718, 0x07}, + {0x371a, 0x1c}, + {0x371b, 0x00}, + {0x3720, 0x08}, + {0x3725, 0x32}, + {0x3727, 0x05}, + {0x3760, 0x02}, + {0x3761, 0x17}, + {0x3762, 0x02}, + {0x3763, 0x02}, + {0x3764, 0x02}, + {0x3765, 0x2c}, + {0x3766, 0x04}, + {0x3767, 0x2c}, + {0x3768, 0x02}, + {0x3769, 0x00}, + {0x376b, 0x20}, + {0x376e, 0x03}, + {0x37b0, 0x00}, + {0x37b1, 0xab}, + {0x37b2, 0x01}, + {0x37b3, 0x82}, + {0x37b4, 0x00}, + {0x37b5, 0xe4}, + {0x37b6, 0x01}, + {0x37b7, 0xee}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0f}, + {0x3805, 0x1f}, + {0x3806, 0x09}, + {0x3807, 0x7f}, + {0x3808, 0x0f}, + {0x3809, 0x10}, + {0x380a, 0x09}, + {0x380b, 0x70}, + {0x380c, 0x02}, + {0x380d, 0x80}, + {0x380e, 0x13}, + {0x380f, 0x88}, + {0x3810, 0x00}, + {0x3811, 0x08}, + {0x3812, 0x00}, + {0x3813, 0x07}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3820, 0x00}, + {0x3821, 0x04}, + {0x3822, 0x00}, + {0x3823, 0x04}, + {0x3828, 0x0f}, + {0x382a, 0x80}, + {0x382e, 0x41}, + {0x3837, 0x08}, + {0x383a, 0x81}, + {0x383b, 0x81}, + {0x383c, 0x11}, + {0x383d, 0x11}, + {0x383e, 0x00}, + {0x383f, 0x38}, + {0x3840, 0x00}, + {0x3847, 0x00}, + {0x384a, 0x00}, + {0x384c, 0x02}, + {0x384d, 0x80}, + {0x3856, 0x50}, + {0x3857, 0x30}, + {0x3858, 0x80}, + {0x3859, 0x40}, + {0x3860, 0x00}, + {0x3888, 0x00}, + {0x3889, 0x00}, + {0x388a, 0x00}, + {0x388b, 0x00}, + {0x388c, 0x00}, + {0x388d, 0x00}, + {0x388e, 0x00}, + {0x388f, 0x00}, + {0x3894, 0x00}, + {0x3895, 0x00}, + {0x3c84, 0x00}, + {0x3d85, 0x8b}, + {0x3daa, 0x80}, + {0x3dab, 0x14}, + {0x3dac, 0x80}, + {0x3dad, 0xc8}, + {0x3dae, 0x81}, + {0x3daf, 0x7b}, + {0x3f00, 0x10}, + {0x3f01, 0x11}, + {0x3f06, 0x0d}, + {0x3f07, 0x0b}, + {0x3f08, 0x0d}, + {0x3f09, 0x0b}, + {0x3f0a, 0x01}, + {0x3f0b, 0x11}, + {0x3f0c, 0x33}, + {0x4001, 0x07}, + {0x4007, 0x20}, + {0x4008, 0x00}, + {0x4009, 0x05}, + {0x400a, 0x00}, + {0x400b, 0x08}, + {0x400c, 0x00}, + {0x400d, 0x08}, + {0x400e, 0x14}, + {0x4010, 0xf4}, + {0x4011, 0x03}, + {0x4012, 0x55}, + {0x4015, 0x00}, + {0x4016, 0x2d}, + {0x4017, 0x00}, + {0x4018, 0x0f}, + {0x401b, 0x08}, + {0x401c, 0x00}, + {0x401d, 0x10}, + {0x401e, 0x02}, + {0x401f, 0x00}, + {0x4050, 0x06}, + {0x4051, 0xff}, + {0x4052, 0xff}, + {0x4053, 0xff}, + {0x4054, 0xff}, + {0x4055, 0xff}, + {0x4056, 0xff}, + {0x4057, 0x7f}, + {0x4058, 0x00}, + {0x4059, 0x00}, + {0x405a, 0x00}, + {0x405b, 0x00}, + {0x405c, 0x07}, + {0x405d, 0xff}, + {0x405e, 0x07}, + {0x405f, 0xff}, + {0x4080, 0x78}, + {0x4081, 0x78}, + {0x4082, 0x78}, + {0x4083, 0x78}, + {0x4019, 0x00}, + {0x401a, 0x40}, + {0x4020, 0x04}, + {0x4021, 0x00}, + {0x4022, 0x04}, + {0x4023, 0x00}, + {0x4024, 0x04}, + {0x4025, 0x00}, + {0x4026, 0x04}, + {0x4027, 0x00}, + {0x4030, 0x00}, + {0x4031, 0x00}, + {0x4032, 0x00}, + {0x4033, 0x00}, + {0x4034, 0x00}, + {0x4035, 0x00}, + {0x4036, 0x00}, + {0x4037, 0x00}, + {0x4040, 0x00}, + {0x4041, 0x80}, + {0x4042, 0x00}, + {0x4043, 0x80}, + {0x4044, 0x00}, + {0x4045, 0x80}, + {0x4046, 0x00}, + {0x4047, 0x80}, + {0x4060, 0x00}, + {0x4061, 0x00}, + {0x4062, 0x00}, + {0x4063, 0x00}, + {0x4064, 0x00}, + {0x4065, 0x00}, + {0x4066, 0x00}, + {0x4067, 0x00}, + {0x4068, 0x00}, + {0x4069, 0x00}, + {0x406a, 0x00}, + {0x406b, 0x00}, + {0x406c, 0x00}, + {0x406d, 0x00}, + {0x406e, 0x00}, + {0x406f, 0x00}, + {0x4070, 0x00}, + {0x4071, 0x00}, + {0x4072, 0x00}, + {0x4073, 0x00}, + {0x4074, 0x00}, + {0x4075, 0x00}, + {0x4076, 0x00}, + {0x4077, 0x00}, + {0x4078, 0x00}, + {0x4079, 0x00}, + {0x407a, 0x00}, + {0x407b, 0x00}, + {0x407c, 0x00}, + {0x407d, 0x00}, + {0x407e, 0x00}, + {0x407f, 0x00}, + {0x40e0, 0x00}, + {0x40e1, 0x00}, + {0x40e2, 0x00}, + {0x40e3, 0x00}, + {0x40e4, 0x00}, + {0x40e5, 0x00}, + {0x40e6, 0x00}, + {0x40e7, 0x00}, + {0x40e8, 0x00}, + {0x40e9, 0x80}, + {0x40ea, 0x00}, + {0x40eb, 0x80}, + {0x40ec, 0x00}, + {0x40ed, 0x80}, + {0x40ee, 0x00}, + {0x40ef, 0x80}, + {0x40f0, 0x02}, + {0x40f1, 0x04}, + {0x4300, 0x00}, + {0x4301, 0x00}, + {0x4302, 0x00}, + {0x4303, 0x00}, + {0x4304, 0x00}, + {0x4305, 0x00}, + {0x4306, 0x00}, + {0x4307, 0x00}, + {0x4308, 0x00}, + {0x4309, 0x00}, + {0x430a, 0x00}, + {0x430b, 0xff}, + {0x430c, 0xff}, + {0x430d, 0x00}, + {0x430e, 0x00}, + {0x4315, 0x00}, + {0x4316, 0x00}, + {0x4317, 0x00}, + {0x4318, 0x00}, + {0x4319, 0x00}, + {0x431a, 0x00}, + {0x431b, 0x00}, + {0x431c, 0x00}, + {0x4500, 0x07}, + {0x4501, 0x00}, + {0x4502, 0x00}, + {0x4503, 0x0f}, + {0x4504, 0x80}, + {0x4506, 0x01}, + {0x4509, 0x05}, + {0x450c, 0x00}, + {0x450d, 0x20}, + {0x450e, 0x00}, + {0x450f, 0x00}, + {0x4510, 0x00}, + {0x4523, 0x00}, + {0x4526, 0x00}, + {0x4542, 0x00}, + {0x4543, 0x00}, + {0x4544, 0x00}, + {0x4545, 0x00}, + {0x4546, 0x00}, + {0x4547, 0x10}, + {0x4602, 0x00}, + {0x4603, 0x15}, + {0x460b, 0x07}, + {0x4680, 0x11}, + {0x4686, 0x00}, + {0x4687, 0x00}, + {0x4700, 0x00}, + {0x4800, 0x64}, + {0x4806, 0x40}, + {0x480b, 0x10}, + {0x480c, 0x80}, + {0x480f, 0x32}, + {0x4813, 0xe4}, + {0x4837, 0x14}, + {0x4850, 0x42}, + {0x4884, 0x04}, + {0x4c00, 0xf8}, + {0x4c01, 0x44}, + {0x4c03, 0x00}, + {0x4d00, 0x00}, + {0x4d01, 0x16}, + {0x4d04, 0x10}, + {0x4d05, 0x00}, + {0x4d06, 0x0c}, + {0x4d07, 0x00}, + {0x3d84, 0x04}, + {0x3680, 0xa4}, + {0x3682, 0x80}, + {0x3601, 0x40}, + {0x3602, 0x90}, + {0x3608, 0x0a}, + {0x3938, 0x09}, + {0x3a74, 0x84}, + {0x3a99, 0x84}, + {0x3ab9, 0xa6}, + {0x3aba, 0xba}, + {0x3b12, 0x84}, + {0x3b14, 0xbb}, + {0x3b15, 0xbf}, + {0x3a29, 0x26}, + {0x3a1f, 0x8a}, + {0x3a22, 0x91}, + {0x3a25, 0x96}, + {0x3a28, 0xb4}, + {0x3a2b, 0xba}, + {0x3a2e, 0xbf}, + {0x3a31, 0xc1}, + {0x3a20, 0x00}, + {0x3939, 0x9d}, + {0x3902, 0x0e}, + {0x3903, 0x0e}, + {0x3904, 0x0e}, + {0x3905, 0x0e}, + {0x3906, 0x07}, + {0x3907, 0x0d}, + {0x3908, 0x11}, + {0x3909, 0x12}, + {0x360f, 0x99}, + {0x390c, 0x33}, + {0x390d, 0x66}, + {0x390e, 0xaa}, + {0x3911, 0x90}, + {0x3913, 0x90}, + {0x3915, 0x90}, + {0x3917, 0x90}, + {0x3b3f, 0x9d}, + {0x3b45, 0x9d}, + {0x3b1b, 0xc9}, + {0x3b21, 0xc9}, + {0x3440, 0xa4}, + {0x3a23, 0x15}, + {0x3a26, 0x1d}, + {0x3a2c, 0x4a}, + {0x3a2f, 0x18}, + {0x3a32, 0x55}, + {0x3b0a, 0x01}, + {0x3b0b, 0x00}, + {0x3b0e, 0x01}, + {0x3b0f, 0x00}, + {0x392c, 0x02}, + {0x392d, 0x02}, + {0x392e, 0x04}, + {0x392f, 0x03}, + {0x3930, 0x08}, + {0x3931, 0x07}, + {0x3932, 0x10}, + {0x3933, 0x0c}, + {0x3609, 0x08}, + {0x3921, 0x0f}, + {0x3928, 0x15}, + {0x3929, 0x2a}, + {0x392a, 0x54}, + {0x392b, 0xa8}, + {0x3426, 0x10}, + {0x3407, 0x01}, + {0x3404, 0x01}, + {0x3500, 0x00}, + {0x3501, 0x10}, + {0x3502, 0x10}, + {0x3508, 0x0f}, + {0x3509, 0x80}, + {0x5a80, 0x75}, + {0x5a81, 0x75}, + {0x5a82, 0x75}, + {0x5a83, 0x75}, + {0x5a84, 0x75}, + {0x5a85, 0x75}, + {0x5a86, 0x75}, + {0x5a87, 0x75}, + {0x5a88, 0x75}, + {0x5a89, 0x75}, + {0x5a8a, 0x75}, + {0x5a8b, 0x75}, + {0x5a8c, 0x75}, + {0x5a8d, 0x75}, + {0x5a8e, 0x75}, + {0x5a8f, 0x75}, + {0x5a90, 0x75}, + {0x5a91, 0x75}, + {0x5a92, 0x75}, + {0x5a93, 0x75}, + {0x5a94, 0x75}, + {0x5a95, 0x75}, + {0x5a96, 0x75}, + {0x5a97, 0x75}, + {0x5a98, 0x75}, + {0x5a99, 0x75}, + {0x5a9a, 0x75}, + {0x5a9b, 0x75}, + {0x5a9c, 0x75}, + {0x5a9d, 0x75}, + {0x5a9e, 0x75}, + {0x5a9f, 0x75}, + {0x5aa0, 0x75}, + {0x5aa1, 0x75}, + {0x5aa2, 0x75}, + {0x5aa3, 0x75}, + {0x5aa4, 0x75}, + {0x5aa5, 0x75}, + {0x5aa6, 0x75}, + {0x5aa7, 0x75}, + {0x5aa8, 0x75}, + {0x5aa9, 0x75}, + {0x5aaa, 0x75}, + {0x5aab, 0x75}, + {0x5aac, 0x75}, + {0x5aad, 0x75}, + {0x5aae, 0x75}, + {0x5aaf, 0x75}, + {0x5ab0, 0x75}, + {0x5ab1, 0x75}, + {0x5ab2, 0x75}, + {0x5ab3, 0x75}, + {0x5ab4, 0x75}, + {0x5ab5, 0x75}, + {0x5ab6, 0x75}, + {0x5ab7, 0x75}, + {0x5ab8, 0x75}, + {0x5ab9, 0x75}, + {0x5aba, 0x75}, + {0x5abb, 0x75}, + {0x5abc, 0x75}, + {0x5abd, 0x75}, + {0x5abe, 0x75}, + {0x5abf, 0x75}, + {0x5ac0, 0x75}, + {0x5ac1, 0x75}, + {0x5ac2, 0x75}, + {0x5ac3, 0x75}, + {0x5ac4, 0x75}, + {0x5ac5, 0x75}, + {0x5ac6, 0x75}, + {0x5ac7, 0x75}, + {0x5ac8, 0x75}, + {0x5ac9, 0x75}, + {0x5aca, 0x75}, + {0x5acb, 0x75}, + {0x5acc, 0x75}, + {0x5acd, 0x75}, + {0x5ace, 0x75}, + {0x5acf, 0x75}, + {0x5ad0, 0x75}, + {0x5ad1, 0x75}, + {0x5ad2, 0x75}, + {0x5ad3, 0x75}, + {0x5ad4, 0x75}, + {0x5ad5, 0x75}, + {0x5ad6, 0x75}, + {0x5ad7, 0x75}, + {0x5ad8, 0x75}, + {0x5ad9, 0x75}, + {0x5ada, 0x75}, + {0x5adb, 0x75}, + {0x5adc, 0x75}, + {0x5add, 0x75}, + {0x5ade, 0x75}, + {0x5adf, 0x75}, + {0x5ae0, 0x75}, + {0x5ae1, 0x75}, + {0x5ae2, 0x75}, + {0x5ae3, 0x75}, + {0x5ae4, 0x75}, + {0x5ae5, 0x75}, + {0x5ae6, 0x75}, + {0x5ae7, 0x75}, + {0x5ae8, 0x75}, + {0x5ae9, 0x75}, + {0x5aea, 0x75}, + {0x5aeb, 0x75}, + {0x5aec, 0x75}, + {0x5aed, 0x75}, + {0x5aee, 0x75}, + {0x5aef, 0x75}, + {0x5af0, 0x75}, + {0x5af1, 0x75}, + {0x5af2, 0x75}, + {0x5af3, 0x75}, + {0x5af4, 0x75}, + {0x5af5, 0x75}, + {0x5af6, 0x75}, + {0x5af7, 0x75}, + {0x5af8, 0x75}, + {0x5af9, 0x75}, + {0x5afa, 0x75}, + {0x5afb, 0x75}, + {0x5afc, 0x75}, + {0x5afd, 0x75}, + {0x5afe, 0x75}, + {0x5aff, 0x75}, + {0x5b00, 0x75}, + {0x5b01, 0x75}, + {0x5b02, 0x75}, + {0x5b03, 0x75}, + {0x5b04, 0x75}, + {0x5b05, 0x75}, + {0x5b06, 0x75}, + {0x5b07, 0x75}, + {0x5b08, 0x75}, + {0x5b09, 0x75}, + {0x5b0a, 0x75}, + {0x5b0b, 0x75}, + {0x5b0c, 0x75}, + {0x5b0d, 0x75}, + {0x5b0e, 0x75}, + {0x5b0f, 0x75}, + {0x5b10, 0x75}, + {0x5b11, 0x75}, + {0x5b12, 0x75}, + {0x5b13, 0x75}, + {0x5b14, 0x75}, + {0x5b15, 0x75}, + {0x5b16, 0x75}, + {0x5b17, 0x75}, + {0x5b18, 0x75}, + {0x5b19, 0x75}, + {0x5b1a, 0x75}, + {0x5b1b, 0x75}, + {0x5b1c, 0x75}, + {0x5b1d, 0x75}, + {0x5b1e, 0x75}, + {0x5b1f, 0x75}, + {0x5b20, 0x75}, + {0x5b21, 0x75}, + {0x5b22, 0x75}, + {0x5b23, 0x75}, + {0x5b24, 0x75}, + {0x5b25, 0x75}, + {0x5b26, 0x75}, + {0x5b27, 0x75}, + {0x5b28, 0x75}, + {0x5b29, 0x75}, + {0x5b2a, 0x75}, + {0x5b2b, 0x75}, + {0x5b2c, 0x75}, + {0x5b2d, 0x75}, + {0x5b2e, 0x75}, + {0x5b2f, 0x75}, + {0x5b30, 0x75}, + {0x5b31, 0x75}, + {0x5b32, 0x75}, + {0x5b33, 0x75}, + {0x5b34, 0x75}, + {0x5b35, 0x75}, + {0x5b36, 0x75}, + {0x5b37, 0x75}, + {0x5b38, 0x75}, + {0x5b39, 0x75}, + {0x5b3a, 0x75}, + {0x5b3b, 0x75}, + {0x5b3c, 0x75}, + {0x5b3d, 0x75}, + {0x5b3e, 0x75}, + {0x5b3f, 0x75}, + {0x5b40, 0x75}, + {0x5b41, 0x75}, + {0x5b42, 0x75}, + {0x5b43, 0x75}, + {0x5b44, 0x75}, + {0x5b45, 0x75}, + {0x5b46, 0x75}, + {0x5b47, 0x75}, + {0x5b48, 0x75}, + {0x5b49, 0x75}, + {0x5b4a, 0x75}, + {0x5b4b, 0x75}, + {0x5b4c, 0x75}, + {0x5b4d, 0x75}, + {0x5b4e, 0x75}, + {0x5b4f, 0x75}, + {0x5b50, 0x75}, + {0x5b51, 0x75}, + {0x5b52, 0x75}, + {0x5b53, 0x75}, + {0x5b54, 0x75}, + {0x5b55, 0x75}, + {0x5b56, 0x75}, + {0x5b57, 0x75}, + {0x5b58, 0x75}, + {0x5b59, 0x75}, + {0x5b5a, 0x75}, + {0x5b5b, 0x75}, + {0x5b5c, 0x75}, + {0x5b5d, 0x75}, + {0x5b5e, 0x75}, + {0x5b5f, 0x75}, + {0x5b60, 0x75}, + {0x5b61, 0x75}, + {0x5b62, 0x75}, + {0x5b63, 0x75}, + {0x5b64, 0x75}, + {0x5b65, 0x75}, + {0x5b66, 0x75}, + {0x5b67, 0x75}, + {0x5b68, 0x75}, + {0x5b69, 0x75}, + {0x5b6a, 0x75}, + {0x5b6b, 0x75}, + {0x5b6c, 0x75}, + {0x5b6d, 0x75}, + {0x5b6e, 0x75}, + {0x5b6f, 0x75}, + {0x5b70, 0x75}, + {0x5b71, 0x75}, + {0x5b72, 0x75}, + {0x5b73, 0x75}, + {0x5b74, 0x75}, + {0x5b75, 0x75}, + {0x5b76, 0x75}, + {0x5b77, 0x75}, + {0x5b78, 0x75}, + {0x5b79, 0x75}, + {0x5b7a, 0x75}, + {0x5b7b, 0x75}, + {0x5b7c, 0x75}, + {0x5b7d, 0x75}, + {0x5b7e, 0x75}, + {0x5b7f, 0x75}, + {0x5b80, 0x75}, + {0x5b81, 0x75}, + {0x5b82, 0x75}, + {0x5b83, 0x75}, + {0x5b84, 0x75}, + {0x5b85, 0x75}, + {0x5b86, 0x75}, + {0x5b87, 0x75}, + {0x5b88, 0x75}, + {0x5b89, 0x75}, + {0x5b8a, 0x75}, + {0x5b8b, 0x75}, + {0x5b8c, 0x75}, + {0x5b8d, 0x75}, + {0x5b8e, 0x75}, + {0x5b8f, 0x75}, + {0x5b90, 0x75}, + {0x5b91, 0x75}, + {0x5b92, 0x75}, + {0x5b93, 0x75}, + {0x5b94, 0x75}, + {0x5b95, 0x75}, + {0x5b96, 0x75}, + {0x5b97, 0x75}, + {0x5b98, 0x75}, + {0x5b99, 0x75}, + {0x5b9a, 0x75}, + {0x5b9b, 0x75}, + {0x5b9c, 0x75}, + {0x5b9d, 0x75}, + {0x5b9e, 0x75}, + {0x5b9f, 0x75}, + {0x5bc0, 0x75}, + {0x5bc1, 0x75}, + {0x5bc2, 0x75}, + {0x5bc3, 0x75}, + {0x5bc4, 0x75}, + {0x5bc5, 0x75}, + {0x5bc6, 0x75}, + {0x5bc7, 0x75}, + {0x5bc8, 0x75}, + {0x5bc9, 0x75}, + {0x5bca, 0x75}, + {0x5bcb, 0x75}, + {0x5bcc, 0x75}, + {0x5bcd, 0x75}, + {0x5bce, 0x75}, + {0x5bcf, 0x75}, + {0x5bd0, 0x75}, + {0x5bd1, 0x75}, + {0x5bd2, 0x75}, + {0x5bd3, 0x75}, + {0x5bd4, 0x75}, + {0x5bd5, 0x75}, + {0x5bd6, 0x75}, + {0x5bd7, 0x75}, + {0x5bd8, 0x75}, + {0x5bd9, 0x75}, + {0x5bda, 0x75}, + {0x5bdb, 0x75}, + {0x5bdc, 0x75}, + {0x5bdd, 0x75}, + {0x5bde, 0x75}, + {0x5bdf, 0x75}, + {0x5be0, 0x75}, + {0x5be1, 0x75}, + {0x5be2, 0x75}, + {0x5be3, 0x75}, + {0x5be4, 0x75}, + {0x5be5, 0x75}, + {0x5be6, 0x75}, + {0x5be7, 0x75}, + {0x5be8, 0x75}, + {0x5be9, 0x75}, + {0x5bea, 0x75}, + {0x5beb, 0x75}, + {0x5bec, 0x75}, + {0x5bed, 0x75}, + {0x5bee, 0x75}, + {0x5bef, 0x75}, + {0x5bf0, 0x75}, + {0x5bf1, 0x75}, + {0x5bf2, 0x75}, + {0x5bf3, 0x75}, + {0x5bf4, 0x75}, + {0x5bf5, 0x75}, + {0x5bf6, 0x75}, + {0x5bf7, 0x75}, + {0x5bf8, 0x75}, + {0x5bf9, 0x75}, + {0x5bfa, 0x75}, + {0x5bfb, 0x75}, + {0x5bfc, 0x75}, + {0x5bfd, 0x75}, + {0x5bfe, 0x75}, + {0x5bff, 0x75}, + {0x5c00, 0x75}, + {0x5c01, 0x75}, + {0x5c02, 0x75}, + {0x5c03, 0x75}, + {0x5c04, 0x75}, + {0x5c05, 0x75}, + {0x5c06, 0x75}, + {0x5c07, 0x75}, + {0x5c08, 0x75}, + {0x5c09, 0x75}, + {0x5c0a, 0x75}, + {0x5c0b, 0x75}, + {0x5c0c, 0x75}, + {0x5c0d, 0x75}, + {0x5c0e, 0x75}, + {0x5c0f, 0x75}, + {0x5c10, 0x75}, + {0x5c11, 0x75}, + {0x5c12, 0x75}, + {0x5c13, 0x75}, + {0x5c14, 0x75}, + {0x5c15, 0x75}, + {0x5c16, 0x75}, + {0x5c17, 0x75}, + {0x5c18, 0x75}, + {0x5c19, 0x75}, + {0x5c1a, 0x75}, + {0x5c1b, 0x75}, + {0x5c1c, 0x75}, + {0x5c1d, 0x75}, + {0x5c1e, 0x75}, + {0x5c1f, 0x75}, + {0x5c20, 0x75}, + {0x5c21, 0x75}, + {0x5c22, 0x75}, + {0x5c23, 0x75}, + {0x5c24, 0x75}, + {0x5c25, 0x75}, + {0x5c26, 0x75}, + {0x5c27, 0x75}, + {0x5c28, 0x75}, + {0x5c29, 0x75}, + {0x5c2a, 0x75}, + {0x5c2b, 0x75}, + {0x5c2c, 0x75}, + {0x5c2d, 0x75}, + {0x5c2e, 0x75}, + {0x5c2f, 0x75}, + {0x5c30, 0x75}, + {0x5c31, 0x75}, + {0x5c32, 0x75}, + {0x5c33, 0x75}, + {0x5c34, 0x75}, + {0x5c35, 0x75}, + {0x5c36, 0x75}, + {0x5c37, 0x75}, + {0x5c38, 0x75}, + {0x5c39, 0x75}, + {0x5c3a, 0x75}, + {0x5c3b, 0x75}, + {0x5c3c, 0x75}, + {0x5c3d, 0x75}, + {0x5c3e, 0x75}, + {0x5c3f, 0x75}, + {0x5c40, 0x75}, + {0x5c41, 0x75}, + {0x5c42, 0x75}, + {0x5c43, 0x75}, + {0x5c44, 0x75}, + {0x5c45, 0x75}, + {0x5c46, 0x75}, + {0x5c47, 0x75}, + {0x5c48, 0x75}, + {0x5c49, 0x75}, + {0x5c4a, 0x75}, + {0x5c4b, 0x75}, + {0x5c4c, 0x75}, + {0x5c4d, 0x75}, + {0x5c4e, 0x75}, + {0x5c4f, 0x75}, + {0x5c50, 0x75}, + {0x5c51, 0x75}, + {0x5c52, 0x75}, + {0x5c53, 0x75}, + {0x5c54, 0x75}, + {0x5c55, 0x75}, + {0x5c56, 0x75}, + {0x5c57, 0x75}, + {0x5c58, 0x75}, + {0x5c59, 0x75}, + {0x5c5a, 0x75}, + {0x5c5b, 0x75}, + {0x5c5c, 0x75}, + {0x5c5d, 0x75}, + {0x5c5e, 0x75}, + {0x5c5f, 0x75}, + {0x5c60, 0x75}, + {0x5c61, 0x75}, + {0x5c62, 0x75}, + {0x5c63, 0x75}, + {0x5c64, 0x75}, + {0x5c65, 0x75}, + {0x5c66, 0x75}, + {0x5c67, 0x75}, + {0x5c68, 0x75}, + {0x5c69, 0x75}, + {0x5c6a, 0x75}, + {0x5c6b, 0x75}, + {0x5c6c, 0x75}, + {0x5c6d, 0x75}, + {0x5c6e, 0x75}, + {0x5c6f, 0x75}, + {0x5c70, 0x75}, + {0x5c71, 0x75}, + {0x5c72, 0x75}, + {0x5c73, 0x75}, + {0x5c74, 0x75}, + {0x5c75, 0x75}, + {0x5c76, 0x75}, + {0x5c77, 0x75}, + {0x5c78, 0x75}, + {0x5c79, 0x75}, + {0x5c7a, 0x75}, + {0x5c7b, 0x75}, + {0x5c7c, 0x75}, + {0x5c7d, 0x75}, + {0x5c7e, 0x75}, + {0x5c7f, 0x75}, + {0x5c80, 0x75}, + {0x5c81, 0x75}, + {0x5c82, 0x75}, + {0x5c83, 0x75}, + {0x5c84, 0x75}, + {0x5c85, 0x75}, + {0x5c86, 0x75}, + {0x5c87, 0x75}, + {0x5c88, 0x75}, + {0x5c89, 0x75}, + {0x5c8a, 0x75}, + {0x5c8b, 0x75}, + {0x5c8c, 0x75}, + {0x5c8d, 0x75}, + {0x5c8e, 0x75}, + {0x5c8f, 0x75}, + {0x5c90, 0x75}, + {0x5c91, 0x75}, + {0x5c92, 0x75}, + {0x5c93, 0x75}, + {0x5c94, 0x75}, + {0x5c95, 0x75}, + {0x5c96, 0x75}, + {0x5c97, 0x75}, + {0x5c98, 0x75}, + {0x5c99, 0x75}, + {0x5c9a, 0x75}, + {0x5c9b, 0x75}, + {0x5c9c, 0x75}, + {0x5c9d, 0x75}, + {0x5c9e, 0x75}, + {0x5c9f, 0x75}, + {0x5ca0, 0x75}, + {0x5ca1, 0x75}, + {0x5ca2, 0x75}, + {0x5ca3, 0x75}, + {0x5ca4, 0x75}, + {0x5ca5, 0x75}, + {0x5ca6, 0x75}, + {0x5ca7, 0x75}, + {0x5ca8, 0x75}, + {0x5ca9, 0x75}, + {0x5caa, 0x75}, + {0x5cab, 0x75}, + {0x5cac, 0x75}, + {0x5cad, 0x75}, + {0x5cae, 0x75}, + {0x5caf, 0x75}, + {0x5cb0, 0x75}, + {0x5cb1, 0x75}, + {0x5cb2, 0x75}, + {0x5cb3, 0x75}, + {0x5cb4, 0x75}, + {0x5cb5, 0x75}, + {0x5cb6, 0x75}, + {0x5cb7, 0x75}, + {0x5cb8, 0x75}, + {0x5cb9, 0x75}, + {0x5cba, 0x75}, + {0x5cbb, 0x75}, + {0x5cbc, 0x75}, + {0x5cbd, 0x75}, + {0x5cbe, 0x75}, + {0x5cbf, 0x75}, + {0x5cc0, 0x75}, + {0x5cc1, 0x75}, + {0x5cc2, 0x75}, + {0x5cc3, 0x75}, + {0x5cc4, 0x75}, + {0x5cc5, 0x75}, + {0x5cc6, 0x75}, + {0x5cc7, 0x75}, + {0x5cc8, 0x75}, + {0x5cc9, 0x75}, + {0x5cca, 0x75}, + {0x5ccb, 0x75}, + {0x5ccc, 0x75}, + {0x5ccd, 0x75}, + {0x5cce, 0x75}, + {0x5ccf, 0x75}, + {0x5cd0, 0x75}, + {0x5cd1, 0x75}, + {0x5cd2, 0x75}, + {0x5cd3, 0x75}, + {0x5cd4, 0x75}, + {0x5cd5, 0x75}, + {0x5cd6, 0x75}, + {0x5cd7, 0x75}, + {0x5cd8, 0x75}, + {0x5cd9, 0x75}, + {0x5cda, 0x75}, + {0x5cdb, 0x75}, + {0x5cdc, 0x75}, + {0x5cdd, 0x75}, + {0x5cde, 0x75}, + {0x5cdf, 0x75}, + {0x5ce0, 0x75}, + {0x5ce1, 0x75}, + {0x5ce2, 0x75}, + {0x5ce3, 0x75}, + {0x5ce4, 0x75}, + {0x5ce5, 0x75}, + {0x5ce6, 0x75}, + {0x5ce7, 0x75}, + {0x5ce8, 0x75}, + {0x5ce9, 0x75}, + {0x5cea, 0x75}, + {0x5ceb, 0x75}, + {0x5cec, 0x75}, + {0x5ced, 0x75}, + {0x5cee, 0x75}, + {0x5cef, 0x75}, + {0x5cf0, 0x75}, + {0x5cf1, 0x75}, + {0x5cf2, 0x75}, + {0x5cf3, 0x75}, + {0x5cf4, 0x75}, + {0x5cf5, 0x75}, + {0x5cf6, 0x75}, + {0x5cf7, 0x75}, + {0x5cf8, 0x75}, + {0x5cf9, 0x75}, + {0x5cfa, 0x75}, + {0x5cfb, 0x75}, + {0x5cfc, 0x75}, + {0x5cfd, 0x75}, + {0x5cfe, 0x75}, + {0x5cff, 0x75}, + {0x5d00, 0x75}, + {0x5d01, 0x75}, + {0x5d02, 0x75}, + {0x5d03, 0x75}, + {0x5d04, 0x75}, + {0x5d05, 0x75}, + {0x5d06, 0x75}, + {0x5d07, 0x75}, + {0x5d08, 0x75}, + {0x5d09, 0x75}, + {0x5d0a, 0x75}, + {0x5d0b, 0x75}, + {0x5d0c, 0x75}, + {0x5d0d, 0x75}, + {0x5d0e, 0x75}, + {0x5d0f, 0x75}, + {0x5d10, 0x75}, + {0x5d11, 0x75}, + {0x5d12, 0x75}, + {0x5d13, 0x75}, + {0x5d14, 0x75}, + {0x5d15, 0x75}, + {0x5d16, 0x75}, + {0x5d17, 0x75}, + {0x5d18, 0x75}, + {0x5d19, 0x75}, + {0x5d1a, 0x75}, + {0x5d1b, 0x75}, + {0x5d1c, 0x75}, + {0x5d1d, 0x75}, + {0x5d1e, 0x75}, + {0x5d1f, 0x75}, + {0x5d20, 0x75}, + {0x5d21, 0x75}, + {0x5d22, 0x75}, + {0x5d23, 0x75}, + {0x5d24, 0x75}, + {0x5d25, 0x75}, + {0x5d26, 0x75}, + {0x5d27, 0x75}, + {0x5d28, 0x75}, + {0x5d29, 0x75}, + {0x5d2a, 0x75}, + {0x5d2b, 0x75}, + {0x5d2c, 0x75}, + {0x5d2d, 0x75}, + {0x5d2e, 0x75}, + {0x5d2f, 0x75}, + {0x5d30, 0x75}, + {0x5d31, 0x75}, + {0x5d32, 0x75}, + {0x5d33, 0x75}, + {0x5d34, 0x75}, + {0x5d35, 0x75}, + {0x5d36, 0x75}, + {0x5d37, 0x75}, + {0x5d38, 0x75}, + {0x5d39, 0x75}, + {0x5d3a, 0x75}, + {0x5d3b, 0x75}, + {0x5d3c, 0x75}, + {0x5d3d, 0x75}, + {0x5d3e, 0x75}, + {0x5d3f, 0x75}, + {0x5d40, 0x75}, + {0x5d41, 0x75}, + {0x5d42, 0x75}, + {0x5d43, 0x75}, + {0x5d44, 0x75}, + {0x5d45, 0x75}, + {0x5d46, 0x75}, + {0x5d47, 0x75}, + {0x5d48, 0x75}, + {0x5d49, 0x75}, + {0x5d4a, 0x75}, + {0x5d4b, 0x75}, + {0x5d4c, 0x75}, + {0x5d4d, 0x75}, + {0x5d4e, 0x75}, + {0x5d4f, 0x75}, + {0x5d50, 0x75}, + {0x5d51, 0x75}, + {0x5d52, 0x75}, + {0x5d53, 0x75}, + {0x5d54, 0x75}, + {0x5d55, 0x75}, + {0x5d56, 0x75}, + {0x5d57, 0x75}, + {0x5d58, 0x75}, + {0x5d59, 0x75}, + {0x5d5a, 0x75}, + {0x5d5b, 0x75}, + {0x5d5c, 0x75}, + {0x5d5d, 0x75}, + {0x5d5e, 0x75}, + {0x5d5f, 0x75}, + {0x5d60, 0x75}, + {0x5d61, 0x75}, + {0x5d62, 0x75}, + {0x5d63, 0x75}, + {0x5d64, 0x75}, + {0x5d65, 0x75}, + {0x5d66, 0x75}, + {0x5d67, 0x75}, + {0x5d68, 0x75}, + {0x5d69, 0x75}, + {0x5d6a, 0x75}, + {0x5d6b, 0x75}, + {0x5d6c, 0x75}, + {0x5d6d, 0x75}, + {0x5d6e, 0x75}, + {0x5d6f, 0x75}, + {0x5d70, 0x75}, + {0x5d71, 0x75}, + {0x5d72, 0x75}, + {0x5d73, 0x75}, + {0x5d74, 0x75}, + {0x5d75, 0x75}, + {0x5d76, 0x75}, + {0x5d77, 0x75}, + {0x5d78, 0x75}, + {0x5d79, 0x75}, + {0x5d7a, 0x75}, + {0x5d7b, 0x75}, + {0x5d7c, 0x75}, + {0x5d7d, 0x75}, + {0x5d7e, 0x75}, + {0x5d7f, 0x75}, + {0x5d80, 0x75}, + {0x5d81, 0x75}, + {0x5d82, 0x75}, + {0x5d83, 0x75}, + {0x5d84, 0x75}, + {0x5d85, 0x75}, + {0x5d86, 0x75}, + {0x5d87, 0x75}, + {0x5d88, 0x75}, + {0x5d89, 0x75}, + {0x5d8a, 0x75}, + {0x5d8b, 0x75}, + {0x5d8c, 0x75}, + {0x5d8d, 0x75}, + {0x5d8e, 0x75}, + {0x5d8f, 0x75}, + {0x5d90, 0x75}, + {0x5d91, 0x75}, + {0x5d92, 0x75}, + {0x5d93, 0x75}, + {0x5d94, 0x75}, + {0x5d95, 0x75}, + {0x5d96, 0x75}, + {0x5d97, 0x75}, + {0x5d98, 0x75}, + {0x5d99, 0x75}, + {0x5d9a, 0x75}, + {0x5d9b, 0x75}, + {0x5d9c, 0x75}, + {0x5d9d, 0x75}, + {0x5d9e, 0x75}, + {0x5d9f, 0x75}, + {0x5da0, 0x75}, + {0x5da1, 0x75}, + {0x5da2, 0x75}, + {0x5da3, 0x75}, + {0x5da4, 0x75}, + {0x5da5, 0x75}, + {0x5da6, 0x75}, + {0x5da7, 0x75}, + {0x5da8, 0x75}, + {0x5da9, 0x75}, + {0x5daa, 0x75}, + {0x5dab, 0x75}, + {0x5dac, 0x75}, + {0x5dad, 0x75}, + {0x5dae, 0x75}, + {0x5daf, 0x75}, + {0x5db0, 0x75}, + {0x5db1, 0x75}, + {0x5db2, 0x75}, + {0x5db3, 0x75}, + {0x5db4, 0x75}, + {0x5db5, 0x75}, + {0x5db6, 0x75}, + {0x5db7, 0x75}, + {0x5db8, 0x75}, + {0x5db9, 0x75}, + {0x5dba, 0x75}, + {0x5dbb, 0x75}, + {0x5dbc, 0x75}, + {0x5dbd, 0x75}, + {0x5dbe, 0x75}, + {0x5dbf, 0x75}, + {0x5dc0, 0x75}, + {0x5dc1, 0x75}, + {0x5dc2, 0x75}, + {0x5dc3, 0x75}, + {0x5dc4, 0x75}, + {0x5dc5, 0x75}, + {0x5dc6, 0x75}, + {0x5dc7, 0x75}, + {0x5dc8, 0x75}, + {0x5dc9, 0x75}, + {0x5dca, 0x75}, + {0x5dcb, 0x75}, + {0x5dcc, 0x75}, + {0x5dcd, 0x75}, + {0x5dce, 0x75}, + {0x5dcf, 0x75}, + {0x5dd0, 0x75}, + {0x5dd1, 0x75}, + {0x5dd2, 0x75}, + {0x5dd3, 0x75}, + {0x5dd4, 0x75}, + {0x5dd5, 0x75}, + {0x5dd6, 0x75}, + {0x5dd7, 0x75}, + {0x5dd8, 0x75}, + {0x5dd9, 0x75}, + {0x5dda, 0x75}, + {0x5ddb, 0x75}, + {0x5ddc, 0x75}, + {0x5ddd, 0x75}, + {0x5dde, 0x75}, + {0x5ddf, 0x75}, + {0x5de0, 0x75}, + {0x5de1, 0x75}, + {0x5de2, 0x75}, + {0x5de3, 0x75}, + {0x5de4, 0x75}, + {0x5de5, 0x75}, + {0x5de6, 0x75}, + {0x5de7, 0x75}, + {0x5de8, 0x75}, + {0x5de9, 0x75}, + {0x5dea, 0x75}, + {0x5deb, 0x75}, + {0x5dec, 0x75}, + {0x5ded, 0x75}, + {0x5dee, 0x75}, + {0x5def, 0x75}, + {0x5df0, 0x75}, + {0x5df1, 0x75}, + {0x5df2, 0x75}, + {0x5df3, 0x75}, + {0x5df4, 0x75}, + {0x5df5, 0x75}, + {0x5df6, 0x75}, + {0x5df7, 0x75}, + {0x5df8, 0x75}, + {0x5df9, 0x75}, + {0x5dfa, 0x75}, + {0x5dfb, 0x75}, + {0x5dfc, 0x75}, + {0x5dfd, 0x75}, + {0x5dfe, 0x75}, + {0x5dff, 0x75}, + {0x5e00, 0x75}, + {0x5e01, 0x75}, + {0x5e02, 0x75}, + {0x5e03, 0x75}, + {0x5e04, 0x75}, + {0x5e05, 0x75}, + {0x5e06, 0x75}, + {0x5e07, 0x75}, + {0x5e08, 0x75}, + {0x5e09, 0x75}, + {0x5e0a, 0x75}, + {0x5e0b, 0x75}, + {0x5e0c, 0x75}, + {0x5e0d, 0x75}, + {0x5e0e, 0x75}, + {0x5e0f, 0x75}, + {0x5e10, 0x75}, + {0x5e11, 0x75}, + {0x5e12, 0x75}, + {0x5e13, 0x75}, + {0x5e14, 0x75}, + {0x5e15, 0x75}, + {0x5e16, 0x75}, + {0x5e17, 0x75}, + {0x5e18, 0x75}, + {0x5e19, 0x75}, + {0x5e1a, 0x75}, + {0x5e1b, 0x75}, + {0x5e1c, 0x75}, + {0x5e1d, 0x75}, + {0x5e1e, 0x75}, + {0x5e1f, 0x75}, + {0x5e20, 0x75}, + {0x5e21, 0x75}, + {0x5e22, 0x75}, + {0x5e23, 0x75}, + {0x5e24, 0x75}, + {0x5e25, 0x75}, + {0x5e26, 0x75}, + {0x5e27, 0x75}, + {0x5e28, 0x75}, + {0x5e29, 0x75}, + {0x5e2a, 0x75}, + {0x5e2b, 0x75}, + {0x5e2c, 0x75}, + {0x5e2d, 0x75}, + {0x5e2e, 0x75}, + {0x5e2f, 0x75}, + {0x5e30, 0x75}, + {0x5e31, 0x75}, + {0x5e32, 0x75}, + {0x5e33, 0x75}, + {0x5e34, 0x75}, + {0x5e35, 0x75}, + {0x5e36, 0x75}, + {0x5e37, 0x75}, + {0x5e38, 0x75}, + {0x5e39, 0x75}, + {0x5e3a, 0x75}, + {0x5e3b, 0x75}, + {0x5e3c, 0x75}, + {0x5e3d, 0x75}, + {0x5e3e, 0x75}, + {0x5e3f, 0x75}, + {0x5e40, 0x75}, + {0x5e41, 0x75}, + {0x5e42, 0x75}, + {0x5e43, 0x75}, + {0x5e44, 0x75}, + {0x5e45, 0x75}, + {0x5e46, 0x75}, + {0x5e47, 0x75}, + {0x5e48, 0x75}, + {0x5e49, 0x75}, + {0x5e4a, 0x75}, + {0x5e4b, 0x75}, + {0x5e4c, 0x75}, + {0x5e4d, 0x75}, + {0x5e4e, 0x75}, + {0x5e4f, 0x75}, + {0x5e50, 0x75}, + {0x5e51, 0x75}, + {0x5e52, 0x75}, + {0x5e53, 0x75}, + {0x5e54, 0x75}, + {0x5e55, 0x75}, + {0x5e56, 0x75}, + {0x5e57, 0x75}, + {0x5e58, 0x75}, + {0x5e59, 0x75}, + {0x5e5a, 0x75}, + {0x5e5b, 0x75}, + {0x5e5c, 0x75}, + {0x5e5d, 0x75}, + {0x5e5e, 0x75}, + {0x5e5f, 0x75}, + {0x5e60, 0x75}, + {0x5e61, 0x75}, + {0x5e62, 0x75}, + {0x5e63, 0x75}, + {0x5e64, 0x75}, + {0x5e65, 0x75}, + {0x5e66, 0x75}, + {0x5e67, 0x75}, + {0x5e68, 0x75}, + {0x5e69, 0x75}, + {0x5e6a, 0x75}, + {0x5e6b, 0x75}, + {0x5e6c, 0x75}, + {0x5e6d, 0x75}, + {0x5e6e, 0x75}, + {0x5e6f, 0x75}, + {0x5e70, 0x75}, + {0x5e71, 0x75}, + {0x5e72, 0x75}, + {0x5e73, 0x75}, + {0x5e74, 0x75}, + {0x5e75, 0x75}, + {0x5e76, 0x75}, + {0x5e77, 0x75}, + {0x5e78, 0x75}, + {0x5e79, 0x75}, + {0x5e7a, 0x75}, + {0x5e7b, 0x75}, + {0x5e7c, 0x75}, + {0x5e7d, 0x75}, + {0x5e7e, 0x75}, + {0x5e7f, 0x75}, + {0x5e80, 0x75}, + {0x5e81, 0x75}, + {0x5e82, 0x75}, + {0x5e83, 0x75}, + {0x5e84, 0x75}, + {0x5e85, 0x75}, + {0x5e86, 0x75}, + {0x5e87, 0x75}, + {0x5e88, 0x75}, + {0x5e89, 0x75}, + {0x5e8a, 0x75}, + {0x5e8b, 0x75}, + {0x5e8c, 0x75}, + {0x5e8d, 0x75}, + {0x5e8e, 0x75}, + {0x5e8f, 0x75}, + {0x5e90, 0x75}, + {0x5e91, 0x75}, + {0x5e92, 0x75}, + {0x5e93, 0x75}, + {0x5e94, 0x75}, + {0x5e95, 0x75}, + {0x5e96, 0x75}, + {0x5e97, 0x75}, + {0x5e98, 0x75}, + {0x5e99, 0x75}, + {0x5e9a, 0x75}, + {0x5e9b, 0x75}, + {0x5e9c, 0x75}, + {0x5e9d, 0x75}, + {0x5e9e, 0x75}, + {0x5e9f, 0x75}, + {0x5ea0, 0x75}, + {0x5ea1, 0x75}, + {0x5ea2, 0x75}, + {0x5ea3, 0x75}, + {0x5ea4, 0x75}, + {0x5ea5, 0x75}, + {0x5ea6, 0x75}, + {0x5ea7, 0x75}, + {0x5ea8, 0x75}, + {0x5ea9, 0x75}, + {0x5eaa, 0x75}, + {0x5eab, 0x75}, + {0x5eac, 0x75}, + {0x5ead, 0x75}, + {0x5eae, 0x75}, + {0x5eaf, 0x75}, + {0x5eb0, 0x75}, + {0x5eb1, 0x75}, + {0x5eb2, 0x75}, + {0x5eb3, 0x75}, + {0x5eb4, 0x75}, + {0x5eb5, 0x75}, + {0x5eb6, 0x75}, + {0x5eb7, 0x75}, + {0x5eb8, 0x75}, + {0x5eb9, 0x75}, + {0x5eba, 0x75}, + {0x5ebb, 0x75}, + {0x5ebc, 0x75}, + {0x5ebd, 0x75}, + {0x5ebe, 0x75}, + {0x5ebf, 0x75}, + {0x5ec0, 0x75}, + {0x5ec1, 0x75}, + {0x5ec2, 0x75}, + {0x5ec3, 0x75}, + {0x5ec4, 0x75}, + {0x5ec5, 0x75}, + {0x5ec6, 0x75}, + {0x5ec7, 0x75}, + {0x5ec8, 0x75}, + {0x5ec9, 0x75}, + {0x5eca, 0x75}, + {0x5ecb, 0x75}, + {0x5ecc, 0x75}, + {0x5ecd, 0x75}, + {0x5ece, 0x75}, + {0x5ecf, 0x75}, + {0x5ed0, 0x75}, + {0x5ed1, 0x75}, + {0x5ed2, 0x75}, + {0x5ed3, 0x75}, + {0x5ed4, 0x75}, + {0x5ed5, 0x75}, + {0x5ed6, 0x75}, + {0x5ed7, 0x75}, + {0x5ed8, 0x75}, + {0x5ed9, 0x75}, + {0x5eda, 0x75}, + {0x5edb, 0x75}, + {0x5edc, 0x75}, + {0x5edd, 0x75}, + {0x5ede, 0x75}, + {0x5edf, 0x75}, + {0x5ee0, 0x75}, + {0x5ee1, 0x75}, + {0x5ee2, 0x75}, + {0x5ee3, 0x75}, + {0x5ee4, 0x75}, + {0x5ee5, 0x75}, + {0x5ee6, 0x75}, + {0x5ee7, 0x75}, + {0x5ee8, 0x75}, + {0x5ee9, 0x75}, + {0x5eea, 0x75}, + {0x5eeb, 0x75}, + {0x5eec, 0x75}, + {0x5eed, 0x75}, + {0x5eee, 0x75}, + {0x5eef, 0x75}, + {0x5ef0, 0x75}, + {0x5ef1, 0x75}, + {0x5ef2, 0x75}, + {0x5ef3, 0x75}, + {0x5ef4, 0x75}, + {0x5ef5, 0x75}, + {0x5ef6, 0x75}, + {0x5ef7, 0x75}, + {0x5ef8, 0x75}, + {0x5ef9, 0x75}, + {0x5efa, 0x75}, + {0x5efb, 0x75}, + {0x5efc, 0x75}, + {0x5efd, 0x75}, + {0x5efe, 0x75}, + {0x5eff, 0x75}, + {0x5f00, 0x75}, + {0x5f01, 0x75}, + {0x5f02, 0x75}, + {0x5f03, 0x75}, + {0x5f04, 0x75}, + {0x5f05, 0x75}, + {0x5f06, 0x75}, + {0x5f07, 0x75}, + {0x5f08, 0x75}, + {0x5f09, 0x75}, + {0x5f0a, 0x75}, + {0x5f0b, 0x75}, + {0x5f0c, 0x75}, + {0x5f0d, 0x75}, + {0x5f0e, 0x75}, + {0x5f0f, 0x75}, + {0x5f10, 0x75}, + {0x5f11, 0x75}, + {0x5f12, 0x75}, + {0x5f13, 0x75}, + {0x5f14, 0x75}, + {0x5f15, 0x75}, + {0x5f16, 0x75}, + {0x5f17, 0x75}, + {0x5f18, 0x75}, + {0x5f19, 0x75}, + {0x5f1a, 0x75}, + {0x5f1b, 0x75}, + {0x5f1c, 0x75}, + {0x5f1d, 0x75}, + {0x5f1e, 0x75}, + {0x5f1f, 0x75}, +}; + +static const struct ov08x40_reg mode_1928x1208_regs[] = { + {0x5000, 0x55}, + {0x5001, 0x00}, + {0x5008, 0xb0}, + {0x50c1, 0x00}, + {0x53c1, 0x00}, + {0x5f40, 0x00}, + {0x5f41, 0x40}, + {0x0300, 0x3a}, + {0x0301, 0xc8}, + {0x0302, 0x31}, + {0x0303, 0x03}, + {0x0304, 0x01}, + {0x0305, 0xa1}, + {0x0306, 0x04}, + {0x0307, 0x01}, + {0x0308, 0x03}, + {0x0309, 0x03}, + {0x0310, 0x0a}, + {0x0311, 0x02}, + {0x0312, 0x01}, + {0x0313, 0x08}, + {0x0314, 0x66}, + {0x0315, 0x00}, + {0x0316, 0x34}, + {0x0320, 0x02}, + {0x0321, 0x03}, + {0x0323, 0x05}, + {0x0324, 0x01}, + {0x0325, 0xb8}, + {0x0326, 0x4a}, + {0x0327, 0x04}, + {0x0329, 0x00}, + {0x032a, 0x05}, + {0x032b, 0x00}, + {0x032c, 0x00}, + {0x032d, 0x00}, + {0x032e, 0x02}, + {0x032f, 0xa0}, + {0x0350, 0x00}, + {0x0360, 0x01}, + {0x1216, 0x60}, + {0x1217, 0x5b}, + {0x1218, 0x00}, + {0x1220, 0x24}, + {0x198a, 0x00}, + {0x198b, 0x01}, + {0x198e, 0x00}, + {0x198f, 0x01}, + {0x3009, 0x04}, + {0x3012, 0x41}, + {0x3015, 0x00}, + {0x3016, 0xb0}, + {0x3017, 0xf0}, + {0x3018, 0xf0}, + {0x3019, 0xd2}, + {0x301a, 0xb0}, + {0x301c, 0x81}, + {0x301d, 0x02}, + {0x301e, 0x80}, + {0x3022, 0xf0}, + {0x3025, 0x89}, + {0x3030, 0x03}, + {0x3044, 0xc2}, + {0x3050, 0x35}, + {0x3051, 0x60}, + {0x3052, 0x25}, + {0x3053, 0x00}, + {0x3054, 0x00}, + {0x3055, 0x02}, + {0x3056, 0x80}, + {0x3057, 0x80}, + {0x3058, 0x80}, + {0x3059, 0x00}, + {0x3107, 0x86}, + {0x3400, 0x1c}, + {0x3401, 0x80}, + {0x3402, 0x8c}, + {0x3419, 0x08}, + {0x341a, 0xaf}, + {0x341b, 0x30}, + {0x3420, 0x00}, + {0x3421, 0x00}, + {0x3422, 0x00}, + {0x3423, 0x00}, + {0x3424, 0x00}, + {0x3425, 0x00}, + {0x3426, 0x00}, + {0x3427, 0x00}, + {0x3428, 0x0f}, + {0x3429, 0x00}, + {0x342a, 0x00}, + {0x342b, 0x00}, + {0x342c, 0x00}, + {0x342d, 0x00}, + {0x342e, 0x00}, + {0x342f, 0x11}, + {0x3430, 0x11}, + {0x3431, 0x10}, + {0x3432, 0x00}, + {0x3433, 0x00}, + {0x3434, 0x00}, + {0x3435, 0x00}, + {0x3436, 0x00}, + {0x3437, 0x00}, + {0x3442, 0x02}, + {0x3443, 0x02}, + {0x3444, 0x07}, + {0x3450, 0x00}, + {0x3451, 0x00}, + {0x3452, 0x18}, + {0x3453, 0x18}, + {0x3454, 0x00}, + {0x3455, 0x80}, + {0x3456, 0x08}, + {0x3500, 0x00}, + {0x3501, 0x02}, + {0x3502, 0x00}, + {0x3504, 0x4c}, + {0x3506, 0x30}, + {0x3507, 0x00}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {0x350a, 0x01}, + {0x350b, 0x00}, + {0x350c, 0x00}, + {0x3540, 0x00}, + {0x3541, 0x01}, + {0x3542, 0x00}, + {0x3544, 0x4c}, + {0x3546, 0x30}, + {0x3547, 0x00}, + {0x3548, 0x01}, + {0x3549, 0x00}, + {0x354a, 0x01}, + {0x354b, 0x00}, + {0x354c, 0x00}, + {0x3688, 0x02}, + {0x368a, 0x2e}, + {0x368e, 0x71}, + {0x3696, 0xd1}, + {0x3699, 0x00}, + {0x369a, 0x00}, + {0x36a4, 0x00}, + {0x36a6, 0x00}, + {0x3711, 0x00}, + {0x3712, 0x50}, + {0x3713, 0x00}, + {0x3714, 0x21}, + {0x3716, 0x00}, + {0x3718, 0x07}, + {0x371a, 0x1c}, + {0x371b, 0x00}, + {0x3720, 0x08}, + {0x3725, 0x32}, + {0x3727, 0x05}, + {0x3760, 0x02}, + {0x3761, 0x28}, + {0x3762, 0x02}, + {0x3763, 0x02}, + {0x3764, 0x02}, + {0x3765, 0x2c}, + {0x3766, 0x04}, + {0x3767, 0x2c}, + {0x3768, 0x02}, + {0x3769, 0x00}, + {0x376b, 0x20}, + {0x376e, 0x07}, + {0x37b0, 0x01}, + {0x37b1, 0x0f}, + {0x37b2, 0x01}, + {0x37b3, 0xd6}, + {0x37b4, 0x01}, + {0x37b5, 0x48}, + {0x37b6, 0x02}, + {0x37b7, 0x40}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0f}, + {0x3805, 0x1f}, + {0x3806, 0x09}, + {0x3807, 0x7f}, + {0x3808, 0x07}, + {0x3809, 0x88}, + {0x380a, 0x04}, + {0x380b, 0xb8}, + {0x380c, 0x02}, + {0x380d, 0xd0}, + {0x380e, 0x11}, + {0x380f, 0x5c}, + {0x3810, 0x00}, + {0x3811, 0x04}, + {0x3812, 0x00}, + {0x3813, 0x03}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3820, 0x02}, + {0x3821, 0x14}, + {0x3822, 0x00}, + {0x3823, 0x04}, + {0x3828, 0x0f}, + {0x382a, 0x80}, + {0x382e, 0x41}, + {0x3837, 0x08}, + {0x383a, 0x81}, + {0x383b, 0x81}, + {0x383c, 0x11}, + {0x383d, 0x11}, + {0x383e, 0x00}, + {0x383f, 0x38}, + {0x3840, 0x00}, + {0x3847, 0x00}, + {0x384a, 0x00}, + {0x384c, 0x02}, + {0x384d, 0xd0}, + {0x3856, 0x50}, + {0x3857, 0x30}, + {0x3858, 0x80}, + {0x3859, 0x40}, + {0x3860, 0x00}, + {0x3888, 0x00}, + {0x3889, 0x00}, + {0x388a, 0x00}, + {0x388b, 0x00}, + {0x388c, 0x00}, + {0x388d, 0x00}, + {0x388e, 0x00}, + {0x388f, 0x00}, + {0x3894, 0x00}, + {0x3895, 0x00}, + {0x3c84, 0x00}, + {0x3d85, 0x8b}, + {0x3daa, 0x80}, + {0x3dab, 0x14}, + {0x3dac, 0x80}, + {0x3dad, 0xc8}, + {0x3dae, 0x81}, + {0x3daf, 0x7b}, + {0x3f00, 0x10}, + {0x3f01, 0x11}, + {0x3f06, 0x0d}, + {0x3f07, 0x0b}, + {0x3f08, 0x0d}, + {0x3f09, 0x0b}, + {0x3f0a, 0x01}, + {0x3f0b, 0x11}, + {0x3f0c, 0x33}, + {0x4001, 0x07}, + {0x4007, 0x20}, + {0x4008, 0x00}, + {0x4009, 0x05}, + {0x400a, 0x00}, + {0x400b, 0x04}, + {0x400c, 0x00}, + {0x400d, 0x04}, + {0x400e, 0x14}, + {0x4010, 0xf4}, + {0x4011, 0x03}, + {0x4012, 0x55}, + {0x4015, 0x00}, + {0x4016, 0x27}, + {0x4017, 0x00}, + {0x4018, 0x0f}, + {0x401b, 0x08}, + {0x401c, 0x00}, + {0x401d, 0x10}, + {0x401e, 0x02}, + {0x401f, 0x00}, + {0x4050, 0x06}, + {0x4051, 0xff}, + {0x4052, 0xff}, + {0x4053, 0xff}, + {0x4054, 0xff}, + {0x4055, 0xff}, + {0x4056, 0xff}, + {0x4057, 0x7f}, + {0x4058, 0x00}, + {0x4059, 0x00}, + {0x405a, 0x00}, + {0x405b, 0x00}, + {0x405c, 0x07}, + {0x405d, 0xff}, + {0x405e, 0x07}, + {0x405f, 0xff}, + {0x4080, 0x78}, + {0x4081, 0x78}, + {0x4082, 0x78}, + {0x4083, 0x78}, + {0x4019, 0x00}, + {0x401a, 0x40}, + {0x4020, 0x04}, + {0x4021, 0x00}, + {0x4022, 0x04}, + {0x4023, 0x00}, + {0x4024, 0x04}, + {0x4025, 0x00}, + {0x4026, 0x04}, + {0x4027, 0x00}, + {0x4030, 0x00}, + {0x4031, 0x00}, + {0x4032, 0x00}, + {0x4033, 0x00}, + {0x4034, 0x00}, + {0x4035, 0x00}, + {0x4036, 0x00}, + {0x4037, 0x00}, + {0x4040, 0x00}, + {0x4041, 0x80}, + {0x4042, 0x00}, + {0x4043, 0x80}, + {0x4044, 0x00}, + {0x4045, 0x80}, + {0x4046, 0x00}, + {0x4047, 0x80}, + {0x4060, 0x00}, + {0x4061, 0x00}, + {0x4062, 0x00}, + {0x4063, 0x00}, + {0x4064, 0x00}, + {0x4065, 0x00}, + {0x4066, 0x00}, + {0x4067, 0x00}, + {0x4068, 0x00}, + {0x4069, 0x00}, + {0x406a, 0x00}, + {0x406b, 0x00}, + {0x406c, 0x00}, + {0x406d, 0x00}, + {0x406e, 0x00}, + {0x406f, 0x00}, + {0x4070, 0x00}, + {0x4071, 0x00}, + {0x4072, 0x00}, + {0x4073, 0x00}, + {0x4074, 0x00}, + {0x4075, 0x00}, + {0x4076, 0x00}, + {0x4077, 0x00}, + {0x4078, 0x00}, + {0x4079, 0x00}, + {0x407a, 0x00}, + {0x407b, 0x00}, + {0x407c, 0x00}, + {0x407d, 0x00}, + {0x407e, 0x00}, + {0x407f, 0x00}, + {0x40e0, 0x00}, + {0x40e1, 0x00}, + {0x40e2, 0x00}, + {0x40e3, 0x00}, + {0x40e4, 0x00}, + {0x40e5, 0x00}, + {0x40e6, 0x00}, + {0x40e7, 0x00}, + {0x40e8, 0x00}, + {0x40e9, 0x80}, + {0x40ea, 0x00}, + {0x40eb, 0x80}, + {0x40ec, 0x00}, + {0x40ed, 0x80}, + {0x40ee, 0x00}, + {0x40ef, 0x80}, + {0x40f0, 0x02}, + {0x40f1, 0x04}, + {0x4300, 0x00}, + {0x4301, 0x00}, + {0x4302, 0x00}, + {0x4303, 0x00}, + {0x4304, 0x00}, + {0x4305, 0x00}, + {0x4306, 0x00}, + {0x4307, 0x00}, + {0x4308, 0x00}, + {0x4309, 0x00}, + {0x430a, 0x00}, + {0x430b, 0xff}, + {0x430c, 0xff}, + {0x430d, 0x00}, + {0x430e, 0x00}, + {0x4315, 0x00}, + {0x4316, 0x00}, + {0x4317, 0x00}, + {0x4318, 0x00}, + {0x4319, 0x00}, + {0x431a, 0x00}, + {0x431b, 0x00}, + {0x431c, 0x00}, + {0x4500, 0x07}, + {0x4501, 0x10}, + {0x4502, 0x00}, + {0x4503, 0x0f}, + {0x4504, 0x80}, + {0x4506, 0x01}, + {0x4509, 0x05}, + {0x450c, 0x00}, + {0x450d, 0x20}, + {0x450e, 0x00}, + {0x450f, 0x00}, + {0x4510, 0x00}, + {0x4523, 0x00}, + {0x4526, 0x00}, + {0x4542, 0x00}, + {0x4543, 0x00}, + {0x4544, 0x00}, + {0x4545, 0x00}, + {0x4546, 0x00}, + {0x4547, 0x10}, + {0x4602, 0x00}, + {0x4603, 0x15}, + {0x460b, 0x07}, + {0x4680, 0x11}, + {0x4686, 0x00}, + {0x4687, 0x00}, + {0x4700, 0x00}, + {0x4800, 0x64}, + {0x4806, 0x40}, + {0x480b, 0x10}, + {0x480c, 0x80}, + {0x480f, 0x32}, + {0x4813, 0xe4}, + {0x4837, 0x14}, + {0x4850, 0x42}, + {0x4884, 0x04}, + {0x4c00, 0xf8}, + {0x4c01, 0x44}, + {0x4c03, 0x00}, + {0x4d00, 0x00}, + {0x4d01, 0x16}, + {0x4d04, 0x10}, + {0x4d05, 0x00}, + {0x4d06, 0x0c}, + {0x4d07, 0x00}, + {0x3d84, 0x04}, + {0x3680, 0xa4}, + {0x3682, 0x80}, + {0x3601, 0x40}, + {0x3602, 0x90}, + {0x3608, 0x0a}, + {0x3938, 0x09}, + {0x3a74, 0x84}, + {0x3a99, 0x84}, + {0x3ab9, 0xa6}, + {0x3aba, 0xba}, + {0x3b12, 0x84}, + {0x3b14, 0xbb}, + {0x3b15, 0xbf}, + {0x3a29, 0x26}, + {0x3a1f, 0x8a}, + {0x3a22, 0x91}, + {0x3a25, 0x96}, + {0x3a28, 0xb4}, + {0x3a2b, 0xba}, + {0x3a2e, 0xbf}, + {0x3a31, 0xc1}, + {0x3a20, 0x05}, + {0x3939, 0x6b}, + {0x3902, 0x10}, + {0x3903, 0x10}, + {0x3904, 0x10}, + {0x3905, 0x10}, + {0x3906, 0x01}, + {0x3907, 0x0b}, + {0x3908, 0x10}, + {0x3909, 0x13}, + {0x360f, 0x99}, + {0x390b, 0x11}, + {0x390c, 0x21}, + {0x390d, 0x32}, + {0x390e, 0x76}, + {0x3911, 0x90}, + {0x3913, 0x90}, + {0x3b3f, 0x9d}, + {0x3b45, 0x9d}, + {0x3b1b, 0xc9}, + {0x3b21, 0xc9}, + {0x3a1a, 0x1c}, + {0x3a23, 0x15}, + {0x3a26, 0x17}, + {0x3a2c, 0x50}, + {0x3a2f, 0x18}, + {0x3a32, 0x4f}, + {0x3ace, 0x01}, + {0x3ad2, 0x01}, + {0x3ad6, 0x01}, + {0x3ada, 0x01}, + {0x3ade, 0x01}, + {0x3ae2, 0x01}, + {0x3aee, 0x01}, + {0x3af2, 0x01}, + {0x3af6, 0x01}, + {0x3afa, 0x01}, + {0x3afe, 0x01}, + {0x3b02, 0x01}, + {0x3b06, 0x01}, + {0x3b0a, 0x01}, + {0x3b0b, 0x00}, + {0x3b0e, 0x01}, + {0x3b0f, 0x00}, + {0x392c, 0x02}, + {0x392d, 0x01}, + {0x392e, 0x04}, + {0x392f, 0x03}, + {0x3930, 0x09}, + {0x3931, 0x07}, + {0x3932, 0x10}, + {0x3933, 0x0d}, + {0x3609, 0x08}, + {0x3921, 0x0f}, + {0x3928, 0x15}, + {0x3929, 0x2a}, + {0x392a, 0x52}, + {0x392b, 0xa3}, + {0x340b, 0x1b}, + {0x3426, 0x10}, + {0x3407, 0x01}, + {0x3404, 0x01}, + {0x3500, 0x00}, + {0x3501, 0x08}, + {0x3502, 0x10}, + {0x3508, 0x04}, + {0x3509, 0x00}, +}; + +static const char * const ov08x40_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +/* Configurations for supported link frequencies */ +#define OV08X40_LINK_FREQ_400MHZ 400000000ULL + +#define OV08X40_EXT_CLK 19200000 +#define OV08X40_DATA_LANES 4 + +/* + * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample + * data rate => double data rate; number of lanes => 4; bits per pixel => 10 + */ +static u64 link_freq_to_pixel_rate(u64 f) +{ + f *= 2 * OV08X40_DATA_LANES; + do_div(f, 10); + + return f; +} + +/* Menu items for LINK_FREQ V4L2 control */ +static const s64 link_freq_menu_items[] = { + OV08X40_LINK_FREQ_400MHZ, +}; + +/* Link frequency configs */ +static const struct ov08x40_link_freq_config link_freq_configs[] = { + [OV08X40_LINK_FREQ_400MHZ_INDEX] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_800mbps), + .regs = mipi_data_rate_800mbps, + } + }, +}; + +/* Mode configs */ +static const struct ov08x40_mode supported_modes[] = { + { + .width = 3856, + .height = 2416, + .vts_def = OV08X40_VTS_30FPS, + .vts_min = OV08X40_VTS_30FPS, + .lanes = 4, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3856x2416_regs), + .regs = mode_3856x2416_regs, + }, + .link_freq_index = OV08X40_LINK_FREQ_400MHZ_INDEX, + }, + { + .width = 1928, + .height = 1208, + .vts_def = OV08X40_VTS_BIN_30FPS, + .vts_min = OV08X40_VTS_BIN_30FPS, + .lanes = 4, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1928x1208_regs), + .regs = mode_1928x1208_regs, + }, + .link_freq_index = OV08X40_LINK_FREQ_400MHZ_INDEX, + }, +}; + +struct ov08x40 { + struct v4l2_subdev sd; + struct media_pad pad; + + struct v4l2_ctrl_handler ctrl_handler; + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + + /* Current mode */ + const struct ov08x40_mode *cur_mode; + + /* Mutex for serialized access */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +#define to_ov08x40(_sd) container_of(_sd, struct ov08x40, sd) + +/* Read registers up to 4 at a time */ +static int ov08x40_read_reg(struct ov08x40 *ov08x, + u16 reg, u32 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd); + struct i2c_msg msgs[2]; + u8 *data_be_p; + int ret; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + + if (len > 4) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +/* Write registers up to 4 at a time */ +static int ov08x40_write_reg(struct ov08x40 *ov08x, + u16 reg, u32 len, u32 __val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd); + int buf_i, val_i; + u8 buf[6], *val_p; + __be32 val; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val = cpu_to_be32(__val); + val_p = (u8 *)&val; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +/* Write a list of registers */ +static int ov08x40_write_regs(struct ov08x40 *ov08x, + const struct ov08x40_reg *regs, u32 len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd); + int ret; + u32 i; + + for (i = 0; i < len; i++) { + ret = ov08x40_write_reg(ov08x, regs[i].address, 1, + regs[i].val); + + if (ret) { + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error = %d\n", + regs[i].address, ret); + + return ret; + } + } + + return 0; +} + +static int ov08x40_write_reg_list(struct ov08x40 *ov08x, + const struct ov08x40_reg_list *r_list) +{ + return ov08x40_write_regs(ov08x, r_list->regs, r_list->num_of_regs); +} + +static int ov08x40_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + const struct ov08x40_mode *default_mode = &supported_modes[0]; + struct ov08x40 *ov08x = to_ov08x40(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->state, 0); + + mutex_lock(&ov08x->mutex); + + /* Initialize try_fmt */ + try_fmt->width = default_mode->width; + try_fmt->height = default_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + /* No crop or compose */ + mutex_unlock(&ov08x->mutex); + + return 0; +} + +static int ov08x40_update_digital_gain(struct ov08x40 *ov08x, u32 d_gain) +{ + int ret; + u32 val; + + /* + * 0x350C[1:0], 0x350B[7:0], 0x350A[4:0] + */ + + val = (d_gain & OV08X40_DGTL_GAIN_L_MASK) << OV08X40_DGTL_GAIN_L_SHIFT; + ret = ov08x40_write_reg(ov08x, OV08X40_REG_DGTL_GAIN_L, + OV08X40_REG_VALUE_08BIT, val); + if (ret) + return ret; + + val = (d_gain >> OV08X40_DGTL_GAIN_M_SHIFT) & OV08X40_DGTL_GAIN_M_MASK; + ret = ov08x40_write_reg(ov08x, OV08X40_REG_DGTL_GAIN_M, + OV08X40_REG_VALUE_08BIT, val); + if (ret) + return ret; + + val = (d_gain >> OV08X40_DGTL_GAIN_H_SHIFT) & OV08X40_DGTL_GAIN_H_MASK; + + return ov08x40_write_reg(ov08x, OV08X40_REG_DGTL_GAIN_H, + OV08X40_REG_VALUE_08BIT, val); +} + +static int ov08x40_enable_test_pattern(struct ov08x40 *ov08x, u32 pattern) +{ + int ret; + u32 val; + + ret = ov08x40_read_reg(ov08x, OV08X40_REG_TEST_PATTERN, + OV08X40_REG_VALUE_08BIT, &val); + if (ret) + return ret; + + if (pattern) { + ret = ov08x40_read_reg(ov08x, OV08X40_REG_ISP, + OV08X40_REG_VALUE_08BIT, &val); + if (ret) + return ret; + + ret = ov08x40_write_reg(ov08x, OV08X40_REG_ISP, + OV08X40_REG_VALUE_08BIT, + val | BIT(1)); + if (ret) + return ret; + + ret = ov08x40_read_reg(ov08x, OV08X40_REG_SHORT_TEST_PATTERN, + OV08X40_REG_VALUE_08BIT, &val); + if (ret) + return ret; + + ret = ov08x40_write_reg(ov08x, OV08X40_REG_SHORT_TEST_PATTERN, + OV08X40_REG_VALUE_08BIT, + val | BIT(0)); + if (ret) + return ret; + + ret = ov08x40_read_reg(ov08x, OV08X40_REG_TEST_PATTERN, + OV08X40_REG_VALUE_08BIT, &val); + if (ret) + return ret; + + val &= OV08X40_TEST_PATTERN_MASK; + val |= ((pattern - 1) << OV08X40_TEST_PATTERN_BAR_SHIFT) | + OV08X40_TEST_PATTERN_ENABLE; + } else { + val &= ~OV08X40_TEST_PATTERN_ENABLE; + } + + return ov08x40_write_reg(ov08x, OV08X40_REG_TEST_PATTERN, + OV08X40_REG_VALUE_08BIT, val); +} + +static int ov08x40_set_ctrl_hflip(struct ov08x40 *ov08x, u32 ctrl_val) +{ + int ret; + u32 val; + + ret = ov08x40_read_reg(ov08x, OV08X40_REG_MIRROR, + OV08X40_REG_VALUE_08BIT, &val); + if (ret) + return ret; + + return ov08x40_write_reg(ov08x, OV08X40_REG_MIRROR, + OV08X40_REG_VALUE_08BIT, + ctrl_val ? val | BIT(2) : val & ~BIT(2)); +} + +static int ov08x40_set_ctrl_vflip(struct ov08x40 *ov08x, u32 ctrl_val) +{ + int ret; + u32 val; + + ret = ov08x40_read_reg(ov08x, OV08X40_REG_VFLIP, + OV08X40_REG_VALUE_08BIT, &val); + if (ret) + return ret; + + return ov08x40_write_reg(ov08x, OV08X40_REG_VFLIP, + OV08X40_REG_VALUE_08BIT, + ctrl_val ? val | BIT(2) : val & ~BIT(2)); +} + +static int ov08x40_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov08x40 *ov08x = container_of(ctrl->handler, + struct ov08x40, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd); + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = ov08x->cur_mode->height + ctrl->val - OV08X40_EXPOSURE_MAX_MARGIN; + __v4l2_ctrl_modify_range(ov08x->exposure, + ov08x->exposure->minimum, + max, ov08x->exposure->step, max); + break; + } + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = ov08x40_write_reg(ov08x, OV08X40_REG_ANALOG_GAIN, + OV08X40_REG_VALUE_16BIT, + ctrl->val << 1); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = ov08x40_update_digital_gain(ov08x, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + ret = ov08x40_write_reg(ov08x, OV08X40_REG_EXPOSURE, + OV08X40_REG_VALUE_24BIT, + ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = ov08x40_write_reg(ov08x, OV08X40_REG_VTS, + OV08X40_REG_VALUE_16BIT, + ov08x->cur_mode->height + + ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov08x40_enable_test_pattern(ov08x, ctrl->val); + break; + case V4L2_CID_HFLIP: + ov08x40_set_ctrl_hflip(ov08x, ctrl->val); + break; + case V4L2_CID_VFLIP: + ov08x40_set_ctrl_vflip(ov08x, ctrl->val); + break; + default: + dev_info(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov08x40_ctrl_ops = { + .s_ctrl = ov08x40_set_ctrl, +}; + +static int ov08x40_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + /* Only one bayer order(GRBG) is supported */ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int ov08x40_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static void ov08x40_update_pad_format(const struct ov08x40_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->format.field = V4L2_FIELD_NONE; +} + +static int ov08x40_do_get_pad_format(struct ov08x40 *ov08x, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt; + struct v4l2_subdev *sd = &ov08x->sd; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + fmt->format = *framefmt; + } else { + ov08x40_update_pad_format(ov08x->cur_mode, fmt); + } + + return 0; +} + +static int ov08x40_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov08x40 *ov08x = to_ov08x40(sd); + int ret; + + mutex_lock(&ov08x->mutex); + ret = ov08x40_do_get_pad_format(ov08x, sd_state, fmt); + mutex_unlock(&ov08x->mutex); + + return ret; +} + +static int +ov08x40_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov08x40 *ov08x = to_ov08x40(sd); + const struct ov08x40_mode *mode; + struct v4l2_mbus_framefmt *framefmt; + s32 vblank_def; + s32 vblank_min; + s64 h_blank; + s64 pixel_rate; + s64 link_freq; + + mutex_lock(&ov08x->mutex); + + /* Only one raw bayer(GRBG) order is supported */ + if (fmt->format.code != MEDIA_BUS_FMT_SGRBG10_1X10) + fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, fmt->format.height); + ov08x40_update_pad_format(mode, fmt); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + *framefmt = fmt->format; + } else { + ov08x->cur_mode = mode; + __v4l2_ctrl_s_ctrl(ov08x->link_freq, mode->link_freq_index); + link_freq = link_freq_menu_items[mode->link_freq_index]; + pixel_rate = link_freq_to_pixel_rate(link_freq); + __v4l2_ctrl_s_ctrl_int64(ov08x->pixel_rate, pixel_rate); + + /* Update limits and set FPS to default */ + vblank_def = ov08x->cur_mode->vts_def - + ov08x->cur_mode->height; + vblank_min = ov08x->cur_mode->vts_min - + ov08x->cur_mode->height; + __v4l2_ctrl_modify_range(ov08x->vblank, vblank_min, + OV08X40_VTS_MAX + - ov08x->cur_mode->height, + 1, + vblank_def); + __v4l2_ctrl_s_ctrl(ov08x->vblank, vblank_def); + h_blank = + link_freq_configs[mode->link_freq_index].pixels_per_line + - ov08x->cur_mode->width; + __v4l2_ctrl_modify_range(ov08x->hblank, h_blank, + h_blank, 1, h_blank); + } + + mutex_unlock(&ov08x->mutex); + + return 0; +} + +static int ov08x40_start_streaming(struct ov08x40 *ov08x) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd); + const struct ov08x40_reg_list *reg_list; + int ret, link_freq_index; + + /* Get out of from software reset */ + ret = ov08x40_write_reg(ov08x, OV08X40_REG_SOFTWARE_RST, + OV08X40_REG_VALUE_08BIT, OV08X40_SOFTWARE_RST); + if (ret) { + dev_err(&client->dev, "%s failed to set powerup registers\n", + __func__); + return ret; + } + + link_freq_index = ov08x->cur_mode->link_freq_index; + reg_list = &link_freq_configs[link_freq_index].reg_list; + + ret = ov08x40_write_reg_list(ov08x, reg_list); + if (ret) { + dev_err(&client->dev, "%s failed to set plls\n", __func__); + return ret; + } + + /* Apply default values of current mode */ + reg_list = &ov08x->cur_mode->reg_list; + ret = ov08x40_write_reg_list(ov08x, reg_list); + if (ret) { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + return ret; + } + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(ov08x->sd.ctrl_handler); + if (ret) + return ret; + + return ov08x40_write_reg(ov08x, OV08X40_REG_MODE_SELECT, + OV08X40_REG_VALUE_08BIT, + OV08X40_MODE_STREAMING); +} + +/* Stop streaming */ +static int ov08x40_stop_streaming(struct ov08x40 *ov08x) +{ + return ov08x40_write_reg(ov08x, OV08X40_REG_MODE_SELECT, + OV08X40_REG_VALUE_08BIT, OV08X40_MODE_STANDBY); +} + +static int ov08x40_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct ov08x40 *ov08x = to_ov08x40(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + mutex_lock(&ov08x->mutex); + if (ov08x->streaming == enable) { + mutex_unlock(&ov08x->mutex); + return 0; + } + + if (enable) { + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + goto err_unlock; + + /* + * Apply default & customized values + * and then start streaming. + */ + ret = ov08x40_start_streaming(ov08x); + if (ret) + goto err_rpm_put; + } else { + ov08x40_stop_streaming(ov08x); + pm_runtime_put(&client->dev); + } + + ov08x->streaming = enable; + mutex_unlock(&ov08x->mutex); + + return ret; + +err_rpm_put: + pm_runtime_put(&client->dev); +err_unlock: + mutex_unlock(&ov08x->mutex); + + return ret; +} + +static int __maybe_unused ov08x40_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov08x40 *ov08x = to_ov08x40(sd); + + if (ov08x->streaming) + ov08x40_stop_streaming(ov08x); + + return 0; +} + +static int __maybe_unused ov08x40_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov08x40 *ov08x = to_ov08x40(sd); + int ret; + + if (ov08x->streaming) { + ret = ov08x40_start_streaming(ov08x); + if (ret) + goto error; + } + + return 0; + +error: + ov08x40_stop_streaming(ov08x); + ov08x->streaming = false; + return ret; +} + +/* Verify chip ID */ +static int ov08x40_identify_module(struct ov08x40 *ov08x) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd); + int ret; + u32 val; + + ret = ov08x40_read_reg(ov08x, OV08X40_REG_CHIP_ID, + OV08X40_REG_VALUE_24BIT, &val); + if (ret) + return ret; + + if (val != OV08X40_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + OV08X40_CHIP_ID, val); + return -EIO; + } + + return 0; +} + +static const struct v4l2_subdev_video_ops ov08x40_video_ops = { + .s_stream = ov08x40_set_stream, +}; + +static const struct v4l2_subdev_pad_ops ov08x40_pad_ops = { + .enum_mbus_code = ov08x40_enum_mbus_code, + .get_fmt = ov08x40_get_pad_format, + .set_fmt = ov08x40_set_pad_format, + .enum_frame_size = ov08x40_enum_frame_size, +}; + +static const struct v4l2_subdev_ops ov08x40_subdev_ops = { + .video = &ov08x40_video_ops, + .pad = &ov08x40_pad_ops, +}; + +static const struct media_entity_operations ov08x40_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops ov08x40_internal_ops = { + .open = ov08x40_open, +}; + +static int ov08x40_init_controls(struct ov08x40 *ov08x) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd); + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 exposure_max; + s64 vblank_def; + s64 vblank_min; + s64 hblank; + s64 pixel_rate_min; + s64 pixel_rate_max; + const struct ov08x40_mode *mode; + u32 max; + int ret; + + ctrl_hdlr = &ov08x->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10); + if (ret) + return ret; + + mutex_init(&ov08x->mutex); + ctrl_hdlr->lock = &ov08x->mutex; + max = ARRAY_SIZE(link_freq_menu_items) - 1; + ov08x->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &ov08x40_ctrl_ops, + V4L2_CID_LINK_FREQ, + max, + 0, + link_freq_menu_items); + if (ov08x->link_freq) + ov08x->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]); + pixel_rate_min = 0; + /* By default, PIXEL_RATE is read only */ + ov08x->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov08x40_ctrl_ops, + V4L2_CID_PIXEL_RATE, + pixel_rate_min, pixel_rate_max, + 1, pixel_rate_max); + + mode = ov08x->cur_mode; + vblank_def = mode->vts_def - mode->height; + vblank_min = mode->vts_min - mode->height; + ov08x->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov08x40_ctrl_ops, + V4L2_CID_VBLANK, + vblank_min, + OV08X40_VTS_MAX - mode->height, 1, + vblank_def); + + hblank = link_freq_configs[mode->link_freq_index].pixels_per_line - + mode->width; + ov08x->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov08x40_ctrl_ops, + V4L2_CID_HBLANK, + hblank, hblank, 1, hblank); + if (ov08x->hblank) + ov08x->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + exposure_max = mode->vts_def - OV08X40_EXPOSURE_MAX_MARGIN; + ov08x->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov08x40_ctrl_ops, + V4L2_CID_EXPOSURE, + OV08X40_EXPOSURE_MIN, + exposure_max, OV08X40_EXPOSURE_STEP, + exposure_max); + + v4l2_ctrl_new_std(ctrl_hdlr, &ov08x40_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV08X40_ANA_GAIN_MIN, OV08X40_ANA_GAIN_MAX, + OV08X40_ANA_GAIN_STEP, OV08X40_ANA_GAIN_DEFAULT); + + /* Digital gain */ + v4l2_ctrl_new_std(ctrl_hdlr, &ov08x40_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + OV08X40_DGTL_GAIN_MIN, OV08X40_DGTL_GAIN_MAX, + OV08X40_DGTL_GAIN_STEP, OV08X40_DGTL_GAIN_DEFAULT); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov08x40_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov08x40_test_pattern_menu) - 1, + 0, 0, ov08x40_test_pattern_menu); + + v4l2_ctrl_new_std(ctrl_hdlr, &ov08x40_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(ctrl_hdlr, &ov08x40_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(&client->dev, "%s control init failed (%d)\n", + __func__, ret); + goto error; + } + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov08x40_ctrl_ops, + &props); + if (ret) + goto error; + + ov08x->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + mutex_destroy(&ov08x->mutex); + + return ret; +} + +static void ov08x40_free_controls(struct ov08x40 *ov08x) +{ + v4l2_ctrl_handler_free(ov08x->sd.ctrl_handler); + mutex_destroy(&ov08x->mutex); +} + +static int ov08x40_check_hwcfg(struct device *dev) +{ + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + unsigned int i, j; + int ret; + u32 ext_clk; + + if (!fwnode) + return -ENXIO; + + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &ext_clk); + if (ret) { + dev_err(dev, "can't get clock frequency"); + return ret; + } + + if (ext_clk != OV08X40_EXT_CLK) { + dev_err(dev, "external clock %d is not supported", + ext_clk); + return -EINVAL; + } + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -ENXIO; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV08X40_DATA_LANES) { + dev_err(dev, "number of CSI2 data lanes %d is not supported", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto out_err; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequencies defined"); + ret = -EINVAL; + goto out_err; + } + + for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { + for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { + if (link_freq_menu_items[i] == + bus_cfg.link_frequencies[j]) + break; + } + + if (j == bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequency %lld supported", + link_freq_menu_items[i]); + ret = -EINVAL; + goto out_err; + } + } + +out_err: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int ov08x40_probe(struct i2c_client *client) +{ + struct ov08x40 *ov08x; + int ret; + + /* Check HW config */ + ret = ov08x40_check_hwcfg(&client->dev); + if (ret) { + dev_err(&client->dev, "failed to check hwcfg: %d", ret); + return ret; + } + + ov08x = devm_kzalloc(&client->dev, sizeof(*ov08x), GFP_KERNEL); + if (!ov08x) + return -ENOMEM; + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&ov08x->sd, client, &ov08x40_subdev_ops); + + /* Check module identity */ + ret = ov08x40_identify_module(ov08x); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d\n", ret); + return ret; + } + + /* Set default mode to max resolution */ + ov08x->cur_mode = &supported_modes[0]; + + ret = ov08x40_init_controls(ov08x); + if (ret) + return ret; + + /* Initialize subdev */ + ov08x->sd.internal_ops = &ov08x40_internal_ops; + ov08x->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov08x->sd.entity.ops = &ov08x40_subdev_entity_ops; + ov08x->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + ov08x->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&ov08x->sd.entity, 1, &ov08x->pad); + if (ret) { + dev_err(&client->dev, "%s failed:%d\n", __func__, ret); + goto error_handler_free; + } + + ret = v4l2_async_register_subdev_sensor(&ov08x->sd); + if (ret < 0) + goto error_media_entity; + + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&ov08x->sd.entity); + +error_handler_free: + ov08x40_free_controls(ov08x); + + return ret; +} + +static int ov08x40_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov08x40 *ov08x = to_ov08x40(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + ov08x40_free_controls(ov08x); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +static const struct dev_pm_ops ov08x40_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ov08x40_suspend, ov08x40_resume) +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id ov08x40_acpi_ids[] = { + {"OVTI08F4"}, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(acpi, ov08x40_acpi_ids); +#endif + +static struct i2c_driver ov08x40_i2c_driver = { + .driver = { + .name = "ov08x40", + .pm = &ov08x40_pm_ops, + .acpi_match_table = ACPI_PTR(ov08x40_acpi_ids), + }, + .probe_new = ov08x40_probe, + .remove = ov08x40_remove, +}; + +module_i2c_driver(ov08x40_i2c_driver); + +MODULE_AUTHOR("Jason Chen "); +MODULE_AUTHOR("Shawn Tu "); +MODULE_DESCRIPTION("OmniVision OV08X40 sensor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 6cbd33e75ec829a2c95ba1f4403c804cf1d85c06 Mon Sep 17 00:00:00 2001 From: Mikhail Rudenko Date: Sat, 22 Oct 2022 19:20:06 +0300 Subject: media: dt-bindings: i2c: document OV4689 Add device-tree binding documentation for OV4689 image sensor driver, and the relevant MAINTAINERS entries. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Mikhail Rudenko Signed-off-by: Sakari Ailus --- .../devicetree/bindings/media/i2c/ovti,ov4689.yaml | 134 +++++++++++++++++++++ MAINTAINERS | 7 ++ 2 files changed, 141 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml (limited to 'MAINTAINERS') diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml new file mode 100644 index 000000000000..50579c947f3c --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml @@ -0,0 +1,134 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ovti,ov4689.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Omnivision OV4689 CMOS + +maintainers: + - Mikhail Rudenko + +description: | + The Omnivision OV4689 is a high performance, 1/3-inch, 4 megapixel + image sensor. Ihis chip supports high frame rate speeds up to 90 fps + at 2688x1520 resolution. It is programmable through an I2C + interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2 + connection. + +allOf: + - $ref: /schemas/media/video-interface-devices.yaml# + +properties: + compatible: + const: ovti,ov4689 + + reg: + maxItems: 1 + + clocks: + description: + External clock (XVCLK) for the sensor, 6-64 MHz + maxItems: 1 + + dovdd-supply: + description: + Digital I/O voltage supply, 1.7-3.0 V + + avdd-supply: + description: + Analog voltage supply, 2.6-3.0 V + + dvdd-supply: + description: + Digital core voltage supply, 1.1-1.3 V + + powerdown-gpios: + description: + GPIO connected to the powerdown pin (active low) + + reset-gpios: + maxItems: 1 + description: + GPIO connected to the reset pin (active low) + + orientation: true + + rotation: true + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + description: + Output port node, single endpoint describing the CSI-2 transmitter + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + oneOf: + - items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + - items: + - const: 1 + - const: 2 + - items: + - const: 1 + link-frequencies: true + + required: + - data-lanes + - link-frequencies + +required: + - compatible + - reg + - clocks + - dovdd-supply + - avdd-supply + - dvdd-supply + - port + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + ov4689: camera@36 { + compatible = "ovti,ov4689"; + reg = <0x36>; + + clocks = <&ov4689_clk>; + + avdd-supply = <&ov4689_avdd>; + dovdd-supply = <&ov4689_dovdd>; + dvdd-supply = <&ov4689_dvdd>; + + powerdown-gpios = <&pio 107 GPIO_ACTIVE_LOW>; + reset-gpios = <&pio 109 GPIO_ACTIVE_LOW>; + + orientation = <2>; + rotation = <0>; + + port { + wcam_out: endpoint { + remote-endpoint = <&mipi_in_wcam>; + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <504000000>; + }; + }; + }; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index f3ac270d9fe8..c736744888c9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15221,6 +15221,13 @@ S: Maintained T: git git://linuxtv.org/media_tree.git F: drivers/media/i2c/ov2740.c +OMNIVISION OV4689 SENSOR DRIVER +M: Mikhail Rudenko +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml + OMNIVISION OV5640 SENSOR DRIVER M: Steve Longerbeam L: linux-media@vger.kernel.org -- cgit v1.2.3 From 32a437db49aae789eb4c53450b7deedc43e397ae Mon Sep 17 00:00:00 2001 From: Mikhail Rudenko Date: Sat, 22 Oct 2022 19:20:07 +0300 Subject: media: i2c: add support for OV4689 Add a V4L2 sub-device driver for OmniVision OV4689 image sensor. This is a 4 Mpx image sensor using the I2C bus for control and the CSI-2 bus for data. This driver supports following features: - manual exposure and analog gain control support - test pattern support - media controller support - runtime PM support - support following resolutions: + 2688x1520 at 30 fps The driver provides all mandatory V4L2 controls for compatibility with libcamera. The sensor supports 1/2/4-lane CSI-2 modes, but the driver implements 4 lane mode only at this moment. Signed-off-by: Mikhail Rudenko Signed-off-by: Sakari Ailus --- MAINTAINERS | 1 + drivers/media/i2c/Kconfig | 13 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov4689.c | 1026 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1041 insertions(+) create mode 100644 drivers/media/i2c/ov4689.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index c736744888c9..518326835156 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15227,6 +15227,7 @@ L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml +F: drivers/media/i2c/ov5647.c OMNIVISION OV5640 SENSOR DRIVER M: Steve Longerbeam diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 8d749c01df70..48c0368baaaa 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -458,6 +458,19 @@ config VIDEO_OV2740 To compile this driver as a module, choose M here: the module will be called ov2740. +config VIDEO_OV4689 + tristate "OmniVision OV4689 sensor support" + depends on GPIOLIB && VIDEO_DEV && I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor-level driver for the OmniVision + OV4689 camera. + + To compile this driver as a module, choose M here: the + module will be called ov4689. + config VIDEO_OV5640 tristate "OmniVision OV5640 sensor support" depends on OF diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 83be451ef326..c9959fd21b3e 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_OV2680) += ov2680.o obj-$(CONFIG_VIDEO_OV2685) += ov2685.o obj-$(CONFIG_VIDEO_OV2740) += ov2740.o +obj-$(CONFIG_VIDEO_OV4689) += ov4689.o obj-$(CONFIG_VIDEO_OV5640) += ov5640.o obj-$(CONFIG_VIDEO_OV5645) += ov5645.o obj-$(CONFIG_VIDEO_OV5647) += ov5647.o diff --git a/drivers/media/i2c/ov4689.c b/drivers/media/i2c/ov4689.c new file mode 100644 index 000000000000..419ff7371ba8 --- /dev/null +++ b/drivers/media/i2c/ov4689.c @@ -0,0 +1,1026 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ov4689 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * Copyright (C) 2022 Mikhail Rudenko + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CHIP_ID 0x004688 +#define OV4689_REG_CHIP_ID 0x300a + +#define OV4689_XVCLK_FREQ 24000000 + +#define OV4689_REG_CTRL_MODE 0x0100 +#define OV4689_MODE_SW_STANDBY 0x0 +#define OV4689_MODE_STREAMING BIT(0) + +#define OV4689_REG_EXPOSURE 0x3500 +#define OV4689_EXPOSURE_MIN 4 +#define OV4689_EXPOSURE_STEP 1 +#define OV4689_VTS_MAX 0x7fff + +#define OV4689_REG_GAIN_H 0x3508 +#define OV4689_REG_GAIN_L 0x3509 +#define OV4689_GAIN_H_MASK 0x07 +#define OV4689_GAIN_H_SHIFT 8 +#define OV4689_GAIN_L_MASK 0xff +#define OV4689_GAIN_STEP 1 +#define OV4689_GAIN_DEFAULT 0x80 + +#define OV4689_REG_TEST_PATTERN 0x5040 +#define OV4689_TEST_PATTERN_ENABLE 0x80 +#define OV4689_TEST_PATTERN_DISABLE 0x0 + +#define OV4689_REG_VTS 0x380e + +#define REG_NULL 0xFFFF + +#define OV4689_REG_VALUE_08BIT 1 +#define OV4689_REG_VALUE_16BIT 2 +#define OV4689_REG_VALUE_24BIT 3 + +#define OV4689_LANES 4 + +static const char *const ov4689_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +struct regval { + u16 addr; + u8 val; +}; + +enum ov4689_mode_id { + OV4689_MODE_2688_1520 = 0, + OV4689_NUM_MODES, +}; + +struct ov4689_mode { + enum ov4689_mode_id id; + u32 width; + u32 height; + u32 max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + u32 pixel_rate; + u32 sensor_width; + u32 sensor_height; + u32 crop_top; + u32 crop_left; + const struct regval *reg_list; +}; + +struct ov4689 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov4689_supply_names)]; + + struct v4l2_subdev subdev; + struct media_pad pad; + + u32 clock_rate; + + struct mutex mutex; /* lock to protect streaming, ctrls and cur_mode */ + bool streaming; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + + const struct ov4689_mode *cur_mode; +}; + +#define to_ov4689(sd) container_of(sd, struct ov4689, subdev) + +struct ov4689_gain_range { + u32 logical_min; + u32 logical_max; + u32 offset; + u32 divider; + u32 physical_min; + u32 physical_max; +}; + +/* + * Xclk 24Mhz + * max_framerate 30fps + * mipi_datarate per lane 1008Mbps + */ +static const struct regval ov4689_2688x1520_regs[] = { + {0x0103, 0x01}, {0x3638, 0x00}, {0x0300, 0x00}, + {0x0302, 0x2a}, {0x0303, 0x00}, {0x0304, 0x03}, + {0x030b, 0x00}, {0x030d, 0x1e}, {0x030e, 0x04}, + {0x030f, 0x01}, {0x0312, 0x01}, {0x031e, 0x00}, + {0x3000, 0x20}, {0x3002, 0x00}, {0x3018, 0x72}, + {0x3020, 0x93}, {0x3021, 0x03}, {0x3022, 0x01}, + {0x3031, 0x0a}, {0x303f, 0x0c}, {0x3305, 0xf1}, + {0x3307, 0x04}, {0x3309, 0x29}, {0x3500, 0x00}, + {0x3501, 0x60}, {0x3502, 0x00}, {0x3503, 0x04}, + {0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00}, + {0x3507, 0x00}, {0x3508, 0x00}, {0x3509, 0x80}, + {0x350a, 0x00}, {0x350b, 0x00}, {0x350c, 0x00}, + {0x350d, 0x00}, {0x350e, 0x00}, {0x350f, 0x80}, + {0x3510, 0x00}, {0x3511, 0x00}, {0x3512, 0x00}, + {0x3513, 0x00}, {0x3514, 0x00}, {0x3515, 0x80}, + {0x3516, 0x00}, {0x3517, 0x00}, {0x3518, 0x00}, + {0x3519, 0x00}, {0x351a, 0x00}, {0x351b, 0x80}, + {0x351c, 0x00}, {0x351d, 0x00}, {0x351e, 0x00}, + {0x351f, 0x00}, {0x3520, 0x00}, {0x3521, 0x80}, + {0x3522, 0x08}, {0x3524, 0x08}, {0x3526, 0x08}, + {0x3528, 0x08}, {0x352a, 0x08}, {0x3602, 0x00}, + {0x3603, 0x40}, {0x3604, 0x02}, {0x3605, 0x00}, + {0x3606, 0x00}, {0x3607, 0x00}, {0x3609, 0x12}, + {0x360a, 0x40}, {0x360c, 0x08}, {0x360f, 0xe5}, + {0x3608, 0x8f}, {0x3611, 0x00}, {0x3613, 0xf7}, + {0x3616, 0x58}, {0x3619, 0x99}, {0x361b, 0x60}, + {0x361c, 0x7a}, {0x361e, 0x79}, {0x361f, 0x02}, + {0x3632, 0x00}, {0x3633, 0x10}, {0x3634, 0x10}, + {0x3635, 0x10}, {0x3636, 0x15}, {0x3646, 0x86}, + {0x364a, 0x0b}, {0x3700, 0x17}, {0x3701, 0x22}, + {0x3703, 0x10}, {0x370a, 0x37}, {0x3705, 0x00}, + {0x3706, 0x63}, {0x3709, 0x3c}, {0x370b, 0x01}, + {0x370c, 0x30}, {0x3710, 0x24}, {0x3711, 0x0c}, + {0x3716, 0x00}, {0x3720, 0x28}, {0x3729, 0x7b}, + {0x372a, 0x84}, {0x372b, 0xbd}, {0x372c, 0xbc}, + {0x372e, 0x52}, {0x373c, 0x0e}, {0x373e, 0x33}, + {0x3743, 0x10}, {0x3744, 0x88}, {0x3745, 0xc0}, + {0x374a, 0x43}, {0x374c, 0x00}, {0x374e, 0x23}, + {0x3751, 0x7b}, {0x3752, 0x84}, {0x3753, 0xbd}, + {0x3754, 0xbc}, {0x3756, 0x52}, {0x375c, 0x00}, + {0x3760, 0x00}, {0x3761, 0x00}, {0x3762, 0x00}, + {0x3763, 0x00}, {0x3764, 0x00}, {0x3767, 0x04}, + {0x3768, 0x04}, {0x3769, 0x08}, {0x376a, 0x08}, + {0x376b, 0x20}, {0x376c, 0x00}, {0x376d, 0x00}, + {0x376e, 0x00}, {0x3773, 0x00}, {0x3774, 0x51}, + {0x3776, 0xbd}, {0x3777, 0xbd}, {0x3781, 0x18}, + {0x3783, 0x25}, {0x3798, 0x1b}, {0x3800, 0x00}, + {0x3801, 0x08}, {0x3802, 0x00}, {0x3803, 0x04}, + {0x3804, 0x0a}, {0x3805, 0x97}, {0x3806, 0x05}, + {0x3807, 0xfb}, {0x3808, 0x0a}, {0x3809, 0x80}, + {0x380a, 0x05}, {0x380b, 0xf0}, {0x380c, 0x0a}, + {0x380d, 0x0e}, {0x380e, 0x06}, {0x380f, 0x12}, + {0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00}, + {0x3813, 0x04}, {0x3814, 0x01}, {0x3815, 0x01}, + {0x3819, 0x01}, {0x3820, 0x00}, {0x3821, 0x06}, + {0x3829, 0x00}, {0x382a, 0x01}, {0x382b, 0x01}, + {0x382d, 0x7f}, {0x3830, 0x04}, {0x3836, 0x01}, + {0x3837, 0x00}, {0x3841, 0x02}, {0x3846, 0x08}, + {0x3847, 0x07}, {0x3d85, 0x36}, {0x3d8c, 0x71}, + {0x3d8d, 0xcb}, {0x3f0a, 0x00}, {0x4000, 0xf1}, + {0x4001, 0x40}, {0x4002, 0x04}, {0x4003, 0x14}, + {0x400e, 0x00}, {0x4011, 0x00}, {0x401a, 0x00}, + {0x401b, 0x00}, {0x401c, 0x00}, {0x401d, 0x00}, + {0x401f, 0x00}, {0x4020, 0x00}, {0x4021, 0x10}, + {0x4022, 0x07}, {0x4023, 0xcf}, {0x4024, 0x09}, + {0x4025, 0x60}, {0x4026, 0x09}, {0x4027, 0x6f}, + {0x4028, 0x00}, {0x4029, 0x02}, {0x402a, 0x06}, + {0x402b, 0x04}, {0x402c, 0x02}, {0x402d, 0x02}, + {0x402e, 0x0e}, {0x402f, 0x04}, {0x4302, 0xff}, + {0x4303, 0xff}, {0x4304, 0x00}, {0x4305, 0x00}, + {0x4306, 0x00}, {0x4308, 0x02}, {0x4500, 0x6c}, + {0x4501, 0xc4}, {0x4502, 0x40}, {0x4503, 0x01}, + {0x4601, 0xa7}, {0x4800, 0x04}, {0x4813, 0x08}, + {0x481f, 0x40}, {0x4829, 0x78}, {0x4837, 0x10}, + {0x4b00, 0x2a}, {0x4b0d, 0x00}, {0x4d00, 0x04}, + {0x4d01, 0x42}, {0x4d02, 0xd1}, {0x4d03, 0x93}, + {0x4d04, 0xf5}, {0x4d05, 0xc1}, {0x5000, 0xf3}, + {0x5001, 0x11}, {0x5004, 0x00}, {0x500a, 0x00}, + {0x500b, 0x00}, {0x5032, 0x00}, {0x5040, 0x00}, + {0x5050, 0x0c}, {0x5500, 0x00}, {0x5501, 0x10}, + {0x5502, 0x01}, {0x5503, 0x0f}, {0x8000, 0x00}, + {0x8001, 0x00}, {0x8002, 0x00}, {0x8003, 0x00}, + {0x8004, 0x00}, {0x8005, 0x00}, {0x8006, 0x00}, + {0x8007, 0x00}, {0x8008, 0x00}, {0x3638, 0x00}, + {REG_NULL, 0x00}, +}; + +static const struct ov4689_mode supported_modes[] = { + { + .id = OV4689_MODE_2688_1520, + .width = 2688, + .height = 1520, + .sensor_width = 2720, + .sensor_height = 1536, + .crop_top = 8, + .crop_left = 16, + .max_fps = 30, + .exp_def = 1536, + .hts_def = 4 * 2574, + .vts_def = 1554, + .pixel_rate = 480000000, + .reg_list = ov4689_2688x1520_regs, + }, +}; + +static const u64 link_freq_menu_items[] = { 504000000 }; + +static const char *const ov4689_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +/* + * These coefficients are based on those used in Rockchip's camera + * engine, with minor tweaks for continuity. + */ +static const struct ov4689_gain_range ov4689_gain_ranges[] = { + { + .logical_min = 0, + .logical_max = 255, + .offset = 0, + .divider = 1, + .physical_min = 0, + .physical_max = 255, + }, + { + .logical_min = 256, + .logical_max = 511, + .offset = 252, + .divider = 2, + .physical_min = 376, + .physical_max = 504, + }, + { + .logical_min = 512, + .logical_max = 1023, + .offset = 758, + .divider = 4, + .physical_min = 884, + .physical_max = 1012, + }, + { + .logical_min = 1024, + .logical_max = 2047, + .offset = 1788, + .divider = 8, + .physical_min = 1912, + .physical_max = 2047, + }, +}; + +/* Write registers up to 4 at a time */ +static int ov4689_write_reg(struct i2c_client *client, u16 reg, u32 len, + u32 val) +{ + u32 buf_i, val_i; + __be32 val_be; + u8 *val_p; + u8 buf[6]; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int ov4689_write_array(struct i2c_client *client, + const struct regval *regs) +{ + int ret = 0; + u32 i; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = ov4689_write_reg(client, regs[i].addr, + OV4689_REG_VALUE_08BIT, regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int ov4689_read_reg(struct i2c_client *client, u16 reg, unsigned int len, + u32 *val) +{ + __be16 reg_addr_be = cpu_to_be16(reg); + struct i2c_msg msgs[2]; + __be32 data_be = 0; + u8 *data_be_p; + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static void ov4689_fill_fmt(const struct ov4689_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; +} + +static int ov4689_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format; + struct ov4689 *ov4689 = to_ov4689(sd); + + /* only one mode supported for now */ + ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt); + + return 0; +} + +static int ov4689_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format; + struct ov4689 *ov4689 = to_ov4689(sd); + + /* only one mode supported for now */ + ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt); + + return 0; +} + +static int ov4689_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + + return 0; +} + +static int ov4689_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int ov4689_enable_test_pattern(struct ov4689 *ov4689, u32 pattern) +{ + u32 val; + + if (pattern) + val = (pattern - 1) | OV4689_TEST_PATTERN_ENABLE; + else + val = OV4689_TEST_PATTERN_DISABLE; + + return ov4689_write_reg(ov4689->client, OV4689_REG_TEST_PATTERN, + OV4689_REG_VALUE_08BIT, val); +} + +static int ov4689_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + const struct ov4689_mode *mode = to_ov4689(sd)->cur_mode; + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = mode->sensor_width; + sel->r.height = mode->sensor_height; + return 0; + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.top = mode->crop_top; + sel->r.left = mode->crop_left; + sel->r.width = mode->width; + sel->r.height = mode->height; + return 0; + } + + return -EINVAL; +} + +static int ov4689_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ov4689 *ov4689 = to_ov4689(sd); + struct i2c_client *client = ov4689->client; + int ret = 0; + + mutex_lock(&ov4689->mutex); + + on = !!on; + if (on == ov4689->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + goto unlock_and_return; + + ret = ov4689_write_array(ov4689->client, + ov4689->cur_mode->reg_list); + if (ret) { + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + + ret = __v4l2_ctrl_handler_setup(&ov4689->ctrl_handler); + if (ret) { + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + + ret = ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE, + OV4689_REG_VALUE_08BIT, + OV4689_MODE_STREAMING); + if (ret) { + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE, + OV4689_REG_VALUE_08BIT, + OV4689_MODE_SW_STANDBY); + pm_runtime_put(&client->dev); + } + + ov4689->streaming = on; + +unlock_and_return: + mutex_unlock(&ov4689->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ov4689_cal_delay(struct ov4689 *ov4689, u32 cycles) +{ + return DIV_ROUND_UP(cycles * 1000, + DIV_ROUND_UP(ov4689->clock_rate, 1000)); +} + +static int __maybe_unused ov4689_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov4689 *ov4689 = to_ov4689(sd); + u32 delay_us; + int ret; + + ret = clk_prepare_enable(ov4689->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + gpiod_set_value_cansleep(ov4689->reset_gpio, 1); + + ret = regulator_bulk_enable(ARRAY_SIZE(ov4689_supply_names), + ov4689->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + gpiod_set_value_cansleep(ov4689->reset_gpio, 0); + usleep_range(500, 1000); + gpiod_set_value_cansleep(ov4689->pwdn_gpio, 0); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = ov4689_cal_delay(ov4689, 8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(ov4689->xvclk); + + return ret; +} + +static int __maybe_unused ov4689_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov4689 *ov4689 = to_ov4689(sd); + + gpiod_set_value_cansleep(ov4689->pwdn_gpio, 1); + clk_disable_unprepare(ov4689->xvclk); + gpiod_set_value_cansleep(ov4689->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(ov4689_supply_names), + ov4689->supplies); + return 0; +} + +static int ov4689_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov4689 *ov4689 = to_ov4689(sd); + struct v4l2_mbus_framefmt *try_fmt; + + mutex_lock(&ov4689->mutex); + + try_fmt = v4l2_subdev_get_try_format(sd, fh->state, 0); + /* Initialize try_fmt */ + ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], try_fmt); + + mutex_unlock(&ov4689->mutex); + + return 0; +} + +static const struct dev_pm_ops ov4689_pm_ops = { + SET_RUNTIME_PM_OPS(ov4689_power_off, ov4689_power_on, NULL) +}; + +static const struct v4l2_subdev_internal_ops ov4689_internal_ops = { + .open = ov4689_open, +}; + +static const struct v4l2_subdev_video_ops ov4689_video_ops = { + .s_stream = ov4689_s_stream, +}; + +static const struct v4l2_subdev_pad_ops ov4689_pad_ops = { + .enum_mbus_code = ov4689_enum_mbus_code, + .enum_frame_size = ov4689_enum_frame_sizes, + .get_fmt = ov4689_get_fmt, + .set_fmt = ov4689_set_fmt, + .get_selection = ov4689_get_selection, +}; + +static const struct v4l2_subdev_ops ov4689_subdev_ops = { + .video = &ov4689_video_ops, + .pad = &ov4689_pad_ops, +}; + +/* + * Map userspace (logical) gain to sensor (physical) gain using + * ov4689_gain_ranges table. + */ +static int ov4689_map_gain(struct ov4689 *ov4689, int logical_gain, int *result) +{ + const struct device *dev = &ov4689->client->dev; + const struct ov4689_gain_range *range; + unsigned int n; + + for (n = 0; n < ARRAY_SIZE(ov4689_gain_ranges); n++) { + if (logical_gain >= ov4689_gain_ranges[n].logical_min && + logical_gain <= ov4689_gain_ranges[n].logical_max) { + break; + } + } + + if (n == ARRAY_SIZE(ov4689_gain_ranges)) { + dev_warn_ratelimited(dev, "no mapping found for gain %d\n", + logical_gain); + return -EINVAL; + } + + range = &ov4689_gain_ranges[n]; + + *result = clamp(range->offset + (logical_gain) / range->divider, + range->physical_min, range->physical_max); + return 0; +} + +static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov4689 *ov4689 = + container_of(ctrl->handler, struct ov4689, ctrl_handler); + struct i2c_client *client = ov4689->client; + int sensor_gain; + s64 max_expo; + int ret; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max_expo = ov4689->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(ov4689->exposure, + ov4689->exposure->minimum, max_expo, + ov4689->exposure->step, + ov4689->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = ov4689_write_reg(ov4689->client, OV4689_REG_EXPOSURE, + OV4689_REG_VALUE_24BIT, ctrl->val << 4); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = ov4689_map_gain(ov4689, ctrl->val, &sensor_gain); + + ret = ret ?: + ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_H, + OV4689_REG_VALUE_08BIT, + (sensor_gain >> OV4689_GAIN_H_SHIFT) & + OV4689_GAIN_H_MASK); + ret = ret ?: + ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_L, + OV4689_REG_VALUE_08BIT, + sensor_gain & OV4689_GAIN_L_MASK); + break; + case V4L2_CID_VBLANK: + ret = ov4689_write_reg(ov4689->client, OV4689_REG_VTS, + OV4689_REG_VALUE_16BIT, + ctrl->val + ov4689->cur_mode->height); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov4689_enable_test_pattern(ov4689, ctrl->val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov4689_ctrl_ops = { + .s_ctrl = ov4689_set_ctrl, +}; + +static int ov4689_initialize_controls(struct ov4689 *ov4689) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov4689->subdev); + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *handler; + const struct ov4689_mode *mode; + s64 exposure_max, vblank_def; + struct v4l2_ctrl *ctrl; + s64 h_blank_def; + int ret; + + handler = &ov4689->ctrl_handler; + mode = ov4689->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 10); + if (ret) + return ret; + handler->lock = &ov4689->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0, + link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0, + mode->pixel_rate, 1, mode->pixel_rate); + + h_blank_def = mode->hts_def - mode->width; + ctrl = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, h_blank_def, + h_blank_def, 1, h_blank_def); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VBLANK, + vblank_def, OV4689_VTS_MAX - mode->height, 1, + vblank_def); + + exposure_max = mode->vts_def - 4; + ov4689->exposure = + v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_EXPOSURE, + OV4689_EXPOSURE_MIN, exposure_max, + OV4689_EXPOSURE_STEP, mode->exp_def); + + v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + ov4689_gain_ranges[0].logical_min, + ov4689_gain_ranges[ARRAY_SIZE(ov4689_gain_ranges) - 1] + .logical_max, + OV4689_GAIN_STEP, OV4689_GAIN_DEFAULT); + + v4l2_ctrl_new_std_menu_items(handler, &ov4689_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov4689_test_pattern_menu) - 1, + 0, 0, ov4689_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&ov4689->client->dev, "Failed to init controls(%d)\n", + ret); + goto err_free_handler; + } + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto err_free_handler; + + ret = v4l2_ctrl_new_fwnode_properties(handler, &ov4689_ctrl_ops, + &props); + if (ret) + goto err_free_handler; + + ov4689->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int ov4689_check_sensor_id(struct ov4689 *ov4689, + struct i2c_client *client) +{ + struct device *dev = &ov4689->client->dev; + u32 id = 0; + int ret; + + ret = ov4689_read_reg(client, OV4689_REG_CHIP_ID, + OV4689_REG_VALUE_16BIT, &id); + if (ret) { + dev_err(dev, "Cannot read sensor ID\n"); + return ret; + } + + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor ID %06x, expected %06x\n", + id, CHIP_ID); + return -ENODEV; + } + + dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID); + + return 0; +} + +static int ov4689_configure_regulators(struct ov4689 *ov4689) +{ + unsigned int supplies_count = ARRAY_SIZE(ov4689_supply_names); + unsigned int i; + + for (i = 0; i < supplies_count; i++) + ov4689->supplies[i].supply = ov4689_supply_names[i]; + + return devm_regulator_bulk_get(&ov4689->client->dev, supplies_count, + ov4689->supplies); +} + +static u64 ov4689_check_link_frequency(struct v4l2_fwnode_endpoint *ep) +{ + unsigned int freqs_count = ARRAY_SIZE(link_freq_menu_items); + const u64 *freqs = link_freq_menu_items; + unsigned int i, j; + + for (i = 0; i < freqs_count; i++) { + for (j = 0; j < ep->nr_of_link_frequencies; j++) + if (freqs[i] == ep->link_frequencies[j]) + return freqs[i]; + } + + return 0; +} + +static int ov4689_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct fwnode_handle *endpoint; + int ret; + + endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!endpoint) + return -EINVAL; + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); + fwnode_handle_put(endpoint); + if (ret) + return ret; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV4689_LANES) { + dev_err(dev, "Only a 4-lane CSI2 config is supported"); + ret = -EINVAL; + goto out_free_bus_cfg; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "No link frequencies defined\n"); + ret = -EINVAL; + goto out_free_bus_cfg; + } + + if (!ov4689_check_link_frequency(&bus_cfg)) { + dev_err(dev, "No supported link frequency found\n"); + ret = -EINVAL; + } + +out_free_bus_cfg: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int ov4689_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct v4l2_subdev *sd; + struct ov4689 *ov4689; + int ret; + + ret = ov4689_check_hwcfg(dev); + if (ret) + return ret; + + ov4689 = devm_kzalloc(dev, sizeof(*ov4689), GFP_KERNEL); + if (!ov4689) + return -ENOMEM; + + ov4689->client = client; + ov4689->cur_mode = &supported_modes[OV4689_MODE_2688_1520]; + + ov4689->xvclk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(ov4689->xvclk)) + return dev_err_probe(dev, PTR_ERR(ov4689->xvclk), + "Failed to get external clock\n"); + + if (!ov4689->xvclk) { + dev_dbg(dev, + "No clock provided, using clock-frequency property\n"); + device_property_read_u32(dev, "clock-frequency", + &ov4689->clock_rate); + } else { + ov4689->clock_rate = clk_get_rate(ov4689->xvclk); + } + + if (ov4689->clock_rate != OV4689_XVCLK_FREQ) { + dev_err(dev, + "External clock rate mismatch: got %d Hz, expected %d Hz\n", + ov4689->clock_rate, OV4689_XVCLK_FREQ); + return -EINVAL; + } + + ov4689->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(ov4689->reset_gpio)) { + dev_err(dev, "Failed to get reset-gpios\n"); + return PTR_ERR(ov4689->reset_gpio); + } + + ov4689->pwdn_gpio = devm_gpiod_get_optional(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(ov4689->pwdn_gpio)) { + dev_err(dev, "Failed to get pwdn-gpios\n"); + return PTR_ERR(ov4689->pwdn_gpio); + } + + ret = ov4689_configure_regulators(ov4689); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get power regulators\n"); + + mutex_init(&ov4689->mutex); + + sd = &ov4689->subdev; + v4l2_i2c_subdev_init(sd, client, &ov4689_subdev_ops); + ret = ov4689_initialize_controls(ov4689); + if (ret) + goto err_destroy_mutex; + + ret = ov4689_power_on(dev); + if (ret) + goto err_free_handler; + + ret = ov4689_check_sensor_id(ov4689, client); + if (ret) + goto err_power_off; + + sd->internal_ops = &ov4689_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + ov4689->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov4689->pad); + if (ret < 0) + goto err_power_off; + + ret = v4l2_async_register_subdev_sensor(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: + media_entity_cleanup(&sd->entity); +err_power_off: + ov4689_power_off(dev); +err_free_handler: + v4l2_ctrl_handler_free(&ov4689->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ov4689->mutex); + + return ret; +} + +static void ov4689_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov4689 *ov4689 = to_ov4689(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + + v4l2_ctrl_handler_free(&ov4689->ctrl_handler); + mutex_destroy(&ov4689->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + ov4689_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct of_device_id ov4689_of_match[] = { + { .compatible = "ovti,ov4689" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ov4689_of_match); + +static struct i2c_driver ov4689_i2c_driver = { + .driver = { + .name = "ov4689", + .pm = &ov4689_pm_ops, + .of_match_table = ov4689_of_match, + }, + .probe_new = ov4689_probe, + .remove = ov4689_remove, +}; + +module_i2c_driver(ov4689_i2c_driver); + +MODULE_DESCRIPTION("OmniVision ov4689 sensor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 38d07a960f9bba5ba362036187f5a4eda385146c Mon Sep 17 00:00:00 2001 From: Benjamin Mugnier Date: Tue, 11 Oct 2022 14:30:28 +0200 Subject: media: dt-bindings: Add ST VGXY61 camera sensor binding Add device tree binding for the ST VGXY61 camera sensor, and update MAINTAINERS file. Signed-off-by: Benjamin Mugnier Signed-off-by: Sakari Ailus --- .../bindings/media/i2c/st,st-vgxy61.yaml | 113 +++++++++++++++++++++ MAINTAINERS | 10 ++ 2 files changed, 123 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/st,st-vgxy61.yaml (limited to 'MAINTAINERS') diff --git a/Documentation/devicetree/bindings/media/i2c/st,st-vgxy61.yaml b/Documentation/devicetree/bindings/media/i2c/st,st-vgxy61.yaml new file mode 100644 index 000000000000..6597e1d0e65f --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/st,st-vgxy61.yaml @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (c) 2022 STMicroelectronics SA. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/st,st-vgxy61.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics VGxy61 HDR Global Shutter Sensor Family Device Tree Bindings + +maintainers: + - Benjamin Mugnier + - Sylvain Petinot + +description: |- + STMicroelectronics VGxy61 family has a CSI-2 output port. CSI-2 output is a + quad lanes 800Mbps per lane. + Supported formats are RAW8, RAW10, RAW12, RAW14 and RAW16. + Following part number are supported + - VG5661 and VG6661 are 1.6 Mpx (1464 x 1104) monochrome and color sensors. + Maximum frame rate is 75 fps. + - VG5761 and VG6761 are 2.3 Mpx (1944 x 1204) monochrome and color sensors. + Maximum frame rate is 60 fps. + +properties: + compatible: + const: st,st-vgxy61 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + VCORE-supply: + description: + Sensor digital core supply. Must be 1.2 volts. + + VDDIO-supply: + description: + Sensor digital IO supply. Must be 1.8 volts. + + VANA-supply: + description: + Sensor analog supply. Must be 2.8 volts. + + reset-gpios: + description: + Reference to the GPIO connected to the reset pin, if any. + This is an active low signal to the vgxy61. + + st,strobe-gpios-polarity: + description: + Invert polarity of illuminator's lights strobe GPIOs. + These GPIOs directly drive the illuminator LEDs. + type: boolean + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + description: + CSI lanes to use + items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + + remote-endpoint: true + + required: + - data-lanes + +required: + - compatible + - clocks + - VCORE-supply + - VDDIO-supply + - VANA-supply + - port + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + vgxy61: csi2tx@10 { + compatible = "st,st-vgxy61"; + reg = <0x10>; + clocks = <&clk_ext_camera>; + VCORE-supply = <&v1v2>; + VDDIO-supply = <&v1v8>; + VANA-supply = <&v2v8>; + reset-gpios = <&mfxgpio 18 GPIO_ACTIVE_LOW>; + port { + ep0: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = <&mipi_csi2_out>; + }; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 518326835156..1e4f85bd903e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19522,6 +19522,16 @@ S: Maintained F: Documentation/hwmon/stpddc60.rst F: drivers/hwmon/pmbus/stpddc60.c +ST VGXY61 DRIVER +M: Benjamin Mugnier +M: Sylvain Petinot +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: Documentation/devicetree/bindings/media/i2c/st,st-vgxy61.yaml +F: Documentation/userspace-api/media/drivers/st-vgxy61.rst +F: drivers/media/i2c/st-vgxy61.c + ST VL53L0X ToF RANGER(I2C) IIO DRIVER M: Song Qiang L: linux-iio@vger.kernel.org -- cgit v1.2.3 From b55eef5226b71edf5422de246bc189da1fdc9000 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Thu, 13 Oct 2022 18:46:12 +0100 Subject: powercap: arm_scmi: Add SCMI Powercap based driver Add a powercap driver that, using the ARM SCMI Protocol to query the SCMI platform firmware for the list of existing Powercap domains, registers all of such discovered domains under the new 'arm-scmi' powercap control type. A new simple powercap zone and constraint is registered for all the SCMI powercap zones that are found. Reviewed-by: Lukasz Luba Signed-off-by: Cristian Marussi Acked-by: Sudeep Holla Signed-off-by: Rafael J. Wysocki --- MAINTAINERS | 1 + drivers/powercap/Kconfig | 13 + drivers/powercap/Makefile | 1 + drivers/powercap/arm_scmi_powercap.c | 509 +++++++++++++++++++++++++++++++++++ 4 files changed, 524 insertions(+) create mode 100644 drivers/powercap/arm_scmi_powercap.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index e04d944005ba..141251b562d9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19977,6 +19977,7 @@ F: drivers/clk/clk-sc[mp]i.c F: drivers/cpufreq/sc[mp]i-cpufreq.c F: drivers/firmware/arm_scmi/ F: drivers/firmware/arm_scpi.c +F: drivers/powercap/arm_scmi_powercap.c F: drivers/regulator/scmi-regulator.c F: drivers/reset/reset-scmi.c F: include/linux/sc[mp]i_protocol.h diff --git a/drivers/powercap/Kconfig b/drivers/powercap/Kconfig index 515e3ceb3393..90d33cd1b670 100644 --- a/drivers/powercap/Kconfig +++ b/drivers/powercap/Kconfig @@ -44,6 +44,19 @@ config IDLE_INJECT synchronously on a set of specified CPUs or alternatively on a per CPU basis. +config ARM_SCMI_POWERCAP + tristate "ARM SCMI Powercap driver" + depends on ARM_SCMI_PROTOCOL + help + This enables support for the ARM Powercap based on ARM SCMI + Powercap protocol. + + ARM SCMI Powercap protocol allows power limits to be enforced + and monitored against the SCMI Powercap domains advertised as + available by the SCMI platform firmware. + + When compiled as module it will be called arm_scmi_powercap.ko. + config DTPM bool "Power capping for Dynamic Thermal Power Management (EXPERIMENTAL)" depends on OF diff --git a/drivers/powercap/Makefile b/drivers/powercap/Makefile index 494617cdad88..4474201b4aa7 100644 --- a/drivers/powercap/Makefile +++ b/drivers/powercap/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_POWERCAP) += powercap_sys.o obj-$(CONFIG_INTEL_RAPL_CORE) += intel_rapl_common.o obj-$(CONFIG_INTEL_RAPL) += intel_rapl_msr.o obj-$(CONFIG_IDLE_INJECT) += idle_inject.o +obj-$(CONFIG_ARM_SCMI_POWERCAP) += arm_scmi_powercap.o diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_scmi_powercap.c new file mode 100644 index 000000000000..05d0e516176a --- /dev/null +++ b/drivers/powercap/arm_scmi_powercap.c @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SCMI Powercap support. + * + * Copyright (C) 2022 ARM Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define to_scmi_powercap_zone(z) \ + container_of(z, struct scmi_powercap_zone, zone) + +static const struct scmi_powercap_proto_ops *powercap_ops; + +struct scmi_powercap_zone { + unsigned int height; + struct device *dev; + struct scmi_protocol_handle *ph; + const struct scmi_powercap_info *info; + struct scmi_powercap_zone *spzones; + struct powercap_zone zone; + struct list_head node; +}; + +struct scmi_powercap_root { + unsigned int num_zones; + struct scmi_powercap_zone *spzones; + struct list_head *registered_zones; +}; + +static struct powercap_control_type *scmi_top_pcntrl; + +static int scmi_powercap_zone_release(struct powercap_zone *pz) +{ + return 0; +} + +static int scmi_powercap_get_max_power_range_uw(struct powercap_zone *pz, + u64 *max_power_range_uw) +{ + *max_power_range_uw = U32_MAX; + return 0; +} + +static int scmi_powercap_get_power_uw(struct powercap_zone *pz, + u64 *power_uw) +{ + struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz); + u32 avg_power, pai; + int ret; + + if (!spz->info->powercap_monitoring) + return -EINVAL; + + ret = powercap_ops->measurements_get(spz->ph, spz->info->id, &avg_power, + &pai); + if (ret) + return ret; + + *power_uw = avg_power; + if (spz->info->powercap_scale_mw) + *power_uw *= 1000; + + return 0; +} + +static const struct powercap_zone_ops zone_ops = { + .get_max_power_range_uw = scmi_powercap_get_max_power_range_uw, + .get_power_uw = scmi_powercap_get_power_uw, + .release = scmi_powercap_zone_release, +}; + +static void scmi_powercap_normalize_cap(const struct scmi_powercap_zone *spz, + u64 power_limit_uw, u32 *norm) +{ + bool scale_mw = spz->info->powercap_scale_mw; + u64 val; + + val = scale_mw ? DIV_ROUND_UP_ULL(power_limit_uw, 1000) : power_limit_uw; + /* + * This cast is lossless since here @req_power is certain to be within + * the range [min_power_cap, max_power_cap] whose bounds are assured to + * be two unsigned 32bits quantities. + */ + *norm = clamp_t(u32, val, spz->info->min_power_cap, + spz->info->max_power_cap); + *norm = rounddown(*norm, spz->info->power_cap_step); + + val = (scale_mw) ? *norm * 1000 : *norm; + if (power_limit_uw != val) + dev_dbg(spz->dev, + "Normalized %s:CAP - requested:%llu - normalized:%llu\n", + spz->info->name, power_limit_uw, val); +} + +static int scmi_powercap_set_power_limit_uw(struct powercap_zone *pz, int cid, + u64 power_uw) +{ + struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz); + u32 norm_power; + + if (!spz->info->powercap_cap_config) + return -EINVAL; + + scmi_powercap_normalize_cap(spz, power_uw, &norm_power); + + return powercap_ops->cap_set(spz->ph, spz->info->id, norm_power, false); +} + +static int scmi_powercap_get_power_limit_uw(struct powercap_zone *pz, int cid, + u64 *power_limit_uw) +{ + struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz); + u32 power; + int ret; + + ret = powercap_ops->cap_get(spz->ph, spz->info->id, &power); + if (ret) + return ret; + + *power_limit_uw = power; + if (spz->info->powercap_scale_mw) + *power_limit_uw *= 1000; + + return 0; +} + +static void scmi_powercap_normalize_time(const struct scmi_powercap_zone *spz, + u64 time_us, u32 *norm) +{ + /* + * This cast is lossless since here @time_us is certain to be within the + * range [min_pai, max_pai] whose bounds are assured to be two unsigned + * 32bits quantities. + */ + *norm = clamp_t(u32, time_us, spz->info->min_pai, spz->info->max_pai); + *norm = rounddown(*norm, spz->info->pai_step); + + if (time_us != *norm) + dev_dbg(spz->dev, + "Normalized %s:PAI - requested:%llu - normalized:%u\n", + spz->info->name, time_us, *norm); +} + +static int scmi_powercap_set_time_window_us(struct powercap_zone *pz, int cid, + u64 time_window_us) +{ + struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz); + u32 norm_pai; + + if (!spz->info->powercap_pai_config) + return -EINVAL; + + scmi_powercap_normalize_time(spz, time_window_us, &norm_pai); + + return powercap_ops->pai_set(spz->ph, spz->info->id, norm_pai); +} + +static int scmi_powercap_get_time_window_us(struct powercap_zone *pz, int cid, + u64 *time_window_us) +{ + struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz); + int ret; + u32 pai; + + ret = powercap_ops->pai_get(spz->ph, spz->info->id, &pai); + if (ret) + return ret; + + *time_window_us = pai; + + return 0; +} + +static int scmi_powercap_get_max_power_uw(struct powercap_zone *pz, int cid, + u64 *max_power_uw) +{ + struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz); + + *max_power_uw = spz->info->max_power_cap; + if (spz->info->powercap_scale_mw) + *max_power_uw *= 1000; + + return 0; +} + +static int scmi_powercap_get_min_power_uw(struct powercap_zone *pz, int cid, + u64 *min_power_uw) +{ + struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz); + + *min_power_uw = spz->info->min_power_cap; + if (spz->info->powercap_scale_mw) + *min_power_uw *= 1000; + + return 0; +} + +static int scmi_powercap_get_max_time_window_us(struct powercap_zone *pz, + int cid, u64 *time_window_us) +{ + struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz); + + *time_window_us = spz->info->max_pai; + + return 0; +} + +static int scmi_powercap_get_min_time_window_us(struct powercap_zone *pz, + int cid, u64 *time_window_us) +{ + struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz); + + *time_window_us = (u64)spz->info->min_pai; + + return 0; +} + +static const char *scmi_powercap_get_name(struct powercap_zone *pz, int cid) +{ + return "SCMI power-cap"; +} + +static const struct powercap_zone_constraint_ops constraint_ops = { + .set_power_limit_uw = scmi_powercap_set_power_limit_uw, + .get_power_limit_uw = scmi_powercap_get_power_limit_uw, + .set_time_window_us = scmi_powercap_set_time_window_us, + .get_time_window_us = scmi_powercap_get_time_window_us, + .get_max_power_uw = scmi_powercap_get_max_power_uw, + .get_min_power_uw = scmi_powercap_get_min_power_uw, + .get_max_time_window_us = scmi_powercap_get_max_time_window_us, + .get_min_time_window_us = scmi_powercap_get_min_time_window_us, + .get_name = scmi_powercap_get_name, +}; + +static void scmi_powercap_unregister_all_zones(struct scmi_powercap_root *pr) +{ + int i; + + /* Un-register children zones first starting from the leaves */ + for (i = pr->num_zones - 1; i >= 0; i--) { + if (!list_empty(&pr->registered_zones[i])) { + struct scmi_powercap_zone *spz; + + list_for_each_entry(spz, &pr->registered_zones[i], node) + powercap_unregister_zone(scmi_top_pcntrl, + &spz->zone); + } + } +} + +static inline bool +scmi_powercap_is_zone_registered(struct scmi_powercap_zone *spz) +{ + return !list_empty(&spz->node); +} + +static inline unsigned int +scmi_powercap_get_zone_height(struct scmi_powercap_zone *spz) +{ + if (spz->info->parent_id == SCMI_POWERCAP_ROOT_ZONE_ID) + return 0; + + return spz->spzones[spz->info->parent_id].height + 1; +} + +static inline struct scmi_powercap_zone * +scmi_powercap_get_parent_zone(struct scmi_powercap_zone *spz) +{ + if (spz->info->parent_id == SCMI_POWERCAP_ROOT_ZONE_ID) + return NULL; + + return &spz->spzones[spz->info->parent_id]; +} + +/** + * scmi_powercap_register_zone - Register an SCMI powercap zone recursively + * + * @pr: A reference to the root powercap zones descriptors + * @spz: A reference to the SCMI powercap zone to register + * + * When registering SCMI powercap zones with the powercap framework we should + * take care to always register zones starting from the root ones and to + * deregister starting from the leaves. + * + * Unfortunately we cannot assume that the array of available SCMI powercap + * zones provided by the SCMI platform firmware is built to comply with such + * requirement. + * + * This function, given an SCMI powercap zone to register, takes care to walk + * the SCMI powercap zones tree up to the root looking recursively for + * unregistered parent zones before registering the provided zone; at the same + * time each registered zone height in such a tree is accounted for and each + * zone, once registered, is stored in the @registered_zones array that is + * indexed by zone height: this way will be trivial, at unregister time, to walk + * the @registered_zones array backward and unregister all the zones starting + * from the leaves, removing children zones before parents. + * + * While doing this, we prune away any zone marked as invalid (like the ones + * sporting an SCMI abstract power scale) as long as they are positioned as + * leaves in the SCMI powercap zones hierarchy: any non-leaf invalid zone causes + * the entire process to fail since we cannot assume the correctness of an SCMI + * powercap zones hierarchy if some of the internal nodes are missing. + * + * Note that the array of SCMI powercap zones as returned by the SCMI platform + * is known to be sane, i.e. zones relationships have been validated at the + * protocol layer. + * + * Return: 0 on Success + */ +static int scmi_powercap_register_zone(struct scmi_powercap_root *pr, + struct scmi_powercap_zone *spz) +{ + int ret = 0; + struct scmi_powercap_zone *parent; + + if (!spz->info) + return ret; + + parent = scmi_powercap_get_parent_zone(spz); + if (parent && !scmi_powercap_is_zone_registered(parent)) { + /* + * Bail out if a parent domain was marked as unsupported: + * only domains participating as leaves can be skipped. + */ + if (!parent->info) + return -ENODEV; + + ret = scmi_powercap_register_zone(pr, parent); + if (ret) + return ret; + } + + if (!scmi_powercap_is_zone_registered(spz)) { + struct powercap_zone *z; + + z = powercap_register_zone(&spz->zone, + scmi_top_pcntrl, + spz->info->name, + parent ? &parent->zone : NULL, + &zone_ops, 1, &constraint_ops); + if (!IS_ERR(z)) { + spz->height = scmi_powercap_get_zone_height(spz); + list_add(&spz->node, + &pr->registered_zones[spz->height]); + dev_dbg(spz->dev, + "Registered node %s - parent %s - height:%d\n", + spz->info->name, + parent ? parent->info->name : "ROOT", + spz->height); + ret = 0; + } else { + ret = PTR_ERR(z); + dev_err(spz->dev, + "Error registering node:%s - parent:%s - h:%d - ret:%d\n", + spz->info->name, + parent ? parent->info->name : "ROOT", + spz->height, ret); + } + } + + return ret; +} + +static int scmi_powercap_probe(struct scmi_device *sdev) +{ + int ret, i; + struct scmi_powercap_root *pr; + struct scmi_powercap_zone *spz; + struct scmi_protocol_handle *ph; + struct device *dev = &sdev->dev; + + if (!sdev->handle) + return -ENODEV; + + powercap_ops = sdev->handle->devm_protocol_get(sdev, + SCMI_PROTOCOL_POWERCAP, + &ph); + if (IS_ERR(powercap_ops)) + return PTR_ERR(powercap_ops); + + pr = devm_kzalloc(dev, sizeof(*pr), GFP_KERNEL); + if (!pr) + return -ENOMEM; + + ret = powercap_ops->num_domains_get(ph); + if (ret < 0) { + dev_err(dev, "number of powercap domains not found\n"); + return ret; + } + pr->num_zones = ret; + + pr->spzones = devm_kcalloc(dev, pr->num_zones, + sizeof(*pr->spzones), GFP_KERNEL); + if (!pr->spzones) + return -ENOMEM; + + /* Allocate for worst possible scenario of maximum tree height. */ + pr->registered_zones = devm_kcalloc(dev, pr->num_zones, + sizeof(*pr->registered_zones), + GFP_KERNEL); + if (!pr->registered_zones) + return -ENOMEM; + + for (i = 0, spz = pr->spzones; i < pr->num_zones; i++, spz++) { + /* + * Powercap domains are validate by the protocol layer, i.e. + * when only non-NULL domains are returned here, whose + * parent_id is assured to point to another valid domain. + */ + spz->info = powercap_ops->info_get(ph, i); + + spz->dev = dev; + spz->ph = ph; + spz->spzones = pr->spzones; + INIT_LIST_HEAD(&spz->node); + INIT_LIST_HEAD(&pr->registered_zones[i]); + + /* + * Forcibly skip powercap domains using an abstract scale. + * Note that only leaves domains can be skipped, so this could + * lead later to a global failure. + */ + if (!spz->info->powercap_scale_uw && + !spz->info->powercap_scale_mw) { + dev_warn(dev, + "Abstract power scale not supported. Skip %s.\n", + spz->info->name); + spz->info = NULL; + continue; + } + } + + /* + * Scan array of retrieved SCMI powercap domains and register them + * recursively starting from the root domains. + */ + for (i = 0, spz = pr->spzones; i < pr->num_zones; i++, spz++) { + ret = scmi_powercap_register_zone(pr, spz); + if (ret) { + dev_err(dev, + "Failed to register powercap zone %s - ret:%d\n", + spz->info->name, ret); + scmi_powercap_unregister_all_zones(pr); + return ret; + } + } + + dev_set_drvdata(dev, pr); + + dev_info(dev, "Registered %d SCMI Powercap domains !\n", pr->num_zones); + + return ret; +} + +static void scmi_powercap_remove(struct scmi_device *sdev) +{ + struct device *dev = &sdev->dev; + struct scmi_powercap_root *pr = dev_get_drvdata(dev); + + scmi_powercap_unregister_all_zones(pr); +} + +static const struct scmi_device_id scmi_id_table[] = { + { SCMI_PROTOCOL_POWERCAP, "powercap" }, + { }, +}; +MODULE_DEVICE_TABLE(scmi, scmi_id_table); + +static struct scmi_driver scmi_powercap_driver = { + .name = "scmi-powercap", + .probe = scmi_powercap_probe, + .remove = scmi_powercap_remove, + .id_table = scmi_id_table, +}; + +static int __init scmi_powercap_init(void) +{ + int ret; + + scmi_top_pcntrl = powercap_register_control_type(NULL, "arm-scmi", NULL); + if (IS_ERR(scmi_top_pcntrl)) + return PTR_ERR(scmi_top_pcntrl); + + ret = scmi_register(&scmi_powercap_driver); + if (ret) + powercap_unregister_control_type(scmi_top_pcntrl); + + return ret; +} +module_init(scmi_powercap_init); + +static void __exit scmi_powercap_exit(void) +{ + scmi_unregister(&scmi_powercap_driver); + + powercap_unregister_control_type(scmi_top_pcntrl); +} +module_exit(scmi_powercap_exit); + +MODULE_AUTHOR("Cristian Marussi "); +MODULE_DESCRIPTION("ARM SCMI Powercap driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 75aa7915d103ac2cf8c1cf16392673a81f8afc35 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 12 Aug 2022 11:36:02 +0530 Subject: MAINTAINERS: Make Manivannan Sadhasivam the maintainer of qcom_edac The current maintainers have left Qualcomm and their email addresses were bouncing. Since I couldn't get hold of them now, I'm volunteering myself to maintain this driver. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Borislav Petkov Acked-by: Sai Prakash Ranjan Link: https://lore.kernel.org/r/20220812060602.7672-4-manivannan.sadhasivam@linaro.org --- MAINTAINERS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..6d75828ea7b2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7475,8 +7475,7 @@ S: Maintained F: drivers/edac/pnd2_edac.[ch] EDAC-QCOM -M: Channagoud Kadabi -M: Venkata Narendra Kumar Gutta +M: Manivannan Sadhasivam L: linux-arm-msm@vger.kernel.org L: linux-edac@vger.kernel.org S: Maintained -- cgit v1.2.3 From e6629dcb00adeebcfeaee45b1c987a84eb3ce1ba Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 26 Oct 2022 19:33:28 +0800 Subject: scsi: MAINTAINERS: Make Xiang Chen HiSilicon SAS controller driver maintainer I am soon leaving Huawei, so will no longer maintain this driver. However I will stay active in upstream Linux storage domain. Xiang Chen has worked on the driver for as long as I have and has good knowledge of the driver, so should do a good job. Signed-off-by: John Garry Link: https://lore.kernel.org/r/1666784008-125519-1-git-send-email-john.garry@huawei.com Signed-off-by: Martin K. Petersen --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..174de5e8d4c8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9290,7 +9290,7 @@ F: Documentation/devicetree/bindings/infiniband/hisilicon-hns-roce.txt F: drivers/infiniband/hw/hns/ HISILICON SAS Controller -M: John Garry +M: Xiang Chen S: Supported W: http://www.hisilicon.com F: Documentation/devicetree/bindings/scsi/hisilicon-sas.txt -- cgit v1.2.3 From a96dfea1df25bf22f4b02080a85ac87f7a3977d0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 16 Oct 2022 09:15:04 +0300 Subject: media: dt-bindings: Convert imx290.txt to YAML Convert the Sony IMX290 DT binding from text to YAML. Add Manivannan as a maintainer given that he is listed in MAINTAINERS for the file, as volunteering myself. The name of the input clock, "xclk", is wrong as the hardware manual names it INCK. As the device has a single clock, the name could be omitted, but that would require a corresponding change to the driver and is thus a candidate for further patches. Signed-off-by: Laurent Pinchart Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sakari Ailus --- .../devicetree/bindings/media/i2c/imx290.txt | 57 --------- .../devicetree/bindings/media/i2c/sony,imx290.yaml | 129 +++++++++++++++++++++ MAINTAINERS | 2 +- 3 files changed, 130 insertions(+), 58 deletions(-) delete mode 100644 Documentation/devicetree/bindings/media/i2c/imx290.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml (limited to 'MAINTAINERS') diff --git a/Documentation/devicetree/bindings/media/i2c/imx290.txt b/Documentation/devicetree/bindings/media/i2c/imx290.txt deleted file mode 100644 index a3cc21410f7c..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/imx290.txt +++ /dev/null @@ -1,57 +0,0 @@ -* Sony IMX290 1/2.8-Inch CMOS Image Sensor - -The Sony IMX290 is a 1/2.8-Inch CMOS Solid-state image sensor with -Square Pixel for Color Cameras. It is programmable through I2C and 4-wire -interfaces. The sensor output is available via CMOS logic parallel SDR output, -Low voltage LVDS DDR output and CSI-2 serial data output. The CSI-2 bus is the -default. No bindings have been defined for the other busses. - -Required Properties: -- compatible: Should be "sony,imx290" -- reg: I2C bus address of the device -- clocks: Reference to the xclk clock. -- clock-names: Should be "xclk". -- clock-frequency: Frequency of the xclk clock in Hz. -- vdddo-supply: Sensor digital IO regulator. -- vdda-supply: Sensor analog regulator. -- vddd-supply: Sensor digital core regulator. - -Optional Properties: -- reset-gpios: Sensor reset GPIO - -The imx290 device node should contain one 'port' child node with -an 'endpoint' subnode. For further reading on port node refer to -Documentation/devicetree/bindings/media/video-interfaces.txt. - -Required Properties on endpoint: -- data-lanes: check ../video-interfaces.txt -- link-frequencies: check ../video-interfaces.txt -- remote-endpoint: check ../video-interfaces.txt - -Example: - &i2c1 { - ... - imx290: camera-sensor@1a { - compatible = "sony,imx290"; - reg = <0x1a>; - - reset-gpios = <&msmgpio 35 GPIO_ACTIVE_LOW>; - pinctrl-names = "default"; - pinctrl-0 = <&camera_rear_default>; - - clocks = <&gcc GCC_CAMSS_MCLK0_CLK>; - clock-names = "xclk"; - clock-frequency = <37125000>; - - vdddo-supply = <&camera_vdddo_1v8>; - vdda-supply = <&camera_vdda_2v8>; - vddd-supply = <&camera_vddd_1v5>; - - port { - imx290_ep: endpoint { - data-lanes = <1 2 3 4>; - link-frequencies = /bits/ 64 <445500000>; - remote-endpoint = <&csiphy0_ep>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml new file mode 100644 index 000000000000..21377daae026 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml @@ -0,0 +1,129 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/sony,imx290.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sony IMX290 1/2.8-Inch CMOS Image Sensor + +maintainers: + - Manivannan Sadhasivam + - Laurent Pinchart + +description: |- + The Sony IMX290 is a 1/2.8-Inch CMOS Solid-state image sensor with Square + Pixel for Color Cameras. It is programmable through I2C and 4-wire + interfaces. The sensor output is available via CMOS logic parallel SDR + output, Low voltage LVDS DDR output and CSI-2 serial data output. The CSI-2 + bus is the default. No bindings have been defined for the other busses. + +properties: + compatible: + enum: + - sony,imx290 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + description: Input clock (37.125 MHz or 74.25 MHz) + items: + - const: xclk + + clock-frequency: + description: Frequency of the xclk clock in Hz + + vdda-supply: + description: Analog power supply (2.9V) + + vddd-supply: + description: Digital core power supply (1.2V) + + vdddo-supply: + description: Digital I/O power supply (1.8V) + + reset-gpios: + description: Sensor reset (XCLR) GPIO + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + description: | + Video output port + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + anyOf: + - items: + - const: 1 + - const: 2 + - items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + + link-frequencies: true + + required: + - data-lanes + - link-frequencies + + additionalProperties: false + +required: + - compatible + - reg + - clocks + - clock-names + - clock-frequency + - vdda-supply + - vddd-supply + - vdddo-supply + - port + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + imx290: camera-sensor@1a { + compatible = "sony,imx290"; + reg = <0x1a>; + + pinctrl-names = "default"; + pinctrl-0 = <&camera_rear_default>; + + clocks = <&gcc 90>; + clock-names = "xclk"; + clock-frequency = <37125000>; + + vdddo-supply = <&camera_vdddo_1v8>; + vdda-supply = <&camera_vdda_2v8>; + vddd-supply = <&camera_vddd_1v5>; + + reset-gpios = <&msmgpio 35 GPIO_ACTIVE_LOW>; + + port { + imx290_ep: endpoint { + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <445500000>; + remote-endpoint = <&csiphy0_ep>; + }; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 1e4f85bd903e..498e6dc45efc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19207,7 +19207,7 @@ M: Manivannan Sadhasivam L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git -F: Documentation/devicetree/bindings/media/i2c/imx290.txt +F: Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml F: drivers/media/i2c/imx290.c SONY IMX319 SENSOR DRIVER -- cgit v1.2.3 From e803188400d32d28ecfbef0878c289e3c7026723 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 27 Sep 2022 07:54:54 +0000 Subject: crypto: rockchip - add myself as maintainer Nobody is set as maintainer of rockchip crypto, I propose to do it as I have already reworked lot of this code. Reviewed-by: John Keeping Signed-off-by: Corentin Labbe Signed-off-by: Herbert Xu --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..3489126acd1f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17753,6 +17753,13 @@ F: Documentation/ABI/*/sysfs-driver-hid-roccat* F: drivers/hid/hid-roccat* F: include/linux/hid-roccat* +ROCKCHIP CRYPTO DRIVERS +M: Corentin Labbe +L: linux-crypto@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/crypto/rockchip,rk3288-crypto.yaml +F: drivers/crypto/rockchip/ + ROCKCHIP I2S TDM DRIVER M: Nicolas Frattaroli L: linux-rockchip@lists.infradead.org -- cgit v1.2.3 From c27ce08b806d606cd5cd0e8252d1ed2b729b5b55 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 20 Sep 2022 19:48:55 +0100 Subject: dt-bindings: soc: renesas: Move renesas.yaml from arm to soc renesas.yaml lists out all the Renesas SoC's and the platforms/EVK's which is either ARM32/ARM64. It would rather make sense if we move renesas.yaml to the soc/renesas folder instead. This is in preparation for adding a new SoC (RZ/Five) from Renesas which is based on RISC-V. While at it drop the old entry for renesas.yaml from MAINTAINERS file and there is no need to update the new file path of renesas.yaml as we already have an entry for Documentation/devicetree/bindings/soc/renesas/ folder. Signed-off-by: Lad Prabhakar Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220920184904.90495-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- Documentation/devicetree/bindings/arm/renesas.yaml | 471 --------------------- .../devicetree/bindings/soc/renesas/renesas.yaml | 471 +++++++++++++++++++++ MAINTAINERS | 1 - 3 files changed, 471 insertions(+), 472 deletions(-) delete mode 100644 Documentation/devicetree/bindings/arm/renesas.yaml create mode 100644 Documentation/devicetree/bindings/soc/renesas/renesas.yaml (limited to 'MAINTAINERS') diff --git a/Documentation/devicetree/bindings/arm/renesas.yaml b/Documentation/devicetree/bindings/arm/renesas.yaml deleted file mode 100644 index f51464a08aff..000000000000 --- a/Documentation/devicetree/bindings/arm/renesas.yaml +++ /dev/null @@ -1,471 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/arm/renesas.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Renesas SH-Mobile, R-Mobile, and R-Car Platform - -maintainers: - - Geert Uytterhoeven - -properties: - $nodename: - const: '/' - compatible: - oneOf: - - description: Emma Mobile EV2 - items: - - enum: - - renesas,kzm9d # Kyoto Microcomputer Co. KZM-A9-Dual - - const: renesas,emev2 - - - description: RZ/A1H (R7S72100) - items: - - enum: - - renesas,genmai # Genmai (RTK772100BC00000BR) - - renesas,gr-peach # GR-Peach (X28A-M01-E/F) - - renesas,rskrza1 # RSKRZA1 (YR0K77210C000BE) - - const: renesas,r7s72100 - - - description: RZ/A2 (R7S9210) - items: - - enum: - - renesas,rza2mevb # RZ/A2M Eval Board (RTK7921053S00000BE) - - const: renesas,r7s9210 - - - description: SH-Mobile AG5 (R8A73A00/SH73A0) - items: - - enum: - - renesas,kzm9g # Kyoto Microcomputer Co. KZM-A9-GT - - const: renesas,sh73a0 - - - description: R-Mobile APE6 (R8A73A40) - items: - - enum: - - renesas,ape6evm - - const: renesas,r8a73a4 - - - description: R-Mobile A1 (R8A77400) - items: - - enum: - - renesas,armadillo800eva # Atmark Techno Armadillo-800 EVA - - const: renesas,r8a7740 - - - description: RZ/G1H (R8A77420) - items: - - enum: - # iWave Systems RZ/G1H Qseven System On Module (iW-RainboW-G21M-Qseven) - - iwave,g21m - - const: renesas,r8a7742 - - - items: - - enum: - # iWave Systems RZ/G1H Qseven Development Platform (iW-RainboW-G21D-Qseven) - - iwave,g21d - - const: iwave,g21m - - const: renesas,r8a7742 - - - description: RZ/G1M (R8A77430) - items: - - enum: - # iWave Systems RZ/G1M Qseven Development Platform (iW-RainboW-G20D-Qseven) - - iwave,g20d - - const: iwave,g20m - - const: renesas,r8a7743 - - - items: - - enum: - # iWave Systems RZ/G1M Qseven System On Module (iW-RainboW-G20M-Qseven) - - iwave,g20m - - renesas,sk-rzg1m # SK-RZG1M (YR8A77430S000BE) - - const: renesas,r8a7743 - - - description: RZ/G1N (R8A77440) - items: - - enum: - # iWave Systems RZ/G1N Qseven Development Platform (iW-RainboW-G20D-Qseven) - - iwave,g20d - - const: iwave,g20m - - const: renesas,r8a7744 - - - items: - - enum: - # iWave Systems RZ/G1N Qseven System On Module (iW-RainboW-G20M-Qseven) - - iwave,g20m - - const: renesas,r8a7744 - - - description: RZ/G1E (R8A77450) - items: - - enum: - - iwave,g22m # iWave Systems RZ/G1E SODIMM System On Module (iW-RainboW-G22M-SM) - - renesas,sk-rzg1e # SK-RZG1E (YR8A77450S000BE) - - const: renesas,r8a7745 - - - description: iWave Systems RZ/G1E SODIMM SOM Development Platform (iW-RainboW-G22D) - items: - - const: iwave,g22d - - const: iwave,g22m - - const: renesas,r8a7745 - - - description: RZ/G1C (R8A77470) - items: - - enum: - - iwave,g23s #iWave Systems RZ/G1C Single Board Computer (iW-RainboW-G23S) - - const: renesas,r8a77470 - - - description: RZ/G2M (R8A774A1) - items: - - enum: - - hoperun,hihope-rzg2m # HopeRun HiHope RZ/G2M platform - - beacon,beacon-rzg2m # Beacon EmbeddedWorks RZ/G2M Kit - - const: renesas,r8a774a1 - - - items: - - enum: - - hoperun,hihope-rzg2-ex # HopeRun expansion board for HiHope RZ/G2 platforms - - const: hoperun,hihope-rzg2m - - const: renesas,r8a774a1 - - - description: RZ/G2N (R8A774B1) - items: - - enum: - - beacon,beacon-rzg2n # Beacon EmbeddedWorks RZ/G2N Kit - - hoperun,hihope-rzg2n # HopeRun HiHope RZ/G2N platform - - const: renesas,r8a774b1 - - - items: - - enum: - - hoperun,hihope-rzg2-ex # HopeRun expansion board for HiHope RZ/G2 platforms - - const: hoperun,hihope-rzg2n - - const: renesas,r8a774b1 - - - description: RZ/G2E (R8A774C0) - items: - - enum: - - si-linux,cat874 # Silicon Linux RZ/G2E 96board platform (CAT874) - - const: renesas,r8a774c0 - - - items: - - enum: - - si-linux,cat875 # Silicon Linux sub board for CAT874 (CAT875) - - const: si-linux,cat874 - - const: renesas,r8a774c0 - - - description: RZ/G2H (R8A774E1) - items: - - enum: - - beacon,beacon-rzg2h # Beacon EmbeddedWorks RZ/G2H Kit - - hoperun,hihope-rzg2h # HopeRun HiHope RZ/G2H platform - - const: renesas,r8a774e1 - - - items: - - enum: - - hoperun,hihope-rzg2-ex # HopeRun expansion board for HiHope RZ/G2 platforms - - const: hoperun,hihope-rzg2h - - const: renesas,r8a774e1 - - - description: R-Car M1A (R8A77781) - items: - - enum: - - renesas,bockw - - const: renesas,r8a7778 - - - description: R-Car H1 (R8A77790) - items: - - enum: - - renesas,marzen # Marzen (R0P7779A00010S) - - const: renesas,r8a7779 - - - description: R-Car H2 (R8A77900) - items: - - enum: - - renesas,lager # Lager (RTP0RC7790SEB00010S) - - renesas,stout # Stout (ADAS Starterkit, Y-R-CAR-ADAS-SKH2-BOARD) - - const: renesas,r8a7790 - - - description: R-Car M2-W (R8A77910) - items: - - enum: - - renesas,henninger - - renesas,koelsch # Koelsch (RTP0RC7791SEB00010S) - - renesas,porter # Porter (M2-LCDP) - - const: renesas,r8a7791 - - - description: R-Car V2H (R8A77920) - items: - - enum: - - renesas,blanche # Blanche (RTP0RC7792SEB00010S) - - renesas,wheat # Wheat (RTP0RC7792ASKB0000JE) - - const: renesas,r8a7792 - - - description: R-Car M2-N (R8A77930) - items: - - enum: - - renesas,gose # Gose (RTP0RC7793SEB00010S) - - const: renesas,r8a7793 - - - description: R-Car E2 (R8A77940) - items: - - enum: - - renesas,alt # Alt (RTP0RC7794SEB00010S) - - renesas,silk # SILK (RTP0RC7794LCB00011S) - - const: renesas,r8a7794 - - - description: R-Car H3 (R8A77950) - items: - - enum: - # H3ULCB (R-Car Starter Kit Premier, RTP0RC7795SKBX0010SA00 (H3 ES1.1)) - # H3ULCB (R-Car Starter Kit Premier, RTP0RC77951SKBX010SA00 (H3 ES2.0)) - - renesas,h3ulcb - - renesas,salvator-x # Salvator-X (RTP0RC7795SIPB0010S) - - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version, RTP0RC7795SIPB0012S) - - const: renesas,r8a7795 - - - description: R-Car M3-W (R8A77960) - items: - - enum: - - renesas,m3ulcb # M3ULCB (R-Car Starter Kit Pro, RTP0RC7796SKBX0010SA09 (M3 ES1.0)) - - renesas,salvator-x # Salvator-X (RTP0RC7796SIPB0011S) - - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version, RTP0RC7796SIPB0012S) - - const: renesas,r8a7796 - - - description: R-Car M3-W+ (R8A77961) - items: - - enum: - - renesas,m3ulcb # M3ULCB (R-Car Starter Kit Pro, RTP8J77961ASKB0SK0SA05A (M3 ES3.0)) - - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version, RTP0RC7796SIPB0012SA5A) - - const: renesas,r8a77961 - - - description: Kingfisher (SBEV-RCAR-KF-M03) - oneOf: - - items: - - const: shimafuji,kingfisher - - enum: - - renesas,h3ulcb - - renesas,m3ulcb - - renesas,m3nulcb - - enum: - - renesas,r8a7795 - - renesas,r8a7796 - - renesas,r8a77961 - - renesas,r8a77965 - - items: - - const: shimafuji,kingfisher - - enum: - - renesas,h3ulcb - - renesas,m3ulcb - - renesas,m3nulcb - - enum: - - renesas,r8a779m0 - - renesas,r8a779m1 - - renesas,r8a779m2 - - renesas,r8a779m3 - - renesas,r8a779m4 - - renesas,r8a779m5 - - renesas,r8a779m8 - - renesas,r8a779mb - - enum: - - renesas,r8a7795 - - renesas,r8a77961 - - renesas,r8a77965 - - - description: R-Car M3-N (R8A77965) - items: - - enum: - - renesas,m3nulcb # M3NULCB (R-Car Starter Kit Pro, RTP0RC77965SKBX010SA00 (M3-N ES1.1)) - - renesas,salvator-x # Salvator-X (RTP0RC7796SIPB0011S (M3-N)) - - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version, RTP0RC77965SIPB012S) - - const: renesas,r8a77965 - - - description: R-Car V3M (R8A77970) - items: - - enum: - - renesas,eagle # Eagle (RTP0RC77970SEB0010S) - - renesas,v3msk # V3MSK (Y-ASK-RCAR-V3M-WS10) - - const: renesas,r8a77970 - - - description: R-Car V3H (R8A77980) - items: - - enum: - - renesas,condor # Condor (RTP0RC77980SEB0010SS/RTP0RC77980SEB0010SA01) - - renesas,v3hsk # V3HSK (Y-ASK-RCAR-V3H-WS10) - - const: renesas,r8a77980 - - - description: R-Car V3H2 (R8A77980A) - items: - - enum: - - renesas,condor-i # Condor-I (RTP0RC77980SEBS012SA01) - - const: renesas,r8a77980a - - const: renesas,r8a77980 - - - description: R-Car E3 (R8A77990) - items: - - enum: - - renesas,ebisu # Ebisu (RTP0RC77990SEB0010S) - - const: renesas,r8a77990 - - - description: R-Car D3 (R8A77995) - items: - - enum: - - renesas,draak # Draak (RTP0RC77995SEB0010S) - - const: renesas,r8a77995 - - - description: R-Car V3U (R8A779A0) - items: - - enum: - - renesas,falcon-cpu # Falcon CPU board (RTP0RC779A0CPB0010S) - - const: renesas,r8a779a0 - - - items: - - enum: - - renesas,falcon-breakout # Falcon BreakOut board (RTP0RC779A0BOB0010S) - - const: renesas,falcon-cpu - - const: renesas,r8a779a0 - - - description: R-Car S4-8 (R8A779F0) - items: - - enum: - - renesas,spider-cpu # Spider CPU board (RTP8A779F0ASKB0SC2S) - - const: renesas,r8a779f0 - - - items: - - enum: - - renesas,spider-breakout # Spider BreakOut board (RTP8A779F0ASKB0SB0S) - - const: renesas,spider-cpu - - const: renesas,r8a779f0 - - - description: R-Car V4H (R8A779G0) - items: - - enum: - - renesas,white-hawk-cpu # White Hawk CPU board (RTP8A779G0ASKB0FC0SA000) - - const: renesas,r8a779g0 - - - items: - - enum: - - renesas,white-hawk-breakout # White Hawk BreakOut board (RTP8A779G0ASKB0SB0SA000) - - const: renesas,white-hawk-cpu - - const: renesas,r8a779g0 - - - description: R-Car H3e (R8A779M0) - items: - - enum: - - renesas,h3ulcb # H3ULCB (R-Car Starter Kit Premier) - - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version) - - const: renesas,r8a779m0 - - const: renesas,r8a7795 - - - description: R-Car H3e-2G (R8A779M1) - items: - - enum: - - renesas,h3ulcb # H3ULCB (R-Car Starter Kit Premier) - - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version) - - const: renesas,r8a779m1 - - const: renesas,r8a7795 - - - description: R-Car M3e (R8A779M2) - items: - - enum: - - renesas,m3ulcb # M3ULCB (R-Car Starter Kit Pro) - - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version) - - const: renesas,r8a779m2 - - const: renesas,r8a77961 - - - description: R-Car M3e-2G (R8A779M3) - items: - - enum: - - renesas,m3ulcb # M3ULCB (R-Car Starter Kit Pro) - - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version) - - const: renesas,r8a779m3 - - const: renesas,r8a77961 - - - description: R-Car M3Ne (R8A779M4) - items: - - enum: - - renesas,m3nulcb # M3NULCB (R-Car Starter Kit Pro) - - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version) - - const: renesas,r8a779m4 - - const: renesas,r8a77965 - - - description: R-Car M3Ne-2G (R8A779M5) - items: - - enum: - - renesas,m3nulcb # M3NULCB (R-Car Starter Kit Pro) - - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version) - - const: renesas,r8a779m5 - - const: renesas,r8a77965 - - - description: R-Car E3e (R8A779M6) - items: - - enum: - - renesas,ebisu # Ebisu - - const: renesas,r8a779m6 - - const: renesas,r8a77990 - - - description: R-Car D3e (R8A779M7) - items: - - enum: - - renesas,draak # Draak - - const: renesas,r8a779m7 - - const: renesas,r8a77995 - - - description: R-Car H3Ne (R8A779M8) - items: - - enum: - - renesas,h3ulcb # H3ULCB (R-Car Starter Kit Premier) - - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version) - - const: renesas,r8a779m8 - - const: renesas,r8a7795 - - - description: R-Car H3Ne-1.7G (R8A779MB) - items: - - enum: - - renesas,h3ulcb # H3ULCB (R-Car Starter Kit Premier) - - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version) - - const: renesas,r8a779mb - - const: renesas,r8a7795 - - - description: RZ/N1D (R9A06G032) - items: - - enum: - - renesas,rzn1d400-db # RZN1D-DB (RZ/N1D Demo Board for the RZ/N1D 400 pins package) - - const: renesas,r9a06g032 - - - description: RZ/G2UL (R9A07G043) - items: - - enum: - - renesas,smarc-evk # SMARC EVK - - enum: - - renesas,r9a07g043u11 # RZ/G2UL Type-1 - - renesas,r9a07g043u12 # RZ/G2UL Type-2 - - const: renesas,r9a07g043 - - - description: RZ/G2{L,LC} (R9A07G044) - items: - - enum: - - renesas,smarc-evk # SMARC EVK - - enum: - - renesas,r9a07g044c1 # Single Cortex-A55 RZ/G2LC - - renesas,r9a07g044c2 # Dual Cortex-A55 RZ/G2LC - - renesas,r9a07g044l1 # Single Cortex-A55 RZ/G2L - - renesas,r9a07g044l2 # Dual Cortex-A55 RZ/G2L - - const: renesas,r9a07g044 - - - description: RZ/V2L (R9A07G054) - items: - - enum: - - renesas,smarc-evk # SMARC EVK - - enum: - - renesas,r9a07g054l1 # Single Cortex-A55 RZ/V2L - - renesas,r9a07g054l2 # Dual Cortex-A55 RZ/V2L - - const: renesas,r9a07g054 - - - description: RZ/V2M (R9A09G011) - items: - - enum: - - renesas,rzv2mevk2 # RZ/V2M Eval Board v2.0 - - const: renesas,r9a09g011 - -additionalProperties: true - -... diff --git a/Documentation/devicetree/bindings/soc/renesas/renesas.yaml b/Documentation/devicetree/bindings/soc/renesas/renesas.yaml new file mode 100644 index 000000000000..07c5e6ebd5a0 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/renesas/renesas.yaml @@ -0,0 +1,471 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/renesas/renesas.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas SH-Mobile, R-Mobile, and R-Car Platform + +maintainers: + - Geert Uytterhoeven + +properties: + $nodename: + const: '/' + compatible: + oneOf: + - description: Emma Mobile EV2 + items: + - enum: + - renesas,kzm9d # Kyoto Microcomputer Co. KZM-A9-Dual + - const: renesas,emev2 + + - description: RZ/A1H (R7S72100) + items: + - enum: + - renesas,genmai # Genmai (RTK772100BC00000BR) + - renesas,gr-peach # GR-Peach (X28A-M01-E/F) + - renesas,rskrza1 # RSKRZA1 (YR0K77210C000BE) + - const: renesas,r7s72100 + + - description: RZ/A2 (R7S9210) + items: + - enum: + - renesas,rza2mevb # RZ/A2M Eval Board (RTK7921053S00000BE) + - const: renesas,r7s9210 + + - description: SH-Mobile AG5 (R8A73A00/SH73A0) + items: + - enum: + - renesas,kzm9g # Kyoto Microcomputer Co. KZM-A9-GT + - const: renesas,sh73a0 + + - description: R-Mobile APE6 (R8A73A40) + items: + - enum: + - renesas,ape6evm + - const: renesas,r8a73a4 + + - description: R-Mobile A1 (R8A77400) + items: + - enum: + - renesas,armadillo800eva # Atmark Techno Armadillo-800 EVA + - const: renesas,r8a7740 + + - description: RZ/G1H (R8A77420) + items: + - enum: + # iWave Systems RZ/G1H Qseven System On Module (iW-RainboW-G21M-Qseven) + - iwave,g21m + - const: renesas,r8a7742 + + - items: + - enum: + # iWave Systems RZ/G1H Qseven Development Platform (iW-RainboW-G21D-Qseven) + - iwave,g21d + - const: iwave,g21m + - const: renesas,r8a7742 + + - description: RZ/G1M (R8A77430) + items: + - enum: + # iWave Systems RZ/G1M Qseven Development Platform (iW-RainboW-G20D-Qseven) + - iwave,g20d + - const: iwave,g20m + - const: renesas,r8a7743 + + - items: + - enum: + # iWave Systems RZ/G1M Qseven System On Module (iW-RainboW-G20M-Qseven) + - iwave,g20m + - renesas,sk-rzg1m # SK-RZG1M (YR8A77430S000BE) + - const: renesas,r8a7743 + + - description: RZ/G1N (R8A77440) + items: + - enum: + # iWave Systems RZ/G1N Qseven Development Platform (iW-RainboW-G20D-Qseven) + - iwave,g20d + - const: iwave,g20m + - const: renesas,r8a7744 + + - items: + - enum: + # iWave Systems RZ/G1N Qseven System On Module (iW-RainboW-G20M-Qseven) + - iwave,g20m + - const: renesas,r8a7744 + + - description: RZ/G1E (R8A77450) + items: + - enum: + - iwave,g22m # iWave Systems RZ/G1E SODIMM System On Module (iW-RainboW-G22M-SM) + - renesas,sk-rzg1e # SK-RZG1E (YR8A77450S000BE) + - const: renesas,r8a7745 + + - description: iWave Systems RZ/G1E SODIMM SOM Development Platform (iW-RainboW-G22D) + items: + - const: iwave,g22d + - const: iwave,g22m + - const: renesas,r8a7745 + + - description: RZ/G1C (R8A77470) + items: + - enum: + - iwave,g23s #iWave Systems RZ/G1C Single Board Computer (iW-RainboW-G23S) + - const: renesas,r8a77470 + + - description: RZ/G2M (R8A774A1) + items: + - enum: + - hoperun,hihope-rzg2m # HopeRun HiHope RZ/G2M platform + - beacon,beacon-rzg2m # Beacon EmbeddedWorks RZ/G2M Kit + - const: renesas,r8a774a1 + + - items: + - enum: + - hoperun,hihope-rzg2-ex # HopeRun expansion board for HiHope RZ/G2 platforms + - const: hoperun,hihope-rzg2m + - const: renesas,r8a774a1 + + - description: RZ/G2N (R8A774B1) + items: + - enum: + - beacon,beacon-rzg2n # Beacon EmbeddedWorks RZ/G2N Kit + - hoperun,hihope-rzg2n # HopeRun HiHope RZ/G2N platform + - const: renesas,r8a774b1 + + - items: + - enum: + - hoperun,hihope-rzg2-ex # HopeRun expansion board for HiHope RZ/G2 platforms + - const: hoperun,hihope-rzg2n + - const: renesas,r8a774b1 + + - description: RZ/G2E (R8A774C0) + items: + - enum: + - si-linux,cat874 # Silicon Linux RZ/G2E 96board platform (CAT874) + - const: renesas,r8a774c0 + + - items: + - enum: + - si-linux,cat875 # Silicon Linux sub board for CAT874 (CAT875) + - const: si-linux,cat874 + - const: renesas,r8a774c0 + + - description: RZ/G2H (R8A774E1) + items: + - enum: + - beacon,beacon-rzg2h # Beacon EmbeddedWorks RZ/G2H Kit + - hoperun,hihope-rzg2h # HopeRun HiHope RZ/G2H platform + - const: renesas,r8a774e1 + + - items: + - enum: + - hoperun,hihope-rzg2-ex # HopeRun expansion board for HiHope RZ/G2 platforms + - const: hoperun,hihope-rzg2h + - const: renesas,r8a774e1 + + - description: R-Car M1A (R8A77781) + items: + - enum: + - renesas,bockw + - const: renesas,r8a7778 + + - description: R-Car H1 (R8A77790) + items: + - enum: + - renesas,marzen # Marzen (R0P7779A00010S) + - const: renesas,r8a7779 + + - description: R-Car H2 (R8A77900) + items: + - enum: + - renesas,lager # Lager (RTP0RC7790SEB00010S) + - renesas,stout # Stout (ADAS Starterkit, Y-R-CAR-ADAS-SKH2-BOARD) + - const: renesas,r8a7790 + + - description: R-Car M2-W (R8A77910) + items: + - enum: + - renesas,henninger + - renesas,koelsch # Koelsch (RTP0RC7791SEB00010S) + - renesas,porter # Porter (M2-LCDP) + - const: renesas,r8a7791 + + - description: R-Car V2H (R8A77920) + items: + - enum: + - renesas,blanche # Blanche (RTP0RC7792SEB00010S) + - renesas,wheat # Wheat (RTP0RC7792ASKB0000JE) + - const: renesas,r8a7792 + + - description: R-Car M2-N (R8A77930) + items: + - enum: + - renesas,gose # Gose (RTP0RC7793SEB00010S) + - const: renesas,r8a7793 + + - description: R-Car E2 (R8A77940) + items: + - enum: + - renesas,alt # Alt (RTP0RC7794SEB00010S) + - renesas,silk # SILK (RTP0RC7794LCB00011S) + - const: renesas,r8a7794 + + - description: R-Car H3 (R8A77950) + items: + - enum: + # H3ULCB (R-Car Starter Kit Premier, RTP0RC7795SKBX0010SA00 (H3 ES1.1)) + # H3ULCB (R-Car Starter Kit Premier, RTP0RC77951SKBX010SA00 (H3 ES2.0)) + - renesas,h3ulcb + - renesas,salvator-x # Salvator-X (RTP0RC7795SIPB0010S) + - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version, RTP0RC7795SIPB0012S) + - const: renesas,r8a7795 + + - description: R-Car M3-W (R8A77960) + items: + - enum: + - renesas,m3ulcb # M3ULCB (R-Car Starter Kit Pro, RTP0RC7796SKBX0010SA09 (M3 ES1.0)) + - renesas,salvator-x # Salvator-X (RTP0RC7796SIPB0011S) + - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version, RTP0RC7796SIPB0012S) + - const: renesas,r8a7796 + + - description: R-Car M3-W+ (R8A77961) + items: + - enum: + - renesas,m3ulcb # M3ULCB (R-Car Starter Kit Pro, RTP8J77961ASKB0SK0SA05A (M3 ES3.0)) + - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version, RTP0RC7796SIPB0012SA5A) + - const: renesas,r8a77961 + + - description: Kingfisher (SBEV-RCAR-KF-M03) + oneOf: + - items: + - const: shimafuji,kingfisher + - enum: + - renesas,h3ulcb + - renesas,m3ulcb + - renesas,m3nulcb + - enum: + - renesas,r8a7795 + - renesas,r8a7796 + - renesas,r8a77961 + - renesas,r8a77965 + - items: + - const: shimafuji,kingfisher + - enum: + - renesas,h3ulcb + - renesas,m3ulcb + - renesas,m3nulcb + - enum: + - renesas,r8a779m0 + - renesas,r8a779m1 + - renesas,r8a779m2 + - renesas,r8a779m3 + - renesas,r8a779m4 + - renesas,r8a779m5 + - renesas,r8a779m8 + - renesas,r8a779mb + - enum: + - renesas,r8a7795 + - renesas,r8a77961 + - renesas,r8a77965 + + - description: R-Car M3-N (R8A77965) + items: + - enum: + - renesas,m3nulcb # M3NULCB (R-Car Starter Kit Pro, RTP0RC77965SKBX010SA00 (M3-N ES1.1)) + - renesas,salvator-x # Salvator-X (RTP0RC7796SIPB0011S (M3-N)) + - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version, RTP0RC77965SIPB012S) + - const: renesas,r8a77965 + + - description: R-Car V3M (R8A77970) + items: + - enum: + - renesas,eagle # Eagle (RTP0RC77970SEB0010S) + - renesas,v3msk # V3MSK (Y-ASK-RCAR-V3M-WS10) + - const: renesas,r8a77970 + + - description: R-Car V3H (R8A77980) + items: + - enum: + - renesas,condor # Condor (RTP0RC77980SEB0010SS/RTP0RC77980SEB0010SA01) + - renesas,v3hsk # V3HSK (Y-ASK-RCAR-V3H-WS10) + - const: renesas,r8a77980 + + - description: R-Car V3H2 (R8A77980A) + items: + - enum: + - renesas,condor-i # Condor-I (RTP0RC77980SEBS012SA01) + - const: renesas,r8a77980a + - const: renesas,r8a77980 + + - description: R-Car E3 (R8A77990) + items: + - enum: + - renesas,ebisu # Ebisu (RTP0RC77990SEB0010S) + - const: renesas,r8a77990 + + - description: R-Car D3 (R8A77995) + items: + - enum: + - renesas,draak # Draak (RTP0RC77995SEB0010S) + - const: renesas,r8a77995 + + - description: R-Car V3U (R8A779A0) + items: + - enum: + - renesas,falcon-cpu # Falcon CPU board (RTP0RC779A0CPB0010S) + - const: renesas,r8a779a0 + + - items: + - enum: + - renesas,falcon-breakout # Falcon BreakOut board (RTP0RC779A0BOB0010S) + - const: renesas,falcon-cpu + - const: renesas,r8a779a0 + + - description: R-Car S4-8 (R8A779F0) + items: + - enum: + - renesas,spider-cpu # Spider CPU board (RTP8A779F0ASKB0SC2S) + - const: renesas,r8a779f0 + + - items: + - enum: + - renesas,spider-breakout # Spider BreakOut board (RTP8A779F0ASKB0SB0S) + - const: renesas,spider-cpu + - const: renesas,r8a779f0 + + - description: R-Car V4H (R8A779G0) + items: + - enum: + - renesas,white-hawk-cpu # White Hawk CPU board (RTP8A779G0ASKB0FC0SA000) + - const: renesas,r8a779g0 + + - items: + - enum: + - renesas,white-hawk-breakout # White Hawk BreakOut board (RTP8A779G0ASKB0SB0SA000) + - const: renesas,white-hawk-cpu + - const: renesas,r8a779g0 + + - description: R-Car H3e (R8A779M0) + items: + - enum: + - renesas,h3ulcb # H3ULCB (R-Car Starter Kit Premier) + - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version) + - const: renesas,r8a779m0 + - const: renesas,r8a7795 + + - description: R-Car H3e-2G (R8A779M1) + items: + - enum: + - renesas,h3ulcb # H3ULCB (R-Car Starter Kit Premier) + - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version) + - const: renesas,r8a779m1 + - const: renesas,r8a7795 + + - description: R-Car M3e (R8A779M2) + items: + - enum: + - renesas,m3ulcb # M3ULCB (R-Car Starter Kit Pro) + - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version) + - const: renesas,r8a779m2 + - const: renesas,r8a77961 + + - description: R-Car M3e-2G (R8A779M3) + items: + - enum: + - renesas,m3ulcb # M3ULCB (R-Car Starter Kit Pro) + - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version) + - const: renesas,r8a779m3 + - const: renesas,r8a77961 + + - description: R-Car M3Ne (R8A779M4) + items: + - enum: + - renesas,m3nulcb # M3NULCB (R-Car Starter Kit Pro) + - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version) + - const: renesas,r8a779m4 + - const: renesas,r8a77965 + + - description: R-Car M3Ne-2G (R8A779M5) + items: + - enum: + - renesas,m3nulcb # M3NULCB (R-Car Starter Kit Pro) + - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version) + - const: renesas,r8a779m5 + - const: renesas,r8a77965 + + - description: R-Car E3e (R8A779M6) + items: + - enum: + - renesas,ebisu # Ebisu + - const: renesas,r8a779m6 + - const: renesas,r8a77990 + + - description: R-Car D3e (R8A779M7) + items: + - enum: + - renesas,draak # Draak + - const: renesas,r8a779m7 + - const: renesas,r8a77995 + + - description: R-Car H3Ne (R8A779M8) + items: + - enum: + - renesas,h3ulcb # H3ULCB (R-Car Starter Kit Premier) + - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version) + - const: renesas,r8a779m8 + - const: renesas,r8a7795 + + - description: R-Car H3Ne-1.7G (R8A779MB) + items: + - enum: + - renesas,h3ulcb # H3ULCB (R-Car Starter Kit Premier) + - renesas,salvator-xs # Salvator-XS (Salvator-X 2nd version) + - const: renesas,r8a779mb + - const: renesas,r8a7795 + + - description: RZ/N1D (R9A06G032) + items: + - enum: + - renesas,rzn1d400-db # RZN1D-DB (RZ/N1D Demo Board for the RZ/N1D 400 pins package) + - const: renesas,r9a06g032 + + - description: RZ/G2UL (R9A07G043) + items: + - enum: + - renesas,smarc-evk # SMARC EVK + - enum: + - renesas,r9a07g043u11 # RZ/G2UL Type-1 + - renesas,r9a07g043u12 # RZ/G2UL Type-2 + - const: renesas,r9a07g043 + + - description: RZ/G2{L,LC} (R9A07G044) + items: + - enum: + - renesas,smarc-evk # SMARC EVK + - enum: + - renesas,r9a07g044c1 # Single Cortex-A55 RZ/G2LC + - renesas,r9a07g044c2 # Dual Cortex-A55 RZ/G2LC + - renesas,r9a07g044l1 # Single Cortex-A55 RZ/G2L + - renesas,r9a07g044l2 # Dual Cortex-A55 RZ/G2L + - const: renesas,r9a07g044 + + - description: RZ/V2L (R9A07G054) + items: + - enum: + - renesas,smarc-evk # SMARC EVK + - enum: + - renesas,r9a07g054l1 # Single Cortex-A55 RZ/V2L + - renesas,r9a07g054l2 # Dual Cortex-A55 RZ/V2L + - const: renesas,r9a07g054 + + - description: RZ/V2M (R9A09G011) + items: + - enum: + - renesas,rzv2mevk2 # RZ/V2M Eval Board v2.0 + - const: renesas,r9a09g011 + +additionalProperties: true + +... diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..b69648ab877d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2699,7 +2699,6 @@ S: Supported Q: http://patchwork.kernel.org/project/linux-renesas-soc/list/ C: irc://irc.libera.chat/renesas-soc T: git git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-devel.git next -F: Documentation/devicetree/bindings/arm/renesas.yaml F: Documentation/devicetree/bindings/hwinfo/renesas,prr.yaml F: Documentation/devicetree/bindings/soc/renesas/ F: arch/arm/boot/dts/emev2* -- cgit v1.2.3 From 96fce387d58fa8eae6e8d9b1ecdfbc18292d7a68 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 28 Sep 2022 14:17:05 -0700 Subject: kunit/memcpy: Add dynamic size and window tests The "side effects" memmove() test accidentally found[1] a corner case in the recent refactoring of the i386 assembly memmove(), but missed another corner case. Instead of hoping to get lucky next time, implement much more complete tests of memcpy() and memmove() -- especially the moving window overlap for memmove() -- which catches all the issues encountered and should catch anything new. [1] https://lore.kernel.org/lkml/CAKwvOdkaKTa2aiA90VzFrChNQM6O_ro+b7VWs=op70jx-DKaXA@mail.gmail.com Cc: Nick Desaulniers Tested-by: Nick Desaulniers Signed-off-by: Kees Cook --- MAINTAINERS | 1 + lib/memcpy_kunit.c | 205 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..9dd8d74c4df0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8044,6 +8044,7 @@ S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening F: include/linux/fortify-string.h F: lib/fortify_kunit.c +F: lib/memcpy_kunit.c F: lib/test_fortify/* F: scripts/test_fortify.sh K: \b__NO_FORTIFY\b diff --git a/lib/memcpy_kunit.c b/lib/memcpy_kunit.c index 2b5cc70ac53f..c4a7107edd43 100644 --- a/lib/memcpy_kunit.c +++ b/lib/memcpy_kunit.c @@ -270,6 +270,208 @@ static void memset_test(struct kunit *test) #undef TEST_OP } +static u8 large_src[1024]; +static u8 large_dst[2048]; +static const u8 large_zero[2048]; + +static void set_random_nonzero(struct kunit *test, u8 *byte) +{ + int failed_rng = 0; + + while (*byte == 0) { + get_random_bytes(byte, 1); + KUNIT_ASSERT_LT_MSG(test, failed_rng++, 100, + "Is the RNG broken?"); + } +} + +static void init_large(struct kunit *test) +{ + + /* Get many bit patterns. */ + get_random_bytes(large_src, ARRAY_SIZE(large_src)); + + /* Make sure we have non-zero edges. */ + set_random_nonzero(test, &large_src[0]); + set_random_nonzero(test, &large_src[ARRAY_SIZE(large_src) - 1]); + + /* Explicitly zero the entire destination. */ + memset(large_dst, 0, ARRAY_SIZE(large_dst)); +} + +/* + * Instead of an indirect function call for "copy" or a giant macro, + * use a bool to pick memcpy or memmove. + */ +static void copy_large_test(struct kunit *test, bool use_memmove) +{ + init_large(test); + + /* Copy a growing number of non-overlapping bytes ... */ + for (int bytes = 1; bytes <= ARRAY_SIZE(large_src); bytes++) { + /* Over a shifting destination window ... */ + for (int offset = 0; offset < ARRAY_SIZE(large_src); offset++) { + int right_zero_pos = offset + bytes; + int right_zero_size = ARRAY_SIZE(large_dst) - right_zero_pos; + + /* Copy! */ + if (use_memmove) + memmove(large_dst + offset, large_src, bytes); + else + memcpy(large_dst + offset, large_src, bytes); + + /* Did we touch anything before the copy area? */ + KUNIT_ASSERT_EQ_MSG(test, + memcmp(large_dst, large_zero, offset), 0, + "with size %d at offset %d", bytes, offset); + /* Did we touch anything after the copy area? */ + KUNIT_ASSERT_EQ_MSG(test, + memcmp(&large_dst[right_zero_pos], large_zero, right_zero_size), 0, + "with size %d at offset %d", bytes, offset); + + /* Are we byte-for-byte exact across the copy? */ + KUNIT_ASSERT_EQ_MSG(test, + memcmp(large_dst + offset, large_src, bytes), 0, + "with size %d at offset %d", bytes, offset); + + /* Zero out what we copied for the next cycle. */ + memset(large_dst + offset, 0, bytes); + } + /* Avoid stall warnings if this loop gets slow. */ + cond_resched(); + } +} + +static void memcpy_large_test(struct kunit *test) +{ + copy_large_test(test, false); +} + +static void memmove_large_test(struct kunit *test) +{ + copy_large_test(test, true); +} + +/* + * On the assumption that boundary conditions are going to be the most + * sensitive, instead of taking a full step (inc) each iteration, + * take single index steps for at least the first "inc"-many indexes + * from the "start" and at least the last "inc"-many indexes before + * the "end". When in the middle, take full "inc"-wide steps. For + * example, calling next_step(idx, 1, 15, 3) with idx starting at 0 + * would see the following pattern: 1 2 3 4 7 10 11 12 13 14 15. + */ +static int next_step(int idx, int start, int end, int inc) +{ + start += inc; + end -= inc; + + if (idx < start || idx + inc > end) + inc = 1; + return idx + inc; +} + +static void inner_loop(struct kunit *test, int bytes, int d_off, int s_off) +{ + int left_zero_pos, left_zero_size; + int right_zero_pos, right_zero_size; + int src_pos, src_orig_pos, src_size; + int pos; + + /* Place the source in the destination buffer. */ + memcpy(&large_dst[s_off], large_src, bytes); + + /* Copy to destination offset. */ + memmove(&large_dst[d_off], &large_dst[s_off], bytes); + + /* Make sure destination entirely matches. */ + KUNIT_ASSERT_EQ_MSG(test, memcmp(&large_dst[d_off], large_src, bytes), 0, + "with size %d at src offset %d and dest offset %d", + bytes, s_off, d_off); + + /* Calculate the expected zero spans. */ + if (s_off < d_off) { + left_zero_pos = 0; + left_zero_size = s_off; + + right_zero_pos = d_off + bytes; + right_zero_size = ARRAY_SIZE(large_dst) - right_zero_pos; + + src_pos = s_off; + src_orig_pos = 0; + src_size = d_off - s_off; + } else { + left_zero_pos = 0; + left_zero_size = d_off; + + right_zero_pos = s_off + bytes; + right_zero_size = ARRAY_SIZE(large_dst) - right_zero_pos; + + src_pos = d_off + bytes; + src_orig_pos = src_pos - s_off; + src_size = right_zero_pos - src_pos; + } + + /* Check non-overlapping source is unchanged.*/ + KUNIT_ASSERT_EQ_MSG(test, + memcmp(&large_dst[src_pos], &large_src[src_orig_pos], src_size), 0, + "with size %d at src offset %d and dest offset %d", + bytes, s_off, d_off); + + /* Check leading buffer contents are zero. */ + KUNIT_ASSERT_EQ_MSG(test, + memcmp(&large_dst[left_zero_pos], large_zero, left_zero_size), 0, + "with size %d at src offset %d and dest offset %d", + bytes, s_off, d_off); + /* Check trailing buffer contents are zero. */ + KUNIT_ASSERT_EQ_MSG(test, + memcmp(&large_dst[right_zero_pos], large_zero, right_zero_size), 0, + "with size %d at src offset %d and dest offset %d", + bytes, s_off, d_off); + + /* Zero out everything not already zeroed.*/ + pos = left_zero_pos + left_zero_size; + memset(&large_dst[pos], 0, right_zero_pos - pos); +} + +static void memmove_overlap_test(struct kunit *test) +{ + /* + * Running all possible offset and overlap combinations takes a + * very long time. Instead, only check up to 128 bytes offset + * into the destination buffer (which should result in crossing + * cachelines), with a step size of 1 through 7 to try to skip some + * redundancy. + */ + static const int offset_max = 128; /* less than ARRAY_SIZE(large_src); */ + static const int bytes_step = 7; + static const int window_step = 7; + + static const int bytes_start = 1; + static const int bytes_end = ARRAY_SIZE(large_src) + 1; + + init_large(test); + + /* Copy a growing number of overlapping bytes ... */ + for (int bytes = bytes_start; bytes < bytes_end; + bytes = next_step(bytes, bytes_start, bytes_end, bytes_step)) { + + /* Over a shifting destination window ... */ + for (int d_off = 0; d_off < offset_max; d_off++) { + int s_start = max(d_off - bytes, 0); + int s_end = min_t(int, d_off + bytes, ARRAY_SIZE(large_src)); + + /* Over a shifting source window ... */ + for (int s_off = s_start; s_off < s_end; + s_off = next_step(s_off, s_start, s_end, window_step)) + inner_loop(test, bytes, d_off, s_off); + + /* Avoid stall warnings. */ + cond_resched(); + } + } +} + static void strtomem_test(struct kunit *test) { static const char input[sizeof(unsigned long)] = "hi"; @@ -325,7 +527,10 @@ static void strtomem_test(struct kunit *test) static struct kunit_case memcpy_test_cases[] = { KUNIT_CASE(memset_test), KUNIT_CASE(memcpy_test), + KUNIT_CASE(memcpy_large_test), KUNIT_CASE(memmove_test), + KUNIT_CASE(memmove_large_test), + KUNIT_CASE(memmove_overlap_test), KUNIT_CASE(strtomem_test), {} }; -- cgit v1.2.3 From c2741cbe7f8aba56a79a150536863eb1f62f9af6 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 29 Sep 2022 16:54:16 +0200 Subject: dt-bindings: media: st,stmipid02: Convert the text bindings to YAML Convert the text STMIPID02 DT bindings to YAML DT format to permit validation of DTs using this I2C CSI-2 to CPI bridge. Reviewed-by: Benjamin Mugnier Signed-off-by: Marek Vasut Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220929145416.16336-1-marex@denx.de Signed-off-by: Rob Herring --- .../bindings/media/i2c/st,st-mipid02.txt | 82 ---------- .../bindings/media/i2c/st,st-mipid02.yaml | 176 +++++++++++++++++++++ MAINTAINERS | 2 +- 3 files changed, 177 insertions(+), 83 deletions(-) delete mode 100644 Documentation/devicetree/bindings/media/i2c/st,st-mipid02.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/st,st-mipid02.yaml (limited to 'MAINTAINERS') diff --git a/Documentation/devicetree/bindings/media/i2c/st,st-mipid02.txt b/Documentation/devicetree/bindings/media/i2c/st,st-mipid02.txt deleted file mode 100644 index 7976e6c40a80..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/st,st-mipid02.txt +++ /dev/null @@ -1,82 +0,0 @@ -STMicroelectronics MIPID02 CSI-2 to PARALLEL bridge - -MIPID02 has two CSI-2 input ports, only one of those ports can be active at a -time. Active port input stream will be de-serialized and its content outputted -through PARALLEL output port. -CSI-2 first input port is a dual lane 800Mbps per lane whereas CSI-2 second -input port is a single lane 800Mbps. Both ports support clock and data lane -polarity swap. First port also supports data lane swap. -PARALLEL output port has a maximum width of 12 bits. -Supported formats are RAW6, RAW7, RAW8, RAW10, RAW12, RGB565, RGB888, RGB444, -YUV420 8-bit, YUV422 8-bit and YUV420 10-bit. - -Required Properties: -- compatible: shall be "st,st-mipid02" -- clocks: reference to the xclk input clock. -- clock-names: shall be "xclk". -- VDDE-supply: sensor digital IO supply. Must be 1.8 volts. -- VDDIN-supply: sensor internal regulator supply. Must be 1.8 volts. - -Optional Properties: -- reset-gpios: reference to the GPIO connected to the xsdn pin, if any. - This is an active low signal to the mipid02. - -Required subnodes: - - ports: A ports node with one port child node per device input and output - port, in accordance with the video interface bindings defined in - Documentation/devicetree/bindings/media/video-interfaces.txt. The - port nodes are numbered as follows: - - Port Description - ----------------------------- - 0 CSI-2 first input port - 1 CSI-2 second input port - 2 PARALLEL output - -Endpoint node required property for CSI-2 connection is: -- data-lanes: shall be <1> for Port 1. for Port 0 dual-lane operation shall be -<1 2> or <2 1>. For Port 0 single-lane operation shall be <1> or <2>. -Endpoint node optional property for CSI-2 connection is: -- lane-polarities: any lane can be inverted or not. - -Endpoint node required property for PARALLEL connection is: -- bus-width: shall be set to <6>, <7>, <8>, <10> or <12>. -Endpoint node optional properties for PARALLEL connection are: -- hsync-active: active state of the HSYNC signal, 0/1 for LOW/HIGH respectively. -LOW being the default. -- vsync-active: active state of the VSYNC signal, 0/1 for LOW/HIGH respectively. -LOW being the default. - -Example: - -mipid02: csi2rx@14 { - compatible = "st,st-mipid02"; - reg = <0x14>; - status = "okay"; - clocks = <&clk_ext_camera_12>; - clock-names = "xclk"; - VDDE-supply = <&vdd>; - VDDIN-supply = <&vdd>; - ports { - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - - ep0: endpoint { - data-lanes = <1 2>; - remote-endpoint = <&mipi_csi2_in>; - }; - }; - port@2 { - reg = <2>; - - ep2: endpoint { - bus-width = <8>; - hsync-active = <0>; - vsync-active = <0>; - remote-endpoint = <¶llel_out>; - }; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/media/i2c/st,st-mipid02.yaml b/Documentation/devicetree/bindings/media/i2c/st,st-mipid02.yaml new file mode 100644 index 000000000000..19a39d753aad --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/st,st-mipid02.yaml @@ -0,0 +1,176 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/st,st-mipid02.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics MIPID02 CSI-2 to PARALLEL bridge + +maintainers: + - Benjamin Mugnier + - Sylvain Petinot + +description: + MIPID02 has two CSI-2 input ports, only one of those ports can be + active at a time. Active port input stream will be de-serialized + and its content outputted through PARALLEL output port. + CSI-2 first input port is a dual lane 800Mbps per lane whereas CSI-2 + second input port is a single lane 800Mbps. Both ports support clock + and data lane polarity swap. First port also supports data lane swap. + PARALLEL output port has a maximum width of 12 bits. + Supported formats are RAW6, RAW7, RAW8, RAW10, RAW12, RGB565, RGB888, + RGB444, YUV420 8-bit, YUV422 8-bit and YUV420 10-bit. + +properties: + compatible: + const: st,st-mipid02 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + const: xclk + + VDDE-supply: + description: + Sensor digital IO supply. Must be 1.8 volts. + + VDDIN-supply: + description: + Sensor internal regulator supply. Must be 1.8 volts. + + reset-gpios: + description: + Reference to the GPIO connected to the xsdn pin, if any. + This is an active low signal to the mipid02. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: CSI-2 first input port + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + description: + Single-lane operation shall be <1> or <2> . + Dual-lane operation shall be <1 2> or <2 1> . + minItems: 1 + maxItems: 2 + + lane-polarities: + description: + Any lane can be inverted or not. + minItems: 1 + maxItems: 2 + + required: + - data-lanes + + port@1: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: CSI-2 second input port + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + description: + Single-lane operation shall be <1> or <2> . + maxItems: 1 + + lane-polarities: + description: + Any lane can be inverted or not. + maxItems: 1 + + required: + - data-lanes + + port@2: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: Output port + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + bus-width: + enum: [6, 7, 8, 10, 12] + + required: + - bus-width + + anyOf: + - required: + - port@0 + - required: + - port@1 + + required: + - port@2 + +additionalProperties: false + +required: + - compatible + - reg + - clocks + - clock-names + - VDDE-supply + - VDDIN-supply + - ports + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + mipid02: csi2rx@14 { + compatible = "st,st-mipid02"; + reg = <0x14>; + status = "okay"; + clocks = <&clk_ext_camera_12>; + clock-names = "xclk"; + VDDE-supply = <&vdd>; + VDDIN-supply = <&vdd>; + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + + ep0: endpoint { + data-lanes = <1 2>; + remote-endpoint = <&mipi_csi2_in>; + }; + }; + port@2 { + reg = <2>; + + ep2: endpoint { + bus-width = <8>; + hsync-active = <0>; + vsync-active = <0>; + remote-endpoint = <¶llel_out>; + }; + }; + }; + }; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..c379db61b800 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19476,7 +19476,7 @@ M: Sylvain Petinot L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git -F: Documentation/devicetree/bindings/media/i2c/st,st-mipid02.txt +F: Documentation/devicetree/bindings/media/i2c/st,st-mipid02.yaml F: drivers/media/i2c/st-mipid02.c ST STM32 I2C/SMBUS DRIVER -- cgit v1.2.3 From 7e5f430eb20108bfb0f255e27304de2e28505a8e Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Sun, 18 Sep 2022 20:48:43 +0200 Subject: MAINTAINERS: Make Mauro EDAC reviewer Mauro hasn't really been maintaining EDAC for a long while now. Make him a reviewer instead. Signed-off-by: Borislav Petkov Acked-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/20220926180343.23346-1-bp@alien8.de --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 6d75828ea7b2..3c4852298687 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7356,9 +7356,9 @@ F: drivers/edac/thunderx_edac* EDAC-CORE M: Borislav Petkov -M: Mauro Carvalho Chehab M: Tony Luck R: James Morse +R: Mauro Carvalho Chehab R: Robert Richter L: linux-edac@vger.kernel.org S: Supported -- cgit v1.2.3 From 70479a40954cf353e87a486997a3477108c75aa9 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 28 Oct 2022 17:26:21 +0800 Subject: net: phy: Add driver for Motorcomm yt8521 gigabit ethernet phy Add a driver for the motorcomm yt8521 gigabit ethernet phy. We have verified the driver on StarFive VisionFive development board, which is developed by Shanghai StarFive Technology Co., Ltd.. On the board, yt8521 gigabit ethernet phy works in utp mode, RGMII interface, supports 1000M/100M/10M speeds, and wol(magic package). Signed-off-by: Frank Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- MAINTAINERS | 1 + drivers/net/phy/Kconfig | 2 +- drivers/net/phy/motorcomm.c | 1635 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 1635 insertions(+), 3 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 377aedb3808b..7d44c5a6607d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13943,6 +13943,7 @@ F: include/uapi/linux/meye.h MOTORCOMM PHY DRIVER M: Peter Geis +M: Frank L: netdev@vger.kernel.org S: Maintained F: drivers/net/phy/motorcomm.c diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index c57a0262fb64..040c8bf6d05b 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -260,7 +260,7 @@ config MOTORCOMM_PHY tristate "Motorcomm PHYs" help Enables support for Motorcomm network PHYs. - Currently supports the YT8511 gigabit PHY. + Currently supports the YT8511, YT8521 Gigabit Ethernet PHYs. config NATIONAL_PHY tristate "National Semiconductor PHYs" diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c index 7e6ac2c5e27e..c7593f224177 100644 --- a/drivers/net/phy/motorcomm.c +++ b/drivers/net/phy/motorcomm.c @@ -1,15 +1,106 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Driver for Motorcomm PHYs + * Motorcomm 8511/8521 PHY driver. * * Author: Peter Geis + * Author: Frank */ +#include #include #include #include #define PHY_ID_YT8511 0x0000010a +#define PHY_ID_YT8521 0x0000011A + +/* YT8521 Register Overview + * UTP Register space | FIBER Register space + * ------------------------------------------------------------ + * | UTP MII | FIBER MII | + * | UTP MMD | | + * | UTP Extended | FIBER Extended | + * ------------------------------------------------------------ + * | Common Extended | + * ------------------------------------------------------------ + */ + +/* 0x10 ~ 0x15 , 0x1E and 0x1F are common MII registers of yt phy */ + +/* Specific Function Control Register */ +#define YTPHY_SPECIFIC_FUNCTION_CONTROL_REG 0x10 + +/* 2b00 Manual MDI configuration + * 2b01 Manual MDIX configuration + * 2b10 Reserved + * 2b11 Enable automatic crossover for all modes *default* + */ +#define YTPHY_SFCR_MDI_CROSSOVER_MODE_MASK (BIT(6) | BIT(5)) +#define YTPHY_SFCR_CROSSOVER_EN BIT(3) +#define YTPHY_SFCR_SQE_TEST_EN BIT(2) +#define YTPHY_SFCR_POLARITY_REVERSAL_EN BIT(1) +#define YTPHY_SFCR_JABBER_DIS BIT(0) + +/* Specific Status Register */ +#define YTPHY_SPECIFIC_STATUS_REG 0x11 +#define YTPHY_SSR_SPEED_MODE_OFFSET 14 + +#define YTPHY_SSR_SPEED_MODE_MASK (BIT(15) | BIT(14)) +#define YTPHY_SSR_SPEED_10M 0x0 +#define YTPHY_SSR_SPEED_100M 0x1 +#define YTPHY_SSR_SPEED_1000M 0x2 +#define YTPHY_SSR_DUPLEX_OFFSET 13 +#define YTPHY_SSR_DUPLEX BIT(13) +#define YTPHY_SSR_PAGE_RECEIVED BIT(12) +#define YTPHY_SSR_SPEED_DUPLEX_RESOLVED BIT(11) +#define YTPHY_SSR_LINK BIT(10) +#define YTPHY_SSR_MDIX_CROSSOVER BIT(6) +#define YTPHY_SSR_DOWNGRADE BIT(5) +#define YTPHY_SSR_TRANSMIT_PAUSE BIT(3) +#define YTPHY_SSR_RECEIVE_PAUSE BIT(2) +#define YTPHY_SSR_POLARITY BIT(1) +#define YTPHY_SSR_JABBER BIT(0) + +/* Interrupt enable Register */ +#define YTPHY_INTERRUPT_ENABLE_REG 0x12 +#define YTPHY_IER_WOL BIT(6) + +/* Interrupt Status Register */ +#define YTPHY_INTERRUPT_STATUS_REG 0x13 +#define YTPHY_ISR_AUTONEG_ERR BIT(15) +#define YTPHY_ISR_SPEED_CHANGED BIT(14) +#define YTPHY_ISR_DUPLEX_CHANGED BIT(13) +#define YTPHY_ISR_PAGE_RECEIVED BIT(12) +#define YTPHY_ISR_LINK_FAILED BIT(11) +#define YTPHY_ISR_LINK_SUCCESSED BIT(10) +#define YTPHY_ISR_WOL BIT(6) +#define YTPHY_ISR_WIRESPEED_DOWNGRADE BIT(5) +#define YTPHY_ISR_SERDES_LINK_FAILED BIT(3) +#define YTPHY_ISR_SERDES_LINK_SUCCESSED BIT(2) +#define YTPHY_ISR_POLARITY_CHANGED BIT(1) +#define YTPHY_ISR_JABBER_HAPPENED BIT(0) + +/* Speed Auto Downgrade Control Register */ +#define YTPHY_SPEED_AUTO_DOWNGRADE_CONTROL_REG 0x14 +#define YTPHY_SADCR_SPEED_DOWNGRADE_EN BIT(5) + +/* If these bits are set to 3, the PHY attempts five times ( 3(set value) + + * additional 2) before downgrading, default 0x3 + */ +#define YTPHY_SADCR_SPEED_RETRY_LIMIT (0x3 << 2) + +/* Rx Error Counter Register */ +#define YTPHY_RX_ERROR_COUNTER_REG 0x15 + +/* Extended Register's Address Offset Register */ +#define YTPHY_PAGE_SELECT 0x1E + +/* Extended Register's Data Register */ +#define YTPHY_PAGE_DATA 0x1F + +/* FIBER Auto-Negotiation link partner ability */ +#define YTPHY_FLPA_PAUSE (0x3 << 7) +#define YTPHY_FLPA_ASYM_PAUSE (0x2 << 7) #define YT8511_PAGE_SELECT 0x1e #define YT8511_PAGE 0x1f @@ -38,6 +129,352 @@ #define YT8511_DELAY_FE_TX_EN (0xf << 12) #define YT8511_DELAY_FE_TX_DIS (0x2 << 12) +/* Extended register is different from MMD Register and MII Register. + * We can use ytphy_read_ext/ytphy_write_ext/ytphy_modify_ext function to + * operate extended register. + * Extended Register start + */ + +/* Phy gmii clock gating Register */ +#define YT8521_CLOCK_GATING_REG 0xC +#define YT8521_CGR_RX_CLK_EN BIT(12) + +#define YT8521_EXTREG_SLEEP_CONTROL1_REG 0x27 +#define YT8521_ESC1R_SLEEP_SW BIT(15) +#define YT8521_ESC1R_PLLON_SLP BIT(14) + +/* Phy fiber Link timer cfg2 Register */ +#define YT8521_LINK_TIMER_CFG2_REG 0xA5 +#define YT8521_LTCR_EN_AUTOSEN BIT(15) + +/* 0xA000, 0xA001, 0xA003 ,and 0xA006 ~ 0xA00A are common ext registers + * of yt8521 phy. There is no need to switch reg space when operating these + * registers. + */ + +#define YT8521_REG_SPACE_SELECT_REG 0xA000 +#define YT8521_RSSR_SPACE_MASK BIT(1) +#define YT8521_RSSR_FIBER_SPACE (0x1 << 1) +#define YT8521_RSSR_UTP_SPACE (0x0 << 1) +#define YT8521_RSSR_TO_BE_ARBITRATED (0xFF) + +#define YT8521_CHIP_CONFIG_REG 0xA001 +#define YT8521_CCR_SW_RST BIT(15) + +#define YT8521_CCR_MODE_SEL_MASK (BIT(2) | BIT(1) | BIT(0)) +#define YT8521_CCR_MODE_UTP_TO_RGMII 0 +#define YT8521_CCR_MODE_FIBER_TO_RGMII 1 +#define YT8521_CCR_MODE_UTP_FIBER_TO_RGMII 2 +#define YT8521_CCR_MODE_UTP_TO_SGMII 3 +#define YT8521_CCR_MODE_SGPHY_TO_RGMAC 4 +#define YT8521_CCR_MODE_SGMAC_TO_RGPHY 5 +#define YT8521_CCR_MODE_UTP_TO_FIBER_AUTO 6 +#define YT8521_CCR_MODE_UTP_TO_FIBER_FORCE 7 + +/* 3 phy polling modes,poll mode combines utp and fiber mode*/ +#define YT8521_MODE_FIBER 0x1 +#define YT8521_MODE_UTP 0x2 +#define YT8521_MODE_POLL 0x3 + +#define YT8521_RGMII_CONFIG1_REG 0xA003 + +/* TX Gig-E Delay is bits 3:0, default 0x1 + * TX Fast-E Delay is bits 7:4, default 0xf + * RX Delay is bits 13:10, default 0x0 + * Delay = 150ps * N + * On = 2250ps, off = 0ps + */ +#define YT8521_RC1R_RX_DELAY_MASK (0xF << 10) +#define YT8521_RC1R_RX_DELAY_EN (0xF << 10) +#define YT8521_RC1R_RX_DELAY_DIS (0x0 << 10) +#define YT8521_RC1R_FE_TX_DELAY_MASK (0xF << 4) +#define YT8521_RC1R_FE_TX_DELAY_EN (0xF << 4) +#define YT8521_RC1R_FE_TX_DELAY_DIS (0x0 << 4) +#define YT8521_RC1R_GE_TX_DELAY_MASK (0xF << 0) +#define YT8521_RC1R_GE_TX_DELAY_EN (0xF << 0) +#define YT8521_RC1R_GE_TX_DELAY_DIS (0x0 << 0) + +#define YTPHY_MISC_CONFIG_REG 0xA006 +#define YTPHY_MCR_FIBER_SPEED_MASK BIT(0) +#define YTPHY_MCR_FIBER_1000BX (0x1 << 0) +#define YTPHY_MCR_FIBER_100FX (0x0 << 0) + +/* WOL MAC ADDR: MACADDR2(highest), MACADDR1(middle), MACADDR0(lowest) */ +#define YTPHY_WOL_MACADDR2_REG 0xA007 +#define YTPHY_WOL_MACADDR1_REG 0xA008 +#define YTPHY_WOL_MACADDR0_REG 0xA009 + +#define YTPHY_WOL_CONFIG_REG 0xA00A +#define YTPHY_WCR_INTR_SEL BIT(6) +#define YTPHY_WCR_ENABLE BIT(3) + +/* 2b00 84ms + * 2b01 168ms *default* + * 2b10 336ms + * 2b11 672ms + */ +#define YTPHY_WCR_PULSE_WIDTH_MASK (BIT(2) | BIT(1)) +#define YTPHY_WCR_PULSE_WIDTH_672MS (BIT(2) | BIT(1)) + +/* 1b0 Interrupt and WOL events is level triggered and active LOW *default* + * 1b1 Interrupt and WOL events is pulse triggered and active LOW + */ +#define YTPHY_WCR_TYPE_PULSE BIT(0) + +/* Extended Register end */ + +struct yt8521_priv { + /* combo_advertising is used for case of YT8521 in combo mode, + * this means that yt8521 may work in utp or fiber mode which depends + * on which media is connected (YT8521_RSSR_TO_BE_ARBITRATED). + */ + __ETHTOOL_DECLARE_LINK_MODE_MASK(combo_advertising); + + /* YT8521_MODE_FIBER / YT8521_MODE_UTP / YT8521_MODE_POLL*/ + u8 polling_mode; + u8 strap_mode; /* 8 working modes */ + /* current reg page of yt8521 phy: + * YT8521_RSSR_UTP_SPACE + * YT8521_RSSR_FIBER_SPACE + * YT8521_RSSR_TO_BE_ARBITRATED + */ + u8 reg_page; +}; + +/** + * ytphy_read_ext() - read a PHY's extended register + * @phydev: a pointer to a &struct phy_device + * @regnum: register number to read + * + * NOTE:The caller must have taken the MDIO bus lock. + * + * returns the value of regnum reg or negative error code + */ +static int ytphy_read_ext(struct phy_device *phydev, u16 regnum) +{ + int ret; + + ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum); + if (ret < 0) + return ret; + + return __phy_read(phydev, YTPHY_PAGE_DATA); +} + +/** + * ytphy_read_ext_with_lock() - read a PHY's extended register + * @phydev: a pointer to a &struct phy_device + * @regnum: register number to read + * + * returns the value of regnum reg or negative error code + */ +static int ytphy_read_ext_with_lock(struct phy_device *phydev, u16 regnum) +{ + int ret; + + phy_lock_mdio_bus(phydev); + ret = ytphy_read_ext(phydev, regnum); + phy_unlock_mdio_bus(phydev); + + return ret; +} + +/** + * ytphy_write_ext() - write a PHY's extended register + * @phydev: a pointer to a &struct phy_device + * @regnum: register number to write + * @val: value to write to @regnum + * + * NOTE:The caller must have taken the MDIO bus lock. + * + * returns 0 or negative error code + */ +static int ytphy_write_ext(struct phy_device *phydev, u16 regnum, u16 val) +{ + int ret; + + ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum); + if (ret < 0) + return ret; + + return __phy_write(phydev, YTPHY_PAGE_DATA, val); +} + +/** + * ytphy_write_ext_with_lock() - write a PHY's extended register + * @phydev: a pointer to a &struct phy_device + * @regnum: register number to write + * @val: value to write to @regnum + * + * returns 0 or negative error code + */ +static int ytphy_write_ext_with_lock(struct phy_device *phydev, u16 regnum, + u16 val) +{ + int ret; + + phy_lock_mdio_bus(phydev); + ret = ytphy_write_ext(phydev, regnum, val); + phy_unlock_mdio_bus(phydev); + + return ret; +} + +/** + * ytphy_modify_ext() - bits modify a PHY's extended register + * @phydev: a pointer to a &struct phy_device + * @regnum: register number to write + * @mask: bit mask of bits to clear + * @set: bit mask of bits to set + * + * NOTE: Convenience function which allows a PHY's extended register to be + * modified as new register value = (old register value & ~mask) | set. + * The caller must have taken the MDIO bus lock. + * + * returns 0 or negative error code + */ +static int ytphy_modify_ext(struct phy_device *phydev, u16 regnum, u16 mask, + u16 set) +{ + int ret; + + ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum); + if (ret < 0) + return ret; + + return __phy_modify(phydev, YTPHY_PAGE_DATA, mask, set); +} + +/** + * ytphy_modify_ext_with_lock() - bits modify a PHY's extended register + * @phydev: a pointer to a &struct phy_device + * @regnum: register number to write + * @mask: bit mask of bits to clear + * @set: bit mask of bits to set + * + * NOTE: Convenience function which allows a PHY's extended register to be + * modified as new register value = (old register value & ~mask) | set. + * + * returns 0 or negative error code + */ +static int ytphy_modify_ext_with_lock(struct phy_device *phydev, u16 regnum, + u16 mask, u16 set) +{ + int ret; + + phy_lock_mdio_bus(phydev); + ret = ytphy_modify_ext(phydev, regnum, mask, set); + phy_unlock_mdio_bus(phydev); + + return ret; +} + +/** + * ytphy_get_wol() - report whether wake-on-lan is enabled + * @phydev: a pointer to a &struct phy_device + * @wol: a pointer to a &struct ethtool_wolinfo + * + * NOTE: YTPHY_WOL_CONFIG_REG is common ext reg. + */ +static void ytphy_get_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + int wol_config; + + wol->supported = WAKE_MAGIC; + wol->wolopts = 0; + + wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG); + if (wol_config < 0) + return; + + if (wol_config & YTPHY_WCR_ENABLE) + wol->wolopts |= WAKE_MAGIC; +} + +/** + * ytphy_set_wol() - turn wake-on-lan on or off + * @phydev: a pointer to a &struct phy_device + * @wol: a pointer to a &struct ethtool_wolinfo + * + * NOTE: YTPHY_WOL_CONFIG_REG, YTPHY_WOL_MACADDR2_REG, YTPHY_WOL_MACADDR1_REG + * and YTPHY_WOL_MACADDR0_REG are common ext reg. The + * YTPHY_INTERRUPT_ENABLE_REG of UTP is special, fiber also use this register. + * + * returns 0 or negative errno code + */ +static int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) +{ + struct net_device *p_attached_dev; + const u16 mac_addr_reg[] = { + YTPHY_WOL_MACADDR2_REG, + YTPHY_WOL_MACADDR1_REG, + YTPHY_WOL_MACADDR0_REG, + }; + const u8 *mac_addr; + int old_page; + int ret = 0; + u16 mask; + u16 val; + u8 i; + + if (wol->wolopts & WAKE_MAGIC) { + p_attached_dev = phydev->attached_dev; + if (!p_attached_dev) + return -ENODEV; + + mac_addr = (const u8 *)p_attached_dev->dev_addr; + if (!is_valid_ether_addr(mac_addr)) + return -EINVAL; + + /* lock mdio bus then switch to utp reg space */ + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); + if (old_page < 0) + goto err_restore_page; + + /* Store the device address for the magic packet */ + for (i = 0; i < 3; i++) { + ret = ytphy_write_ext(phydev, mac_addr_reg[i], + ((mac_addr[i * 2] << 8)) | + (mac_addr[i * 2 + 1])); + if (ret < 0) + goto err_restore_page; + } + + /* Enable WOL feature */ + mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL; + val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL; + val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS; + ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, val); + if (ret < 0) + goto err_restore_page; + + /* Enable WOL interrupt */ + ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0, + YTPHY_IER_WOL); + if (ret < 0) + goto err_restore_page; + + } else { + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); + if (old_page < 0) + goto err_restore_page; + + /* Disable WOL feature */ + mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL; + ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, 0); + + /* Disable WOL interrupt */ + ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, + YTPHY_IER_WOL, 0); + if (ret < 0) + goto err_restore_page; + } + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + static int yt8511_read_page(struct phy_device *phydev) { return __phy_read(phydev, YT8511_PAGE_SELECT); @@ -111,6 +548,1181 @@ err_restore_page: return phy_restore_page(phydev, oldpage, ret); } +/** + * yt8521_read_page() - read reg page + * @phydev: a pointer to a &struct phy_device + * + * returns current reg space of yt8521 (YT8521_RSSR_FIBER_SPACE/ + * YT8521_RSSR_UTP_SPACE) or negative errno code + */ +static int yt8521_read_page(struct phy_device *phydev) +{ + int old_page; + + old_page = ytphy_read_ext(phydev, YT8521_REG_SPACE_SELECT_REG); + if (old_page < 0) + return old_page; + + if ((old_page & YT8521_RSSR_SPACE_MASK) == YT8521_RSSR_FIBER_SPACE) + return YT8521_RSSR_FIBER_SPACE; + + return YT8521_RSSR_UTP_SPACE; +}; + +/** + * yt8521_write_page() - write reg page + * @phydev: a pointer to a &struct phy_device + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to write. + * + * returns 0 or negative errno code + */ +static int yt8521_write_page(struct phy_device *phydev, int page) +{ + int mask = YT8521_RSSR_SPACE_MASK; + int set; + + if ((page & YT8521_RSSR_SPACE_MASK) == YT8521_RSSR_FIBER_SPACE) + set = YT8521_RSSR_FIBER_SPACE; + else + set = YT8521_RSSR_UTP_SPACE; + + return ytphy_modify_ext(phydev, YT8521_REG_SPACE_SELECT_REG, mask, set); +}; + +/** + * yt8521_probe() - read chip config then set suitable polling_mode + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8521_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct yt8521_priv *priv; + int chip_config; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + phydev->priv = priv; + + chip_config = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); + if (chip_config < 0) + return chip_config; + + priv->strap_mode = chip_config & YT8521_CCR_MODE_SEL_MASK; + switch (priv->strap_mode) { + case YT8521_CCR_MODE_FIBER_TO_RGMII: + case YT8521_CCR_MODE_SGPHY_TO_RGMAC: + case YT8521_CCR_MODE_SGMAC_TO_RGPHY: + priv->polling_mode = YT8521_MODE_FIBER; + priv->reg_page = YT8521_RSSR_FIBER_SPACE; + phydev->port = PORT_FIBRE; + break; + case YT8521_CCR_MODE_UTP_FIBER_TO_RGMII: + case YT8521_CCR_MODE_UTP_TO_FIBER_AUTO: + case YT8521_CCR_MODE_UTP_TO_FIBER_FORCE: + priv->polling_mode = YT8521_MODE_POLL; + priv->reg_page = YT8521_RSSR_TO_BE_ARBITRATED; + phydev->port = PORT_NONE; + break; + case YT8521_CCR_MODE_UTP_TO_SGMII: + case YT8521_CCR_MODE_UTP_TO_RGMII: + priv->polling_mode = YT8521_MODE_UTP; + priv->reg_page = YT8521_RSSR_UTP_SPACE; + phydev->port = PORT_TP; + break; + } + /* set default reg space */ + if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { + ret = ytphy_write_ext_with_lock(phydev, + YT8521_REG_SPACE_SELECT_REG, + priv->reg_page); + if (ret < 0) + return ret; + } + + return 0; +} + +/** + * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp + * @phydev: a pointer to a &struct phy_device + * + * NOTE:The caller must have taken the MDIO bus lock. + * + * returns 0 or negative errno code + */ +static int ytphy_utp_read_lpa(struct phy_device *phydev) +{ + int lpa, lpagb; + + if (phydev->autoneg == AUTONEG_ENABLE) { + if (!phydev->autoneg_complete) { + mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, + 0); + mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, 0); + return 0; + } + + if (phydev->is_gigabit_capable) { + lpagb = __phy_read(phydev, MII_STAT1000); + if (lpagb < 0) + return lpagb; + + if (lpagb & LPA_1000MSFAIL) { + int adv = __phy_read(phydev, MII_CTRL1000); + + if (adv < 0) + return adv; + + if (adv & CTL1000_ENABLE_MASTER) + phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n"); + else + phydev_err(phydev, "Master/Slave resolution failed\n"); + return -ENOLINK; + } + + mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, + lpagb); + } + + lpa = __phy_read(phydev, MII_LPA); + if (lpa < 0) + return lpa; + + mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa); + } else { + linkmode_zero(phydev->lp_advertising); + } + + return 0; +} + +/** + * yt8521_adjust_status() - update speed and duplex to phydev. when in fiber + * mode, adjust speed and duplex. + * @phydev: a pointer to a &struct phy_device + * @status: yt8521 status read from YTPHY_SPECIFIC_STATUS_REG + * @is_utp: false(yt8521 work in fiber mode) or true(yt8521 work in utp mode) + * + * NOTE:The caller must have taken the MDIO bus lock. + * + * returns 0 + */ +static int yt8521_adjust_status(struct phy_device *phydev, int status, + bool is_utp) +{ + int speed_mode, duplex; + int speed; + int err; + int lpa; + + if (is_utp) + duplex = (status & YTPHY_SSR_DUPLEX) >> YTPHY_SSR_DUPLEX_OFFSET; + else + duplex = DUPLEX_FULL; /* for fiber, it always DUPLEX_FULL */ + + speed_mode = (status & YTPHY_SSR_SPEED_MODE_MASK) >> + YTPHY_SSR_SPEED_MODE_OFFSET; + + switch (speed_mode) { + case YTPHY_SSR_SPEED_10M: + if (is_utp) + speed = SPEED_10; + else + /* for fiber, it will never run here, default to + * SPEED_UNKNOWN + */ + speed = SPEED_UNKNOWN; + break; + case YTPHY_SSR_SPEED_100M: + speed = SPEED_100; + break; + case YTPHY_SSR_SPEED_1000M: + speed = SPEED_1000; + break; + default: + speed = SPEED_UNKNOWN; + break; + } + + phydev->speed = speed; + phydev->duplex = duplex; + + if (is_utp) { + err = ytphy_utp_read_lpa(phydev); + if (err < 0) + return err; + + phy_resolve_aneg_pause(phydev); + } else { + lpa = __phy_read(phydev, MII_LPA); + if (lpa < 0) + return lpa; + + /* only support 1000baseX Full */ + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, + phydev->lp_advertising, lpa & LPA_1000XFULL); + + if (!(lpa & YTPHY_FLPA_PAUSE)) { + phydev->pause = 0; + phydev->asym_pause = 0; + } else if ((lpa & YTPHY_FLPA_ASYM_PAUSE)) { + phydev->pause = 1; + phydev->asym_pause = 1; + } else { + phydev->pause = 1; + phydev->asym_pause = 0; + } + } + + return 0; +} + +/** + * yt8521_read_status_paged() - determines the speed and duplex of one page + * @phydev: a pointer to a &struct phy_device + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to + * operate. + * + * returns 1 (utp or fiber link),0 (no link) or negative errno code + */ +static int yt8521_read_status_paged(struct phy_device *phydev, int page) +{ + int fiber_latch_val; + int fiber_curr_val; + int old_page; + int ret = 0; + int status; + int link; + + linkmode_zero(phydev->lp_advertising); + phydev->duplex = DUPLEX_UNKNOWN; + phydev->speed = SPEED_UNKNOWN; + phydev->asym_pause = 0; + phydev->pause = 0; + + /* YT8521 has two reg space (utp/fiber) for linkup with utp/fiber + * respectively. but for utp/fiber combo mode, reg space should be + * arbitrated based on media priority. by default, utp takes + * priority. reg space should be properly set before read + * YTPHY_SPECIFIC_STATUS_REG. + */ + + page &= YT8521_RSSR_SPACE_MASK; + old_page = phy_select_page(phydev, page); + if (old_page < 0) + goto err_restore_page; + + /* Read YTPHY_SPECIFIC_STATUS_REG, which indicates the speed and duplex + * of the PHY is actually using. + */ + ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG); + if (ret < 0) + goto err_restore_page; + + status = ret; + link = !!(status & YTPHY_SSR_LINK); + + /* When PHY is in fiber mode, speed transferred from 1000Mbps to + * 100Mbps,there is not link down from YTPHY_SPECIFIC_STATUS_REG, so + * we need check MII_BMSR to identify such case. + */ + if (page == YT8521_RSSR_FIBER_SPACE) { + ret = __phy_read(phydev, MII_BMSR); + if (ret < 0) + goto err_restore_page; + + fiber_latch_val = ret; + ret = __phy_read(phydev, MII_BMSR); + if (ret < 0) + goto err_restore_page; + + fiber_curr_val = ret; + if (link && fiber_latch_val != fiber_curr_val) { + link = 0; + phydev_info(phydev, + "%s, fiber link down detect, latch = %04x, curr = %04x\n", + __func__, fiber_latch_val, fiber_curr_val); + } + } else { + /* Read autonegotiation status */ + ret = __phy_read(phydev, MII_BMSR); + if (ret < 0) + goto err_restore_page; + + phydev->autoneg_complete = ret & BMSR_ANEGCOMPLETE ? 1 : 0; + } + + if (link) { + if (page == YT8521_RSSR_UTP_SPACE) + yt8521_adjust_status(phydev, status, true); + else + yt8521_adjust_status(phydev, status, false); + } + return phy_restore_page(phydev, old_page, link); + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8521_read_status() - determines the negotiated speed and duplex + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8521_read_status(struct phy_device *phydev) +{ + struct yt8521_priv *priv = phydev->priv; + int link_fiber = 0; + int link_utp; + int link; + int ret; + + if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { + link = yt8521_read_status_paged(phydev, priv->reg_page); + if (link < 0) + return link; + } else { + /* when page is YT8521_RSSR_TO_BE_ARBITRATED, arbitration is + * needed. by default, utp is higher priority. + */ + + link_utp = yt8521_read_status_paged(phydev, + YT8521_RSSR_UTP_SPACE); + if (link_utp < 0) + return link_utp; + + if (!link_utp) { + link_fiber = yt8521_read_status_paged(phydev, + YT8521_RSSR_FIBER_SPACE); + if (link_fiber < 0) + return link_fiber; + } + + link = link_utp || link_fiber; + } + + if (link) { + if (phydev->link == 0) { + /* arbitrate reg space based on linkup media type. */ + if (priv->polling_mode == YT8521_MODE_POLL && + priv->reg_page == YT8521_RSSR_TO_BE_ARBITRATED) { + if (link_fiber) + priv->reg_page = + YT8521_RSSR_FIBER_SPACE; + else + priv->reg_page = YT8521_RSSR_UTP_SPACE; + + ret = ytphy_write_ext_with_lock(phydev, + YT8521_REG_SPACE_SELECT_REG, + priv->reg_page); + if (ret < 0) + return ret; + + phydev->port = link_fiber ? PORT_FIBRE : PORT_TP; + + phydev_info(phydev, "%s, link up, media: %s\n", + __func__, + (phydev->port == PORT_TP) ? + "UTP" : "Fiber"); + } + } + phydev->link = 1; + } else { + if (phydev->link == 1) { + phydev_info(phydev, "%s, link down, media: %s\n", + __func__, (phydev->port == PORT_TP) ? + "UTP" : "Fiber"); + + /* When in YT8521_MODE_POLL mode, need prepare for next + * arbitration. + */ + if (priv->polling_mode == YT8521_MODE_POLL) { + priv->reg_page = YT8521_RSSR_TO_BE_ARBITRATED; + phydev->port = PORT_NONE; + } + } + + phydev->link = 0; + } + + return 0; +} + +/** + * yt8521_modify_bmcr_paged - bits modify a PHY's BMCR register of one page + * @phydev: the phy_device struct + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to operate + * @mask: bit mask of bits to clear + * @set: bit mask of bits to set + * + * NOTE: Convenience function which allows a PHY's BMCR register to be + * modified as new register value = (old register value & ~mask) | set. + * YT8521 has two space (utp/fiber) and three mode (utp/fiber/poll), each space + * has MII_BMCR. poll mode combines utp and faber,so need do both. + * If it is reset, it will wait for completion. + * + * returns 0 or negative errno code + */ +static int yt8521_modify_bmcr_paged(struct phy_device *phydev, int page, + u16 mask, u16 set) +{ + int max_cnt = 500; /* the max wait time of reset ~ 500 ms */ + int old_page; + int ret = 0; + + old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK); + if (old_page < 0) + goto err_restore_page; + + ret = __phy_modify(phydev, MII_BMCR, mask, set); + if (ret < 0) + goto err_restore_page; + + /* If it is reset, need to wait for the reset to complete */ + if (set == BMCR_RESET) { + while (max_cnt--) { + usleep_range(1000, 1100); + ret = __phy_read(phydev, MII_BMCR); + if (ret < 0) + goto err_restore_page; + + if (!(ret & BMCR_RESET)) + return phy_restore_page(phydev, old_page, 0); + } + } + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8521_modify_utp_fiber_bmcr - bits modify a PHY's BMCR register + * @phydev: the phy_device struct + * @mask: bit mask of bits to clear + * @set: bit mask of bits to set + * + * NOTE: Convenience function which allows a PHY's BMCR register to be + * modified as new register value = (old register value & ~mask) | set. + * YT8521 has two space (utp/fiber) and three mode (utp/fiber/poll), each space + * has MII_BMCR. poll mode combines utp and faber,so need do both. + * + * returns 0 or negative errno code + */ +static int yt8521_modify_utp_fiber_bmcr(struct phy_device *phydev, u16 mask, + u16 set) +{ + struct yt8521_priv *priv = phydev->priv; + int ret; + + if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { + ret = yt8521_modify_bmcr_paged(phydev, priv->reg_page, mask, + set); + if (ret < 0) + return ret; + } else { + ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_UTP_SPACE, + mask, set); + if (ret < 0) + return ret; + + ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_FIBER_SPACE, + mask, set); + if (ret < 0) + return ret; + } + return 0; +} + +/** + * yt8521_soft_reset() - called to issue a PHY software reset + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8521_soft_reset(struct phy_device *phydev) +{ + return yt8521_modify_utp_fiber_bmcr(phydev, 0, BMCR_RESET); +} + +/** + * yt8521_suspend() - suspend the hardware + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8521_suspend(struct phy_device *phydev) +{ + int wol_config; + + /* YTPHY_WOL_CONFIG_REG is common ext reg */ + wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG); + if (wol_config < 0) + return wol_config; + + /* if wol enable, do nothing */ + if (wol_config & YTPHY_WCR_ENABLE) + return 0; + + return yt8521_modify_utp_fiber_bmcr(phydev, 0, BMCR_PDOWN); +} + +/** + * yt8521_resume() - resume the hardware + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8521_resume(struct phy_device *phydev) +{ + int ret; + int wol_config; + + /* disable auto sleep */ + ret = ytphy_modify_ext_with_lock(phydev, + YT8521_EXTREG_SLEEP_CONTROL1_REG, + YT8521_ESC1R_SLEEP_SW, 0); + if (ret < 0) + return ret; + + wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG); + if (wol_config < 0) + return wol_config; + + /* if wol enable, do nothing */ + if (wol_config & YTPHY_WCR_ENABLE) + return 0; + + return yt8521_modify_utp_fiber_bmcr(phydev, BMCR_PDOWN, 0); +} + +/** + * yt8521_config_init() - called to initialize the PHY + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8521_config_init(struct phy_device *phydev) +{ + int old_page; + int ret = 0; + u16 val; + + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); + if (old_page < 0) + goto err_restore_page; + + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS; + val |= YT8521_RC1R_RX_DELAY_DIS; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS; + val |= YT8521_RC1R_RX_DELAY_EN; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN; + val |= YT8521_RC1R_RX_DELAY_DIS; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN; + val |= YT8521_RC1R_RX_DELAY_EN; + break; + case PHY_INTERFACE_MODE_SGMII: + break; + default: /* do not support other modes */ + ret = -EOPNOTSUPP; + goto err_restore_page; + } + + /* set rgmii delay mode */ + if (phydev->interface != PHY_INTERFACE_MODE_SGMII) { + ret = ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG, + (YT8521_RC1R_RX_DELAY_MASK | + YT8521_RC1R_FE_TX_DELAY_MASK | + YT8521_RC1R_GE_TX_DELAY_MASK), + val); + if (ret < 0) + goto err_restore_page; + } + + /* disable auto sleep */ + ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG, + YT8521_ESC1R_SLEEP_SW, 0); + if (ret < 0) + goto err_restore_page; + + /* enable RXC clock when no wire plug */ + ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG, + YT8521_CGR_RX_CLK_EN, 0); + if (ret < 0) + goto err_restore_page; + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8521_prepare_fiber_features() - A small helper function that setup + * fiber's features. + * @phydev: a pointer to a &struct phy_device + * @dst: a pointer to store fiber's features + */ +static void yt8521_prepare_fiber_features(struct phy_device *phydev, + unsigned long *dst) +{ + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, dst); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, dst); + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, dst); + linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, dst); +} + +/** + * yt8521_fiber_setup_forced - configures/forces speed from @phydev + * @phydev: target phy_device struct + * + * NOTE:The caller must have taken the MDIO bus lock. + * + * returns 0 or negative errno code + */ +static int yt8521_fiber_setup_forced(struct phy_device *phydev) +{ + u16 val; + int ret; + + if (phydev->speed == SPEED_1000) + val = YTPHY_MCR_FIBER_1000BX; + else if (phydev->speed == SPEED_100) + val = YTPHY_MCR_FIBER_100FX; + else + return -EINVAL; + + ret = __phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0); + if (ret < 0) + return ret; + + /* disable Fiber auto sensing */ + ret = ytphy_modify_ext(phydev, YT8521_LINK_TIMER_CFG2_REG, + YT8521_LTCR_EN_AUTOSEN, 0); + if (ret < 0) + return ret; + + ret = ytphy_modify_ext(phydev, YTPHY_MISC_CONFIG_REG, + YTPHY_MCR_FIBER_SPEED_MASK, val); + if (ret < 0) + return ret; + + return ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG, + YT8521_CCR_SW_RST, 0); +} + +/** + * ytphy_check_and_restart_aneg - Enable and restart auto-negotiation + * @phydev: target phy_device struct + * @restart: whether aneg restart is requested + * + * NOTE:The caller must have taken the MDIO bus lock. + * + * returns 0 or negative errno code + */ +static int ytphy_check_and_restart_aneg(struct phy_device *phydev, bool restart) +{ + int ret; + + if (!restart) { + /* Advertisement hasn't changed, but maybe aneg was never on to + * begin with? Or maybe phy was isolated? + */ + ret = __phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + + if (!(ret & BMCR_ANENABLE) || (ret & BMCR_ISOLATE)) + restart = true; + } + /* Enable and Restart Autonegotiation + * Don't isolate the PHY if we're negotiating + */ + if (restart) + return __phy_modify(phydev, MII_BMCR, BMCR_ISOLATE, + BMCR_ANENABLE | BMCR_ANRESTART); + + return 0; +} + +/** + * yt8521_fiber_config_aneg - restart auto-negotiation or write + * YTPHY_MISC_CONFIG_REG. + * @phydev: target phy_device struct + * + * NOTE:The caller must have taken the MDIO bus lock. + * + * returns 0 or negative errno code + */ +static int yt8521_fiber_config_aneg(struct phy_device *phydev) +{ + int err, changed = 0; + int bmcr; + u16 adv; + + if (phydev->autoneg != AUTONEG_ENABLE) + return yt8521_fiber_setup_forced(phydev); + + /* enable Fiber auto sensing */ + err = ytphy_modify_ext(phydev, YT8521_LINK_TIMER_CFG2_REG, + 0, YT8521_LTCR_EN_AUTOSEN); + if (err < 0) + return err; + + err = ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG, + YT8521_CCR_SW_RST, 0); + if (err < 0) + return err; + + bmcr = __phy_read(phydev, MII_BMCR); + if (bmcr < 0) + return bmcr; + + /* When it is coming from fiber forced mode, add bmcr power down + * and power up to let aneg work fine. + */ + if (!(bmcr & BMCR_ANENABLE)) { + __phy_modify(phydev, MII_BMCR, 0, BMCR_PDOWN); + usleep_range(1000, 1100); + __phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0); + } + + adv = linkmode_adv_to_mii_adv_x(phydev->advertising, + ETHTOOL_LINK_MODE_1000baseX_Full_BIT); + + /* Setup fiber advertisement */ + err = __phy_modify_changed(phydev, MII_ADVERTISE, + ADVERTISE_1000XHALF | ADVERTISE_1000XFULL | + ADVERTISE_1000XPAUSE | + ADVERTISE_1000XPSE_ASYM, + adv); + if (err < 0) + return err; + + if (err > 0) + changed = 1; + + return ytphy_check_and_restart_aneg(phydev, changed); +} + +/** + * ytphy_setup_master_slave + * @phydev: target phy_device struct + * + * NOTE: The caller must have taken the MDIO bus lock. + * + * returns 0 or negative errno code + */ +static int ytphy_setup_master_slave(struct phy_device *phydev) +{ + u16 ctl = 0; + + if (!phydev->is_gigabit_capable) + return 0; + + switch (phydev->master_slave_set) { + case MASTER_SLAVE_CFG_MASTER_PREFERRED: + ctl |= CTL1000_PREFER_MASTER; + break; + case MASTER_SLAVE_CFG_SLAVE_PREFERRED: + break; + case MASTER_SLAVE_CFG_MASTER_FORCE: + ctl |= CTL1000_AS_MASTER; + fallthrough; + case MASTER_SLAVE_CFG_SLAVE_FORCE: + ctl |= CTL1000_ENABLE_MASTER; + break; + case MASTER_SLAVE_CFG_UNKNOWN: + case MASTER_SLAVE_CFG_UNSUPPORTED: + return 0; + default: + phydev_warn(phydev, "Unsupported Master/Slave mode\n"); + return -EOPNOTSUPP; + } + + return __phy_modify_changed(phydev, MII_CTRL1000, + (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER | + CTL1000_PREFER_MASTER), ctl); +} + +/** + * ytphy_utp_config_advert - sanitize and advertise auto-negotiation parameters + * @phydev: target phy_device struct + * + * NOTE: Writes MII_ADVERTISE with the appropriate values, + * after sanitizing the values to make sure we only advertise + * what is supported. Returns < 0 on error, 0 if the PHY's advertisement + * hasn't changed, and > 0 if it has changed. + * The caller must have taken the MDIO bus lock. + * + * returns 0 or negative errno code + */ +static int ytphy_utp_config_advert(struct phy_device *phydev) +{ + int err, bmsr, changed = 0; + u32 adv; + + /* Only allow advertising what this PHY supports */ + linkmode_and(phydev->advertising, phydev->advertising, + phydev->supported); + + adv = linkmode_adv_to_mii_adv_t(phydev->advertising); + + /* Setup standard advertisement */ + err = __phy_modify_changed(phydev, MII_ADVERTISE, + ADVERTISE_ALL | ADVERTISE_100BASE4 | + ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM, + adv); + if (err < 0) + return err; + if (err > 0) + changed = 1; + + bmsr = __phy_read(phydev, MII_BMSR); + if (bmsr < 0) + return bmsr; + + /* Per 802.3-2008, Section 22.2.4.2.16 Extended status all + * 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a + * logical 1. + */ + if (!(bmsr & BMSR_ESTATEN)) + return changed; + + adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); + + err = __phy_modify_changed(phydev, MII_CTRL1000, + ADVERTISE_1000FULL | ADVERTISE_1000HALF, + adv); + if (err < 0) + return err; + if (err > 0) + changed = 1; + + return changed; +} + +/** + * ytphy_utp_config_aneg - restart auto-negotiation or write BMCR + * @phydev: target phy_device struct + * @changed: whether autoneg is requested + * + * NOTE: If auto-negotiation is enabled, we configure the + * advertising, and then restart auto-negotiation. If it is not + * enabled, then we write the BMCR. + * The caller must have taken the MDIO bus lock. + * + * returns 0 or negative errno code + */ +static int ytphy_utp_config_aneg(struct phy_device *phydev, bool changed) +{ + int err; + u16 ctl; + + err = ytphy_setup_master_slave(phydev); + if (err < 0) + return err; + else if (err) + changed = true; + + if (phydev->autoneg != AUTONEG_ENABLE) { + /* configures/forces speed/duplex from @phydev */ + + ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex); + + return __phy_modify(phydev, MII_BMCR, ~(BMCR_LOOPBACK | + BMCR_ISOLATE | BMCR_PDOWN), ctl); + } + + err = ytphy_utp_config_advert(phydev); + if (err < 0) /* error */ + return err; + else if (err) + changed = true; + + return ytphy_check_and_restart_aneg(phydev, changed); +} + +/** + * yt8521_config_aneg_paged() - switch reg space then call genphy_config_aneg + * of one page + * @phydev: a pointer to a &struct phy_device + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to + * operate. + * + * returns 0 or negative errno code + */ +static int yt8521_config_aneg_paged(struct phy_device *phydev, int page) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(fiber_supported); + struct yt8521_priv *priv = phydev->priv; + int old_page; + int ret = 0; + + page &= YT8521_RSSR_SPACE_MASK; + + old_page = phy_select_page(phydev, page); + if (old_page < 0) + goto err_restore_page; + + /* If reg_page is YT8521_RSSR_TO_BE_ARBITRATED, + * phydev->advertising should be updated. + */ + if (priv->reg_page == YT8521_RSSR_TO_BE_ARBITRATED) { + linkmode_zero(fiber_supported); + yt8521_prepare_fiber_features(phydev, fiber_supported); + + /* prepare fiber_supported, then setup advertising. */ + if (page == YT8521_RSSR_FIBER_SPACE) { + linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, + fiber_supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, + fiber_supported); + linkmode_and(phydev->advertising, + priv->combo_advertising, fiber_supported); + } else { + /* ETHTOOL_LINK_MODE_Autoneg_BIT is also used in utp */ + linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + fiber_supported); + linkmode_andnot(phydev->advertising, + priv->combo_advertising, + fiber_supported); + } + } + + if (page == YT8521_RSSR_FIBER_SPACE) + ret = yt8521_fiber_config_aneg(phydev); + else + ret = ytphy_utp_config_aneg(phydev, false); + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8521_config_aneg() - change reg space then call yt8521_config_aneg_paged + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8521_config_aneg(struct phy_device *phydev) +{ + struct yt8521_priv *priv = phydev->priv; + int ret; + + if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { + ret = yt8521_config_aneg_paged(phydev, priv->reg_page); + if (ret < 0) + return ret; + } else { + /* If reg_page is YT8521_RSSR_TO_BE_ARBITRATED, + * phydev->advertising need to be saved at first run. + * Because it contains the advertising which supported by both + * mac and yt8521(utp and fiber). + */ + if (linkmode_empty(priv->combo_advertising)) { + linkmode_copy(priv->combo_advertising, + phydev->advertising); + } + + ret = yt8521_config_aneg_paged(phydev, YT8521_RSSR_UTP_SPACE); + if (ret < 0) + return ret; + + ret = yt8521_config_aneg_paged(phydev, YT8521_RSSR_FIBER_SPACE); + if (ret < 0) + return ret; + + /* we don't known which will be link, so restore + * phydev->advertising as default value. + */ + linkmode_copy(phydev->advertising, priv->combo_advertising); + } + return 0; +} + +/** + * yt8521_aneg_done_paged() - determines the auto negotiation result of one + * page. + * @phydev: a pointer to a &struct phy_device + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to + * operate. + * + * returns 0(no link)or 1(fiber or utp link) or negative errno code + */ +static int yt8521_aneg_done_paged(struct phy_device *phydev, int page) +{ + int old_page; + int ret = 0; + int link; + + old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK); + if (old_page < 0) + goto err_restore_page; + + ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG); + if (ret < 0) + goto err_restore_page; + + link = !!(ret & YTPHY_SSR_LINK); + ret = link; + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8521_aneg_done() - determines the auto negotiation result + * @phydev: a pointer to a &struct phy_device + * + * returns 0(no link)or 1(fiber or utp link) or negative errno code + */ +static int yt8521_aneg_done(struct phy_device *phydev) +{ + struct yt8521_priv *priv = phydev->priv; + int link_fiber = 0; + int link_utp; + int link; + + if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { + link = yt8521_aneg_done_paged(phydev, priv->reg_page); + } else { + link_utp = yt8521_aneg_done_paged(phydev, + YT8521_RSSR_UTP_SPACE); + if (link_utp < 0) + return link_utp; + + if (!link_utp) { + link_fiber = yt8521_aneg_done_paged(phydev, + YT8521_RSSR_FIBER_SPACE); + if (link_fiber < 0) + return link_fiber; + } + link = link_fiber || link_utp; + phydev_info(phydev, "%s, link_fiber: %d, link_utp: %d\n", + __func__, link_fiber, link_utp); + } + + return link; +} + +/** + * ytphy_utp_read_abilities - read PHY abilities from Clause 22 registers + * @phydev: target phy_device struct + * + * NOTE: Reads the PHY's abilities and populates + * phydev->supported accordingly. + * The caller must have taken the MDIO bus lock. + * + * returns 0 or negative errno code + */ +static int ytphy_utp_read_abilities(struct phy_device *phydev) +{ + int val; + + linkmode_set_bit_array(phy_basic_ports_array, + ARRAY_SIZE(phy_basic_ports_array), + phydev->supported); + + val = __phy_read(phydev, MII_BMSR); + if (val < 0) + return val; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported, + val & BMSR_ANEGCAPABLE); + + linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported, + val & BMSR_100FULL); + linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported, + val & BMSR_100HALF); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, phydev->supported, + val & BMSR_10FULL); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, phydev->supported, + val & BMSR_10HALF); + + if (val & BMSR_ESTATEN) { + val = __phy_read(phydev, MII_ESTATUS); + if (val < 0) + return val; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + phydev->supported, val & ESTATUS_1000_TFULL); + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, + phydev->supported, val & ESTATUS_1000_THALF); + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, + phydev->supported, val & ESTATUS_1000_XFULL); + } + + return 0; +} + +/** + * yt8521_get_features_paged() - read supported link modes for one page + * @phydev: a pointer to a &struct phy_device + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to + * operate. + * + * returns 0 or negative errno code + */ +static int yt8521_get_features_paged(struct phy_device *phydev, int page) +{ + int old_page; + int ret = 0; + + page &= YT8521_RSSR_SPACE_MASK; + old_page = phy_select_page(phydev, page); + if (old_page < 0) + goto err_restore_page; + + if (page == YT8521_RSSR_FIBER_SPACE) { + linkmode_zero(phydev->supported); + yt8521_prepare_fiber_features(phydev, phydev->supported); + } else { + ret = ytphy_utp_read_abilities(phydev); + if (ret < 0) + goto err_restore_page; + } + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8521_get_features - switch reg space then call yt8521_get_features_paged + * @phydev: target phy_device struct + * + * returns 0 or negative errno code + */ +static int yt8521_get_features(struct phy_device *phydev) +{ + struct yt8521_priv *priv = phydev->priv; + int ret; + + if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { + ret = yt8521_get_features_paged(phydev, priv->reg_page); + } else { + ret = yt8521_get_features_paged(phydev, + YT8521_RSSR_UTP_SPACE); + if (ret < 0) + return ret; + + /* add fiber's features to phydev->supported */ + yt8521_prepare_fiber_features(phydev, phydev->supported); + } + return ret; +} + static struct phy_driver motorcomm_phy_drvs[] = { { PHY_ID_MATCH_EXACT(PHY_ID_YT8511), @@ -121,16 +1733,35 @@ static struct phy_driver motorcomm_phy_drvs[] = { .read_page = yt8511_read_page, .write_page = yt8511_write_page, }, + { + PHY_ID_MATCH_EXACT(PHY_ID_YT8521), + .name = "YT8521 Gigabit Ethernet", + .get_features = yt8521_get_features, + .probe = yt8521_probe, + .read_page = yt8521_read_page, + .write_page = yt8521_write_page, + .get_wol = ytphy_get_wol, + .set_wol = ytphy_set_wol, + .config_aneg = yt8521_config_aneg, + .aneg_done = yt8521_aneg_done, + .config_init = yt8521_config_init, + .read_status = yt8521_read_status, + .soft_reset = yt8521_soft_reset, + .suspend = yt8521_suspend, + .resume = yt8521_resume, + }, }; module_phy_driver(motorcomm_phy_drvs); -MODULE_DESCRIPTION("Motorcomm PHY driver"); +MODULE_DESCRIPTION("Motorcomm 8511/8521 PHY driver"); MODULE_AUTHOR("Peter Geis"); +MODULE_AUTHOR("Frank"); MODULE_LICENSE("GPL"); static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = { { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) }, + { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) }, { /* sentinal */ } }; -- cgit v1.2.3 From 41eefc46a3a4682976afb5f8c4b9734ed6bfd406 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sun, 2 Oct 2022 09:51:46 -0700 Subject: string: Convert strscpy() self-test to KUnit Convert the strscpy() self-test to a KUnit test. Cc: David Gow Cc: Tobin C. Harding Tested-by: Nathan Chancellor Link: https://lore.kernel.org/lkml/Y072ZMk/hNkfwqMv@dev-arch.thelio-3990X Signed-off-by: Kees Cook --- MAINTAINERS | 1 + lib/Kconfig.debug | 8 +-- lib/Makefile | 2 +- lib/strscpy_kunit.c | 129 ++++++++++++++++++++++++++++++++++++++++++++ lib/test_strscpy.c | 150 ---------------------------------------------------- 5 files changed, 136 insertions(+), 154 deletions(-) create mode 100644 lib/strscpy_kunit.c delete mode 100644 lib/test_strscpy.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 9dd8d74c4df0..232d78340d79 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8045,6 +8045,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/har F: include/linux/fortify-string.h F: lib/fortify_kunit.c F: lib/memcpy_kunit.c +F: lib/strscpy_kunit.c F: lib/test_fortify/* F: scripts/test_fortify.sh K: \b__NO_FORTIFY\b diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 3fc7abffc7aa..e0a4d52e434c 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2215,9 +2215,6 @@ config STRING_SELFTEST config TEST_STRING_HELPERS tristate "Test functions located in the string_helpers module at runtime" -config TEST_STRSCPY - tristate "Test strscpy*() family of functions at runtime" - config TEST_KSTRTOX tristate "Test kstrto*() family of functions at runtime" @@ -2583,6 +2580,11 @@ config HW_BREAKPOINT_KUNIT_TEST If unsure, say N. +config STRSCPY_KUNIT_TEST + tristate "Test strscpy*() family of functions at runtime" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + config TEST_UDELAY tristate "udelay test driver" help diff --git a/lib/Makefile b/lib/Makefile index 161d6a724ff7..1905e5c26849 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -82,7 +82,6 @@ obj-$(CONFIG_TEST_DYNAMIC_DEBUG) += test_dynamic_debug.o obj-$(CONFIG_TEST_PRINTF) += test_printf.o obj-$(CONFIG_TEST_SCANF) += test_scanf.o obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o -obj-$(CONFIG_TEST_STRSCPY) += test_strscpy.o obj-$(CONFIG_TEST_UUID) += test_uuid.o obj-$(CONFIG_TEST_XARRAY) += test_xarray.o obj-$(CONFIG_TEST_PARMAN) += test_parman.o @@ -380,6 +379,7 @@ obj-$(CONFIG_OVERFLOW_KUNIT_TEST) += overflow_kunit.o CFLAGS_stackinit_kunit.o += $(call cc-disable-warning, switch-unreachable) obj-$(CONFIG_STACKINIT_KUNIT_TEST) += stackinit_kunit.o obj-$(CONFIG_FORTIFY_KUNIT_TEST) += fortify_kunit.o +obj-$(CONFIG_STRSCPY_KUNIT_TEST) += strscpy_kunit.o obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o diff --git a/lib/strscpy_kunit.c b/lib/strscpy_kunit.c new file mode 100644 index 000000000000..98523f828d3a --- /dev/null +++ b/lib/strscpy_kunit.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Kernel module for testing 'strscpy' family of functions. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include + +/* + * tc() - Run a specific test case. + * @src: Source string, argument to strscpy_pad() + * @count: Size of destination buffer, argument to strscpy_pad() + * @expected: Expected return value from call to strscpy_pad() + * @terminator: 1 if there should be a terminating null byte 0 otherwise. + * @chars: Number of characters from the src string expected to be + * written to the dst buffer. + * @pad: Number of pad characters expected (in the tail of dst buffer). + * (@pad does not include the null terminator byte.) + * + * Calls strscpy_pad() and verifies the return value and state of the + * destination buffer after the call returns. + */ +static void tc(struct kunit *test, char *src, int count, int expected, + int chars, int terminator, int pad) +{ + int nr_bytes_poison; + int max_expected; + int max_count; + int written; + char buf[6]; + int index, i; + const char POISON = 'z'; + + KUNIT_ASSERT_TRUE_MSG(test, src != NULL, + "null source string not supported"); + + memset(buf, POISON, sizeof(buf)); + /* Future proofing test suite, validate args */ + max_count = sizeof(buf) - 2; /* Space for null and to verify overflow */ + max_expected = count - 1; /* Space for the null */ + + KUNIT_ASSERT_LE_MSG(test, count, max_count, + "count (%d) is too big (%d) ... aborting", count, max_count); + KUNIT_EXPECT_LE_MSG(test, expected, max_expected, + "expected (%d) is bigger than can possibly be returned (%d)", + expected, max_expected); + + written = strscpy_pad(buf, src, count); + KUNIT_ASSERT_EQ(test, written, expected); + + if (count && written == -E2BIG) { + KUNIT_ASSERT_EQ_MSG(test, 0, strncmp(buf, src, count - 1), + "buffer state invalid for -E2BIG"); + KUNIT_ASSERT_EQ_MSG(test, buf[count - 1], '\0', + "too big string is not null terminated correctly"); + } + + for (i = 0; i < chars; i++) + KUNIT_ASSERT_EQ_MSG(test, buf[i], src[i], + "buf[i]==%c != src[i]==%c", buf[i], src[i]); + + if (terminator) + KUNIT_ASSERT_EQ_MSG(test, buf[count - 1], '\0', + "string is not null terminated correctly"); + + for (i = 0; i < pad; i++) { + index = chars + terminator + i; + KUNIT_ASSERT_EQ_MSG(test, buf[index], '\0', + "padding missing at index: %d", i); + } + + nr_bytes_poison = sizeof(buf) - chars - terminator - pad; + for (i = 0; i < nr_bytes_poison; i++) { + index = sizeof(buf) - 1 - i; /* Check from the end back */ + KUNIT_ASSERT_EQ_MSG(test, buf[index], POISON, + "poison value missing at index: %d", i); + } +} + +static void strscpy_test(struct kunit *test) +{ + /* + * tc() uses a destination buffer of size 6 and needs at + * least 2 characters spare (one for null and one to check for + * overflow). This means we should only call tc() with + * strings up to a maximum of 4 characters long and 'count' + * should not exceed 4. To test with longer strings increase + * the buffer size in tc(). + */ + + /* tc(test, src, count, expected, chars, terminator, pad) */ + tc(test, "a", 0, -E2BIG, 0, 0, 0); + tc(test, "", 0, -E2BIG, 0, 0, 0); + + tc(test, "a", 1, -E2BIG, 0, 1, 0); + tc(test, "", 1, 0, 0, 1, 0); + + tc(test, "ab", 2, -E2BIG, 1, 1, 0); + tc(test, "a", 2, 1, 1, 1, 0); + tc(test, "", 2, 0, 0, 1, 1); + + tc(test, "abc", 3, -E2BIG, 2, 1, 0); + tc(test, "ab", 3, 2, 2, 1, 0); + tc(test, "a", 3, 1, 1, 1, 1); + tc(test, "", 3, 0, 0, 1, 2); + + tc(test, "abcd", 4, -E2BIG, 3, 1, 0); + tc(test, "abc", 4, 3, 3, 1, 0); + tc(test, "ab", 4, 2, 2, 1, 1); + tc(test, "a", 4, 1, 1, 1, 2); + tc(test, "", 4, 0, 0, 1, 3); +} + +static struct kunit_case strscpy_test_cases[] = { + KUNIT_CASE(strscpy_test), + {} +}; + +static struct kunit_suite strscpy_test_suite = { + .name = "strscpy", + .test_cases = strscpy_test_cases, +}; + +kunit_test_suite(strscpy_test_suite); + +MODULE_AUTHOR("Tobin C. Harding "); +MODULE_LICENSE("GPL"); diff --git a/lib/test_strscpy.c b/lib/test_strscpy.c deleted file mode 100644 index a827f94601f5..000000000000 --- a/lib/test_strscpy.c +++ /dev/null @@ -1,150 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include - -#include "../tools/testing/selftests/kselftest_module.h" - -/* - * Kernel module for testing 'strscpy' family of functions. - */ - -KSTM_MODULE_GLOBALS(); - -/* - * tc() - Run a specific test case. - * @src: Source string, argument to strscpy_pad() - * @count: Size of destination buffer, argument to strscpy_pad() - * @expected: Expected return value from call to strscpy_pad() - * @terminator: 1 if there should be a terminating null byte 0 otherwise. - * @chars: Number of characters from the src string expected to be - * written to the dst buffer. - * @pad: Number of pad characters expected (in the tail of dst buffer). - * (@pad does not include the null terminator byte.) - * - * Calls strscpy_pad() and verifies the return value and state of the - * destination buffer after the call returns. - */ -static int __init tc(char *src, int count, int expected, - int chars, int terminator, int pad) -{ - int nr_bytes_poison; - int max_expected; - int max_count; - int written; - char buf[6]; - int index, i; - const char POISON = 'z'; - - total_tests++; - - if (!src) { - pr_err("null source string not supported\n"); - return -1; - } - - memset(buf, POISON, sizeof(buf)); - /* Future proofing test suite, validate args */ - max_count = sizeof(buf) - 2; /* Space for null and to verify overflow */ - max_expected = count - 1; /* Space for the null */ - if (count > max_count) { - pr_err("count (%d) is too big (%d) ... aborting", count, max_count); - return -1; - } - if (expected > max_expected) { - pr_warn("expected (%d) is bigger than can possibly be returned (%d)", - expected, max_expected); - } - - written = strscpy_pad(buf, src, count); - if ((written) != (expected)) { - pr_err("%d != %d (written, expected)\n", written, expected); - goto fail; - } - - if (count && written == -E2BIG) { - if (strncmp(buf, src, count - 1) != 0) { - pr_err("buffer state invalid for -E2BIG\n"); - goto fail; - } - if (buf[count - 1] != '\0') { - pr_err("too big string is not null terminated correctly\n"); - goto fail; - } - } - - for (i = 0; i < chars; i++) { - if (buf[i] != src[i]) { - pr_err("buf[i]==%c != src[i]==%c\n", buf[i], src[i]); - goto fail; - } - } - - if (terminator) { - if (buf[count - 1] != '\0') { - pr_err("string is not null terminated correctly\n"); - goto fail; - } - } - - for (i = 0; i < pad; i++) { - index = chars + terminator + i; - if (buf[index] != '\0') { - pr_err("padding missing at index: %d\n", i); - goto fail; - } - } - - nr_bytes_poison = sizeof(buf) - chars - terminator - pad; - for (i = 0; i < nr_bytes_poison; i++) { - index = sizeof(buf) - 1 - i; /* Check from the end back */ - if (buf[index] != POISON) { - pr_err("poison value missing at index: %d\n", i); - goto fail; - } - } - - return 0; -fail: - failed_tests++; - return -1; -} - -static void __init selftest(void) -{ - /* - * tc() uses a destination buffer of size 6 and needs at - * least 2 characters spare (one for null and one to check for - * overflow). This means we should only call tc() with - * strings up to a maximum of 4 characters long and 'count' - * should not exceed 4. To test with longer strings increase - * the buffer size in tc(). - */ - - /* tc(src, count, expected, chars, terminator, pad) */ - KSTM_CHECK_ZERO(tc("a", 0, -E2BIG, 0, 0, 0)); - KSTM_CHECK_ZERO(tc("", 0, -E2BIG, 0, 0, 0)); - - KSTM_CHECK_ZERO(tc("a", 1, -E2BIG, 0, 1, 0)); - KSTM_CHECK_ZERO(tc("", 1, 0, 0, 1, 0)); - - KSTM_CHECK_ZERO(tc("ab", 2, -E2BIG, 1, 1, 0)); - KSTM_CHECK_ZERO(tc("a", 2, 1, 1, 1, 0)); - KSTM_CHECK_ZERO(tc("", 2, 0, 0, 1, 1)); - - KSTM_CHECK_ZERO(tc("abc", 3, -E2BIG, 2, 1, 0)); - KSTM_CHECK_ZERO(tc("ab", 3, 2, 2, 1, 0)); - KSTM_CHECK_ZERO(tc("a", 3, 1, 1, 1, 1)); - KSTM_CHECK_ZERO(tc("", 3, 0, 0, 1, 2)); - - KSTM_CHECK_ZERO(tc("abcd", 4, -E2BIG, 3, 1, 0)); - KSTM_CHECK_ZERO(tc("abc", 4, 3, 3, 1, 0)); - KSTM_CHECK_ZERO(tc("ab", 4, 2, 2, 1, 1)); - KSTM_CHECK_ZERO(tc("a", 4, 1, 1, 1, 2)); - KSTM_CHECK_ZERO(tc("", 4, 0, 0, 1, 3)); -} - -KSTM_MODULE_LOADERS(test_strscpy); -MODULE_AUTHOR("Tobin C. Harding "); -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From fb3d88ab354b3b07e805aba9d67cbb43d23dc70e Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sun, 2 Oct 2022 19:45:23 -0700 Subject: siphash: Convert selftest to KUnit Convert the siphash self-test to KUnit so it will be included in "all KUnit tests" coverage, and can be run individually still: $ ./tools/testing/kunit/kunit.py run siphash ... [02:58:45] Starting KUnit Kernel (1/1)... [02:58:45] ============================================================ [02:58:45] =================== siphash (1 subtest) ==================== [02:58:45] [PASSED] siphash_test [02:58:45] ===================== [PASSED] siphash ===================== [02:58:45] ============================================================ [02:58:45] Testing complete. Ran 1 tests: passed: 1 [02:58:45] Elapsed time: 21.421s total, 4.306s configuring, 16.947s building, 0.148s running Cc: Vlastimil Babka Cc: "Steven Rostedt (Google)" Cc: Yury Norov Cc: Sander Vanheule Acked-by: "Jason A. Donenfeld" Link: https://lore.kernel.org/lkml/CAHmME9r+9MPH6zk3Vn=buEMSbQiWMFryqqzerKarmjYk+tHLJA@mail.gmail.com Tested-by: David Gow Signed-off-by: Kees Cook --- MAINTAINERS | 2 +- lib/Kconfig.debug | 20 ++--- lib/Makefile | 2 +- lib/siphash_kunit.c | 197 ++++++++++++++++++++++++++++++++++++++++++++++ lib/test_siphash.c | 222 ---------------------------------------------------- 5 files changed, 210 insertions(+), 233 deletions(-) create mode 100644 lib/siphash_kunit.c delete mode 100644 lib/test_siphash.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 232d78340d79..1cd80c113721 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18864,7 +18864,7 @@ M: Jason A. Donenfeld S: Maintained F: include/linux/siphash.h F: lib/siphash.c -F: lib/test_siphash.c +F: lib/siphash_kunit.c SIS 190 ETHERNET DRIVER M: Francois Romieu diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index e0a4d52e434c..50cc1c4efadd 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2244,15 +2244,6 @@ config TEST_RHASHTABLE If unsure, say N. -config TEST_SIPHASH - tristate "Perform selftest on siphash functions" - help - Enable this option to test the kernel's siphash () hash - functions on boot (or module load). - - This is intended to help people writing architecture-specific - optimized versions. If unsure, say N. - config TEST_IDA tristate "Perform selftest on IDA functions" @@ -2585,6 +2576,17 @@ config STRSCPY_KUNIT_TEST depends on KUNIT default KUNIT_ALL_TESTS +config SIPHASH_KUNIT_TEST + tristate "Perform selftest on siphash functions" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + Enable this option to test the kernel's siphash () hash + functions on boot (or module load). + + This is intended to help people writing architecture-specific + optimized versions. If unsure, say N. + config TEST_UDELAY tristate "udelay test driver" help diff --git a/lib/Makefile b/lib/Makefile index 1905e5c26849..77c7951c8cf0 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -62,7 +62,6 @@ obj-$(CONFIG_TEST_BITOPS) += test_bitops.o CFLAGS_test_bitops.o += -Werror obj-$(CONFIG_CPUMASK_KUNIT_TEST) += cpumask_kunit.o obj-$(CONFIG_TEST_SYSCTL) += test_sysctl.o -obj-$(CONFIG_TEST_SIPHASH) += test_siphash.o obj-$(CONFIG_HASH_KUNIT_TEST) += test_hash.o obj-$(CONFIG_TEST_IDA) += test_ida.o obj-$(CONFIG_TEST_UBSAN) += test_ubsan.o @@ -380,6 +379,7 @@ CFLAGS_stackinit_kunit.o += $(call cc-disable-warning, switch-unreachable) obj-$(CONFIG_STACKINIT_KUNIT_TEST) += stackinit_kunit.o obj-$(CONFIG_FORTIFY_KUNIT_TEST) += fortify_kunit.o obj-$(CONFIG_STRSCPY_KUNIT_TEST) += strscpy_kunit.o +obj-$(CONFIG_SIPHASH_KUNIT_TEST) += siphash_kunit.o obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o diff --git a/lib/siphash_kunit.c b/lib/siphash_kunit.c new file mode 100644 index 000000000000..a3c697e8be35 --- /dev/null +++ b/lib/siphash_kunit.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* Copyright (C) 2016-2022 Jason A. Donenfeld . All Rights Reserved. + * + * Test cases for siphash.c + * + * SipHash: a fast short-input PRF + * https://131002.net/siphash/ + * + * This implementation is specifically for SipHash2-4 for a secure PRF + * and HalfSipHash1-3/SipHash1-3 for an insecure PRF only suitable for + * hashtables. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +/* Test vectors taken from reference source available at: + * https://github.com/veorq/SipHash + */ + +static const siphash_key_t test_key_siphash = + {{ 0x0706050403020100ULL, 0x0f0e0d0c0b0a0908ULL }}; + +static const u64 test_vectors_siphash[64] = { + 0x726fdb47dd0e0e31ULL, 0x74f839c593dc67fdULL, 0x0d6c8009d9a94f5aULL, + 0x85676696d7fb7e2dULL, 0xcf2794e0277187b7ULL, 0x18765564cd99a68dULL, + 0xcbc9466e58fee3ceULL, 0xab0200f58b01d137ULL, 0x93f5f5799a932462ULL, + 0x9e0082df0ba9e4b0ULL, 0x7a5dbbc594ddb9f3ULL, 0xf4b32f46226bada7ULL, + 0x751e8fbc860ee5fbULL, 0x14ea5627c0843d90ULL, 0xf723ca908e7af2eeULL, + 0xa129ca6149be45e5ULL, 0x3f2acc7f57c29bdbULL, 0x699ae9f52cbe4794ULL, + 0x4bc1b3f0968dd39cULL, 0xbb6dc91da77961bdULL, 0xbed65cf21aa2ee98ULL, + 0xd0f2cbb02e3b67c7ULL, 0x93536795e3a33e88ULL, 0xa80c038ccd5ccec8ULL, + 0xb8ad50c6f649af94ULL, 0xbce192de8a85b8eaULL, 0x17d835b85bbb15f3ULL, + 0x2f2e6163076bcfadULL, 0xde4daaaca71dc9a5ULL, 0xa6a2506687956571ULL, + 0xad87a3535c49ef28ULL, 0x32d892fad841c342ULL, 0x7127512f72f27cceULL, + 0xa7f32346f95978e3ULL, 0x12e0b01abb051238ULL, 0x15e034d40fa197aeULL, + 0x314dffbe0815a3b4ULL, 0x027990f029623981ULL, 0xcadcd4e59ef40c4dULL, + 0x9abfd8766a33735cULL, 0x0e3ea96b5304a7d0ULL, 0xad0c42d6fc585992ULL, + 0x187306c89bc215a9ULL, 0xd4a60abcf3792b95ULL, 0xf935451de4f21df2ULL, + 0xa9538f0419755787ULL, 0xdb9acddff56ca510ULL, 0xd06c98cd5c0975ebULL, + 0xe612a3cb9ecba951ULL, 0xc766e62cfcadaf96ULL, 0xee64435a9752fe72ULL, + 0xa192d576b245165aULL, 0x0a8787bf8ecb74b2ULL, 0x81b3e73d20b49b6fULL, + 0x7fa8220ba3b2eceaULL, 0x245731c13ca42499ULL, 0xb78dbfaf3a8d83bdULL, + 0xea1ad565322a1a0bULL, 0x60e61c23a3795013ULL, 0x6606d7e446282b93ULL, + 0x6ca4ecb15c5f91e1ULL, 0x9f626da15c9625f3ULL, 0xe51b38608ef25f57ULL, + 0x958a324ceb064572ULL +}; + +#if BITS_PER_LONG == 64 +static const hsiphash_key_t test_key_hsiphash = + {{ 0x0706050403020100ULL, 0x0f0e0d0c0b0a0908ULL }}; + +static const u32 test_vectors_hsiphash[64] = { + 0x050fc4dcU, 0x7d57ca93U, 0x4dc7d44dU, + 0xe7ddf7fbU, 0x88d38328U, 0x49533b67U, + 0xc59f22a7U, 0x9bb11140U, 0x8d299a8eU, + 0x6c063de4U, 0x92ff097fU, 0xf94dc352U, + 0x57b4d9a2U, 0x1229ffa7U, 0xc0f95d34U, + 0x2a519956U, 0x7d908b66U, 0x63dbd80cU, + 0xb473e63eU, 0x8d297d1cU, 0xa6cce040U, + 0x2b45f844U, 0xa320872eU, 0xdae6c123U, + 0x67349c8cU, 0x705b0979U, 0xca9913a5U, + 0x4ade3b35U, 0xef6cd00dU, 0x4ab1e1f4U, + 0x43c5e663U, 0x8c21d1bcU, 0x16a7b60dU, + 0x7a8ff9bfU, 0x1f2a753eU, 0xbf186b91U, + 0xada26206U, 0xa3c33057U, 0xae3a36a1U, + 0x7b108392U, 0x99e41531U, 0x3f1ad944U, + 0xc8138825U, 0xc28949a6U, 0xfaf8876bU, + 0x9f042196U, 0x68b1d623U, 0x8b5114fdU, + 0xdf074c46U, 0x12cc86b3U, 0x0a52098fU, + 0x9d292f9aU, 0xa2f41f12U, 0x43a71ed0U, + 0x73f0bce6U, 0x70a7e980U, 0x243c6d75U, + 0xfdb71513U, 0xa67d8a08U, 0xb7e8f148U, + 0xf7a644eeU, 0x0f1837f2U, 0x4b6694e0U, + 0xb7bbb3a8U +}; +#else +static const hsiphash_key_t test_key_hsiphash = + {{ 0x03020100U, 0x07060504U }}; + +static const u32 test_vectors_hsiphash[64] = { + 0x5814c896U, 0xe7e864caU, 0xbc4b0e30U, + 0x01539939U, 0x7e059ea6U, 0x88e3d89bU, + 0xa0080b65U, 0x9d38d9d6U, 0x577999b1U, + 0xc839caedU, 0xe4fa32cfU, 0x959246eeU, + 0x6b28096cU, 0x66dd9cd6U, 0x16658a7cU, + 0xd0257b04U, 0x8b31d501U, 0x2b1cd04bU, + 0x06712339U, 0x522aca67U, 0x911bb605U, + 0x90a65f0eU, 0xf826ef7bU, 0x62512debU, + 0x57150ad7U, 0x5d473507U, 0x1ec47442U, + 0xab64afd3U, 0x0a4100d0U, 0x6d2ce652U, + 0x2331b6a3U, 0x08d8791aU, 0xbc6dda8dU, + 0xe0f6c934U, 0xb0652033U, 0x9b9851ccU, + 0x7c46fb7fU, 0x732ba8cbU, 0xf142997aU, + 0xfcc9aa1bU, 0x05327eb2U, 0xe110131cU, + 0xf9e5e7c0U, 0xa7d708a6U, 0x11795ab1U, + 0x65671619U, 0x9f5fff91U, 0xd89c5267U, + 0x007783ebU, 0x95766243U, 0xab639262U, + 0x9c7e1390U, 0xc368dda6U, 0x38ddc455U, + 0xfa13d379U, 0x979ea4e8U, 0x53ecd77eU, + 0x2ee80657U, 0x33dbb66aU, 0xae3f0577U, + 0x88b4c4ccU, 0x3e7f480bU, 0x74c1ebf8U, + 0x87178304U +}; +#endif + +#define chk(hash, vector, fmt...) \ + KUNIT_EXPECT_EQ_MSG(test, hash, vector, fmt) + +static void siphash_test(struct kunit *test) +{ + u8 in[64] __aligned(SIPHASH_ALIGNMENT); + u8 in_unaligned[65] __aligned(SIPHASH_ALIGNMENT); + u8 i; + + for (i = 0; i < 64; ++i) { + in[i] = i; + in_unaligned[i + 1] = i; + chk(siphash(in, i, &test_key_siphash), + test_vectors_siphash[i], + "siphash self-test aligned %u: FAIL", i + 1); + chk(siphash(in_unaligned + 1, i, &test_key_siphash), + test_vectors_siphash[i], + "siphash self-test unaligned %u: FAIL", i + 1); + chk(hsiphash(in, i, &test_key_hsiphash), + test_vectors_hsiphash[i], + "hsiphash self-test aligned %u: FAIL", i + 1); + chk(hsiphash(in_unaligned + 1, i, &test_key_hsiphash), + test_vectors_hsiphash[i], + "hsiphash self-test unaligned %u: FAIL", i + 1); + } + chk(siphash_1u64(0x0706050403020100ULL, &test_key_siphash), + test_vectors_siphash[8], + "siphash self-test 1u64: FAIL"); + chk(siphash_2u64(0x0706050403020100ULL, 0x0f0e0d0c0b0a0908ULL, + &test_key_siphash), + test_vectors_siphash[16], + "siphash self-test 2u64: FAIL"); + chk(siphash_3u64(0x0706050403020100ULL, 0x0f0e0d0c0b0a0908ULL, + 0x1716151413121110ULL, &test_key_siphash), + test_vectors_siphash[24], + "siphash self-test 3u64: FAIL"); + chk(siphash_4u64(0x0706050403020100ULL, 0x0f0e0d0c0b0a0908ULL, + 0x1716151413121110ULL, 0x1f1e1d1c1b1a1918ULL, + &test_key_siphash), + test_vectors_siphash[32], + "siphash self-test 4u64: FAIL"); + chk(siphash_1u32(0x03020100U, &test_key_siphash), + test_vectors_siphash[4], + "siphash self-test 1u32: FAIL"); + chk(siphash_2u32(0x03020100U, 0x07060504U, &test_key_siphash), + test_vectors_siphash[8], + "siphash self-test 2u32: FAIL"); + chk(siphash_3u32(0x03020100U, 0x07060504U, + 0x0b0a0908U, &test_key_siphash), + test_vectors_siphash[12], + "siphash self-test 3u32: FAIL"); + chk(siphash_4u32(0x03020100U, 0x07060504U, + 0x0b0a0908U, 0x0f0e0d0cU, &test_key_siphash), + test_vectors_siphash[16], + "siphash self-test 4u32: FAIL"); + chk(hsiphash_1u32(0x03020100U, &test_key_hsiphash), + test_vectors_hsiphash[4], + "hsiphash self-test 1u32: FAIL"); + chk(hsiphash_2u32(0x03020100U, 0x07060504U, &test_key_hsiphash), + test_vectors_hsiphash[8], + "hsiphash self-test 2u32: FAIL"); + chk(hsiphash_3u32(0x03020100U, 0x07060504U, + 0x0b0a0908U, &test_key_hsiphash), + test_vectors_hsiphash[12], + "hsiphash self-test 3u32: FAIL"); + chk(hsiphash_4u32(0x03020100U, 0x07060504U, + 0x0b0a0908U, 0x0f0e0d0cU, &test_key_hsiphash), + test_vectors_hsiphash[16], + "hsiphash self-test 4u32: FAIL"); +} + +static struct kunit_case siphash_test_cases[] = { + KUNIT_CASE(siphash_test), + {} +}; + +static struct kunit_suite siphash_test_suite = { + .name = "siphash", + .test_cases = siphash_test_cases, +}; + +kunit_test_suite(siphash_test_suite); + +MODULE_AUTHOR("Jason A. Donenfeld "); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/lib/test_siphash.c b/lib/test_siphash.c deleted file mode 100644 index a96788d0141d..000000000000 --- a/lib/test_siphash.c +++ /dev/null @@ -1,222 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) -/* Copyright (C) 2016-2022 Jason A. Donenfeld . All Rights Reserved. - * - * Test cases for siphash.c - * - * SipHash: a fast short-input PRF - * https://131002.net/siphash/ - * - * This implementation is specifically for SipHash2-4 for a secure PRF - * and HalfSipHash1-3/SipHash1-3 for an insecure PRF only suitable for - * hashtables. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include - -/* Test vectors taken from reference source available at: - * https://github.com/veorq/SipHash - */ - -static const siphash_key_t test_key_siphash = - {{ 0x0706050403020100ULL, 0x0f0e0d0c0b0a0908ULL }}; - -static const u64 test_vectors_siphash[64] = { - 0x726fdb47dd0e0e31ULL, 0x74f839c593dc67fdULL, 0x0d6c8009d9a94f5aULL, - 0x85676696d7fb7e2dULL, 0xcf2794e0277187b7ULL, 0x18765564cd99a68dULL, - 0xcbc9466e58fee3ceULL, 0xab0200f58b01d137ULL, 0x93f5f5799a932462ULL, - 0x9e0082df0ba9e4b0ULL, 0x7a5dbbc594ddb9f3ULL, 0xf4b32f46226bada7ULL, - 0x751e8fbc860ee5fbULL, 0x14ea5627c0843d90ULL, 0xf723ca908e7af2eeULL, - 0xa129ca6149be45e5ULL, 0x3f2acc7f57c29bdbULL, 0x699ae9f52cbe4794ULL, - 0x4bc1b3f0968dd39cULL, 0xbb6dc91da77961bdULL, 0xbed65cf21aa2ee98ULL, - 0xd0f2cbb02e3b67c7ULL, 0x93536795e3a33e88ULL, 0xa80c038ccd5ccec8ULL, - 0xb8ad50c6f649af94ULL, 0xbce192de8a85b8eaULL, 0x17d835b85bbb15f3ULL, - 0x2f2e6163076bcfadULL, 0xde4daaaca71dc9a5ULL, 0xa6a2506687956571ULL, - 0xad87a3535c49ef28ULL, 0x32d892fad841c342ULL, 0x7127512f72f27cceULL, - 0xa7f32346f95978e3ULL, 0x12e0b01abb051238ULL, 0x15e034d40fa197aeULL, - 0x314dffbe0815a3b4ULL, 0x027990f029623981ULL, 0xcadcd4e59ef40c4dULL, - 0x9abfd8766a33735cULL, 0x0e3ea96b5304a7d0ULL, 0xad0c42d6fc585992ULL, - 0x187306c89bc215a9ULL, 0xd4a60abcf3792b95ULL, 0xf935451de4f21df2ULL, - 0xa9538f0419755787ULL, 0xdb9acddff56ca510ULL, 0xd06c98cd5c0975ebULL, - 0xe612a3cb9ecba951ULL, 0xc766e62cfcadaf96ULL, 0xee64435a9752fe72ULL, - 0xa192d576b245165aULL, 0x0a8787bf8ecb74b2ULL, 0x81b3e73d20b49b6fULL, - 0x7fa8220ba3b2eceaULL, 0x245731c13ca42499ULL, 0xb78dbfaf3a8d83bdULL, - 0xea1ad565322a1a0bULL, 0x60e61c23a3795013ULL, 0x6606d7e446282b93ULL, - 0x6ca4ecb15c5f91e1ULL, 0x9f626da15c9625f3ULL, 0xe51b38608ef25f57ULL, - 0x958a324ceb064572ULL -}; - -#if BITS_PER_LONG == 64 -static const hsiphash_key_t test_key_hsiphash = - {{ 0x0706050403020100ULL, 0x0f0e0d0c0b0a0908ULL }}; - -static const u32 test_vectors_hsiphash[64] = { - 0x050fc4dcU, 0x7d57ca93U, 0x4dc7d44dU, - 0xe7ddf7fbU, 0x88d38328U, 0x49533b67U, - 0xc59f22a7U, 0x9bb11140U, 0x8d299a8eU, - 0x6c063de4U, 0x92ff097fU, 0xf94dc352U, - 0x57b4d9a2U, 0x1229ffa7U, 0xc0f95d34U, - 0x2a519956U, 0x7d908b66U, 0x63dbd80cU, - 0xb473e63eU, 0x8d297d1cU, 0xa6cce040U, - 0x2b45f844U, 0xa320872eU, 0xdae6c123U, - 0x67349c8cU, 0x705b0979U, 0xca9913a5U, - 0x4ade3b35U, 0xef6cd00dU, 0x4ab1e1f4U, - 0x43c5e663U, 0x8c21d1bcU, 0x16a7b60dU, - 0x7a8ff9bfU, 0x1f2a753eU, 0xbf186b91U, - 0xada26206U, 0xa3c33057U, 0xae3a36a1U, - 0x7b108392U, 0x99e41531U, 0x3f1ad944U, - 0xc8138825U, 0xc28949a6U, 0xfaf8876bU, - 0x9f042196U, 0x68b1d623U, 0x8b5114fdU, - 0xdf074c46U, 0x12cc86b3U, 0x0a52098fU, - 0x9d292f9aU, 0xa2f41f12U, 0x43a71ed0U, - 0x73f0bce6U, 0x70a7e980U, 0x243c6d75U, - 0xfdb71513U, 0xa67d8a08U, 0xb7e8f148U, - 0xf7a644eeU, 0x0f1837f2U, 0x4b6694e0U, - 0xb7bbb3a8U -}; -#else -static const hsiphash_key_t test_key_hsiphash = - {{ 0x03020100U, 0x07060504U }}; - -static const u32 test_vectors_hsiphash[64] = { - 0x5814c896U, 0xe7e864caU, 0xbc4b0e30U, - 0x01539939U, 0x7e059ea6U, 0x88e3d89bU, - 0xa0080b65U, 0x9d38d9d6U, 0x577999b1U, - 0xc839caedU, 0xe4fa32cfU, 0x959246eeU, - 0x6b28096cU, 0x66dd9cd6U, 0x16658a7cU, - 0xd0257b04U, 0x8b31d501U, 0x2b1cd04bU, - 0x06712339U, 0x522aca67U, 0x911bb605U, - 0x90a65f0eU, 0xf826ef7bU, 0x62512debU, - 0x57150ad7U, 0x5d473507U, 0x1ec47442U, - 0xab64afd3U, 0x0a4100d0U, 0x6d2ce652U, - 0x2331b6a3U, 0x08d8791aU, 0xbc6dda8dU, - 0xe0f6c934U, 0xb0652033U, 0x9b9851ccU, - 0x7c46fb7fU, 0x732ba8cbU, 0xf142997aU, - 0xfcc9aa1bU, 0x05327eb2U, 0xe110131cU, - 0xf9e5e7c0U, 0xa7d708a6U, 0x11795ab1U, - 0x65671619U, 0x9f5fff91U, 0xd89c5267U, - 0x007783ebU, 0x95766243U, 0xab639262U, - 0x9c7e1390U, 0xc368dda6U, 0x38ddc455U, - 0xfa13d379U, 0x979ea4e8U, 0x53ecd77eU, - 0x2ee80657U, 0x33dbb66aU, 0xae3f0577U, - 0x88b4c4ccU, 0x3e7f480bU, 0x74c1ebf8U, - 0x87178304U -}; -#endif - -static int __init siphash_test_init(void) -{ - u8 in[64] __aligned(SIPHASH_ALIGNMENT); - u8 in_unaligned[65] __aligned(SIPHASH_ALIGNMENT); - u8 i; - int ret = 0; - - for (i = 0; i < 64; ++i) { - in[i] = i; - in_unaligned[i + 1] = i; - if (siphash(in, i, &test_key_siphash) != - test_vectors_siphash[i]) { - pr_info("siphash self-test aligned %u: FAIL\n", i + 1); - ret = -EINVAL; - } - if (siphash(in_unaligned + 1, i, &test_key_siphash) != - test_vectors_siphash[i]) { - pr_info("siphash self-test unaligned %u: FAIL\n", i + 1); - ret = -EINVAL; - } - if (hsiphash(in, i, &test_key_hsiphash) != - test_vectors_hsiphash[i]) { - pr_info("hsiphash self-test aligned %u: FAIL\n", i + 1); - ret = -EINVAL; - } - if (hsiphash(in_unaligned + 1, i, &test_key_hsiphash) != - test_vectors_hsiphash[i]) { - pr_info("hsiphash self-test unaligned %u: FAIL\n", i + 1); - ret = -EINVAL; - } - } - if (siphash_1u64(0x0706050403020100ULL, &test_key_siphash) != - test_vectors_siphash[8]) { - pr_info("siphash self-test 1u64: FAIL\n"); - ret = -EINVAL; - } - if (siphash_2u64(0x0706050403020100ULL, 0x0f0e0d0c0b0a0908ULL, - &test_key_siphash) != test_vectors_siphash[16]) { - pr_info("siphash self-test 2u64: FAIL\n"); - ret = -EINVAL; - } - if (siphash_3u64(0x0706050403020100ULL, 0x0f0e0d0c0b0a0908ULL, - 0x1716151413121110ULL, &test_key_siphash) != - test_vectors_siphash[24]) { - pr_info("siphash self-test 3u64: FAIL\n"); - ret = -EINVAL; - } - if (siphash_4u64(0x0706050403020100ULL, 0x0f0e0d0c0b0a0908ULL, - 0x1716151413121110ULL, 0x1f1e1d1c1b1a1918ULL, - &test_key_siphash) != test_vectors_siphash[32]) { - pr_info("siphash self-test 4u64: FAIL\n"); - ret = -EINVAL; - } - if (siphash_1u32(0x03020100U, &test_key_siphash) != - test_vectors_siphash[4]) { - pr_info("siphash self-test 1u32: FAIL\n"); - ret = -EINVAL; - } - if (siphash_2u32(0x03020100U, 0x07060504U, &test_key_siphash) != - test_vectors_siphash[8]) { - pr_info("siphash self-test 2u32: FAIL\n"); - ret = -EINVAL; - } - if (siphash_3u32(0x03020100U, 0x07060504U, - 0x0b0a0908U, &test_key_siphash) != - test_vectors_siphash[12]) { - pr_info("siphash self-test 3u32: FAIL\n"); - ret = -EINVAL; - } - if (siphash_4u32(0x03020100U, 0x07060504U, - 0x0b0a0908U, 0x0f0e0d0cU, &test_key_siphash) != - test_vectors_siphash[16]) { - pr_info("siphash self-test 4u32: FAIL\n"); - ret = -EINVAL; - } - if (hsiphash_1u32(0x03020100U, &test_key_hsiphash) != - test_vectors_hsiphash[4]) { - pr_info("hsiphash self-test 1u32: FAIL\n"); - ret = -EINVAL; - } - if (hsiphash_2u32(0x03020100U, 0x07060504U, &test_key_hsiphash) != - test_vectors_hsiphash[8]) { - pr_info("hsiphash self-test 2u32: FAIL\n"); - ret = -EINVAL; - } - if (hsiphash_3u32(0x03020100U, 0x07060504U, - 0x0b0a0908U, &test_key_hsiphash) != - test_vectors_hsiphash[12]) { - pr_info("hsiphash self-test 3u32: FAIL\n"); - ret = -EINVAL; - } - if (hsiphash_4u32(0x03020100U, 0x07060504U, - 0x0b0a0908U, 0x0f0e0d0cU, &test_key_hsiphash) != - test_vectors_hsiphash[16]) { - pr_info("hsiphash self-test 4u32: FAIL\n"); - ret = -EINVAL; - } - if (!ret) - pr_info("self-tests: pass\n"); - return ret; -} - -static void __exit siphash_test_exit(void) -{ -} - -module_init(siphash_test_init); -module_exit(siphash_test_exit); - -MODULE_AUTHOR("Jason A. Donenfeld "); -MODULE_LICENSE("Dual BSD/GPL"); -- cgit v1.2.3 From 444064185df7c4b0f7bb41e25b1bb197a1e4a918 Mon Sep 17 00:00:00 2001 From: Carlos Bilbao Date: Mon, 24 Oct 2022 09:55:20 -0500 Subject: Documentation: Start translations to Spanish Start the process of translating kernel documentation to Spanish. Create directory sp_SP/ instead of es_ES/ (diverging from format of prior translated directories) since this directory should accept any dialects of Spanish. Include an index and a disclaimer, following the approach of prior translations. Add Carlos Bilbao as MAINTAINER of this effort. Reviewed-by: Miguel Ojeda Signed-off-by: Carlos Bilbao Link: https://lore.kernel.org/r/20221024145521.69465-2-carlos.bilbao@amd.com Signed-off-by: Jonathan Corbet --- Documentation/translations/index.rst | 1 + Documentation/translations/sp_SP/disclaimer-sp.rst | 6 ++ Documentation/translations/sp_SP/index.rst | 72 ++++++++++++++++++++++ MAINTAINERS | 5 ++ 4 files changed, 84 insertions(+) create mode 100644 Documentation/translations/sp_SP/disclaimer-sp.rst create mode 100644 Documentation/translations/sp_SP/index.rst (limited to 'MAINTAINERS') diff --git a/Documentation/translations/index.rst b/Documentation/translations/index.rst index 1175a47d07f0..b826c34791c0 100644 --- a/Documentation/translations/index.rst +++ b/Documentation/translations/index.rst @@ -12,6 +12,7 @@ Translations it_IT/index ko_KR/index ja_JP/index + sp_SP/index .. _translations_disclaimer: diff --git a/Documentation/translations/sp_SP/disclaimer-sp.rst b/Documentation/translations/sp_SP/disclaimer-sp.rst new file mode 100644 index 000000000000..a400034e95f9 --- /dev/null +++ b/Documentation/translations/sp_SP/disclaimer-sp.rst @@ -0,0 +1,6 @@ +:orphan: + +.. warning:: + Si tiene alguna duda sobre la exactitud del contenido de esta + traducción, la única referencia válida es la documentación oficial en + inglés. diff --git a/Documentation/translations/sp_SP/index.rst b/Documentation/translations/sp_SP/index.rst new file mode 100644 index 000000000000..816d45e081e9 --- /dev/null +++ b/Documentation/translations/sp_SP/index.rst @@ -0,0 +1,72 @@ +.. _sp_linux_doc: + +===================== +Traducción al español +===================== + +.. raw:: latex + + \kerneldocCJKoff + +:maintainer: Carlos Bilbao + +.. _sp_disclaimer: + +Advertencia +=========== + +El objetivo de esta traducción es facilitar la lectura y comprensión para +aquellos que no entiendan inglés o duden de sus interpretaciones, o +simplemente para aquellos que prefieran leer en el idioma español. Sin +embargo, tenga en cuenta que la *única* documentación oficial es la que +está en inglés: :ref:`linux_doc` + +La propagación simultánea de la traducción de una modificación en +:ref:`linux_doc` es altamente improbable. Los maintainers y colaboradores +de la traducción intentan mantener sus traducciones al día, en tanto les +es posible. Por tanto, no existe ninguna garantía de que una traducción +esté actualizada con las últimas modificaciones. Si lo que lee en una +traducción no se corresponde con lo que ve en el código fuente, informe +al maintainer de la traducción y, si puede, consulte la documentación en +inglés. + +Una traducción no es una * bifurcación * de la documentación oficial, por +lo que los usuarios no encontrarán aquí ninguna información que no sea la +versión oficial. Cualquier adición, supresión o modificación de los +contenidos deberá ser realizada anteriormente en los documentos en inglés. +Posteriormente, y cuando sea posible, dicho cambio debería aplicarse +también a las traducciones. Los maintainers de las traducciones aceptan +contribuciones que son puramente de interés relativo a la traducción (por +ejemplo, nuevas traducciones, actualizaciones, correcciones, etc.). + +Las traducciones tratan de ser lo más precisas posible pero no es posible +convertir directamente un idioma a otro. Cada idioma tiene su propia +gramática, y una cultura tras ella, por lo tanto, la traducción de una +oración al inglés se podría modificar para adaptarla al español. Por esta +razón, cuando lea esta traducción, puede encontrar algunas diferencias en +la forma, pero todavía transmiten el mensaje original. A pesar de la gran +difusión del inglés en el idioma hablado, cuando sea posible, expresiones +en inglés serán reemplazadas por las palabras correspondientes en español. + +Si necesita ayuda para comunicarse con la comunidad de Linux pero no se +siente cómodo escribiendo en inglés, puede pedir ayuda al maintainer para +obtener una traducción. + +Muchos países hablan español, cada uno con su propia cultura, expresiones, +y diferencias gramaticales en ocasiones significativas. Las traducciones de +los maintainers pueden utilizar el español con el que dichos maintainers se +sientan más cómodos. En principio, estas pequeñas diferencias no deberían +suponer una gran barrera para hablantes de distintas versiones del español, +pero en caso de duda se puede consultar a los maintainers. + +La documentación del kernel Linux +================================= + +Este es el nivel superior de la documentación del kernel en idioma español. +La traducción es incompleta, y podría encontrar advertencias que indiquen +la falta de una traducción o de un grupo de traducciones. + +En términos más generales, la documentación, como el kernel mismo, están en +constante desarrollo. Las mejoras en la documentación siempre son +bienvenidas; de modo que, si desea ayudar, únase a la lista de correo +linux-doc en vger.kernel.org. diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..6f2ec7c71a4c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19333,6 +19333,11 @@ W: https://linuxtv.org Q: http://patchwork.linuxtv.org/project/linux-media/list/ F: drivers/media/dvb-frontends/sp2* +SPANISH DOCUMENTATION +M: Carlos Bilbao +S: Maintained +F: Documentation/translations/sp_SP/ + SPARC + UltraSPARC (sparc/sparc64) M: "David S. Miller" L: sparclinux@vger.kernel.org -- cgit v1.2.3 From 3821e96a01d658e770074331b56cec88c169a418 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Mon, 31 Oct 2022 12:02:23 +0100 Subject: MAINTAINERS: arm,pl353-smc: correct dt-binding path Commit de67fa80c669 ("dt-bindings: memory-controllers: arm,pl353-smc: Extend to support 'arm,pl354' SMC") renames the arm,pl353-smc.yaml memory-controller dt-binding, but misses to adjust its reference in MAINTAINERS. Signed-off-by: Lukas Bulwahn Link: https://lore.kernel.org/r/20221031110223.30203-1-lukas.bulwahn@gmail.com Signed-off-by: Krzysztof Kozlowski --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..599a84f600c9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1685,7 +1685,7 @@ M: Miquel Raynal M: Naga Sureshkumar Relli L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -F: Documentation/devicetree/bindings/memory-controllers/arm,pl353-smc.yaml +F: Documentation/devicetree/bindings/memory-controllers/arm,pl35x-smc.yaml F: drivers/memory/pl353-smc.c ARM PRIMECELL CLCD PL110 DRIVER -- cgit v1.2.3 From e77f7ba726cc0c9b1c62b295d2aac42c3a18ebd1 Mon Sep 17 00:00:00 2001 From: Weilong Chen Date: Tue, 1 Nov 2022 16:07:28 +0800 Subject: dt-bindings: i2c: add entry for hisilicon,ascend910-i2c Add the new compatible for HiSilicon i2c. Signed-off-by: Weilong Chen Reviewed-by: Rob Herring Reviewed-by: Yicong Yang Signed-off-by: Wolfram Sang --- .../bindings/i2c/hisilicon,ascend910-i2c.yaml | 73 ++++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 74 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/hisilicon,ascend910-i2c.yaml (limited to 'MAINTAINERS') diff --git a/Documentation/devicetree/bindings/i2c/hisilicon,ascend910-i2c.yaml b/Documentation/devicetree/bindings/i2c/hisilicon,ascend910-i2c.yaml new file mode 100644 index 000000000000..7d7a8de7bcd8 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/hisilicon,ascend910-i2c.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/hisilicon,ascend910-i2c.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: HiSilicon common I2C controller + +maintainers: + - Yicong Yang + +description: + The HiSilicon common I2C controller can be used for many different + types of SoC such as Huawei Ascend AI series chips. + +allOf: + - $ref: /schemas/i2c/i2c-controller.yaml# + +properties: + compatible: + const: hisilicon,ascend910-i2c + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-frequency: + default: 400000 + + i2c-sda-falling-time-ns: + default: 343 + + i2c-scl-falling-time-ns: + default: 203 + + i2c-sda-hold-time-ns: + default: 830 + + i2c-scl-rising-time-ns: + default: 365 + + i2c-digital-filter-width-ns: + default: 0 + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include + + i2c@38b0000 { + compatible = "hisilicon,ascend910-i2c"; + reg = <0x38b0000 0x10000>; + interrupts = ; + i2c-sda-falling-time-ns = <56>; + i2c-scl-falling-time-ns = <56>; + i2c-sda-hold-time-ns = <56>; + i2c-scl-rising-time-ns = <56>; + i2c-digital-filter; + i2c-digital-filter-width-ns = <0x0>; + clocks = <&alg_clk>; + clock-frequency = <400000>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 379945f82a64..1d09b36b9b9c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9214,6 +9214,7 @@ M: Yicong Yang L: linux-i2c@vger.kernel.org S: Maintained W: https://www.hisilicon.com +F: Documentation/devicetree/bindings/i2c/hisilicon,ascend910-i2c.yaml F: drivers/i2c/busses/i2c-hisi.c HISILICON LPC BUS DRIVER -- cgit v1.2.3 From cd044ff918116cff8633780d2b4b21646d4ee3dd Mon Sep 17 00:00:00 2001 From: Job Noorman Date: Mon, 7 Nov 2022 11:24:44 -0800 Subject: dt-bindings: touchscreen: add Himax hx83112b bindings This patch adds device tree bindings for Himax 83112b touchscreen devices. Signed-off-by: Job Noorman Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20221107105604.26541-2-job@noorman.info Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/himax,hx83112b.yaml | 63 ++++++++++++++++++++++ MAINTAINERS | 6 +++ 2 files changed, 69 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/himax,hx83112b.yaml (limited to 'MAINTAINERS') diff --git a/Documentation/devicetree/bindings/input/touchscreen/himax,hx83112b.yaml b/Documentation/devicetree/bindings/input/touchscreen/himax,hx83112b.yaml new file mode 100644 index 000000000000..be2ba185c086 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/himax,hx83112b.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/touchscreen/himax,hx83112b.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Himax hx83112b touchscreen controller bindings + +maintainers: + - Job Noorman + +allOf: + - $ref: touchscreen.yaml# + +properties: + compatible: + enum: + - himax,hx83112b + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + reset-gpios: + maxItems: 1 + + touchscreen-inverted-x: true + touchscreen-inverted-y: true + touchscreen-size-x: true + touchscreen-size-y: true + touchscreen-swapped-x-y: true + +additionalProperties: false + +required: + - compatible + - reg + - interrupts + - reset-gpios + - touchscreen-size-x + - touchscreen-size-y + +examples: + - | + #include + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + touchscreen@48 { + compatible = "himax,hx83112b"; + reg = <0x48>; + interrupt-parent = <&tlmm>; + interrupts = <65 IRQ_TYPE_LEVEL_LOW>; + touchscreen-size-x = <1080>; + touchscreen-size-y = <2160>; + reset-gpios = <&tlmm 64 GPIO_ACTIVE_LOW>; + }; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..469ab6382906 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9162,6 +9162,12 @@ W: http://www.highpoint-tech.com F: Documentation/scsi/hptiop.rst F: drivers/scsi/hptiop.c +HIMAX HX83112B TOUCHSCREEN SUPPORT +M: Job Noorman +L: linux-input@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/input/touchscreen/himax,hx83112b.yaml + HIPPI M: Jes Sorensen L: linux-hippi@sunsite.dk -- cgit v1.2.3 From 0f382cade7221425535092292f670466836347e5 Mon Sep 17 00:00:00 2001 From: Job Noorman Date: Mon, 7 Nov 2022 11:25:05 -0800 Subject: Input: add driver for Himax hx83112b touchscreen devices This patch adds support for Himax hx83112b touchscreen devices. As there are no publicly available data sheets for these devices, the implementation is based on the driver of the downstream Android kernel used in the Fairphone 3. This patch is a complete rewrite, though, and the code bears no resemblence to the original implementation. The driver has been tested on the aforementioned phone. Signed-off-by: Job Noorman Reviewed-by: Jeff LaBundy Link: https://lore.kernel.org/r/20221107105604.26541-3-job@noorman.info Signed-off-by: Dmitry Torokhov --- MAINTAINERS | 1 + drivers/input/touchscreen/Kconfig | 12 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/himax_hx83112b.c | 365 +++++++++++++++++++++++++++++ 4 files changed, 379 insertions(+) create mode 100644 drivers/input/touchscreen/himax_hx83112b.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 469ab6382906..8d9fe0ac632c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9167,6 +9167,7 @@ M: Job Noorman L: linux-input@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/input/touchscreen/himax,hx83112b.yaml +F: drivers/input/touchscreen/himax_hx83112b.c HIPPI M: Jes Sorensen diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 317250b62e08..3f68eee71a4e 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1407,4 +1407,16 @@ config TOUCHSCREEN_ZINITIX To compile this driver as a module, choose M here: the module will be called zinitix. +config TOUCHSCREEN_HIMAX_HX83112B + tristate "Himax hx83112b touchscreen driver" + depends on I2C + select REGMAP_I2C + help + Say Y here to enable support for Himax hx83112b touchscreens. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called himax_hx83112b. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 03ad13f3fcce..4968c370479a 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -118,3 +118,4 @@ obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW) += raspberrypi-ts.o obj-$(CONFIG_TOUCHSCREEN_IQS5XX) += iqs5xx.o obj-$(CONFIG_TOUCHSCREEN_ZINITIX) += zinitix.o +obj-$(CONFIG_TOUCHSCREEN_HIMAX_HX83112B) += himax_hx83112b.o diff --git a/drivers/input/touchscreen/himax_hx83112b.c b/drivers/input/touchscreen/himax_hx83112b.c new file mode 100644 index 000000000000..737cc134e8f9 --- /dev/null +++ b/drivers/input/touchscreen/himax_hx83112b.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Himax hx83112b touchscreens + * + * Copyright (C) 2022 Job Noorman + * + * This code is based on "Himax Android Driver Sample Code for QCT platform": + * + * Copyright (C) 2017 Himax Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HIMAX_ID_83112B 0x83112b + +#define HIMAX_MAX_POINTS 10 + +#define HIMAX_REG_CFG_SET_ADDR 0x00 +#define HIMAX_REG_CFG_INIT_READ 0x0c +#define HIMAX_REG_CFG_READ_VALUE 0x08 +#define HIMAX_REG_READ_EVENT 0x30 + +#define HIMAX_CFG_PRODUCT_ID 0x900000d0 + +#define HIMAX_INVALID_COORD 0xffff + +struct himax_event_point { + __be16 x; + __be16 y; +} __packed; + +struct himax_event { + struct himax_event_point points[HIMAX_MAX_POINTS]; + u8 majors[HIMAX_MAX_POINTS]; + u8 pad0[2]; + u8 num_points; + u8 pad1[2]; + u8 checksum_fix; +} __packed; + +static_assert(sizeof(struct himax_event) == 56); + +struct himax_ts_data { + struct gpio_desc *gpiod_rst; + struct input_dev *input_dev; + struct i2c_client *client; + struct regmap *regmap; + struct touchscreen_properties props; +}; + +static const struct regmap_config himax_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + +static int himax_read_config(struct himax_ts_data *ts, u32 address, u32 *dst) +{ + int error; + + error = regmap_write(ts->regmap, HIMAX_REG_CFG_SET_ADDR, address); + if (error) + return error; + + error = regmap_write(ts->regmap, HIMAX_REG_CFG_INIT_READ, 0x0); + if (error) + return error; + + error = regmap_read(ts->regmap, HIMAX_REG_CFG_READ_VALUE, dst); + if (error) + return error; + + return 0; +} + +static void himax_reset(struct himax_ts_data *ts) +{ + gpiod_set_value_cansleep(ts->gpiod_rst, 1); + + /* Delay copied from downstream driver */ + msleep(20); + gpiod_set_value_cansleep(ts->gpiod_rst, 0); + + /* + * The downstream driver doesn't contain this delay but is seems safer + * to include it. The range is just a guess that seems to work well. + */ + usleep_range(1000, 1100); +} + +static int himax_read_product_id(struct himax_ts_data *ts, u32 *product_id) +{ + int error; + + error = himax_read_config(ts, HIMAX_CFG_PRODUCT_ID, product_id); + if (error) + return error; + + *product_id >>= 8; + return 0; +} + +static int himax_check_product_id(struct himax_ts_data *ts) +{ + int error; + u32 product_id; + + error = himax_read_product_id(ts, &product_id); + if (error) + return error; + + dev_dbg(&ts->client->dev, "Product id: %x\n", product_id); + + switch (product_id) { + case HIMAX_ID_83112B: + return 0; + + default: + dev_err(&ts->client->dev, + "Unknown product id: %x\n", product_id); + return -EINVAL; + } +} + +static int himax_input_register(struct himax_ts_data *ts) +{ + int error; + + ts->input_dev = devm_input_allocate_device(&ts->client->dev); + if (!ts->input_dev) { + dev_err(&ts->client->dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + + ts->input_dev->name = "Himax Touchscreen"; + + input_set_capability(ts->input_dev, EV_ABS, ABS_MT_POSITION_X); + input_set_capability(ts->input_dev, EV_ABS, ABS_MT_POSITION_Y); + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 200, 0, 0); + + touchscreen_parse_properties(ts->input_dev, true, &ts->props); + + error = input_mt_init_slots(ts->input_dev, HIMAX_MAX_POINTS, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) { + dev_err(&ts->client->dev, + "Failed to initialize MT slots: %d\n", error); + return error; + } + + error = input_register_device(ts->input_dev); + if (error) { + dev_err(&ts->client->dev, + "Failed to register input device: %d\n", error); + return error; + } + + return 0; +} + +static u8 himax_event_get_num_points(const struct himax_event *event) +{ + if (event->num_points == 0xff) + return 0; + else + return event->num_points & 0x0f; +} + +static bool himax_process_event_point(struct himax_ts_data *ts, + const struct himax_event *event, + int point_index) +{ + const struct himax_event_point *point = &event->points[point_index]; + u16 x = be16_to_cpu(point->x); + u16 y = be16_to_cpu(point->y); + u8 w = event->majors[point_index]; + + if (x == HIMAX_INVALID_COORD || y == HIMAX_INVALID_COORD) + return false; + + input_mt_slot(ts->input_dev, point_index); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); + touchscreen_report_pos(ts->input_dev, &ts->props, x, y, true); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); + return true; +} + +static void himax_process_event(struct himax_ts_data *ts, + const struct himax_event *event) +{ + int i; + int num_points_left = himax_event_get_num_points(event); + + for (i = 0; i < HIMAX_MAX_POINTS && num_points_left > 0; i++) { + if (himax_process_event_point(ts, event, i)) + num_points_left--; + } + + input_mt_sync_frame(ts->input_dev); + input_sync(ts->input_dev); +} + +static bool himax_verify_checksum(struct himax_ts_data *ts, + const struct himax_event *event) +{ + u8 *data = (u8 *)event; + int i; + u16 checksum = 0; + + for (i = 0; i < sizeof(*event); i++) + checksum += data[i]; + + if ((checksum & 0x00ff) != 0) { + dev_err(&ts->client->dev, "Wrong event checksum: %04x\n", + checksum); + return false; + } + + return true; +} + +static int himax_handle_input(struct himax_ts_data *ts) +{ + int error; + struct himax_event event; + + error = regmap_raw_read(ts->regmap, HIMAX_REG_READ_EVENT, &event, + sizeof(event)); + if (error) { + dev_err(&ts->client->dev, "Failed to read input event: %d\n", + error); + return error; + } + + /* + * Only process the current event when it has a valid checksum but + * don't consider it a fatal error when it doesn't. + */ + if (himax_verify_checksum(ts, &event)) + himax_process_event(ts, &event); + + return 0; +} + +static irqreturn_t himax_irq_handler(int irq, void *dev_id) +{ + int error; + struct himax_ts_data *ts = dev_id; + + error = himax_handle_input(ts); + if (error) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static int himax_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int error; + struct device *dev = &client->dev; + struct himax_ts_data *ts; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(dev, "I2C check functionality failed\n"); + return -ENXIO; + } + + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + i2c_set_clientdata(client, ts); + ts->client = client; + + ts->regmap = devm_regmap_init_i2c(client, &himax_regmap_config); + error = PTR_ERR_OR_ZERO(ts->regmap); + if (error) { + dev_err(dev, "Failed to initialize regmap: %d\n", error); + return error; + } + + ts->gpiod_rst = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + error = PTR_ERR_OR_ZERO(ts->gpiod_rst); + if (error) { + dev_err(dev, "Failed to get reset GPIO: %d\n", error); + return error; + } + + himax_reset(ts); + + error = himax_check_product_id(ts); + if (error) + return error; + + error = himax_input_register(ts); + if (error) + return error; + + error = devm_request_threaded_irq(dev, client->irq, NULL, + himax_irq_handler, IRQF_ONESHOT, + client->name, ts); + if (error) + return error; + + return 0; +} + +static int himax_suspend(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + + disable_irq(ts->client->irq); + return 0; +} + +static int himax_resume(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + + enable_irq(ts->client->irq); + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(himax_pm_ops, himax_suspend, himax_resume); + +static const struct i2c_device_id himax_ts_id[] = { + { "hx83112b", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, himax_ts_id); + +#ifdef CONFIG_OF +static const struct of_device_id himax_of_match[] = { + { .compatible = "himax,hx83112b" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, himax_of_match); +#endif + +static struct i2c_driver himax_ts_driver = { + .probe = himax_probe, + .id_table = himax_ts_id, + .driver = { + .name = "Himax-hx83112b-TS", + .of_match_table = of_match_ptr(himax_of_match), + .pm = pm_sleep_ptr(&himax_pm_ops), + }, +}; +module_i2c_driver(himax_ts_driver); + +MODULE_AUTHOR("Job Noorman "); +MODULE_DESCRIPTION("Himax hx83112b touchscreen driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 95e7fc84c78adeef654acb919f04d98a87e6372d Mon Sep 17 00:00:00 2001 From: Weilong Chen Date: Tue, 1 Nov 2022 16:24:42 +0800 Subject: dt-bindings: gpio: add entry for hisilicon,ascend910-gpio Add the new compatible for HiSilicon gpio controller driver. Signed-off-by: Weilong Chen Reviewed-by: Rob Herring Reviewed-by: Yicong Yang Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/hisilicon,ascend910-gpio.yaml | 56 ++++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 57 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/hisilicon,ascend910-gpio.yaml (limited to 'MAINTAINERS') diff --git a/Documentation/devicetree/bindings/gpio/hisilicon,ascend910-gpio.yaml b/Documentation/devicetree/bindings/gpio/hisilicon,ascend910-gpio.yaml new file mode 100644 index 000000000000..735d97d645a0 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/hisilicon,ascend910-gpio.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/hisilicon,ascend910-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: HiSilicon common GPIO controller + +maintainers: + - Jay Fang + +description: + The HiSilicon common GPIO controller can be used for many different + types of SoC such as Huawei Ascend AI series chips. + +properties: + compatible: + const: hisilicon,ascend910-gpio + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + + ngpios: + minimum: 1 + maximum: 32 + +required: + - compatible + - reg + - interrupts + - gpio-controller + - "#gpio-cells" + - ngpios + +additionalProperties: false + +examples: + - | + #include + + gpio@840d0000 { + compatible = "hisilicon,ascend910-gpio"; + reg = <0x840d0000 0x1000>; + ngpios = <32>; + gpio-controller; + #gpio-cells = <2>; + interrupts = ; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 74efa0492c43..02f333c1093e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9198,6 +9198,7 @@ HISILICON GPIO DRIVER M: Jay Fang L: linux-gpio@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/gpio/hisilicon,ascend910-gpio.yaml F: drivers/gpio/gpio-hisi.c HISILICON HIGH PERFORMANCE RSA ENGINE DRIVER (HPRE) -- cgit v1.2.3 From d0ee86d3c0f0da1481cfab51a386fc22b00a0630 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 7 Nov 2022 10:22:07 +0100 Subject: MAINTAINERS: Update Konrad Dybcio's email address Use my new Linaro address in place of my SoMainline one. Signed-off-by: Konrad Dybcio Signed-off-by: Konrad Dybcio Reviewed-by: Neil Armstrong Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221107092207.5832-1-konrad.dybcio@linaro.org --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..3bdac09e9050 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2620,7 +2620,7 @@ W: http://www.armlinux.org.uk/ ARM/QUALCOMM SUPPORT M: Andy Gross M: Bjorn Andersson -R: Konrad Dybcio +R: Konrad Dybcio L: linux-arm-msm@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux.git -- cgit v1.2.3 From 1a0257c352638916fdaffaac2ddedb8e049312f3 Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Thu, 27 Oct 2022 02:09:57 -0400 Subject: MAINTAINERS: Update GPU Scheduler email MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update GPU Scheduler maintainer email. Cc: Alex Deucher Cc: Christian König Cc: Daniel Vetter Cc: Dave Airlie Cc: AMD Graphics Cc: Direct Rendering Infrastructure - Development Signed-off-by: Luben Tuikov Acked-by: Christian König Acked-by: Alex Deucher Link: https://patchwork.freedesktop.org/patch/508985/ --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 30e3df70daec..dc1a964634cd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7113,7 +7113,7 @@ F: drivers/gpu/drm/ttm/ F: include/drm/ttm/ DRM GPU SCHEDULER -M: Andrey Grodzovsky +M: Luben Tuikov L: dri-devel@lists.freedesktop.org S: Maintained T: git git://anongit.freedesktop.org/drm/drm-misc -- cgit v1.2.3 From 65f199b2b40d82e48c79af2f4b5e9fafb290b231 Mon Sep 17 00:00:00 2001 From: "Uladzislau Rezki (Sony)" Date: Tue, 18 Oct 2022 20:10:53 +0200 Subject: vmalloc: add reviewers for vmalloc code Add myself and Christoph Hellwig as reviewers for vmalloc. [akpm@linux-foundation.org: coding-style cleanups] Link: https://lkml.kernel.org/r/20221018181053.434508-8-urezki@gmail.com Signed-off-by: Uladzislau Rezki (Sony) Reviewed-by: Steven Rostedt (Google) Acked-by: Christoph Hellwig Cc: Matthew Wilcox (Oracle) Cc: Nicholas Piggin Cc: Oleksiy Avramchenko Signed-off-by: Andrew Morton --- MAINTAINERS | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 046ff06ff97f..631bd2e8c5c4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13294,10 +13294,20 @@ F: include/linux/memory_hotplug.h F: include/linux/mm.h F: include/linux/mmzone.h F: include/linux/pagewalk.h -F: include/linux/vmalloc.h F: mm/ F: tools/testing/selftests/vm/ +VMALLOC +M: Andrew Morton +R: Uladzislau Rezki +R: Christoph Hellwig +L: linux-mm@kvack.org +S: Maintained +W: http://www.linux-mm.org +T: git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +F: include/linux/vmalloc.h +F: mm/vmalloc.c + MEMORY HOT(UN)PLUG M: David Hildenbrand M: Oscar Salvador -- cgit v1.2.3 From 6c873654bd413b6f2c77aa4724b42142a8353ff3 Mon Sep 17 00:00:00 2001 From: Yassine Oudjana Date: Fri, 28 Oct 2022 18:34:56 +0300 Subject: dt-bindings: pinctrl: mediatek,mt6779-pinctrl: Add MT6797 Combine MT6797 pin controller document into MT6779 one. reg and reg-names property constraints are set using conditionals. A conditional is also used to make interrupt-related properties required on the MT6779 pin controller only, since the MT6797 controller doesn't support interrupts (or not yet, at least). drive-strength and slew-rate properties which weren't described in the MT6779 document before are brought in from the MT6797 one. Both pin controllers share a common driver core so they should both support these properties. Signed-off-by: Yassine Oudjana Reviewed-by: Rob Herring Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20221028153505.23741-5-y.oudjana@protonmail.com Signed-off-by: Linus Walleij --- .../bindings/pinctrl/mediatek,mt6779-pinctrl.yaml | 87 +++++++--- .../bindings/pinctrl/mediatek,mt6797-pinctrl.yaml | 176 --------------------- MAINTAINERS | 2 +- 3 files changed, 67 insertions(+), 198 deletions(-) delete mode 100644 Documentation/devicetree/bindings/pinctrl/mediatek,mt6797-pinctrl.yaml (limited to 'MAINTAINERS') diff --git a/Documentation/devicetree/bindings/pinctrl/mediatek,mt6779-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/mediatek,mt6779-pinctrl.yaml index d45f0e75a698..a2141eb0854e 100644 --- a/Documentation/devicetree/bindings/pinctrl/mediatek,mt6779-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/mediatek,mt6779-pinctrl.yaml @@ -8,6 +8,7 @@ title: Mediatek MT6779 Pin Controller maintainers: - Andy Teng + - Sean Wang description: The MediaTek pin controller on MT6779 is used to control pin @@ -15,23 +16,14 @@ description: properties: compatible: - const: mediatek,mt6779-pinctrl + enum: + - mediatek,mt6779-pinctrl + - mediatek,mt6797-pinctrl reg: - minItems: 9 - maxItems: 9 - - reg-names: - items: - - const: "gpio" - - const: "iocfg_rm" - - const: "iocfg_br" - - const: "iocfg_lm" - - const: "iocfg_lb" - - const: "iocfg_rt" - - const: "iocfg_lt" - - const: "iocfg_tl" - - const: "eint" + description: Physical addresses for GPIO base(s) and EINT registers. + + reg-names: true gpio-controller: true @@ -58,18 +50,65 @@ properties: "#interrupt-cells": const: 2 -allOf: - - $ref: "pinctrl.yaml#" - required: - compatible - reg - reg-names - gpio-controller - "#gpio-cells" - - interrupt-controller - - interrupts - - "#interrupt-cells" + +allOf: + - $ref: "pinctrl.yaml#" + - if: + properties: + compatible: + contains: + const: mediatek,mt6779-pinctrl + then: + properties: + reg: + minItems: 9 + maxItems: 9 + + reg-names: + items: + - const: gpio + - const: iocfg_rm + - const: iocfg_br + - const: iocfg_lm + - const: iocfg_lb + - const: iocfg_rt + - const: iocfg_lt + - const: iocfg_tl + - const: eint + - if: + properties: + compatible: + contains: + const: mediatek,mt6797-pinctrl + then: + properties: + reg: + minItems: 5 + maxItems: 5 + + reg-names: + items: + - const: gpio + - const: iocfgl + - const: iocfgb + - const: iocfgr + - const: iocfgt + - if: + properties: + reg-names: + contains: + const: eint + then: + required: + - interrupts + - interrupt-controller + - "#interrupt-cells" patternProperties: '-[0-9]*$': @@ -111,6 +150,12 @@ patternProperties: input-schmitt-disable: true + drive-strength: + enum: [2, 4, 8, 12, 16] + + slew-rate: + enum: [0, 1] + mediatek,pull-up-adv: description: | Pull up setings for 2 pull resistors, R0 and R1. User can diff --git a/Documentation/devicetree/bindings/pinctrl/mediatek,mt6797-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/mediatek,mt6797-pinctrl.yaml deleted file mode 100644 index 637a8386e23e..000000000000 --- a/Documentation/devicetree/bindings/pinctrl/mediatek,mt6797-pinctrl.yaml +++ /dev/null @@ -1,176 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/pinctrl/mediatek,mt6797-pinctrl.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Mediatek MT6797 Pin Controller - -maintainers: - - Sean Wang - -description: |+ - The MediaTek's MT6797 Pin controller is used to control SoC pins. - -properties: - compatible: - const: mediatek,mt6797-pinctrl - - reg: - minItems: 5 - maxItems: 5 - - reg-names: - items: - - const: gpio - - const: iocfgl - - const: iocfgb - - const: iocfgr - - const: iocfgt - - gpio-controller: true - - "#gpio-cells": - const: 2 - description: | - Number of cells in GPIO specifier. Since the generic GPIO - binding is used, the amount of cells must be specified as 2. See the below - mentioned gpio binding representation for description of particular cells. - - interrupt-controller: true - - interrupts: - maxItems: 1 - - "#interrupt-cells": - const: 2 - -allOf: - - $ref: "pinctrl.yaml#" - -required: - - compatible - - reg - - reg-names - - gpio-controller - - "#gpio-cells" - -patternProperties: - '-[0-9]+$': - type: object - additionalProperties: false - patternProperties: - 'pins': - type: object - additionalProperties: false - description: | - A pinctrl node should contain at least one subnodes representing the - pinctrl groups available on the machine. Each subnode will list the - pins it needs, and how they should be configured, with regard to muxer - configuration, pullups, drive strength, input enable/disable and input - schmitt. - $ref: "/schemas/pinctrl/pincfg-node.yaml" - - properties: - pinmux: - description: - integer array, represents gpio pin number and mux setting. - Supported pin number and mux varies for different SoCs, and are - defined as macros in -pinfunc.h directly. - - bias-disable: true - - bias-pull-up: true - - bias-pull-down: true - - input-enable: true - - input-disable: true - - output-enable: true - - output-low: true - - output-high: true - - input-schmitt-enable: true - - input-schmitt-disable: true - - drive-strength: - enum: [2, 4, 8, 12, 16] - - slew-rate: - enum: [0, 1] - - mediatek,pull-up-adv: - description: | - Pull up setings for 2 pull resistors, R0 and R1. User can - configure those special pins. Valid arguments are described as below: - 0: (R1, R0) = (0, 0) which means R1 disabled and R0 disabled. - 1: (R1, R0) = (0, 1) which means R1 disabled and R0 enabled. - 2: (R1, R0) = (1, 0) which means R1 enabled and R0 disabled. - 3: (R1, R0) = (1, 1) which means R1 enabled and R0 enabled. - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [0, 1, 2, 3] - - mediatek,pull-down-adv: - description: | - Pull down settings for 2 pull resistors, R0 and R1. User can - configure those special pins. Valid arguments are described as below: - 0: (R1, R0) = (0, 0) which means R1 disabled and R0 disabled. - 1: (R1, R0) = (0, 1) which means R1 disabled and R0 enabled. - 2: (R1, R0) = (1, 0) which means R1 enabled and R0 disabled. - 3: (R1, R0) = (1, 1) which means R1 enabled and R0 enabled. - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [0, 1, 2, 3] - - mediatek,tdsel: - description: | - An integer describing the steps for output level shifter duty - cycle when asserted (high pulse width adjustment). Valid arguments - are from 0 to 15. - $ref: /schemas/types.yaml#/definitions/uint32 - - mediatek,rdsel: - description: | - An integer describing the steps for input level shifter duty cycle - when asserted (high pulse width adjustment). Valid arguments are - from 0 to 63. - $ref: /schemas/types.yaml#/definitions/uint32 - - required: - - pinmux - -additionalProperties: false - -examples: - - | - #include - #include - #include - - soc { - #address-cells = <2>; - #size-cells = <2>; - - pio: pinctrl@10005000 { - compatible = "mediatek,mt6797-pinctrl"; - reg = <0 0x10005000 0 0x1000>, - <0 0x10002000 0 0x400>, - <0 0x10002400 0 0x400>, - <0 0x10002800 0 0x400>, - <0 0x10002C00 0 0x400>; - reg-names = "gpio", "iocfgl", "iocfgb", "iocfgr", "iocfgt"; - gpio-controller; - #gpio-cells = <2>; - - uart_pins_a: uart-0 { - pins1 { - pinmux = , - ; - }; - }; - }; - }; diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..4aece82ae6ca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16284,7 +16284,7 @@ M: Sean Wang L: linux-mediatek@lists.infradead.org (moderated for non-subscribers) S: Maintained F: Documentation/devicetree/bindings/pinctrl/mediatek,mt65xx-pinctrl.yaml -F: Documentation/devicetree/bindings/pinctrl/mediatek,mt6797-pinctrl.yaml +F: Documentation/devicetree/bindings/pinctrl/mediatek,mt6779-pinctrl.yaml F: Documentation/devicetree/bindings/pinctrl/mediatek,mt7622-pinctrl.yaml F: Documentation/devicetree/bindings/pinctrl/mediatek,mt8183-pinctrl.yaml F: drivers/pinctrl/mediatek/ -- cgit v1.2.3 From bb3098eead990ca3379357564ae57b8107b40d34 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Tue, 8 Nov 2022 23:01:19 +0530 Subject: dt-bindings: display: Document Jadard JD9365DA-H3 DSI panel Jadard JD9365DA-H3 is WXGA MIPI DSI panel and it support TFT dot matrix LCD with 800RGBx1280 dots at maximum. Document it. Cc: dri-devel@lists.freedesktop.org Signed-off-by: Jagan Teki Reviewed-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20221108173120.618312-3-jagan@edgeble.ai --- .../bindings/display/panel/jadard,jd9365da-h3.yaml | 70 ++++++++++++++++++++++ MAINTAINERS | 5 ++ 2 files changed, 75 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml (limited to 'MAINTAINERS') diff --git a/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml b/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml new file mode 100644 index 000000000000..c06902e4fe70 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/jadard,jd9365da-h3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Jadard JD9365DA-HE WXGA DSI panel + +maintainers: + - Jagan Teki + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + - chongzhou,cz101b4001 + - const: jadard,jd9365da-h3 + + reg: true + + vdd-supply: + description: supply regulator for VDD, usually 3.3V + + vccio-supply: + description: supply regulator for VCCIO, usually 1.8V + + reset-gpios: true + + backlight: true + + port: true + +required: + - compatible + - reg + - vdd-supply + - vccio-supply + - reset-gpios + +additionalProperties: false + +examples: + - | + #include + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "chongzhou,cz101b4001", "jadard,jd9365da-h3"; + reg = <0>; + vdd-supply = <&lcd_3v3>; + vccio-supply = <&vcca_1v8>; + reset-gpios = <&gpio1 RK_PC2 GPIO_ACTIVE_HIGH>; + backlight = <&backlight>; + + port { + mipi_in_panel: endpoint { + remote-endpoint = <&mipi_out_panel>; + }; + }; + }; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index dc1a964634cd..492eb76ec054 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6503,6 +6503,11 @@ S: Orphan / Obsolete F: drivers/gpu/drm/i810/ F: include/uapi/drm/i810_drm.h +DRM DRIVER FOR JADARD JD9365DA-H3 MIPI-DSI LCD PANELS +M: Jagan Teki +S: Maintained +F: Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml + DRM DRIVER FOR LOGICVC DISPLAY CONTROLLER M: Paul Kocialkowski S: Supported -- cgit v1.2.3 From 6b818c533dd8615a803a72733eace58fd06e5a3c Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Tue, 8 Nov 2022 23:01:20 +0530 Subject: drm: panel: Add Jadard JD9365DA-H3 DSI panel Jadard JD9365DA-H3 is WXGA MIPI DSI panel and it support TFT dot matrix LCD with 800RGBx1280 dots at maximum. Add support for it. Cc: dri-devel@lists.freedesktop.org Signed-off-by: Jagan Teki Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20221108173120.618312-4-jagan@edgeble.ai --- MAINTAINERS | 1 + drivers/gpu/drm/panel/Kconfig | 10 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c | 473 +++++++++++++++++++++++ 4 files changed, 485 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 492eb76ec054..178a7a383b5f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6507,6 +6507,7 @@ DRM DRIVER FOR JADARD JD9365DA-H3 MIPI-DSI LCD PANELS M: Jagan Teki S: Maintained F: Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml +F: drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c DRM DRIVER FOR LOGICVC DISPLAY CONTROLLER M: Paul Kocialkowski diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index a582ddd583c2..6f6cc6662b2e 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -203,6 +203,16 @@ config DRM_PANEL_INNOLUX_P079ZCA 24 bit RGB per pixel. It provides a MIPI DSI interface to the host and has a built-in LED backlight. +config DRM_PANEL_JADARD_JD9365DA_H3 + tristate "Jadard JD9365DA-H3 WXGA DSI panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for Jadard JD9365DA-H3 + WXGA MIPI DSI panel. The panel support TFT dot matrix LCD with + 800RGBx1280 dots at maximum. + config DRM_PANEL_JDI_LT070ME05000 tristate "JDI LT070ME05000 WUXGA DSI panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 34e717382dbb..af0b1ebdbac8 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o obj-$(CONFIG_DRM_PANEL_INNOLUX_EJ030NA) += panel-innolux-ej030na.o obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o +obj-$(CONFIG_DRM_PANEL_JADARD_JD9365DA_H3) += panel-jadard-jd9365da-h3.o obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o obj-$(CONFIG_DRM_PANEL_JDI_R63452) += panel-jdi-fhd-r63452.o obj-$(CONFIG_DRM_PANEL_KHADAS_TS050) += panel-khadas-ts050.o diff --git a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c new file mode 100644 index 000000000000..48c1702a863b --- /dev/null +++ b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 Radxa Limited + * Copyright (c) 2022 Edgeble AI Technologies Pvt. Ltd. + * + * Author: + * - Jagan Teki + * - Stephen Chen + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define JD9365DA_INIT_CMD_LEN 2 + +struct jadard_init_cmd { + u8 data[JD9365DA_INIT_CMD_LEN]; +}; + +struct jadard_panel_desc { + const struct drm_display_mode mode; + unsigned int lanes; + enum mipi_dsi_pixel_format format; + const struct jadard_init_cmd *init_cmds; + u32 num_init_cmds; +}; + +struct jadard { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + const struct jadard_panel_desc *desc; + + struct regulator *vdd; + struct regulator *vccio; + struct gpio_desc *reset; +}; + +static inline struct jadard *panel_to_jadard(struct drm_panel *panel) +{ + return container_of(panel, struct jadard, panel); +} + +static int jadard_enable(struct drm_panel *panel) +{ + struct device *dev = panel->dev; + struct jadard *jadard = panel_to_jadard(panel); + const struct jadard_panel_desc *desc = jadard->desc; + struct mipi_dsi_device *dsi = jadard->dsi; + unsigned int i; + int err; + + msleep(10); + + for (i = 0; i < desc->num_init_cmds; i++) { + const struct jadard_init_cmd *cmd = &desc->init_cmds[i]; + + err = mipi_dsi_dcs_write_buffer(dsi, cmd->data, JD9365DA_INIT_CMD_LEN); + if (err < 0) + return err; + } + + msleep(120); + + err = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (err < 0) + DRM_DEV_ERROR(dev, "failed to exit sleep mode ret = %d\n", err); + + err = mipi_dsi_dcs_set_display_on(dsi); + if (err < 0) + DRM_DEV_ERROR(dev, "failed to set display on ret = %d\n", err); + + return 0; +} + +static int jadard_disable(struct drm_panel *panel) +{ + struct device *dev = panel->dev; + struct jadard *jadard = panel_to_jadard(panel); + int ret; + + ret = mipi_dsi_dcs_set_display_off(jadard->dsi); + if (ret < 0) + DRM_DEV_ERROR(dev, "failed to set display off: %d\n", ret); + + ret = mipi_dsi_dcs_enter_sleep_mode(jadard->dsi); + if (ret < 0) + DRM_DEV_ERROR(dev, "failed to enter sleep mode: %d\n", ret); + + return 0; +} + +static int jadard_prepare(struct drm_panel *panel) +{ + struct jadard *jadard = panel_to_jadard(panel); + int ret; + + ret = regulator_enable(jadard->vccio); + if (ret) + return ret; + + ret = regulator_enable(jadard->vdd); + if (ret) + return ret; + + gpiod_set_value(jadard->reset, 1); + msleep(5); + + gpiod_set_value(jadard->reset, 0); + msleep(10); + + gpiod_set_value(jadard->reset, 1); + msleep(120); + + return 0; +} + +static int jadard_unprepare(struct drm_panel *panel) +{ + struct jadard *jadard = panel_to_jadard(panel); + + gpiod_set_value(jadard->reset, 1); + msleep(120); + + regulator_disable(jadard->vdd); + regulator_disable(jadard->vccio); + + return 0; +} + +static int jadard_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct jadard *jadard = panel_to_jadard(panel); + const struct drm_display_mode *desc_mode = &jadard->desc->mode; + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, desc_mode); + if (!mode) { + DRM_DEV_ERROR(&jadard->dsi->dev, "failed to add mode %ux%ux@%u\n", + desc_mode->hdisplay, desc_mode->vdisplay, + drm_mode_vrefresh(desc_mode)); + return -ENOMEM; + } + + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + + return 1; +} + +static const struct drm_panel_funcs jadard_funcs = { + .disable = jadard_disable, + .unprepare = jadard_unprepare, + .prepare = jadard_prepare, + .enable = jadard_enable, + .get_modes = jadard_get_modes, +}; + +static const struct jadard_init_cmd cz101b4001_init_cmds[] = { + { .data = { 0xE0, 0x00 } }, + { .data = { 0xE1, 0x93 } }, + { .data = { 0xE2, 0x65 } }, + { .data = { 0xE3, 0xF8 } }, + { .data = { 0x80, 0x03 } }, + { .data = { 0xE0, 0x01 } }, + { .data = { 0x00, 0x00 } }, + { .data = { 0x01, 0x3B } }, + { .data = { 0x0C, 0x74 } }, + { .data = { 0x17, 0x00 } }, + { .data = { 0x18, 0xAF } }, + { .data = { 0x19, 0x00 } }, + { .data = { 0x1A, 0x00 } }, + { .data = { 0x1B, 0xAF } }, + { .data = { 0x1C, 0x00 } }, + { .data = { 0x35, 0x26 } }, + { .data = { 0x37, 0x09 } }, + { .data = { 0x38, 0x04 } }, + { .data = { 0x39, 0x00 } }, + { .data = { 0x3A, 0x01 } }, + { .data = { 0x3C, 0x78 } }, + { .data = { 0x3D, 0xFF } }, + { .data = { 0x3E, 0xFF } }, + { .data = { 0x3F, 0x7F } }, + { .data = { 0x40, 0x06 } }, + { .data = { 0x41, 0xA0 } }, + { .data = { 0x42, 0x81 } }, + { .data = { 0x43, 0x14 } }, + { .data = { 0x44, 0x23 } }, + { .data = { 0x45, 0x28 } }, + { .data = { 0x55, 0x02 } }, + { .data = { 0x57, 0x69 } }, + { .data = { 0x59, 0x0A } }, + { .data = { 0x5A, 0x2A } }, + { .data = { 0x5B, 0x17 } }, + { .data = { 0x5D, 0x7F } }, + { .data = { 0x5E, 0x6B } }, + { .data = { 0x5F, 0x5C } }, + { .data = { 0x60, 0x4F } }, + { .data = { 0x61, 0x4D } }, + { .data = { 0x62, 0x3F } }, + { .data = { 0x63, 0x42 } }, + { .data = { 0x64, 0x2B } }, + { .data = { 0x65, 0x44 } }, + { .data = { 0x66, 0x43 } }, + { .data = { 0x67, 0x43 } }, + { .data = { 0x68, 0x63 } }, + { .data = { 0x69, 0x52 } }, + { .data = { 0x6A, 0x5A } }, + { .data = { 0x6B, 0x4F } }, + { .data = { 0x6C, 0x4E } }, + { .data = { 0x6D, 0x20 } }, + { .data = { 0x6E, 0x0F } }, + { .data = { 0x6F, 0x00 } }, + { .data = { 0x70, 0x7F } }, + { .data = { 0x71, 0x6B } }, + { .data = { 0x72, 0x5C } }, + { .data = { 0x73, 0x4F } }, + { .data = { 0x74, 0x4D } }, + { .data = { 0x75, 0x3F } }, + { .data = { 0x76, 0x42 } }, + { .data = { 0x77, 0x2B } }, + { .data = { 0x78, 0x44 } }, + { .data = { 0x79, 0x43 } }, + { .data = { 0x7A, 0x43 } }, + { .data = { 0x7B, 0x63 } }, + { .data = { 0x7C, 0x52 } }, + { .data = { 0x7D, 0x5A } }, + { .data = { 0x7E, 0x4F } }, + { .data = { 0x7F, 0x4E } }, + { .data = { 0x80, 0x20 } }, + { .data = { 0x81, 0x0F } }, + { .data = { 0x82, 0x00 } }, + { .data = { 0xE0, 0x02 } }, + { .data = { 0x00, 0x02 } }, + { .data = { 0x01, 0x02 } }, + { .data = { 0x02, 0x00 } }, + { .data = { 0x03, 0x00 } }, + { .data = { 0x04, 0x1E } }, + { .data = { 0x05, 0x1E } }, + { .data = { 0x06, 0x1F } }, + { .data = { 0x07, 0x1F } }, + { .data = { 0x08, 0x1F } }, + { .data = { 0x09, 0x17 } }, + { .data = { 0x0A, 0x17 } }, + { .data = { 0x0B, 0x37 } }, + { .data = { 0x0C, 0x37 } }, + { .data = { 0x0D, 0x47 } }, + { .data = { 0x0E, 0x47 } }, + { .data = { 0x0F, 0x45 } }, + { .data = { 0x10, 0x45 } }, + { .data = { 0x11, 0x4B } }, + { .data = { 0x12, 0x4B } }, + { .data = { 0x13, 0x49 } }, + { .data = { 0x14, 0x49 } }, + { .data = { 0x15, 0x1F } }, + { .data = { 0x16, 0x01 } }, + { .data = { 0x17, 0x01 } }, + { .data = { 0x18, 0x00 } }, + { .data = { 0x19, 0x00 } }, + { .data = { 0x1A, 0x1E } }, + { .data = { 0x1B, 0x1E } }, + { .data = { 0x1C, 0x1F } }, + { .data = { 0x1D, 0x1F } }, + { .data = { 0x1E, 0x1F } }, + { .data = { 0x1F, 0x17 } }, + { .data = { 0x20, 0x17 } }, + { .data = { 0x21, 0x37 } }, + { .data = { 0x22, 0x37 } }, + { .data = { 0x23, 0x46 } }, + { .data = { 0x24, 0x46 } }, + { .data = { 0x25, 0x44 } }, + { .data = { 0x26, 0x44 } }, + { .data = { 0x27, 0x4A } }, + { .data = { 0x28, 0x4A } }, + { .data = { 0x29, 0x48 } }, + { .data = { 0x2A, 0x48 } }, + { .data = { 0x2B, 0x1F } }, + { .data = { 0x2C, 0x01 } }, + { .data = { 0x2D, 0x01 } }, + { .data = { 0x2E, 0x00 } }, + { .data = { 0x2F, 0x00 } }, + { .data = { 0x30, 0x1F } }, + { .data = { 0x31, 0x1F } }, + { .data = { 0x32, 0x1E } }, + { .data = { 0x33, 0x1E } }, + { .data = { 0x34, 0x1F } }, + { .data = { 0x35, 0x17 } }, + { .data = { 0x36, 0x17 } }, + { .data = { 0x37, 0x37 } }, + { .data = { 0x38, 0x37 } }, + { .data = { 0x39, 0x08 } }, + { .data = { 0x3A, 0x08 } }, + { .data = { 0x3B, 0x0A } }, + { .data = { 0x3C, 0x0A } }, + { .data = { 0x3D, 0x04 } }, + { .data = { 0x3E, 0x04 } }, + { .data = { 0x3F, 0x06 } }, + { .data = { 0x40, 0x06 } }, + { .data = { 0x41, 0x1F } }, + { .data = { 0x42, 0x02 } }, + { .data = { 0x43, 0x02 } }, + { .data = { 0x44, 0x00 } }, + { .data = { 0x45, 0x00 } }, + { .data = { 0x46, 0x1F } }, + { .data = { 0x47, 0x1F } }, + { .data = { 0x48, 0x1E } }, + { .data = { 0x49, 0x1E } }, + { .data = { 0x4A, 0x1F } }, + { .data = { 0x4B, 0x17 } }, + { .data = { 0x4C, 0x17 } }, + { .data = { 0x4D, 0x37 } }, + { .data = { 0x4E, 0x37 } }, + { .data = { 0x4F, 0x09 } }, + { .data = { 0x50, 0x09 } }, + { .data = { 0x51, 0x0B } }, + { .data = { 0x52, 0x0B } }, + { .data = { 0x53, 0x05 } }, + { .data = { 0x54, 0x05 } }, + { .data = { 0x55, 0x07 } }, + { .data = { 0x56, 0x07 } }, + { .data = { 0x57, 0x1F } }, + { .data = { 0x58, 0x40 } }, + { .data = { 0x5B, 0x30 } }, + { .data = { 0x5C, 0x16 } }, + { .data = { 0x5D, 0x34 } }, + { .data = { 0x5E, 0x05 } }, + { .data = { 0x5F, 0x02 } }, + { .data = { 0x63, 0x00 } }, + { .data = { 0x64, 0x6A } }, + { .data = { 0x67, 0x73 } }, + { .data = { 0x68, 0x1D } }, + { .data = { 0x69, 0x08 } }, + { .data = { 0x6A, 0x6A } }, + { .data = { 0x6B, 0x08 } }, + { .data = { 0x6C, 0x00 } }, + { .data = { 0x6D, 0x00 } }, + { .data = { 0x6E, 0x00 } }, + { .data = { 0x6F, 0x88 } }, + { .data = { 0x75, 0xFF } }, + { .data = { 0x77, 0xDD } }, + { .data = { 0x78, 0x3F } }, + { .data = { 0x79, 0x15 } }, + { .data = { 0x7A, 0x17 } }, + { .data = { 0x7D, 0x14 } }, + { .data = { 0x7E, 0x82 } }, + { .data = { 0xE0, 0x04 } }, + { .data = { 0x00, 0x0E } }, + { .data = { 0x02, 0xB3 } }, + { .data = { 0x09, 0x61 } }, + { .data = { 0x0E, 0x48 } }, + { .data = { 0xE0, 0x00 } }, + { .data = { 0xE6, 0x02 } }, + { .data = { 0xE7, 0x0C } }, +}; + +static const struct jadard_panel_desc cz101b4001_desc = { + .mode = { + .clock = 70000, + + .hdisplay = 800, + .hsync_start = 800 + 40, + .hsync_end = 800 + 40 + 18, + .htotal = 800 + 40 + 18 + 20, + + .vdisplay = 1280, + .vsync_start = 1280 + 20, + .vsync_end = 1280 + 20 + 4, + .vtotal = 1280 + 20 + 4 + 20, + + .width_mm = 62, + .height_mm = 110, + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, + }, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .init_cmds = cz101b4001_init_cmds, + .num_init_cmds = ARRAY_SIZE(cz101b4001_init_cmds), +}; + +static int jadard_dsi_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + const struct jadard_panel_desc *desc; + struct jadard *jadard; + int ret; + + jadard = devm_kzalloc(&dsi->dev, sizeof(*jadard), GFP_KERNEL); + if (!jadard) + return -ENOMEM; + + desc = of_device_get_match_data(dev); + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_NO_EOT_PACKET; + dsi->format = desc->format; + dsi->lanes = desc->lanes; + + jadard->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(jadard->reset)) { + DRM_DEV_ERROR(&dsi->dev, "failed to get our reset GPIO\n"); + return PTR_ERR(jadard->reset); + } + + jadard->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(jadard->vdd)) { + DRM_DEV_ERROR(&dsi->dev, "failed to get vdd regulator\n"); + return PTR_ERR(jadard->vdd); + } + + jadard->vccio = devm_regulator_get(dev, "vccio"); + if (IS_ERR(jadard->vccio)) { + DRM_DEV_ERROR(&dsi->dev, "failed to get vccio regulator\n"); + return PTR_ERR(jadard->vccio); + } + + drm_panel_init(&jadard->panel, dev, &jadard_funcs, + DRM_MODE_CONNECTOR_DSI); + + ret = drm_panel_of_backlight(&jadard->panel); + if (ret) + return ret; + + drm_panel_add(&jadard->panel); + + mipi_dsi_set_drvdata(dsi, jadard); + jadard->dsi = dsi; + jadard->desc = desc; + + ret = mipi_dsi_attach(dsi); + if (ret < 0) + drm_panel_remove(&jadard->panel); + + return ret; +} + +static void jadard_dsi_remove(struct mipi_dsi_device *dsi) +{ + struct jadard *jadard = mipi_dsi_get_drvdata(dsi); + + mipi_dsi_detach(dsi); + drm_panel_remove(&jadard->panel); +} + +static const struct of_device_id jadard_of_match[] = { + { .compatible = "chongzhou,cz101b4001", .data = &cz101b4001_desc }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jadard_of_match); + +static struct mipi_dsi_driver jadard_driver = { + .probe = jadard_dsi_probe, + .remove = jadard_dsi_remove, + .driver = { + .name = "jadard-jd9365da", + .of_match_table = jadard_of_match, + }, +}; +module_mipi_dsi_driver(jadard_driver); + +MODULE_AUTHOR("Jagan Teki "); +MODULE_AUTHOR("Stephen Chen "); +MODULE_DESCRIPTION("Jadard JD9365DA-H3 WXGA DSI panel"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From f9a3c8488762284c855942d917200d2846392e40 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Fri, 28 Oct 2022 13:56:19 +0200 Subject: MAINTAINERS: adjust entry after arm64 efi-entry.S file movement Commit 6ed27461c9c8 ("arm64: efi: Move efi-entry.S into the libstub source directory") moves arch/arm64/kernel/efi-entry.S to drivers/firmware/efi/libstub/arm64-entry.S, but misses to adjust the MAINTAINERS file. Hence, ./scripts/get_maintainer.pl --self-test=patterns complains about a broken file pattern. Adjust the file entry in EXTENSIBLE FIRMWARE INTERFACE (EFI) to this file movement. As the file drivers/firmware/efi/libstub/arm64-entry.S is already covered by the entry drivers/firmware/efi/, simply remove the arm64 file entry. Signed-off-by: Lukas Bulwahn Signed-off-by: Ard Biesheuvel --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 046ff06ff97f..6d49b9554046 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7789,7 +7789,6 @@ F: Documentation/admin-guide/efi-stub.rst F: arch/*/include/asm/efi.h F: arch/*/kernel/efi.c F: arch/arm/boot/compressed/efi-header.S -F: arch/arm64/kernel/efi-entry.S F: arch/x86/platform/efi/ F: drivers/firmware/efi/ F: include/linux/efi*.h -- cgit v1.2.3 From a39a1466dae5e3ed0dd7c03334a60894e4d1f334 Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Thu, 13 Oct 2022 14:46:36 -0700 Subject: MAINTAINERS: git://github -> https://github.com for awilliam MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Github deprecated the git:// links about a year ago, so let's move to the https:// URLs instead. Reported-by: Conor Dooley Link: https://github.blog/2021-09-01-improving-git-protocol-security-github/ Signed-off-by: Palmer Dabbelt Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20221013214636.30721-1-palmer@rivosinc.com Signed-off-by: Alex Williamson --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 046ff06ff97f..daa6a7a755ec 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21539,7 +21539,7 @@ M: Alex Williamson R: Cornelia Huck L: kvm@vger.kernel.org S: Maintained -T: git git://github.com/awilliam/linux-vfio.git +T: git https://github.com/awilliam/linux-vfio.git F: Documentation/ABI/testing/sysfs-devices-vfio-dev F: Documentation/driver-api/vfio.rst F: drivers/vfio/ -- cgit v1.2.3 From fd325cd648f15eb9a8b32a68de3bafc72bcfe753 Mon Sep 17 00:00:00 2001 From: Long Li Date: Thu, 3 Nov 2022 12:16:25 -0700 Subject: net: mana: Move header files to a common location In preparation to add MANA RDMA driver, move all the required header files to a common location for use by both Ethernet and RDMA drivers. Reviewed-by: Dexuan Cui Signed-off-by: Long Li Link: https://lore.kernel.org/r/1667502990-2559-8-git-send-email-longli@linuxonhyperv.com Acked-by: Haiyang Zhang Signed-off-by: Leon Romanovsky --- MAINTAINERS | 1 + drivers/net/ethernet/microsoft/mana/gdma.h | 689 --------------------- drivers/net/ethernet/microsoft/mana/gdma_main.c | 2 +- drivers/net/ethernet/microsoft/mana/hw_channel.c | 4 +- drivers/net/ethernet/microsoft/mana/hw_channel.h | 195 ------ drivers/net/ethernet/microsoft/mana/mana.h | 650 ------------------- .../net/ethernet/microsoft/mana/mana_auxiliary.h | 10 - drivers/net/ethernet/microsoft/mana/mana_bpf.c | 2 +- drivers/net/ethernet/microsoft/mana/mana_en.c | 4 +- drivers/net/ethernet/microsoft/mana/mana_ethtool.c | 2 +- drivers/net/ethernet/microsoft/mana/shm_channel.c | 2 +- drivers/net/ethernet/microsoft/mana/shm_channel.h | 21 - include/net/mana/gdma.h | 689 +++++++++++++++++++++ include/net/mana/hw_channel.h | 195 ++++++ include/net/mana/mana.h | 650 +++++++++++++++++++ include/net/mana/mana_auxiliary.h | 10 + include/net/mana/shm_channel.h | 21 + 17 files changed, 1574 insertions(+), 1573 deletions(-) delete mode 100644 drivers/net/ethernet/microsoft/mana/gdma.h delete mode 100644 drivers/net/ethernet/microsoft/mana/hw_channel.h delete mode 100644 drivers/net/ethernet/microsoft/mana/mana.h delete mode 100644 drivers/net/ethernet/microsoft/mana/mana_auxiliary.h delete mode 100644 drivers/net/ethernet/microsoft/mana/shm_channel.h create mode 100644 include/net/mana/gdma.h create mode 100644 include/net/mana/hw_channel.h create mode 100644 include/net/mana/mana.h create mode 100644 include/net/mana/mana_auxiliary.h create mode 100644 include/net/mana/shm_channel.h (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 379945f82a64..441a65d41eb4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9541,6 +9541,7 @@ F: include/asm-generic/hyperv-tlfs.h F: include/asm-generic/mshyperv.h F: include/clocksource/hyperv_timer.h F: include/linux/hyperv.h +F: include/net/mana F: include/uapi/linux/hyperv.h F: net/vmw_vsock/hyperv_transport.c F: tools/hv/ diff --git a/drivers/net/ethernet/microsoft/mana/gdma.h b/drivers/net/ethernet/microsoft/mana/gdma.h deleted file mode 100644 index 72eaec2470c0..000000000000 --- a/drivers/net/ethernet/microsoft/mana/gdma.h +++ /dev/null @@ -1,689 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ -/* Copyright (c) 2021, Microsoft Corporation. */ - -#ifndef _GDMA_H -#define _GDMA_H - -#include -#include - -#include "shm_channel.h" - -/* Structures labeled with "HW DATA" are exchanged with the hardware. All of - * them are naturally aligned and hence don't need __packed. - */ - -enum gdma_request_type { - GDMA_VERIFY_VF_DRIVER_VERSION = 1, - GDMA_QUERY_MAX_RESOURCES = 2, - GDMA_LIST_DEVICES = 3, - GDMA_REGISTER_DEVICE = 4, - GDMA_DEREGISTER_DEVICE = 5, - GDMA_GENERATE_TEST_EQE = 10, - GDMA_CREATE_QUEUE = 12, - GDMA_DISABLE_QUEUE = 13, - GDMA_CREATE_DMA_REGION = 25, - GDMA_DMA_REGION_ADD_PAGES = 26, - GDMA_DESTROY_DMA_REGION = 27, -}; - -enum gdma_queue_type { - GDMA_INVALID_QUEUE, - GDMA_SQ, - GDMA_RQ, - GDMA_CQ, - GDMA_EQ, -}; - -enum gdma_work_request_flags { - GDMA_WR_NONE = 0, - GDMA_WR_OOB_IN_SGL = BIT(0), - GDMA_WR_PAD_BY_SGE0 = BIT(1), -}; - -enum gdma_eqe_type { - GDMA_EQE_COMPLETION = 3, - GDMA_EQE_TEST_EVENT = 64, - GDMA_EQE_HWC_INIT_EQ_ID_DB = 129, - GDMA_EQE_HWC_INIT_DATA = 130, - GDMA_EQE_HWC_INIT_DONE = 131, -}; - -enum { - GDMA_DEVICE_NONE = 0, - GDMA_DEVICE_HWC = 1, - GDMA_DEVICE_MANA = 2, -}; - -struct gdma_resource { - /* Protect the bitmap */ - spinlock_t lock; - - /* The bitmap size in bits. */ - u32 size; - - /* The bitmap tracks the resources. */ - unsigned long *map; -}; - -union gdma_doorbell_entry { - u64 as_uint64; - - struct { - u64 id : 24; - u64 reserved : 8; - u64 tail_ptr : 31; - u64 arm : 1; - } cq; - - struct { - u64 id : 24; - u64 wqe_cnt : 8; - u64 tail_ptr : 32; - } rq; - - struct { - u64 id : 24; - u64 reserved : 8; - u64 tail_ptr : 32; - } sq; - - struct { - u64 id : 16; - u64 reserved : 16; - u64 tail_ptr : 31; - u64 arm : 1; - } eq; -}; /* HW DATA */ - -struct gdma_msg_hdr { - u32 hdr_type; - u32 msg_type; - u16 msg_version; - u16 hwc_msg_id; - u32 msg_size; -}; /* HW DATA */ - -struct gdma_dev_id { - union { - struct { - u16 type; - u16 instance; - }; - - u32 as_uint32; - }; -}; /* HW DATA */ - -struct gdma_req_hdr { - struct gdma_msg_hdr req; - struct gdma_msg_hdr resp; /* The expected response */ - struct gdma_dev_id dev_id; - u32 activity_id; -}; /* HW DATA */ - -struct gdma_resp_hdr { - struct gdma_msg_hdr response; - struct gdma_dev_id dev_id; - u32 activity_id; - u32 status; - u32 reserved; -}; /* HW DATA */ - -struct gdma_general_req { - struct gdma_req_hdr hdr; -}; /* HW DATA */ - -#define GDMA_MESSAGE_V1 1 - -struct gdma_general_resp { - struct gdma_resp_hdr hdr; -}; /* HW DATA */ - -#define GDMA_STANDARD_HEADER_TYPE 0 - -static inline void mana_gd_init_req_hdr(struct gdma_req_hdr *hdr, u32 code, - u32 req_size, u32 resp_size) -{ - hdr->req.hdr_type = GDMA_STANDARD_HEADER_TYPE; - hdr->req.msg_type = code; - hdr->req.msg_version = GDMA_MESSAGE_V1; - hdr->req.msg_size = req_size; - - hdr->resp.hdr_type = GDMA_STANDARD_HEADER_TYPE; - hdr->resp.msg_type = code; - hdr->resp.msg_version = GDMA_MESSAGE_V1; - hdr->resp.msg_size = resp_size; -} - -/* The 16-byte struct is part of the GDMA work queue entry (WQE). */ -struct gdma_sge { - u64 address; - u32 mem_key; - u32 size; -}; /* HW DATA */ - -struct gdma_wqe_request { - struct gdma_sge *sgl; - u32 num_sge; - - u32 inline_oob_size; - const void *inline_oob_data; - - u32 flags; - u32 client_data_unit; -}; - -enum gdma_page_type { - GDMA_PAGE_TYPE_4K, -}; - -#define GDMA_INVALID_DMA_REGION 0 - -struct gdma_mem_info { - struct device *dev; - - dma_addr_t dma_handle; - void *virt_addr; - u64 length; - - /* Allocated by the PF driver */ - u64 gdma_region; -}; - -#define REGISTER_ATB_MST_MKEY_LOWER_SIZE 8 - -struct gdma_dev { - struct gdma_context *gdma_context; - - struct gdma_dev_id dev_id; - - u32 pdid; - u32 doorbell; - u32 gpa_mkey; - - /* GDMA driver specific pointer */ - void *driver_data; - - struct auxiliary_device *adev; -}; - -#define MINIMUM_SUPPORTED_PAGE_SIZE PAGE_SIZE - -#define GDMA_CQE_SIZE 64 -#define GDMA_EQE_SIZE 16 -#define GDMA_MAX_SQE_SIZE 512 -#define GDMA_MAX_RQE_SIZE 256 - -#define GDMA_COMP_DATA_SIZE 0x3C - -#define GDMA_EVENT_DATA_SIZE 0xC - -/* The WQE size must be a multiple of the Basic Unit, which is 32 bytes. */ -#define GDMA_WQE_BU_SIZE 32 - -#define INVALID_PDID UINT_MAX -#define INVALID_DOORBELL UINT_MAX -#define INVALID_MEM_KEY UINT_MAX -#define INVALID_QUEUE_ID UINT_MAX -#define INVALID_PCI_MSIX_INDEX UINT_MAX - -struct gdma_comp { - u32 cqe_data[GDMA_COMP_DATA_SIZE / 4]; - u32 wq_num; - bool is_sq; -}; - -struct gdma_event { - u32 details[GDMA_EVENT_DATA_SIZE / 4]; - u8 type; -}; - -struct gdma_queue; - -struct mana_eq { - struct gdma_queue *eq; -}; - -typedef void gdma_eq_callback(void *context, struct gdma_queue *q, - struct gdma_event *e); - -typedef void gdma_cq_callback(void *context, struct gdma_queue *q); - -/* The 'head' is the producer index. For SQ/RQ, when the driver posts a WQE - * (Note: the WQE size must be a multiple of the 32-byte Basic Unit), the - * driver increases the 'head' in BUs rather than in bytes, and notifies - * the HW of the updated head. For EQ/CQ, the driver uses the 'head' to track - * the HW head, and increases the 'head' by 1 for every processed EQE/CQE. - * - * The 'tail' is the consumer index for SQ/RQ. After the CQE of the SQ/RQ is - * processed, the driver increases the 'tail' to indicate that WQEs have - * been consumed by the HW, so the driver can post new WQEs into the SQ/RQ. - * - * The driver doesn't use the 'tail' for EQ/CQ, because the driver ensures - * that the EQ/CQ is big enough so they can't overflow, and the driver uses - * the owner bits mechanism to detect if the queue has become empty. - */ -struct gdma_queue { - struct gdma_dev *gdma_dev; - - enum gdma_queue_type type; - u32 id; - - struct gdma_mem_info mem_info; - - void *queue_mem_ptr; - u32 queue_size; - - bool monitor_avl_buf; - - u32 head; - u32 tail; - - /* Extra fields specific to EQ/CQ. */ - union { - struct { - bool disable_needed; - - gdma_eq_callback *callback; - void *context; - - unsigned int msix_index; - - u32 log2_throttle_limit; - } eq; - - struct { - gdma_cq_callback *callback; - void *context; - - struct gdma_queue *parent; /* For CQ/EQ relationship */ - } cq; - }; -}; - -struct gdma_queue_spec { - enum gdma_queue_type type; - bool monitor_avl_buf; - unsigned int queue_size; - - /* Extra fields specific to EQ/CQ. */ - union { - struct { - gdma_eq_callback *callback; - void *context; - - unsigned long log2_throttle_limit; - } eq; - - struct { - gdma_cq_callback *callback; - void *context; - - struct gdma_queue *parent_eq; - - } cq; - }; -}; - -struct gdma_irq_context { - void (*handler)(void *arg); - void *arg; -}; - -struct gdma_context { - struct device *dev; - - /* Per-vPort max number of queues */ - unsigned int max_num_queues; - unsigned int max_num_msix; - unsigned int num_msix_usable; - struct gdma_resource msix_resource; - struct gdma_irq_context *irq_contexts; - - /* This maps a CQ index to the queue structure. */ - unsigned int max_num_cqs; - struct gdma_queue **cq_table; - - /* Protect eq_test_event and test_event_eq_id */ - struct mutex eq_test_event_mutex; - struct completion eq_test_event; - u32 test_event_eq_id; - - bool is_pf; - phys_addr_t bar0_pa; - void __iomem *bar0_va; - void __iomem *shm_base; - void __iomem *db_page_base; - phys_addr_t phys_db_page_base; - u32 db_page_size; - - /* Shared memory chanenl (used to bootstrap HWC) */ - struct shm_channel shm_channel; - - /* Hardware communication channel (HWC) */ - struct gdma_dev hwc; - - /* Azure network adapter */ - struct gdma_dev mana; -}; - -#define MAX_NUM_GDMA_DEVICES 4 - -static inline bool mana_gd_is_mana(struct gdma_dev *gd) -{ - return gd->dev_id.type == GDMA_DEVICE_MANA; -} - -static inline bool mana_gd_is_hwc(struct gdma_dev *gd) -{ - return gd->dev_id.type == GDMA_DEVICE_HWC; -} - -u8 *mana_gd_get_wqe_ptr(const struct gdma_queue *wq, u32 wqe_offset); -u32 mana_gd_wq_avail_space(struct gdma_queue *wq); - -int mana_gd_test_eq(struct gdma_context *gc, struct gdma_queue *eq); - -int mana_gd_create_hwc_queue(struct gdma_dev *gd, - const struct gdma_queue_spec *spec, - struct gdma_queue **queue_ptr); - -int mana_gd_create_mana_eq(struct gdma_dev *gd, - const struct gdma_queue_spec *spec, - struct gdma_queue **queue_ptr); - -int mana_gd_create_mana_wq_cq(struct gdma_dev *gd, - const struct gdma_queue_spec *spec, - struct gdma_queue **queue_ptr); - -void mana_gd_destroy_queue(struct gdma_context *gc, struct gdma_queue *queue); - -int mana_gd_poll_cq(struct gdma_queue *cq, struct gdma_comp *comp, int num_cqe); - -void mana_gd_ring_cq(struct gdma_queue *cq, u8 arm_bit); - -struct gdma_wqe { - u32 reserved :24; - u32 last_vbytes :8; - - union { - u32 flags; - - struct { - u32 num_sge :8; - u32 inline_oob_size_div4:3; - u32 client_oob_in_sgl :1; - u32 reserved1 :4; - u32 client_data_unit :14; - u32 reserved2 :2; - }; - }; -}; /* HW DATA */ - -#define INLINE_OOB_SMALL_SIZE 8 -#define INLINE_OOB_LARGE_SIZE 24 - -#define MAX_TX_WQE_SIZE 512 -#define MAX_RX_WQE_SIZE 256 - -struct gdma_cqe { - u32 cqe_data[GDMA_COMP_DATA_SIZE / 4]; - - union { - u32 as_uint32; - - struct { - u32 wq_num : 24; - u32 is_sq : 1; - u32 reserved : 4; - u32 owner_bits : 3; - }; - } cqe_info; -}; /* HW DATA */ - -#define GDMA_CQE_OWNER_BITS 3 - -#define GDMA_CQE_OWNER_MASK ((1 << GDMA_CQE_OWNER_BITS) - 1) - -#define SET_ARM_BIT 1 - -#define GDMA_EQE_OWNER_BITS 3 - -union gdma_eqe_info { - u32 as_uint32; - - struct { - u32 type : 8; - u32 reserved1 : 8; - u32 client_id : 2; - u32 reserved2 : 11; - u32 owner_bits : 3; - }; -}; /* HW DATA */ - -#define GDMA_EQE_OWNER_MASK ((1 << GDMA_EQE_OWNER_BITS) - 1) -#define INITIALIZED_OWNER_BIT(log2_num_entries) (1UL << (log2_num_entries)) - -struct gdma_eqe { - u32 details[GDMA_EVENT_DATA_SIZE / 4]; - u32 eqe_info; -}; /* HW DATA */ - -#define GDMA_REG_DB_PAGE_OFFSET 8 -#define GDMA_REG_DB_PAGE_SIZE 0x10 -#define GDMA_REG_SHM_OFFSET 0x18 - -#define GDMA_PF_REG_DB_PAGE_SIZE 0xD0 -#define GDMA_PF_REG_DB_PAGE_OFF 0xC8 -#define GDMA_PF_REG_SHM_OFF 0x70 - -#define GDMA_SRIOV_REG_CFG_BASE_OFF 0x108 - -#define MANA_PF_DEVICE_ID 0x00B9 -#define MANA_VF_DEVICE_ID 0x00BA - -struct gdma_posted_wqe_info { - u32 wqe_size_in_bu; -}; - -/* GDMA_GENERATE_TEST_EQE */ -struct gdma_generate_test_event_req { - struct gdma_req_hdr hdr; - u32 queue_index; -}; /* HW DATA */ - -/* GDMA_VERIFY_VF_DRIVER_VERSION */ -enum { - GDMA_PROTOCOL_V1 = 1, - GDMA_PROTOCOL_FIRST = GDMA_PROTOCOL_V1, - GDMA_PROTOCOL_LAST = GDMA_PROTOCOL_V1, -}; - -#define GDMA_DRV_CAP_FLAG_1_EQ_SHARING_MULTI_VPORT BIT(0) - -#define GDMA_DRV_CAP_FLAGS1 GDMA_DRV_CAP_FLAG_1_EQ_SHARING_MULTI_VPORT - -#define GDMA_DRV_CAP_FLAGS2 0 - -#define GDMA_DRV_CAP_FLAGS3 0 - -#define GDMA_DRV_CAP_FLAGS4 0 - -struct gdma_verify_ver_req { - struct gdma_req_hdr hdr; - - /* Mandatory fields required for protocol establishment */ - u64 protocol_ver_min; - u64 protocol_ver_max; - - /* Gdma Driver Capability Flags */ - u64 gd_drv_cap_flags1; - u64 gd_drv_cap_flags2; - u64 gd_drv_cap_flags3; - u64 gd_drv_cap_flags4; - - /* Advisory fields */ - u64 drv_ver; - u32 os_type; /* Linux = 0x10; Windows = 0x20; Other = 0x30 */ - u32 reserved; - u32 os_ver_major; - u32 os_ver_minor; - u32 os_ver_build; - u32 os_ver_platform; - u64 reserved_2; - u8 os_ver_str1[128]; - u8 os_ver_str2[128]; - u8 os_ver_str3[128]; - u8 os_ver_str4[128]; -}; /* HW DATA */ - -struct gdma_verify_ver_resp { - struct gdma_resp_hdr hdr; - u64 gdma_protocol_ver; - u64 pf_cap_flags1; - u64 pf_cap_flags2; - u64 pf_cap_flags3; - u64 pf_cap_flags4; -}; /* HW DATA */ - -/* GDMA_QUERY_MAX_RESOURCES */ -struct gdma_query_max_resources_resp { - struct gdma_resp_hdr hdr; - u32 status; - u32 max_sq; - u32 max_rq; - u32 max_cq; - u32 max_eq; - u32 max_db; - u32 max_mst; - u32 max_cq_mod_ctx; - u32 max_mod_cq; - u32 max_msix; -}; /* HW DATA */ - -/* GDMA_LIST_DEVICES */ -struct gdma_list_devices_resp { - struct gdma_resp_hdr hdr; - u32 num_of_devs; - u32 reserved; - struct gdma_dev_id devs[64]; -}; /* HW DATA */ - -/* GDMA_REGISTER_DEVICE */ -struct gdma_register_device_resp { - struct gdma_resp_hdr hdr; - u32 pdid; - u32 gpa_mkey; - u32 db_id; -}; /* HW DATA */ - -/* GDMA_CREATE_QUEUE */ -struct gdma_create_queue_req { - struct gdma_req_hdr hdr; - u32 type; - u32 reserved1; - u32 pdid; - u32 doolbell_id; - u64 gdma_region; - u32 reserved2; - u32 queue_size; - u32 log2_throttle_limit; - u32 eq_pci_msix_index; - u32 cq_mod_ctx_id; - u32 cq_parent_eq_id; - u8 rq_drop_on_overrun; - u8 rq_err_on_wqe_overflow; - u8 rq_chain_rec_wqes; - u8 sq_hw_db; - u32 reserved3; -}; /* HW DATA */ - -struct gdma_create_queue_resp { - struct gdma_resp_hdr hdr; - u32 queue_index; -}; /* HW DATA */ - -/* GDMA_DISABLE_QUEUE */ -struct gdma_disable_queue_req { - struct gdma_req_hdr hdr; - u32 type; - u32 queue_index; - u32 alloc_res_id_on_creation; -}; /* HW DATA */ - -/* GDMA_CREATE_DMA_REGION */ -struct gdma_create_dma_region_req { - struct gdma_req_hdr hdr; - - /* The total size of the DMA region */ - u64 length; - - /* The offset in the first page */ - u32 offset_in_page; - - /* enum gdma_page_type */ - u32 gdma_page_type; - - /* The total number of pages */ - u32 page_count; - - /* If page_addr_list_len is smaller than page_count, - * the remaining page addresses will be added via the - * message GDMA_DMA_REGION_ADD_PAGES. - */ - u32 page_addr_list_len; - u64 page_addr_list[]; -}; /* HW DATA */ - -struct gdma_create_dma_region_resp { - struct gdma_resp_hdr hdr; - u64 gdma_region; -}; /* HW DATA */ - -/* GDMA_DMA_REGION_ADD_PAGES */ -struct gdma_dma_region_add_pages_req { - struct gdma_req_hdr hdr; - - u64 gdma_region; - - u32 page_addr_list_len; - u32 reserved3; - - u64 page_addr_list[]; -}; /* HW DATA */ - -/* GDMA_DESTROY_DMA_REGION */ -struct gdma_destroy_dma_region_req { - struct gdma_req_hdr hdr; - - u64 gdma_region; -}; /* HW DATA */ - -int mana_gd_verify_vf_version(struct pci_dev *pdev); - -int mana_gd_register_device(struct gdma_dev *gd); -int mana_gd_deregister_device(struct gdma_dev *gd); - -int mana_gd_post_work_request(struct gdma_queue *wq, - const struct gdma_wqe_request *wqe_req, - struct gdma_posted_wqe_info *wqe_info); - -int mana_gd_post_and_ring(struct gdma_queue *queue, - const struct gdma_wqe_request *wqe, - struct gdma_posted_wqe_info *wqe_info); - -int mana_gd_alloc_res_map(u32 res_avail, struct gdma_resource *r); -void mana_gd_free_res_map(struct gdma_resource *r); - -void mana_gd_wq_ring_doorbell(struct gdma_context *gc, - struct gdma_queue *queue); - -int mana_gd_alloc_memory(struct gdma_context *gc, unsigned int length, - struct gdma_mem_info *gmi); - -void mana_gd_free_memory(struct gdma_mem_info *gmi); - -int mana_gd_send_request(struct gdma_context *gc, u32 req_len, const void *req, - u32 resp_len, void *resp); -#endif /* _GDMA_H */ diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index f0e22954d5c0..69795bc679e7 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -6,7 +6,7 @@ #include #include -#include "mana.h" +#include static u32 mana_gd_r32(struct gdma_context *g, u64 offset) { diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c index 543a5d5c304f..76829ab43d40 100644 --- a/drivers/net/ethernet/microsoft/mana/hw_channel.c +++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright (c) 2021, Microsoft Corporation. */ -#include "gdma.h" -#include "hw_channel.h" +#include +#include static int mana_hwc_get_msg_index(struct hw_channel_context *hwc, u16 *msg_id) { diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.h b/drivers/net/ethernet/microsoft/mana/hw_channel.h deleted file mode 100644 index 6a757a6e2732..000000000000 --- a/drivers/net/ethernet/microsoft/mana/hw_channel.h +++ /dev/null @@ -1,195 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ -/* Copyright (c) 2021, Microsoft Corporation. */ - -#ifndef _HW_CHANNEL_H -#define _HW_CHANNEL_H - -#define DEFAULT_LOG2_THROTTLING_FOR_ERROR_EQ 4 - -#define HW_CHANNEL_MAX_REQUEST_SIZE 0x1000 -#define HW_CHANNEL_MAX_RESPONSE_SIZE 0x1000 - -#define HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH 1 - -#define HWC_INIT_DATA_CQID 1 -#define HWC_INIT_DATA_RQID 2 -#define HWC_INIT_DATA_SQID 3 -#define HWC_INIT_DATA_QUEUE_DEPTH 4 -#define HWC_INIT_DATA_MAX_REQUEST 5 -#define HWC_INIT_DATA_MAX_RESPONSE 6 -#define HWC_INIT_DATA_MAX_NUM_CQS 7 -#define HWC_INIT_DATA_PDID 8 -#define HWC_INIT_DATA_GPA_MKEY 9 -#define HWC_INIT_DATA_PF_DEST_RQ_ID 10 -#define HWC_INIT_DATA_PF_DEST_CQ_ID 11 - -/* Structures labeled with "HW DATA" are exchanged with the hardware. All of - * them are naturally aligned and hence don't need __packed. - */ - -union hwc_init_eq_id_db { - u32 as_uint32; - - struct { - u32 eq_id : 16; - u32 doorbell : 16; - }; -}; /* HW DATA */ - -union hwc_init_type_data { - u32 as_uint32; - - struct { - u32 value : 24; - u32 type : 8; - }; -}; /* HW DATA */ - -struct hwc_rx_oob { - u32 type : 6; - u32 eom : 1; - u32 som : 1; - u32 vendor_err : 8; - u32 reserved1 : 16; - - u32 src_virt_wq : 24; - u32 src_vfid : 8; - - u32 reserved2; - - union { - u32 wqe_addr_low; - u32 wqe_offset; - }; - - u32 wqe_addr_high; - - u32 client_data_unit : 14; - u32 reserved3 : 18; - - u32 tx_oob_data_size; - - u32 chunk_offset : 21; - u32 reserved4 : 11; -}; /* HW DATA */ - -struct hwc_tx_oob { - u32 reserved1; - - u32 reserved2; - - u32 vrq_id : 24; - u32 dest_vfid : 8; - - u32 vrcq_id : 24; - u32 reserved3 : 8; - - u32 vscq_id : 24; - u32 loopback : 1; - u32 lso_override: 1; - u32 dest_pf : 1; - u32 reserved4 : 5; - - u32 vsq_id : 24; - u32 reserved5 : 8; -}; /* HW DATA */ - -struct hwc_work_request { - void *buf_va; - void *buf_sge_addr; - u32 buf_len; - u32 msg_size; - - struct gdma_wqe_request wqe_req; - struct hwc_tx_oob tx_oob; - - struct gdma_sge sge; -}; - -/* hwc_dma_buf represents the array of in-flight WQEs. - * mem_info as know as the GDMA mapped memory is partitioned and used by - * in-flight WQEs. - * The number of WQEs is determined by the number of in-flight messages. - */ -struct hwc_dma_buf { - struct gdma_mem_info mem_info; - - u32 gpa_mkey; - - u32 num_reqs; - struct hwc_work_request reqs[]; -}; - -typedef void hwc_rx_event_handler_t(void *ctx, u32 gdma_rxq_id, - const struct hwc_rx_oob *rx_oob); - -typedef void hwc_tx_event_handler_t(void *ctx, u32 gdma_txq_id, - const struct hwc_rx_oob *rx_oob); - -struct hwc_cq { - struct hw_channel_context *hwc; - - struct gdma_queue *gdma_cq; - struct gdma_queue *gdma_eq; - struct gdma_comp *comp_buf; - u16 queue_depth; - - hwc_rx_event_handler_t *rx_event_handler; - void *rx_event_ctx; - - hwc_tx_event_handler_t *tx_event_handler; - void *tx_event_ctx; -}; - -struct hwc_wq { - struct hw_channel_context *hwc; - - struct gdma_queue *gdma_wq; - struct hwc_dma_buf *msg_buf; - u16 queue_depth; - - struct hwc_cq *hwc_cq; -}; - -struct hwc_caller_ctx { - struct completion comp_event; - void *output_buf; - u32 output_buflen; - - u32 error; /* Linux error code */ - u32 status_code; -}; - -struct hw_channel_context { - struct gdma_dev *gdma_dev; - struct device *dev; - - u16 num_inflight_msg; - u32 max_req_msg_size; - - u16 hwc_init_q_depth_max; - u32 hwc_init_max_req_msg_size; - u32 hwc_init_max_resp_msg_size; - - struct completion hwc_init_eqe_comp; - - struct hwc_wq *rxq; - struct hwc_wq *txq; - struct hwc_cq *cq; - - struct semaphore sema; - struct gdma_resource inflight_msg_res; - - u32 pf_dest_vrq_id; - u32 pf_dest_vrcq_id; - - struct hwc_caller_ctx *caller_ctx; -}; - -int mana_hwc_create_channel(struct gdma_context *gc); -void mana_hwc_destroy_channel(struct gdma_context *gc); - -int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len, - const void *req, u32 resp_len, void *resp); - -#endif /* _HW_CHANNEL_H */ diff --git a/drivers/net/ethernet/microsoft/mana/mana.h b/drivers/net/ethernet/microsoft/mana/mana.h deleted file mode 100644 index 6e9e86fb4c02..000000000000 --- a/drivers/net/ethernet/microsoft/mana/mana.h +++ /dev/null @@ -1,650 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ -/* Copyright (c) 2021, Microsoft Corporation. */ - -#ifndef _MANA_H -#define _MANA_H - -#include "gdma.h" -#include "hw_channel.h" - -/* Microsoft Azure Network Adapter (MANA)'s definitions - * - * Structures labeled with "HW DATA" are exchanged with the hardware. All of - * them are naturally aligned and hence don't need __packed. - */ - -/* MANA protocol version */ -#define MANA_MAJOR_VERSION 0 -#define MANA_MINOR_VERSION 1 -#define MANA_MICRO_VERSION 1 - -typedef u64 mana_handle_t; -#define INVALID_MANA_HANDLE ((mana_handle_t)-1) - -enum TRI_STATE { - TRI_STATE_UNKNOWN = -1, - TRI_STATE_FALSE = 0, - TRI_STATE_TRUE = 1 -}; - -/* Number of entries for hardware indirection table must be in power of 2 */ -#define MANA_INDIRECT_TABLE_SIZE 64 -#define MANA_INDIRECT_TABLE_MASK (MANA_INDIRECT_TABLE_SIZE - 1) - -/* The Toeplitz hash key's length in bytes: should be multiple of 8 */ -#define MANA_HASH_KEY_SIZE 40 - -#define COMP_ENTRY_SIZE 64 - -#define ADAPTER_MTU_SIZE 1500 -#define MAX_FRAME_SIZE (ADAPTER_MTU_SIZE + 14) - -#define RX_BUFFERS_PER_QUEUE 512 - -#define MAX_SEND_BUFFERS_PER_QUEUE 256 - -#define EQ_SIZE (8 * PAGE_SIZE) -#define LOG2_EQ_THROTTLE 3 - -#define MAX_PORTS_IN_MANA_DEV 256 - -struct mana_stats_rx { - u64 packets; - u64 bytes; - u64 xdp_drop; - u64 xdp_tx; - u64 xdp_redirect; - struct u64_stats_sync syncp; -}; - -struct mana_stats_tx { - u64 packets; - u64 bytes; - u64 xdp_xmit; - struct u64_stats_sync syncp; -}; - -struct mana_txq { - struct gdma_queue *gdma_sq; - - union { - u32 gdma_txq_id; - struct { - u32 reserved1 : 10; - u32 vsq_frame : 14; - u32 reserved2 : 8; - }; - }; - - u16 vp_offset; - - struct net_device *ndev; - - /* The SKBs are sent to the HW and we are waiting for the CQEs. */ - struct sk_buff_head pending_skbs; - struct netdev_queue *net_txq; - - atomic_t pending_sends; - - struct mana_stats_tx stats; -}; - -/* skb data and frags dma mappings */ -struct mana_skb_head { - dma_addr_t dma_handle[MAX_SKB_FRAGS + 1]; - - u32 size[MAX_SKB_FRAGS + 1]; -}; - -#define MANA_HEADROOM sizeof(struct mana_skb_head) - -enum mana_tx_pkt_format { - MANA_SHORT_PKT_FMT = 0, - MANA_LONG_PKT_FMT = 1, -}; - -struct mana_tx_short_oob { - u32 pkt_fmt : 2; - u32 is_outer_ipv4 : 1; - u32 is_outer_ipv6 : 1; - u32 comp_iphdr_csum : 1; - u32 comp_tcp_csum : 1; - u32 comp_udp_csum : 1; - u32 supress_txcqe_gen : 1; - u32 vcq_num : 24; - - u32 trans_off : 10; /* Transport header offset */ - u32 vsq_frame : 14; - u32 short_vp_offset : 8; -}; /* HW DATA */ - -struct mana_tx_long_oob { - u32 is_encap : 1; - u32 inner_is_ipv6 : 1; - u32 inner_tcp_opt : 1; - u32 inject_vlan_pri_tag : 1; - u32 reserved1 : 12; - u32 pcp : 3; /* 802.1Q */ - u32 dei : 1; /* 802.1Q */ - u32 vlan_id : 12; /* 802.1Q */ - - u32 inner_frame_offset : 10; - u32 inner_ip_rel_offset : 6; - u32 long_vp_offset : 12; - u32 reserved2 : 4; - - u32 reserved3; - u32 reserved4; -}; /* HW DATA */ - -struct mana_tx_oob { - struct mana_tx_short_oob s_oob; - struct mana_tx_long_oob l_oob; -}; /* HW DATA */ - -enum mana_cq_type { - MANA_CQ_TYPE_RX, - MANA_CQ_TYPE_TX, -}; - -enum mana_cqe_type { - CQE_INVALID = 0, - CQE_RX_OKAY = 1, - CQE_RX_COALESCED_4 = 2, - CQE_RX_OBJECT_FENCE = 3, - CQE_RX_TRUNCATED = 4, - - CQE_TX_OKAY = 32, - CQE_TX_SA_DROP = 33, - CQE_TX_MTU_DROP = 34, - CQE_TX_INVALID_OOB = 35, - CQE_TX_INVALID_ETH_TYPE = 36, - CQE_TX_HDR_PROCESSING_ERROR = 37, - CQE_TX_VF_DISABLED = 38, - CQE_TX_VPORT_IDX_OUT_OF_RANGE = 39, - CQE_TX_VPORT_DISABLED = 40, - CQE_TX_VLAN_TAGGING_VIOLATION = 41, -}; - -#define MANA_CQE_COMPLETION 1 - -struct mana_cqe_header { - u32 cqe_type : 6; - u32 client_type : 2; - u32 vendor_err : 24; -}; /* HW DATA */ - -/* NDIS HASH Types */ -#define NDIS_HASH_IPV4 BIT(0) -#define NDIS_HASH_TCP_IPV4 BIT(1) -#define NDIS_HASH_UDP_IPV4 BIT(2) -#define NDIS_HASH_IPV6 BIT(3) -#define NDIS_HASH_TCP_IPV6 BIT(4) -#define NDIS_HASH_UDP_IPV6 BIT(5) -#define NDIS_HASH_IPV6_EX BIT(6) -#define NDIS_HASH_TCP_IPV6_EX BIT(7) -#define NDIS_HASH_UDP_IPV6_EX BIT(8) - -#define MANA_HASH_L3 (NDIS_HASH_IPV4 | NDIS_HASH_IPV6 | NDIS_HASH_IPV6_EX) -#define MANA_HASH_L4 \ - (NDIS_HASH_TCP_IPV4 | NDIS_HASH_UDP_IPV4 | NDIS_HASH_TCP_IPV6 | \ - NDIS_HASH_UDP_IPV6 | NDIS_HASH_TCP_IPV6_EX | NDIS_HASH_UDP_IPV6_EX) - -struct mana_rxcomp_perpkt_info { - u32 pkt_len : 16; - u32 reserved1 : 16; - u32 reserved2; - u32 pkt_hash; -}; /* HW DATA */ - -#define MANA_RXCOMP_OOB_NUM_PPI 4 - -/* Receive completion OOB */ -struct mana_rxcomp_oob { - struct mana_cqe_header cqe_hdr; - - u32 rx_vlan_id : 12; - u32 rx_vlantag_present : 1; - u32 rx_outer_iphdr_csum_succeed : 1; - u32 rx_outer_iphdr_csum_fail : 1; - u32 reserved1 : 1; - u32 rx_hashtype : 9; - u32 rx_iphdr_csum_succeed : 1; - u32 rx_iphdr_csum_fail : 1; - u32 rx_tcp_csum_succeed : 1; - u32 rx_tcp_csum_fail : 1; - u32 rx_udp_csum_succeed : 1; - u32 rx_udp_csum_fail : 1; - u32 reserved2 : 1; - - struct mana_rxcomp_perpkt_info ppi[MANA_RXCOMP_OOB_NUM_PPI]; - - u32 rx_wqe_offset; -}; /* HW DATA */ - -struct mana_tx_comp_oob { - struct mana_cqe_header cqe_hdr; - - u32 tx_data_offset; - - u32 tx_sgl_offset : 5; - u32 tx_wqe_offset : 27; - - u32 reserved[12]; -}; /* HW DATA */ - -struct mana_rxq; - -#define CQE_POLLING_BUFFER 512 - -struct mana_cq { - struct gdma_queue *gdma_cq; - - /* Cache the CQ id (used to verify if each CQE comes to the right CQ. */ - u32 gdma_id; - - /* Type of the CQ: TX or RX */ - enum mana_cq_type type; - - /* Pointer to the mana_rxq that is pushing RX CQEs to the queue. - * Only and must be non-NULL if type is MANA_CQ_TYPE_RX. - */ - struct mana_rxq *rxq; - - /* Pointer to the mana_txq that is pushing TX CQEs to the queue. - * Only and must be non-NULL if type is MANA_CQ_TYPE_TX. - */ - struct mana_txq *txq; - - /* Buffer which the CQ handler can copy the CQE's into. */ - struct gdma_comp gdma_comp_buf[CQE_POLLING_BUFFER]; - - /* NAPI data */ - struct napi_struct napi; - int work_done; - int budget; -}; - -#define GDMA_MAX_RQE_SGES 15 - -struct mana_recv_buf_oob { - /* A valid GDMA work request representing the data buffer. */ - struct gdma_wqe_request wqe_req; - - void *buf_va; - dma_addr_t buf_dma_addr; - - /* SGL of the buffer going to be sent has part of the work request. */ - u32 num_sge; - struct gdma_sge sgl[GDMA_MAX_RQE_SGES]; - - /* Required to store the result of mana_gd_post_work_request. - * gdma_posted_wqe_info.wqe_size_in_bu is required for progressing the - * work queue when the WQE is consumed. - */ - struct gdma_posted_wqe_info wqe_inf; -}; - -struct mana_rxq { - struct gdma_queue *gdma_rq; - /* Cache the gdma receive queue id */ - u32 gdma_id; - - /* Index of RQ in the vPort, not gdma receive queue id */ - u32 rxq_idx; - - u32 datasize; - - mana_handle_t rxobj; - - struct mana_cq rx_cq; - - struct completion fence_event; - - struct net_device *ndev; - - /* Total number of receive buffers to be allocated */ - u32 num_rx_buf; - - u32 buf_index; - - struct mana_stats_rx stats; - - struct bpf_prog __rcu *bpf_prog; - struct xdp_rxq_info xdp_rxq; - struct page *xdp_save_page; - bool xdp_flush; - int xdp_rc; /* XDP redirect return code */ - - /* MUST BE THE LAST MEMBER: - * Each receive buffer has an associated mana_recv_buf_oob. - */ - struct mana_recv_buf_oob rx_oobs[]; -}; - -struct mana_tx_qp { - struct mana_txq txq; - - struct mana_cq tx_cq; - - mana_handle_t tx_object; -}; - -struct mana_ethtool_stats { - u64 stop_queue; - u64 wake_queue; -}; - -struct mana_context { - struct gdma_dev *gdma_dev; - - u16 num_ports; - - struct mana_eq *eqs; - - struct net_device *ports[MAX_PORTS_IN_MANA_DEV]; -}; - -struct mana_port_context { - struct mana_context *ac; - struct net_device *ndev; - - u8 mac_addr[ETH_ALEN]; - - enum TRI_STATE rss_state; - - mana_handle_t default_rxobj; - bool tx_shortform_allowed; - u16 tx_vp_offset; - - struct mana_tx_qp *tx_qp; - - /* Indirection Table for RX & TX. The values are queue indexes */ - u32 indir_table[MANA_INDIRECT_TABLE_SIZE]; - - /* Indirection table containing RxObject Handles */ - mana_handle_t rxobj_table[MANA_INDIRECT_TABLE_SIZE]; - - /* Hash key used by the NIC */ - u8 hashkey[MANA_HASH_KEY_SIZE]; - - /* This points to an array of num_queues of RQ pointers. */ - struct mana_rxq **rxqs; - - struct bpf_prog *bpf_prog; - - /* Create num_queues EQs, SQs, SQ-CQs, RQs and RQ-CQs, respectively. */ - unsigned int max_queues; - unsigned int num_queues; - - mana_handle_t port_handle; - mana_handle_t pf_filter_handle; - - /* Mutex for sharing access to vport_use_count */ - struct mutex vport_mutex; - int vport_use_count; - - u16 port_idx; - - bool port_is_up; - bool port_st_save; /* Saved port state */ - - struct mana_ethtool_stats eth_stats; -}; - -int mana_start_xmit(struct sk_buff *skb, struct net_device *ndev); -int mana_config_rss(struct mana_port_context *ac, enum TRI_STATE rx, - bool update_hash, bool update_tab); - -int mana_alloc_queues(struct net_device *ndev); -int mana_attach(struct net_device *ndev); -int mana_detach(struct net_device *ndev, bool from_close); - -int mana_probe(struct gdma_dev *gd, bool resuming); -void mana_remove(struct gdma_dev *gd, bool suspending); - -void mana_xdp_tx(struct sk_buff *skb, struct net_device *ndev); -int mana_xdp_xmit(struct net_device *ndev, int n, struct xdp_frame **frames, - u32 flags); -u32 mana_run_xdp(struct net_device *ndev, struct mana_rxq *rxq, - struct xdp_buff *xdp, void *buf_va, uint pkt_len); -struct bpf_prog *mana_xdp_get(struct mana_port_context *apc); -void mana_chn_setxdp(struct mana_port_context *apc, struct bpf_prog *prog); -int mana_bpf(struct net_device *ndev, struct netdev_bpf *bpf); - -extern const struct ethtool_ops mana_ethtool_ops; - -struct mana_obj_spec { - u32 queue_index; - u64 gdma_region; - u32 queue_size; - u32 attached_eq; - u32 modr_ctx_id; -}; - -enum mana_command_code { - MANA_QUERY_DEV_CONFIG = 0x20001, - MANA_QUERY_GF_STAT = 0x20002, - MANA_CONFIG_VPORT_TX = 0x20003, - MANA_CREATE_WQ_OBJ = 0x20004, - MANA_DESTROY_WQ_OBJ = 0x20005, - MANA_FENCE_RQ = 0x20006, - MANA_CONFIG_VPORT_RX = 0x20007, - MANA_QUERY_VPORT_CONFIG = 0x20008, - - /* Privileged commands for the PF mode */ - MANA_REGISTER_FILTER = 0x28000, - MANA_DEREGISTER_FILTER = 0x28001, - MANA_REGISTER_HW_PORT = 0x28003, - MANA_DEREGISTER_HW_PORT = 0x28004, -}; - -/* Query Device Configuration */ -struct mana_query_device_cfg_req { - struct gdma_req_hdr hdr; - - /* MANA Nic Driver Capability flags */ - u64 mn_drv_cap_flags1; - u64 mn_drv_cap_flags2; - u64 mn_drv_cap_flags3; - u64 mn_drv_cap_flags4; - - u32 proto_major_ver; - u32 proto_minor_ver; - u32 proto_micro_ver; - - u32 reserved; -}; /* HW DATA */ - -struct mana_query_device_cfg_resp { - struct gdma_resp_hdr hdr; - - u64 pf_cap_flags1; - u64 pf_cap_flags2; - u64 pf_cap_flags3; - u64 pf_cap_flags4; - - u16 max_num_vports; - u16 reserved; - u32 max_num_eqs; -}; /* HW DATA */ - -/* Query vPort Configuration */ -struct mana_query_vport_cfg_req { - struct gdma_req_hdr hdr; - u32 vport_index; -}; /* HW DATA */ - -struct mana_query_vport_cfg_resp { - struct gdma_resp_hdr hdr; - u32 max_num_sq; - u32 max_num_rq; - u32 num_indirection_ent; - u32 reserved1; - u8 mac_addr[6]; - u8 reserved2[2]; - mana_handle_t vport; -}; /* HW DATA */ - -/* Configure vPort */ -struct mana_config_vport_req { - struct gdma_req_hdr hdr; - mana_handle_t vport; - u32 pdid; - u32 doorbell_pageid; -}; /* HW DATA */ - -struct mana_config_vport_resp { - struct gdma_resp_hdr hdr; - u16 tx_vport_offset; - u8 short_form_allowed; - u8 reserved; -}; /* HW DATA */ - -/* Create WQ Object */ -struct mana_create_wqobj_req { - struct gdma_req_hdr hdr; - mana_handle_t vport; - u32 wq_type; - u32 reserved; - u64 wq_gdma_region; - u64 cq_gdma_region; - u32 wq_size; - u32 cq_size; - u32 cq_moderation_ctx_id; - u32 cq_parent_qid; -}; /* HW DATA */ - -struct mana_create_wqobj_resp { - struct gdma_resp_hdr hdr; - u32 wq_id; - u32 cq_id; - mana_handle_t wq_obj; -}; /* HW DATA */ - -/* Destroy WQ Object */ -struct mana_destroy_wqobj_req { - struct gdma_req_hdr hdr; - u32 wq_type; - u32 reserved; - mana_handle_t wq_obj_handle; -}; /* HW DATA */ - -struct mana_destroy_wqobj_resp { - struct gdma_resp_hdr hdr; -}; /* HW DATA */ - -/* Fence RQ */ -struct mana_fence_rq_req { - struct gdma_req_hdr hdr; - mana_handle_t wq_obj_handle; -}; /* HW DATA */ - -struct mana_fence_rq_resp { - struct gdma_resp_hdr hdr; -}; /* HW DATA */ - -/* Configure vPort Rx Steering */ -struct mana_cfg_rx_steer_req { - struct gdma_req_hdr hdr; - mana_handle_t vport; - u16 num_indir_entries; - u16 indir_tab_offset; - u32 rx_enable; - u32 rss_enable; - u8 update_default_rxobj; - u8 update_hashkey; - u8 update_indir_tab; - u8 reserved; - mana_handle_t default_rxobj; - u8 hashkey[MANA_HASH_KEY_SIZE]; -}; /* HW DATA */ - -struct mana_cfg_rx_steer_resp { - struct gdma_resp_hdr hdr; -}; /* HW DATA */ - -/* Register HW vPort */ -struct mana_register_hw_vport_req { - struct gdma_req_hdr hdr; - u16 attached_gfid; - u8 is_pf_default_vport; - u8 reserved1; - u8 allow_all_ether_types; - u8 reserved2; - u8 reserved3; - u8 reserved4; -}; /* HW DATA */ - -struct mana_register_hw_vport_resp { - struct gdma_resp_hdr hdr; - mana_handle_t hw_vport_handle; -}; /* HW DATA */ - -/* Deregister HW vPort */ -struct mana_deregister_hw_vport_req { - struct gdma_req_hdr hdr; - mana_handle_t hw_vport_handle; -}; /* HW DATA */ - -struct mana_deregister_hw_vport_resp { - struct gdma_resp_hdr hdr; -}; /* HW DATA */ - -/* Register filter */ -struct mana_register_filter_req { - struct gdma_req_hdr hdr; - mana_handle_t vport; - u8 mac_addr[6]; - u8 reserved1; - u8 reserved2; - u8 reserved3; - u8 reserved4; - u16 reserved5; - u32 reserved6; - u32 reserved7; - u32 reserved8; -}; /* HW DATA */ - -struct mana_register_filter_resp { - struct gdma_resp_hdr hdr; - mana_handle_t filter_handle; -}; /* HW DATA */ - -/* Deregister filter */ -struct mana_deregister_filter_req { - struct gdma_req_hdr hdr; - mana_handle_t filter_handle; -}; /* HW DATA */ - -struct mana_deregister_filter_resp { - struct gdma_resp_hdr hdr; -}; /* HW DATA */ - -#define MANA_MAX_NUM_QUEUES 64 - -#define MANA_SHORT_VPORT_OFFSET_MAX ((1U << 8) - 1) - -struct mana_tx_package { - struct gdma_wqe_request wqe_req; - struct gdma_sge sgl_array[5]; - struct gdma_sge *sgl_ptr; - - struct mana_tx_oob tx_oob; - - struct gdma_posted_wqe_info wqe_info; -}; - -int mana_create_wq_obj(struct mana_port_context *apc, - mana_handle_t vport, - u32 wq_type, struct mana_obj_spec *wq_spec, - struct mana_obj_spec *cq_spec, - mana_handle_t *wq_obj); - -void mana_destroy_wq_obj(struct mana_port_context *apc, u32 wq_type, - mana_handle_t wq_obj); - -int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id, - u32 doorbell_pg_id); -void mana_uncfg_vport(struct mana_port_context *apc); -#endif /* _MANA_H */ diff --git a/drivers/net/ethernet/microsoft/mana/mana_auxiliary.h b/drivers/net/ethernet/microsoft/mana/mana_auxiliary.h deleted file mode 100644 index 373d59756846..000000000000 --- a/drivers/net/ethernet/microsoft/mana/mana_auxiliary.h +++ /dev/null @@ -1,10 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2022, Microsoft Corporation. */ - -#include "mana.h" -#include - -struct mana_adev { - struct auxiliary_device adev; - struct gdma_dev *mdev; -}; diff --git a/drivers/net/ethernet/microsoft/mana/mana_bpf.c b/drivers/net/ethernet/microsoft/mana/mana_bpf.c index 421fd39ff3a8..3caea631229c 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_bpf.c +++ b/drivers/net/ethernet/microsoft/mana/mana_bpf.c @@ -8,7 +8,7 @@ #include #include -#include "mana.h" +#include void mana_xdp_tx(struct sk_buff *skb, struct net_device *ndev) { diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index b6303a43fa7c..ffa2a0e2c213 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -12,8 +12,8 @@ #include #include -#include "mana.h" -#include "mana_auxiliary.h" +#include +#include static DEFINE_IDA(mana_adev_ida); diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c index c530db76880f..6f98de6d7440 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c +++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c @@ -5,7 +5,7 @@ #include #include -#include "mana.h" +#include static const struct { char name[ETH_GSTRING_LEN]; diff --git a/drivers/net/ethernet/microsoft/mana/shm_channel.c b/drivers/net/ethernet/microsoft/mana/shm_channel.c index da255da62176..5553af9c8085 100644 --- a/drivers/net/ethernet/microsoft/mana/shm_channel.c +++ b/drivers/net/ethernet/microsoft/mana/shm_channel.c @@ -6,7 +6,7 @@ #include #include -#include "shm_channel.h" +#include #define PAGE_FRAME_L48_WIDTH_BYTES 6 #define PAGE_FRAME_L48_WIDTH_BITS (PAGE_FRAME_L48_WIDTH_BYTES * 8) diff --git a/drivers/net/ethernet/microsoft/mana/shm_channel.h b/drivers/net/ethernet/microsoft/mana/shm_channel.h deleted file mode 100644 index 5199b41497ff..000000000000 --- a/drivers/net/ethernet/microsoft/mana/shm_channel.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ -/* Copyright (c) 2021, Microsoft Corporation. */ - -#ifndef _SHM_CHANNEL_H -#define _SHM_CHANNEL_H - -struct shm_channel { - struct device *dev; - void __iomem *base; -}; - -void mana_smc_init(struct shm_channel *sc, struct device *dev, - void __iomem *base); - -int mana_smc_setup_hwc(struct shm_channel *sc, bool reset_vf, u64 eq_addr, - u64 cq_addr, u64 rq_addr, u64 sq_addr, - u32 eq_msix_index); - -int mana_smc_teardown_hwc(struct shm_channel *sc, bool reset_vf); - -#endif /* _SHM_CHANNEL_H */ diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h new file mode 100644 index 000000000000..72eaec2470c0 --- /dev/null +++ b/include/net/mana/gdma.h @@ -0,0 +1,689 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright (c) 2021, Microsoft Corporation. */ + +#ifndef _GDMA_H +#define _GDMA_H + +#include +#include + +#include "shm_channel.h" + +/* Structures labeled with "HW DATA" are exchanged with the hardware. All of + * them are naturally aligned and hence don't need __packed. + */ + +enum gdma_request_type { + GDMA_VERIFY_VF_DRIVER_VERSION = 1, + GDMA_QUERY_MAX_RESOURCES = 2, + GDMA_LIST_DEVICES = 3, + GDMA_REGISTER_DEVICE = 4, + GDMA_DEREGISTER_DEVICE = 5, + GDMA_GENERATE_TEST_EQE = 10, + GDMA_CREATE_QUEUE = 12, + GDMA_DISABLE_QUEUE = 13, + GDMA_CREATE_DMA_REGION = 25, + GDMA_DMA_REGION_ADD_PAGES = 26, + GDMA_DESTROY_DMA_REGION = 27, +}; + +enum gdma_queue_type { + GDMA_INVALID_QUEUE, + GDMA_SQ, + GDMA_RQ, + GDMA_CQ, + GDMA_EQ, +}; + +enum gdma_work_request_flags { + GDMA_WR_NONE = 0, + GDMA_WR_OOB_IN_SGL = BIT(0), + GDMA_WR_PAD_BY_SGE0 = BIT(1), +}; + +enum gdma_eqe_type { + GDMA_EQE_COMPLETION = 3, + GDMA_EQE_TEST_EVENT = 64, + GDMA_EQE_HWC_INIT_EQ_ID_DB = 129, + GDMA_EQE_HWC_INIT_DATA = 130, + GDMA_EQE_HWC_INIT_DONE = 131, +}; + +enum { + GDMA_DEVICE_NONE = 0, + GDMA_DEVICE_HWC = 1, + GDMA_DEVICE_MANA = 2, +}; + +struct gdma_resource { + /* Protect the bitmap */ + spinlock_t lock; + + /* The bitmap size in bits. */ + u32 size; + + /* The bitmap tracks the resources. */ + unsigned long *map; +}; + +union gdma_doorbell_entry { + u64 as_uint64; + + struct { + u64 id : 24; + u64 reserved : 8; + u64 tail_ptr : 31; + u64 arm : 1; + } cq; + + struct { + u64 id : 24; + u64 wqe_cnt : 8; + u64 tail_ptr : 32; + } rq; + + struct { + u64 id : 24; + u64 reserved : 8; + u64 tail_ptr : 32; + } sq; + + struct { + u64 id : 16; + u64 reserved : 16; + u64 tail_ptr : 31; + u64 arm : 1; + } eq; +}; /* HW DATA */ + +struct gdma_msg_hdr { + u32 hdr_type; + u32 msg_type; + u16 msg_version; + u16 hwc_msg_id; + u32 msg_size; +}; /* HW DATA */ + +struct gdma_dev_id { + union { + struct { + u16 type; + u16 instance; + }; + + u32 as_uint32; + }; +}; /* HW DATA */ + +struct gdma_req_hdr { + struct gdma_msg_hdr req; + struct gdma_msg_hdr resp; /* The expected response */ + struct gdma_dev_id dev_id; + u32 activity_id; +}; /* HW DATA */ + +struct gdma_resp_hdr { + struct gdma_msg_hdr response; + struct gdma_dev_id dev_id; + u32 activity_id; + u32 status; + u32 reserved; +}; /* HW DATA */ + +struct gdma_general_req { + struct gdma_req_hdr hdr; +}; /* HW DATA */ + +#define GDMA_MESSAGE_V1 1 + +struct gdma_general_resp { + struct gdma_resp_hdr hdr; +}; /* HW DATA */ + +#define GDMA_STANDARD_HEADER_TYPE 0 + +static inline void mana_gd_init_req_hdr(struct gdma_req_hdr *hdr, u32 code, + u32 req_size, u32 resp_size) +{ + hdr->req.hdr_type = GDMA_STANDARD_HEADER_TYPE; + hdr->req.msg_type = code; + hdr->req.msg_version = GDMA_MESSAGE_V1; + hdr->req.msg_size = req_size; + + hdr->resp.hdr_type = GDMA_STANDARD_HEADER_TYPE; + hdr->resp.msg_type = code; + hdr->resp.msg_version = GDMA_MESSAGE_V1; + hdr->resp.msg_size = resp_size; +} + +/* The 16-byte struct is part of the GDMA work queue entry (WQE). */ +struct gdma_sge { + u64 address; + u32 mem_key; + u32 size; +}; /* HW DATA */ + +struct gdma_wqe_request { + struct gdma_sge *sgl; + u32 num_sge; + + u32 inline_oob_size; + const void *inline_oob_data; + + u32 flags; + u32 client_data_unit; +}; + +enum gdma_page_type { + GDMA_PAGE_TYPE_4K, +}; + +#define GDMA_INVALID_DMA_REGION 0 + +struct gdma_mem_info { + struct device *dev; + + dma_addr_t dma_handle; + void *virt_addr; + u64 length; + + /* Allocated by the PF driver */ + u64 gdma_region; +}; + +#define REGISTER_ATB_MST_MKEY_LOWER_SIZE 8 + +struct gdma_dev { + struct gdma_context *gdma_context; + + struct gdma_dev_id dev_id; + + u32 pdid; + u32 doorbell; + u32 gpa_mkey; + + /* GDMA driver specific pointer */ + void *driver_data; + + struct auxiliary_device *adev; +}; + +#define MINIMUM_SUPPORTED_PAGE_SIZE PAGE_SIZE + +#define GDMA_CQE_SIZE 64 +#define GDMA_EQE_SIZE 16 +#define GDMA_MAX_SQE_SIZE 512 +#define GDMA_MAX_RQE_SIZE 256 + +#define GDMA_COMP_DATA_SIZE 0x3C + +#define GDMA_EVENT_DATA_SIZE 0xC + +/* The WQE size must be a multiple of the Basic Unit, which is 32 bytes. */ +#define GDMA_WQE_BU_SIZE 32 + +#define INVALID_PDID UINT_MAX +#define INVALID_DOORBELL UINT_MAX +#define INVALID_MEM_KEY UINT_MAX +#define INVALID_QUEUE_ID UINT_MAX +#define INVALID_PCI_MSIX_INDEX UINT_MAX + +struct gdma_comp { + u32 cqe_data[GDMA_COMP_DATA_SIZE / 4]; + u32 wq_num; + bool is_sq; +}; + +struct gdma_event { + u32 details[GDMA_EVENT_DATA_SIZE / 4]; + u8 type; +}; + +struct gdma_queue; + +struct mana_eq { + struct gdma_queue *eq; +}; + +typedef void gdma_eq_callback(void *context, struct gdma_queue *q, + struct gdma_event *e); + +typedef void gdma_cq_callback(void *context, struct gdma_queue *q); + +/* The 'head' is the producer index. For SQ/RQ, when the driver posts a WQE + * (Note: the WQE size must be a multiple of the 32-byte Basic Unit), the + * driver increases the 'head' in BUs rather than in bytes, and notifies + * the HW of the updated head. For EQ/CQ, the driver uses the 'head' to track + * the HW head, and increases the 'head' by 1 for every processed EQE/CQE. + * + * The 'tail' is the consumer index for SQ/RQ. After the CQE of the SQ/RQ is + * processed, the driver increases the 'tail' to indicate that WQEs have + * been consumed by the HW, so the driver can post new WQEs into the SQ/RQ. + * + * The driver doesn't use the 'tail' for EQ/CQ, because the driver ensures + * that the EQ/CQ is big enough so they can't overflow, and the driver uses + * the owner bits mechanism to detect if the queue has become empty. + */ +struct gdma_queue { + struct gdma_dev *gdma_dev; + + enum gdma_queue_type type; + u32 id; + + struct gdma_mem_info mem_info; + + void *queue_mem_ptr; + u32 queue_size; + + bool monitor_avl_buf; + + u32 head; + u32 tail; + + /* Extra fields specific to EQ/CQ. */ + union { + struct { + bool disable_needed; + + gdma_eq_callback *callback; + void *context; + + unsigned int msix_index; + + u32 log2_throttle_limit; + } eq; + + struct { + gdma_cq_callback *callback; + void *context; + + struct gdma_queue *parent; /* For CQ/EQ relationship */ + } cq; + }; +}; + +struct gdma_queue_spec { + enum gdma_queue_type type; + bool monitor_avl_buf; + unsigned int queue_size; + + /* Extra fields specific to EQ/CQ. */ + union { + struct { + gdma_eq_callback *callback; + void *context; + + unsigned long log2_throttle_limit; + } eq; + + struct { + gdma_cq_callback *callback; + void *context; + + struct gdma_queue *parent_eq; + + } cq; + }; +}; + +struct gdma_irq_context { + void (*handler)(void *arg); + void *arg; +}; + +struct gdma_context { + struct device *dev; + + /* Per-vPort max number of queues */ + unsigned int max_num_queues; + unsigned int max_num_msix; + unsigned int num_msix_usable; + struct gdma_resource msix_resource; + struct gdma_irq_context *irq_contexts; + + /* This maps a CQ index to the queue structure. */ + unsigned int max_num_cqs; + struct gdma_queue **cq_table; + + /* Protect eq_test_event and test_event_eq_id */ + struct mutex eq_test_event_mutex; + struct completion eq_test_event; + u32 test_event_eq_id; + + bool is_pf; + phys_addr_t bar0_pa; + void __iomem *bar0_va; + void __iomem *shm_base; + void __iomem *db_page_base; + phys_addr_t phys_db_page_base; + u32 db_page_size; + + /* Shared memory chanenl (used to bootstrap HWC) */ + struct shm_channel shm_channel; + + /* Hardware communication channel (HWC) */ + struct gdma_dev hwc; + + /* Azure network adapter */ + struct gdma_dev mana; +}; + +#define MAX_NUM_GDMA_DEVICES 4 + +static inline bool mana_gd_is_mana(struct gdma_dev *gd) +{ + return gd->dev_id.type == GDMA_DEVICE_MANA; +} + +static inline bool mana_gd_is_hwc(struct gdma_dev *gd) +{ + return gd->dev_id.type == GDMA_DEVICE_HWC; +} + +u8 *mana_gd_get_wqe_ptr(const struct gdma_queue *wq, u32 wqe_offset); +u32 mana_gd_wq_avail_space(struct gdma_queue *wq); + +int mana_gd_test_eq(struct gdma_context *gc, struct gdma_queue *eq); + +int mana_gd_create_hwc_queue(struct gdma_dev *gd, + const struct gdma_queue_spec *spec, + struct gdma_queue **queue_ptr); + +int mana_gd_create_mana_eq(struct gdma_dev *gd, + const struct gdma_queue_spec *spec, + struct gdma_queue **queue_ptr); + +int mana_gd_create_mana_wq_cq(struct gdma_dev *gd, + const struct gdma_queue_spec *spec, + struct gdma_queue **queue_ptr); + +void mana_gd_destroy_queue(struct gdma_context *gc, struct gdma_queue *queue); + +int mana_gd_poll_cq(struct gdma_queue *cq, struct gdma_comp *comp, int num_cqe); + +void mana_gd_ring_cq(struct gdma_queue *cq, u8 arm_bit); + +struct gdma_wqe { + u32 reserved :24; + u32 last_vbytes :8; + + union { + u32 flags; + + struct { + u32 num_sge :8; + u32 inline_oob_size_div4:3; + u32 client_oob_in_sgl :1; + u32 reserved1 :4; + u32 client_data_unit :14; + u32 reserved2 :2; + }; + }; +}; /* HW DATA */ + +#define INLINE_OOB_SMALL_SIZE 8 +#define INLINE_OOB_LARGE_SIZE 24 + +#define MAX_TX_WQE_SIZE 512 +#define MAX_RX_WQE_SIZE 256 + +struct gdma_cqe { + u32 cqe_data[GDMA_COMP_DATA_SIZE / 4]; + + union { + u32 as_uint32; + + struct { + u32 wq_num : 24; + u32 is_sq : 1; + u32 reserved : 4; + u32 owner_bits : 3; + }; + } cqe_info; +}; /* HW DATA */ + +#define GDMA_CQE_OWNER_BITS 3 + +#define GDMA_CQE_OWNER_MASK ((1 << GDMA_CQE_OWNER_BITS) - 1) + +#define SET_ARM_BIT 1 + +#define GDMA_EQE_OWNER_BITS 3 + +union gdma_eqe_info { + u32 as_uint32; + + struct { + u32 type : 8; + u32 reserved1 : 8; + u32 client_id : 2; + u32 reserved2 : 11; + u32 owner_bits : 3; + }; +}; /* HW DATA */ + +#define GDMA_EQE_OWNER_MASK ((1 << GDMA_EQE_OWNER_BITS) - 1) +#define INITIALIZED_OWNER_BIT(log2_num_entries) (1UL << (log2_num_entries)) + +struct gdma_eqe { + u32 details[GDMA_EVENT_DATA_SIZE / 4]; + u32 eqe_info; +}; /* HW DATA */ + +#define GDMA_REG_DB_PAGE_OFFSET 8 +#define GDMA_REG_DB_PAGE_SIZE 0x10 +#define GDMA_REG_SHM_OFFSET 0x18 + +#define GDMA_PF_REG_DB_PAGE_SIZE 0xD0 +#define GDMA_PF_REG_DB_PAGE_OFF 0xC8 +#define GDMA_PF_REG_SHM_OFF 0x70 + +#define GDMA_SRIOV_REG_CFG_BASE_OFF 0x108 + +#define MANA_PF_DEVICE_ID 0x00B9 +#define MANA_VF_DEVICE_ID 0x00BA + +struct gdma_posted_wqe_info { + u32 wqe_size_in_bu; +}; + +/* GDMA_GENERATE_TEST_EQE */ +struct gdma_generate_test_event_req { + struct gdma_req_hdr hdr; + u32 queue_index; +}; /* HW DATA */ + +/* GDMA_VERIFY_VF_DRIVER_VERSION */ +enum { + GDMA_PROTOCOL_V1 = 1, + GDMA_PROTOCOL_FIRST = GDMA_PROTOCOL_V1, + GDMA_PROTOCOL_LAST = GDMA_PROTOCOL_V1, +}; + +#define GDMA_DRV_CAP_FLAG_1_EQ_SHARING_MULTI_VPORT BIT(0) + +#define GDMA_DRV_CAP_FLAGS1 GDMA_DRV_CAP_FLAG_1_EQ_SHARING_MULTI_VPORT + +#define GDMA_DRV_CAP_FLAGS2 0 + +#define GDMA_DRV_CAP_FLAGS3 0 + +#define GDMA_DRV_CAP_FLAGS4 0 + +struct gdma_verify_ver_req { + struct gdma_req_hdr hdr; + + /* Mandatory fields required for protocol establishment */ + u64 protocol_ver_min; + u64 protocol_ver_max; + + /* Gdma Driver Capability Flags */ + u64 gd_drv_cap_flags1; + u64 gd_drv_cap_flags2; + u64 gd_drv_cap_flags3; + u64 gd_drv_cap_flags4; + + /* Advisory fields */ + u64 drv_ver; + u32 os_type; /* Linux = 0x10; Windows = 0x20; Other = 0x30 */ + u32 reserved; + u32 os_ver_major; + u32 os_ver_minor; + u32 os_ver_build; + u32 os_ver_platform; + u64 reserved_2; + u8 os_ver_str1[128]; + u8 os_ver_str2[128]; + u8 os_ver_str3[128]; + u8 os_ver_str4[128]; +}; /* HW DATA */ + +struct gdma_verify_ver_resp { + struct gdma_resp_hdr hdr; + u64 gdma_protocol_ver; + u64 pf_cap_flags1; + u64 pf_cap_flags2; + u64 pf_cap_flags3; + u64 pf_cap_flags4; +}; /* HW DATA */ + +/* GDMA_QUERY_MAX_RESOURCES */ +struct gdma_query_max_resources_resp { + struct gdma_resp_hdr hdr; + u32 status; + u32 max_sq; + u32 max_rq; + u32 max_cq; + u32 max_eq; + u32 max_db; + u32 max_mst; + u32 max_cq_mod_ctx; + u32 max_mod_cq; + u32 max_msix; +}; /* HW DATA */ + +/* GDMA_LIST_DEVICES */ +struct gdma_list_devices_resp { + struct gdma_resp_hdr hdr; + u32 num_of_devs; + u32 reserved; + struct gdma_dev_id devs[64]; +}; /* HW DATA */ + +/* GDMA_REGISTER_DEVICE */ +struct gdma_register_device_resp { + struct gdma_resp_hdr hdr; + u32 pdid; + u32 gpa_mkey; + u32 db_id; +}; /* HW DATA */ + +/* GDMA_CREATE_QUEUE */ +struct gdma_create_queue_req { + struct gdma_req_hdr hdr; + u32 type; + u32 reserved1; + u32 pdid; + u32 doolbell_id; + u64 gdma_region; + u32 reserved2; + u32 queue_size; + u32 log2_throttle_limit; + u32 eq_pci_msix_index; + u32 cq_mod_ctx_id; + u32 cq_parent_eq_id; + u8 rq_drop_on_overrun; + u8 rq_err_on_wqe_overflow; + u8 rq_chain_rec_wqes; + u8 sq_hw_db; + u32 reserved3; +}; /* HW DATA */ + +struct gdma_create_queue_resp { + struct gdma_resp_hdr hdr; + u32 queue_index; +}; /* HW DATA */ + +/* GDMA_DISABLE_QUEUE */ +struct gdma_disable_queue_req { + struct gdma_req_hdr hdr; + u32 type; + u32 queue_index; + u32 alloc_res_id_on_creation; +}; /* HW DATA */ + +/* GDMA_CREATE_DMA_REGION */ +struct gdma_create_dma_region_req { + struct gdma_req_hdr hdr; + + /* The total size of the DMA region */ + u64 length; + + /* The offset in the first page */ + u32 offset_in_page; + + /* enum gdma_page_type */ + u32 gdma_page_type; + + /* The total number of pages */ + u32 page_count; + + /* If page_addr_list_len is smaller than page_count, + * the remaining page addresses will be added via the + * message GDMA_DMA_REGION_ADD_PAGES. + */ + u32 page_addr_list_len; + u64 page_addr_list[]; +}; /* HW DATA */ + +struct gdma_create_dma_region_resp { + struct gdma_resp_hdr hdr; + u64 gdma_region; +}; /* HW DATA */ + +/* GDMA_DMA_REGION_ADD_PAGES */ +struct gdma_dma_region_add_pages_req { + struct gdma_req_hdr hdr; + + u64 gdma_region; + + u32 page_addr_list_len; + u32 reserved3; + + u64 page_addr_list[]; +}; /* HW DATA */ + +/* GDMA_DESTROY_DMA_REGION */ +struct gdma_destroy_dma_region_req { + struct gdma_req_hdr hdr; + + u64 gdma_region; +}; /* HW DATA */ + +int mana_gd_verify_vf_version(struct pci_dev *pdev); + +int mana_gd_register_device(struct gdma_dev *gd); +int mana_gd_deregister_device(struct gdma_dev *gd); + +int mana_gd_post_work_request(struct gdma_queue *wq, + const struct gdma_wqe_request *wqe_req, + struct gdma_posted_wqe_info *wqe_info); + +int mana_gd_post_and_ring(struct gdma_queue *queue, + const struct gdma_wqe_request *wqe, + struct gdma_posted_wqe_info *wqe_info); + +int mana_gd_alloc_res_map(u32 res_avail, struct gdma_resource *r); +void mana_gd_free_res_map(struct gdma_resource *r); + +void mana_gd_wq_ring_doorbell(struct gdma_context *gc, + struct gdma_queue *queue); + +int mana_gd_alloc_memory(struct gdma_context *gc, unsigned int length, + struct gdma_mem_info *gmi); + +void mana_gd_free_memory(struct gdma_mem_info *gmi); + +int mana_gd_send_request(struct gdma_context *gc, u32 req_len, const void *req, + u32 resp_len, void *resp); +#endif /* _GDMA_H */ diff --git a/include/net/mana/hw_channel.h b/include/net/mana/hw_channel.h new file mode 100644 index 000000000000..6a757a6e2732 --- /dev/null +++ b/include/net/mana/hw_channel.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright (c) 2021, Microsoft Corporation. */ + +#ifndef _HW_CHANNEL_H +#define _HW_CHANNEL_H + +#define DEFAULT_LOG2_THROTTLING_FOR_ERROR_EQ 4 + +#define HW_CHANNEL_MAX_REQUEST_SIZE 0x1000 +#define HW_CHANNEL_MAX_RESPONSE_SIZE 0x1000 + +#define HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH 1 + +#define HWC_INIT_DATA_CQID 1 +#define HWC_INIT_DATA_RQID 2 +#define HWC_INIT_DATA_SQID 3 +#define HWC_INIT_DATA_QUEUE_DEPTH 4 +#define HWC_INIT_DATA_MAX_REQUEST 5 +#define HWC_INIT_DATA_MAX_RESPONSE 6 +#define HWC_INIT_DATA_MAX_NUM_CQS 7 +#define HWC_INIT_DATA_PDID 8 +#define HWC_INIT_DATA_GPA_MKEY 9 +#define HWC_INIT_DATA_PF_DEST_RQ_ID 10 +#define HWC_INIT_DATA_PF_DEST_CQ_ID 11 + +/* Structures labeled with "HW DATA" are exchanged with the hardware. All of + * them are naturally aligned and hence don't need __packed. + */ + +union hwc_init_eq_id_db { + u32 as_uint32; + + struct { + u32 eq_id : 16; + u32 doorbell : 16; + }; +}; /* HW DATA */ + +union hwc_init_type_data { + u32 as_uint32; + + struct { + u32 value : 24; + u32 type : 8; + }; +}; /* HW DATA */ + +struct hwc_rx_oob { + u32 type : 6; + u32 eom : 1; + u32 som : 1; + u32 vendor_err : 8; + u32 reserved1 : 16; + + u32 src_virt_wq : 24; + u32 src_vfid : 8; + + u32 reserved2; + + union { + u32 wqe_addr_low; + u32 wqe_offset; + }; + + u32 wqe_addr_high; + + u32 client_data_unit : 14; + u32 reserved3 : 18; + + u32 tx_oob_data_size; + + u32 chunk_offset : 21; + u32 reserved4 : 11; +}; /* HW DATA */ + +struct hwc_tx_oob { + u32 reserved1; + + u32 reserved2; + + u32 vrq_id : 24; + u32 dest_vfid : 8; + + u32 vrcq_id : 24; + u32 reserved3 : 8; + + u32 vscq_id : 24; + u32 loopback : 1; + u32 lso_override: 1; + u32 dest_pf : 1; + u32 reserved4 : 5; + + u32 vsq_id : 24; + u32 reserved5 : 8; +}; /* HW DATA */ + +struct hwc_work_request { + void *buf_va; + void *buf_sge_addr; + u32 buf_len; + u32 msg_size; + + struct gdma_wqe_request wqe_req; + struct hwc_tx_oob tx_oob; + + struct gdma_sge sge; +}; + +/* hwc_dma_buf represents the array of in-flight WQEs. + * mem_info as know as the GDMA mapped memory is partitioned and used by + * in-flight WQEs. + * The number of WQEs is determined by the number of in-flight messages. + */ +struct hwc_dma_buf { + struct gdma_mem_info mem_info; + + u32 gpa_mkey; + + u32 num_reqs; + struct hwc_work_request reqs[]; +}; + +typedef void hwc_rx_event_handler_t(void *ctx, u32 gdma_rxq_id, + const struct hwc_rx_oob *rx_oob); + +typedef void hwc_tx_event_handler_t(void *ctx, u32 gdma_txq_id, + const struct hwc_rx_oob *rx_oob); + +struct hwc_cq { + struct hw_channel_context *hwc; + + struct gdma_queue *gdma_cq; + struct gdma_queue *gdma_eq; + struct gdma_comp *comp_buf; + u16 queue_depth; + + hwc_rx_event_handler_t *rx_event_handler; + void *rx_event_ctx; + + hwc_tx_event_handler_t *tx_event_handler; + void *tx_event_ctx; +}; + +struct hwc_wq { + struct hw_channel_context *hwc; + + struct gdma_queue *gdma_wq; + struct hwc_dma_buf *msg_buf; + u16 queue_depth; + + struct hwc_cq *hwc_cq; +}; + +struct hwc_caller_ctx { + struct completion comp_event; + void *output_buf; + u32 output_buflen; + + u32 error; /* Linux error code */ + u32 status_code; +}; + +struct hw_channel_context { + struct gdma_dev *gdma_dev; + struct device *dev; + + u16 num_inflight_msg; + u32 max_req_msg_size; + + u16 hwc_init_q_depth_max; + u32 hwc_init_max_req_msg_size; + u32 hwc_init_max_resp_msg_size; + + struct completion hwc_init_eqe_comp; + + struct hwc_wq *rxq; + struct hwc_wq *txq; + struct hwc_cq *cq; + + struct semaphore sema; + struct gdma_resource inflight_msg_res; + + u32 pf_dest_vrq_id; + u32 pf_dest_vrcq_id; + + struct hwc_caller_ctx *caller_ctx; +}; + +int mana_hwc_create_channel(struct gdma_context *gc); +void mana_hwc_destroy_channel(struct gdma_context *gc); + +int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len, + const void *req, u32 resp_len, void *resp); + +#endif /* _HW_CHANNEL_H */ diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h new file mode 100644 index 000000000000..6e9e86fb4c02 --- /dev/null +++ b/include/net/mana/mana.h @@ -0,0 +1,650 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright (c) 2021, Microsoft Corporation. */ + +#ifndef _MANA_H +#define _MANA_H + +#include "gdma.h" +#include "hw_channel.h" + +/* Microsoft Azure Network Adapter (MANA)'s definitions + * + * Structures labeled with "HW DATA" are exchanged with the hardware. All of + * them are naturally aligned and hence don't need __packed. + */ + +/* MANA protocol version */ +#define MANA_MAJOR_VERSION 0 +#define MANA_MINOR_VERSION 1 +#define MANA_MICRO_VERSION 1 + +typedef u64 mana_handle_t; +#define INVALID_MANA_HANDLE ((mana_handle_t)-1) + +enum TRI_STATE { + TRI_STATE_UNKNOWN = -1, + TRI_STATE_FALSE = 0, + TRI_STATE_TRUE = 1 +}; + +/* Number of entries for hardware indirection table must be in power of 2 */ +#define MANA_INDIRECT_TABLE_SIZE 64 +#define MANA_INDIRECT_TABLE_MASK (MANA_INDIRECT_TABLE_SIZE - 1) + +/* The Toeplitz hash key's length in bytes: should be multiple of 8 */ +#define MANA_HASH_KEY_SIZE 40 + +#define COMP_ENTRY_SIZE 64 + +#define ADAPTER_MTU_SIZE 1500 +#define MAX_FRAME_SIZE (ADAPTER_MTU_SIZE + 14) + +#define RX_BUFFERS_PER_QUEUE 512 + +#define MAX_SEND_BUFFERS_PER_QUEUE 256 + +#define EQ_SIZE (8 * PAGE_SIZE) +#define LOG2_EQ_THROTTLE 3 + +#define MAX_PORTS_IN_MANA_DEV 256 + +struct mana_stats_rx { + u64 packets; + u64 bytes; + u64 xdp_drop; + u64 xdp_tx; + u64 xdp_redirect; + struct u64_stats_sync syncp; +}; + +struct mana_stats_tx { + u64 packets; + u64 bytes; + u64 xdp_xmit; + struct u64_stats_sync syncp; +}; + +struct mana_txq { + struct gdma_queue *gdma_sq; + + union { + u32 gdma_txq_id; + struct { + u32 reserved1 : 10; + u32 vsq_frame : 14; + u32 reserved2 : 8; + }; + }; + + u16 vp_offset; + + struct net_device *ndev; + + /* The SKBs are sent to the HW and we are waiting for the CQEs. */ + struct sk_buff_head pending_skbs; + struct netdev_queue *net_txq; + + atomic_t pending_sends; + + struct mana_stats_tx stats; +}; + +/* skb data and frags dma mappings */ +struct mana_skb_head { + dma_addr_t dma_handle[MAX_SKB_FRAGS + 1]; + + u32 size[MAX_SKB_FRAGS + 1]; +}; + +#define MANA_HEADROOM sizeof(struct mana_skb_head) + +enum mana_tx_pkt_format { + MANA_SHORT_PKT_FMT = 0, + MANA_LONG_PKT_FMT = 1, +}; + +struct mana_tx_short_oob { + u32 pkt_fmt : 2; + u32 is_outer_ipv4 : 1; + u32 is_outer_ipv6 : 1; + u32 comp_iphdr_csum : 1; + u32 comp_tcp_csum : 1; + u32 comp_udp_csum : 1; + u32 supress_txcqe_gen : 1; + u32 vcq_num : 24; + + u32 trans_off : 10; /* Transport header offset */ + u32 vsq_frame : 14; + u32 short_vp_offset : 8; +}; /* HW DATA */ + +struct mana_tx_long_oob { + u32 is_encap : 1; + u32 inner_is_ipv6 : 1; + u32 inner_tcp_opt : 1; + u32 inject_vlan_pri_tag : 1; + u32 reserved1 : 12; + u32 pcp : 3; /* 802.1Q */ + u32 dei : 1; /* 802.1Q */ + u32 vlan_id : 12; /* 802.1Q */ + + u32 inner_frame_offset : 10; + u32 inner_ip_rel_offset : 6; + u32 long_vp_offset : 12; + u32 reserved2 : 4; + + u32 reserved3; + u32 reserved4; +}; /* HW DATA */ + +struct mana_tx_oob { + struct mana_tx_short_oob s_oob; + struct mana_tx_long_oob l_oob; +}; /* HW DATA */ + +enum mana_cq_type { + MANA_CQ_TYPE_RX, + MANA_CQ_TYPE_TX, +}; + +enum mana_cqe_type { + CQE_INVALID = 0, + CQE_RX_OKAY = 1, + CQE_RX_COALESCED_4 = 2, + CQE_RX_OBJECT_FENCE = 3, + CQE_RX_TRUNCATED = 4, + + CQE_TX_OKAY = 32, + CQE_TX_SA_DROP = 33, + CQE_TX_MTU_DROP = 34, + CQE_TX_INVALID_OOB = 35, + CQE_TX_INVALID_ETH_TYPE = 36, + CQE_TX_HDR_PROCESSING_ERROR = 37, + CQE_TX_VF_DISABLED = 38, + CQE_TX_VPORT_IDX_OUT_OF_RANGE = 39, + CQE_TX_VPORT_DISABLED = 40, + CQE_TX_VLAN_TAGGING_VIOLATION = 41, +}; + +#define MANA_CQE_COMPLETION 1 + +struct mana_cqe_header { + u32 cqe_type : 6; + u32 client_type : 2; + u32 vendor_err : 24; +}; /* HW DATA */ + +/* NDIS HASH Types */ +#define NDIS_HASH_IPV4 BIT(0) +#define NDIS_HASH_TCP_IPV4 BIT(1) +#define NDIS_HASH_UDP_IPV4 BIT(2) +#define NDIS_HASH_IPV6 BIT(3) +#define NDIS_HASH_TCP_IPV6 BIT(4) +#define NDIS_HASH_UDP_IPV6 BIT(5) +#define NDIS_HASH_IPV6_EX BIT(6) +#define NDIS_HASH_TCP_IPV6_EX BIT(7) +#define NDIS_HASH_UDP_IPV6_EX BIT(8) + +#define MANA_HASH_L3 (NDIS_HASH_IPV4 | NDIS_HASH_IPV6 | NDIS_HASH_IPV6_EX) +#define MANA_HASH_L4 \ + (NDIS_HASH_TCP_IPV4 | NDIS_HASH_UDP_IPV4 | NDIS_HASH_TCP_IPV6 | \ + NDIS_HASH_UDP_IPV6 | NDIS_HASH_TCP_IPV6_EX | NDIS_HASH_UDP_IPV6_EX) + +struct mana_rxcomp_perpkt_info { + u32 pkt_len : 16; + u32 reserved1 : 16; + u32 reserved2; + u32 pkt_hash; +}; /* HW DATA */ + +#define MANA_RXCOMP_OOB_NUM_PPI 4 + +/* Receive completion OOB */ +struct mana_rxcomp_oob { + struct mana_cqe_header cqe_hdr; + + u32 rx_vlan_id : 12; + u32 rx_vlantag_present : 1; + u32 rx_outer_iphdr_csum_succeed : 1; + u32 rx_outer_iphdr_csum_fail : 1; + u32 reserved1 : 1; + u32 rx_hashtype : 9; + u32 rx_iphdr_csum_succeed : 1; + u32 rx_iphdr_csum_fail : 1; + u32 rx_tcp_csum_succeed : 1; + u32 rx_tcp_csum_fail : 1; + u32 rx_udp_csum_succeed : 1; + u32 rx_udp_csum_fail : 1; + u32 reserved2 : 1; + + struct mana_rxcomp_perpkt_info ppi[MANA_RXCOMP_OOB_NUM_PPI]; + + u32 rx_wqe_offset; +}; /* HW DATA */ + +struct mana_tx_comp_oob { + struct mana_cqe_header cqe_hdr; + + u32 tx_data_offset; + + u32 tx_sgl_offset : 5; + u32 tx_wqe_offset : 27; + + u32 reserved[12]; +}; /* HW DATA */ + +struct mana_rxq; + +#define CQE_POLLING_BUFFER 512 + +struct mana_cq { + struct gdma_queue *gdma_cq; + + /* Cache the CQ id (used to verify if each CQE comes to the right CQ. */ + u32 gdma_id; + + /* Type of the CQ: TX or RX */ + enum mana_cq_type type; + + /* Pointer to the mana_rxq that is pushing RX CQEs to the queue. + * Only and must be non-NULL if type is MANA_CQ_TYPE_RX. + */ + struct mana_rxq *rxq; + + /* Pointer to the mana_txq that is pushing TX CQEs to the queue. + * Only and must be non-NULL if type is MANA_CQ_TYPE_TX. + */ + struct mana_txq *txq; + + /* Buffer which the CQ handler can copy the CQE's into. */ + struct gdma_comp gdma_comp_buf[CQE_POLLING_BUFFER]; + + /* NAPI data */ + struct napi_struct napi; + int work_done; + int budget; +}; + +#define GDMA_MAX_RQE_SGES 15 + +struct mana_recv_buf_oob { + /* A valid GDMA work request representing the data buffer. */ + struct gdma_wqe_request wqe_req; + + void *buf_va; + dma_addr_t buf_dma_addr; + + /* SGL of the buffer going to be sent has part of the work request. */ + u32 num_sge; + struct gdma_sge sgl[GDMA_MAX_RQE_SGES]; + + /* Required to store the result of mana_gd_post_work_request. + * gdma_posted_wqe_info.wqe_size_in_bu is required for progressing the + * work queue when the WQE is consumed. + */ + struct gdma_posted_wqe_info wqe_inf; +}; + +struct mana_rxq { + struct gdma_queue *gdma_rq; + /* Cache the gdma receive queue id */ + u32 gdma_id; + + /* Index of RQ in the vPort, not gdma receive queue id */ + u32 rxq_idx; + + u32 datasize; + + mana_handle_t rxobj; + + struct mana_cq rx_cq; + + struct completion fence_event; + + struct net_device *ndev; + + /* Total number of receive buffers to be allocated */ + u32 num_rx_buf; + + u32 buf_index; + + struct mana_stats_rx stats; + + struct bpf_prog __rcu *bpf_prog; + struct xdp_rxq_info xdp_rxq; + struct page *xdp_save_page; + bool xdp_flush; + int xdp_rc; /* XDP redirect return code */ + + /* MUST BE THE LAST MEMBER: + * Each receive buffer has an associated mana_recv_buf_oob. + */ + struct mana_recv_buf_oob rx_oobs[]; +}; + +struct mana_tx_qp { + struct mana_txq txq; + + struct mana_cq tx_cq; + + mana_handle_t tx_object; +}; + +struct mana_ethtool_stats { + u64 stop_queue; + u64 wake_queue; +}; + +struct mana_context { + struct gdma_dev *gdma_dev; + + u16 num_ports; + + struct mana_eq *eqs; + + struct net_device *ports[MAX_PORTS_IN_MANA_DEV]; +}; + +struct mana_port_context { + struct mana_context *ac; + struct net_device *ndev; + + u8 mac_addr[ETH_ALEN]; + + enum TRI_STATE rss_state; + + mana_handle_t default_rxobj; + bool tx_shortform_allowed; + u16 tx_vp_offset; + + struct mana_tx_qp *tx_qp; + + /* Indirection Table for RX & TX. The values are queue indexes */ + u32 indir_table[MANA_INDIRECT_TABLE_SIZE]; + + /* Indirection table containing RxObject Handles */ + mana_handle_t rxobj_table[MANA_INDIRECT_TABLE_SIZE]; + + /* Hash key used by the NIC */ + u8 hashkey[MANA_HASH_KEY_SIZE]; + + /* This points to an array of num_queues of RQ pointers. */ + struct mana_rxq **rxqs; + + struct bpf_prog *bpf_prog; + + /* Create num_queues EQs, SQs, SQ-CQs, RQs and RQ-CQs, respectively. */ + unsigned int max_queues; + unsigned int num_queues; + + mana_handle_t port_handle; + mana_handle_t pf_filter_handle; + + /* Mutex for sharing access to vport_use_count */ + struct mutex vport_mutex; + int vport_use_count; + + u16 port_idx; + + bool port_is_up; + bool port_st_save; /* Saved port state */ + + struct mana_ethtool_stats eth_stats; +}; + +int mana_start_xmit(struct sk_buff *skb, struct net_device *ndev); +int mana_config_rss(struct mana_port_context *ac, enum TRI_STATE rx, + bool update_hash, bool update_tab); + +int mana_alloc_queues(struct net_device *ndev); +int mana_attach(struct net_device *ndev); +int mana_detach(struct net_device *ndev, bool from_close); + +int mana_probe(struct gdma_dev *gd, bool resuming); +void mana_remove(struct gdma_dev *gd, bool suspending); + +void mana_xdp_tx(struct sk_buff *skb, struct net_device *ndev); +int mana_xdp_xmit(struct net_device *ndev, int n, struct xdp_frame **frames, + u32 flags); +u32 mana_run_xdp(struct net_device *ndev, struct mana_rxq *rxq, + struct xdp_buff *xdp, void *buf_va, uint pkt_len); +struct bpf_prog *mana_xdp_get(struct mana_port_context *apc); +void mana_chn_setxdp(struct mana_port_context *apc, struct bpf_prog *prog); +int mana_bpf(struct net_device *ndev, struct netdev_bpf *bpf); + +extern const struct ethtool_ops mana_ethtool_ops; + +struct mana_obj_spec { + u32 queue_index; + u64 gdma_region; + u32 queue_size; + u32 attached_eq; + u32 modr_ctx_id; +}; + +enum mana_command_code { + MANA_QUERY_DEV_CONFIG = 0x20001, + MANA_QUERY_GF_STAT = 0x20002, + MANA_CONFIG_VPORT_TX = 0x20003, + MANA_CREATE_WQ_OBJ = 0x20004, + MANA_DESTROY_WQ_OBJ = 0x20005, + MANA_FENCE_RQ = 0x20006, + MANA_CONFIG_VPORT_RX = 0x20007, + MANA_QUERY_VPORT_CONFIG = 0x20008, + + /* Privileged commands for the PF mode */ + MANA_REGISTER_FILTER = 0x28000, + MANA_DEREGISTER_FILTER = 0x28001, + MANA_REGISTER_HW_PORT = 0x28003, + MANA_DEREGISTER_HW_PORT = 0x28004, +}; + +/* Query Device Configuration */ +struct mana_query_device_cfg_req { + struct gdma_req_hdr hdr; + + /* MANA Nic Driver Capability flags */ + u64 mn_drv_cap_flags1; + u64 mn_drv_cap_flags2; + u64 mn_drv_cap_flags3; + u64 mn_drv_cap_flags4; + + u32 proto_major_ver; + u32 proto_minor_ver; + u32 proto_micro_ver; + + u32 reserved; +}; /* HW DATA */ + +struct mana_query_device_cfg_resp { + struct gdma_resp_hdr hdr; + + u64 pf_cap_flags1; + u64 pf_cap_flags2; + u64 pf_cap_flags3; + u64 pf_cap_flags4; + + u16 max_num_vports; + u16 reserved; + u32 max_num_eqs; +}; /* HW DATA */ + +/* Query vPort Configuration */ +struct mana_query_vport_cfg_req { + struct gdma_req_hdr hdr; + u32 vport_index; +}; /* HW DATA */ + +struct mana_query_vport_cfg_resp { + struct gdma_resp_hdr hdr; + u32 max_num_sq; + u32 max_num_rq; + u32 num_indirection_ent; + u32 reserved1; + u8 mac_addr[6]; + u8 reserved2[2]; + mana_handle_t vport; +}; /* HW DATA */ + +/* Configure vPort */ +struct mana_config_vport_req { + struct gdma_req_hdr hdr; + mana_handle_t vport; + u32 pdid; + u32 doorbell_pageid; +}; /* HW DATA */ + +struct mana_config_vport_resp { + struct gdma_resp_hdr hdr; + u16 tx_vport_offset; + u8 short_form_allowed; + u8 reserved; +}; /* HW DATA */ + +/* Create WQ Object */ +struct mana_create_wqobj_req { + struct gdma_req_hdr hdr; + mana_handle_t vport; + u32 wq_type; + u32 reserved; + u64 wq_gdma_region; + u64 cq_gdma_region; + u32 wq_size; + u32 cq_size; + u32 cq_moderation_ctx_id; + u32 cq_parent_qid; +}; /* HW DATA */ + +struct mana_create_wqobj_resp { + struct gdma_resp_hdr hdr; + u32 wq_id; + u32 cq_id; + mana_handle_t wq_obj; +}; /* HW DATA */ + +/* Destroy WQ Object */ +struct mana_destroy_wqobj_req { + struct gdma_req_hdr hdr; + u32 wq_type; + u32 reserved; + mana_handle_t wq_obj_handle; +}; /* HW DATA */ + +struct mana_destroy_wqobj_resp { + struct gdma_resp_hdr hdr; +}; /* HW DATA */ + +/* Fence RQ */ +struct mana_fence_rq_req { + struct gdma_req_hdr hdr; + mana_handle_t wq_obj_handle; +}; /* HW DATA */ + +struct mana_fence_rq_resp { + struct gdma_resp_hdr hdr; +}; /* HW DATA */ + +/* Configure vPort Rx Steering */ +struct mana_cfg_rx_steer_req { + struct gdma_req_hdr hdr; + mana_handle_t vport; + u16 num_indir_entries; + u16 indir_tab_offset; + u32 rx_enable; + u32 rss_enable; + u8 update_default_rxobj; + u8 update_hashkey; + u8 update_indir_tab; + u8 reserved; + mana_handle_t default_rxobj; + u8 hashkey[MANA_HASH_KEY_SIZE]; +}; /* HW DATA */ + +struct mana_cfg_rx_steer_resp { + struct gdma_resp_hdr hdr; +}; /* HW DATA */ + +/* Register HW vPort */ +struct mana_register_hw_vport_req { + struct gdma_req_hdr hdr; + u16 attached_gfid; + u8 is_pf_default_vport; + u8 reserved1; + u8 allow_all_ether_types; + u8 reserved2; + u8 reserved3; + u8 reserved4; +}; /* HW DATA */ + +struct mana_register_hw_vport_resp { + struct gdma_resp_hdr hdr; + mana_handle_t hw_vport_handle; +}; /* HW DATA */ + +/* Deregister HW vPort */ +struct mana_deregister_hw_vport_req { + struct gdma_req_hdr hdr; + mana_handle_t hw_vport_handle; +}; /* HW DATA */ + +struct mana_deregister_hw_vport_resp { + struct gdma_resp_hdr hdr; +}; /* HW DATA */ + +/* Register filter */ +struct mana_register_filter_req { + struct gdma_req_hdr hdr; + mana_handle_t vport; + u8 mac_addr[6]; + u8 reserved1; + u8 reserved2; + u8 reserved3; + u8 reserved4; + u16 reserved5; + u32 reserved6; + u32 reserved7; + u32 reserved8; +}; /* HW DATA */ + +struct mana_register_filter_resp { + struct gdma_resp_hdr hdr; + mana_handle_t filter_handle; +}; /* HW DATA */ + +/* Deregister filter */ +struct mana_deregister_filter_req { + struct gdma_req_hdr hdr; + mana_handle_t filter_handle; +}; /* HW DATA */ + +struct mana_deregister_filter_resp { + struct gdma_resp_hdr hdr; +}; /* HW DATA */ + +#define MANA_MAX_NUM_QUEUES 64 + +#define MANA_SHORT_VPORT_OFFSET_MAX ((1U << 8) - 1) + +struct mana_tx_package { + struct gdma_wqe_request wqe_req; + struct gdma_sge sgl_array[5]; + struct gdma_sge *sgl_ptr; + + struct mana_tx_oob tx_oob; + + struct gdma_posted_wqe_info wqe_info; +}; + +int mana_create_wq_obj(struct mana_port_context *apc, + mana_handle_t vport, + u32 wq_type, struct mana_obj_spec *wq_spec, + struct mana_obj_spec *cq_spec, + mana_handle_t *wq_obj); + +void mana_destroy_wq_obj(struct mana_port_context *apc, u32 wq_type, + mana_handle_t wq_obj); + +int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id, + u32 doorbell_pg_id); +void mana_uncfg_vport(struct mana_port_context *apc); +#endif /* _MANA_H */ diff --git a/include/net/mana/mana_auxiliary.h b/include/net/mana/mana_auxiliary.h new file mode 100644 index 000000000000..373d59756846 --- /dev/null +++ b/include/net/mana/mana_auxiliary.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2022, Microsoft Corporation. */ + +#include "mana.h" +#include + +struct mana_adev { + struct auxiliary_device adev; + struct gdma_dev *mdev; +}; diff --git a/include/net/mana/shm_channel.h b/include/net/mana/shm_channel.h new file mode 100644 index 000000000000..5199b41497ff --- /dev/null +++ b/include/net/mana/shm_channel.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright (c) 2021, Microsoft Corporation. */ + +#ifndef _SHM_CHANNEL_H +#define _SHM_CHANNEL_H + +struct shm_channel { + struct device *dev; + void __iomem *base; +}; + +void mana_smc_init(struct shm_channel *sc, struct device *dev, + void __iomem *base); + +int mana_smc_setup_hwc(struct shm_channel *sc, bool reset_vf, u64 eq_addr, + u64 cq_addr, u64 rq_addr, u64 sq_addr, + u32 eq_msix_index); + +int mana_smc_teardown_hwc(struct shm_channel *sc, bool reset_vf); + +#endif /* _SHM_CHANNEL_H */ -- cgit v1.2.3 From 461e1857d6bf15cf5df103383e255f3425ac0038 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 28 Oct 2022 17:59:20 +0100 Subject: MAINTAINERS: Add entry for Renesas RISC-V Add RISC-V architecture as part of ARM/Renesas architecture, as they have the same maintainers, use the same development collaboration infrastructure, and share many files. Signed-off-by: Lad Prabhakar Reviewed-by: Conor Dooley Reviewed-by: Guo Ren Acked-by: Palmer Dabbelt Link: https://lore.kernel.org/r/20221028165921.94487-7-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..7290f18d479c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2691,7 +2691,7 @@ F: arch/arm/boot/dts/rtd* F: arch/arm/mach-realtek/ F: arch/arm64/boot/dts/realtek/ -ARM/RENESAS ARCHITECTURE +ARM/RISC-V/RENESAS ARCHITECTURE M: Geert Uytterhoeven M: Magnus Damm L: linux-renesas-soc@vger.kernel.org @@ -2713,6 +2713,7 @@ F: arch/arm/configs/shmobile_defconfig F: arch/arm/include/debug/renesas-scif.S F: arch/arm/mach-shmobile/ F: arch/arm64/boot/dts/renesas/ +F: arch/riscv/boot/dts/renesas/ F: drivers/soc/renesas/ F: include/linux/soc/renesas/ -- cgit v1.2.3 From a07b3835b895b0ef772f88c4b7c38e9fed52b1ff Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Wed, 9 Nov 2022 15:26:41 +0800 Subject: MAINTAINERS: Update hinic maintainers from orphan HINIC is marked orphan for 14 months from the commit "5cfe5109a1d7", but there are lots of HINIC in use. I have a SP582 NIC (hi1822 inside which is a kind of HINIC SOC), and implement based on hinic driver, and if there are some patches for HINIC, I can test and do some code review. I'm active in linux contribution, if possible, I want to take the hinic maintainership. Add my email here to receive patches. Signed-off-by: Cai Huoqing Signed-off-by: David S. Miller --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index eb5fe2fd4d7a..6aea837ff9d4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9438,8 +9438,9 @@ F: Documentation/devicetree/bindings/iio/humidity/st,hts221.yaml F: drivers/iio/humidity/hts221* HUAWEI ETHERNET DRIVER +M: Cai Huoqing L: netdev@vger.kernel.org -S: Orphan +S: Maintained F: Documentation/networking/device_drivers/ethernet/huawei/hinic.rst F: drivers/net/ethernet/huawei/hinic/ -- cgit v1.2.3 From 90050f80509c7e97cd957b025b62e6e49c2c6bc0 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 5 Nov 2022 23:36:23 +0100 Subject: MAINTAINERS: update MEDIATEK ETHERNET entry Update MEDIATEK ETHERNET driver maintainer file enty adding myself to maintainers list Acked-by: Felix Fietkau Acked-by: Sean Wang Signed-off-by: Lorenzo Bianconi Signed-off-by: David S. Miller --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 6aea837ff9d4..14ee1c72d01a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12926,6 +12926,7 @@ M: Felix Fietkau M: John Crispin M: Sean Wang M: Mark Lee +M: Lorenzo Bianconi L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/mediatek/ -- cgit v1.2.3 From 0266a177631d4c6b963b5b12dd986a8c5abdbf06 Mon Sep 17 00:00:00 2001 From: Long Li Date: Thu, 3 Nov 2022 12:16:30 -0700 Subject: RDMA/mana_ib: Add a driver for Microsoft Azure Network Adapter Add a RDMA VF driver for Microsoft Azure Network Adapter (MANA). Co-developed-by: Ajay Sharma Signed-off-by: Ajay Sharma Reviewed-by: Dexuan Cui Signed-off-by: Long Li Link: https://lore.kernel.org/r/1667502990-2559-13-git-send-email-longli@linuxonhyperv.com Signed-off-by: Leon Romanovsky --- MAINTAINERS | 9 + drivers/infiniband/Kconfig | 1 + drivers/infiniband/hw/Makefile | 1 + drivers/infiniband/hw/mana/Kconfig | 10 + drivers/infiniband/hw/mana/Makefile | 4 + drivers/infiniband/hw/mana/cq.c | 79 +++++ drivers/infiniband/hw/mana/device.c | 117 +++++++ drivers/infiniband/hw/mana/main.c | 521 ++++++++++++++++++++++++++++++++ drivers/infiniband/hw/mana/mana_ib.h | 162 ++++++++++ drivers/infiniband/hw/mana/mr.c | 198 ++++++++++++ drivers/infiniband/hw/mana/qp.c | 506 +++++++++++++++++++++++++++++++ drivers/infiniband/hw/mana/wq.c | 115 +++++++ include/net/mana/mana.h | 3 + include/uapi/rdma/ib_user_ioctl_verbs.h | 1 + include/uapi/rdma/mana-abi.h | 66 ++++ 15 files changed, 1793 insertions(+) create mode 100644 drivers/infiniband/hw/mana/Kconfig create mode 100644 drivers/infiniband/hw/mana/Makefile create mode 100644 drivers/infiniband/hw/mana/cq.c create mode 100644 drivers/infiniband/hw/mana/device.c create mode 100644 drivers/infiniband/hw/mana/main.c create mode 100644 drivers/infiniband/hw/mana/mana_ib.h create mode 100644 drivers/infiniband/hw/mana/mr.c create mode 100644 drivers/infiniband/hw/mana/qp.c create mode 100644 drivers/infiniband/hw/mana/wq.c create mode 100644 include/uapi/rdma/mana-abi.h (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 441a65d41eb4..4db8e4e02c05 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13669,6 +13669,15 @@ F: drivers/scsi/smartpqi/smartpqi*.[ch] F: include/linux/cciss*.h F: include/uapi/linux/cciss*.h +MICROSOFT MANA RDMA DRIVER +M: Long Li +M: Ajay Sharma +L: linux-rdma@vger.kernel.org +S: Supported +F: drivers/infiniband/hw/mana/ +F: include/net/mana +F: include/uapi/rdma/mana-abi.h + MICROSOFT SURFACE AGGREGATOR TABLET-MODE SWITCH M: Maximilian Luz L: platform-driver-x86@vger.kernel.org diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig index aa36ac618e72..ccc874478f0b 100644 --- a/drivers/infiniband/Kconfig +++ b/drivers/infiniband/Kconfig @@ -85,6 +85,7 @@ source "drivers/infiniband/hw/erdma/Kconfig" source "drivers/infiniband/hw/hfi1/Kconfig" source "drivers/infiniband/hw/hns/Kconfig" source "drivers/infiniband/hw/irdma/Kconfig" +source "drivers/infiniband/hw/mana/Kconfig" source "drivers/infiniband/hw/mlx4/Kconfig" source "drivers/infiniband/hw/mlx5/Kconfig" source "drivers/infiniband/hw/mthca/Kconfig" diff --git a/drivers/infiniband/hw/Makefile b/drivers/infiniband/hw/Makefile index 6b3a88046125..1211f4317a9f 100644 --- a/drivers/infiniband/hw/Makefile +++ b/drivers/infiniband/hw/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_INFINIBAND_QIB) += qib/ obj-$(CONFIG_INFINIBAND_CXGB4) += cxgb4/ obj-$(CONFIG_INFINIBAND_EFA) += efa/ obj-$(CONFIG_INFINIBAND_IRDMA) += irdma/ +obj-$(CONFIG_MANA_INFINIBAND) += mana/ obj-$(CONFIG_MLX4_INFINIBAND) += mlx4/ obj-$(CONFIG_MLX5_INFINIBAND) += mlx5/ obj-$(CONFIG_INFINIBAND_OCRDMA) += ocrdma/ diff --git a/drivers/infiniband/hw/mana/Kconfig b/drivers/infiniband/hw/mana/Kconfig new file mode 100644 index 000000000000..546640657bac --- /dev/null +++ b/drivers/infiniband/hw/mana/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +config MANA_INFINIBAND + tristate "Microsoft Azure Network Adapter support" + depends on NETDEVICES && ETHERNET && PCI && MICROSOFT_MANA + help + This driver provides low-level RDMA support for Microsoft Azure + Network Adapter (MANA). MANA supports RDMA features that can be used + for workloads (e.g. DPDK, MPI etc) that uses RDMA verbs to directly + access hardware from user-mode processes in Microsoft Azure cloud + environment. diff --git a/drivers/infiniband/hw/mana/Makefile b/drivers/infiniband/hw/mana/Makefile new file mode 100644 index 000000000000..88655fe5e398 --- /dev/null +++ b/drivers/infiniband/hw/mana/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_MANA_INFINIBAND) += mana_ib.o + +mana_ib-y := device.o main.o wq.o qp.o cq.o mr.o diff --git a/drivers/infiniband/hw/mana/cq.c b/drivers/infiniband/hw/mana/cq.c new file mode 100644 index 000000000000..d141cab8a1e6 --- /dev/null +++ b/drivers/infiniband/hw/mana/cq.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022, Microsoft Corporation. All rights reserved. + */ + +#include "mana_ib.h" + +int mana_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, + struct ib_udata *udata) +{ + struct mana_ib_cq *cq = container_of(ibcq, struct mana_ib_cq, ibcq); + struct ib_device *ibdev = ibcq->device; + struct mana_ib_create_cq ucmd = {}; + struct mana_ib_dev *mdev; + int err; + + mdev = container_of(ibdev, struct mana_ib_dev, ib_dev); + + if (udata->inlen < sizeof(ucmd)) + return -EINVAL; + + err = ib_copy_from_udata(&ucmd, udata, min(sizeof(ucmd), udata->inlen)); + if (err) { + ibdev_dbg(ibdev, + "Failed to copy from udata for create cq, %d\n", err); + return err; + } + + if (attr->cqe > MAX_SEND_BUFFERS_PER_QUEUE) { + ibdev_dbg(ibdev, "CQE %d exceeding limit\n", attr->cqe); + return -EINVAL; + } + + cq->cqe = attr->cqe; + cq->umem = ib_umem_get(ibdev, ucmd.buf_addr, cq->cqe * COMP_ENTRY_SIZE, + IB_ACCESS_LOCAL_WRITE); + if (IS_ERR(cq->umem)) { + err = PTR_ERR(cq->umem); + ibdev_dbg(ibdev, "Failed to get umem for create cq, err %d\n", + err); + return err; + } + + err = mana_ib_gd_create_dma_region(mdev, cq->umem, &cq->gdma_region); + if (err) { + ibdev_dbg(ibdev, + "Failed to create dma region for create cq, %d\n", + err); + goto err_release_umem; + } + + ibdev_dbg(ibdev, + "mana_ib_gd_create_dma_region ret %d gdma_region 0x%llx\n", + err, cq->gdma_region); + + /* + * The CQ ID is not known at this time. The ID is generated at create_qp + */ + + return 0; + +err_release_umem: + ib_umem_release(cq->umem); + return err; +} + +int mana_ib_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata) +{ + struct mana_ib_cq *cq = container_of(ibcq, struct mana_ib_cq, ibcq); + struct ib_device *ibdev = ibcq->device; + struct mana_ib_dev *mdev; + + mdev = container_of(ibdev, struct mana_ib_dev, ib_dev); + + mana_ib_gd_destroy_dma_region(mdev, cq->gdma_region); + ib_umem_release(cq->umem); + + return 0; +} diff --git a/drivers/infiniband/hw/mana/device.c b/drivers/infiniband/hw/mana/device.c new file mode 100644 index 000000000000..d4541b8707e4 --- /dev/null +++ b/drivers/infiniband/hw/mana/device.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022, Microsoft Corporation. All rights reserved. + */ + +#include "mana_ib.h" +#include + +MODULE_DESCRIPTION("Microsoft Azure Network Adapter IB driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(NET_MANA); + +static const struct ib_device_ops mana_ib_dev_ops = { + .owner = THIS_MODULE, + .driver_id = RDMA_DRIVER_MANA, + .uverbs_abi_ver = MANA_IB_UVERBS_ABI_VERSION, + + .alloc_pd = mana_ib_alloc_pd, + .alloc_ucontext = mana_ib_alloc_ucontext, + .create_cq = mana_ib_create_cq, + .create_qp = mana_ib_create_qp, + .create_rwq_ind_table = mana_ib_create_rwq_ind_table, + .create_wq = mana_ib_create_wq, + .dealloc_pd = mana_ib_dealloc_pd, + .dealloc_ucontext = mana_ib_dealloc_ucontext, + .dereg_mr = mana_ib_dereg_mr, + .destroy_cq = mana_ib_destroy_cq, + .destroy_qp = mana_ib_destroy_qp, + .destroy_rwq_ind_table = mana_ib_destroy_rwq_ind_table, + .destroy_wq = mana_ib_destroy_wq, + .disassociate_ucontext = mana_ib_disassociate_ucontext, + .get_port_immutable = mana_ib_get_port_immutable, + .mmap = mana_ib_mmap, + .modify_qp = mana_ib_modify_qp, + .modify_wq = mana_ib_modify_wq, + .query_device = mana_ib_query_device, + .query_gid = mana_ib_query_gid, + .query_port = mana_ib_query_port, + .reg_user_mr = mana_ib_reg_user_mr, + + INIT_RDMA_OBJ_SIZE(ib_cq, mana_ib_cq, ibcq), + INIT_RDMA_OBJ_SIZE(ib_pd, mana_ib_pd, ibpd), + INIT_RDMA_OBJ_SIZE(ib_qp, mana_ib_qp, ibqp), + INIT_RDMA_OBJ_SIZE(ib_ucontext, mana_ib_ucontext, ibucontext), + INIT_RDMA_OBJ_SIZE(ib_rwq_ind_table, mana_ib_rwq_ind_table, + ib_ind_table), +}; + +static int mana_ib_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct mana_adev *madev = container_of(adev, struct mana_adev, adev); + struct gdma_dev *mdev = madev->mdev; + struct mana_context *mc; + struct mana_ib_dev *dev; + int ret; + + mc = mdev->driver_data; + + dev = ib_alloc_device(mana_ib_dev, ib_dev); + if (!dev) + return -ENOMEM; + + ib_set_device_ops(&dev->ib_dev, &mana_ib_dev_ops); + + dev->ib_dev.phys_port_cnt = mc->num_ports; + + ibdev_dbg(&dev->ib_dev, "mdev=%p id=%d num_ports=%d\n", mdev, + mdev->dev_id.as_uint32, dev->ib_dev.phys_port_cnt); + + dev->gdma_dev = mdev; + dev->ib_dev.node_type = RDMA_NODE_IB_CA; + + /* + * num_comp_vectors needs to set to the max MSIX index + * when interrupts and event queues are implemented + */ + dev->ib_dev.num_comp_vectors = 1; + dev->ib_dev.dev.parent = mdev->gdma_context->dev; + + ret = ib_register_device(&dev->ib_dev, "mana_%d", + mdev->gdma_context->dev); + if (ret) { + ib_dealloc_device(&dev->ib_dev); + return ret; + } + + dev_set_drvdata(&adev->dev, dev); + + return 0; +} + +static void mana_ib_remove(struct auxiliary_device *adev) +{ + struct mana_ib_dev *dev = dev_get_drvdata(&adev->dev); + + ib_unregister_device(&dev->ib_dev); + ib_dealloc_device(&dev->ib_dev); +} + +static const struct auxiliary_device_id mana_id_table[] = { + { + .name = "mana.rdma", + }, + {}, +}; + +MODULE_DEVICE_TABLE(auxiliary, mana_id_table); + +static struct auxiliary_driver mana_driver = { + .name = "rdma", + .probe = mana_ib_probe, + .remove = mana_ib_remove, + .id_table = mana_id_table, +}; + +module_auxiliary_driver(mana_driver); diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c new file mode 100644 index 000000000000..8b3bc302d6f3 --- /dev/null +++ b/drivers/infiniband/hw/mana/main.c @@ -0,0 +1,521 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022, Microsoft Corporation. All rights reserved. + */ + +#include "mana_ib.h" + +void mana_ib_uncfg_vport(struct mana_ib_dev *dev, struct mana_ib_pd *pd, + u32 port) +{ + struct gdma_dev *gd = dev->gdma_dev; + struct mana_port_context *mpc; + struct net_device *ndev; + struct mana_context *mc; + + mc = gd->driver_data; + ndev = mc->ports[port]; + mpc = netdev_priv(ndev); + + mutex_lock(&pd->vport_mutex); + + pd->vport_use_count--; + WARN_ON(pd->vport_use_count < 0); + + if (!pd->vport_use_count) + mana_uncfg_vport(mpc); + + mutex_unlock(&pd->vport_mutex); +} + +int mana_ib_cfg_vport(struct mana_ib_dev *dev, u32 port, struct mana_ib_pd *pd, + u32 doorbell_id) +{ + struct gdma_dev *mdev = dev->gdma_dev; + struct mana_port_context *mpc; + struct mana_context *mc; + struct net_device *ndev; + int err; + + mc = mdev->driver_data; + ndev = mc->ports[port]; + mpc = netdev_priv(ndev); + + mutex_lock(&pd->vport_mutex); + + pd->vport_use_count++; + if (pd->vport_use_count > 1) { + ibdev_dbg(&dev->ib_dev, + "Skip as this PD is already configured vport\n"); + mutex_unlock(&pd->vport_mutex); + return 0; + } + + err = mana_cfg_vport(mpc, pd->pdn, doorbell_id); + if (err) { + pd->vport_use_count--; + mutex_unlock(&pd->vport_mutex); + + ibdev_dbg(&dev->ib_dev, "Failed to configure vPort %d\n", err); + return err; + } + + mutex_unlock(&pd->vport_mutex); + + pd->tx_shortform_allowed = mpc->tx_shortform_allowed; + pd->tx_vp_offset = mpc->tx_vp_offset; + + ibdev_dbg(&dev->ib_dev, "vport handle %llx pdid %x doorbell_id %x\n", + mpc->port_handle, pd->pdn, doorbell_id); + + return 0; +} + +int mana_ib_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) +{ + struct mana_ib_pd *pd = container_of(ibpd, struct mana_ib_pd, ibpd); + struct ib_device *ibdev = ibpd->device; + struct gdma_create_pd_resp resp = {}; + struct gdma_create_pd_req req = {}; + enum gdma_pd_flags flags = 0; + struct mana_ib_dev *dev; + struct gdma_dev *mdev; + int err; + + dev = container_of(ibdev, struct mana_ib_dev, ib_dev); + mdev = dev->gdma_dev; + + mana_gd_init_req_hdr(&req.hdr, GDMA_CREATE_PD, sizeof(req), + sizeof(resp)); + + req.flags = flags; + err = mana_gd_send_request(mdev->gdma_context, sizeof(req), &req, + sizeof(resp), &resp); + + if (err || resp.hdr.status) { + ibdev_dbg(&dev->ib_dev, + "Failed to get pd_id err %d status %u\n", err, + resp.hdr.status); + if (!err) + err = -EPROTO; + + return err; + } + + pd->pd_handle = resp.pd_handle; + pd->pdn = resp.pd_id; + ibdev_dbg(&dev->ib_dev, "pd_handle 0x%llx pd_id %d\n", + pd->pd_handle, pd->pdn); + + mutex_init(&pd->vport_mutex); + pd->vport_use_count = 0; + return 0; +} + +int mana_ib_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) +{ + struct mana_ib_pd *pd = container_of(ibpd, struct mana_ib_pd, ibpd); + struct ib_device *ibdev = ibpd->device; + struct gdma_destory_pd_resp resp = {}; + struct gdma_destroy_pd_req req = {}; + struct mana_ib_dev *dev; + struct gdma_dev *mdev; + int err; + + dev = container_of(ibdev, struct mana_ib_dev, ib_dev); + mdev = dev->gdma_dev; + + mana_gd_init_req_hdr(&req.hdr, GDMA_DESTROY_PD, sizeof(req), + sizeof(resp)); + + req.pd_handle = pd->pd_handle; + err = mana_gd_send_request(mdev->gdma_context, sizeof(req), &req, + sizeof(resp), &resp); + + if (err || resp.hdr.status) { + ibdev_dbg(&dev->ib_dev, + "Failed to destroy pd_handle 0x%llx err %d status %u", + pd->pd_handle, err, resp.hdr.status); + if (!err) + err = -EPROTO; + } + + return err; +} + +static int mana_gd_destroy_doorbell_page(struct gdma_context *gc, + int doorbell_page) +{ + struct gdma_destroy_resource_range_req req = {}; + struct gdma_resp_hdr resp = {}; + int err; + + mana_gd_init_req_hdr(&req.hdr, GDMA_DESTROY_RESOURCE_RANGE, + sizeof(req), sizeof(resp)); + + req.resource_type = GDMA_RESOURCE_DOORBELL_PAGE; + req.num_resources = 1; + req.allocated_resources = doorbell_page; + + err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); + if (err || resp.status) { + dev_err(gc->dev, + "Failed to destroy doorbell page: ret %d, 0x%x\n", + err, resp.status); + return err ?: -EPROTO; + } + + return 0; +} + +static int mana_gd_allocate_doorbell_page(struct gdma_context *gc, + int *doorbell_page) +{ + struct gdma_allocate_resource_range_req req = {}; + struct gdma_allocate_resource_range_resp resp = {}; + int err; + + mana_gd_init_req_hdr(&req.hdr, GDMA_ALLOCATE_RESOURCE_RANGE, + sizeof(req), sizeof(resp)); + + req.resource_type = GDMA_RESOURCE_DOORBELL_PAGE; + req.num_resources = 1; + req.alignment = 1; + + /* Have GDMA start searching from 0 */ + req.allocated_resources = 0; + + err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); + if (err || resp.hdr.status) { + dev_err(gc->dev, + "Failed to allocate doorbell page: ret %d, 0x%x\n", + err, resp.hdr.status); + return err ?: -EPROTO; + } + + *doorbell_page = resp.allocated_resources; + + return 0; +} + +int mana_ib_alloc_ucontext(struct ib_ucontext *ibcontext, + struct ib_udata *udata) +{ + struct mana_ib_ucontext *ucontext = + container_of(ibcontext, struct mana_ib_ucontext, ibucontext); + struct ib_device *ibdev = ibcontext->device; + struct mana_ib_dev *mdev; + struct gdma_context *gc; + struct gdma_dev *dev; + int doorbell_page; + int ret; + + mdev = container_of(ibdev, struct mana_ib_dev, ib_dev); + dev = mdev->gdma_dev; + gc = dev->gdma_context; + + /* Allocate a doorbell page index */ + ret = mana_gd_allocate_doorbell_page(gc, &doorbell_page); + if (ret) { + ibdev_dbg(ibdev, "Failed to allocate doorbell page %d\n", ret); + return ret; + } + + ibdev_dbg(ibdev, "Doorbell page allocated %d\n", doorbell_page); + + ucontext->doorbell = doorbell_page; + + return 0; +} + +void mana_ib_dealloc_ucontext(struct ib_ucontext *ibcontext) +{ + struct mana_ib_ucontext *mana_ucontext = + container_of(ibcontext, struct mana_ib_ucontext, ibucontext); + struct ib_device *ibdev = ibcontext->device; + struct mana_ib_dev *mdev; + struct gdma_context *gc; + int ret; + + mdev = container_of(ibdev, struct mana_ib_dev, ib_dev); + gc = mdev->gdma_dev->gdma_context; + + ret = mana_gd_destroy_doorbell_page(gc, mana_ucontext->doorbell); + if (ret) + ibdev_dbg(ibdev, "Failed to destroy doorbell page %d\n", ret); +} + +static int +mana_ib_gd_first_dma_region(struct mana_ib_dev *dev, + struct gdma_context *gc, + struct gdma_create_dma_region_req *create_req, + size_t num_pages, mana_handle_t *gdma_region) +{ + struct gdma_create_dma_region_resp create_resp = {}; + unsigned int create_req_msg_size; + int err; + + create_req_msg_size = + struct_size(create_req, page_addr_list, num_pages); + create_req->page_addr_list_len = num_pages; + + err = mana_gd_send_request(gc, create_req_msg_size, create_req, + sizeof(create_resp), &create_resp); + if (err || create_resp.hdr.status) { + ibdev_dbg(&dev->ib_dev, + "Failed to create DMA region: %d, 0x%x\n", + err, create_resp.hdr.status); + if (!err) + err = -EPROTO; + + return err; + } + + *gdma_region = create_resp.dma_region_handle; + ibdev_dbg(&dev->ib_dev, "Created DMA region handle 0x%llx\n", + *gdma_region); + + return 0; +} + +static int +mana_ib_gd_add_dma_region(struct mana_ib_dev *dev, struct gdma_context *gc, + struct gdma_dma_region_add_pages_req *add_req, + unsigned int num_pages, u32 expected_status) +{ + unsigned int add_req_msg_size = + struct_size(add_req, page_addr_list, num_pages); + struct gdma_general_resp add_resp = {}; + int err; + + mana_gd_init_req_hdr(&add_req->hdr, GDMA_DMA_REGION_ADD_PAGES, + add_req_msg_size, sizeof(add_resp)); + add_req->page_addr_list_len = num_pages; + + err = mana_gd_send_request(gc, add_req_msg_size, add_req, + sizeof(add_resp), &add_resp); + if (err || add_resp.hdr.status != expected_status) { + ibdev_dbg(&dev->ib_dev, + "Failed to create DMA region: %d, 0x%x\n", + err, add_resp.hdr.status); + + if (!err) + err = -EPROTO; + + return err; + } + + return 0; +} + +int mana_ib_gd_create_dma_region(struct mana_ib_dev *dev, struct ib_umem *umem, + mana_handle_t *gdma_region) +{ + struct gdma_dma_region_add_pages_req *add_req = NULL; + size_t num_pages_processed = 0, num_pages_to_handle; + struct gdma_create_dma_region_req *create_req; + unsigned int create_req_msg_size; + struct hw_channel_context *hwc; + struct ib_block_iter biter; + size_t max_pgs_add_cmd = 0; + size_t max_pgs_create_cmd; + struct gdma_context *gc; + size_t num_pages_total; + struct gdma_dev *mdev; + unsigned long page_sz; + unsigned int tail = 0; + u64 *page_addr_list; + void *request_buf; + int err; + + mdev = dev->gdma_dev; + gc = mdev->gdma_context; + hwc = gc->hwc.driver_data; + + /* Hardware requires dma region to align to chosen page size */ + page_sz = ib_umem_find_best_pgsz(umem, PAGE_SZ_BM, 0); + if (!page_sz) { + ibdev_dbg(&dev->ib_dev, "failed to find page size.\n"); + return -ENOMEM; + } + num_pages_total = ib_umem_num_dma_blocks(umem, page_sz); + + max_pgs_create_cmd = + (hwc->max_req_msg_size - sizeof(*create_req)) / sizeof(u64); + num_pages_to_handle = + min_t(size_t, num_pages_total, max_pgs_create_cmd); + create_req_msg_size = + struct_size(create_req, page_addr_list, num_pages_to_handle); + + request_buf = kzalloc(hwc->max_req_msg_size, GFP_KERNEL); + if (!request_buf) + return -ENOMEM; + + create_req = request_buf; + mana_gd_init_req_hdr(&create_req->hdr, GDMA_CREATE_DMA_REGION, + create_req_msg_size, + sizeof(struct gdma_create_dma_region_resp)); + + create_req->length = umem->length; + create_req->offset_in_page = umem->address & (page_sz - 1); + create_req->gdma_page_type = order_base_2(page_sz) - PAGE_SHIFT; + create_req->page_count = num_pages_total; + + ibdev_dbg(&dev->ib_dev, "size_dma_region %lu num_pages_total %lu\n", + umem->length, num_pages_total); + + ibdev_dbg(&dev->ib_dev, "page_sz %lu offset_in_page %u\n", + page_sz, create_req->offset_in_page); + + ibdev_dbg(&dev->ib_dev, "num_pages_to_handle %lu, gdma_page_type %u", + num_pages_to_handle, create_req->gdma_page_type); + + page_addr_list = create_req->page_addr_list; + rdma_umem_for_each_dma_block(umem, &biter, page_sz) { + page_addr_list[tail++] = rdma_block_iter_dma_address(&biter); + if (tail < num_pages_to_handle) + continue; + + if (!num_pages_processed) { + /* First create message */ + err = mana_ib_gd_first_dma_region(dev, gc, create_req, + tail, gdma_region); + if (err) + goto out; + + max_pgs_add_cmd = (hwc->max_req_msg_size - + sizeof(*add_req)) / sizeof(u64); + + add_req = request_buf; + add_req->dma_region_handle = *gdma_region; + add_req->reserved3 = 0; + page_addr_list = add_req->page_addr_list; + } else { + /* Subsequent create messages */ + u32 expected_s = 0; + + if (num_pages_processed + num_pages_to_handle < + num_pages_total) + expected_s = GDMA_STATUS_MORE_ENTRIES; + + err = mana_ib_gd_add_dma_region(dev, gc, add_req, tail, + expected_s); + if (err) + break; + } + + num_pages_processed += tail; + tail = 0; + + /* The remaining pages to create */ + num_pages_to_handle = + min_t(size_t, + num_pages_total - num_pages_processed, + max_pgs_add_cmd); + } + + if (err) + mana_ib_gd_destroy_dma_region(dev, *gdma_region); + +out: + kfree(request_buf); + return err; +} + +int mana_ib_gd_destroy_dma_region(struct mana_ib_dev *dev, u64 gdma_region) +{ + struct gdma_dev *mdev = dev->gdma_dev; + struct gdma_context *gc; + + gc = mdev->gdma_context; + ibdev_dbg(&dev->ib_dev, "destroy dma region 0x%llx\n", gdma_region); + + return mana_gd_destroy_dma_region(gc, gdma_region); +} + +int mana_ib_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vma) +{ + struct mana_ib_ucontext *mana_ucontext = + container_of(ibcontext, struct mana_ib_ucontext, ibucontext); + struct ib_device *ibdev = ibcontext->device; + struct mana_ib_dev *mdev; + struct gdma_context *gc; + phys_addr_t pfn; + pgprot_t prot; + int ret; + + mdev = container_of(ibdev, struct mana_ib_dev, ib_dev); + gc = mdev->gdma_dev->gdma_context; + + if (vma->vm_pgoff != 0) { + ibdev_dbg(ibdev, "Unexpected vm_pgoff %lu\n", vma->vm_pgoff); + return -EINVAL; + } + + /* Map to the page indexed by ucontext->doorbell */ + pfn = (gc->phys_db_page_base + + gc->db_page_size * mana_ucontext->doorbell) >> + PAGE_SHIFT; + prot = pgprot_writecombine(vma->vm_page_prot); + + ret = rdma_user_mmap_io(ibcontext, vma, pfn, gc->db_page_size, prot, + NULL); + if (ret) + ibdev_dbg(ibdev, "can't rdma_user_mmap_io ret %d\n", ret); + else + ibdev_dbg(ibdev, "mapped I/O pfn 0x%llx page_size %u, ret %d\n", + pfn, gc->db_page_size, ret); + + return ret; +} + +int mana_ib_get_port_immutable(struct ib_device *ibdev, u32 port_num, + struct ib_port_immutable *immutable) +{ + /* + * This version only support RAW_PACKET + * other values need to be filled for other types + */ + immutable->core_cap_flags = RDMA_CORE_PORT_RAW_PACKET; + + return 0; +} + +int mana_ib_query_device(struct ib_device *ibdev, struct ib_device_attr *props, + struct ib_udata *uhw) +{ + props->max_qp = MANA_MAX_NUM_QUEUES; + props->max_qp_wr = MAX_SEND_BUFFERS_PER_QUEUE; + + /* + * max_cqe could be potentially much bigger. + * As this version of driver only support RAW QP, set it to the same + * value as max_qp_wr + */ + props->max_cqe = MAX_SEND_BUFFERS_PER_QUEUE; + + props->max_mr_size = MANA_IB_MAX_MR_SIZE; + props->max_mr = MANA_IB_MAX_MR; + props->max_send_sge = MAX_TX_WQE_SGL_ENTRIES; + props->max_recv_sge = MAX_RX_WQE_SGL_ENTRIES; + + return 0; +} + +int mana_ib_query_port(struct ib_device *ibdev, u32 port, + struct ib_port_attr *props) +{ + /* This version doesn't return port properties */ + return 0; +} + +int mana_ib_query_gid(struct ib_device *ibdev, u32 port, int index, + union ib_gid *gid) +{ + /* This version doesn't return GID properties */ + return 0; +} + +void mana_ib_disassociate_ucontext(struct ib_ucontext *ibcontext) +{ +} diff --git a/drivers/infiniband/hw/mana/mana_ib.h b/drivers/infiniband/hw/mana/mana_ib.h new file mode 100644 index 000000000000..502cc8672eef --- /dev/null +++ b/drivers/infiniband/hw/mana/mana_ib.h @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 Microsoft Corporation. All rights reserved. + */ + +#ifndef _MANA_IB_H_ +#define _MANA_IB_H_ + +#include +#include +#include +#include +#include + +#include + +#define PAGE_SZ_BM \ + (SZ_4K | SZ_8K | SZ_16K | SZ_32K | SZ_64K | SZ_128K | SZ_256K | \ + SZ_512K | SZ_1M | SZ_2M) + +/* MANA doesn't have any limit for MR size */ +#define MANA_IB_MAX_MR_SIZE U64_MAX + +/* + * The hardware limit of number of MRs is greater than maximum number of MRs + * that can possibly represent in 24 bits + */ +#define MANA_IB_MAX_MR 0xFFFFFFu + +struct mana_ib_dev { + struct ib_device ib_dev; + struct gdma_dev *gdma_dev; +}; + +struct mana_ib_wq { + struct ib_wq ibwq; + struct ib_umem *umem; + int wqe; + u32 wq_buf_size; + u64 gdma_region; + u64 id; + mana_handle_t rx_object; +}; + +struct mana_ib_pd { + struct ib_pd ibpd; + u32 pdn; + mana_handle_t pd_handle; + + /* Mutex for sharing access to vport_use_count */ + struct mutex vport_mutex; + int vport_use_count; + + bool tx_shortform_allowed; + u32 tx_vp_offset; +}; + +struct mana_ib_mr { + struct ib_mr ibmr; + struct ib_umem *umem; + mana_handle_t mr_handle; +}; + +struct mana_ib_cq { + struct ib_cq ibcq; + struct ib_umem *umem; + int cqe; + u64 gdma_region; + u64 id; +}; + +struct mana_ib_qp { + struct ib_qp ibqp; + + /* Work queue info */ + struct ib_umem *sq_umem; + int sqe; + u64 sq_gdma_region; + u64 sq_id; + mana_handle_t tx_object; + + /* The port on the IB device, starting with 1 */ + u32 port; +}; + +struct mana_ib_ucontext { + struct ib_ucontext ibucontext; + u32 doorbell; +}; + +struct mana_ib_rwq_ind_table { + struct ib_rwq_ind_table ib_ind_table; +}; + +int mana_ib_gd_create_dma_region(struct mana_ib_dev *dev, struct ib_umem *umem, + mana_handle_t *gdma_region); + +int mana_ib_gd_destroy_dma_region(struct mana_ib_dev *dev, + mana_handle_t gdma_region); + +struct ib_wq *mana_ib_create_wq(struct ib_pd *pd, + struct ib_wq_init_attr *init_attr, + struct ib_udata *udata); + +int mana_ib_modify_wq(struct ib_wq *wq, struct ib_wq_attr *wq_attr, + u32 wq_attr_mask, struct ib_udata *udata); + +int mana_ib_destroy_wq(struct ib_wq *ibwq, struct ib_udata *udata); + +int mana_ib_create_rwq_ind_table(struct ib_rwq_ind_table *ib_rwq_ind_table, + struct ib_rwq_ind_table_init_attr *init_attr, + struct ib_udata *udata); + +int mana_ib_destroy_rwq_ind_table(struct ib_rwq_ind_table *ib_rwq_ind_tbl); + +struct ib_mr *mana_ib_get_dma_mr(struct ib_pd *ibpd, int access_flags); + +struct ib_mr *mana_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, + u64 iova, int access_flags, + struct ib_udata *udata); + +int mana_ib_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata); + +int mana_ib_create_qp(struct ib_qp *qp, struct ib_qp_init_attr *qp_init_attr, + struct ib_udata *udata); + +int mana_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, + int attr_mask, struct ib_udata *udata); + +int mana_ib_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata); + +int mana_ib_cfg_vport(struct mana_ib_dev *dev, u32 port_id, + struct mana_ib_pd *pd, u32 doorbell_id); +void mana_ib_uncfg_vport(struct mana_ib_dev *dev, struct mana_ib_pd *pd, + u32 port); + +int mana_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, + struct ib_udata *udata); + +int mana_ib_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata); + +int mana_ib_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata); +int mana_ib_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata); + +int mana_ib_alloc_ucontext(struct ib_ucontext *ibcontext, + struct ib_udata *udata); +void mana_ib_dealloc_ucontext(struct ib_ucontext *ibcontext); + +int mana_ib_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vma); + +int mana_ib_get_port_immutable(struct ib_device *ibdev, u32 port_num, + struct ib_port_immutable *immutable); +int mana_ib_query_device(struct ib_device *ibdev, struct ib_device_attr *props, + struct ib_udata *uhw); +int mana_ib_query_port(struct ib_device *ibdev, u32 port, + struct ib_port_attr *props); +int mana_ib_query_gid(struct ib_device *ibdev, u32 port, int index, + union ib_gid *gid); + +void mana_ib_disassociate_ucontext(struct ib_ucontext *ibcontext); + +#endif diff --git a/drivers/infiniband/hw/mana/mr.c b/drivers/infiniband/hw/mana/mr.c new file mode 100644 index 000000000000..a56236cdd9ee --- /dev/null +++ b/drivers/infiniband/hw/mana/mr.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022, Microsoft Corporation. All rights reserved. + */ + +#include "mana_ib.h" + +#define VALID_MR_FLAGS \ + (IB_ACCESS_LOCAL_WRITE | IB_ACCESS_REMOTE_WRITE | IB_ACCESS_REMOTE_READ) + +static enum gdma_mr_access_flags +mana_ib_verbs_to_gdma_access_flags(int access_flags) +{ + enum gdma_mr_access_flags flags = GDMA_ACCESS_FLAG_LOCAL_READ; + + if (access_flags & IB_ACCESS_LOCAL_WRITE) + flags |= GDMA_ACCESS_FLAG_LOCAL_WRITE; + + if (access_flags & IB_ACCESS_REMOTE_WRITE) + flags |= GDMA_ACCESS_FLAG_REMOTE_WRITE; + + if (access_flags & IB_ACCESS_REMOTE_READ) + flags |= GDMA_ACCESS_FLAG_REMOTE_READ; + + return flags; +} + +static int mana_ib_gd_create_mr(struct mana_ib_dev *dev, struct mana_ib_mr *mr, + struct gdma_create_mr_params *mr_params) +{ + struct gdma_create_mr_response resp = {}; + struct gdma_create_mr_request req = {}; + struct gdma_dev *mdev = dev->gdma_dev; + struct gdma_context *gc; + int err; + + gc = mdev->gdma_context; + + mana_gd_init_req_hdr(&req.hdr, GDMA_CREATE_MR, sizeof(req), + sizeof(resp)); + req.pd_handle = mr_params->pd_handle; + req.mr_type = mr_params->mr_type; + + switch (mr_params->mr_type) { + case GDMA_MR_TYPE_GVA: + req.gva.dma_region_handle = mr_params->gva.dma_region_handle; + req.gva.virtual_address = mr_params->gva.virtual_address; + req.gva.access_flags = mr_params->gva.access_flags; + break; + + default: + ibdev_dbg(&dev->ib_dev, + "invalid param (GDMA_MR_TYPE) passed, type %d\n", + req.mr_type); + return -EINVAL; + } + + err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); + + if (err || resp.hdr.status) { + ibdev_dbg(&dev->ib_dev, "Failed to create mr %d, %u", err, + resp.hdr.status); + if (!err) + err = -EPROTO; + + return err; + } + + mr->ibmr.lkey = resp.lkey; + mr->ibmr.rkey = resp.rkey; + mr->mr_handle = resp.mr_handle; + + return 0; +} + +static int mana_ib_gd_destroy_mr(struct mana_ib_dev *dev, + gdma_obj_handle_t mr_handle) +{ + struct gdma_destroy_mr_response resp = {}; + struct gdma_destroy_mr_request req = {}; + struct gdma_dev *mdev = dev->gdma_dev; + struct gdma_context *gc; + int err; + + gc = mdev->gdma_context; + + mana_gd_init_req_hdr(&req.hdr, GDMA_DESTROY_MR, sizeof(req), + sizeof(resp)); + + req.mr_handle = mr_handle; + + err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); + if (err || resp.hdr.status) { + dev_err(gc->dev, "Failed to destroy MR: %d, 0x%x\n", err, + resp.hdr.status); + if (!err) + err = -EPROTO; + return err; + } + + return 0; +} + +struct ib_mr *mana_ib_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 length, + u64 iova, int access_flags, + struct ib_udata *udata) +{ + struct mana_ib_pd *pd = container_of(ibpd, struct mana_ib_pd, ibpd); + struct gdma_create_mr_params mr_params = {}; + struct ib_device *ibdev = ibpd->device; + gdma_obj_handle_t dma_region_handle; + struct mana_ib_dev *dev; + struct mana_ib_mr *mr; + int err; + + dev = container_of(ibdev, struct mana_ib_dev, ib_dev); + + ibdev_dbg(ibdev, + "start 0x%llx, iova 0x%llx length 0x%llx access_flags 0x%x", + start, iova, length, access_flags); + + if (access_flags & ~VALID_MR_FLAGS) + return ERR_PTR(-EINVAL); + + mr = kzalloc(sizeof(*mr), GFP_KERNEL); + if (!mr) + return ERR_PTR(-ENOMEM); + + mr->umem = ib_umem_get(ibdev, start, length, access_flags); + if (IS_ERR(mr->umem)) { + err = PTR_ERR(mr->umem); + ibdev_dbg(ibdev, + "Failed to get umem for register user-mr, %d\n", err); + goto err_free; + } + + err = mana_ib_gd_create_dma_region(dev, mr->umem, &dma_region_handle); + if (err) { + ibdev_dbg(ibdev, "Failed create dma region for user-mr, %d\n", + err); + goto err_umem; + } + + ibdev_dbg(ibdev, + "mana_ib_gd_create_dma_region ret %d gdma_region %llx\n", err, + dma_region_handle); + + mr_params.pd_handle = pd->pd_handle; + mr_params.mr_type = GDMA_MR_TYPE_GVA; + mr_params.gva.dma_region_handle = dma_region_handle; + mr_params.gva.virtual_address = iova; + mr_params.gva.access_flags = + mana_ib_verbs_to_gdma_access_flags(access_flags); + + err = mana_ib_gd_create_mr(dev, mr, &mr_params); + if (err) + goto err_dma_region; + + /* + * There is no need to keep track of dma_region_handle after MR is + * successfully created. The dma_region_handle is tracked in the PF + * as part of the lifecycle of this MR. + */ + + return &mr->ibmr; + +err_dma_region: + mana_gd_destroy_dma_region(dev->gdma_dev->gdma_context, + dma_region_handle); + +err_umem: + ib_umem_release(mr->umem); + +err_free: + kfree(mr); + return ERR_PTR(err); +} + +int mana_ib_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata) +{ + struct mana_ib_mr *mr = container_of(ibmr, struct mana_ib_mr, ibmr); + struct ib_device *ibdev = ibmr->device; + struct mana_ib_dev *dev; + int err; + + dev = container_of(ibdev, struct mana_ib_dev, ib_dev); + + err = mana_ib_gd_destroy_mr(dev, mr->mr_handle); + if (err) + return err; + + if (mr->umem) + ib_umem_release(mr->umem); + + kfree(mr); + + return 0; +} diff --git a/drivers/infiniband/hw/mana/qp.c b/drivers/infiniband/hw/mana/qp.c new file mode 100644 index 000000000000..ea15ec77e321 --- /dev/null +++ b/drivers/infiniband/hw/mana/qp.c @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022, Microsoft Corporation. All rights reserved. + */ + +#include "mana_ib.h" + +static int mana_ib_cfg_vport_steering(struct mana_ib_dev *dev, + struct net_device *ndev, + mana_handle_t default_rxobj, + mana_handle_t ind_table[], + u32 log_ind_tbl_size, u32 rx_hash_key_len, + u8 *rx_hash_key) +{ + struct mana_port_context *mpc = netdev_priv(ndev); + struct mana_cfg_rx_steer_req *req = NULL; + struct mana_cfg_rx_steer_resp resp = {}; + mana_handle_t *req_indir_tab; + struct gdma_context *gc; + struct gdma_dev *mdev; + u32 req_buf_size; + int i, err; + + mdev = dev->gdma_dev; + gc = mdev->gdma_context; + + req_buf_size = + sizeof(*req) + sizeof(mana_handle_t) * MANA_INDIRECT_TABLE_SIZE; + req = kzalloc(req_buf_size, GFP_KERNEL); + if (!req) + return -ENOMEM; + + mana_gd_init_req_hdr(&req->hdr, MANA_CONFIG_VPORT_RX, req_buf_size, + sizeof(resp)); + + req->vport = mpc->port_handle; + req->rx_enable = 1; + req->update_default_rxobj = 1; + req->default_rxobj = default_rxobj; + req->hdr.dev_id = mdev->dev_id; + + /* If there are more than 1 entries in indirection table, enable RSS */ + if (log_ind_tbl_size) + req->rss_enable = true; + + req->num_indir_entries = MANA_INDIRECT_TABLE_SIZE; + req->indir_tab_offset = sizeof(*req); + req->update_indir_tab = true; + + req_indir_tab = (mana_handle_t *)(req + 1); + /* The ind table passed to the hardware must have + * MANA_INDIRECT_TABLE_SIZE entries. Adjust the verb + * ind_table to MANA_INDIRECT_TABLE_SIZE if required + */ + ibdev_dbg(&dev->ib_dev, "ind table size %u\n", 1 << log_ind_tbl_size); + for (i = 0; i < MANA_INDIRECT_TABLE_SIZE; i++) { + req_indir_tab[i] = ind_table[i % (1 << log_ind_tbl_size)]; + ibdev_dbg(&dev->ib_dev, "index %u handle 0x%llx\n", i, + req_indir_tab[i]); + } + + req->update_hashkey = true; + if (rx_hash_key_len) + memcpy(req->hashkey, rx_hash_key, rx_hash_key_len); + else + netdev_rss_key_fill(req->hashkey, MANA_HASH_KEY_SIZE); + + ibdev_dbg(&dev->ib_dev, "vport handle %llu default_rxobj 0x%llx\n", + req->vport, default_rxobj); + + err = mana_gd_send_request(gc, req_buf_size, req, sizeof(resp), &resp); + if (err) { + netdev_err(ndev, "Failed to configure vPort RX: %d\n", err); + goto out; + } + + if (resp.hdr.status) { + netdev_err(ndev, "vPort RX configuration failed: 0x%x\n", + resp.hdr.status); + err = -EPROTO; + goto out; + } + + netdev_info(ndev, "Configured steering vPort %llu log_entries %u\n", + mpc->port_handle, log_ind_tbl_size); + +out: + kfree(req); + return err; +} + +static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, + struct ib_qp_init_attr *attr, + struct ib_udata *udata) +{ + struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp); + struct mana_ib_dev *mdev = + container_of(pd->device, struct mana_ib_dev, ib_dev); + struct ib_rwq_ind_table *ind_tbl = attr->rwq_ind_tbl; + struct mana_ib_create_qp_rss_resp resp = {}; + struct mana_ib_create_qp_rss ucmd = {}; + struct gdma_dev *gd = mdev->gdma_dev; + mana_handle_t *mana_ind_table; + struct mana_port_context *mpc; + struct mana_context *mc; + struct net_device *ndev; + struct mana_ib_cq *cq; + struct mana_ib_wq *wq; + unsigned int ind_tbl_size; + struct ib_cq *ibcq; + struct ib_wq *ibwq; + int i = 0; + u32 port; + int ret; + + mc = gd->driver_data; + + if (!udata || udata->inlen < sizeof(ucmd)) + return -EINVAL; + + ret = ib_copy_from_udata(&ucmd, udata, min(sizeof(ucmd), udata->inlen)); + if (ret) { + ibdev_dbg(&mdev->ib_dev, + "Failed copy from udata for create rss-qp, err %d\n", + ret); + return ret; + } + + if (attr->cap.max_recv_wr > MAX_SEND_BUFFERS_PER_QUEUE) { + ibdev_dbg(&mdev->ib_dev, + "Requested max_recv_wr %d exceeding limit\n", + attr->cap.max_recv_wr); + return -EINVAL; + } + + if (attr->cap.max_recv_sge > MAX_RX_WQE_SGL_ENTRIES) { + ibdev_dbg(&mdev->ib_dev, + "Requested max_recv_sge %d exceeding limit\n", + attr->cap.max_recv_sge); + return -EINVAL; + } + + ind_tbl_size = 1 << ind_tbl->log_ind_tbl_size; + if (ind_tbl_size > MANA_INDIRECT_TABLE_SIZE) { + ibdev_dbg(&mdev->ib_dev, + "Indirect table size %d exceeding limit\n", + ind_tbl_size); + return -EINVAL; + } + + if (ucmd.rx_hash_function != MANA_IB_RX_HASH_FUNC_TOEPLITZ) { + ibdev_dbg(&mdev->ib_dev, + "RX Hash function is not supported, %d\n", + ucmd.rx_hash_function); + return -EINVAL; + } + + /* IB ports start with 1, MANA start with 0 */ + port = ucmd.port; + if (port < 1 || port > mc->num_ports) { + ibdev_dbg(&mdev->ib_dev, "Invalid port %u in creating qp\n", + port); + return -EINVAL; + } + ndev = mc->ports[port - 1]; + mpc = netdev_priv(ndev); + + ibdev_dbg(&mdev->ib_dev, "rx_hash_function %d port %d\n", + ucmd.rx_hash_function, port); + + mana_ind_table = kcalloc(ind_tbl_size, sizeof(mana_handle_t), + GFP_KERNEL); + if (!mana_ind_table) { + ret = -ENOMEM; + goto fail; + } + + qp->port = port; + + for (i = 0; i < ind_tbl_size; i++) { + struct mana_obj_spec wq_spec = {}; + struct mana_obj_spec cq_spec = {}; + + ibwq = ind_tbl->ind_tbl[i]; + wq = container_of(ibwq, struct mana_ib_wq, ibwq); + + ibcq = ibwq->cq; + cq = container_of(ibcq, struct mana_ib_cq, ibcq); + + wq_spec.gdma_region = wq->gdma_region; + wq_spec.queue_size = wq->wq_buf_size; + + cq_spec.gdma_region = cq->gdma_region; + cq_spec.queue_size = cq->cqe * COMP_ENTRY_SIZE; + cq_spec.modr_ctx_id = 0; + cq_spec.attached_eq = GDMA_CQ_NO_EQ; + + ret = mana_create_wq_obj(mpc, mpc->port_handle, GDMA_RQ, + &wq_spec, &cq_spec, &wq->rx_object); + if (ret) + goto fail; + + /* The GDMA regions are now owned by the WQ object */ + wq->gdma_region = GDMA_INVALID_DMA_REGION; + cq->gdma_region = GDMA_INVALID_DMA_REGION; + + wq->id = wq_spec.queue_index; + cq->id = cq_spec.queue_index; + + ibdev_dbg(&mdev->ib_dev, + "ret %d rx_object 0x%llx wq id %llu cq id %llu\n", + ret, wq->rx_object, wq->id, cq->id); + + resp.entries[i].cqid = cq->id; + resp.entries[i].wqid = wq->id; + + mana_ind_table[i] = wq->rx_object; + } + resp.num_entries = i; + + ret = mana_ib_cfg_vport_steering(mdev, ndev, wq->rx_object, + mana_ind_table, + ind_tbl->log_ind_tbl_size, + ucmd.rx_hash_key_len, + ucmd.rx_hash_key); + if (ret) + goto fail; + + ret = ib_copy_to_udata(udata, &resp, sizeof(resp)); + if (ret) { + ibdev_dbg(&mdev->ib_dev, + "Failed to copy to udata create rss-qp, %d\n", + ret); + goto fail; + } + + kfree(mana_ind_table); + + return 0; + +fail: + while (i-- > 0) { + ibwq = ind_tbl->ind_tbl[i]; + wq = container_of(ibwq, struct mana_ib_wq, ibwq); + mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object); + } + + kfree(mana_ind_table); + + return ret; +} + +static int mana_ib_create_qp_raw(struct ib_qp *ibqp, struct ib_pd *ibpd, + struct ib_qp_init_attr *attr, + struct ib_udata *udata) +{ + struct mana_ib_pd *pd = container_of(ibpd, struct mana_ib_pd, ibpd); + struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp); + struct mana_ib_dev *mdev = + container_of(ibpd->device, struct mana_ib_dev, ib_dev); + struct mana_ib_cq *send_cq = + container_of(attr->send_cq, struct mana_ib_cq, ibcq); + struct mana_ib_ucontext *mana_ucontext = + rdma_udata_to_drv_context(udata, struct mana_ib_ucontext, + ibucontext); + struct mana_ib_create_qp_resp resp = {}; + struct gdma_dev *gd = mdev->gdma_dev; + struct mana_ib_create_qp ucmd = {}; + struct mana_obj_spec wq_spec = {}; + struct mana_obj_spec cq_spec = {}; + struct mana_port_context *mpc; + struct mana_context *mc; + struct net_device *ndev; + struct ib_umem *umem; + int err; + u32 port; + + mc = gd->driver_data; + + if (!mana_ucontext || udata->inlen < sizeof(ucmd)) + return -EINVAL; + + err = ib_copy_from_udata(&ucmd, udata, min(sizeof(ucmd), udata->inlen)); + if (err) { + ibdev_dbg(&mdev->ib_dev, + "Failed to copy from udata create qp-raw, %d\n", err); + return err; + } + + /* IB ports start with 1, MANA Ethernet ports start with 0 */ + port = ucmd.port; + if (ucmd.port > mc->num_ports) + return -EINVAL; + + if (attr->cap.max_send_wr > MAX_SEND_BUFFERS_PER_QUEUE) { + ibdev_dbg(&mdev->ib_dev, + "Requested max_send_wr %d exceeding limit\n", + attr->cap.max_send_wr); + return -EINVAL; + } + + if (attr->cap.max_send_sge > MAX_TX_WQE_SGL_ENTRIES) { + ibdev_dbg(&mdev->ib_dev, + "Requested max_send_sge %d exceeding limit\n", + attr->cap.max_send_sge); + return -EINVAL; + } + + ndev = mc->ports[port - 1]; + mpc = netdev_priv(ndev); + ibdev_dbg(&mdev->ib_dev, "port %u ndev %p mpc %p\n", port, ndev, mpc); + + err = mana_ib_cfg_vport(mdev, port - 1, pd, mana_ucontext->doorbell); + if (err) + return -ENODEV; + + qp->port = port; + + ibdev_dbg(&mdev->ib_dev, "ucmd sq_buf_addr 0x%llx port %u\n", + ucmd.sq_buf_addr, ucmd.port); + + umem = ib_umem_get(ibpd->device, ucmd.sq_buf_addr, ucmd.sq_buf_size, + IB_ACCESS_LOCAL_WRITE); + if (IS_ERR(umem)) { + err = PTR_ERR(umem); + ibdev_dbg(&mdev->ib_dev, + "Failed to get umem for create qp-raw, err %d\n", + err); + goto err_free_vport; + } + qp->sq_umem = umem; + + err = mana_ib_gd_create_dma_region(mdev, qp->sq_umem, + &qp->sq_gdma_region); + if (err) { + ibdev_dbg(&mdev->ib_dev, + "Failed to create dma region for create qp-raw, %d\n", + err); + goto err_release_umem; + } + + ibdev_dbg(&mdev->ib_dev, + "mana_ib_gd_create_dma_region ret %d gdma_region 0x%llx\n", + err, qp->sq_gdma_region); + + /* Create a WQ on the same port handle used by the Ethernet */ + wq_spec.gdma_region = qp->sq_gdma_region; + wq_spec.queue_size = ucmd.sq_buf_size; + + cq_spec.gdma_region = send_cq->gdma_region; + cq_spec.queue_size = send_cq->cqe * COMP_ENTRY_SIZE; + cq_spec.modr_ctx_id = 0; + cq_spec.attached_eq = GDMA_CQ_NO_EQ; + + err = mana_create_wq_obj(mpc, mpc->port_handle, GDMA_SQ, &wq_spec, + &cq_spec, &qp->tx_object); + if (err) { + ibdev_dbg(&mdev->ib_dev, + "Failed to create wq for create raw-qp, err %d\n", + err); + goto err_destroy_dma_region; + } + + /* The GDMA regions are now owned by the WQ object */ + qp->sq_gdma_region = GDMA_INVALID_DMA_REGION; + send_cq->gdma_region = GDMA_INVALID_DMA_REGION; + + qp->sq_id = wq_spec.queue_index; + send_cq->id = cq_spec.queue_index; + + ibdev_dbg(&mdev->ib_dev, + "ret %d qp->tx_object 0x%llx sq id %llu cq id %llu\n", err, + qp->tx_object, qp->sq_id, send_cq->id); + + resp.sqid = qp->sq_id; + resp.cqid = send_cq->id; + resp.tx_vp_offset = pd->tx_vp_offset; + + err = ib_copy_to_udata(udata, &resp, sizeof(resp)); + if (err) { + ibdev_dbg(&mdev->ib_dev, + "Failed copy udata for create qp-raw, %d\n", + err); + goto err_destroy_wq_obj; + } + + return 0; + +err_destroy_wq_obj: + mana_destroy_wq_obj(mpc, GDMA_SQ, qp->tx_object); + +err_destroy_dma_region: + mana_ib_gd_destroy_dma_region(mdev, qp->sq_gdma_region); + +err_release_umem: + ib_umem_release(umem); + +err_free_vport: + mana_ib_uncfg_vport(mdev, pd, port - 1); + + return err; +} + +int mana_ib_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attr, + struct ib_udata *udata) +{ + switch (attr->qp_type) { + case IB_QPT_RAW_PACKET: + /* When rwq_ind_tbl is used, it's for creating WQs for RSS */ + if (attr->rwq_ind_tbl) + return mana_ib_create_qp_rss(ibqp, ibqp->pd, attr, + udata); + + return mana_ib_create_qp_raw(ibqp, ibqp->pd, attr, udata); + default: + /* Creating QP other than IB_QPT_RAW_PACKET is not supported */ + ibdev_dbg(ibqp->device, "Creating QP type %u not supported\n", + attr->qp_type); + } + + return -EINVAL; +} + +int mana_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, + int attr_mask, struct ib_udata *udata) +{ + /* modify_qp is not supported by this version of the driver */ + return -EOPNOTSUPP; +} + +static int mana_ib_destroy_qp_rss(struct mana_ib_qp *qp, + struct ib_rwq_ind_table *ind_tbl, + struct ib_udata *udata) +{ + struct mana_ib_dev *mdev = + container_of(qp->ibqp.device, struct mana_ib_dev, ib_dev); + struct gdma_dev *gd = mdev->gdma_dev; + struct mana_port_context *mpc; + struct mana_context *mc; + struct net_device *ndev; + struct mana_ib_wq *wq; + struct ib_wq *ibwq; + int i; + + mc = gd->driver_data; + ndev = mc->ports[qp->port - 1]; + mpc = netdev_priv(ndev); + + for (i = 0; i < (1 << ind_tbl->log_ind_tbl_size); i++) { + ibwq = ind_tbl->ind_tbl[i]; + wq = container_of(ibwq, struct mana_ib_wq, ibwq); + ibdev_dbg(&mdev->ib_dev, "destroying wq->rx_object %llu\n", + wq->rx_object); + mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object); + } + + return 0; +} + +static int mana_ib_destroy_qp_raw(struct mana_ib_qp *qp, struct ib_udata *udata) +{ + struct mana_ib_dev *mdev = + container_of(qp->ibqp.device, struct mana_ib_dev, ib_dev); + struct gdma_dev *gd = mdev->gdma_dev; + struct ib_pd *ibpd = qp->ibqp.pd; + struct mana_port_context *mpc; + struct mana_context *mc; + struct net_device *ndev; + struct mana_ib_pd *pd; + + mc = gd->driver_data; + ndev = mc->ports[qp->port - 1]; + mpc = netdev_priv(ndev); + pd = container_of(ibpd, struct mana_ib_pd, ibpd); + + mana_destroy_wq_obj(mpc, GDMA_SQ, qp->tx_object); + + if (qp->sq_umem) { + mana_ib_gd_destroy_dma_region(mdev, qp->sq_gdma_region); + ib_umem_release(qp->sq_umem); + } + + mana_ib_uncfg_vport(mdev, pd, qp->port - 1); + + return 0; +} + +int mana_ib_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) +{ + struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp); + + switch (ibqp->qp_type) { + case IB_QPT_RAW_PACKET: + if (ibqp->rwq_ind_tbl) + return mana_ib_destroy_qp_rss(qp, ibqp->rwq_ind_tbl, + udata); + + return mana_ib_destroy_qp_raw(qp, udata); + + default: + ibdev_dbg(ibqp->device, "Unexpected QP type %u\n", + ibqp->qp_type); + } + + return -ENOENT; +} diff --git a/drivers/infiniband/hw/mana/wq.c b/drivers/infiniband/hw/mana/wq.c new file mode 100644 index 000000000000..372d361510e0 --- /dev/null +++ b/drivers/infiniband/hw/mana/wq.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022, Microsoft Corporation. All rights reserved. + */ + +#include "mana_ib.h" + +struct ib_wq *mana_ib_create_wq(struct ib_pd *pd, + struct ib_wq_init_attr *init_attr, + struct ib_udata *udata) +{ + struct mana_ib_dev *mdev = + container_of(pd->device, struct mana_ib_dev, ib_dev); + struct mana_ib_create_wq ucmd = {}; + struct mana_ib_wq *wq; + struct ib_umem *umem; + int err; + + if (udata->inlen < sizeof(ucmd)) + return ERR_PTR(-EINVAL); + + err = ib_copy_from_udata(&ucmd, udata, min(sizeof(ucmd), udata->inlen)); + if (err) { + ibdev_dbg(&mdev->ib_dev, + "Failed to copy from udata for create wq, %d\n", err); + return ERR_PTR(err); + } + + wq = kzalloc(sizeof(*wq), GFP_KERNEL); + if (!wq) + return ERR_PTR(-ENOMEM); + + ibdev_dbg(&mdev->ib_dev, "ucmd wq_buf_addr 0x%llx\n", ucmd.wq_buf_addr); + + umem = ib_umem_get(pd->device, ucmd.wq_buf_addr, ucmd.wq_buf_size, + IB_ACCESS_LOCAL_WRITE); + if (IS_ERR(umem)) { + err = PTR_ERR(umem); + ibdev_dbg(&mdev->ib_dev, + "Failed to get umem for create wq, err %d\n", err); + goto err_free_wq; + } + + wq->umem = umem; + wq->wqe = init_attr->max_wr; + wq->wq_buf_size = ucmd.wq_buf_size; + wq->rx_object = INVALID_MANA_HANDLE; + + err = mana_ib_gd_create_dma_region(mdev, wq->umem, &wq->gdma_region); + if (err) { + ibdev_dbg(&mdev->ib_dev, + "Failed to create dma region for create wq, %d\n", + err); + goto err_release_umem; + } + + ibdev_dbg(&mdev->ib_dev, + "mana_ib_gd_create_dma_region ret %d gdma_region 0x%llx\n", + err, wq->gdma_region); + + /* WQ ID is returned at wq_create time, doesn't know the value yet */ + + return &wq->ibwq; + +err_release_umem: + ib_umem_release(umem); + +err_free_wq: + kfree(wq); + + return ERR_PTR(err); +} + +int mana_ib_modify_wq(struct ib_wq *wq, struct ib_wq_attr *wq_attr, + u32 wq_attr_mask, struct ib_udata *udata) +{ + /* modify_wq is not supported by this version of the driver */ + return -EOPNOTSUPP; +} + +int mana_ib_destroy_wq(struct ib_wq *ibwq, struct ib_udata *udata) +{ + struct mana_ib_wq *wq = container_of(ibwq, struct mana_ib_wq, ibwq); + struct ib_device *ib_dev = ibwq->device; + struct mana_ib_dev *mdev; + + mdev = container_of(ib_dev, struct mana_ib_dev, ib_dev); + + mana_ib_gd_destroy_dma_region(mdev, wq->gdma_region); + ib_umem_release(wq->umem); + + kfree(wq); + + return 0; +} + +int mana_ib_create_rwq_ind_table(struct ib_rwq_ind_table *ib_rwq_ind_table, + struct ib_rwq_ind_table_init_attr *init_attr, + struct ib_udata *udata) +{ + /* + * There is no additional data in ind_table to be maintained by this + * driver, do nothing + */ + return 0; +} + +int mana_ib_destroy_rwq_ind_table(struct ib_rwq_ind_table *ib_rwq_ind_tbl) +{ + /* + * There is no additional data in ind_table to be maintained by this + * driver, do nothing + */ + return 0; +} diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h index 713a8f8cca9a..20212ffeefb9 100644 --- a/include/net/mana/mana.h +++ b/include/net/mana/mana.h @@ -412,6 +412,9 @@ int mana_bpf(struct net_device *ndev, struct netdev_bpf *bpf); extern const struct ethtool_ops mana_ethtool_ops; +/* A CQ can be created not associated with any EQ */ +#define GDMA_CQ_NO_EQ 0xffff + struct mana_obj_spec { u32 queue_index; u64 gdma_region; diff --git a/include/uapi/rdma/ib_user_ioctl_verbs.h b/include/uapi/rdma/ib_user_ioctl_verbs.h index 7dd56210226f..e0c25537fd2e 100644 --- a/include/uapi/rdma/ib_user_ioctl_verbs.h +++ b/include/uapi/rdma/ib_user_ioctl_verbs.h @@ -251,6 +251,7 @@ enum rdma_driver_id { RDMA_DRIVER_EFA, RDMA_DRIVER_SIW, RDMA_DRIVER_ERDMA, + RDMA_DRIVER_MANA, }; enum ib_uverbs_gid_type { diff --git a/include/uapi/rdma/mana-abi.h b/include/uapi/rdma/mana-abi.h new file mode 100644 index 000000000000..5fcb31b37fb9 --- /dev/null +++ b/include/uapi/rdma/mana-abi.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) */ +/* + * Copyright (c) 2022, Microsoft Corporation. All rights reserved. + */ + +#ifndef MANA_ABI_USER_H +#define MANA_ABI_USER_H + +#include +#include + +/* + * Increment this value if any changes that break userspace ABI + * compatibility are made. + */ + +#define MANA_IB_UVERBS_ABI_VERSION 1 + +struct mana_ib_create_cq { + __aligned_u64 buf_addr; +}; + +struct mana_ib_create_qp { + __aligned_u64 sq_buf_addr; + __u32 sq_buf_size; + __u32 port; +}; + +struct mana_ib_create_qp_resp { + __u32 sqid; + __u32 cqid; + __u32 tx_vp_offset; + __u32 reserved; +}; + +struct mana_ib_create_wq { + __aligned_u64 wq_buf_addr; + __u32 wq_buf_size; + __u32 reserved; +}; + +/* RX Hash function flags */ +enum mana_ib_rx_hash_function_flags { + MANA_IB_RX_HASH_FUNC_TOEPLITZ = 1 << 0, +}; + +struct mana_ib_create_qp_rss { + __aligned_u64 rx_hash_fields_mask; + __u8 rx_hash_function; + __u8 reserved[7]; + __u32 rx_hash_key_len; + __u8 rx_hash_key[40]; + __u32 port; +}; + +struct rss_resp_entry { + __u32 cqid; + __u32 wqid; +}; + +struct mana_ib_create_qp_rss_resp { + __aligned_u64 num_entries; + struct rss_resp_entry entries[64]; +}; + +#endif -- cgit v1.2.3 From b82621ac8450d0de7570506c59f298dbf99ded64 Mon Sep 17 00:00:00 2001 From: Yinbo Zhu Date: Fri, 11 Nov 2022 13:42:00 +0800 Subject: soc: loongson: add GUTS driver for loongson-2 platforms The global utilities block controls PCIE device enabling, alternate function selection for multiplexed signals, consistency of HDA, USB and PCIE, configuration of memory controller, rtc controller, lio controller, and clock control. This patch adds a driver to manage and access global utilities block for LoongArch architecture Loongson-2 SoCs. Initially only reading SVR and registering soc device are supported. Other guts accesses, such as reading firmware configuration by default, should eventually be added into this driver as well. Signed-off-by: Yinbo Zhu Reviewed-by: Arnd Bergmann Signed-off-by: Arnd Bergmann --- MAINTAINERS | 6 ++ drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/loongson/Kconfig | 18 ++++ drivers/soc/loongson/Makefile | 6 ++ drivers/soc/loongson/loongson2_guts.c | 192 ++++++++++++++++++++++++++++++++++ 6 files changed, 224 insertions(+) create mode 100644 drivers/soc/loongson/Kconfig create mode 100644 drivers/soc/loongson/Makefile create mode 100644 drivers/soc/loongson/loongson2_guts.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index e04d944005ba..90772b2b896f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12008,6 +12008,12 @@ F: drivers/*/*loongarch* F: Documentation/loongarch/ F: Documentation/translations/zh_CN/loongarch/ +LOONGSON-2 SOC SERIES GUTS DRIVER +M: Yinbo Zhu +L: loongarch@lists.linux.dev +S: Maintained +F: drivers/soc/loongson/loongson2_guts.c + LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI) M: Sathya Prakash M: Sreekanth Reddy diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index e461c071189b..5dbb09f843f7 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -13,6 +13,7 @@ source "drivers/soc/fujitsu/Kconfig" source "drivers/soc/imx/Kconfig" source "drivers/soc/ixp4xx/Kconfig" source "drivers/soc/litex/Kconfig" +source "drivers/soc/loongson/Kconfig" source "drivers/soc/mediatek/Kconfig" source "drivers/soc/microchip/Kconfig" source "drivers/soc/pxa/Kconfig" diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 69ba6508cf2c..fff513bd522d 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -18,6 +18,7 @@ obj-y += imx/ obj-y += ixp4xx/ obj-$(CONFIG_SOC_XWAY) += lantiq/ obj-$(CONFIG_LITEX_SOC_CONTROLLER) += litex/ +obj-y += loongson/ obj-y += mediatek/ obj-y += microchip/ obj-y += pxa/ diff --git a/drivers/soc/loongson/Kconfig b/drivers/soc/loongson/Kconfig new file mode 100644 index 000000000000..707f56358dc4 --- /dev/null +++ b/drivers/soc/loongson/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Loongson-2 series SoC drivers +# + +config LOONGSON2_GUTS + tristate "Loongson-2 SoC Global UtiliTieS (GUTS) register block" + depends on LOONGARCH || COMPILE_TEST + select SOC_BUS + help + The global utilities block controls PCIE device enabling, alternate + function selection for multiplexed signals, consistency of HDA, USB + and PCIE, configuration of memory controller, rtc controller, lio + controller, and clock control. This patch adds a driver to manage + and access global utilities block for LoongArch architecture Loongson-2 + SoCs. Initially only reading SVR and registering soc device are + supported. Other guts accesses, such as reading firmware configuration + by default, should eventually be added into this driver as well. diff --git a/drivers/soc/loongson/Makefile b/drivers/soc/loongson/Makefile new file mode 100644 index 000000000000..263c486df638 --- /dev/null +++ b/drivers/soc/loongson/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Makefile for the Linux Kernel SoC Loongson-2 specific device drivers +# + +obj-$(CONFIG_LOONGSON2_GUTS) += loongson2_guts.o diff --git a/drivers/soc/loongson/loongson2_guts.c b/drivers/soc/loongson/loongson2_guts.c new file mode 100644 index 000000000000..bace4bc8e03b --- /dev/null +++ b/drivers/soc/loongson/loongson2_guts.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Author: Yinbo Zhu + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct soc_device_attribute soc_dev_attr; +static struct soc_device *soc_dev; + +/* + * Global Utility Registers. + * + * Not all registers defined in this structure are available on all chips, so + * you are expected to know whether a given register actually exists on your + * chip before you access it. + * + * Also, some registers are similar on different chips but have slightly + * different names. In these cases, one name is chosen to avoid extraneous + * #ifdefs. + */ +struct scfg_guts { + u32 svr; /* Version Register */ + u8 res0[4]; + u16 feature; /* Feature Register */ + u32 vendor; /* Vendor Register */ + u8 res1[6]; + u32 id; + u8 res2[0x3ff8 - 0x18]; + u32 chip; +}; + +static struct guts { + struct scfg_guts __iomem *regs; + bool little_endian; +} *guts; + +struct loongson2_soc_die_attr { + char *die; + u32 svr; + u32 mask; +}; + +/* SoC die attribute definition for Loongson-2 platform */ +static const struct loongson2_soc_die_attr loongson2_soc_die[] = { + + /* + * LoongArch-based SoCs Loongson-2 Series + */ + + /* Die: 2k1000, SoC: 2k1000 */ + { .die = "2K1000", + .svr = 0x00000013, + .mask = 0x000000ff, + }, + { }, +}; + +static const struct loongson2_soc_die_attr *loongson2_soc_die_match( + u32 svr, const struct loongson2_soc_die_attr *matches) +{ + while (matches->svr) { + if (matches->svr == (svr & matches->mask)) + return matches; + matches++; + }; + + return NULL; +} + +static u32 loongson2_guts_get_svr(void) +{ + u32 svr = 0; + + if (!guts || !guts->regs) + return svr; + + if (guts->little_endian) + svr = ioread32(&guts->regs->svr); + else + svr = ioread32be(&guts->regs->svr); + + return svr; +} + +static int loongson2_guts_probe(struct platform_device *pdev) +{ + struct device_node *root, *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct resource *res; + const struct loongson2_soc_die_attr *soc_die; + const char *machine; + u32 svr; + + /* Initialize guts */ + guts = devm_kzalloc(dev, sizeof(*guts), GFP_KERNEL); + if (!guts) + return -ENOMEM; + + guts->little_endian = of_property_read_bool(np, "little-endian"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + guts->regs = ioremap(res->start, res->end - res->start + 1); + if (IS_ERR(guts->regs)) + return PTR_ERR(guts->regs); + + /* Register soc device */ + root = of_find_node_by_path("/"); + if (of_property_read_string(root, "model", &machine)) + of_property_read_string_index(root, "compatible", 0, &machine); + of_node_put(root); + if (machine) + soc_dev_attr.machine = devm_kstrdup(dev, machine, GFP_KERNEL); + + svr = loongson2_guts_get_svr(); + soc_die = loongson2_soc_die_match(svr, loongson2_soc_die); + if (soc_die) { + soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL, + "Loongson %s", soc_die->die); + } else { + soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL, "Loongson"); + } + if (!soc_dev_attr.family) + return -ENOMEM; + soc_dev_attr.soc_id = devm_kasprintf(dev, GFP_KERNEL, + "svr:0x%08x", svr); + if (!soc_dev_attr.soc_id) + return -ENOMEM; + soc_dev_attr.revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d", + (svr >> 4) & 0xf, svr & 0xf); + if (!soc_dev_attr.revision) + return -ENOMEM; + + soc_dev = soc_device_register(&soc_dev_attr); + if (IS_ERR(soc_dev)) + return PTR_ERR(soc_dev); + + pr_info("Machine: %s\n", soc_dev_attr.machine); + pr_info("SoC family: %s\n", soc_dev_attr.family); + pr_info("SoC ID: %s, Revision: %s\n", + soc_dev_attr.soc_id, soc_dev_attr.revision); + + return 0; +} + +static int loongson2_guts_remove(struct platform_device *dev) +{ + soc_device_unregister(soc_dev); + + return 0; +} + +/* + * Table for matching compatible strings, for device tree + * guts node, for Loongson-2 SoCs. + */ +static const struct of_device_id loongson2_guts_of_match[] = { + { .compatible = "loongson,ls2k-chipid", }, + {} +}; +MODULE_DEVICE_TABLE(of, loongson2_guts_of_match); + +static struct platform_driver loongson2_guts_driver = { + .driver = { + .name = "loongson2-guts", + .of_match_table = loongson2_guts_of_match, + }, + .probe = loongson2_guts_probe, + .remove = loongson2_guts_remove, +}; + +static int __init loongson2_guts_init(void) +{ + return platform_driver_register(&loongson2_guts_driver); +} +core_initcall(loongson2_guts_init); + +static void __exit loongson2_guts_exit(void) +{ + platform_driver_unregister(&loongson2_guts_driver); +} +module_exit(loongson2_guts_exit); + +MODULE_DESCRIPTION("Loongson2 GUTS driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 06ebd23a33ec726b9c19ba05b4e0257811fce4bf Mon Sep 17 00:00:00 2001 From: Yinbo Zhu Date: Fri, 11 Nov 2022 13:42:01 +0800 Subject: dt-bindings: soc: add loongson-2 chipid Add the Loongson-2 SoC chipid binding with DT schema format using json-schema. Signed-off-by: Yinbo Zhu Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20221111054201.18528-2-zhuyinbo@loongson.cn' Signed-off-by: Arnd Bergmann --- .../bindings/hwinfo/loongson,ls2k-chipid.yaml | 38 ++++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 39 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwinfo/loongson,ls2k-chipid.yaml (limited to 'MAINTAINERS') diff --git a/Documentation/devicetree/bindings/hwinfo/loongson,ls2k-chipid.yaml b/Documentation/devicetree/bindings/hwinfo/loongson,ls2k-chipid.yaml new file mode 100644 index 000000000000..9d0c36ec1982 --- /dev/null +++ b/Documentation/devicetree/bindings/hwinfo/loongson,ls2k-chipid.yaml @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwinfo/loongson,ls2k-chipid.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Loongson-2 SoC ChipID + +maintainers: + - Yinbo Zhu + +description: | + Loongson-2 SoC contains many groups of global utilities register + blocks, of which the ChipID group registers record SoC version, + feature, vendor and id information. + +properties: + compatible: + const: loongson,ls2k-chipid + + reg: + maxItems: 1 + + little-endian: true + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + chipid: chipid@1fe00000 { + compatible = "loongson,ls2k-chipid"; + reg = <0x1fe00000 0x3ffc>; + little-endian; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 90772b2b896f..4fa3a1ef487c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12012,6 +12012,7 @@ LOONGSON-2 SOC SERIES GUTS DRIVER M: Yinbo Zhu L: loongarch@lists.linux.dev S: Maintained +F: Documentation/devicetree/bindings/hwinfo/loongson,ls2k-chipid.yaml F: drivers/soc/loongson/loongson2_guts.c LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI) -- cgit v1.2.3 From a77272c16041862c5c8d450c8701139887e1bb05 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 27 Sep 2022 22:45:21 +0200 Subject: platform/x86: dell: Add new dell-wmi-ddv driver The dell-wmi-ddv driver adds support for reading the current temperature and ePPID of ACPI batteries on supported Dell machines. Since the WMI interface used by this driver does not do any input validation and thus cannot be used for probing, the driver depends on the ACPI battery extension machanism to discover batteries. The driver also supports a debugfs interface for retrieving buffers containing fan and thermal sensor information. Since the meaing of the content of those buffers is currently unknown, the interface is meant for reverse-engineering and will likely be replaced with an hwmon interface once the meaning has been understood. The driver was tested on a Dell Inspiron 3505. Signed-off-by: Armin Wolf Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220927204521.601887-3-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- Documentation/ABI/testing/debugfs-dell-wmi-ddv | 21 ++ .../ABI/testing/sysfs-platform-dell-wmi-ddv | 7 + MAINTAINERS | 7 + drivers/platform/x86/dell/Kconfig | 13 + drivers/platform/x86/dell/Makefile | 1 + drivers/platform/x86/dell/dell-wmi-ddv.c | 364 +++++++++++++++++++++ drivers/platform/x86/wmi.c | 1 + 7 files changed, 414 insertions(+) create mode 100644 Documentation/ABI/testing/debugfs-dell-wmi-ddv create mode 100644 Documentation/ABI/testing/sysfs-platform-dell-wmi-ddv create mode 100644 drivers/platform/x86/dell/dell-wmi-ddv.c (limited to 'MAINTAINERS') diff --git a/Documentation/ABI/testing/debugfs-dell-wmi-ddv b/Documentation/ABI/testing/debugfs-dell-wmi-ddv new file mode 100644 index 000000000000..fbcc5d6f7388 --- /dev/null +++ b/Documentation/ABI/testing/debugfs-dell-wmi-ddv @@ -0,0 +1,21 @@ +What: /sys/kernel/debug/dell-wmi-ddv-/fan_sensor_information +Date: September 2022 +KernelVersion: 6.1 +Contact: Armin Wolf +Description: + This file contains the contents of the fan sensor information buffer, + which contains fan sensor entries and a terminating character (0xFF). + + Each fan sensor entry consists of three bytes with an unknown meaning, + interested people may use this file for reverse-engineering. + +What: /sys/kernel/debug/dell-wmi-ddv-/thermal_sensor_information +Date: September 2022 +KernelVersion: 6.1 +Contact: Armin Wolf +Description: + This file contains the contents of the thermal sensor information buffer, + which contains thermal sensor entries and a terminating character (0xFF). + + Each thermal sensor entry consists of five bytes with an unknown meaning, + interested people may use this file for reverse-engineering. diff --git a/Documentation/ABI/testing/sysfs-platform-dell-wmi-ddv b/Documentation/ABI/testing/sysfs-platform-dell-wmi-ddv new file mode 100644 index 000000000000..1d97ad615c66 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-dell-wmi-ddv @@ -0,0 +1,7 @@ +What: /sys/class/power_supply//eppid +Date: September 2022 +KernelVersion: 6.1 +Contact: Armin Wolf +Description: + Reports the Dell ePPID (electronic Dell Piece Part Identification) + of the ACPI battery. diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..f8fa72d1452e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5851,6 +5851,13 @@ L: Dell.Client.Kernel@dell.com S: Maintained F: drivers/platform/x86/dell/dell-wmi-descriptor.c +DELL WMI DDV DRIVER +M: Armin Wolf +S: Maintained +F: Documentation/ABI/testing/debugfs-dell-wmi-ddv +F: Documentation/ABI/testing/sysfs-platform-dell-wmi-ddv +F: drivers/platform/x86/dell/dell-wmi-ddv.c + DELL WMI SYSMAN DRIVER M: Divya Bharathi M: Prasanth Ksr diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig index 25421e061c47..d319de8f2132 100644 --- a/drivers/platform/x86/dell/Kconfig +++ b/drivers/platform/x86/dell/Kconfig @@ -189,6 +189,19 @@ config DELL_WMI_DESCRIPTOR default n depends on ACPI_WMI +config DELL_WMI_DDV + tristate "Dell WMI sensors Support" + default m + depends on ACPI_BATTERY + depends on ACPI_WMI + help + This option adds support for WMI-based sensors like + battery temperature sensors found on some Dell notebooks. + It also supports reading of the battery ePPID. + + To compile this drivers as a module, choose M here: the module will + be called dell-wmi-ddv. + config DELL_WMI_LED tristate "External LED on Dell Business Netbooks" default m diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile index ddba1df71e80..1b8942426622 100644 --- a/drivers/platform/x86/dell/Makefile +++ b/drivers/platform/x86/dell/Makefile @@ -19,5 +19,6 @@ dell-wmi-objs := dell-wmi-base.o dell-wmi-$(CONFIG_DELL_WMI_PRIVACY) += dell-wmi-privacy.o obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o +obj-$(CONFIG_DELL_WMI_DDV) += dell-wmi-ddv.o obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman/ diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c new file mode 100644 index 000000000000..699feae3c435 --- /dev/null +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux driver for WMI sensor information on Dell notebooks. + * + * Copyright (C) 2022 Armin Wolf + */ + +#define pr_format(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRIVER_NAME "dell-wmi-ddv" + +#define DELL_DDV_SUPPORTED_INTERFACE 2 +#define DELL_DDV_GUID "8A42EA14-4F2A-FD45-6422-0087F7A7E608" + +enum dell_ddv_method { + DELL_DDV_BATTERY_DESIGN_CAPACITY = 0x01, + DELL_DDV_BATTERY_FULL_CHARGE_CAPACITY = 0x02, + DELL_DDV_BATTERY_MANUFACTURE_NAME = 0x03, + DELL_DDV_BATTERY_MANUFACTURE_DATE = 0x04, + DELL_DDV_BATTERY_SERIAL_NUMBER = 0x05, + DELL_DDV_BATTERY_CHEMISTRY_VALUE = 0x06, + DELL_DDV_BATTERY_TEMPERATURE = 0x07, + DELL_DDV_BATTERY_CURRENT = 0x08, + DELL_DDV_BATTERY_VOLTAGE = 0x09, + DELL_DDV_BATTERY_MANUFACTURER_ACCESS = 0x0A, + DELL_DDV_BATTERY_RELATIVE_CHARGE_STATE = 0x0B, + DELL_DDV_BATTERY_CYCLE_COUNT = 0x0C, + DELL_DDV_BATTERY_EPPID = 0x0D, + DELL_DDV_BATTERY_RAW_ANALYTICS_START = 0x0E, + DELL_DDV_BATTERY_RAW_ANALYTICS = 0x0F, + DELL_DDV_BATTERY_DESIGN_VOLTAGE = 0x10, + + DELL_DDV_INTERFACE_VERSION = 0x12, + + DELL_DDV_FAN_SENSOR_INFORMATION = 0x20, + DELL_DDV_THERMAL_SENSOR_INFORMATION = 0x22, +}; + +struct dell_wmi_ddv_data { + struct acpi_battery_hook hook; + struct device_attribute temp_attr; + struct device_attribute eppid_attr; + struct wmi_device *wdev; +}; + +static int dell_wmi_ddv_query_type(struct wmi_device *wdev, enum dell_ddv_method method, u32 arg, + union acpi_object **result, acpi_object_type type) +{ + struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; + const struct acpi_buffer in = { + .length = sizeof(arg), + .pointer = &arg, + }; + union acpi_object *obj; + acpi_status ret; + + ret = wmidev_evaluate_method(wdev, 0x0, method, &in, &out); + if (ACPI_FAILURE(ret)) + return -EIO; + + obj = out.pointer; + if (!obj) + return -ENODATA; + + if (obj->type != type) { + kfree(obj); + return -EIO; + } + + *result = obj; + + return 0; +} + +static int dell_wmi_ddv_query_integer(struct wmi_device *wdev, enum dell_ddv_method method, + u32 arg, u32 *res) +{ + union acpi_object *obj; + int ret; + + ret = dell_wmi_ddv_query_type(wdev, method, arg, &obj, ACPI_TYPE_INTEGER); + if (ret < 0) + return ret; + + if (obj->integer.value <= U32_MAX) + *res = (u32)obj->integer.value; + else + ret = -ERANGE; + + kfree(obj); + + return ret; +} + +static int dell_wmi_ddv_query_buffer(struct wmi_device *wdev, enum dell_ddv_method method, + u32 arg, union acpi_object **result) +{ + union acpi_object *obj; + u64 buffer_size; + int ret; + + ret = dell_wmi_ddv_query_type(wdev, method, arg, &obj, ACPI_TYPE_PACKAGE); + if (ret < 0) + return ret; + + if (obj->package.count != 2) + goto err_free; + + if (obj->package.elements[0].type != ACPI_TYPE_INTEGER) + goto err_free; + + buffer_size = obj->package.elements[0].integer.value; + + if (obj->package.elements[1].type != ACPI_TYPE_BUFFER) + goto err_free; + + if (buffer_size != obj->package.elements[1].buffer.length) { + dev_warn(&wdev->dev, + FW_WARN "ACPI buffer size (%llu) does not match WMI buffer size (%d)\n", + buffer_size, obj->package.elements[1].buffer.length); + + goto err_free; + } + + *result = obj; + + return 0; + +err_free: + kfree(obj); + + return -EIO; +} + +static int dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_method method, + u32 arg, union acpi_object **result) +{ + return dell_wmi_ddv_query_type(wdev, method, arg, result, ACPI_TYPE_STRING); +} + +static int dell_wmi_ddv_battery_index(struct acpi_device *acpi_dev, u32 *index) +{ + const char *uid_str; + + uid_str = acpi_device_uid(acpi_dev); + if (!uid_str) + return -ENODEV; + + return kstrtou32(uid_str, 10, index); +} + +static ssize_t temp_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dell_wmi_ddv_data *data = container_of(attr, struct dell_wmi_ddv_data, temp_attr); + u32 index, value; + int ret; + + ret = dell_wmi_ddv_battery_index(to_acpi_device(dev->parent), &index); + if (ret < 0) + return ret; + + ret = dell_wmi_ddv_query_integer(data->wdev, DELL_DDV_BATTERY_TEMPERATURE, index, &value); + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%d\n", DIV_ROUND_CLOSEST(value, 10)); +} + +static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dell_wmi_ddv_data *data = container_of(attr, struct dell_wmi_ddv_data, eppid_attr); + union acpi_object *obj; + u32 index; + int ret; + + ret = dell_wmi_ddv_battery_index(to_acpi_device(dev->parent), &index); + if (ret < 0) + return ret; + + ret = dell_wmi_ddv_query_string(data->wdev, DELL_DDV_BATTERY_EPPID, index, &obj); + if (ret < 0) + return ret; + + ret = sysfs_emit(buf, "%s\n", obj->string.pointer); + + kfree(obj); + + return ret; +} + +static int dell_wmi_ddv_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook) +{ + struct dell_wmi_ddv_data *data = container_of(hook, struct dell_wmi_ddv_data, hook); + u32 index; + int ret; + + /* Return 0 instead of error to avoid being unloaded */ + ret = dell_wmi_ddv_battery_index(to_acpi_device(battery->dev.parent), &index); + if (ret < 0) + return 0; + + ret = device_create_file(&battery->dev, &data->temp_attr); + if (ret < 0) + return ret; + + ret = device_create_file(&battery->dev, &data->eppid_attr); + if (ret < 0) { + device_remove_file(&battery->dev, &data->temp_attr); + + return ret; + } + + return 0; +} + +static int dell_wmi_ddv_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook) +{ + struct dell_wmi_ddv_data *data = container_of(hook, struct dell_wmi_ddv_data, hook); + + device_remove_file(&battery->dev, &data->temp_attr); + device_remove_file(&battery->dev, &data->eppid_attr); + + return 0; +} + +static void dell_wmi_ddv_battery_remove(void *data) +{ + struct acpi_battery_hook *hook = data; + + battery_hook_unregister(hook); +} + +static int dell_wmi_ddv_battery_add(struct dell_wmi_ddv_data *data) +{ + data->hook.name = "Dell DDV Battery Extension"; + data->hook.add_battery = dell_wmi_ddv_add_battery; + data->hook.remove_battery = dell_wmi_ddv_remove_battery; + + sysfs_attr_init(&data->temp_attr.attr); + data->temp_attr.attr.name = "temp"; + data->temp_attr.attr.mode = 0444; + data->temp_attr.show = temp_show; + + sysfs_attr_init(&data->eppid_attr.attr); + data->eppid_attr.attr.name = "eppid"; + data->eppid_attr.attr.mode = 0444; + data->eppid_attr.show = eppid_show; + + battery_hook_register(&data->hook); + + return devm_add_action_or_reset(&data->wdev->dev, dell_wmi_ddv_battery_remove, &data->hook); +} + +static int dell_wmi_ddv_buffer_read(struct seq_file *seq, enum dell_ddv_method method) +{ + struct device *dev = seq->private; + struct dell_wmi_ddv_data *data = dev_get_drvdata(dev); + union acpi_object *obj; + union acpi_object buf; + int ret; + + ret = dell_wmi_ddv_query_buffer(data->wdev, method, 0, &obj); + if (ret < 0) + return ret; + + buf = obj->package.elements[1]; + ret = seq_write(seq, buf.buffer.pointer, buf.buffer.length); + kfree(obj); + + return ret; +} + +static int dell_wmi_ddv_fan_read(struct seq_file *seq, void *offset) +{ + return dell_wmi_ddv_buffer_read(seq, DELL_DDV_FAN_SENSOR_INFORMATION); +} + +static int dell_wmi_ddv_temp_read(struct seq_file *seq, void *offset) +{ + return dell_wmi_ddv_buffer_read(seq, DELL_DDV_THERMAL_SENSOR_INFORMATION); +} + +static void dell_wmi_ddv_debugfs_remove(void *data) +{ + struct dentry *entry = data; + + debugfs_remove(entry); +} + +static void dell_wmi_ddv_debugfs_init(struct wmi_device *wdev) +{ + struct dentry *entry; + char name[64]; + + scnprintf(name, ARRAY_SIZE(name), "%s-%s", DRIVER_NAME, dev_name(&wdev->dev)); + entry = debugfs_create_dir(name, NULL); + + debugfs_create_devm_seqfile(&wdev->dev, "fan_sensor_information", entry, + dell_wmi_ddv_fan_read); + debugfs_create_devm_seqfile(&wdev->dev, "thermal_sensor_information", entry, + dell_wmi_ddv_temp_read); + + devm_add_action_or_reset(&wdev->dev, dell_wmi_ddv_debugfs_remove, entry); +} + +static int dell_wmi_ddv_probe(struct wmi_device *wdev, const void *context) +{ + struct dell_wmi_ddv_data *data; + u32 version; + int ret; + + ret = dell_wmi_ddv_query_integer(wdev, DELL_DDV_INTERFACE_VERSION, 0, &version); + if (ret < 0) + return ret; + + dev_dbg(&wdev->dev, "WMI interface version: %d\n", version); + if (version != DELL_DDV_SUPPORTED_INTERFACE) + return -ENODEV; + + data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + dev_set_drvdata(&wdev->dev, data); + data->wdev = wdev; + + dell_wmi_ddv_debugfs_init(wdev); + + return dell_wmi_ddv_battery_add(data); +} + +static const struct wmi_device_id dell_wmi_ddv_id_table[] = { + { DELL_DDV_GUID, NULL }, + { } +}; +MODULE_DEVICE_TABLE(wmi, dell_wmi_ddv_id_table); + +static struct wmi_driver dell_wmi_ddv_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .id_table = dell_wmi_ddv_id_table, + .probe = dell_wmi_ddv_probe, +}; +module_wmi_driver(dell_wmi_ddv_driver); + +MODULE_AUTHOR("Armin Wolf "); +MODULE_DESCRIPTION("Dell WMI sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 223550a10d4d..5ffc00480aef 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -105,6 +105,7 @@ MODULE_DEVICE_TABLE(acpi, wmi_device_ids); /* allow duplicate GUIDs as these device drivers use struct wmi_driver */ static const char * const allow_duplicates[] = { "05901221-D566-11D1-B2F0-00A0C9062910", /* wmi-bmof */ + "8A42EA14-4F2A-FD45-6422-0087F7A7E608", /* dell-wmi-ddv */ NULL }; -- cgit v1.2.3 From 6e9b8992b122cb12688bd259fc99e67d1be234eb Mon Sep 17 00:00:00 2001 From: Jorge Lopez Date: Thu, 20 Oct 2022 15:10:28 -0500 Subject: platform/x86: Move existing HP drivers to a new hp subdir The purpose of this patch is to provide a central location where all HP related drivers are found. HP drivers will recide under drivers/platform/x86/hp directory. Introduce changes to Kconfig file to list all HP driver under "HP X86 Platform Specific Device Drivers" menu option. Additional changes include update MAINTAINERS file to indicate hp related drivers new path. Signed-off-by: Jorge Lopez Link: https://lore.kernel.org/r/20221020201033.12790-2-jorge.lopez2@hp.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- MAINTAINERS | 4 +- drivers/platform/x86/Kconfig | 43 +- drivers/platform/x86/Makefile | 4 +- drivers/platform/x86/hp-wmi.c | 1566 ---------------------------------- drivers/platform/x86/hp/Kconfig | 63 ++ drivers/platform/x86/hp/Makefile | 10 + drivers/platform/x86/hp/hp-wmi.c | 1566 ++++++++++++++++++++++++++++++++++ drivers/platform/x86/hp/hp_accel.c | 387 +++++++++ drivers/platform/x86/hp/tc1100-wmi.c | 265 ++++++ drivers/platform/x86/hp_accel.c | 387 --------- drivers/platform/x86/tc1100-wmi.c | 265 ------ 11 files changed, 2295 insertions(+), 2265 deletions(-) delete mode 100644 drivers/platform/x86/hp-wmi.c create mode 100644 drivers/platform/x86/hp/Kconfig create mode 100644 drivers/platform/x86/hp/Makefile create mode 100644 drivers/platform/x86/hp/hp-wmi.c create mode 100644 drivers/platform/x86/hp/hp_accel.c create mode 100644 drivers/platform/x86/hp/tc1100-wmi.c delete mode 100644 drivers/platform/x86/hp_accel.c delete mode 100644 drivers/platform/x86/tc1100-wmi.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index f8fa72d1452e..893c63e8beef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9366,7 +9366,7 @@ F: drivers/net/wireless/intersil/hostap/ HP COMPAQ TC1100 TABLET WMI EXTRAS DRIVER L: platform-driver-x86@vger.kernel.org S: Orphan -F: drivers/platform/x86/tc1100-wmi.c +F: drivers/platform/x86/hp/tc1100-wmi.c HPET: High Precision Event Timers driver M: Clemens Ladisch @@ -11856,7 +11856,7 @@ M: Eric Piel S: Maintained F: Documentation/misc-devices/lis3lv02d.rst F: drivers/misc/lis3lv02d/ -F: drivers/platform/x86/hp_accel.c +F: drivers/platform/x86/hp/hp_accel.c LIST KUNIT TEST M: David Gow diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index f5312f51de19..5692385e2d26 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -424,24 +424,7 @@ config GPD_POCKET_FAN of the CPU temperature. Say Y or M if the kernel may be used on a GPD pocket. -config HP_ACCEL - tristate "HP laptop accelerometer" - depends on INPUT && ACPI - depends on SERIO_I8042 - select SENSORS_LIS3LV02D - select NEW_LEDS - select LEDS_CLASS - help - This driver provides support for the "Mobile Data Protection System 3D" - or "3D DriveGuard" feature of HP laptops. On such systems the driver - should load automatically (via ACPI alias). - - Support for a led indicating disk protection will be provided as - hp::hddprotect. For more information on the feature, refer to - Documentation/misc-devices/lis3lv02d.rst. - - To compile this driver as a module, choose M here: the module will - be called hp_accel. +source "drivers/platform/x86/hp/Kconfig" config WIRELESS_HOTKEY tristate "Wireless hotkey button" @@ -455,30 +438,6 @@ config WIRELESS_HOTKEY To compile this driver as a module, choose M here: the module will be called wireless-hotkey. -config HP_WMI - tristate "HP WMI extras" - depends on ACPI_WMI - depends on INPUT - depends on RFKILL || RFKILL = n - select INPUT_SPARSEKMAP - select ACPI_PLATFORM_PROFILE - select HWMON - help - Say Y here if you want to support WMI-based hotkeys on HP laptops and - to read data from WMI such as docking or ambient light sensor state. - - To compile this driver as a module, choose M here: the module will - be called hp-wmi. - -config TC1100_WMI - tristate "HP Compaq TC1100 Tablet WMI Extras" - depends on !X86_64 - depends on ACPI - depends on ACPI_WMI - help - This is a driver for the WMI extensions (wireless and bluetooth power - control) of the HP Compaq TC1100 tablet. - config IBM_RTL tristate "Device driver to enable PRTL support" depends on PCI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 5a428caa654a..1d3d1b02541b 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -55,9 +55,7 @@ obj-$(CONFIG_FUJITSU_TABLET) += fujitsu-tablet.o obj-$(CONFIG_GPD_POCKET_FAN) += gpd-pocket-fan.o # Hewlett Packard -obj-$(CONFIG_HP_ACCEL) += hp_accel.o -obj-$(CONFIG_HP_WMI) += hp-wmi.o -obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o +obj-$(CONFIG_X86_PLATFORM_DRIVERS_HP) += hp/ # Hewlett Packard Enterprise obj-$(CONFIG_UV_SYSFS) += uv_sysfs.o diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c deleted file mode 100644 index 0a99058be813..000000000000 --- a/drivers/platform/x86/hp-wmi.c +++ /dev/null @@ -1,1566 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * HP WMI hotkeys - * - * Copyright (C) 2008 Red Hat - * Copyright (C) 2010, 2011 Anssi Hannula - * - * Portions based on wistron_btns.c: - * Copyright (C) 2005 Miloslav Trmac - * Copyright (C) 2005 Bernhard Rosenkraenzer - * Copyright (C) 2005 Dmitry Torokhov - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Matthew Garrett "); -MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); -MODULE_LICENSE("GPL"); - -MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C"); -MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); - -#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" -#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4" -#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95 -#define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required - -/* DMI board names of devices that should use the omen specific path for - * thermal profiles. - * This was obtained by taking a look in the windows omen command center - * app and parsing a json file that they use to figure out what capabilities - * the device should have. - * A device is considered an omen if the DisplayName in that list contains - * "OMEN", and it can use the thermal profile stuff if the "Feature" array - * contains "PerformanceControl". - */ -static const char * const omen_thermal_profile_boards[] = { - "84DA", "84DB", "84DC", "8574", "8575", "860A", "87B5", "8572", "8573", - "8600", "8601", "8602", "8605", "8606", "8607", "8746", "8747", "8749", - "874A", "8603", "8604", "8748", "886B", "886C", "878A", "878B", "878C", - "88C8", "88CB", "8786", "8787", "8788", "88D1", "88D2", "88F4", "88FD", - "88F5", "88F6", "88F7", "88FE", "88FF", "8900", "8901", "8902", "8912", - "8917", "8918", "8949", "894A", "89EB" -}; - -/* DMI Board names of Omen laptops that are specifically set to be thermal - * profile version 0 by the Omen Command Center app, regardless of what - * the get system design information WMI call returns - */ -static const char *const omen_thermal_profile_force_v0_boards[] = { - "8607", "8746", "8747", "8749", "874A", "8748" -}; - -enum hp_wmi_radio { - HPWMI_WIFI = 0x0, - HPWMI_BLUETOOTH = 0x1, - HPWMI_WWAN = 0x2, - HPWMI_GPS = 0x3, -}; - -enum hp_wmi_event_ids { - HPWMI_DOCK_EVENT = 0x01, - HPWMI_PARK_HDD = 0x02, - HPWMI_SMART_ADAPTER = 0x03, - HPWMI_BEZEL_BUTTON = 0x04, - HPWMI_WIRELESS = 0x05, - HPWMI_CPU_BATTERY_THROTTLE = 0x06, - HPWMI_LOCK_SWITCH = 0x07, - HPWMI_LID_SWITCH = 0x08, - HPWMI_SCREEN_ROTATION = 0x09, - HPWMI_COOLSENSE_SYSTEM_MOBILE = 0x0A, - HPWMI_COOLSENSE_SYSTEM_HOT = 0x0B, - HPWMI_PROXIMITY_SENSOR = 0x0C, - HPWMI_BACKLIT_KB_BRIGHTNESS = 0x0D, - HPWMI_PEAKSHIFT_PERIOD = 0x0F, - HPWMI_BATTERY_CHARGE_PERIOD = 0x10, - HPWMI_SANITIZATION_MODE = 0x17, - HPWMI_SMART_EXPERIENCE_APP = 0x21, -}; - -/* - * struct bios_args buffer is dynamically allocated. New WMI command types - * were introduced that exceeds 128-byte data size. Changes to handle - * the data size allocation scheme were kept in hp_wmi_perform_qurey function. - */ -struct bios_args { - u32 signature; - u32 command; - u32 commandtype; - u32 datasize; - u8 data[]; -}; - -enum hp_wmi_commandtype { - HPWMI_DISPLAY_QUERY = 0x01, - HPWMI_HDDTEMP_QUERY = 0x02, - HPWMI_ALS_QUERY = 0x03, - HPWMI_HARDWARE_QUERY = 0x04, - HPWMI_WIRELESS_QUERY = 0x05, - HPWMI_BATTERY_QUERY = 0x07, - HPWMI_BIOS_QUERY = 0x09, - HPWMI_FEATURE_QUERY = 0x0b, - HPWMI_HOTKEY_QUERY = 0x0c, - HPWMI_FEATURE2_QUERY = 0x0d, - HPWMI_WIRELESS2_QUERY = 0x1b, - HPWMI_POSTCODEERROR_QUERY = 0x2a, - HPWMI_SYSTEM_DEVICE_MODE = 0x40, - HPWMI_THERMAL_PROFILE_QUERY = 0x4c, -}; - -enum hp_wmi_gm_commandtype { - HPWMI_FAN_SPEED_GET_QUERY = 0x11, - HPWMI_SET_PERFORMANCE_MODE = 0x1A, - HPWMI_FAN_SPEED_MAX_GET_QUERY = 0x26, - HPWMI_FAN_SPEED_MAX_SET_QUERY = 0x27, - HPWMI_GET_SYSTEM_DESIGN_DATA = 0x28, -}; - -enum hp_wmi_command { - HPWMI_READ = 0x01, - HPWMI_WRITE = 0x02, - HPWMI_ODM = 0x03, - HPWMI_GM = 0x20008, -}; - -enum hp_wmi_hardware_mask { - HPWMI_DOCK_MASK = 0x01, - HPWMI_TABLET_MASK = 0x04, -}; - -struct bios_return { - u32 sigpass; - u32 return_code; -}; - -enum hp_return_value { - HPWMI_RET_WRONG_SIGNATURE = 0x02, - HPWMI_RET_UNKNOWN_COMMAND = 0x03, - HPWMI_RET_UNKNOWN_CMDTYPE = 0x04, - HPWMI_RET_INVALID_PARAMETERS = 0x05, -}; - -enum hp_wireless2_bits { - HPWMI_POWER_STATE = 0x01, - HPWMI_POWER_SOFT = 0x02, - HPWMI_POWER_BIOS = 0x04, - HPWMI_POWER_HARD = 0x08, - HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD, -}; - -enum hp_thermal_profile_omen_v0 { - HP_OMEN_V0_THERMAL_PROFILE_DEFAULT = 0x00, - HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE = 0x01, - HP_OMEN_V0_THERMAL_PROFILE_COOL = 0x02, -}; - -enum hp_thermal_profile_omen_v1 { - HP_OMEN_V1_THERMAL_PROFILE_DEFAULT = 0x30, - HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE = 0x31, - HP_OMEN_V1_THERMAL_PROFILE_COOL = 0x50, -}; - -enum hp_thermal_profile { - HP_THERMAL_PROFILE_PERFORMANCE = 0x00, - HP_THERMAL_PROFILE_DEFAULT = 0x01, - HP_THERMAL_PROFILE_COOL = 0x02, - HP_THERMAL_PROFILE_QUIET = 0x03, -}; - -#define IS_HWBLOCKED(x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW) -#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT) - -struct bios_rfkill2_device_state { - u8 radio_type; - u8 bus_type; - u16 vendor_id; - u16 product_id; - u16 subsys_vendor_id; - u16 subsys_product_id; - u8 rfkill_id; - u8 power; - u8 unknown[4]; -}; - -/* 7 devices fit into the 128 byte buffer */ -#define HPWMI_MAX_RFKILL2_DEVICES 7 - -struct bios_rfkill2_state { - u8 unknown[7]; - u8 count; - u8 pad[8]; - struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES]; -}; - -static const struct key_entry hp_wmi_keymap[] = { - { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, - { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, - { KE_KEY, 0x20e6, { KEY_PROG1 } }, - { KE_KEY, 0x20e8, { KEY_MEDIA } }, - { KE_KEY, 0x2142, { KEY_MEDIA } }, - { KE_KEY, 0x213b, { KEY_INFO } }, - { KE_KEY, 0x2169, { KEY_ROTATE_DISPLAY } }, - { KE_KEY, 0x216a, { KEY_SETUP } }, - { KE_KEY, 0x21a9, { KEY_TOUCHPAD_OFF } }, - { KE_KEY, 0x121a9, { KEY_TOUCHPAD_ON } }, - { KE_KEY, 0x231b, { KEY_HELP } }, - { KE_END, 0 } -}; - -static struct input_dev *hp_wmi_input_dev; -static struct platform_device *hp_wmi_platform_dev; -static struct platform_profile_handler platform_profile_handler; -static bool platform_profile_support; -static bool zero_insize_support; - -static struct rfkill *wifi_rfkill; -static struct rfkill *bluetooth_rfkill; -static struct rfkill *wwan_rfkill; - -struct rfkill2_device { - u8 id; - int num; - struct rfkill *rfkill; -}; - -static int rfkill2_count; -static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES]; - -/* - * Chassis Types values were obtained from SMBIOS reference - * specification version 3.00. A complete list of system enclosures - * and chassis types is available on Table 17. - */ -static const char * const tablet_chassis_types[] = { - "30", /* Tablet*/ - "31", /* Convertible */ - "32" /* Detachable */ -}; - -#define DEVICE_MODE_TABLET 0x06 - -/* map output size to the corresponding WMI method id */ -static inline int encode_outsize_for_pvsz(int outsize) -{ - if (outsize > 4096) - return -EINVAL; - if (outsize > 1024) - return 5; - if (outsize > 128) - return 4; - if (outsize > 4) - return 3; - if (outsize > 0) - return 2; - return 1; -} - -/* - * hp_wmi_perform_query - * - * query: The commandtype (enum hp_wmi_commandtype) - * write: The command (enum hp_wmi_command) - * buffer: Buffer used as input and/or output - * insize: Size of input buffer - * outsize: Size of output buffer - * - * returns zero on success - * an HP WMI query specific error code (which is positive) - * -EINVAL if the query was not successful at all - * -EINVAL if the output buffer size exceeds buffersize - * - * Note: The buffersize must at least be the maximum of the input and output - * size. E.g. Battery info query is defined to have 1 byte input - * and 128 byte output. The caller would do: - * buffer = kzalloc(128, GFP_KERNEL); - * ret = hp_wmi_perform_query(HPWMI_BATTERY_QUERY, HPWMI_READ, buffer, 1, 128) - */ -static int hp_wmi_perform_query(int query, enum hp_wmi_command command, - void *buffer, int insize, int outsize) -{ - struct acpi_buffer input, output = { ACPI_ALLOCATE_BUFFER, NULL }; - struct bios_return *bios_return; - union acpi_object *obj = NULL; - struct bios_args *args = NULL; - int mid, actual_insize, actual_outsize; - size_t bios_args_size; - int ret; - - mid = encode_outsize_for_pvsz(outsize); - if (WARN_ON(mid < 0)) - return mid; - - actual_insize = max(insize, 128); - bios_args_size = struct_size(args, data, actual_insize); - args = kmalloc(bios_args_size, GFP_KERNEL); - if (!args) - return -ENOMEM; - - input.length = bios_args_size; - input.pointer = args; - - args->signature = 0x55434553; - args->command = command; - args->commandtype = query; - args->datasize = insize; - memcpy(args->data, buffer, flex_array_size(args, data, insize)); - - ret = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output); - if (ret) - goto out_free; - - obj = output.pointer; - if (!obj) { - ret = -EINVAL; - goto out_free; - } - - if (obj->type != ACPI_TYPE_BUFFER) { - pr_warn("query 0x%x returned an invalid object 0x%x\n", query, ret); - ret = -EINVAL; - goto out_free; - } - - bios_return = (struct bios_return *)obj->buffer.pointer; - ret = bios_return->return_code; - - if (ret) { - if (ret != HPWMI_RET_UNKNOWN_COMMAND && - ret != HPWMI_RET_UNKNOWN_CMDTYPE) - pr_warn("query 0x%x returned error 0x%x\n", query, ret); - goto out_free; - } - - /* Ignore output data of zero size */ - if (!outsize) - goto out_free; - - actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return))); - memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize); - memset(buffer + actual_outsize, 0, outsize - actual_outsize); - -out_free: - kfree(obj); - kfree(args); - return ret; -} - -static int hp_wmi_get_fan_speed(int fan) -{ - u8 fsh, fsl; - char fan_data[4] = { fan, 0, 0, 0 }; - - int ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_GET_QUERY, HPWMI_GM, - &fan_data, sizeof(char), - sizeof(fan_data)); - - if (ret != 0) - return -EINVAL; - - fsh = fan_data[2]; - fsl = fan_data[3]; - - return (fsh << 8) | fsl; -} - -static int hp_wmi_read_int(int query) -{ - int val = 0, ret; - - ret = hp_wmi_perform_query(query, HPWMI_READ, &val, - zero_if_sup(val), sizeof(val)); - - if (ret) - return ret < 0 ? ret : -EINVAL; - - return val; -} - -static int hp_wmi_get_dock_state(void) -{ - int state = hp_wmi_read_int(HPWMI_HARDWARE_QUERY); - - if (state < 0) - return state; - - return !!(state & HPWMI_DOCK_MASK); -} - -static int hp_wmi_get_tablet_mode(void) -{ - char system_device_mode[4] = { 0 }; - const char *chassis_type; - bool tablet_found; - int ret; - - chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE); - if (!chassis_type) - return -ENODEV; - - tablet_found = match_string(tablet_chassis_types, - ARRAY_SIZE(tablet_chassis_types), - chassis_type) >= 0; - if (!tablet_found) - return -ENODEV; - - ret = hp_wmi_perform_query(HPWMI_SYSTEM_DEVICE_MODE, HPWMI_READ, - system_device_mode, zero_if_sup(system_device_mode), - sizeof(system_device_mode)); - if (ret < 0) - return ret; - - return system_device_mode[0] == DEVICE_MODE_TABLET; -} - -static int omen_thermal_profile_set(int mode) -{ - char buffer[2] = {0, mode}; - int ret; - - ret = hp_wmi_perform_query(HPWMI_SET_PERFORMANCE_MODE, HPWMI_GM, - &buffer, sizeof(buffer), 0); - - if (ret) - return ret < 0 ? ret : -EINVAL; - - return mode; -} - -static bool is_omen_thermal_profile(void) -{ - const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); - - if (!board_name) - return false; - - return match_string(omen_thermal_profile_boards, - ARRAY_SIZE(omen_thermal_profile_boards), - board_name) >= 0; -} - -static int omen_get_thermal_policy_version(void) -{ - unsigned char buffer[8] = { 0 }; - int ret; - - const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); - - if (board_name) { - int matches = match_string(omen_thermal_profile_force_v0_boards, - ARRAY_SIZE(omen_thermal_profile_force_v0_boards), - board_name); - if (matches >= 0) - return 0; - } - - ret = hp_wmi_perform_query(HPWMI_GET_SYSTEM_DESIGN_DATA, HPWMI_GM, - &buffer, sizeof(buffer), sizeof(buffer)); - - if (ret) - return ret < 0 ? ret : -EINVAL; - - return buffer[3]; -} - -static int omen_thermal_profile_get(void) -{ - u8 data; - - int ret = ec_read(HP_OMEN_EC_THERMAL_PROFILE_OFFSET, &data); - - if (ret) - return ret; - - return data; -} - -static int hp_wmi_fan_speed_max_set(int enabled) -{ - int ret; - - ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_SET_QUERY, HPWMI_GM, - &enabled, sizeof(enabled), 0); - - if (ret) - return ret < 0 ? ret : -EINVAL; - - return enabled; -} - -static int hp_wmi_fan_speed_max_get(void) -{ - int val = 0, ret; - - ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_GET_QUERY, HPWMI_GM, - &val, zero_if_sup(val), sizeof(val)); - - if (ret) - return ret < 0 ? ret : -EINVAL; - - return val; -} - -static int __init hp_wmi_bios_2008_later(void) -{ - int state = 0; - int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, HPWMI_READ, &state, - zero_if_sup(state), sizeof(state)); - if (!ret) - return 1; - - return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; -} - -static int __init hp_wmi_bios_2009_later(void) -{ - u8 state[128]; - int ret = hp_wmi_perform_query(HPWMI_FEATURE2_QUERY, HPWMI_READ, &state, - zero_if_sup(state), sizeof(state)); - if (!ret) - return 1; - - return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; -} - -static int __init hp_wmi_enable_hotkeys(void) -{ - int value = 0x6e; - int ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, HPWMI_WRITE, &value, - sizeof(value), 0); - - return ret <= 0 ? ret : -EINVAL; -} - -static int hp_wmi_set_block(void *data, bool blocked) -{ - enum hp_wmi_radio r = (enum hp_wmi_radio) data; - int query = BIT(r + 8) | ((!blocked) << r); - int ret; - - ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE, - &query, sizeof(query), 0); - - return ret <= 0 ? ret : -EINVAL; -} - -static const struct rfkill_ops hp_wmi_rfkill_ops = { - .set_block = hp_wmi_set_block, -}; - -static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) -{ - int mask = 0x200 << (r * 8); - - int wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); - - /* TBD: Pass error */ - WARN_ONCE(wireless < 0, "error executing HPWMI_WIRELESS_QUERY"); - - return !(wireless & mask); -} - -static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) -{ - int mask = 0x800 << (r * 8); - - int wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); - - /* TBD: Pass error */ - WARN_ONCE(wireless < 0, "error executing HPWMI_WIRELESS_QUERY"); - - return !(wireless & mask); -} - -static int hp_wmi_rfkill2_set_block(void *data, bool blocked) -{ - int rfkill_id = (int)(long)data; - char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked }; - int ret; - - ret = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_WRITE, - buffer, sizeof(buffer), 0); - - return ret <= 0 ? ret : -EINVAL; -} - -static const struct rfkill_ops hp_wmi_rfkill2_ops = { - .set_block = hp_wmi_rfkill2_set_block, -}; - -static int hp_wmi_rfkill2_refresh(void) -{ - struct bios_rfkill2_state state; - int err, i; - - err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state, - zero_if_sup(state), sizeof(state)); - if (err) - return err; - - for (i = 0; i < rfkill2_count; i++) { - int num = rfkill2[i].num; - struct bios_rfkill2_device_state *devstate; - - devstate = &state.device[num]; - - if (num >= state.count || - devstate->rfkill_id != rfkill2[i].id) { - pr_warn("power configuration of the wireless devices unexpectedly changed\n"); - continue; - } - - rfkill_set_states(rfkill2[i].rfkill, - IS_SWBLOCKED(devstate->power), - IS_HWBLOCKED(devstate->power)); - } - - return 0; -} - -static ssize_t display_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int value = hp_wmi_read_int(HPWMI_DISPLAY_QUERY); - - if (value < 0) - return value; - return sprintf(buf, "%d\n", value); -} - -static ssize_t hddtemp_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int value = hp_wmi_read_int(HPWMI_HDDTEMP_QUERY); - - if (value < 0) - return value; - return sprintf(buf, "%d\n", value); -} - -static ssize_t als_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int value = hp_wmi_read_int(HPWMI_ALS_QUERY); - - if (value < 0) - return value; - return sprintf(buf, "%d\n", value); -} - -static ssize_t dock_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int value = hp_wmi_get_dock_state(); - - if (value < 0) - return value; - return sprintf(buf, "%d\n", value); -} - -static ssize_t tablet_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int value = hp_wmi_get_tablet_mode(); - - if (value < 0) - return value; - return sprintf(buf, "%d\n", value); -} - -static ssize_t postcode_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - /* Get the POST error code of previous boot failure. */ - int value = hp_wmi_read_int(HPWMI_POSTCODEERROR_QUERY); - - if (value < 0) - return value; - return sprintf(buf, "0x%x\n", value); -} - -static ssize_t als_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - u32 tmp; - int ret; - - ret = kstrtou32(buf, 10, &tmp); - if (ret) - return ret; - - ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &tmp, - sizeof(tmp), 0); - if (ret) - return ret < 0 ? ret : -EINVAL; - - return count; -} - -static ssize_t postcode_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - u32 tmp = 1; - bool clear; - int ret; - - ret = kstrtobool(buf, &clear); - if (ret) - return ret; - - if (clear == false) - return -EINVAL; - - /* Clear the POST error code. It is kept until cleared. */ - ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, HPWMI_WRITE, &tmp, - sizeof(tmp), 0); - if (ret) - return ret < 0 ? ret : -EINVAL; - - return count; -} - -static DEVICE_ATTR_RO(display); -static DEVICE_ATTR_RO(hddtemp); -static DEVICE_ATTR_RW(als); -static DEVICE_ATTR_RO(dock); -static DEVICE_ATTR_RO(tablet); -static DEVICE_ATTR_RW(postcode); - -static struct attribute *hp_wmi_attrs[] = { - &dev_attr_display.attr, - &dev_attr_hddtemp.attr, - &dev_attr_als.attr, - &dev_attr_dock.attr, - &dev_attr_tablet.attr, - &dev_attr_postcode.attr, - NULL, -}; -ATTRIBUTE_GROUPS(hp_wmi); - -static void hp_wmi_notify(u32 value, void *context) -{ - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; - u32 event_id, event_data; - union acpi_object *obj; - acpi_status status; - u32 *location; - int key_code; - - status = wmi_get_event_data(value, &response); - if (status != AE_OK) { - pr_info("bad event status 0x%x\n", status); - return; - } - - obj = (union acpi_object *)response.pointer; - - if (!obj) - return; - if (obj->type != ACPI_TYPE_BUFFER) { - pr_info("Unknown response received %d\n", obj->type); - kfree(obj); - return; - } - - /* - * Depending on ACPI version the concatenation of id and event data - * inside _WED function will result in a 8 or 16 byte buffer. - */ - location = (u32 *)obj->buffer.pointer; - if (obj->buffer.length == 8) { - event_id = *location; - event_data = *(location + 1); - } else if (obj->buffer.length == 16) { - event_id = *location; - event_data = *(location + 2); - } else { - pr_info("Unknown buffer length %d\n", obj->buffer.length); - kfree(obj); - return; - } - kfree(obj); - - switch (event_id) { - case HPWMI_DOCK_EVENT: - if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit)) - input_report_switch(hp_wmi_input_dev, SW_DOCK, - hp_wmi_get_dock_state()); - if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit)) - input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, - hp_wmi_get_tablet_mode()); - input_sync(hp_wmi_input_dev); - break; - case HPWMI_PARK_HDD: - break; - case HPWMI_SMART_ADAPTER: - break; - case HPWMI_BEZEL_BUTTON: - key_code = hp_wmi_read_int(HPWMI_HOTKEY_QUERY); - if (key_code < 0) - break; - - if (!sparse_keymap_report_event(hp_wmi_input_dev, - key_code, 1, true)) - pr_info("Unknown key code - 0x%x\n", key_code); - break; - case HPWMI_WIRELESS: - if (rfkill2_count) { - hp_wmi_rfkill2_refresh(); - break; - } - - if (wifi_rfkill) - rfkill_set_states(wifi_rfkill, - hp_wmi_get_sw_state(HPWMI_WIFI), - hp_wmi_get_hw_state(HPWMI_WIFI)); - if (bluetooth_rfkill) - rfkill_set_states(bluetooth_rfkill, - hp_wmi_get_sw_state(HPWMI_BLUETOOTH), - hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); - if (wwan_rfkill) - rfkill_set_states(wwan_rfkill, - hp_wmi_get_sw_state(HPWMI_WWAN), - hp_wmi_get_hw_state(HPWMI_WWAN)); - break; - case HPWMI_CPU_BATTERY_THROTTLE: - pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n"); - break; - case HPWMI_LOCK_SWITCH: - break; - case HPWMI_LID_SWITCH: - break; - case HPWMI_SCREEN_ROTATION: - break; - case HPWMI_COOLSENSE_SYSTEM_MOBILE: - break; - case HPWMI_COOLSENSE_SYSTEM_HOT: - break; - case HPWMI_PROXIMITY_SENSOR: - break; - case HPWMI_BACKLIT_KB_BRIGHTNESS: - break; - case HPWMI_PEAKSHIFT_PERIOD: - break; - case HPWMI_BATTERY_CHARGE_PERIOD: - break; - case HPWMI_SANITIZATION_MODE: - break; - case HPWMI_SMART_EXPERIENCE_APP: - break; - default: - pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data); - break; - } -} - -static int __init hp_wmi_input_setup(void) -{ - acpi_status status; - int err, val; - - hp_wmi_input_dev = input_allocate_device(); - if (!hp_wmi_input_dev) - return -ENOMEM; - - hp_wmi_input_dev->name = "HP WMI hotkeys"; - hp_wmi_input_dev->phys = "wmi/input0"; - hp_wmi_input_dev->id.bustype = BUS_HOST; - - __set_bit(EV_SW, hp_wmi_input_dev->evbit); - - /* Dock */ - val = hp_wmi_get_dock_state(); - if (!(val < 0)) { - __set_bit(SW_DOCK, hp_wmi_input_dev->swbit); - input_report_switch(hp_wmi_input_dev, SW_DOCK, val); - } - - /* Tablet mode */ - val = hp_wmi_get_tablet_mode(); - if (!(val < 0)) { - __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); - input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, val); - } - - err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL); - if (err) - goto err_free_dev; - - /* Set initial hardware state */ - input_sync(hp_wmi_input_dev); - - if (!hp_wmi_bios_2009_later() && hp_wmi_bios_2008_later()) - hp_wmi_enable_hotkeys(); - - status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); - if (ACPI_FAILURE(status)) { - err = -EIO; - goto err_free_dev; - } - - err = input_register_device(hp_wmi_input_dev); - if (err) - goto err_uninstall_notifier; - - return 0; - - err_uninstall_notifier: - wmi_remove_notify_handler(HPWMI_EVENT_GUID); - err_free_dev: - input_free_device(hp_wmi_input_dev); - return err; -} - -static void hp_wmi_input_destroy(void) -{ - wmi_remove_notify_handler(HPWMI_EVENT_GUID); - input_unregister_device(hp_wmi_input_dev); -} - -static int __init hp_wmi_rfkill_setup(struct platform_device *device) -{ - int err, wireless; - - wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); - if (wireless < 0) - return wireless; - - err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE, &wireless, - sizeof(wireless), 0); - if (err) - return err; - - if (wireless & 0x1) { - wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, - RFKILL_TYPE_WLAN, - &hp_wmi_rfkill_ops, - (void *) HPWMI_WIFI); - if (!wifi_rfkill) - return -ENOMEM; - rfkill_init_sw_state(wifi_rfkill, - hp_wmi_get_sw_state(HPWMI_WIFI)); - rfkill_set_hw_state(wifi_rfkill, - hp_wmi_get_hw_state(HPWMI_WIFI)); - err = rfkill_register(wifi_rfkill); - if (err) - goto register_wifi_error; - } - - if (wireless & 0x2) { - bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev, - RFKILL_TYPE_BLUETOOTH, - &hp_wmi_rfkill_ops, - (void *) HPWMI_BLUETOOTH); - if (!bluetooth_rfkill) { - err = -ENOMEM; - goto register_bluetooth_error; - } - rfkill_init_sw_state(bluetooth_rfkill, - hp_wmi_get_sw_state(HPWMI_BLUETOOTH)); - rfkill_set_hw_state(bluetooth_rfkill, - hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); - err = rfkill_register(bluetooth_rfkill); - if (err) - goto register_bluetooth_error; - } - - if (wireless & 0x4) { - wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev, - RFKILL_TYPE_WWAN, - &hp_wmi_rfkill_ops, - (void *) HPWMI_WWAN); - if (!wwan_rfkill) { - err = -ENOMEM; - goto register_wwan_error; - } - rfkill_init_sw_state(wwan_rfkill, - hp_wmi_get_sw_state(HPWMI_WWAN)); - rfkill_set_hw_state(wwan_rfkill, - hp_wmi_get_hw_state(HPWMI_WWAN)); - err = rfkill_register(wwan_rfkill); - if (err) - goto register_wwan_error; - } - - return 0; - -register_wwan_error: - rfkill_destroy(wwan_rfkill); - wwan_rfkill = NULL; - if (bluetooth_rfkill) - rfkill_unregister(bluetooth_rfkill); -register_bluetooth_error: - rfkill_destroy(bluetooth_rfkill); - bluetooth_rfkill = NULL; - if (wifi_rfkill) - rfkill_unregister(wifi_rfkill); -register_wifi_error: - rfkill_destroy(wifi_rfkill); - wifi_rfkill = NULL; - return err; -} - -static int __init hp_wmi_rfkill2_setup(struct platform_device *device) -{ - struct bios_rfkill2_state state; - int err, i; - - err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state, - zero_if_sup(state), sizeof(state)); - if (err) - return err < 0 ? err : -EINVAL; - - if (state.count > HPWMI_MAX_RFKILL2_DEVICES) { - pr_warn("unable to parse 0x1b query output\n"); - return -EINVAL; - } - - for (i = 0; i < state.count; i++) { - struct rfkill *rfkill; - enum rfkill_type type; - char *name; - - switch (state.device[i].radio_type) { - case HPWMI_WIFI: - type = RFKILL_TYPE_WLAN; - name = "hp-wifi"; - break; - case HPWMI_BLUETOOTH: - type = RFKILL_TYPE_BLUETOOTH; - name = "hp-bluetooth"; - break; - case HPWMI_WWAN: - type = RFKILL_TYPE_WWAN; - name = "hp-wwan"; - break; - case HPWMI_GPS: - type = RFKILL_TYPE_GPS; - name = "hp-gps"; - break; - default: - pr_warn("unknown device type 0x%x\n", - state.device[i].radio_type); - continue; - } - - if (!state.device[i].vendor_id) { - pr_warn("zero device %d while %d reported\n", - i, state.count); - continue; - } - - rfkill = rfkill_alloc(name, &device->dev, type, - &hp_wmi_rfkill2_ops, (void *)(long)i); - if (!rfkill) { - err = -ENOMEM; - goto fail; - } - - rfkill2[rfkill2_count].id = state.device[i].rfkill_id; - rfkill2[rfkill2_count].num = i; - rfkill2[rfkill2_count].rfkill = rfkill; - - rfkill_init_sw_state(rfkill, - IS_SWBLOCKED(state.device[i].power)); - rfkill_set_hw_state(rfkill, - IS_HWBLOCKED(state.device[i].power)); - - if (!(state.device[i].power & HPWMI_POWER_BIOS)) - pr_info("device %s blocked by BIOS\n", name); - - err = rfkill_register(rfkill); - if (err) { - rfkill_destroy(rfkill); - goto fail; - } - - rfkill2_count++; - } - - return 0; -fail: - for (; rfkill2_count > 0; rfkill2_count--) { - rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill); - rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill); - } - return err; -} - -static int platform_profile_omen_get(struct platform_profile_handler *pprof, - enum platform_profile_option *profile) -{ - int tp; - - tp = omen_thermal_profile_get(); - if (tp < 0) - return tp; - - switch (tp) { - case HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE: - case HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE: - *profile = PLATFORM_PROFILE_PERFORMANCE; - break; - case HP_OMEN_V0_THERMAL_PROFILE_DEFAULT: - case HP_OMEN_V1_THERMAL_PROFILE_DEFAULT: - *profile = PLATFORM_PROFILE_BALANCED; - break; - case HP_OMEN_V0_THERMAL_PROFILE_COOL: - case HP_OMEN_V1_THERMAL_PROFILE_COOL: - *profile = PLATFORM_PROFILE_COOL; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int platform_profile_omen_set(struct platform_profile_handler *pprof, - enum platform_profile_option profile) -{ - int err, tp, tp_version; - - tp_version = omen_get_thermal_policy_version(); - - if (tp_version < 0 || tp_version > 1) - return -EOPNOTSUPP; - - switch (profile) { - case PLATFORM_PROFILE_PERFORMANCE: - if (tp_version == 0) - tp = HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE; - else - tp = HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE; - break; - case PLATFORM_PROFILE_BALANCED: - if (tp_version == 0) - tp = HP_OMEN_V0_THERMAL_PROFILE_DEFAULT; - else - tp = HP_OMEN_V1_THERMAL_PROFILE_DEFAULT; - break; - case PLATFORM_PROFILE_COOL: - if (tp_version == 0) - tp = HP_OMEN_V0_THERMAL_PROFILE_COOL; - else - tp = HP_OMEN_V1_THERMAL_PROFILE_COOL; - break; - default: - return -EOPNOTSUPP; - } - - err = omen_thermal_profile_set(tp); - if (err < 0) - return err; - - return 0; -} - -static int thermal_profile_get(void) -{ - return hp_wmi_read_int(HPWMI_THERMAL_PROFILE_QUERY); -} - -static int thermal_profile_set(int thermal_profile) -{ - return hp_wmi_perform_query(HPWMI_THERMAL_PROFILE_QUERY, HPWMI_WRITE, &thermal_profile, - sizeof(thermal_profile), 0); -} - -static int hp_wmi_platform_profile_get(struct platform_profile_handler *pprof, - enum platform_profile_option *profile) -{ - int tp; - - tp = thermal_profile_get(); - if (tp < 0) - return tp; - - switch (tp) { - case HP_THERMAL_PROFILE_PERFORMANCE: - *profile = PLATFORM_PROFILE_PERFORMANCE; - break; - case HP_THERMAL_PROFILE_DEFAULT: - *profile = PLATFORM_PROFILE_BALANCED; - break; - case HP_THERMAL_PROFILE_COOL: - *profile = PLATFORM_PROFILE_COOL; - break; - case HP_THERMAL_PROFILE_QUIET: - *profile = PLATFORM_PROFILE_QUIET; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int hp_wmi_platform_profile_set(struct platform_profile_handler *pprof, - enum platform_profile_option profile) -{ - int err, tp; - - switch (profile) { - case PLATFORM_PROFILE_PERFORMANCE: - tp = HP_THERMAL_PROFILE_PERFORMANCE; - break; - case PLATFORM_PROFILE_BALANCED: - tp = HP_THERMAL_PROFILE_DEFAULT; - break; - case PLATFORM_PROFILE_COOL: - tp = HP_THERMAL_PROFILE_COOL; - break; - case PLATFORM_PROFILE_QUIET: - tp = HP_THERMAL_PROFILE_QUIET; - break; - default: - return -EOPNOTSUPP; - } - - err = thermal_profile_set(tp); - if (err) - return err; - - return 0; -} - -static int thermal_profile_setup(void) -{ - int err, tp; - - if (is_omen_thermal_profile()) { - tp = omen_thermal_profile_get(); - if (tp < 0) - return tp; - - /* - * call thermal profile write command to ensure that the - * firmware correctly sets the OEM variables - */ - - err = omen_thermal_profile_set(tp); - if (err < 0) - return err; - - platform_profile_handler.profile_get = platform_profile_omen_get; - platform_profile_handler.profile_set = platform_profile_omen_set; - } else { - tp = thermal_profile_get(); - - if (tp < 0) - return tp; - - /* - * call thermal profile write command to ensure that the - * firmware correctly sets the OEM variables for the DPTF - */ - err = thermal_profile_set(tp); - if (err) - return err; - - platform_profile_handler.profile_get = hp_wmi_platform_profile_get; - platform_profile_handler.profile_set = hp_wmi_platform_profile_set; - - set_bit(PLATFORM_PROFILE_QUIET, platform_profile_handler.choices); - } - - set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices); - set_bit(PLATFORM_PROFILE_BALANCED, platform_profile_handler.choices); - set_bit(PLATFORM_PROFILE_PERFORMANCE, platform_profile_handler.choices); - - err = platform_profile_register(&platform_profile_handler); - if (err) - return err; - - platform_profile_support = true; - - return 0; -} - -static int hp_wmi_hwmon_init(void); - -static int __init hp_wmi_bios_setup(struct platform_device *device) -{ - int err; - /* clear detected rfkill devices */ - wifi_rfkill = NULL; - bluetooth_rfkill = NULL; - wwan_rfkill = NULL; - rfkill2_count = 0; - - /* - * In pre-2009 BIOS, command 1Bh return 0x4 to indicate that - * BIOS no longer controls the power for the wireless - * devices. All features supported by this command will no - * longer be supported. - */ - if (!hp_wmi_bios_2009_later()) { - if (hp_wmi_rfkill_setup(device)) - hp_wmi_rfkill2_setup(device); - } - - err = hp_wmi_hwmon_init(); - - if (err < 0) - return err; - - thermal_profile_setup(); - - return 0; -} - -static int __exit hp_wmi_bios_remove(struct platform_device *device) -{ - int i; - - for (i = 0; i < rfkill2_count; i++) { - rfkill_unregister(rfkill2[i].rfkill); - rfkill_destroy(rfkill2[i].rfkill); - } - - if (wifi_rfkill) { - rfkill_unregister(wifi_rfkill); - rfkill_destroy(wifi_rfkill); - } - if (bluetooth_rfkill) { - rfkill_unregister(bluetooth_rfkill); - rfkill_destroy(bluetooth_rfkill); - } - if (wwan_rfkill) { - rfkill_unregister(wwan_rfkill); - rfkill_destroy(wwan_rfkill); - } - - if (platform_profile_support) - platform_profile_remove(); - - return 0; -} - -static int hp_wmi_resume_handler(struct device *device) -{ - /* - * Hardware state may have changed while suspended, so trigger - * input events for the current state. As this is a switch, - * the input layer will only actually pass it on if the state - * changed. - */ - if (hp_wmi_input_dev) { - if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit)) - input_report_switch(hp_wmi_input_dev, SW_DOCK, - hp_wmi_get_dock_state()); - if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit)) - input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, - hp_wmi_get_tablet_mode()); - input_sync(hp_wmi_input_dev); - } - - if (rfkill2_count) - hp_wmi_rfkill2_refresh(); - - if (wifi_rfkill) - rfkill_set_states(wifi_rfkill, - hp_wmi_get_sw_state(HPWMI_WIFI), - hp_wmi_get_hw_state(HPWMI_WIFI)); - if (bluetooth_rfkill) - rfkill_set_states(bluetooth_rfkill, - hp_wmi_get_sw_state(HPWMI_BLUETOOTH), - hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); - if (wwan_rfkill) - rfkill_set_states(wwan_rfkill, - hp_wmi_get_sw_state(HPWMI_WWAN), - hp_wmi_get_hw_state(HPWMI_WWAN)); - - return 0; -} - -static const struct dev_pm_ops hp_wmi_pm_ops = { - .resume = hp_wmi_resume_handler, - .restore = hp_wmi_resume_handler, -}; - -static struct platform_driver hp_wmi_driver = { - .driver = { - .name = "hp-wmi", - .pm = &hp_wmi_pm_ops, - .dev_groups = hp_wmi_groups, - }, - .remove = __exit_p(hp_wmi_bios_remove), -}; - -static umode_t hp_wmi_hwmon_is_visible(const void *data, - enum hwmon_sensor_types type, - u32 attr, int channel) -{ - switch (type) { - case hwmon_pwm: - return 0644; - case hwmon_fan: - if (hp_wmi_get_fan_speed(channel) >= 0) - return 0444; - break; - default: - return 0; - } - - return 0; -} - -static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long *val) -{ - int ret; - - switch (type) { - case hwmon_fan: - ret = hp_wmi_get_fan_speed(channel); - - if (ret < 0) - return ret; - *val = ret; - return 0; - case hwmon_pwm: - switch (hp_wmi_fan_speed_max_get()) { - case 0: - /* 0 is automatic fan, which is 2 for hwmon */ - *val = 2; - return 0; - case 1: - /* 1 is max fan, which is 0 - * (no fan speed control) for hwmon - */ - *val = 0; - return 0; - default: - /* shouldn't happen */ - return -ENODATA; - } - default: - return -EINVAL; - } -} - -static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long val) -{ - switch (type) { - case hwmon_pwm: - switch (val) { - case 0: - /* 0 is no fan speed control (max), which is 1 for us */ - return hp_wmi_fan_speed_max_set(1); - case 2: - /* 2 is automatic speed control, which is 0 for us */ - return hp_wmi_fan_speed_max_set(0); - default: - /* we don't support manual fan speed control */ - return -EINVAL; - } - default: - return -EOPNOTSUPP; - } -} - -static const struct hwmon_channel_info *info[] = { - HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT), - HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE), - NULL -}; - -static const struct hwmon_ops ops = { - .is_visible = hp_wmi_hwmon_is_visible, - .read = hp_wmi_hwmon_read, - .write = hp_wmi_hwmon_write, -}; - -static const struct hwmon_chip_info chip_info = { - .ops = &ops, - .info = info, -}; - -static int hp_wmi_hwmon_init(void) -{ - struct device *dev = &hp_wmi_platform_dev->dev; - struct device *hwmon; - - hwmon = devm_hwmon_device_register_with_info(dev, "hp", &hp_wmi_driver, - &chip_info, NULL); - - if (IS_ERR(hwmon)) { - dev_err(dev, "Could not register hp hwmon device\n"); - return PTR_ERR(hwmon); - } - - return 0; -} - -static int __init hp_wmi_init(void) -{ - int event_capable = wmi_has_guid(HPWMI_EVENT_GUID); - int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); - int err, tmp = 0; - - if (!bios_capable && !event_capable) - return -ENODEV; - - if (hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, HPWMI_READ, &tmp, - sizeof(tmp), sizeof(tmp)) == HPWMI_RET_INVALID_PARAMETERS) - zero_insize_support = true; - - if (event_capable) { - err = hp_wmi_input_setup(); - if (err) - return err; - } - - if (bios_capable) { - hp_wmi_platform_dev = - platform_device_register_simple("hp-wmi", PLATFORM_DEVID_NONE, NULL, 0); - if (IS_ERR(hp_wmi_platform_dev)) { - err = PTR_ERR(hp_wmi_platform_dev); - goto err_destroy_input; - } - - err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup); - if (err) - goto err_unregister_device; - } - - return 0; - -err_unregister_device: - platform_device_unregister(hp_wmi_platform_dev); -err_destroy_input: - if (event_capable) - hp_wmi_input_destroy(); - - return err; -} -module_init(hp_wmi_init); - -static void __exit hp_wmi_exit(void) -{ - if (wmi_has_guid(HPWMI_EVENT_GUID)) - hp_wmi_input_destroy(); - - if (hp_wmi_platform_dev) { - platform_device_unregister(hp_wmi_platform_dev); - platform_driver_unregister(&hp_wmi_driver); - } -} -module_exit(hp_wmi_exit); diff --git a/drivers/platform/x86/hp/Kconfig b/drivers/platform/x86/hp/Kconfig new file mode 100644 index 000000000000..ae165955311c --- /dev/null +++ b/drivers/platform/x86/hp/Kconfig @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# X86 Platform Specific Drivers +# +menuconfig X86_PLATFORM_DRIVERS_HP + bool "HP X86 Platform Specific Device Drivers" + depends on X86_PLATFORM_DEVICES + help + Say Y here to get to see options for device drivers for various + HP x86 platforms, including vendor-specific laptop extension drivers. + This option alone does not add any kernel code. + + If you say N, all options in this submenu will be skipped and disabled. + +if X86_PLATFORM_DRIVERS_HP + +config HP_ACCEL + tristate "HP laptop accelerometer" + default m + depends on INPUT && ACPI + depends on SERIO_I8042 + select SENSORS_LIS3LV02D + select NEW_LEDS + select LEDS_CLASS + help + This driver provides support for the "Mobile Data Protection System 3D" + or "3D DriveGuard" feature of HP laptops. On such systems the driver + should load automatically (via ACPI alias). + + Support for a led indicating disk protection will be provided as + hp::hddprotect. For more information on the feature, refer to + Documentation/misc-devices/lis3lv02d.rst. + + To compile this driver as a module, choose M here: the module will + be called hp_accel. + +config HP_WMI + tristate "HP WMI extras" + default m + depends on ACPI_WMI + depends on INPUT + depends on RFKILL || RFKILL = n + select INPUT_SPARSEKMAP + select ACPI_PLATFORM_PROFILE + select HWMON + help + Say Y here if you want to support WMI-based hotkeys on HP laptops and + to read data from WMI such as docking or ambient light sensor state. + + To compile this driver as a module, choose M here: the module will + be called hp-wmi. + +config TC1100_WMI + tristate "HP Compaq TC1100 Tablet WMI Extras" + default m + depends on !X86_64 + depends on ACPI + depends on ACPI_WMI + help + This is a driver for the WMI extensions (wireless and bluetooth power + control) of the HP Compaq TC1100 tablet. + +endif # X86_PLATFORM_DRIVERS_HP diff --git a/drivers/platform/x86/hp/Makefile b/drivers/platform/x86/hp/Makefile new file mode 100644 index 000000000000..db1eed4cd7c7 --- /dev/null +++ b/drivers/platform/x86/hp/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for linux/drivers/platform/x86/hp +# HP x86 Platform-Specific Drivers +# + +# Hewlett Packard +obj-$(CONFIG_HP_ACCEL) += hp_accel.o +obj-$(CONFIG_HP_WMI) += hp-wmi.o +obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c new file mode 100644 index 000000000000..0a99058be813 --- /dev/null +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -0,0 +1,1566 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HP WMI hotkeys + * + * Copyright (C) 2008 Red Hat + * Copyright (C) 2010, 2011 Anssi Hannula + * + * Portions based on wistron_btns.c: + * Copyright (C) 2005 Miloslav Trmac + * Copyright (C) 2005 Bernhard Rosenkraenzer + * Copyright (C) 2005 Dmitry Torokhov + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Matthew Garrett "); +MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); +MODULE_LICENSE("GPL"); + +MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C"); +MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); + +#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" +#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4" +#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95 +#define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required + +/* DMI board names of devices that should use the omen specific path for + * thermal profiles. + * This was obtained by taking a look in the windows omen command center + * app and parsing a json file that they use to figure out what capabilities + * the device should have. + * A device is considered an omen if the DisplayName in that list contains + * "OMEN", and it can use the thermal profile stuff if the "Feature" array + * contains "PerformanceControl". + */ +static const char * const omen_thermal_profile_boards[] = { + "84DA", "84DB", "84DC", "8574", "8575", "860A", "87B5", "8572", "8573", + "8600", "8601", "8602", "8605", "8606", "8607", "8746", "8747", "8749", + "874A", "8603", "8604", "8748", "886B", "886C", "878A", "878B", "878C", + "88C8", "88CB", "8786", "8787", "8788", "88D1", "88D2", "88F4", "88FD", + "88F5", "88F6", "88F7", "88FE", "88FF", "8900", "8901", "8902", "8912", + "8917", "8918", "8949", "894A", "89EB" +}; + +/* DMI Board names of Omen laptops that are specifically set to be thermal + * profile version 0 by the Omen Command Center app, regardless of what + * the get system design information WMI call returns + */ +static const char *const omen_thermal_profile_force_v0_boards[] = { + "8607", "8746", "8747", "8749", "874A", "8748" +}; + +enum hp_wmi_radio { + HPWMI_WIFI = 0x0, + HPWMI_BLUETOOTH = 0x1, + HPWMI_WWAN = 0x2, + HPWMI_GPS = 0x3, +}; + +enum hp_wmi_event_ids { + HPWMI_DOCK_EVENT = 0x01, + HPWMI_PARK_HDD = 0x02, + HPWMI_SMART_ADAPTER = 0x03, + HPWMI_BEZEL_BUTTON = 0x04, + HPWMI_WIRELESS = 0x05, + HPWMI_CPU_BATTERY_THROTTLE = 0x06, + HPWMI_LOCK_SWITCH = 0x07, + HPWMI_LID_SWITCH = 0x08, + HPWMI_SCREEN_ROTATION = 0x09, + HPWMI_COOLSENSE_SYSTEM_MOBILE = 0x0A, + HPWMI_COOLSENSE_SYSTEM_HOT = 0x0B, + HPWMI_PROXIMITY_SENSOR = 0x0C, + HPWMI_BACKLIT_KB_BRIGHTNESS = 0x0D, + HPWMI_PEAKSHIFT_PERIOD = 0x0F, + HPWMI_BATTERY_CHARGE_PERIOD = 0x10, + HPWMI_SANITIZATION_MODE = 0x17, + HPWMI_SMART_EXPERIENCE_APP = 0x21, +}; + +/* + * struct bios_args buffer is dynamically allocated. New WMI command types + * were introduced that exceeds 128-byte data size. Changes to handle + * the data size allocation scheme were kept in hp_wmi_perform_qurey function. + */ +struct bios_args { + u32 signature; + u32 command; + u32 commandtype; + u32 datasize; + u8 data[]; +}; + +enum hp_wmi_commandtype { + HPWMI_DISPLAY_QUERY = 0x01, + HPWMI_HDDTEMP_QUERY = 0x02, + HPWMI_ALS_QUERY = 0x03, + HPWMI_HARDWARE_QUERY = 0x04, + HPWMI_WIRELESS_QUERY = 0x05, + HPWMI_BATTERY_QUERY = 0x07, + HPWMI_BIOS_QUERY = 0x09, + HPWMI_FEATURE_QUERY = 0x0b, + HPWMI_HOTKEY_QUERY = 0x0c, + HPWMI_FEATURE2_QUERY = 0x0d, + HPWMI_WIRELESS2_QUERY = 0x1b, + HPWMI_POSTCODEERROR_QUERY = 0x2a, + HPWMI_SYSTEM_DEVICE_MODE = 0x40, + HPWMI_THERMAL_PROFILE_QUERY = 0x4c, +}; + +enum hp_wmi_gm_commandtype { + HPWMI_FAN_SPEED_GET_QUERY = 0x11, + HPWMI_SET_PERFORMANCE_MODE = 0x1A, + HPWMI_FAN_SPEED_MAX_GET_QUERY = 0x26, + HPWMI_FAN_SPEED_MAX_SET_QUERY = 0x27, + HPWMI_GET_SYSTEM_DESIGN_DATA = 0x28, +}; + +enum hp_wmi_command { + HPWMI_READ = 0x01, + HPWMI_WRITE = 0x02, + HPWMI_ODM = 0x03, + HPWMI_GM = 0x20008, +}; + +enum hp_wmi_hardware_mask { + HPWMI_DOCK_MASK = 0x01, + HPWMI_TABLET_MASK = 0x04, +}; + +struct bios_return { + u32 sigpass; + u32 return_code; +}; + +enum hp_return_value { + HPWMI_RET_WRONG_SIGNATURE = 0x02, + HPWMI_RET_UNKNOWN_COMMAND = 0x03, + HPWMI_RET_UNKNOWN_CMDTYPE = 0x04, + HPWMI_RET_INVALID_PARAMETERS = 0x05, +}; + +enum hp_wireless2_bits { + HPWMI_POWER_STATE = 0x01, + HPWMI_POWER_SOFT = 0x02, + HPWMI_POWER_BIOS = 0x04, + HPWMI_POWER_HARD = 0x08, + HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD, +}; + +enum hp_thermal_profile_omen_v0 { + HP_OMEN_V0_THERMAL_PROFILE_DEFAULT = 0x00, + HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE = 0x01, + HP_OMEN_V0_THERMAL_PROFILE_COOL = 0x02, +}; + +enum hp_thermal_profile_omen_v1 { + HP_OMEN_V1_THERMAL_PROFILE_DEFAULT = 0x30, + HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE = 0x31, + HP_OMEN_V1_THERMAL_PROFILE_COOL = 0x50, +}; + +enum hp_thermal_profile { + HP_THERMAL_PROFILE_PERFORMANCE = 0x00, + HP_THERMAL_PROFILE_DEFAULT = 0x01, + HP_THERMAL_PROFILE_COOL = 0x02, + HP_THERMAL_PROFILE_QUIET = 0x03, +}; + +#define IS_HWBLOCKED(x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW) +#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT) + +struct bios_rfkill2_device_state { + u8 radio_type; + u8 bus_type; + u16 vendor_id; + u16 product_id; + u16 subsys_vendor_id; + u16 subsys_product_id; + u8 rfkill_id; + u8 power; + u8 unknown[4]; +}; + +/* 7 devices fit into the 128 byte buffer */ +#define HPWMI_MAX_RFKILL2_DEVICES 7 + +struct bios_rfkill2_state { + u8 unknown[7]; + u8 count; + u8 pad[8]; + struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES]; +}; + +static const struct key_entry hp_wmi_keymap[] = { + { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, + { KE_KEY, 0x20e6, { KEY_PROG1 } }, + { KE_KEY, 0x20e8, { KEY_MEDIA } }, + { KE_KEY, 0x2142, { KEY_MEDIA } }, + { KE_KEY, 0x213b, { KEY_INFO } }, + { KE_KEY, 0x2169, { KEY_ROTATE_DISPLAY } }, + { KE_KEY, 0x216a, { KEY_SETUP } }, + { KE_KEY, 0x21a9, { KEY_TOUCHPAD_OFF } }, + { KE_KEY, 0x121a9, { KEY_TOUCHPAD_ON } }, + { KE_KEY, 0x231b, { KEY_HELP } }, + { KE_END, 0 } +}; + +static struct input_dev *hp_wmi_input_dev; +static struct platform_device *hp_wmi_platform_dev; +static struct platform_profile_handler platform_profile_handler; +static bool platform_profile_support; +static bool zero_insize_support; + +static struct rfkill *wifi_rfkill; +static struct rfkill *bluetooth_rfkill; +static struct rfkill *wwan_rfkill; + +struct rfkill2_device { + u8 id; + int num; + struct rfkill *rfkill; +}; + +static int rfkill2_count; +static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES]; + +/* + * Chassis Types values were obtained from SMBIOS reference + * specification version 3.00. A complete list of system enclosures + * and chassis types is available on Table 17. + */ +static const char * const tablet_chassis_types[] = { + "30", /* Tablet*/ + "31", /* Convertible */ + "32" /* Detachable */ +}; + +#define DEVICE_MODE_TABLET 0x06 + +/* map output size to the corresponding WMI method id */ +static inline int encode_outsize_for_pvsz(int outsize) +{ + if (outsize > 4096) + return -EINVAL; + if (outsize > 1024) + return 5; + if (outsize > 128) + return 4; + if (outsize > 4) + return 3; + if (outsize > 0) + return 2; + return 1; +} + +/* + * hp_wmi_perform_query + * + * query: The commandtype (enum hp_wmi_commandtype) + * write: The command (enum hp_wmi_command) + * buffer: Buffer used as input and/or output + * insize: Size of input buffer + * outsize: Size of output buffer + * + * returns zero on success + * an HP WMI query specific error code (which is positive) + * -EINVAL if the query was not successful at all + * -EINVAL if the output buffer size exceeds buffersize + * + * Note: The buffersize must at least be the maximum of the input and output + * size. E.g. Battery info query is defined to have 1 byte input + * and 128 byte output. The caller would do: + * buffer = kzalloc(128, GFP_KERNEL); + * ret = hp_wmi_perform_query(HPWMI_BATTERY_QUERY, HPWMI_READ, buffer, 1, 128) + */ +static int hp_wmi_perform_query(int query, enum hp_wmi_command command, + void *buffer, int insize, int outsize) +{ + struct acpi_buffer input, output = { ACPI_ALLOCATE_BUFFER, NULL }; + struct bios_return *bios_return; + union acpi_object *obj = NULL; + struct bios_args *args = NULL; + int mid, actual_insize, actual_outsize; + size_t bios_args_size; + int ret; + + mid = encode_outsize_for_pvsz(outsize); + if (WARN_ON(mid < 0)) + return mid; + + actual_insize = max(insize, 128); + bios_args_size = struct_size(args, data, actual_insize); + args = kmalloc(bios_args_size, GFP_KERNEL); + if (!args) + return -ENOMEM; + + input.length = bios_args_size; + input.pointer = args; + + args->signature = 0x55434553; + args->command = command; + args->commandtype = query; + args->datasize = insize; + memcpy(args->data, buffer, flex_array_size(args, data, insize)); + + ret = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output); + if (ret) + goto out_free; + + obj = output.pointer; + if (!obj) { + ret = -EINVAL; + goto out_free; + } + + if (obj->type != ACPI_TYPE_BUFFER) { + pr_warn("query 0x%x returned an invalid object 0x%x\n", query, ret); + ret = -EINVAL; + goto out_free; + } + + bios_return = (struct bios_return *)obj->buffer.pointer; + ret = bios_return->return_code; + + if (ret) { + if (ret != HPWMI_RET_UNKNOWN_COMMAND && + ret != HPWMI_RET_UNKNOWN_CMDTYPE) + pr_warn("query 0x%x returned error 0x%x\n", query, ret); + goto out_free; + } + + /* Ignore output data of zero size */ + if (!outsize) + goto out_free; + + actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return))); + memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize); + memset(buffer + actual_outsize, 0, outsize - actual_outsize); + +out_free: + kfree(obj); + kfree(args); + return ret; +} + +static int hp_wmi_get_fan_speed(int fan) +{ + u8 fsh, fsl; + char fan_data[4] = { fan, 0, 0, 0 }; + + int ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_GET_QUERY, HPWMI_GM, + &fan_data, sizeof(char), + sizeof(fan_data)); + + if (ret != 0) + return -EINVAL; + + fsh = fan_data[2]; + fsl = fan_data[3]; + + return (fsh << 8) | fsl; +} + +static int hp_wmi_read_int(int query) +{ + int val = 0, ret; + + ret = hp_wmi_perform_query(query, HPWMI_READ, &val, + zero_if_sup(val), sizeof(val)); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return val; +} + +static int hp_wmi_get_dock_state(void) +{ + int state = hp_wmi_read_int(HPWMI_HARDWARE_QUERY); + + if (state < 0) + return state; + + return !!(state & HPWMI_DOCK_MASK); +} + +static int hp_wmi_get_tablet_mode(void) +{ + char system_device_mode[4] = { 0 }; + const char *chassis_type; + bool tablet_found; + int ret; + + chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE); + if (!chassis_type) + return -ENODEV; + + tablet_found = match_string(tablet_chassis_types, + ARRAY_SIZE(tablet_chassis_types), + chassis_type) >= 0; + if (!tablet_found) + return -ENODEV; + + ret = hp_wmi_perform_query(HPWMI_SYSTEM_DEVICE_MODE, HPWMI_READ, + system_device_mode, zero_if_sup(system_device_mode), + sizeof(system_device_mode)); + if (ret < 0) + return ret; + + return system_device_mode[0] == DEVICE_MODE_TABLET; +} + +static int omen_thermal_profile_set(int mode) +{ + char buffer[2] = {0, mode}; + int ret; + + ret = hp_wmi_perform_query(HPWMI_SET_PERFORMANCE_MODE, HPWMI_GM, + &buffer, sizeof(buffer), 0); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return mode; +} + +static bool is_omen_thermal_profile(void) +{ + const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); + + if (!board_name) + return false; + + return match_string(omen_thermal_profile_boards, + ARRAY_SIZE(omen_thermal_profile_boards), + board_name) >= 0; +} + +static int omen_get_thermal_policy_version(void) +{ + unsigned char buffer[8] = { 0 }; + int ret; + + const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); + + if (board_name) { + int matches = match_string(omen_thermal_profile_force_v0_boards, + ARRAY_SIZE(omen_thermal_profile_force_v0_boards), + board_name); + if (matches >= 0) + return 0; + } + + ret = hp_wmi_perform_query(HPWMI_GET_SYSTEM_DESIGN_DATA, HPWMI_GM, + &buffer, sizeof(buffer), sizeof(buffer)); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return buffer[3]; +} + +static int omen_thermal_profile_get(void) +{ + u8 data; + + int ret = ec_read(HP_OMEN_EC_THERMAL_PROFILE_OFFSET, &data); + + if (ret) + return ret; + + return data; +} + +static int hp_wmi_fan_speed_max_set(int enabled) +{ + int ret; + + ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_SET_QUERY, HPWMI_GM, + &enabled, sizeof(enabled), 0); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return enabled; +} + +static int hp_wmi_fan_speed_max_get(void) +{ + int val = 0, ret; + + ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_GET_QUERY, HPWMI_GM, + &val, zero_if_sup(val), sizeof(val)); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return val; +} + +static int __init hp_wmi_bios_2008_later(void) +{ + int state = 0; + int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, HPWMI_READ, &state, + zero_if_sup(state), sizeof(state)); + if (!ret) + return 1; + + return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; +} + +static int __init hp_wmi_bios_2009_later(void) +{ + u8 state[128]; + int ret = hp_wmi_perform_query(HPWMI_FEATURE2_QUERY, HPWMI_READ, &state, + zero_if_sup(state), sizeof(state)); + if (!ret) + return 1; + + return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; +} + +static int __init hp_wmi_enable_hotkeys(void) +{ + int value = 0x6e; + int ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, HPWMI_WRITE, &value, + sizeof(value), 0); + + return ret <= 0 ? ret : -EINVAL; +} + +static int hp_wmi_set_block(void *data, bool blocked) +{ + enum hp_wmi_radio r = (enum hp_wmi_radio) data; + int query = BIT(r + 8) | ((!blocked) << r); + int ret; + + ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE, + &query, sizeof(query), 0); + + return ret <= 0 ? ret : -EINVAL; +} + +static const struct rfkill_ops hp_wmi_rfkill_ops = { + .set_block = hp_wmi_set_block, +}; + +static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) +{ + int mask = 0x200 << (r * 8); + + int wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); + + /* TBD: Pass error */ + WARN_ONCE(wireless < 0, "error executing HPWMI_WIRELESS_QUERY"); + + return !(wireless & mask); +} + +static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) +{ + int mask = 0x800 << (r * 8); + + int wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); + + /* TBD: Pass error */ + WARN_ONCE(wireless < 0, "error executing HPWMI_WIRELESS_QUERY"); + + return !(wireless & mask); +} + +static int hp_wmi_rfkill2_set_block(void *data, bool blocked) +{ + int rfkill_id = (int)(long)data; + char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked }; + int ret; + + ret = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_WRITE, + buffer, sizeof(buffer), 0); + + return ret <= 0 ? ret : -EINVAL; +} + +static const struct rfkill_ops hp_wmi_rfkill2_ops = { + .set_block = hp_wmi_rfkill2_set_block, +}; + +static int hp_wmi_rfkill2_refresh(void) +{ + struct bios_rfkill2_state state; + int err, i; + + err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state, + zero_if_sup(state), sizeof(state)); + if (err) + return err; + + for (i = 0; i < rfkill2_count; i++) { + int num = rfkill2[i].num; + struct bios_rfkill2_device_state *devstate; + + devstate = &state.device[num]; + + if (num >= state.count || + devstate->rfkill_id != rfkill2[i].id) { + pr_warn("power configuration of the wireless devices unexpectedly changed\n"); + continue; + } + + rfkill_set_states(rfkill2[i].rfkill, + IS_SWBLOCKED(devstate->power), + IS_HWBLOCKED(devstate->power)); + } + + return 0; +} + +static ssize_t display_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_read_int(HPWMI_DISPLAY_QUERY); + + if (value < 0) + return value; + return sprintf(buf, "%d\n", value); +} + +static ssize_t hddtemp_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_read_int(HPWMI_HDDTEMP_QUERY); + + if (value < 0) + return value; + return sprintf(buf, "%d\n", value); +} + +static ssize_t als_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_read_int(HPWMI_ALS_QUERY); + + if (value < 0) + return value; + return sprintf(buf, "%d\n", value); +} + +static ssize_t dock_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_get_dock_state(); + + if (value < 0) + return value; + return sprintf(buf, "%d\n", value); +} + +static ssize_t tablet_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_get_tablet_mode(); + + if (value < 0) + return value; + return sprintf(buf, "%d\n", value); +} + +static ssize_t postcode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + /* Get the POST error code of previous boot failure. */ + int value = hp_wmi_read_int(HPWMI_POSTCODEERROR_QUERY); + + if (value < 0) + return value; + return sprintf(buf, "0x%x\n", value); +} + +static ssize_t als_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 tmp; + int ret; + + ret = kstrtou32(buf, 10, &tmp); + if (ret) + return ret; + + ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &tmp, + sizeof(tmp), 0); + if (ret) + return ret < 0 ? ret : -EINVAL; + + return count; +} + +static ssize_t postcode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 tmp = 1; + bool clear; + int ret; + + ret = kstrtobool(buf, &clear); + if (ret) + return ret; + + if (clear == false) + return -EINVAL; + + /* Clear the POST error code. It is kept until cleared. */ + ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, HPWMI_WRITE, &tmp, + sizeof(tmp), 0); + if (ret) + return ret < 0 ? ret : -EINVAL; + + return count; +} + +static DEVICE_ATTR_RO(display); +static DEVICE_ATTR_RO(hddtemp); +static DEVICE_ATTR_RW(als); +static DEVICE_ATTR_RO(dock); +static DEVICE_ATTR_RO(tablet); +static DEVICE_ATTR_RW(postcode); + +static struct attribute *hp_wmi_attrs[] = { + &dev_attr_display.attr, + &dev_attr_hddtemp.attr, + &dev_attr_als.attr, + &dev_attr_dock.attr, + &dev_attr_tablet.attr, + &dev_attr_postcode.attr, + NULL, +}; +ATTRIBUTE_GROUPS(hp_wmi); + +static void hp_wmi_notify(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + u32 event_id, event_data; + union acpi_object *obj; + acpi_status status; + u32 *location; + int key_code; + + status = wmi_get_event_data(value, &response); + if (status != AE_OK) { + pr_info("bad event status 0x%x\n", status); + return; + } + + obj = (union acpi_object *)response.pointer; + + if (!obj) + return; + if (obj->type != ACPI_TYPE_BUFFER) { + pr_info("Unknown response received %d\n", obj->type); + kfree(obj); + return; + } + + /* + * Depending on ACPI version the concatenation of id and event data + * inside _WED function will result in a 8 or 16 byte buffer. + */ + location = (u32 *)obj->buffer.pointer; + if (obj->buffer.length == 8) { + event_id = *location; + event_data = *(location + 1); + } else if (obj->buffer.length == 16) { + event_id = *location; + event_data = *(location + 2); + } else { + pr_info("Unknown buffer length %d\n", obj->buffer.length); + kfree(obj); + return; + } + kfree(obj); + + switch (event_id) { + case HPWMI_DOCK_EVENT: + if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit)) + input_report_switch(hp_wmi_input_dev, SW_DOCK, + hp_wmi_get_dock_state()); + if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit)) + input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, + hp_wmi_get_tablet_mode()); + input_sync(hp_wmi_input_dev); + break; + case HPWMI_PARK_HDD: + break; + case HPWMI_SMART_ADAPTER: + break; + case HPWMI_BEZEL_BUTTON: + key_code = hp_wmi_read_int(HPWMI_HOTKEY_QUERY); + if (key_code < 0) + break; + + if (!sparse_keymap_report_event(hp_wmi_input_dev, + key_code, 1, true)) + pr_info("Unknown key code - 0x%x\n", key_code); + break; + case HPWMI_WIRELESS: + if (rfkill2_count) { + hp_wmi_rfkill2_refresh(); + break; + } + + if (wifi_rfkill) + rfkill_set_states(wifi_rfkill, + hp_wmi_get_sw_state(HPWMI_WIFI), + hp_wmi_get_hw_state(HPWMI_WIFI)); + if (bluetooth_rfkill) + rfkill_set_states(bluetooth_rfkill, + hp_wmi_get_sw_state(HPWMI_BLUETOOTH), + hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); + if (wwan_rfkill) + rfkill_set_states(wwan_rfkill, + hp_wmi_get_sw_state(HPWMI_WWAN), + hp_wmi_get_hw_state(HPWMI_WWAN)); + break; + case HPWMI_CPU_BATTERY_THROTTLE: + pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n"); + break; + case HPWMI_LOCK_SWITCH: + break; + case HPWMI_LID_SWITCH: + break; + case HPWMI_SCREEN_ROTATION: + break; + case HPWMI_COOLSENSE_SYSTEM_MOBILE: + break; + case HPWMI_COOLSENSE_SYSTEM_HOT: + break; + case HPWMI_PROXIMITY_SENSOR: + break; + case HPWMI_BACKLIT_KB_BRIGHTNESS: + break; + case HPWMI_PEAKSHIFT_PERIOD: + break; + case HPWMI_BATTERY_CHARGE_PERIOD: + break; + case HPWMI_SANITIZATION_MODE: + break; + case HPWMI_SMART_EXPERIENCE_APP: + break; + default: + pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data); + break; + } +} + +static int __init hp_wmi_input_setup(void) +{ + acpi_status status; + int err, val; + + hp_wmi_input_dev = input_allocate_device(); + if (!hp_wmi_input_dev) + return -ENOMEM; + + hp_wmi_input_dev->name = "HP WMI hotkeys"; + hp_wmi_input_dev->phys = "wmi/input0"; + hp_wmi_input_dev->id.bustype = BUS_HOST; + + __set_bit(EV_SW, hp_wmi_input_dev->evbit); + + /* Dock */ + val = hp_wmi_get_dock_state(); + if (!(val < 0)) { + __set_bit(SW_DOCK, hp_wmi_input_dev->swbit); + input_report_switch(hp_wmi_input_dev, SW_DOCK, val); + } + + /* Tablet mode */ + val = hp_wmi_get_tablet_mode(); + if (!(val < 0)) { + __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); + input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, val); + } + + err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL); + if (err) + goto err_free_dev; + + /* Set initial hardware state */ + input_sync(hp_wmi_input_dev); + + if (!hp_wmi_bios_2009_later() && hp_wmi_bios_2008_later()) + hp_wmi_enable_hotkeys(); + + status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); + if (ACPI_FAILURE(status)) { + err = -EIO; + goto err_free_dev; + } + + err = input_register_device(hp_wmi_input_dev); + if (err) + goto err_uninstall_notifier; + + return 0; + + err_uninstall_notifier: + wmi_remove_notify_handler(HPWMI_EVENT_GUID); + err_free_dev: + input_free_device(hp_wmi_input_dev); + return err; +} + +static void hp_wmi_input_destroy(void) +{ + wmi_remove_notify_handler(HPWMI_EVENT_GUID); + input_unregister_device(hp_wmi_input_dev); +} + +static int __init hp_wmi_rfkill_setup(struct platform_device *device) +{ + int err, wireless; + + wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); + if (wireless < 0) + return wireless; + + err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE, &wireless, + sizeof(wireless), 0); + if (err) + return err; + + if (wireless & 0x1) { + wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, + RFKILL_TYPE_WLAN, + &hp_wmi_rfkill_ops, + (void *) HPWMI_WIFI); + if (!wifi_rfkill) + return -ENOMEM; + rfkill_init_sw_state(wifi_rfkill, + hp_wmi_get_sw_state(HPWMI_WIFI)); + rfkill_set_hw_state(wifi_rfkill, + hp_wmi_get_hw_state(HPWMI_WIFI)); + err = rfkill_register(wifi_rfkill); + if (err) + goto register_wifi_error; + } + + if (wireless & 0x2) { + bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev, + RFKILL_TYPE_BLUETOOTH, + &hp_wmi_rfkill_ops, + (void *) HPWMI_BLUETOOTH); + if (!bluetooth_rfkill) { + err = -ENOMEM; + goto register_bluetooth_error; + } + rfkill_init_sw_state(bluetooth_rfkill, + hp_wmi_get_sw_state(HPWMI_BLUETOOTH)); + rfkill_set_hw_state(bluetooth_rfkill, + hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); + err = rfkill_register(bluetooth_rfkill); + if (err) + goto register_bluetooth_error; + } + + if (wireless & 0x4) { + wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev, + RFKILL_TYPE_WWAN, + &hp_wmi_rfkill_ops, + (void *) HPWMI_WWAN); + if (!wwan_rfkill) { + err = -ENOMEM; + goto register_wwan_error; + } + rfkill_init_sw_state(wwan_rfkill, + hp_wmi_get_sw_state(HPWMI_WWAN)); + rfkill_set_hw_state(wwan_rfkill, + hp_wmi_get_hw_state(HPWMI_WWAN)); + err = rfkill_register(wwan_rfkill); + if (err) + goto register_wwan_error; + } + + return 0; + +register_wwan_error: + rfkill_destroy(wwan_rfkill); + wwan_rfkill = NULL; + if (bluetooth_rfkill) + rfkill_unregister(bluetooth_rfkill); +register_bluetooth_error: + rfkill_destroy(bluetooth_rfkill); + bluetooth_rfkill = NULL; + if (wifi_rfkill) + rfkill_unregister(wifi_rfkill); +register_wifi_error: + rfkill_destroy(wifi_rfkill); + wifi_rfkill = NULL; + return err; +} + +static int __init hp_wmi_rfkill2_setup(struct platform_device *device) +{ + struct bios_rfkill2_state state; + int err, i; + + err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state, + zero_if_sup(state), sizeof(state)); + if (err) + return err < 0 ? err : -EINVAL; + + if (state.count > HPWMI_MAX_RFKILL2_DEVICES) { + pr_warn("unable to parse 0x1b query output\n"); + return -EINVAL; + } + + for (i = 0; i < state.count; i++) { + struct rfkill *rfkill; + enum rfkill_type type; + char *name; + + switch (state.device[i].radio_type) { + case HPWMI_WIFI: + type = RFKILL_TYPE_WLAN; + name = "hp-wifi"; + break; + case HPWMI_BLUETOOTH: + type = RFKILL_TYPE_BLUETOOTH; + name = "hp-bluetooth"; + break; + case HPWMI_WWAN: + type = RFKILL_TYPE_WWAN; + name = "hp-wwan"; + break; + case HPWMI_GPS: + type = RFKILL_TYPE_GPS; + name = "hp-gps"; + break; + default: + pr_warn("unknown device type 0x%x\n", + state.device[i].radio_type); + continue; + } + + if (!state.device[i].vendor_id) { + pr_warn("zero device %d while %d reported\n", + i, state.count); + continue; + } + + rfkill = rfkill_alloc(name, &device->dev, type, + &hp_wmi_rfkill2_ops, (void *)(long)i); + if (!rfkill) { + err = -ENOMEM; + goto fail; + } + + rfkill2[rfkill2_count].id = state.device[i].rfkill_id; + rfkill2[rfkill2_count].num = i; + rfkill2[rfkill2_count].rfkill = rfkill; + + rfkill_init_sw_state(rfkill, + IS_SWBLOCKED(state.device[i].power)); + rfkill_set_hw_state(rfkill, + IS_HWBLOCKED(state.device[i].power)); + + if (!(state.device[i].power & HPWMI_POWER_BIOS)) + pr_info("device %s blocked by BIOS\n", name); + + err = rfkill_register(rfkill); + if (err) { + rfkill_destroy(rfkill); + goto fail; + } + + rfkill2_count++; + } + + return 0; +fail: + for (; rfkill2_count > 0; rfkill2_count--) { + rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill); + rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill); + } + return err; +} + +static int platform_profile_omen_get(struct platform_profile_handler *pprof, + enum platform_profile_option *profile) +{ + int tp; + + tp = omen_thermal_profile_get(); + if (tp < 0) + return tp; + + switch (tp) { + case HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE: + case HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE: + *profile = PLATFORM_PROFILE_PERFORMANCE; + break; + case HP_OMEN_V0_THERMAL_PROFILE_DEFAULT: + case HP_OMEN_V1_THERMAL_PROFILE_DEFAULT: + *profile = PLATFORM_PROFILE_BALANCED; + break; + case HP_OMEN_V0_THERMAL_PROFILE_COOL: + case HP_OMEN_V1_THERMAL_PROFILE_COOL: + *profile = PLATFORM_PROFILE_COOL; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int platform_profile_omen_set(struct platform_profile_handler *pprof, + enum platform_profile_option profile) +{ + int err, tp, tp_version; + + tp_version = omen_get_thermal_policy_version(); + + if (tp_version < 0 || tp_version > 1) + return -EOPNOTSUPP; + + switch (profile) { + case PLATFORM_PROFILE_PERFORMANCE: + if (tp_version == 0) + tp = HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE; + else + tp = HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE; + break; + case PLATFORM_PROFILE_BALANCED: + if (tp_version == 0) + tp = HP_OMEN_V0_THERMAL_PROFILE_DEFAULT; + else + tp = HP_OMEN_V1_THERMAL_PROFILE_DEFAULT; + break; + case PLATFORM_PROFILE_COOL: + if (tp_version == 0) + tp = HP_OMEN_V0_THERMAL_PROFILE_COOL; + else + tp = HP_OMEN_V1_THERMAL_PROFILE_COOL; + break; + default: + return -EOPNOTSUPP; + } + + err = omen_thermal_profile_set(tp); + if (err < 0) + return err; + + return 0; +} + +static int thermal_profile_get(void) +{ + return hp_wmi_read_int(HPWMI_THERMAL_PROFILE_QUERY); +} + +static int thermal_profile_set(int thermal_profile) +{ + return hp_wmi_perform_query(HPWMI_THERMAL_PROFILE_QUERY, HPWMI_WRITE, &thermal_profile, + sizeof(thermal_profile), 0); +} + +static int hp_wmi_platform_profile_get(struct platform_profile_handler *pprof, + enum platform_profile_option *profile) +{ + int tp; + + tp = thermal_profile_get(); + if (tp < 0) + return tp; + + switch (tp) { + case HP_THERMAL_PROFILE_PERFORMANCE: + *profile = PLATFORM_PROFILE_PERFORMANCE; + break; + case HP_THERMAL_PROFILE_DEFAULT: + *profile = PLATFORM_PROFILE_BALANCED; + break; + case HP_THERMAL_PROFILE_COOL: + *profile = PLATFORM_PROFILE_COOL; + break; + case HP_THERMAL_PROFILE_QUIET: + *profile = PLATFORM_PROFILE_QUIET; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int hp_wmi_platform_profile_set(struct platform_profile_handler *pprof, + enum platform_profile_option profile) +{ + int err, tp; + + switch (profile) { + case PLATFORM_PROFILE_PERFORMANCE: + tp = HP_THERMAL_PROFILE_PERFORMANCE; + break; + case PLATFORM_PROFILE_BALANCED: + tp = HP_THERMAL_PROFILE_DEFAULT; + break; + case PLATFORM_PROFILE_COOL: + tp = HP_THERMAL_PROFILE_COOL; + break; + case PLATFORM_PROFILE_QUIET: + tp = HP_THERMAL_PROFILE_QUIET; + break; + default: + return -EOPNOTSUPP; + } + + err = thermal_profile_set(tp); + if (err) + return err; + + return 0; +} + +static int thermal_profile_setup(void) +{ + int err, tp; + + if (is_omen_thermal_profile()) { + tp = omen_thermal_profile_get(); + if (tp < 0) + return tp; + + /* + * call thermal profile write command to ensure that the + * firmware correctly sets the OEM variables + */ + + err = omen_thermal_profile_set(tp); + if (err < 0) + return err; + + platform_profile_handler.profile_get = platform_profile_omen_get; + platform_profile_handler.profile_set = platform_profile_omen_set; + } else { + tp = thermal_profile_get(); + + if (tp < 0) + return tp; + + /* + * call thermal profile write command to ensure that the + * firmware correctly sets the OEM variables for the DPTF + */ + err = thermal_profile_set(tp); + if (err) + return err; + + platform_profile_handler.profile_get = hp_wmi_platform_profile_get; + platform_profile_handler.profile_set = hp_wmi_platform_profile_set; + + set_bit(PLATFORM_PROFILE_QUIET, platform_profile_handler.choices); + } + + set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices); + set_bit(PLATFORM_PROFILE_BALANCED, platform_profile_handler.choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, platform_profile_handler.choices); + + err = platform_profile_register(&platform_profile_handler); + if (err) + return err; + + platform_profile_support = true; + + return 0; +} + +static int hp_wmi_hwmon_init(void); + +static int __init hp_wmi_bios_setup(struct platform_device *device) +{ + int err; + /* clear detected rfkill devices */ + wifi_rfkill = NULL; + bluetooth_rfkill = NULL; + wwan_rfkill = NULL; + rfkill2_count = 0; + + /* + * In pre-2009 BIOS, command 1Bh return 0x4 to indicate that + * BIOS no longer controls the power for the wireless + * devices. All features supported by this command will no + * longer be supported. + */ + if (!hp_wmi_bios_2009_later()) { + if (hp_wmi_rfkill_setup(device)) + hp_wmi_rfkill2_setup(device); + } + + err = hp_wmi_hwmon_init(); + + if (err < 0) + return err; + + thermal_profile_setup(); + + return 0; +} + +static int __exit hp_wmi_bios_remove(struct platform_device *device) +{ + int i; + + for (i = 0; i < rfkill2_count; i++) { + rfkill_unregister(rfkill2[i].rfkill); + rfkill_destroy(rfkill2[i].rfkill); + } + + if (wifi_rfkill) { + rfkill_unregister(wifi_rfkill); + rfkill_destroy(wifi_rfkill); + } + if (bluetooth_rfkill) { + rfkill_unregister(bluetooth_rfkill); + rfkill_destroy(bluetooth_rfkill); + } + if (wwan_rfkill) { + rfkill_unregister(wwan_rfkill); + rfkill_destroy(wwan_rfkill); + } + + if (platform_profile_support) + platform_profile_remove(); + + return 0; +} + +static int hp_wmi_resume_handler(struct device *device) +{ + /* + * Hardware state may have changed while suspended, so trigger + * input events for the current state. As this is a switch, + * the input layer will only actually pass it on if the state + * changed. + */ + if (hp_wmi_input_dev) { + if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit)) + input_report_switch(hp_wmi_input_dev, SW_DOCK, + hp_wmi_get_dock_state()); + if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit)) + input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, + hp_wmi_get_tablet_mode()); + input_sync(hp_wmi_input_dev); + } + + if (rfkill2_count) + hp_wmi_rfkill2_refresh(); + + if (wifi_rfkill) + rfkill_set_states(wifi_rfkill, + hp_wmi_get_sw_state(HPWMI_WIFI), + hp_wmi_get_hw_state(HPWMI_WIFI)); + if (bluetooth_rfkill) + rfkill_set_states(bluetooth_rfkill, + hp_wmi_get_sw_state(HPWMI_BLUETOOTH), + hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); + if (wwan_rfkill) + rfkill_set_states(wwan_rfkill, + hp_wmi_get_sw_state(HPWMI_WWAN), + hp_wmi_get_hw_state(HPWMI_WWAN)); + + return 0; +} + +static const struct dev_pm_ops hp_wmi_pm_ops = { + .resume = hp_wmi_resume_handler, + .restore = hp_wmi_resume_handler, +}; + +static struct platform_driver hp_wmi_driver = { + .driver = { + .name = "hp-wmi", + .pm = &hp_wmi_pm_ops, + .dev_groups = hp_wmi_groups, + }, + .remove = __exit_p(hp_wmi_bios_remove), +}; + +static umode_t hp_wmi_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_pwm: + return 0644; + case hwmon_fan: + if (hp_wmi_get_fan_speed(channel) >= 0) + return 0444; + break; + default: + return 0; + } + + return 0; +} + +static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + int ret; + + switch (type) { + case hwmon_fan: + ret = hp_wmi_get_fan_speed(channel); + + if (ret < 0) + return ret; + *val = ret; + return 0; + case hwmon_pwm: + switch (hp_wmi_fan_speed_max_get()) { + case 0: + /* 0 is automatic fan, which is 2 for hwmon */ + *val = 2; + return 0; + case 1: + /* 1 is max fan, which is 0 + * (no fan speed control) for hwmon + */ + *val = 0; + return 0; + default: + /* shouldn't happen */ + return -ENODATA; + } + default: + return -EINVAL; + } +} + +static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_pwm: + switch (val) { + case 0: + /* 0 is no fan speed control (max), which is 1 for us */ + return hp_wmi_fan_speed_max_set(1); + case 2: + /* 2 is automatic speed control, which is 0 for us */ + return hp_wmi_fan_speed_max_set(0); + default: + /* we don't support manual fan speed control */ + return -EINVAL; + } + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_channel_info *info[] = { + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT), + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE), + NULL +}; + +static const struct hwmon_ops ops = { + .is_visible = hp_wmi_hwmon_is_visible, + .read = hp_wmi_hwmon_read, + .write = hp_wmi_hwmon_write, +}; + +static const struct hwmon_chip_info chip_info = { + .ops = &ops, + .info = info, +}; + +static int hp_wmi_hwmon_init(void) +{ + struct device *dev = &hp_wmi_platform_dev->dev; + struct device *hwmon; + + hwmon = devm_hwmon_device_register_with_info(dev, "hp", &hp_wmi_driver, + &chip_info, NULL); + + if (IS_ERR(hwmon)) { + dev_err(dev, "Could not register hp hwmon device\n"); + return PTR_ERR(hwmon); + } + + return 0; +} + +static int __init hp_wmi_init(void) +{ + int event_capable = wmi_has_guid(HPWMI_EVENT_GUID); + int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); + int err, tmp = 0; + + if (!bios_capable && !event_capable) + return -ENODEV; + + if (hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, HPWMI_READ, &tmp, + sizeof(tmp), sizeof(tmp)) == HPWMI_RET_INVALID_PARAMETERS) + zero_insize_support = true; + + if (event_capable) { + err = hp_wmi_input_setup(); + if (err) + return err; + } + + if (bios_capable) { + hp_wmi_platform_dev = + platform_device_register_simple("hp-wmi", PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(hp_wmi_platform_dev)) { + err = PTR_ERR(hp_wmi_platform_dev); + goto err_destroy_input; + } + + err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup); + if (err) + goto err_unregister_device; + } + + return 0; + +err_unregister_device: + platform_device_unregister(hp_wmi_platform_dev); +err_destroy_input: + if (event_capable) + hp_wmi_input_destroy(); + + return err; +} +module_init(hp_wmi_init); + +static void __exit hp_wmi_exit(void) +{ + if (wmi_has_guid(HPWMI_EVENT_GUID)) + hp_wmi_input_destroy(); + + if (hp_wmi_platform_dev) { + platform_device_unregister(hp_wmi_platform_dev); + platform_driver_unregister(&hp_wmi_driver); + } +} +module_exit(hp_wmi_exit); diff --git a/drivers/platform/x86/hp/hp_accel.c b/drivers/platform/x86/hp/hp_accel.c new file mode 100644 index 000000000000..6477591747cf --- /dev/null +++ b/drivers/platform/x86/hp/hp_accel.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * hp_accel.c - Interface between LIS3LV02DL driver and HP ACPI BIOS + * + * Copyright (C) 2007-2008 Yan Burman + * Copyright (C) 2008 Eric Piel + * Copyright (C) 2008-2009 Pavel Machek + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../../misc/lis3lv02d/lis3lv02d.h" + +/* Delayed LEDs infrastructure ------------------------------------ */ + +/* Special LED class that can defer work */ +struct delayed_led_classdev { + struct led_classdev led_classdev; + struct work_struct work; + enum led_brightness new_brightness; + + unsigned int led; /* For driver */ + void (*set_brightness)(struct delayed_led_classdev *data, enum led_brightness value); +}; + +static inline void delayed_set_status_worker(struct work_struct *work) +{ + struct delayed_led_classdev *data = + container_of(work, struct delayed_led_classdev, work); + + data->set_brightness(data, data->new_brightness); +} + +static inline void delayed_sysfs_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct delayed_led_classdev *data = container_of(led_cdev, + struct delayed_led_classdev, led_classdev); + data->new_brightness = brightness; + schedule_work(&data->work); +} + +/* HP-specific accelerometer driver ------------------------------------ */ + +/* e0 25, e0 26, e0 27, e0 28 are scan codes that the accelerometer with acpi id + * HPQ6000 sends through the keyboard bus */ +#define ACCEL_1 0x25 +#define ACCEL_2 0x26 +#define ACCEL_3 0x27 +#define ACCEL_4 0x28 + +/* For automatic insertion of the module */ +static const struct acpi_device_id lis3lv02d_device_ids[] = { + {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ + {"HPQ6000", 0}, /* HP Mobile Data Protection System PNP */ + {"HPQ6007", 0}, /* HP Mobile Data Protection System PNP */ + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); + +/** + * lis3lv02d_acpi_init - initialize the device for ACPI + * @lis3: pointer to the device struct + * + * Returns 0 on success. + */ +static int lis3lv02d_acpi_init(struct lis3lv02d *lis3) +{ + return 0; +} + +/** + * lis3lv02d_acpi_read - ACPI ALRD method: read a register + * @lis3: pointer to the device struct + * @reg: the register to read + * @ret: result of the operation + * + * Returns 0 on success. + */ +static int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret) +{ + struct acpi_device *dev = lis3->bus_priv; + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + unsigned long long lret; + acpi_status status; + + arg0.integer.value = reg; + + status = acpi_evaluate_integer(dev->handle, "ALRD", &args, &lret); + if (ACPI_FAILURE(status)) + return -EINVAL; + *ret = lret; + return 0; +} + +/** + * lis3lv02d_acpi_write - ACPI ALWR method: write to a register + * @lis3: pointer to the device struct + * @reg: the register to write to + * @val: the value to write + * + * Returns 0 on success. + */ +static int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val) +{ + struct acpi_device *dev = lis3->bus_priv; + unsigned long long ret; /* Not used when writting */ + union acpi_object in_obj[2]; + struct acpi_object_list args = { 2, in_obj }; + + in_obj[0].type = ACPI_TYPE_INTEGER; + in_obj[0].integer.value = reg; + in_obj[1].type = ACPI_TYPE_INTEGER; + in_obj[1].integer.value = val; + + if (acpi_evaluate_integer(dev->handle, "ALWR", &args, &ret) != AE_OK) + return -EINVAL; + + return 0; +} + +static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) +{ + lis3_dev.ac = *((union axis_conversion *)dmi->driver_data); + pr_info("hardware type %s found\n", dmi->ident); + + return 1; +} + +/* Represents, for each axis seen by userspace, the corresponding hw axis (+1). + * If the value is negative, the opposite of the hw value is used. */ +#define DEFINE_CONV(name, x, y, z) \ + static union axis_conversion lis3lv02d_axis_##name = \ + { .as_array = { x, y, z } } +DEFINE_CONV(normal, 1, 2, 3); +DEFINE_CONV(y_inverted, 1, -2, 3); +DEFINE_CONV(x_inverted, -1, 2, 3); +DEFINE_CONV(x_inverted_usd, -1, 2, -3); +DEFINE_CONV(z_inverted, 1, 2, -3); +DEFINE_CONV(xy_swap, 2, 1, 3); +DEFINE_CONV(xy_rotated_left, -2, 1, 3); +DEFINE_CONV(xy_rotated_left_usd, -2, 1, -3); +DEFINE_CONV(xy_swap_inverted, -2, -1, 3); +DEFINE_CONV(xy_rotated_right, 2, -1, 3); +DEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3); + +#define AXIS_DMI_MATCH(_ident, _name, _axis) { \ + .ident = _ident, \ + .callback = lis3lv02d_dmi_matched, \ + .matches = { \ + DMI_MATCH(DMI_PRODUCT_NAME, _name) \ + }, \ + .driver_data = &lis3lv02d_axis_##_axis \ +} + +#define AXIS_DMI_MATCH2(_ident, _class1, _name1, \ + _class2, _name2, \ + _axis) { \ + .ident = _ident, \ + .callback = lis3lv02d_dmi_matched, \ + .matches = { \ + DMI_MATCH(DMI_##_class1, _name1), \ + DMI_MATCH(DMI_##_class2, _name2), \ + }, \ + .driver_data = &lis3lv02d_axis_##_axis \ +} +static const struct dmi_system_id lis3lv02d_dmi_ids[] = { + /* product names are truncated to match all kinds of a same model */ + AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), + AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), + AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), + AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), + AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), + AXIS_DMI_MATCH("NC2710", "HP Compaq 2710", xy_swap), + AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), + AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), + AXIS_DMI_MATCH("HP2140", "HP 2140", xy_swap_inverted), + AXIS_DMI_MATCH("NC653x", "HP Compaq 653", xy_rotated_left_usd), + AXIS_DMI_MATCH("NC6730b", "HP Compaq 6730b", xy_rotated_left_usd), + AXIS_DMI_MATCH("NC6730s", "HP Compaq 6730s", xy_swap), + AXIS_DMI_MATCH("NC651xx", "HP Compaq 651", xy_rotated_right), + AXIS_DMI_MATCH("NC6710x", "HP Compaq 6710", xy_swap_yz_inverted), + AXIS_DMI_MATCH("NC6715x", "HP Compaq 6715", y_inverted), + AXIS_DMI_MATCH("NC693xx", "HP EliteBook 693", xy_rotated_right), + AXIS_DMI_MATCH("NC693xx", "HP EliteBook 853", xy_swap), + AXIS_DMI_MATCH("NC854xx", "HP EliteBook 854", y_inverted), + AXIS_DMI_MATCH("NC273xx", "HP EliteBook 273", y_inverted), + /* Intel-based HP Pavilion dv5 */ + AXIS_DMI_MATCH2("HPDV5_I", + PRODUCT_NAME, "HP Pavilion dv5", + BOARD_NAME, "3603", + x_inverted), + /* AMD-based HP Pavilion dv5 */ + AXIS_DMI_MATCH2("HPDV5_A", + PRODUCT_NAME, "HP Pavilion dv5", + BOARD_NAME, "3600", + y_inverted), + AXIS_DMI_MATCH("DV7", "HP Pavilion dv7", x_inverted), + AXIS_DMI_MATCH("HP8710", "HP Compaq 8710", y_inverted), + AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted), + AXIS_DMI_MATCH("HPB432x", "HP ProBook 432", xy_rotated_left), + AXIS_DMI_MATCH("HPB440G3", "HP ProBook 440 G3", x_inverted_usd), + AXIS_DMI_MATCH("HPB440G4", "HP ProBook 440 G4", x_inverted), + AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left), + AXIS_DMI_MATCH("HPB450G0", "HP ProBook 450 G0", x_inverted), + AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), + AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), + AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), + AXIS_DMI_MATCH("HPB655x", "HP ProBook 655", xy_swap_inverted), + AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd), + AXIS_DMI_MATCH("HPB63xx", "HP ProBook 63", xy_swap), + AXIS_DMI_MATCH("HPB64xx", "HP ProBook 64", xy_swap), + AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap), + AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted), + AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted), + AXIS_DMI_MATCH("HPZBook17G5", "HP ZBook 17 G5", x_inverted), + AXIS_DMI_MATCH("HPZBook17", "HP ZBook 17", xy_swap_yz_inverted), + { NULL, } +/* Laptop models without axis info (yet): + * "NC6910" "HP Compaq 6910" + * "NC2400" "HP Compaq nc2400" + * "NX74x0" "HP Compaq nx74" + * "NX6325" "HP Compaq nx6325" + * "NC4400" "HP Compaq nc4400" + */ +}; + +static void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value) +{ + struct acpi_device *dev = lis3_dev.bus_priv; + unsigned long long ret; /* Not used when writing */ + union acpi_object in_obj[1]; + struct acpi_object_list args = { 1, in_obj }; + + in_obj[0].type = ACPI_TYPE_INTEGER; + in_obj[0].integer.value = !!value; + + acpi_evaluate_integer(dev->handle, "ALED", &args, &ret); +} + +static struct delayed_led_classdev hpled_led = { + .led_classdev = { + .name = "hp::hddprotect", + .default_trigger = "none", + .brightness_set = delayed_sysfs_set, + .flags = LED_CORE_SUSPENDRESUME, + }, + .set_brightness = hpled_set, +}; + +static bool hp_accel_i8042_filter(unsigned char data, unsigned char str, + struct serio *port) +{ + static bool extended; + + if (str & I8042_STR_AUXDATA) + return false; + + if (data == 0xe0) { + extended = true; + return true; + } else if (unlikely(extended)) { + extended = false; + + switch (data) { + case ACCEL_1: + case ACCEL_2: + case ACCEL_3: + case ACCEL_4: + return true; + default: + serio_interrupt(port, 0xe0, 0); + return false; + } + } + + return false; +} + +static int lis3lv02d_probe(struct platform_device *device) +{ + int ret; + + lis3_dev.bus_priv = ACPI_COMPANION(&device->dev); + lis3_dev.init = lis3lv02d_acpi_init; + lis3_dev.read = lis3lv02d_acpi_read; + lis3_dev.write = lis3lv02d_acpi_write; + + /* obtain IRQ number of our device from ACPI */ + ret = platform_get_irq_optional(device, 0); + if (ret > 0) + lis3_dev.irq = ret; + + /* If possible use a "standard" axes order */ + if (lis3_dev.ac.x && lis3_dev.ac.y && lis3_dev.ac.z) { + pr_info("Using custom axes %d,%d,%d\n", + lis3_dev.ac.x, lis3_dev.ac.y, lis3_dev.ac.z); + } else if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { + pr_info("laptop model unknown, using default axes configuration\n"); + lis3_dev.ac = lis3lv02d_axis_normal; + } + + /* call the core layer do its init */ + ret = lis3lv02d_init_device(&lis3_dev); + if (ret) + return ret; + + /* filter to remove HPQ6000 accelerometer data + * from keyboard bus stream */ + if (strstr(dev_name(&device->dev), "HPQ6000")) + i8042_install_filter(hp_accel_i8042_filter); + + INIT_WORK(&hpled_led.work, delayed_set_status_worker); + ret = led_classdev_register(NULL, &hpled_led.led_classdev); + if (ret) { + i8042_remove_filter(hp_accel_i8042_filter); + lis3lv02d_joystick_disable(&lis3_dev); + lis3lv02d_poweroff(&lis3_dev); + flush_work(&hpled_led.work); + lis3lv02d_remove_fs(&lis3_dev); + return ret; + } + + return ret; +} + +static int lis3lv02d_remove(struct platform_device *device) +{ + i8042_remove_filter(hp_accel_i8042_filter); + lis3lv02d_joystick_disable(&lis3_dev); + lis3lv02d_poweroff(&lis3_dev); + + led_classdev_unregister(&hpled_led.led_classdev); + flush_work(&hpled_led.work); + + lis3lv02d_remove_fs(&lis3_dev); + return 0; +} + +static int __maybe_unused lis3lv02d_suspend(struct device *dev) +{ + /* make sure the device is off when we suspend */ + lis3lv02d_poweroff(&lis3_dev); + return 0; +} + +static int __maybe_unused lis3lv02d_resume(struct device *dev) +{ + lis3lv02d_poweron(&lis3_dev); + return 0; +} + +static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume); + +/* For the HP MDPS aka 3D Driveguard */ +static struct platform_driver lis3lv02d_driver = { + .probe = lis3lv02d_probe, + .remove = lis3lv02d_remove, + .driver = { + .name = "hp_accel", + .pm = &hp_accel_pm, + .acpi_match_table = lis3lv02d_device_ids, + }, +}; +module_platform_driver(lis3lv02d_driver); + +MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS and support for disk protection LED."); +MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/hp/tc1100-wmi.c b/drivers/platform/x86/hp/tc1100-wmi.c new file mode 100644 index 000000000000..ded26213c420 --- /dev/null +++ b/drivers/platform/x86/hp/tc1100-wmi.c @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HP Compaq TC1100 Tablet WMI Extras Driver + * + * Copyright (C) 2007 Carlos Corbacho + * Copyright (C) 2004 Jamey Hicks + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#define GUID "C364AC71-36DB-495A-8494-B439D472A505" + +#define TC1100_INSTANCE_WIRELESS 1 +#define TC1100_INSTANCE_JOGDIAL 2 + +MODULE_AUTHOR("Jamey Hicks, Carlos Corbacho"); +MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("wmi:C364AC71-36DB-495A-8494-B439D472A505"); + +static struct platform_device *tc1100_device; + +struct tc1100_data { + u32 wireless; + u32 jogdial; +}; + +#ifdef CONFIG_PM +static struct tc1100_data suspend_data; +#endif + +/* -------------------------------------------------------------------------- + Device Management + -------------------------------------------------------------------------- */ + +static int get_state(u32 *out, u8 instance) +{ + u32 tmp; + acpi_status status; + struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + + if (!out) + return -EINVAL; + + if (instance > 2) + return -ENODEV; + + status = wmi_query_block(GUID, instance, &result); + if (ACPI_FAILURE(status)) + return -ENODEV; + + obj = (union acpi_object *) result.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) { + tmp = obj->integer.value; + } else { + tmp = 0; + } + + if (result.length > 0) + kfree(result.pointer); + + switch (instance) { + case TC1100_INSTANCE_WIRELESS: + *out = (tmp == 3) ? 1 : 0; + return 0; + case TC1100_INSTANCE_JOGDIAL: + *out = (tmp == 1) ? 0 : 1; + return 0; + default: + return -ENODEV; + } +} + +static int set_state(u32 *in, u8 instance) +{ + u32 value; + acpi_status status; + struct acpi_buffer input; + + if (!in) + return -EINVAL; + + if (instance > 2) + return -ENODEV; + + switch (instance) { + case TC1100_INSTANCE_WIRELESS: + value = (*in) ? 1 : 2; + break; + case TC1100_INSTANCE_JOGDIAL: + value = (*in) ? 0 : 1; + break; + default: + return -ENODEV; + } + + input.length = sizeof(u32); + input.pointer = &value; + + status = wmi_set_block(GUID, instance, &input); + if (ACPI_FAILURE(status)) + return -ENODEV; + + return 0; +} + +/* -------------------------------------------------------------------------- + FS Interface (/sys) + -------------------------------------------------------------------------- */ + +/* + * Read/ write bool sysfs macro + */ +#define show_set_bool(value, instance) \ +static ssize_t \ +show_bool_##value(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + u32 result; \ + acpi_status status = get_state(&result, instance); \ + if (ACPI_SUCCESS(status)) \ + return sprintf(buf, "%d\n", result); \ + return sprintf(buf, "Read error\n"); \ +} \ +\ +static ssize_t \ +set_bool_##value(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + u32 tmp = simple_strtoul(buf, NULL, 10); \ + acpi_status status = set_state(&tmp, instance); \ + if (ACPI_FAILURE(status)) \ + return -EINVAL; \ + return count; \ +} \ +static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, \ + show_bool_##value, set_bool_##value); + +show_set_bool(wireless, TC1100_INSTANCE_WIRELESS); +show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL); + +static struct attribute *tc1100_attributes[] = { + &dev_attr_wireless.attr, + &dev_attr_jogdial.attr, + NULL +}; + +static const struct attribute_group tc1100_attribute_group = { + .attrs = tc1100_attributes, +}; + +/* -------------------------------------------------------------------------- + Driver Model + -------------------------------------------------------------------------- */ + +static int __init tc1100_probe(struct platform_device *device) +{ + return sysfs_create_group(&device->dev.kobj, &tc1100_attribute_group); +} + + +static int tc1100_remove(struct platform_device *device) +{ + sysfs_remove_group(&device->dev.kobj, &tc1100_attribute_group); + + return 0; +} + +#ifdef CONFIG_PM +static int tc1100_suspend(struct device *dev) +{ + int ret; + + ret = get_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS); + if (ret) + return ret; + + ret = get_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL); + if (ret) + return ret; + + return 0; +} + +static int tc1100_resume(struct device *dev) +{ + int ret; + + ret = set_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS); + if (ret) + return ret; + + ret = set_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL); + if (ret) + return ret; + + return 0; +} + +static const struct dev_pm_ops tc1100_pm_ops = { + .suspend = tc1100_suspend, + .resume = tc1100_resume, + .freeze = tc1100_suspend, + .restore = tc1100_resume, +}; +#endif + +static struct platform_driver tc1100_driver = { + .driver = { + .name = "tc1100-wmi", +#ifdef CONFIG_PM + .pm = &tc1100_pm_ops, +#endif + }, + .remove = tc1100_remove, +}; + +static int __init tc1100_init(void) +{ + int error; + + if (!wmi_has_guid(GUID)) + return -ENODEV; + + tc1100_device = platform_device_alloc("tc1100-wmi", PLATFORM_DEVID_NONE); + if (!tc1100_device) + return -ENOMEM; + + error = platform_device_add(tc1100_device); + if (error) + goto err_device_put; + + error = platform_driver_probe(&tc1100_driver, tc1100_probe); + if (error) + goto err_device_del; + + pr_info("HP Compaq TC1100 Tablet WMI Extras loaded\n"); + return 0; + + err_device_del: + platform_device_del(tc1100_device); + err_device_put: + platform_device_put(tc1100_device); + return error; +} + +static void __exit tc1100_exit(void) +{ + platform_device_unregister(tc1100_device); + platform_driver_unregister(&tc1100_driver); +} + +module_init(tc1100_init); +module_exit(tc1100_exit); diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c deleted file mode 100644 index e9f852f7c27f..000000000000 --- a/drivers/platform/x86/hp_accel.c +++ /dev/null @@ -1,387 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * hp_accel.c - Interface between LIS3LV02DL driver and HP ACPI BIOS - * - * Copyright (C) 2007-2008 Yan Burman - * Copyright (C) 2008 Eric Piel - * Copyright (C) 2008-2009 Pavel Machek - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../../misc/lis3lv02d/lis3lv02d.h" - -/* Delayed LEDs infrastructure ------------------------------------ */ - -/* Special LED class that can defer work */ -struct delayed_led_classdev { - struct led_classdev led_classdev; - struct work_struct work; - enum led_brightness new_brightness; - - unsigned int led; /* For driver */ - void (*set_brightness)(struct delayed_led_classdev *data, enum led_brightness value); -}; - -static inline void delayed_set_status_worker(struct work_struct *work) -{ - struct delayed_led_classdev *data = - container_of(work, struct delayed_led_classdev, work); - - data->set_brightness(data, data->new_brightness); -} - -static inline void delayed_sysfs_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct delayed_led_classdev *data = container_of(led_cdev, - struct delayed_led_classdev, led_classdev); - data->new_brightness = brightness; - schedule_work(&data->work); -} - -/* HP-specific accelerometer driver ------------------------------------ */ - -/* e0 25, e0 26, e0 27, e0 28 are scan codes that the accelerometer with acpi id - * HPQ6000 sends through the keyboard bus */ -#define ACCEL_1 0x25 -#define ACCEL_2 0x26 -#define ACCEL_3 0x27 -#define ACCEL_4 0x28 - -/* For automatic insertion of the module */ -static const struct acpi_device_id lis3lv02d_device_ids[] = { - {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ - {"HPQ6000", 0}, /* HP Mobile Data Protection System PNP */ - {"HPQ6007", 0}, /* HP Mobile Data Protection System PNP */ - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); - -/** - * lis3lv02d_acpi_init - initialize the device for ACPI - * @lis3: pointer to the device struct - * - * Returns 0 on success. - */ -static int lis3lv02d_acpi_init(struct lis3lv02d *lis3) -{ - return 0; -} - -/** - * lis3lv02d_acpi_read - ACPI ALRD method: read a register - * @lis3: pointer to the device struct - * @reg: the register to read - * @ret: result of the operation - * - * Returns 0 on success. - */ -static int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret) -{ - struct acpi_device *dev = lis3->bus_priv; - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list args = { 1, &arg0 }; - unsigned long long lret; - acpi_status status; - - arg0.integer.value = reg; - - status = acpi_evaluate_integer(dev->handle, "ALRD", &args, &lret); - if (ACPI_FAILURE(status)) - return -EINVAL; - *ret = lret; - return 0; -} - -/** - * lis3lv02d_acpi_write - ACPI ALWR method: write to a register - * @lis3: pointer to the device struct - * @reg: the register to write to - * @val: the value to write - * - * Returns 0 on success. - */ -static int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val) -{ - struct acpi_device *dev = lis3->bus_priv; - unsigned long long ret; /* Not used when writting */ - union acpi_object in_obj[2]; - struct acpi_object_list args = { 2, in_obj }; - - in_obj[0].type = ACPI_TYPE_INTEGER; - in_obj[0].integer.value = reg; - in_obj[1].type = ACPI_TYPE_INTEGER; - in_obj[1].integer.value = val; - - if (acpi_evaluate_integer(dev->handle, "ALWR", &args, &ret) != AE_OK) - return -EINVAL; - - return 0; -} - -static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) -{ - lis3_dev.ac = *((union axis_conversion *)dmi->driver_data); - pr_info("hardware type %s found\n", dmi->ident); - - return 1; -} - -/* Represents, for each axis seen by userspace, the corresponding hw axis (+1). - * If the value is negative, the opposite of the hw value is used. */ -#define DEFINE_CONV(name, x, y, z) \ - static union axis_conversion lis3lv02d_axis_##name = \ - { .as_array = { x, y, z } } -DEFINE_CONV(normal, 1, 2, 3); -DEFINE_CONV(y_inverted, 1, -2, 3); -DEFINE_CONV(x_inverted, -1, 2, 3); -DEFINE_CONV(x_inverted_usd, -1, 2, -3); -DEFINE_CONV(z_inverted, 1, 2, -3); -DEFINE_CONV(xy_swap, 2, 1, 3); -DEFINE_CONV(xy_rotated_left, -2, 1, 3); -DEFINE_CONV(xy_rotated_left_usd, -2, 1, -3); -DEFINE_CONV(xy_swap_inverted, -2, -1, 3); -DEFINE_CONV(xy_rotated_right, 2, -1, 3); -DEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3); - -#define AXIS_DMI_MATCH(_ident, _name, _axis) { \ - .ident = _ident, \ - .callback = lis3lv02d_dmi_matched, \ - .matches = { \ - DMI_MATCH(DMI_PRODUCT_NAME, _name) \ - }, \ - .driver_data = &lis3lv02d_axis_##_axis \ -} - -#define AXIS_DMI_MATCH2(_ident, _class1, _name1, \ - _class2, _name2, \ - _axis) { \ - .ident = _ident, \ - .callback = lis3lv02d_dmi_matched, \ - .matches = { \ - DMI_MATCH(DMI_##_class1, _name1), \ - DMI_MATCH(DMI_##_class2, _name2), \ - }, \ - .driver_data = &lis3lv02d_axis_##_axis \ -} -static const struct dmi_system_id lis3lv02d_dmi_ids[] = { - /* product names are truncated to match all kinds of a same model */ - AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), - AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), - AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), - AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), - AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), - AXIS_DMI_MATCH("NC2710", "HP Compaq 2710", xy_swap), - AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), - AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), - AXIS_DMI_MATCH("HP2140", "HP 2140", xy_swap_inverted), - AXIS_DMI_MATCH("NC653x", "HP Compaq 653", xy_rotated_left_usd), - AXIS_DMI_MATCH("NC6730b", "HP Compaq 6730b", xy_rotated_left_usd), - AXIS_DMI_MATCH("NC6730s", "HP Compaq 6730s", xy_swap), - AXIS_DMI_MATCH("NC651xx", "HP Compaq 651", xy_rotated_right), - AXIS_DMI_MATCH("NC6710x", "HP Compaq 6710", xy_swap_yz_inverted), - AXIS_DMI_MATCH("NC6715x", "HP Compaq 6715", y_inverted), - AXIS_DMI_MATCH("NC693xx", "HP EliteBook 693", xy_rotated_right), - AXIS_DMI_MATCH("NC693xx", "HP EliteBook 853", xy_swap), - AXIS_DMI_MATCH("NC854xx", "HP EliteBook 854", y_inverted), - AXIS_DMI_MATCH("NC273xx", "HP EliteBook 273", y_inverted), - /* Intel-based HP Pavilion dv5 */ - AXIS_DMI_MATCH2("HPDV5_I", - PRODUCT_NAME, "HP Pavilion dv5", - BOARD_NAME, "3603", - x_inverted), - /* AMD-based HP Pavilion dv5 */ - AXIS_DMI_MATCH2("HPDV5_A", - PRODUCT_NAME, "HP Pavilion dv5", - BOARD_NAME, "3600", - y_inverted), - AXIS_DMI_MATCH("DV7", "HP Pavilion dv7", x_inverted), - AXIS_DMI_MATCH("HP8710", "HP Compaq 8710", y_inverted), - AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted), - AXIS_DMI_MATCH("HPB432x", "HP ProBook 432", xy_rotated_left), - AXIS_DMI_MATCH("HPB440G3", "HP ProBook 440 G3", x_inverted_usd), - AXIS_DMI_MATCH("HPB440G4", "HP ProBook 440 G4", x_inverted), - AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left), - AXIS_DMI_MATCH("HPB450G0", "HP ProBook 450 G0", x_inverted), - AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), - AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), - AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), - AXIS_DMI_MATCH("HPB655x", "HP ProBook 655", xy_swap_inverted), - AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd), - AXIS_DMI_MATCH("HPB63xx", "HP ProBook 63", xy_swap), - AXIS_DMI_MATCH("HPB64xx", "HP ProBook 64", xy_swap), - AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap), - AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted), - AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted), - AXIS_DMI_MATCH("HPZBook17G5", "HP ZBook 17 G5", x_inverted), - AXIS_DMI_MATCH("HPZBook17", "HP ZBook 17", xy_swap_yz_inverted), - { NULL, } -/* Laptop models without axis info (yet): - * "NC6910" "HP Compaq 6910" - * "NC2400" "HP Compaq nc2400" - * "NX74x0" "HP Compaq nx74" - * "NX6325" "HP Compaq nx6325" - * "NC4400" "HP Compaq nc4400" - */ -}; - -static void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value) -{ - struct acpi_device *dev = lis3_dev.bus_priv; - unsigned long long ret; /* Not used when writing */ - union acpi_object in_obj[1]; - struct acpi_object_list args = { 1, in_obj }; - - in_obj[0].type = ACPI_TYPE_INTEGER; - in_obj[0].integer.value = !!value; - - acpi_evaluate_integer(dev->handle, "ALED", &args, &ret); -} - -static struct delayed_led_classdev hpled_led = { - .led_classdev = { - .name = "hp::hddprotect", - .default_trigger = "none", - .brightness_set = delayed_sysfs_set, - .flags = LED_CORE_SUSPENDRESUME, - }, - .set_brightness = hpled_set, -}; - -static bool hp_accel_i8042_filter(unsigned char data, unsigned char str, - struct serio *port) -{ - static bool extended; - - if (str & I8042_STR_AUXDATA) - return false; - - if (data == 0xe0) { - extended = true; - return true; - } else if (unlikely(extended)) { - extended = false; - - switch (data) { - case ACCEL_1: - case ACCEL_2: - case ACCEL_3: - case ACCEL_4: - return true; - default: - serio_interrupt(port, 0xe0, 0); - return false; - } - } - - return false; -} - -static int lis3lv02d_probe(struct platform_device *device) -{ - int ret; - - lis3_dev.bus_priv = ACPI_COMPANION(&device->dev); - lis3_dev.init = lis3lv02d_acpi_init; - lis3_dev.read = lis3lv02d_acpi_read; - lis3_dev.write = lis3lv02d_acpi_write; - - /* obtain IRQ number of our device from ACPI */ - ret = platform_get_irq_optional(device, 0); - if (ret > 0) - lis3_dev.irq = ret; - - /* If possible use a "standard" axes order */ - if (lis3_dev.ac.x && lis3_dev.ac.y && lis3_dev.ac.z) { - pr_info("Using custom axes %d,%d,%d\n", - lis3_dev.ac.x, lis3_dev.ac.y, lis3_dev.ac.z); - } else if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { - pr_info("laptop model unknown, using default axes configuration\n"); - lis3_dev.ac = lis3lv02d_axis_normal; - } - - /* call the core layer do its init */ - ret = lis3lv02d_init_device(&lis3_dev); - if (ret) - return ret; - - /* filter to remove HPQ6000 accelerometer data - * from keyboard bus stream */ - if (strstr(dev_name(&device->dev), "HPQ6000")) - i8042_install_filter(hp_accel_i8042_filter); - - INIT_WORK(&hpled_led.work, delayed_set_status_worker); - ret = led_classdev_register(NULL, &hpled_led.led_classdev); - if (ret) { - i8042_remove_filter(hp_accel_i8042_filter); - lis3lv02d_joystick_disable(&lis3_dev); - lis3lv02d_poweroff(&lis3_dev); - flush_work(&hpled_led.work); - lis3lv02d_remove_fs(&lis3_dev); - return ret; - } - - return ret; -} - -static int lis3lv02d_remove(struct platform_device *device) -{ - i8042_remove_filter(hp_accel_i8042_filter); - lis3lv02d_joystick_disable(&lis3_dev); - lis3lv02d_poweroff(&lis3_dev); - - led_classdev_unregister(&hpled_led.led_classdev); - flush_work(&hpled_led.work); - - lis3lv02d_remove_fs(&lis3_dev); - return 0; -} - -static int __maybe_unused lis3lv02d_suspend(struct device *dev) -{ - /* make sure the device is off when we suspend */ - lis3lv02d_poweroff(&lis3_dev); - return 0; -} - -static int __maybe_unused lis3lv02d_resume(struct device *dev) -{ - lis3lv02d_poweron(&lis3_dev); - return 0; -} - -static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume); - -/* For the HP MDPS aka 3D Driveguard */ -static struct platform_driver lis3lv02d_driver = { - .probe = lis3lv02d_probe, - .remove = lis3lv02d_remove, - .driver = { - .name = "hp_accel", - .pm = &hp_accel_pm, - .acpi_match_table = lis3lv02d_device_ids, - }, -}; -module_platform_driver(lis3lv02d_driver); - -MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS and support for disk protection LED."); -MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); -MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/tc1100-wmi.c b/drivers/platform/x86/tc1100-wmi.c deleted file mode 100644 index ded26213c420..000000000000 --- a/drivers/platform/x86/tc1100-wmi.c +++ /dev/null @@ -1,265 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * HP Compaq TC1100 Tablet WMI Extras Driver - * - * Copyright (C) 2007 Carlos Corbacho - * Copyright (C) 2004 Jamey Hicks - * Copyright (C) 2001, 2002 Andy Grover - * Copyright (C) 2001, 2002 Paul Diefenbaugh - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include - -#define GUID "C364AC71-36DB-495A-8494-B439D472A505" - -#define TC1100_INSTANCE_WIRELESS 1 -#define TC1100_INSTANCE_JOGDIAL 2 - -MODULE_AUTHOR("Jamey Hicks, Carlos Corbacho"); -MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("wmi:C364AC71-36DB-495A-8494-B439D472A505"); - -static struct platform_device *tc1100_device; - -struct tc1100_data { - u32 wireless; - u32 jogdial; -}; - -#ifdef CONFIG_PM -static struct tc1100_data suspend_data; -#endif - -/* -------------------------------------------------------------------------- - Device Management - -------------------------------------------------------------------------- */ - -static int get_state(u32 *out, u8 instance) -{ - u32 tmp; - acpi_status status; - struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; - - if (!out) - return -EINVAL; - - if (instance > 2) - return -ENODEV; - - status = wmi_query_block(GUID, instance, &result); - if (ACPI_FAILURE(status)) - return -ENODEV; - - obj = (union acpi_object *) result.pointer; - if (obj && obj->type == ACPI_TYPE_INTEGER) { - tmp = obj->integer.value; - } else { - tmp = 0; - } - - if (result.length > 0) - kfree(result.pointer); - - switch (instance) { - case TC1100_INSTANCE_WIRELESS: - *out = (tmp == 3) ? 1 : 0; - return 0; - case TC1100_INSTANCE_JOGDIAL: - *out = (tmp == 1) ? 0 : 1; - return 0; - default: - return -ENODEV; - } -} - -static int set_state(u32 *in, u8 instance) -{ - u32 value; - acpi_status status; - struct acpi_buffer input; - - if (!in) - return -EINVAL; - - if (instance > 2) - return -ENODEV; - - switch (instance) { - case TC1100_INSTANCE_WIRELESS: - value = (*in) ? 1 : 2; - break; - case TC1100_INSTANCE_JOGDIAL: - value = (*in) ? 0 : 1; - break; - default: - return -ENODEV; - } - - input.length = sizeof(u32); - input.pointer = &value; - - status = wmi_set_block(GUID, instance, &input); - if (ACPI_FAILURE(status)) - return -ENODEV; - - return 0; -} - -/* -------------------------------------------------------------------------- - FS Interface (/sys) - -------------------------------------------------------------------------- */ - -/* - * Read/ write bool sysfs macro - */ -#define show_set_bool(value, instance) \ -static ssize_t \ -show_bool_##value(struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - u32 result; \ - acpi_status status = get_state(&result, instance); \ - if (ACPI_SUCCESS(status)) \ - return sprintf(buf, "%d\n", result); \ - return sprintf(buf, "Read error\n"); \ -} \ -\ -static ssize_t \ -set_bool_##value(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - u32 tmp = simple_strtoul(buf, NULL, 10); \ - acpi_status status = set_state(&tmp, instance); \ - if (ACPI_FAILURE(status)) \ - return -EINVAL; \ - return count; \ -} \ -static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, \ - show_bool_##value, set_bool_##value); - -show_set_bool(wireless, TC1100_INSTANCE_WIRELESS); -show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL); - -static struct attribute *tc1100_attributes[] = { - &dev_attr_wireless.attr, - &dev_attr_jogdial.attr, - NULL -}; - -static const struct attribute_group tc1100_attribute_group = { - .attrs = tc1100_attributes, -}; - -/* -------------------------------------------------------------------------- - Driver Model - -------------------------------------------------------------------------- */ - -static int __init tc1100_probe(struct platform_device *device) -{ - return sysfs_create_group(&device->dev.kobj, &tc1100_attribute_group); -} - - -static int tc1100_remove(struct platform_device *device) -{ - sysfs_remove_group(&device->dev.kobj, &tc1100_attribute_group); - - return 0; -} - -#ifdef CONFIG_PM -static int tc1100_suspend(struct device *dev) -{ - int ret; - - ret = get_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS); - if (ret) - return ret; - - ret = get_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL); - if (ret) - return ret; - - return 0; -} - -static int tc1100_resume(struct device *dev) -{ - int ret; - - ret = set_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS); - if (ret) - return ret; - - ret = set_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL); - if (ret) - return ret; - - return 0; -} - -static const struct dev_pm_ops tc1100_pm_ops = { - .suspend = tc1100_suspend, - .resume = tc1100_resume, - .freeze = tc1100_suspend, - .restore = tc1100_resume, -}; -#endif - -static struct platform_driver tc1100_driver = { - .driver = { - .name = "tc1100-wmi", -#ifdef CONFIG_PM - .pm = &tc1100_pm_ops, -#endif - }, - .remove = tc1100_remove, -}; - -static int __init tc1100_init(void) -{ - int error; - - if (!wmi_has_guid(GUID)) - return -ENODEV; - - tc1100_device = platform_device_alloc("tc1100-wmi", PLATFORM_DEVID_NONE); - if (!tc1100_device) - return -ENOMEM; - - error = platform_device_add(tc1100_device); - if (error) - goto err_device_put; - - error = platform_driver_probe(&tc1100_driver, tc1100_probe); - if (error) - goto err_device_del; - - pr_info("HP Compaq TC1100 Tablet WMI Extras loaded\n"); - return 0; - - err_device_del: - platform_device_del(tc1100_device); - err_device_put: - platform_device_put(tc1100_device); - return error; -} - -static void __exit tc1100_exit(void) -{ - platform_device_unregister(tc1100_device); - platform_driver_unregister(&tc1100_driver); -} - -module_init(tc1100_init); -module_exit(tc1100_exit); -- cgit v1.2.3 From 06463f6e98df34908c26aa8e7a31a279646b1f51 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 9 Nov 2022 14:42:49 -0800 Subject: wifi: wl1251: drop support for platform data Remove support for configuring the device via platform data because there are no users of wl1251_platform_data left in the mainline kernel. Signed-off-by: Dmitry Torokhov Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221109224250.2885119-2-dmitry.torokhov@gmail.com --- MAINTAINERS | 1 - drivers/net/wireless/ti/Kconfig | 8 ----- drivers/net/wireless/ti/wilink_platform_data.c | 35 -------------------- drivers/net/wireless/ti/wl1251/sdio.c | 8 +---- drivers/net/wireless/ti/wl1251/spi.c | 15 ++------- drivers/net/wireless/ti/wlcore/spi.c | 1 - include/linux/wl12xx.h | 44 -------------------------- 7 files changed, 4 insertions(+), 108 deletions(-) delete mode 100644 drivers/net/wireless/ti/wilink_platform_data.c delete mode 100644 include/linux/wl12xx.h (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 377aedb3808b..56764f76752b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20665,7 +20665,6 @@ W: https://wireless.wiki.kernel.org/en/users/Drivers/wl12xx W: https://wireless.wiki.kernel.org/en/users/Drivers/wl1251 T: git git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx.git F: drivers/net/wireless/ti/ -F: include/linux/wl12xx.h TIMEKEEPING, CLOCKSOURCE CORE, NTP, ALARMTIMER M: John Stultz diff --git a/drivers/net/wireless/ti/Kconfig b/drivers/net/wireless/ti/Kconfig index 7c0b17a76fe2..3fcd9e395f72 100644 --- a/drivers/net/wireless/ti/Kconfig +++ b/drivers/net/wireless/ti/Kconfig @@ -18,12 +18,4 @@ source "drivers/net/wireless/ti/wl18xx/Kconfig" # keep last for automatic dependencies source "drivers/net/wireless/ti/wlcore/Kconfig" -config WILINK_PLATFORM_DATA - bool "TI WiLink platform data" - depends on WLCORE_SDIO || WL1251_SDIO - default y - help - Small platform data bit needed to pass data to the sdio modules. - - endif # WLAN_VENDOR_TI diff --git a/drivers/net/wireless/ti/wilink_platform_data.c b/drivers/net/wireless/ti/wilink_platform_data.c deleted file mode 100644 index 1de6a62d526f..000000000000 --- a/drivers/net/wireless/ti/wilink_platform_data.c +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * This file is part of wl12xx - * - * Copyright (C) 2010-2011 Texas Instruments, Inc. - */ - -#include -#include -#include - -static struct wl1251_platform_data *wl1251_platform_data; - -int __init wl1251_set_platform_data(const struct wl1251_platform_data *data) -{ - if (wl1251_platform_data) - return -EBUSY; - if (!data) - return -EINVAL; - - wl1251_platform_data = kmemdup(data, sizeof(*data), GFP_KERNEL); - if (!wl1251_platform_data) - return -ENOMEM; - - return 0; -} - -struct wl1251_platform_data *wl1251_get_platform_data(void) -{ - if (!wl1251_platform_data) - return ERR_PTR(-ENODEV); - - return wl1251_platform_data; -} -EXPORT_SYMBOL(wl1251_get_platform_data); diff --git a/drivers/net/wireless/ti/wl1251/sdio.c b/drivers/net/wireless/ti/wl1251/sdio.c index c9a4e9a43400..301bd0043a43 100644 --- a/drivers/net/wireless/ti/wl1251/sdio.c +++ b/drivers/net/wireless/ti/wl1251/sdio.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -197,7 +196,6 @@ static int wl1251_sdio_probe(struct sdio_func *func, struct wl1251 *wl; struct ieee80211_hw *hw; struct wl1251_sdio *wl_sdio; - const struct wl1251_platform_data *wl1251_board_data; struct device_node *np = func->dev.of_node; hw = wl1251_alloc_hw(); @@ -225,11 +223,7 @@ static int wl1251_sdio_probe(struct sdio_func *func, wl->if_priv = wl_sdio; wl->if_ops = &wl1251_sdio_ops; - wl1251_board_data = wl1251_get_platform_data(); - if (!IS_ERR(wl1251_board_data)) { - wl->irq = wl1251_board_data->irq; - wl->use_eeprom = wl1251_board_data->use_eeprom; - } else if (np) { + if (np) { wl->use_eeprom = of_property_read_bool(np, "ti,wl1251-has-eeprom"); wl->irq = of_irq_get(np, 0); if (wl->irq == -EPROBE_DEFER) { diff --git a/drivers/net/wireless/ti/wl1251/spi.c b/drivers/net/wireless/ti/wl1251/spi.c index 9df38726e8b0..08d9814b49c1 100644 --- a/drivers/net/wireless/ti/wl1251/spi.c +++ b/drivers/net/wireless/ti/wl1251/spi.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -226,16 +225,13 @@ static const struct wl1251_if_operations wl1251_spi_ops = { static int wl1251_spi_probe(struct spi_device *spi) { - struct wl1251_platform_data *pdata = dev_get_platdata(&spi->dev); struct device_node *np = spi->dev.of_node; struct ieee80211_hw *hw; struct wl1251 *wl; int ret; - if (!np && !pdata) { - wl1251_error("no platform data"); + if (!np) return -ENODEV; - } hw = wl1251_alloc_hw(); if (IS_ERR(hw)) @@ -259,14 +255,9 @@ static int wl1251_spi_probe(struct spi_device *spi) goto out_free; } - if (np) { - wl->use_eeprom = of_property_read_bool(np, "ti,wl1251-has-eeprom"); - wl->power_gpio = of_get_named_gpio(np, "ti,power-gpio", 0); - } else if (pdata) { - wl->power_gpio = pdata->power_gpio; - wl->use_eeprom = pdata->use_eeprom; - } + wl->use_eeprom = of_property_read_bool(np, "ti,wl1251-has-eeprom"); + wl->power_gpio = of_get_named_gpio(np, "ti,power-gpio", 0); if (wl->power_gpio == -EPROBE_DEFER) { ret = -EPROBE_DEFER; goto out_free; diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index 7eae1ec2eb2b..2d2edddc77bd 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/include/linux/wl12xx.h b/include/linux/wl12xx.h deleted file mode 100644 index 03d61f1d23ab..000000000000 --- a/include/linux/wl12xx.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * This file is part of wl12xx - * - * Copyright (C) 2009 Nokia Corporation - * - * Contact: Luciano Coelho - */ - -#ifndef _LINUX_WL12XX_H -#define _LINUX_WL12XX_H - -#include - -struct wl1251_platform_data { - int power_gpio; - /* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */ - int irq; - bool use_eeprom; -}; - -#ifdef CONFIG_WILINK_PLATFORM_DATA - -int wl1251_set_platform_data(const struct wl1251_platform_data *data); - -struct wl1251_platform_data *wl1251_get_platform_data(void); - -#else - -static inline -int wl1251_set_platform_data(const struct wl1251_platform_data *data) -{ - return -ENOSYS; -} - -static inline -struct wl1251_platform_data *wl1251_get_platform_data(void) -{ - return ERR_PTR(-ENODATA); -} - -#endif - -#endif -- cgit v1.2.3 From 9a758d8756daab5b8fda006e131c066336b16a32 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Fri, 11 Nov 2022 14:30:23 +0100 Subject: drm: Move nomodeset kernel parameter to drivers/video Move the nomodeset kernel parameter to drivers/video to make it available to non-DRM drivers. Adapt the interface, but keep the DRM interface drm_firmware_drivers_only() to avoid churn within DRM. The function should later be inlined into callers. The parameter disables any DRM graphics driver that would replace a driver for firmware-provided scanout buffers. It is an option to easily fallback to basic graphics output if the hardware's native driver is broken. Moving it to a more prominent location wil make it available to fbdev as well. v2: * clarify the meaning of the nomodeset parameter (Javier) Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20221111133024.9897-2-tzimmermann@suse.de --- Documentation/admin-guide/kernel-parameters.txt | 15 ++++++++------ MAINTAINERS | 2 ++ drivers/gpu/drm/Kconfig | 7 +------ drivers/gpu/drm/Makefile | 1 - drivers/gpu/drm/drm_nomodeset.c | 24 ----------------------- drivers/video/Kconfig | 4 ++++ drivers/video/Makefile | 1 + drivers/video/nomodeset.c | 26 +++++++++++++++++++++++++ include/drm/drm_drv.h | 8 +++++++- include/video/nomodeset.h | 8 ++++++++ 10 files changed, 58 insertions(+), 38 deletions(-) delete mode 100644 drivers/gpu/drm/drm_nomodeset.c create mode 100644 drivers/video/nomodeset.c create mode 100644 include/video/nomodeset.h (limited to 'MAINTAINERS') diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index a465d5242774..88561b3ce28a 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3777,12 +3777,15 @@ shutdown the other cpus. Instead use the REBOOT_VECTOR irq. - nomodeset Disable kernel modesetting. DRM drivers will not perform - display-mode changes or accelerated rendering. Only the - system framebuffer will be available for use if this was - set-up by the firmware or boot loader. - - Useful as fallback, or for testing and debugging. + nomodeset Disable kernel modesetting. Most systems' firmware + sets up a display mode and provides framebuffer memory + for output. With nomodeset, DRM and fbdev drivers will + not load if they could possibly displace the pre- + initialized output. Only the system framebuffer will + be available for use. The respective drivers will not + perform display-mode changes or accelerated rendering. + + Useful as error fallback, or for testing and debugging. nomodule Disable module load diff --git a/MAINTAINERS b/MAINTAINERS index 178a7a383b5f..7dc3b719b2a2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6701,8 +6701,10 @@ F: drivers/gpu/drm/drm_aperture.c F: drivers/gpu/drm/tiny/ofdrm.c F: drivers/gpu/drm/tiny/simpledrm.c F: drivers/video/aperture.c +F: drivers/video/nomodeset.c F: include/drm/drm_aperture.h F: include/linux/aperture.h +F: include/video/nomodeset.h DRM DRIVER FOR SIS VIDEO CARDS S: Orphan / Obsolete diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 34f5a092c99e..722ded626399 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -8,7 +8,6 @@ menuconfig DRM tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)" depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && HAS_DMA - select DRM_NOMODESET select DRM_PANEL_ORIENTATION_QUIRKS select HDMI select FB_CMDLINE @@ -19,6 +18,7 @@ menuconfig DRM # gallium uses SYS_kcmp for os_same_file_description() to de-duplicate # device and dmabuf fd. Let's make sure that is available for our userspace. select KCMP + select VIDEO_NOMODESET help Kernel-level support for the Direct Rendering Infrastructure (DRI) introduced in XFree86 4.0. If you say Y here, you need to select @@ -514,11 +514,6 @@ config DRM_EXPORT_FOR_TESTS config DRM_PANEL_ORIENTATION_QUIRKS tristate -# Separate option because nomodeset parameter is global and expected built-in -config DRM_NOMODESET - bool - default n - config DRM_LIB_RANDOM bool default n diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index c44a54cadb61..f92cd7892711 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -72,7 +72,6 @@ drm-$(CONFIG_DRM_PRIVACY_SCREEN) += \ drm_privacy_screen_x86.o obj-$(CONFIG_DRM) += drm.o -obj-$(CONFIG_DRM_NOMODESET) += drm_nomodeset.o obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o # diff --git a/drivers/gpu/drm/drm_nomodeset.c b/drivers/gpu/drm/drm_nomodeset.c deleted file mode 100644 index f3978d5bd3a1..000000000000 --- a/drivers/gpu/drm/drm_nomodeset.c +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include - -static bool drm_nomodeset; - -bool drm_firmware_drivers_only(void) -{ - return drm_nomodeset; -} -EXPORT_SYMBOL(drm_firmware_drivers_only); - -static int __init disable_modeset(char *str) -{ - drm_nomodeset = true; - - pr_warn("Booted with the nomodeset parameter. Only the system framebuffer will be available\n"); - - return 1; -} - -/* Disable kernel modesetting */ -__setup("nomodeset", disable_modeset); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 0587e21abad9..6d2fde6c5d11 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -11,6 +11,10 @@ config APERTURE_HELPERS Support tracking and hand-over of aperture ownership. Required by graphics drivers for firmware-provided framebuffers. +config VIDEO_NOMODESET + bool + default n + if HAS_IOMEM config HAVE_FB_ATMEL diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 5bb6b452cc83..a50eb528ed3c 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_APERTURE_HELPERS) += aperture.o obj-$(CONFIG_VGASTATE) += vgastate.o +obj-$(CONFIG_VIDEO_NOMODESET) += nomodeset.o obj-$(CONFIG_HDMI) += hdmi.o obj-$(CONFIG_VT) += console/ diff --git a/drivers/video/nomodeset.c b/drivers/video/nomodeset.c new file mode 100644 index 000000000000..13cc8b719697 --- /dev/null +++ b/drivers/video/nomodeset.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +#include