Skip to content
Snippets Groups Projects
Commit e55bb20d authored by Stephen Rothwell's avatar Stephen Rothwell
Browse files

Merge remote-tracking branch 'char-misc/char-misc-next'

parents 9c03f2c0 72b6500c
No related branches found
No related tags found
No related merge requests found
Showing
with 773 additions and 127 deletions
Lattice MachXO2 Slave SPI FPGA Manager
Lattice MachXO2 FPGAs support a method of loading the bitstream over
'slave SPI' interface.
See 'MachXO2ProgrammingandConfigurationUsageGuide.pdf' on www.latticesemi.com
Required properties:
- compatible: should contain "lattice,machxo2-slave-spi"
- reg: spi chip select of the FPGA
Example for full FPGA configuration:
fpga-region0 {
compatible = "fpga-region";
fpga-mgr = <&fpga_mgr_spi>;
#address-cells = <0x1>;
#size-cells = <0x1>;
};
spi1: spi@2000 {
...
fpga_mgr_spi: fpga-mgr@0 {
compatible = "lattice,machxo2-slave-spi";
spi-max-frequency = <8000000>;
reg = <0>;
};
};
......@@ -2058,8 +2058,8 @@ static size_t binder_validate_object(struct binder_buffer *buffer, u64 offset)
struct binder_object_header *hdr;
size_t object_size = 0;
if (offset > buffer->data_size - sizeof(*hdr) ||
buffer->data_size < sizeof(*hdr) ||
if (buffer->data_size < sizeof(*hdr) ||
offset > buffer->data_size - sizeof(*hdr) ||
!IS_ALIGNED(offset, sizeof(u32)))
return 0;
......
......@@ -191,7 +191,7 @@ mspec_close(struct vm_area_struct *vma)
*
* Creates a mspec page and maps it to user space.
*/
static int
static vm_fault_t
mspec_fault(struct vm_fault *vmf)
{
unsigned long paddr, maddr;
......@@ -223,14 +223,7 @@ mspec_fault(struct vm_fault *vmf)
pfn = paddr >> PAGE_SHIFT;
/*
* vm_insert_pfn can fail with -EBUSY, but in that case it will
* be because another thread has installed the pte first, so it
* is no problem.
*/
vm_insert_pfn(vmf->vma, vmf->address, pfn);
return VM_FAULT_NOPAGE;
return vmf_insert_pfn(vmf->vma, vmf->address, pfn);
}
static const struct vm_operations_struct mspec_vm_ops = {
......
......@@ -55,6 +55,14 @@ config GOOGLE_MEMCONSOLE_X86_LEGACY
the EBDA on Google servers. If found, this log is exported to
userland in the file /sys/firmware/log.
config GOOGLE_FRAMEBUFFER_COREBOOT
tristate "Coreboot Framebuffer"
depends on FB_SIMPLE
depends on GOOGLE_COREBOOT_TABLE
help
This option enables the kernel to search for a framebuffer in
the coreboot table. If found, it is registered with simplefb.
config GOOGLE_MEMCONSOLE_COREBOOT
tristate "Firmware Memory Console"
depends on GOOGLE_COREBOOT_TABLE
......
......@@ -4,6 +4,7 @@ obj-$(CONFIG_GOOGLE_SMI) += gsmi.o
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE) += coreboot_table.o
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_ACPI) += coreboot_table-acpi.o
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_OF) += coreboot_table-of.o
obj-$(CONFIG_GOOGLE_FRAMEBUFFER_COREBOOT) += framebuffer-coreboot.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE_COREBOOT) += memconsole-coreboot.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY) += memconsole-x86-legacy.o
......
......@@ -53,7 +53,7 @@ static int coreboot_table_acpi_probe(struct platform_device *pdev)
if (!ptr)
return -ENOMEM;
return coreboot_table_init(ptr);
return coreboot_table_init(&pdev->dev, ptr);
}
static int coreboot_table_acpi_remove(struct platform_device *pdev)
......
......@@ -34,7 +34,7 @@ static int coreboot_table_of_probe(struct platform_device *pdev)
if (!ptr)
return -ENOMEM;
return coreboot_table_init(ptr);
return coreboot_table_init(&pdev->dev, ptr);
}
static int coreboot_table_of_remove(struct platform_device *pdev)
......
......@@ -4,6 +4,7 @@
* Module providing coreboot table access.
*
* Copyright 2017 Google Inc.
* Copyright 2017 Samuel Holland <samuel@sholland.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by
......@@ -15,37 +16,96 @@
* GNU General Public License for more details.
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "coreboot_table.h"
struct coreboot_table_entry {
u32 tag;
u32 size;
};
#define CB_DEV(d) container_of(d, struct coreboot_device, dev)
#define CB_DRV(d) container_of(d, struct coreboot_driver, drv)
static struct coreboot_table_header __iomem *ptr_header;
/*
* This function parses the coreboot table for an entry that contains the base
* address of the given entry tag. The coreboot table consists of a header
* directly followed by a number of small, variable-sized entries, which each
* contain an identifying tag and their length as the first two fields.
*/
int coreboot_table_find(int tag, void *data, size_t data_size)
static int coreboot_bus_match(struct device *dev, struct device_driver *drv)
{
struct coreboot_table_header header;
struct coreboot_table_entry entry;
void *ptr_entry;
int i;
struct coreboot_device *device = CB_DEV(dev);
struct coreboot_driver *driver = CB_DRV(drv);
if (!ptr_header)
return -EPROBE_DEFER;
return device->entry.tag == driver->tag;
}
static int coreboot_bus_probe(struct device *dev)
{
int ret = -ENODEV;
struct coreboot_device *device = CB_DEV(dev);
struct coreboot_driver *driver = CB_DRV(dev->driver);
if (driver->probe)
ret = driver->probe(device);
return ret;
}
static int coreboot_bus_remove(struct device *dev)
{
int ret = 0;
struct coreboot_device *device = CB_DEV(dev);
struct coreboot_driver *driver = CB_DRV(dev->driver);
if (driver->remove)
ret = driver->remove(device);
return ret;
}
static struct bus_type coreboot_bus_type = {
.name = "coreboot",
.match = coreboot_bus_match,
.probe = coreboot_bus_probe,
.remove = coreboot_bus_remove,
};
static int __init coreboot_bus_init(void)
{
return bus_register(&coreboot_bus_type);
}
module_init(coreboot_bus_init);
static void coreboot_device_release(struct device *dev)
{
struct coreboot_device *device = CB_DEV(dev);
kfree(device);
}
int coreboot_driver_register(struct coreboot_driver *driver)
{
driver->drv.bus = &coreboot_bus_type;
return driver_register(&driver->drv);
}
EXPORT_SYMBOL(coreboot_driver_register);
void coreboot_driver_unregister(struct coreboot_driver *driver)
{
driver_unregister(&driver->drv);
}
EXPORT_SYMBOL(coreboot_driver_unregister);
int coreboot_table_init(struct device *dev, void __iomem *ptr)
{
int i, ret;
void *ptr_entry;
struct coreboot_device *device;
struct coreboot_table_entry entry;
struct coreboot_table_header header;
ptr_header = ptr;
memcpy_fromio(&header, ptr_header, sizeof(header));
if (strncmp(header.signature, "LBIO", sizeof(header.signature))) {
......@@ -54,37 +114,41 @@ int coreboot_table_find(int tag, void *data, size_t data_size)
}
ptr_entry = (void *)ptr_header + header.header_bytes;
for (i = 0; i < header.table_entries; i++) {
memcpy_fromio(&entry, ptr_entry, sizeof(entry));
if (entry.tag == tag) {
if (data_size < entry.size)
return -EINVAL;
memcpy_fromio(data, ptr_entry, entry.size);
device = kzalloc(sizeof(struct device) + entry.size, GFP_KERNEL);
if (!device) {
ret = -ENOMEM;
break;
}
dev_set_name(&device->dev, "coreboot%d", i);
device->dev.parent = dev;
device->dev.bus = &coreboot_bus_type;
device->dev.release = coreboot_device_release;
memcpy_fromio(&device->entry, ptr_entry, entry.size);
return 0;
ret = device_register(&device->dev);
if (ret) {
put_device(&device->dev);
break;
}
ptr_entry += entry.size;
}
return -ENOENT;
}
EXPORT_SYMBOL(coreboot_table_find);
int coreboot_table_init(void __iomem *ptr)
{
ptr_header = ptr;
return 0;
return ret;
}
EXPORT_SYMBOL(coreboot_table_init);
int coreboot_table_exit(void)
{
if (ptr_header)
if (ptr_header) {
bus_unregister(&coreboot_bus_type);
iounmap(ptr_header);
ptr_header = NULL;
}
return 0;
}
......
......@@ -3,7 +3,9 @@
*
* Internal header for coreboot table access.
*
* Copyright 2014 Gerd Hoffmann <kraxel@redhat.com>
* Copyright 2017 Google Inc.
* Copyright 2017 Samuel Holland <samuel@sholland.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by
......@@ -20,14 +22,6 @@
#include <linux/io.h>
/* List of coreboot entry structures that is used */
struct lb_cbmem_ref {
uint32_t tag;
uint32_t size;
uint64_t cbmem_addr;
};
/* Coreboot table header structure */
struct coreboot_table_header {
char signature[4];
......@@ -38,11 +32,67 @@ struct coreboot_table_header {
u32 table_entries;
};
/* Retrieve coreboot table entry with tag *tag* and copy it to data */
int coreboot_table_find(int tag, void *data, size_t data_size);
/* List of coreboot entry structures that is used */
/* Generic */
struct coreboot_table_entry {
u32 tag;
u32 size;
};
/* Points to a CBMEM entry */
struct lb_cbmem_ref {
u32 tag;
u32 size;
u64 cbmem_addr;
};
/* Describes framebuffer setup by coreboot */
struct lb_framebuffer {
u32 tag;
u32 size;
u64 physical_address;
u32 x_resolution;
u32 y_resolution;
u32 bytes_per_line;
u8 bits_per_pixel;
u8 red_mask_pos;
u8 red_mask_size;
u8 green_mask_pos;
u8 green_mask_size;
u8 blue_mask_pos;
u8 blue_mask_size;
u8 reserved_mask_pos;
u8 reserved_mask_size;
};
/* A device, additionally with information from coreboot. */
struct coreboot_device {
struct device dev;
union {
struct coreboot_table_entry entry;
struct lb_cbmem_ref cbmem_ref;
struct lb_framebuffer framebuffer;
};
};
/* A driver for handling devices described in coreboot tables. */
struct coreboot_driver {
int (*probe)(struct coreboot_device *);
int (*remove)(struct coreboot_device *);
struct device_driver drv;
u32 tag;
};
/* Register a driver that uses the data from a coreboot table. */
int coreboot_driver_register(struct coreboot_driver *driver);
/* Unregister a driver that uses the data from a coreboot table. */
void coreboot_driver_unregister(struct coreboot_driver *driver);
/* Initialize coreboot table module given a pointer to iomem */
int coreboot_table_init(void __iomem *ptr);
int coreboot_table_init(struct device *dev, void __iomem *ptr);
/* Cleanup coreboot table module */
int coreboot_table_exit(void);
......
/*
* framebuffer-coreboot.c
*
* Memory based framebuffer accessed through coreboot table.
*
* Copyright 2012-2013 David Herrmann <dh.herrmann@gmail.com>
* Copyright 2017 Google Inc.
* Copyright 2017 Samuel Holland <samuel@sholland.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/platform_data/simplefb.h>
#include <linux/platform_device.h>
#include "coreboot_table.h"
#define CB_TAG_FRAMEBUFFER 0x12
static const struct simplefb_format formats[] = SIMPLEFB_FORMATS;
static int framebuffer_probe(struct coreboot_device *dev)
{
int i;
u32 length;
struct lb_framebuffer *fb = &dev->framebuffer;
struct platform_device *pdev;
struct resource res;
struct simplefb_platform_data pdata = {
.width = fb->x_resolution,
.height = fb->y_resolution,
.stride = fb->bytes_per_line,
.format = NULL,
};
for (i = 0; i < ARRAY_SIZE(formats); ++i) {
if (fb->bits_per_pixel == formats[i].bits_per_pixel &&
fb->red_mask_pos == formats[i].red.offset &&
fb->red_mask_size == formats[i].red.length &&
fb->green_mask_pos == formats[i].green.offset &&
fb->green_mask_size == formats[i].green.length &&
fb->blue_mask_pos == formats[i].blue.offset &&
fb->blue_mask_size == formats[i].blue.length &&
fb->reserved_mask_pos == formats[i].transp.offset &&
fb->reserved_mask_size == formats[i].transp.length)
pdata.format = formats[i].name;
}
if (!pdata.format)
return -ENODEV;
memset(&res, 0, sizeof(res));
res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
res.name = "Coreboot Framebuffer";
res.start = fb->physical_address;
length = PAGE_ALIGN(fb->y_resolution * fb->bytes_per_line);
res.end = res.start + length - 1;
if (res.end <= res.start)
return -EINVAL;
pdev = platform_device_register_resndata(&dev->dev,
"simple-framebuffer", 0,
&res, 1, &pdata,
sizeof(pdata));
if (IS_ERR(pdev))
pr_warn("coreboot: could not register framebuffer\n");
else
dev_set_drvdata(&dev->dev, pdev);
return PTR_ERR_OR_ZERO(pdev);
}
static int framebuffer_remove(struct coreboot_device *dev)
{
struct platform_device *pdev = dev_get_drvdata(&dev->dev);
platform_device_unregister(pdev);
return 0;
}
static struct coreboot_driver framebuffer_driver = {
.probe = framebuffer_probe,
.remove = framebuffer_remove,
.drv = {
.name = "framebuffer",
},
.tag = CB_TAG_FRAMEBUFFER,
};
static int __init coreboot_framebuffer_init(void)
{
return coreboot_driver_register(&framebuffer_driver);
}
static void coreboot_framebuffer_exit(void)
{
coreboot_driver_unregister(&framebuffer_driver);
}
module_init(coreboot_framebuffer_init);
module_exit(coreboot_framebuffer_exit);
MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
MODULE_LICENSE("GPL");
......@@ -15,9 +15,9 @@
* GNU General Public License for more details.
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "memconsole.h"
#include "coreboot_table.h"
......@@ -73,18 +73,19 @@ static ssize_t memconsole_coreboot_read(char *buf, loff_t pos, size_t count)
return done;
}
static int memconsole_coreboot_init(phys_addr_t physaddr)
static int memconsole_probe(struct coreboot_device *dev)
{
struct cbmem_cons __iomem *tmp_cbmc;
tmp_cbmc = memremap(physaddr, sizeof(*tmp_cbmc), MEMREMAP_WB);
tmp_cbmc = memremap(dev->cbmem_ref.cbmem_addr,
sizeof(*tmp_cbmc), MEMREMAP_WB);
if (!tmp_cbmc)
return -ENOMEM;
/* Read size only once to prevent overrun attack through /dev/mem. */
cbmem_console_size = tmp_cbmc->size_dont_access_after_boot;
cbmem_console = memremap(physaddr,
cbmem_console = memremap(dev->cbmem_ref.cbmem_addr,
cbmem_console_size + sizeof(*cbmem_console),
MEMREMAP_WB);
memunmap(tmp_cbmc);
......@@ -93,26 +94,11 @@ static int memconsole_coreboot_init(phys_addr_t physaddr)
return -ENOMEM;
memconsole_setup(memconsole_coreboot_read);
return 0;
}
static int memconsole_probe(struct platform_device *pdev)
{
int ret;
struct lb_cbmem_ref entry;
ret = coreboot_table_find(CB_TAG_CBMEM_CONSOLE, &entry, sizeof(entry));
if (ret)
return ret;
ret = memconsole_coreboot_init(entry.cbmem_addr);
if (ret)
return ret;
return memconsole_sysfs_init();
}
static int memconsole_remove(struct platform_device *pdev)
static int memconsole_remove(struct coreboot_device *dev)
{
memconsole_exit();
......@@ -122,28 +108,27 @@ static int memconsole_remove(struct platform_device *pdev)
return 0;
}
static struct platform_driver memconsole_driver = {
static struct coreboot_driver memconsole_driver = {
.probe = memconsole_probe,
.remove = memconsole_remove,
.driver = {
.drv = {
.name = "memconsole",
},
.tag = CB_TAG_CBMEM_CONSOLE,
};
static int __init platform_memconsole_init(void)
static void coreboot_memconsole_exit(void)
{
struct platform_device *pdev;
pdev = platform_device_register_simple("memconsole", -1, NULL, 0);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
platform_driver_register(&memconsole_driver);
coreboot_driver_unregister(&memconsole_driver);
}
return 0;
static int __init coreboot_memconsole_init(void)
{
return coreboot_driver_register(&memconsole_driver);
}
module_init(platform_memconsole_init);
module_exit(coreboot_memconsole_exit);
module_init(coreboot_memconsole_init);
MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL");
......@@ -286,20 +286,15 @@ static int vpd_sections_init(phys_addr_t physaddr)
return 0;
}
static int vpd_probe(struct platform_device *pdev)
static int vpd_probe(struct coreboot_device *dev)
{
int ret;
struct lb_cbmem_ref entry;
ret = coreboot_table_find(CB_TAG_VPD, &entry, sizeof(entry));
if (ret)
return ret;
vpd_kobj = kobject_create_and_add("vpd", firmware_kobj);
if (!vpd_kobj)
return -ENOMEM;
ret = vpd_sections_init(entry.cbmem_addr);
ret = vpd_sections_init(dev->cbmem_ref.cbmem_addr);
if (ret) {
kobject_put(vpd_kobj);
return ret;
......@@ -308,7 +303,7 @@ static int vpd_probe(struct platform_device *pdev)
return 0;
}
static int vpd_remove(struct platform_device *pdev)
static int vpd_remove(struct coreboot_device *dev)
{
vpd_section_destroy(&ro_vpd);
vpd_section_destroy(&rw_vpd);
......@@ -318,41 +313,27 @@ static int vpd_remove(struct platform_device *pdev)
return 0;
}
static struct platform_driver vpd_driver = {
static struct coreboot_driver vpd_driver = {
.probe = vpd_probe,
.remove = vpd_remove,
.driver = {
.drv = {
.name = "vpd",
},
.tag = CB_TAG_VPD,
};
static struct platform_device *vpd_pdev;
static int __init vpd_platform_init(void)
static int __init coreboot_vpd_init(void)
{
int ret;
ret = platform_driver_register(&vpd_driver);
if (ret)
return ret;
vpd_pdev = platform_device_register_simple("vpd", -1, NULL, 0);
if (IS_ERR(vpd_pdev)) {
platform_driver_unregister(&vpd_driver);
return PTR_ERR(vpd_pdev);
}
return 0;
return coreboot_driver_register(&vpd_driver);
}
static void __exit vpd_platform_exit(void)
static void __exit coreboot_vpd_exit(void)
{
platform_device_unregister(vpd_pdev);
platform_driver_unregister(&vpd_driver);
coreboot_driver_unregister(&vpd_driver);
}
module_init(vpd_platform_init);
module_exit(vpd_platform_exit);
module_init(coreboot_vpd_init);
module_exit(coreboot_vpd_exit);
MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL");
......@@ -53,7 +53,6 @@ config FPGA_MGR_ALTERA_CVP
config FPGA_MGR_ZYNQ_FPGA
tristate "Xilinx Zynq FPGA"
depends on ARCH_ZYNQ || COMPILE_TEST
depends on HAS_DMA
help
FPGA manager driver support for Xilinx Zynq FPGAs.
......@@ -70,6 +69,13 @@ config FPGA_MGR_ICE40_SPI
help
FPGA manager driver support for Lattice iCE40 FPGAs over SPI.
config FPGA_MGR_MACHXO2_SPI
tristate "Lattice MachXO2 SPI"
depends on SPI
help
FPGA manager driver support for Lattice MachXO2 configuration
over slave SPI interface.
config FPGA_MGR_TS73XX
tristate "Technologic Systems TS-73xx SBC FPGA Manager"
depends on ARCH_EP93XX && MACH_TS72XX
......
......@@ -10,6 +10,7 @@ obj-$(CONFIG_FPGA) += fpga-mgr.o
obj-$(CONFIG_FPGA_MGR_ALTERA_CVP) += altera-cvp.o
obj-$(CONFIG_FPGA_MGR_ALTERA_PS_SPI) += altera-ps-spi.o
obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o
obj-$(CONFIG_FPGA_MGR_MACHXO2_SPI) += machxo2-spi.o
obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o
obj-$(CONFIG_FPGA_MGR_TS73XX) += ts73xx-fpga.o
......
......@@ -95,6 +95,11 @@ static void fpga_region_put(struct fpga_region *region)
* fpga_region_program_fpga - program FPGA
* @region: FPGA region
* Program an FPGA using fpga image info (region->info).
* If the region has a get_bridges function, the exclusive reference for the
* bridges will be held if programming succeeds. This is intended to prevent
* reprogramming the region until the caller considers it safe to do so.
* The caller will need to call fpga_bridges_put() before attempting to
* reprogram the region.
* Return 0 for success or negative error code.
*/
int fpga_region_program_fpga(struct fpga_region *region)
......
// SPDX-License-Identifier: GPL-2.0
/*
* Lattice MachXO2 Slave SPI Driver
*
* Manage Lattice FPGA firmware that is loaded over SPI using
* the slave serial configuration interface.
*
* Copyright (C) 2018 Paolo Pisati <p.pisati@gmail.com>
*/
#include <linux/delay.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/spi/spi.h>
/* MachXO2 Programming Guide - sysCONFIG Programming Commands */
#define IDCODE_PUB {0xe0, 0x00, 0x00, 0x00}
#define ISC_ENABLE {0xc6, 0x08, 0x00, 0x00}
#define ISC_ERASE {0x0e, 0x04, 0x00, 0x00}
#define ISC_PROGRAMDONE {0x5e, 0x00, 0x00, 0x00}
#define LSC_INITADDRESS {0x46, 0x00, 0x00, 0x00}
#define LSC_PROGINCRNV {0x70, 0x00, 0x00, 0x01}
#define LSC_READ_STATUS {0x3c, 0x00, 0x00, 0x00}
#define LSC_REFRESH {0x79, 0x00, 0x00, 0x00}
/*
* Max CCLK in Slave SPI mode according to 'MachXO2 Family Data
* Sheet' sysCONFIG Port Timing Specifications (3-36)
*/
#define MACHXO2_MAX_SPEED 66000000
#define MACHXO2_LOW_DELAY_USEC 5
#define MACHXO2_HIGH_DELAY_USEC 200
#define MACHXO2_REFRESH_USEC 4800
#define MACHXO2_MAX_BUSY_LOOP 128
#define MACHXO2_MAX_REFRESH_LOOP 16
#define MACHXO2_PAGE_SIZE 16
#define MACHXO2_BUF_SIZE (MACHXO2_PAGE_SIZE + 4)
/* Status register bits, errors and error mask */
#define BUSY 12
#define DONE 8
#define DVER 27
#define ENAB 9
#define ERRBITS 23
#define ERRMASK 7
#define FAIL 13
#define ENOERR 0 /* no error */
#define EID 1
#define ECMD 2
#define ECRC 3
#define EPREAM 4 /* preamble error */
#define EABRT 5 /* abort error */
#define EOVERFL 6 /* overflow error */
#define ESDMEOF 7 /* SDM EOF */
static inline u8 get_err(unsigned long *status)
{
return (*status >> ERRBITS) & ERRMASK;
}
static int get_status(struct spi_device *spi, unsigned long *status)
{
struct spi_message msg;
struct spi_transfer rx, tx;
static const u8 cmd[] = LSC_READ_STATUS;
int ret;
memset(&rx, 0, sizeof(rx));
memset(&tx, 0, sizeof(tx));
tx.tx_buf = cmd;
tx.len = sizeof(cmd);
rx.rx_buf = status;
rx.len = 4;
spi_message_init(&msg);
spi_message_add_tail(&tx, &msg);
spi_message_add_tail(&rx, &msg);
ret = spi_sync(spi, &msg);
if (ret)
return ret;
*status = be32_to_cpu(*status);
return 0;
}
#ifdef DEBUG
static const char *get_err_string(u8 err)
{
switch (err) {
case ENOERR: return "No Error";
case EID: return "ID ERR";
case ECMD: return "CMD ERR";
case ECRC: return "CRC ERR";
case EPREAM: return "Preamble ERR";
case EABRT: return "Abort ERR";
case EOVERFL: return "Overflow ERR";
case ESDMEOF: return "SDM EOF";
}
return "Default switch case";
}
#endif
static void dump_status_reg(unsigned long *status)
{
#ifdef DEBUG
pr_debug("machxo2 status: 0x%08lX - done=%d, cfgena=%d, busy=%d, fail=%d, devver=%d, err=%s\n",
*status, test_bit(DONE, status), test_bit(ENAB, status),
test_bit(BUSY, status), test_bit(FAIL, status),
test_bit(DVER, status), get_err_string(get_err(status)));
#endif
}
static int wait_until_not_busy(struct spi_device *spi)
{
unsigned long status;
int ret, loop = 0;
do {
ret = get_status(spi, &status);
if (ret)
return ret;
if (++loop >= MACHXO2_MAX_BUSY_LOOP)
return -EBUSY;
} while (test_bit(BUSY, &status));
return 0;
}
static int machxo2_cleanup(struct fpga_manager *mgr)
{
struct spi_device *spi = mgr->priv;
struct spi_message msg;
struct spi_transfer tx[2];
static const u8 erase[] = ISC_ERASE;
static const u8 refresh[] = LSC_REFRESH;
int ret;
memset(tx, 0, sizeof(tx));
spi_message_init(&msg);
tx[0].tx_buf = &erase;
tx[0].len = sizeof(erase);
spi_message_add_tail(&tx[0], &msg);
ret = spi_sync(spi, &msg);
if (ret)
goto fail;
ret = wait_until_not_busy(spi);
if (ret)
goto fail;
spi_message_init(&msg);
tx[1].tx_buf = &refresh;
tx[1].len = sizeof(refresh);
tx[1].delay_usecs = MACHXO2_REFRESH_USEC;
spi_message_add_tail(&tx[1], &msg);
ret = spi_sync(spi, &msg);
if (ret)
goto fail;
return 0;
fail:
dev_err(&mgr->dev, "Cleanup failed\n");
return ret;
}
static enum fpga_mgr_states machxo2_spi_state(struct fpga_manager *mgr)
{
struct spi_device *spi = mgr->priv;
unsigned long status;
get_status(spi, &status);
if (!test_bit(BUSY, &status) && test_bit(DONE, &status) &&
get_err(&status) == ENOERR)
return FPGA_MGR_STATE_OPERATING;
return FPGA_MGR_STATE_UNKNOWN;
}
static int machxo2_write_init(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count)
{
struct spi_device *spi = mgr->priv;
struct spi_message msg;
struct spi_transfer tx[3];
static const u8 enable[] = ISC_ENABLE;
static const u8 erase[] = ISC_ERASE;
static const u8 initaddr[] = LSC_INITADDRESS;
unsigned long status;
int ret;
if ((info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
dev_err(&mgr->dev,
"Partial reconfiguration is not supported\n");
return -ENOTSUPP;
}
get_status(spi, &status);
dump_status_reg(&status);
memset(tx, 0, sizeof(tx));
spi_message_init(&msg);
tx[0].tx_buf = &enable;
tx[0].len = sizeof(enable);
tx[0].delay_usecs = MACHXO2_LOW_DELAY_USEC;
spi_message_add_tail(&tx[0], &msg);
tx[1].tx_buf = &erase;
tx[1].len = sizeof(erase);
spi_message_add_tail(&tx[1], &msg);
ret = spi_sync(spi, &msg);
if (ret)
goto fail;
ret = wait_until_not_busy(spi);
if (ret)
goto fail;
get_status(spi, &status);
if (test_bit(FAIL, &status))
goto fail;
dump_status_reg(&status);
spi_message_init(&msg);
tx[2].tx_buf = &initaddr;
tx[2].len = sizeof(initaddr);
spi_message_add_tail(&tx[2], &msg);
ret = spi_sync(spi, &msg);
if (ret)
goto fail;
get_status(spi, &status);
dump_status_reg(&status);
return 0;
fail:
dev_err(&mgr->dev, "Error during FPGA init.\n");
return ret;
}
static int machxo2_write(struct fpga_manager *mgr, const char *buf,
size_t count)
{
struct spi_device *spi = mgr->priv;
struct spi_message msg;
struct spi_transfer tx;
static const u8 progincr[] = LSC_PROGINCRNV;
u8 payload[MACHXO2_BUF_SIZE];
unsigned long status;
int i, ret;
if (count % MACHXO2_PAGE_SIZE != 0) {
dev_err(&mgr->dev, "Malformed payload.\n");
return -EINVAL;
}
get_status(spi, &status);
dump_status_reg(&status);
memcpy(payload, &progincr, sizeof(progincr));
for (i = 0; i < count; i += MACHXO2_PAGE_SIZE) {
memcpy(&payload[sizeof(progincr)], &buf[i], MACHXO2_PAGE_SIZE);
memset(&tx, 0, sizeof(tx));
spi_message_init(&msg);
tx.tx_buf = payload;
tx.len = MACHXO2_BUF_SIZE;
tx.delay_usecs = MACHXO2_HIGH_DELAY_USEC;
spi_message_add_tail(&tx, &msg);
ret = spi_sync(spi, &msg);
if (ret) {
dev_err(&mgr->dev, "Error loading the bitstream.\n");
return ret;
}
}
get_status(spi, &status);
dump_status_reg(&status);
return 0;
}
static int machxo2_write_complete(struct fpga_manager *mgr,
struct fpga_image_info *info)
{
struct spi_device *spi = mgr->priv;
struct spi_message msg;
struct spi_transfer tx[2];
static const u8 progdone[] = ISC_PROGRAMDONE;
static const u8 refresh[] = LSC_REFRESH;
unsigned long status;
int ret, refreshloop = 0;
memset(tx, 0, sizeof(tx));
spi_message_init(&msg);
tx[0].tx_buf = &progdone;
tx[0].len = sizeof(progdone);
spi_message_add_tail(&tx[0], &msg);
ret = spi_sync(spi, &msg);
if (ret)
goto fail;
ret = wait_until_not_busy(spi);
if (ret)
goto fail;
get_status(spi, &status);
dump_status_reg(&status);
if (!test_bit(DONE, &status)) {
machxo2_cleanup(mgr);
goto fail;
}
do {
spi_message_init(&msg);
tx[1].tx_buf = &refresh;
tx[1].len = sizeof(refresh);
tx[1].delay_usecs = MACHXO2_REFRESH_USEC;
spi_message_add_tail(&tx[1], &msg);
ret = spi_sync(spi, &msg);
if (ret)
goto fail;
/* check refresh status */
get_status(spi, &status);
dump_status_reg(&status);
if (!test_bit(BUSY, &status) && test_bit(DONE, &status) &&
get_err(&status) == ENOERR)
break;
if (++refreshloop == MACHXO2_MAX_REFRESH_LOOP) {
machxo2_cleanup(mgr);
goto fail;
}
} while (1);
get_status(spi, &status);
dump_status_reg(&status);
return 0;
fail:
dev_err(&mgr->dev, "Refresh failed.\n");
return ret;
}
static const struct fpga_manager_ops machxo2_ops = {
.state = machxo2_spi_state,
.write_init = machxo2_write_init,
.write = machxo2_write,
.write_complete = machxo2_write_complete,
};
static int machxo2_spi_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
if (spi->max_speed_hz > MACHXO2_MAX_SPEED) {
dev_err(dev, "Speed is too high\n");
return -EINVAL;
}
return fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
&machxo2_ops, spi);
}
static int machxo2_spi_remove(struct spi_device *spi)
{
struct device *dev = &spi->dev;
fpga_mgr_unregister(dev);
return 0;
}
static const struct of_device_id of_match[] = {
{ .compatible = "lattice,machxo2-slave-spi", },
{}
};
MODULE_DEVICE_TABLE(of, of_match);
static const struct spi_device_id lattice_ids[] = {
{ "machxo2-slave-spi", 0 },
{ },
};
MODULE_DEVICE_TABLE(spi, lattice_ids);
static struct spi_driver machxo2_spi_driver = {
.driver = {
.name = "machxo2-slave-spi",
.of_match_table = of_match_ptr(of_match),
},
.probe = machxo2_spi_probe,
.remove = machxo2_spi_remove,
.id_table = lattice_ids,
};
module_spi_driver(machxo2_spi_driver)
MODULE_AUTHOR("Paolo Pisati <p.pisati@gmail.com>");
MODULE_DESCRIPTION("Load Lattice FPGA firmware over SPI");
MODULE_LICENSE("GPL v2");
......@@ -128,11 +128,12 @@ void cxl_context_set_mapping(struct cxl_context *ctx,
mutex_unlock(&ctx->mapping_lock);
}
static int cxl_mmap_fault(struct vm_fault *vmf)
static vm_fault_t cxl_mmap_fault(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
struct cxl_context *ctx = vma->vm_file->private_data;
u64 area, offset;
vm_fault_t ret;
offset = vmf->pgoff << PAGE_SHIFT;
......@@ -169,11 +170,11 @@ static int cxl_mmap_fault(struct vm_fault *vmf)
return VM_FAULT_SIGBUS;
}
vm_insert_pfn(vma, vmf->address, (area + offset) >> PAGE_SHIFT);
ret = vmf_insert_pfn(vma, vmf->address, (area + offset) >> PAGE_SHIFT);
mutex_unlock(&ctx->status_mutex);
return VM_FAULT_NOPAGE;
return ret;
}
static const struct vm_operations_struct cxl_mmap_vmops = {
......
......@@ -407,7 +407,7 @@ xpnet_send(struct sk_buff *skb, struct xpnet_pending_msg *queued_msg,
* destination partid. If the destination partid octets are 0xffff,
* this packet is to be broadcast to all connected partitions.
*/
static int
static netdev_tx_t
xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct xpnet_pending_msg *queued_msg;
......
......@@ -735,7 +735,7 @@ static int kim_probe(struct platform_device *pdev)
st_kim_devices[0] = pdev;
}
kim_gdata = kzalloc(sizeof(struct kim_data_s), GFP_ATOMIC);
kim_gdata = kzalloc(sizeof(struct kim_data_s), GFP_KERNEL);
if (!kim_gdata) {
pr_err("no mem to allocate");
return -ENOMEM;
......
......@@ -239,9 +239,13 @@ static int tifm_7xx1_resume(struct pci_dev *dev)
unsigned long timeout;
unsigned int good_sockets = 0, bad_sockets = 0;
unsigned long flags;
unsigned char new_ids[fm->num_sockets];
/* Maximum number of entries is 4 */
unsigned char new_ids[4];
DECLARE_COMPLETION_ONSTACK(finish_resume);
if (WARN_ON(fm->num_sockets > ARRAY_SIZE(new_ids)))
return -ENXIO;
pci_set_power_state(dev, PCI_D0);
pci_restore_state(dev);
rc = pci_enable_device(dev);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment