Browse Source

Initial version of zz9000 driver for Linux/m68k

master
Stefan Reinauer 1 year ago
commit
8c81d8c30c
9 changed files with 1683 additions and 0 deletions
  1. +8
    -0
      .gitignore
  2. +22
    -0
      Makefile
  3. +88
    -0
      README.md
  4. +42
    -0
      zz9000.h
  5. +161
    -0
      zz9000_core.c
  6. +457
    -0
      zz9000_graphics.c
  7. +341
    -0
      zz9000_network.c
  8. +164
    -0
      zz9000_regs.h
  9. +400
    -0
      zz9000_usb.c

+ 8
- 0
.gitignore View File

@@ -0,0 +1,8 @@
*.cmd
*.d
*.mod
*.mod.c
*.ko
*.o
Module.symvers
modules.order

+ 22
- 0
Makefile View File

@@ -0,0 +1,22 @@
obj-m += zz9000.o zz9000_network.o zz9000_usb.o zz9000_graphics.o
zz9000-y := zz9000_core.o

#export KROOT=/lib/modules/$(shell uname -r)/build
export KROOT=/usr/src/linux-source-5.4

MAKEOPTS=ARCH=m68k CROSS_COMPILE=m68k-linux-gnu-

allofit: modules
modules:
@$(MAKE) $(MAKEOPTS) -C $(KROOT) M=$(PWD) modules
modules_install:
@$(MAKE) $(MAKEOPTS) -C $(KROOT) M=$(PWD) modules_install
kernel_clean:
@$(MAKE) $(MAKEOPTS) -C $(KROOT) M=$(PWD) clean

clean: kernel_clean
rm -rf Module.symvers modules.order

FILES := Makefile zz9000.h zz9000_core.c zz9000_usb.c zz9000_network.c zz9000_graphics.c
FILES += zz9000.ko zz9000_network.ko zz9000_usb.ko zz9000_graphics.ko


+ 88
- 0
README.md View File

@@ -0,0 +1,88 @@
# MNT ZZ9000 Drivers (Linux/m68k)

More Info: https://mntre.com/zz9000

## Build

Adjust the path to your Linux kernel sources in the Makefile before
starting. Then you can build the drivers out of the tree with

```
$ make
make[1]: Entering directory '/usr/src/linux-source-5.4'
CC [M] git/zz9000-linux-m68k/zz9000_core.o
LD [M] git/zz9000-linux-m68k/zz9000.o
CC [M] git/zz9000-linux-m68k/zz9000_network.o
CC [M] git/zz9000-linux-m68k/zz9000_usb.o
CC [M] git/zz9000-linux-m68k/zz9000_graphics.o
Building modules, stage 2.
MODPOST 4 modules
CC [M] git/zz9000-linux-m68k/zz9000.mod.o
LD [M] git/zz9000-linux-m68k/zz9000.ko
CC [M] git/zz9000-linux-m68k/zz9000_graphics.mod.o
LD [M] git/zz9000-linux-m68k/zz9000_graphics.ko
CC [M] git/zz9000-linux-m68k/zz9000_network.mod.o
LD [M] git/zz9000-linux-m68k/zz9000_network.ko
CC [M] git/zz9000-linux-m68k/zz9000_usb.mod.o
LD [M] git/zz9000-linux-m68k/zz9000_usb.ko
make[1]: Leaving directory '/usr/src/linux-source-5.4'
```

## Loading the drivers

You can load your drivers e.g. in your rc.local or in your initramfs.
Make sure you actually load it (early on). Debian 10 does not have
rc.local by default.

```
#
# rc.local
#
modprobe zz9000
modprobe zz9000_graphics
modprobe zz9000_network
modprobe zz9000_usb
```

On your next boot you should then see

```
[ 43.210000] MNT ZZ9000 driver v0.1.5.1 beta
[ 43.230000] MNT ZZ9000 Zorro III found at 0x40000000
[ 43.250000] HW version 0
[ 43.260000] FW version 1.5
[ 44.940000] USB capacity: 7669824 512-byte logical blocks: (3.93 GB GB/3.66 GiB GiB)
[ 45.490000] zzusb: zzusb1
[ 47.440000] ZZ9000 Ethernet eth0 MAC ADDR: 68:82:f2:01:00:54
[ 49.080000] zz9000 zorro0: framebuffer at 0x40010000, 0x500000 bytes, mapped to 0xfe68b644
[ 49.100000] zz9000 zorro0: format=16bit RGB(5:6:5), mode=1280x720x16, linelength=2560
[ 49.580000] Console: switching to colour frame buffer device 160x45
[ 51.760000] zz9000 zorro0: fb1: zz9000fb registered!
```

## Graphics

Boot the kernel with amifb=disable. The driver switches to
1280x720-60@16 by default, but supports all resolutions that the card
supports (non-16bit modes currently untested)

## Network

The network driver currently expects the MAC address to be set up by
AmigaOS already.

## USB

Only one USB drive is supported at the moment, and the drive can not be
hot-plugged.

# License / Copyright

Copyright (C) 2020 Stefan Reinauer <stefan.reinauer@coreboot.org>

Work based on existing Linux drivers, and reusing the register
descriptions from the NetBSD driver.

SPDX-License-Identifier: GPL-2.0
https://spdx.org/licenses/GPL-2.0.html


+ 42
- 0
zz9000.h View File

@@ -0,0 +1,42 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */

#ifndef __ZZ9000_H
#define __ZZ9000_H

#include <linux/zorro.h>
#include "zz9000_regs.h"

/* TODO: Move to zorro_ids.h */
#define ZORRO_MANUF_MNT 0x6d6e
#define ZORRO_PROD_MNT_ZZ9000_ZII ZORRO_ID(MNT, 0x03, 0)
#define ZORRO_PROD_MNT_ZZ9000_ZIII ZORRO_ID(MNT, 0x04, 0)

#define ZZ9000_VERSION "0.1.5.1 beta"
struct zz9000_platform_data {
void __iomem *registers;
void __iomem *eth_rx_mem;
void __iomem *eth_tx_mem;
void __iomem *usb_mem;
void __iomem *fb_mem;
struct resource *res[5];
struct zorro_dev *zdev;
struct fb_info *fb_info;
int fb_size;
};

struct zz9000_platform_data *get_zz9000_data(void);

static inline int is_zorro3(struct zorro_dev *zdev)
{
if (zdev->id == ZORRO_PROD_MNT_ZZ9000_ZIII)
return 1;

return 0;
}

#define zz_readw(x) (z_readw(zz9000_data->registers + (x)))
#define zz_readl(x) (z_readl(zz9000_data->registers + (x)))
#define zz_writew(v, x) z_writew((v), zz9000_data->registers + (x))
#define zz_writel(v, x) z_writel((v), zz9000_data->registers + (x))

#endif

+ 161
- 0
zz9000_core.c View File

@@ -0,0 +1,161 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */

#include <linux/console.h>
#include <linux/module.h>
#include <linux/zorro.h>
#include <asm/amigahw.h>
#include "zz9000.h"

static struct zz9000_platform_data *zz9000_data = NULL;

struct zz9000_platform_data *get_zz9000_data(void)
{
return zz9000_data;
}
EXPORT_SYMBOL_GPL(get_zz9000_data);

static void zz9000_remove(struct zorro_dev *zdev)
{
struct zz9000_platform_data *zz9000_data = zorro_get_drvdata(zdev);
uint32_t board = zdev->resource.start;

if (zz9000_data->registers)
iounmap(zz9000_data->registers);
if (zz9000_data->eth_rx_mem)
iounmap(zz9000_data->eth_rx_mem);
if (zz9000_data->eth_tx_mem)
iounmap(zz9000_data->eth_tx_mem);
if (zz9000_data->usb_mem)
iounmap(zz9000_data->usb_mem);
if (zz9000_data->fb_mem)
iounmap(zz9000_data->fb_mem);

if (zz9000_data->res[0])
release_mem_region(board + MNTZZ_REG_BASE, MNTZZ_REG_SIZE);
if (zz9000_data->res[1])
release_mem_region(board + MNTZZ_RX_BASE, MNTZZ_RX_SIZE);
if (zz9000_data->res[2])
release_mem_region(board + MNTZZ_TX_BASE, MNTZZ_TX_SIZE);
if (zz9000_data->res[3])
release_mem_region(board + MNTZZ_USB_BASE, MNTZZ_USB_SIZE);
if (zz9000_data->res[4])
release_mem_region(board + MNTZZ_FB_BASE, zz9000_data->fb_size);
}

static int zz9000_probe(struct zorro_dev *zdev,
const struct zorro_device_id *ent)
{
int ret = 0;
uint32_t board = zdev->resource.start;

printk(KERN_INFO "MNT ZZ9000 driver v%s\n", ZZ9000_VERSION);

zz9000_data =
devm_kzalloc(&zdev->dev, sizeof(*zz9000_data), GFP_KERNEL);
if (!zz9000_data) {
ret = -ENOMEM;
goto out;
}
zorro_set_drvdata(zdev, zz9000_data);

/* How much memory is visible? */
zz9000_data->fb_size = is_zorro3(zdev) ? MNTZZ_FB_SIZE_ZIII : MNTZZ_FB_SIZE_ZII;

zz9000_data->res[0] = request_mem_region(board + MNTZZ_REG_BASE, MNTZZ_REG_SIZE, "ZZ9000 Registers");
if (!zz9000_data->res[0]) {
dev_err(&zdev->dev, "Could not reserve register region.\n");
ret = -ENXIO;
goto cleanup;
}

zz9000_data->res[1] = request_mem_region(board + MNTZZ_RX_BASE, MNTZZ_RX_SIZE, "ZZ9000 Network RX");
if (!zz9000_data->res[1]) {
dev_err(&zdev->dev, "Could not reserve network RX region.\n");
ret = -ENXIO;
goto cleanup;
}

zz9000_data->res[2] = request_mem_region(board + MNTZZ_TX_BASE, MNTZZ_TX_SIZE, "ZZ9000 Network TX");
if (!zz9000_data->res[2]) {
dev_err(&zdev->dev, "Could not reserve network TX region.\n");
ret = -ENXIO;
goto cleanup;
}

zz9000_data->res[3] = request_mem_region(board + MNTZZ_USB_BASE, MNTZZ_USB_SIZE, "ZZ9000 USB");
if (!zz9000_data->res[3]) {
dev_err(&zdev->dev, "Could not reserve USB buffer region.\n");
ret = -ENXIO;
goto cleanup;
}

zz9000_data->res[4] = request_mem_region(board + MNTZZ_FB_BASE, zz9000_data->fb_size, "ZZ9000 Framebuffer");
if (!zz9000_data->res[4]) {
dev_err(&zdev->dev, "Could not reserve framebuffer region.\n");
ret = -ENXIO;
goto cleanup;
}

zz9000_data->registers = z_ioremap(board + MNTZZ_REG_BASE, MNTZZ_REG_SIZE);
zz9000_data->eth_rx_mem = z_ioremap(board + MNTZZ_RX_BASE, MNTZZ_RX_SIZE);
zz9000_data->eth_tx_mem = z_ioremap(board + MNTZZ_TX_BASE, MNTZZ_TX_SIZE);
zz9000_data->usb_mem = z_ioremap(board + MNTZZ_USB_BASE, MNTZZ_USB_SIZE);
/* access the videomem with writethrough cache */
zz9000_data->fb_mem = ioremap_wt(board + MNTZZ_FB_BASE, zz9000_data->fb_size);
zz9000_data->zdev = zdev;

if (!zz9000_data->registers || !zz9000_data->eth_rx_mem ||
!zz9000_data->eth_tx_mem || !zz9000_data->usb_mem ||
!zz9000_data->fb_mem) {
dev_err(&zdev->dev, "Could not ioremap device memory.\n");
ret = -ENXIO;
goto cleanup;
}

printk(KERN_INFO "MNT ZZ9000 Zorro %s found at 0x%08x\n",
is_zorro3(zdev) ? "III" : "II", board);
printk(KERN_INFO " HW version %d\n", zz_readw(MNTZZ_HW_VERSION));
printk(KERN_INFO " FW version %d.%d\n", zz_readw(MNTZZ_FW_VERSION) >> 8,
zz_readw(MNTZZ_FW_VERSION) & 0xff);

if (!ret)
return 0;

cleanup:
zz9000_remove(zdev);
out:
return ret;
}

static const struct zorro_device_id zz9000_zorro_tbl[] = {
{ZORRO_PROD_MNT_ZZ9000_ZII},
{ZORRO_PROD_MNT_ZZ9000_ZIII},
{0}
};

MODULE_DEVICE_TABLE(zorro, zz9000_zorro_tbl);

static struct zorro_driver zz9000_driver = {
.name = "zz9000",
.id_table = zz9000_zorro_tbl,
.probe = zz9000_probe,
.remove = zz9000_remove,
};

static int __init zz9000_init(void)
{
return zorro_register_driver(&zz9000_driver);
}

module_init(zz9000_init);

static void __exit zz9000_exit(void)
{
zorro_unregister_driver(&zz9000_driver);
}

module_exit(zz9000_exit);

MODULE_DESCRIPTION("MNT ZZ9000 core driver");
MODULE_AUTHOR("Stefan Reinauer <stefan.reinauer@coreboot.org>");
MODULE_LICENSE("GPL");

+ 457
- 0
zz9000_graphics.c View File

@@ -0,0 +1,457 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */

#include <drm/drm_fourcc.h>
#include <linux/console.h>
#include <linux/fb.h>
#include <linux/module.h>
#include <linux/zorro.h>
#include <asm/amigahw.h>
#include <asm/amigaints.h>
#include "zz9000.h"

#define PSEUDO_PALETTE_SIZE 16
struct zz9000fb_par {
u32 palette[PSEUDO_PALETTE_SIZE];
};

static const struct fb_fix_screeninfo zz9000fb_fix = {
.id = "ZZ9000",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_TRUECOLOR,
.accel = FB_ACCEL_NONE,
};

static const struct fb_var_screeninfo zz9000fb_var = {
.height = -1,
.width = -1,
.activate = FB_ACTIVATE_NOW,
.vmode = FB_VMODE_NONINTERLACED,
};

struct zz9000fb_format {
const char *name;
u32 bits_per_pixel;
struct fb_bitfield red;
struct fb_bitfield green;
struct fb_bitfield blue;
struct fb_bitfield transp;
u32 fourcc;
};

struct zz9000fb_format zz9000fb_formats[3] = {
{ "16bit RGB(5:6:5)", 16, {11, 5}, {5, 6}, {0, 5}, { 0, 0}, DRM_FORMAT_RGB565 },
{ "15bit RGB(5:5:5)", 16, {10, 5}, {5, 5}, {0, 5}, { 0, 0}, DRM_FORMAT_XRGB1555 },
{ "32bit BGRA(8:8:8:8)", 32, {16, 8}, {8, 8}, {24, 8}, {0, 8}, DRM_FORMAT_BGRA8888 },
};

#define MHz * 1024 * 1024
static struct fb_videomode zz9000fb_modedb[] __initdata = {
{ "1280x720p60", 60, 1280, 720, 13426, 192, 64, 22, 1, 136, 3,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
{ "800x600p60", 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
{ "640x480p60", 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
{ "640x512p60", 60, 640, 512, 41667, 113, 87, 18, 1, 56, 3, // fake, values are @50Hz
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
{ "720x480p60", 60, 720, 480, 37037, 68, 12, 39, 5, 64, 5,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
{ "1024x768p60", 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
{ "1280x1024p60", 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
{ "1920x1080p50", 50, 1920, 1080, 6734, 148, 528, 36, 4, 44, 5,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
{ "1920x1080p60", 60, 1920, 1080, 5787, 328, 120, 34, 1, 208, 3,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
{ "720x576p50", 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED }
};

#define NUM_TOTAL_MODES ARRAY_SIZE(zz9000fb_modedb)
static int defmode = 0;
#define DEFAULT_BPP 16

struct zz9000_platform_data *zz9000_data;
static char *mode = NULL;
static int zz9000_accel = 1;
static int mntzz_vmode = MNTZZ_MODE_1280x720, mntzz_cmode = MNTZZ_MODE_COLOR_16BIT;
static struct zz9000fb_format *format = NULL;

static int zz9000fb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp,
struct fb_info *info)
{
u32 *pal = info->pseudo_palette;
u32 cr = red >> (16 - info->var.red.length);
u32 cg = green >> (16 - info->var.green.length);
u32 cb = blue >> (16 - info->var.blue.length);
u32 value;

if (regno >= PSEUDO_PALETTE_SIZE)
return -EINVAL;

value = (cr << info->var.red.offset) |
(cg << info->var.green.offset) | (cb << info->var.blue.offset);
if (info->var.transp.length > 0) {
u32 mask = (1 << info->var.transp.length) - 1;
mask <<= info->var.transp.offset;
value |= mask;
}
pal[regno] = value;

return 0;
}


/* fb_try_mode says: Returns 1 on success.
* However, all the drivers I looked at return -EINVAL or 0.
*/
static int zz9000fb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
int bytes = 0;

mntzz_vmode = -1;
mntzz_cmode = -1;

if (var->xres == 1280 && var->yres == 720)
mntzz_vmode = MNTZZ_MODE_1280x720;
if (var->xres == 800 && var->yres == 600)
mntzz_vmode = MNTZZ_MODE_800x600;
if (var->xres == 640 && var->yres == 480)
mntzz_vmode = MNTZZ_MODE_640x480;
if (var->xres == 1024 && var->yres == 768)
mntzz_vmode = MNTZZ_MODE_1024x768;
if (var->xres == 1280 && var->yres == 1024)
mntzz_vmode = MNTZZ_MODE_1280x1024;
if (var->xres == 1920 && var->yres == 1080)
if (var->bits_per_pixel <= 16)
mntzz_vmode = MNTZZ_MODE_1920x1080;
if (var->xres == 720 && var->yres == 576)
mntzz_vmode = MNTZZ_MODE_720x576p50;
// FIXME MNTZZ_MODE_1920x1080p50 do we know refresh here?
if (var->xres == 720 && var->yres == 480)
mntzz_vmode = MNTZZ_MODE_720x480;
if (var->xres == 640 && var->yres == 512)
mntzz_vmode = MNTZZ_MODE_640x512;

if (mntzz_vmode == -1)
return -EINVAL;

switch (var->bits_per_pixel) {
case 8:
// Not yet implemented
break;
case 15:
bytes = 2;
mntzz_cmode = MNTZZ_MODE_COLOR_15BIT;
format = &zz9000fb_formats[1];
break;
case 16:
bytes = 2;
mntzz_cmode = MNTZZ_MODE_COLOR_16BIT;
format = &zz9000fb_formats[0];
break;
case 32:
bytes = 4;
mntzz_cmode = MNTZZ_MODE_COLOR_32BIT;
format = &zz9000fb_formats[2];
break;
default:
// Nothing to do
break;
}

// TODO on ZII, check if mode requires more memory than we mapped

if (mntzz_cmode == -1)
return -EINVAL;

var->red = format->red;
var->green = format->green;
var->blue = format->blue;
var->transp = format->transp;

info->fix.line_length = bytes * var->xres;

return 0;
}

/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */

static int zz9000fb_blank(int blank, struct fb_info *info)
{
pr_debug("zz9000fb: %sblank.\n", blank ? "" : "un");
return 0;
}

static void zz9000fb_fill_rectangle(struct fb_info *info, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint32_t color)
{
zz_writew(x, MNTZZ_BLITTER_X1);
zz_writew(y, MNTZZ_BLITTER_Y1);
zz_writew(w, MNTZZ_BLITTER_X2);
zz_writew(h, MNTZZ_BLITTER_Y2);
zz_writew(info->fix.line_length >> 2, MNTZZ_BLITTER_ROW_PITCH);
zz_writew(mntzz_cmode, MNTZZ_BLITTER_COLORMODE);
zz_writew(color >> 16, MNTZZ_BLITTER_RGB_HI);
zz_writew(color & 0xffff, MNTZZ_BLITTER_RGB_LO);
zz_writew(0x00ff, MNTZZ_BLITTER_OP_FILLRECT);
}
static void zz9000fb_fillrect(struct fb_info *info,
const struct fb_fillrect *region)
{
if (zz9000_accel) {
if (region->rop == ROP_COPY)
zz9000fb_fill_rectangle(info, region->dx, region->dy,
region->width, region->height, region->color);
else
cfb_fillrect(info, region);
} else
cfb_fillrect(info, region);
}

static void zz9000fb_copyarea(struct fb_info *info,
const struct fb_copyarea *area)
{
if (zz9000_accel) {
zz_writew(area->dx, MNTZZ_BLITTER_X1);
zz_writew(area->dy, MNTZZ_BLITTER_Y1);
zz_writew(area->width, MNTZZ_BLITTER_X2);
zz_writew(area->height, MNTZZ_BLITTER_Y2);

zz_writew(area->sx, MNTZZ_BLITTER_X3);
zz_writew(area->sy, MNTZZ_BLITTER_Y3);
zz_writew(info->fix.line_length >> 2, MNTZZ_BLITTER_ROW_PITCH);
zz_writew(0xff00 | mntzz_cmode, MNTZZ_BLITTER_COLORMODE);
zz_writew(0x0001, MNTZZ_BLITTER_OP_COPYRECT);
} else
cfb_copyarea(info, area);
}

static void
zz9000fb_imageblit(struct fb_info *info, const struct fb_image *image)
{
// TODO accelerated implementation
cfb_imageblit(info, image);
return;
}

void zz9000fb_fix_vsync(void)
{
/* The AmigaOS driver does this 100x */
int i;
for (i = 0; i < 100; i++) {
zz_writew(0x0000, MNTZZ_VIDEO_CTRL_DATA_HI);
zz_writew(0x0001, MNTZZ_VIDEO_CTRL_DATA_LO);
zz_writew(MNTZZ_OP_VSYNC, MNTZZ_VIDEO_CTRL_OP);
zz_writew(MNTZZ_OP_NOP, MNTZZ_VIDEO_CTRL_OP);
zz_writew(0x0000, MNTZZ_VIDEO_CTRL_DATA_LO);
}
}

void zz9000fb_setmode(struct fb_info *info)
{
zz_writew(MNTZZ_CAPTURE_OFF, MNTZZ_VIDEO_CAPTURE_MODE);
zz_writew(0x0000, MNTZZ_PAN_PTR_HI);
zz_writew(0x0000, MNTZZ_PAN_PTR_LO);
zz_writew(0x0000, MNTZZ_BLITTER_SRC_HI);
zz_writew(0x0000, MNTZZ_BLITTER_SRC_LO);
zz_writew(0x0000, MNTZZ_BLITTER_DST_HI);
zz_writew(0x0000, MNTZZ_BLITTER_DST_LO);

zz_writew(MNTZZ_MODE_SCALE_0 | mntzz_cmode | mntzz_vmode,
MNTZZ_MODE);

zz9000fb_fix_vsync();

zz9000fb_fill_rectangle(info, 0,0, info->var.xres, info->var.yres, 0xaaaaaa); // 0xad55?
}

static void
zz9000_capture_mode(uint32_t capture_mode)
{
uint16_t panPtrHi;
uint16_t panPtrLo;
uint16_t scaleMode;
uint16_t colorMode;
uint16_t dispMode;


switch (capture_mode) {
case MNTZZ_VMODE_720x576:
panPtrHi = MNTZZ_CAPTURE_PAN_PAL >> 16;
panPtrLo = MNTZZ_CAPTURE_PAN_PAL & 0xffff;
dispMode = MNTZZ_MODE_720x576p50;
break;
case MMNTZ_VMODE_800x600:
panPtrHi = MNTZZ_CAPTURE_PAN_VGA >> 16;
panPtrLo = MNTZZ_CAPTURE_PAN_VGA & 0xffff;
dispMode = MNTZZ_MODE_800x600;
break;
default:
panPtrHi = MNTZZ_CAPTURE_PAN_PAL >> 16;
panPtrLo = MNTZZ_CAPTURE_PAN_PAL & 0xffff;
dispMode = MNTZZ_MODE_720x576p50;
break;
}
colorMode = MNTZZ_MODE_COLOR_32BIT;
scaleMode = MNTZZ_MODE_SCALE_2;

zz_writew(panPtrHi, MNTZZ_PAN_PTR_HI);
zz_writew(panPtrLo, MNTZZ_PAN_PTR_LO);
zz_writew(dispMode, MNTZZ_VIDEOCAP_VMODE);
zz_writew(scaleMode | colorMode | dispMode, MNTZZ_MODE);
zz_writew(MNTZZ_CAPTURE_ON, MNTZZ_VIDEO_CAPTURE_MODE);
zz9000fb_fix_vsync();
}

static int zz9000fb_set_par(struct fb_info *info)
{
zz9000fb_setmode(info);

dev_info(&zz9000_data->zdev->dev,
"framebuffer at 0x%lx, 0x%x bytes, mapped to 0x%p\n",
info->fix.smem_start, info->fix.smem_len, info->screen_base);
dev_info(&zz9000_data->zdev->dev, "format=%s, mode=%dx%dx%d, linelength=%d\n",
format->name, info->var.xres, info->var.yres,
info->var.bits_per_pixel, info->fix.line_length);
return 0;
}

static struct fb_ops zz9000fb_ops = {
.owner = THIS_MODULE,
.fb_blank = zz9000fb_blank,
.fb_check_var = zz9000fb_check_var,
.fb_setcolreg = zz9000fb_setcolreg,
.fb_set_par = zz9000fb_set_par,
.fb_fillrect = zz9000fb_fillrect,
.fb_copyarea = zz9000fb_copyarea,
.fb_imageblit = zz9000fb_imageblit,
};

static int __init zz9000fb_setup(char *options)
{
char *this_opt;

if (!options || !*options)
return 0;

while ((this_opt = strsep(&options, ",")) != NULL) {
if (!*this_opt)
continue;
/* Not much here yet */
if (!strcmp(this_opt, "noaccel")) {
zz9000_accel = 0;
} else
mode = this_opt;
}
return 0;
}

static int __init zz9000_graphics_init(struct zz9000_platform_data *data)
{
int ret;

struct fb_info *info;
struct zz9000fb_par *par;
char *option = NULL;
uint32_t board = data->zdev->resource.start;

zz9000_data = data;

if (fb_get_options("zz9000fb", &option))
return -ENODEV;
zz9000fb_setup(option);

info = framebuffer_alloc(sizeof(struct zz9000fb_par), &data->zdev->dev);
if (!info)
return -ENOMEM;
data->fb_info = info;

par = info->par;

info->fix = zz9000fb_fix;
info->fix.smem_start = board + 0x10000;
info->fix.smem_len = is_zorro3(data->zdev) ? 0x500000 : 0x3f0000;
info->fix.mmio_start = board;
info->fix.mmio_len = 0x2000;

info->var = zz9000fb_var;

info->apertures = alloc_apertures(1);
if (!info->apertures) {
ret = -ENOMEM;
goto error_fb_release;
}
info->apertures->ranges[0].base = info->fix.smem_start;
info->apertures->ranges[0].size = info->fix.smem_len;

info->fbops = &zz9000fb_ops;
info->flags = FBINFO_DEFAULT;
info->screen_base = data->fb_mem;

info->pseudo_palette = par->palette;

if (!fb_find_mode(&info->var, info, mode, zz9000fb_modedb,
NUM_TOTAL_MODES, &zz9000fb_modedb[defmode], DEFAULT_BPP))
return -EINVAL;

fb_videomode_to_modelist(zz9000fb_modedb, NUM_TOTAL_MODES,
&info->modelist);

ret = register_framebuffer(info);
if (ret < 0) {
dev_err(&data->zdev->dev, "Unable to register zz9000fb: %d\n",
ret);
goto error_fb_release;
}

dev_info(&data->zdev->dev, "fb%d: zz9000fb registered!\n", info->node);

return 0;

error_fb_release:
framebuffer_release(info);
return ret;
}

int zz9000_graphics_remove(struct zz9000_platform_data *data)
{
zz9000_capture_mode(MNTZZ_VMODE_720x576);
unregister_framebuffer(data->fb_info);
framebuffer_release(data->fb_info);
data->fb_info = NULL;

return 0;
}

static int __init zz9000_graphics_module_init(void)
{
struct zz9000_platform_data *data;

data = get_zz9000_data();
return zz9000_graphics_init(data);
}

module_init(zz9000_graphics_module_init);

static void __exit zz9000_graphics_module_exit(void)
{
struct zz9000_platform_data *data;

data = get_zz9000_data();
zz9000_graphics_remove(data);
}

module_exit(zz9000_graphics_module_exit);

module_param(mode, charp, 0);
MODULE_PARM_DESC(mode,
"\nSelects the desired default display mode in the format XxYxDepth,\n"
"eg. 1024x768x16. Other formats supported include XxY-Depth and\n"
"XxY-Depth@Rate. (default: 1280x720-60@16)\n");

MODULE_DESCRIPTION("MNT ZZ9000 graphics driver");
MODULE_AUTHOR("Stefan Reinauer <stefan.reinauer@coreboot.org>");
MODULE_LICENSE("GPL");

+ 341
- 0
zz9000_network.c View File

@@ -0,0 +1,341 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */

#include <linux/console.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/module.h>
#include <linux/zorro.h>
#include <asm/amigahw.h>
#include <asm/amigaints.h>
#include "zz9000.h"

static struct zz9000_platform_data *zz9000_data;

/* Length definitions */
static unsigned char zz9000_mac_addr[ETH_ALEN];

/* net_device referencing */
static struct net_device *device;
static struct net_device_stats *stats;

/* priv structure that holds the informations about the device. */
struct eth_priv {
spinlock_t lock;
struct net_device *dev;
};

static void zz9000_hw_tx(char *data, int len, struct net_device *dev)
{
void *zz9000_tx_mem = zz9000_data->eth_tx_mem;
uint16_t ret;

memcpy(zz9000_tx_mem, data, len);

#if 0
netdev_dbg(dev, "send data=%p len=%d\n", data, len);

print_hex_dump_debug("eth: ", DUMP_PREFIX_OFFSET,
16, 1, data, len, true);
#endif
zz_writew(len, MNTZZ_ETH_TX);
ret = zz_readw(MNTZZ_ETH_TX);
if (ret != 0)
netdev_dbg(dev, "ret = %d\n", ret);
}

static uint32_t old_serial = 0;

/*
* Read a packet out of the adapter and pass it to the upper layers
*/
static inline int recv_packet(struct net_device *dev)
{
unsigned short pktlen;
struct sk_buff *skb;

uint8_t *frame = (uint8_t *) zz9000_data->eth_rx_mem;
uint32_t ser = ((uint32_t) frame[2] << 8) | ((uint32_t) frame[3]);
pktlen = ((uint32_t) frame[0] << 8) | ((uint32_t) frame[1]);

/* There is not interrupt status register for network,
* so we use the serial number of the frame to determine
* whether there's a new packet
*/
if (old_serial == ser)
return 0;
old_serial = ser;

if (!pktlen) {
netdev_dbg(dev, "%s: pktlen == 0\n", __func__);
dev->stats.rx_errors++;
return -1;
}

skb = dev_alloc_skb(pktlen + 2);
if (!skb) {
netdev_dbg(dev, "%s: out of mem (buf_alloc failed)\n",
__func__);
dev->stats.rx_dropped++;
return -1;
}

skb->dev = dev;
#if 0
skb_reserve(skb, NET_IP_ALIGN);
skb_put(skb, pktlen); /* make room */

//print_hex_dump_debug("eth: ", DUMP_PREFIX_OFFSET,
// 16, 1, frame, pktlen, true);

// Copy buffer to skb
memcpy(skb->data, frame + 4, pktlen);
#else
memcpy(skb_put(skb, pktlen), frame + 4, pktlen);
#endif


/* Mark this frame as accepted */
zz_writew(0x0001, MNTZZ_ETH_RX);

skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_UNNECESSARY; /* Don't check it */
dev->stats.rx_packets++;
dev->stats.rx_bytes += pktlen;

netif_rx(skb);

/* and enqueue packet */
return 1;
}

static irqreturn_t zz9000_eth_interrupt(int irq, void *dev_id)
{
struct eth_priv *priv;
int rcv = 1;
struct net_device *dev = (struct net_device *)dev_id;

/* paranoid */
if (!dev)
return IRQ_HANDLED;

/* Lock the device */
priv = netdev_priv(dev);
spin_lock(&priv->lock);

/* Fetch all available packets. */
while (rcv) {
rcv = recv_packet(dev);
}

/* Unlock the device and we are done */
spin_unlock(&priv->lock);

return IRQ_HANDLED;
}

static int zz9000_eth_open(struct net_device *dev)
{
int ret;

/* Install interrupt handler for IRQ6.
* IRQ2 is currently not supported.
*/
ret = request_irq(IRQ_AMIGA_EXTER, zz9000_eth_interrupt, IRQF_SHARED,
"ZZ9000 Ethernet", dev);
if (ret)
return ret;

/* Enable Interrupts on ZZ9000 */
zz_writew(zz_readw(MNTZZ_CONFIG) | 0x01, MNTZZ_CONFIG);

/* start up the transmission queue */
netif_start_queue(dev);
return 0;
}

static int zz9000_eth_close(struct net_device *dev)
{
/* Disable interrupts on ZZ9000 */
zz_writew(zz_readw(MNTZZ_CONFIG) & 0xfffe, MNTZZ_CONFIG);

/* shutdown the transmission queue */
netif_stop_queue(dev);

/* Remove interrupt handler */
free_irq(IRQ_AMIGA_EXTER, dev);
return 0;
}

static int zz9000_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
int len;
char *data, shortpkt[ETH_ZLEN];

data = skb->data;
len = skb->len;
if (len < ETH_ZLEN) {
memset(shortpkt, 0, ETH_ZLEN);
memcpy(shortpkt, skb->data, skb->len);
len = ETH_ZLEN;
data = shortpkt;
}

/* Send that packet out! */
zz9000_hw_tx(data, len, dev);

dev->stats.tx_packets++;
dev->stats.tx_bytes += len;

dev_kfree_skb(skb);

return 0;
}

static int zz9000_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr,
int cmd)
{
printk(KERN_INFO "zz9000_eth_do_ioctl(%s): 0x%x\n", dev->name, cmd);
return -1;
}

static struct net_device_stats *zz9000_eth_get_stats(struct net_device *dev)
{
// printk(KERN_INFO "zz9000_eth_get_stats(%s)\n", dev->name);
return stats;
}

/*
* This is where ifconfig comes down and tells us who we are, etc.
* We can just ignore this.
*/
static int zz9000_eth_config(struct net_device *dev, struct ifmap *map)
{
printk("zz9000_eth_config(%s)\n", dev->name);
if (dev->flags & IFF_UP) {
return -EBUSY;
}
return 0;
}

static int zz9000_eth_change_mtu(struct net_device *dev, int new_mtu)
{
unsigned long flags = 0;
struct eth_priv *priv = netdev_priv(dev);
spinlock_t *lock = &priv->lock;

printk(KERN_INFO "zz9000_eth_change_mtu(%s)\n", dev->name);

/* Check ranges */
if ((new_mtu < 68) || (new_mtu > 10000)) // Remember to see at the hardware documentation the right especification
return -EINVAL;

spin_unlock_irqrestore(lock, flags);
printk(KERN_INFO "Old mtu: (%d) New mtu: (%d)", dev->mtu, new_mtu);
dev->mtu = new_mtu;
spin_unlock_irqrestore(lock, flags);
return 0; /* Sucess */
}

static void zz9000_eth_tx_timeout(struct net_device *dev)
{
dev->stats.tx_errors++;

netif_wake_queue(dev);
return;
}

/*
* Structure that holds all the options supported by the driver.
*/
static struct net_device_ops zz9000_netdev_ops = {
.ndo_open = zz9000_eth_open,
.ndo_stop = zz9000_eth_close,
.ndo_start_xmit = zz9000_eth_start_xmit,
.ndo_do_ioctl = zz9000_eth_do_ioctl,
.ndo_get_stats = zz9000_eth_get_stats,
.ndo_set_config = zz9000_eth_config,
.ndo_change_mtu = zz9000_eth_change_mtu,
.ndo_tx_timeout = zz9000_eth_tx_timeout,
// Does NAPI even make sense for this device?
//.ndo_poll_controller = zz9000_eth_napi_poll;
};

static void zz9000_eth_setup(struct net_device *dev)
{
zz9000_mac_addr[0] = zz_readw(MNTZZ_ETH_MAC_HI) >> 8;
zz9000_mac_addr[1] = zz_readw(MNTZZ_ETH_MAC_HI) & 0xff;
zz9000_mac_addr[2] = zz_readw(MNTZZ_ETH_MAC_MD) >> 8;
zz9000_mac_addr[3] = zz_readw(MNTZZ_ETH_MAC_MD) & 0xff;
zz9000_mac_addr[4] = zz_readw(MNTZZ_ETH_MAC_LO) >> 8;
zz9000_mac_addr[5] = zz_readw(MNTZZ_ETH_MAC_LO) & 0xff;

ether_setup(dev);

dev->netdev_ops = &zz9000_netdev_ops;
dev->irq = IRQ_AMIGA_EXTER;

dev->hard_header_len = ETH_HLEN;
dev->mtu = ETH_DATA_LEN;
dev->addr_len = ETH_ALEN;
dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
memset(dev->broadcast, 0xFF, ETH_ALEN);
memcpy(dev->dev_addr, zz9000_mac_addr, ETH_ALEN);

stats = &dev->stats;
}

int zz9000_network_init(struct zz9000_platform_data *data)
{
int ret = 0;

zz9000_data = data;

/* Allocating the net device. */
device = alloc_netdev(0, "eth%d", NET_NAME_UNKNOWN, zz9000_eth_setup);

if ((ret = register_netdev(device))) {
printk(KERN_EMERG
"zz9000: net: error %i registering device \"%s\"\n", ret,
device->name);
free_netdev(device);
return -1;
}

printk(KERN_INFO " ZZ9000 Ethernet %s MAC ADDR: %02x:%02x:%02x:%02x:%02x:%02x\n",
dev_name(&device->dev),
zz9000_mac_addr[0], zz9000_mac_addr[1], zz9000_mac_addr[2],
zz9000_mac_addr[3], zz9000_mac_addr[4], zz9000_mac_addr[5]);

return 0;
}

int zz9000_network_remove(struct zz9000_platform_data *data)
{
if (device) {
unregister_netdev(device);
free_netdev(device);
printk(KERN_INFO "zz9000: Network device removed.\n");
}

return 0;
}

static int __init zz9000_network_module_init(void)
{
struct zz9000_platform_data *data = get_zz9000_data();
return zz9000_network_init(data);
}

module_init(zz9000_network_module_init);

static void __exit zz9000_network_module_exit(void)
{
struct zz9000_platform_data *data = get_zz9000_data();
zz9000_network_remove(data);
}

module_exit(zz9000_network_module_exit);

MODULE_DESCRIPTION("MNT ZZ9000 network driver");
MODULE_AUTHOR("Stefan Reinauer <stefan.reinauer@coreboot.org>");
MODULE_LICENSE("GPL");

+ 164
- 0
zz9000_regs.h View File

@@ -0,0 +1,164 @@
/*
* Copyright (c) 2020 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Alain Runa.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/* Address space */
#define MNTZZ_REG_BASE (0x00000000)
#define MNTZZ_REG_SIZE (0x00002000)
#define MNTZZ_FB_BASE (0x00010000)
#define MNTZZ_FB_SIZE_ZIII (0x00ff0000) /* 16 MB - 64 KB */
#define MNTZZ_FB_SIZE_ZII (0x003f0000) /* 4 MB - 64 KB */
#define MNTZZ_RX_BASE (0x00002000)
#define MNTZZ_RX_SIZE (0x00002000)
#define MNTZZ_TX_BASE (0x00008000)
#define MNTZZ_TX_SIZE (0x00002000)
#define MNTZZ_USB_BASE (0x0000a000)
#define MNTZZ_USB_SIZE (0x00001000)

/* Color mode */
#define MNTZZ_COLOR_8BIT (0)
#define MNTZZ_COLOR_16BIT (1)
#define MNTZZ_COLOR_32BIT (2)
#define MNTZZ_COLOR_15BIT (3)

/* Modes of MNTZZ_MODE */
#define MNTZZ_MODE_1280x720 (0)
#define MNTZZ_MODE_800x600 (1)
#define MNTZZ_MODE_640x480 (2)
#define MNTZZ_MODE_1024x768 (3)
#define MNTZZ_MODE_1280x1024 (4)
#define MNTZZ_MODE_1920x1080 (5)
#define MNTZZ_MODE_720x576p50 (6)
#define MNTZZ_MODE_1920x1080p50 (7)
#define MNTZZ_MODE_720x480 (8)
#define MNTZZ_MODE_640x512 (9)

/* Some registers expect the modes and scale factors shifted */
#define MNTZZ_MODE_COLOR_8BIT (MNTZZ_COLOR_8BIT << 8)
#define MNTZZ_MODE_COLOR_16BIT (MNTZZ_COLOR_16BIT << 8)
#define MNTZZ_MODE_COLOR_32BIT (MNTZZ_COLOR_32BIT << 8)
#define MNTZZ_MODE_COLOR_15BIT (MNTZZ_COLOR_15BIT << 8)
#define MNTZZ_MODE_SCALE_0 (0 << 12)
#define MNTZZ_MODE_SCALE_1 (1 << 12)
#define MNTZZ_MODE_SCALE_2 (2 << 12)
#define MNTZZ_MODE_SCALE_3 (3 << 12)

/* Video capture pan */
#define MNTZZ_CAPTURE_PAN_PAL (0x00e00000)
#define MNTZZ_CAPTURE_PAN_VGA (0x00e00bd0)

/* Modes of MNTZZ_VIDEOCAP_VMODE */
#define MMNTZ_VMODE_800x600 (1)
#define MNTZZ_VMODE_720x576 (6)

/* Modes of MNTZZ_VIDEO_CAPTURE_MODE */
#define MNTZZ_CAPTURE_OFF (0)
#define MNTZZ_CAPTURE_ON (1)

/* Operations of MNTZ_VIDEO_CONTROL_OP */
#define MNTZZ_OP_NOP (0)
#define MNTZZ_OP_PALETTE (3)
#define MNTZZ_OP_VSYNC (5)

/* Bits of MNTZZ_CONFIG */
#define MNTZZ_CONFIG_INT6 (1 << 0)

/* REGISTERS */

/* Config and Video */
#define MNTZZ_HW_VERSION 0x00
#define MNTZZ_MODE 0x02
#define MNTZZ_CONFIG 0x04

#define MNTZZ_SPRITE_X 0x06
#define MNTZZ_SPRITE_Y 0x08

#define MNTZZ_PAN_PTR_HI 0x0a
#define MNTZZ_PAN_PTR_LO 0x0c
#define MNTZZ_VIDEOCAP_VMODE 0x0e

/* Blitter */
#define MNTZZ_BLITTER_X1 0x10
#define MNTZZ_BLITTER_Y1 0x12
#define MNTZZ_BLITTER_X2 0x14
#define MNTZZ_BLITTER_Y2 0x16
#define MNTZZ_BLITTER_ROW_PITCH 0x18
#define MNTZZ_BLITTER_X3 0x1a
#define MNTZZ_BLITTER_Y3 0x1c
#define MNTZZ_BLITTER_RGB_HI 0x1e
#define MNTZZ_BLITTER_RGB_LO 0x20
#define MNTZZ_BLITTER_OP_FILLRECT 0x22
#define MNTZZ_BLITTER_OP_COPYRECT 0x24
#define MNTZZ_BLITTER_OP_FILLTEMPLATE 0x26
#define MNTZZ_BLITTER_SRC_HI 0x28
#define MNTZZ_BLITTER_SRC_LO 0x2a
#define MNTZZ_BLITTER_DST_HI 0x2c
#define MNTZZ_BLITTER_DST_LO 0x2e
#define MNTZZ_BLITTER_COLORMODE 0x30
#define MNTZZ_BLITTER_SRC_PITCH 0x32
#define MNTZZ_BLITTER_RGB2_HI 0x34
#define MNTZZ_BLITTER_RGB2_LO 0x36
#define MNTZZ_BLITTER_OP_P2C 0x38
#define MNTZZ_BLITTER_OP_DRAW_LINE 0x3a
#define MNTZZ_BLITTER_OP_P2D 0x3c
#define MNTZZ_BLITTER_OP_INVERTRECT 0x3e

#define MNTZZ_BLITTER_USER1 0x40
#define MNTZZ_BLITTER_USER2 0x42
#define MNTZZ_BLITTER_USER3 0x44
#define MNTZZ_BLITTER_USER4 0x46

/* Sprite cursor */
#define MNTZZ_SPRITE_BITMAP 0x48
#define MNTZZ_SPRITE_COLORS 0x4a

/* Network */
#define MNTZZ_ETH_TX 0x80
#define MNTZZ_ETH_RX 0x82
#define MNTZZ_ETH_MAC_HI 0x84
#define MNTZZ_ETH_MAC_MD 0x86
#define MNTZZ_ETH_MAC_LO 0x88

/* Board hardware */
#define MNTZZ_FW_VERSION 0xc0

/* USB */
#define MNTZZ_USB_TX_HI 0xd0
#define MNTZZ_USB_TX_LO 0xd2
#define MNTZZ_USB_RX_HI 0xd4
#define MNTZZ_USB_RX_LO 0xd6
#define MNTZZ_USB_STATUS 0xd8
#define MNTZZ_USB_BUFSEL 0xda
#define MNTZZ_USB_CAPACITY 0xdc

/* Video Control */
#define MNTZZ_VIDEO_CTRL_DATA_HI 0x1000
#define MNTZZ_VIDEO_CTRL_DATA_LO 0x1002
#define MNTZZ_VIDEO_CTRL_OP 0x1004
#define MNTZZ_VIDEO_CAPTURE_MODE 0x1006



+ 400
- 0
zz9000_usb.c View File

@@ -0,0 +1,400 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */

#include <linux/blk-mq.h>
#include <linux/console.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/string_helpers.h>
#include <uapi/linux/cdrom.h>
#include <uapi/linux/hdreg.h>
#include <asm/div64.h>
#include <asm/zorro.h>

#include "zz9000.h"

static struct zz9000_platform_data *zz9000_data;

typedef struct {
struct zz9000_platform_data *data;
struct request_queue *queue;
struct gendisk *disk;
atomic_t open_counter;
sector_t capacity;
struct blk_mq_tag_set tag_set;
} zz9000_usb_storage_t;

static int zz9000_usb_open(struct block_device *bdev, fmode_t mode)
{
zz9000_usb_storage_t *dev = bdev->bd_disk->private_data;
if (dev == NULL) {
printk(KERN_WARNING "zz9000: USB: Invalid disk private_data in %s.\n", __func__);
return -ENXIO;
}

atomic_inc(&dev->open_counter);

pr_debug("zz9000: USB: Device was opened.\n");

return 0;
}

static void zz9000_usb_release(struct gendisk *disk, fmode_t mode)
{
zz9000_usb_storage_t *dev = disk->private_data;
if (dev) {
atomic_dec(&dev->open_counter);
pr_debug("zz9000: USB: Device was closed.\n");
} else
printk(KERN_WARNING "zz9000: USB: Invalid disk private_data in %s.\n", __func__);
}

static int zz9000_usb_getgeo(zz9000_usb_storage_t * dev,
struct hd_geometry *geo)
{
sector_t quotient;
geo->start = 0;
if (dev->capacity > 63) {
geo->sectors = 63;
quotient = div_u64((dev->capacity + (63 - 1)), 63);

if (quotient > 255) {
geo->heads = 255;
geo->cylinders = (unsigned short)(div_u64((quotient + (255 - 1)), 255));
} else {
geo->heads = (unsigned char)quotient;
geo->cylinders = 1;
}
} else {
// Is this really needed?
geo->sectors = (unsigned char)dev->capacity;
geo->cylinders = 1;
geo->heads = 1;
}
return 0;
}

static int zz9000_usb_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
int ret = -ENOTTY;
zz9000_usb_storage_t *dev = bdev->bd_disk->private_data;

pr_debug("zz9000: USB: ioctl %x received\n", cmd);

switch (cmd) {
case HDIO_GETGEO:
{
struct hd_geometry geo;

ret = zz9000_usb_getgeo(dev, &geo);
if (copy_to_user
((void *)arg, &geo, sizeof(struct hd_geometry)))
ret = -EFAULT;
else
ret = 0;
break;
}

case CDROM_GET_CAPABILITY: /* 0x5331 - get capability */
{
struct gendisk *disk = bdev->bd_disk;

if (bdev->bd_disk && (disk->flags & GENHD_FL_CD))
ret = 0;
else
ret = -EINVAL;
break;
}

default:
{
printk(KERN_WARNING
"zz9000: USB: ioctl %x not implemented.\n", cmd);
ret = -EINVAL;
}
}

return ret;
}

#ifdef CONFIG_COMPAT
static int zz9000_usb_compat_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
// CONFIG_COMPAT is to allow running 32-bit userspace code on a 64-bit kernel
return -ENOTTY; // not supported
}
#endif

static const struct block_device_operations zz9000_usb_fops = {
.owner = THIS_MODULE,
.open = zz9000_usb_open,
.release = zz9000_usb_release,
.ioctl = zz9000_usb_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = zz9000_usb_compat_ioctl,
#endif
};

typedef struct {
//nothing
} zz9000_usb_cmd_t;

static int zz9000_usb_read(zz9000_usb_storage_t *dev, void *b_buf, loff_t pos, unsigned long b_len)
{
struct zz9000_platform_data *data = dev->data;
int ret = 0, i;

pr_debug("read pos %lld len %ld\n", pos, b_len);

zz_writew(b_len >> SECTOR_SHIFT, MNTZZ_USB_STATUS); //num_blocks;
zz_writew((pos >> SECTOR_SHIFT) >> 16, MNTZZ_USB_RX_HI);
zz_writew((pos >> SECTOR_SHIFT) & 0xffff, MNTZZ_USB_RX_LO);

if (zz_readw(MNTZZ_USB_STATUS) != (b_len >> SECTOR_SHIFT))
printk(KERN_WARNING "zz9000: USB: unexpected read status=%d(%d)\n",
zz_readw(MNTZZ_USB_STATUS), (uint32_t)(b_len >> SECTOR_SHIFT));

for (i = 0; i < (b_len >> SECTOR_SHIFT); i++) {
zz_writew(i, MNTZZ_USB_BUFSEL);
memcpy(b_buf + (i << SECTOR_SHIFT), data->usb_mem, 512);
}

return ret;
}

static int zz9000_usb_write(zz9000_usb_storage_t *dev, void *b_buf, loff_t pos, unsigned long b_len)
{
struct zz9000_platform_data *data = dev->data;
int ret = 0, i;

pr_debug("write pos %lld len %ld\n", pos, b_len);

for (i = 0; i < (b_len >> SECTOR_SHIFT); i++) {
zz_writew(i, MNTZZ_USB_BUFSEL);
memcpy(data->usb_mem, b_buf + (i << SECTOR_SHIFT), 512);
}

zz_writew(b_len >> SECTOR_SHIFT, MNTZZ_USB_STATUS);
zz_writew((pos >> SECTOR_SHIFT) >> 16, MNTZZ_USB_TX_HI);
zz_writew((pos >> SECTOR_SHIFT) & 0xffff, MNTZZ_USB_TX_LO);

if (zz_readw(MNTZZ_USB_STATUS) != (b_len >> SECTOR_SHIFT))
printk(KERN_WARNING "zz9000: USB: unexpected write status=%d(%d)\n",
zz_readw(MNTZZ_USB_STATUS), (uint32_t)(b_len >> SECTOR_SHIFT));

return ret;
}

static int zz9000_usb_request(struct request *rq, unsigned int *nr_bytes)
{
int ret = 0;
struct bio_vec bvec;
struct req_iterator iter;
zz9000_usb_storage_t *dev = rq->q->queuedata;
loff_t pos = blk_rq_pos(rq) << SECTOR_SHIFT;
loff_t dev_size = (loff_t) (dev->capacity << SECTOR_SHIFT);

pr_debug("zz9000: USB: request start from sector %lld \n",
blk_rq_pos(rq));

rq_for_each_segment(bvec, rq, iter) {
unsigned long b_len;
void *b_buf;

b_len = bvec.bv_len;
b_buf = page_address(bvec.bv_page) + bvec.bv_offset;

if ((pos + b_len) > dev_size)
b_len = (unsigned long)(dev_size - pos);

if (rq_data_dir(rq)) {
pr_debug("zz9000: USB: Write\n");
ret = zz9000_usb_write(dev, b_buf, pos, b_len);
} else {
pr_debug("zz9000: USB: Read\n");
ret = zz9000_usb_read(dev, b_buf, pos, b_len);
}
pos += b_len;
*nr_bytes += b_len;
}

return ret;
}

static blk_status_t zz9000_usb_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
{
blk_status_t status = BLK_STS_OK;
struct request *rq = bd->rq;

blk_mq_start_request(rq);

{
unsigned int nr_bytes = 0;
if (zz9000_usb_request(rq, &nr_bytes) != 0)
status = BLK_STS_IOERR;

pr_debug("zz9000: USB: request process %d bytes\n", nr_bytes);

if (blk_update_request(rq, status, nr_bytes))
BUG();
__blk_mq_end_request(rq, status);
}

return BLK_STS_OK;
}

static struct blk_mq_ops zz9000_mq_ops = {
.queue_rq = zz9000_usb_queue_rq,
};

int zz9000_usb_major = 0;
static struct request_queue *zz9000_usb_queue;
static zz9000_usb_storage_t *zz9000_usb_storage;

int zz9000_usb_init(struct zz9000_platform_data *data)
{
zz9000_usb_storage_t *dev;
int ret = 0;
int usb_block_size = 512;
unsigned int usb_capacity;
char cap_str_2[10], cap_str_10[10];

zz9000_data = data;

usb_capacity = zz_readl(MNTZZ_USB_CAPACITY);
if (usb_capacity == 0) {
printk(KERN_INFO " USB device not found at boot.\n");
return 0;
}

string_get_size(usb_capacity, usb_block_size,
STRING_UNITS_2, cap_str_2, sizeof(cap_str_2));
string_get_size(usb_capacity, usb_block_size,
STRING_UNITS_10, cap_str_10, sizeof(cap_str_10));

printk(KERN_INFO
" USB capacity: %d %d-byte logical blocks: (%s GB/%s GiB)\n",
usb_capacity, usb_block_size, cap_str_10, cap_str_2);

/* Register block device */
if ((zz9000_usb_major = register_blkdev(0, "zzusb")) <= 0)
return -EIO;

dev = kzalloc(sizeof(zz9000_usb_storage_t), GFP_KERNEL);
if (dev == NULL) {
printk(KERN_WARNING
"zz9000: USB: Unable to allocate %zd bytes\n",
sizeof(zz9000_usb_storage_t));
return -ENOMEM;
}
dev->data = data;
zz9000_usb_storage = dev;

/* Remember capacity in blocks */
dev->capacity = usb_capacity;

/* configure tag_set */
dev->tag_set.cmd_size = sizeof(zz9000_usb_cmd_t);
dev->tag_set.driver_data = dev;

zz9000_usb_queue =
blk_mq_init_sq_queue(&dev->tag_set, &zz9000_mq_ops, 128,
BLK_MQ_F_SHOULD_MERGE);
if (IS_ERR(zz9000_usb_queue)) {
ret = PTR_ERR(zz9000_usb_queue);
printk(KERN_WARNING
"zz9000: USB: Unable to allocate and initialize tag set\n");
return -EIO;
}
dev->queue = zz9000_usb_queue;

dev->queue->queuedata = dev;

/* Set the hardware sector size and the max number of sectors */
//blk_queue_hardsect_size(zz9000_usb_queue, usb_block_size);
blk_queue_logical_block_size(zz9000_usb_queue, usb_block_size);
blk_queue_max_hw_sectors(zz9000_usb_queue, 32);

/* Allocate an associated gendisk */
dev->disk = alloc_disk(16);
if (dev->disk == NULL) {
printk(KERN_WARNING "zz9000: USB: Failed to allocate disk\n");
return -ENOMEM;
}

/* Fill in parameters associated with the gendisk */
dev->disk->flags |= GENHD_FL_REMOVABLE;
dev->disk->fops = &zz9000_usb_fops;
dev->disk->queue = zz9000_usb_queue;
dev->disk->major = zz9000_usb_major;
dev->disk->first_minor = 0;
dev->disk->private_data = dev;

sprintf(dev->disk->disk_name, "zzusb");

/* Set the capacity of USB storage media in number of sectors */
set_capacity(dev->disk, usb_capacity);

/* Add disk to the block I/O subsystem */
add_disk(dev->disk);

pr_debug("zz9000: USB block device was initialized.\n");

return 0;
}

int zz9000_usb_remove(struct zz9000_platform_data *data)
{
zz9000_usb_storage_t *dev = zz9000_usb_storage;

if (dev == NULL)
return 0;

if (dev->disk)
del_gendisk(dev->disk);

if (dev->queue) {
blk_cleanup_queue(dev->queue);
dev->queue = NULL;
}

if (dev->tag_set.tags)
blk_mq_free_tag_set(&dev->tag_set);

if (dev->disk) {
put_disk(dev->disk);
dev->disk = NULL;
}

kfree(dev);
zz9000_usb_storage = NULL;

if (zz9000_usb_major > 0)
unregister_blkdev(zz9000_usb_major, "zzusb");

pr_debug("zz9000: USB block device was removed.\n");

return 0;
}

static int __init zz9000_usb_module_init(void)
{
struct zz9000_platform_data *data = get_zz9000_data();
return zz9000_usb_init(data);
}

module_init(zz9000_usb_module_init);

static void __exit zz9000_usb_module_exit(void)
{
struct zz9000_platform_data *data = get_zz9000_data();
zz9000_usb_remove(data);
}

module_exit(zz9000_usb_module_exit);

MODULE_DESCRIPTION("MNT ZZ9000 USB driver");
MODULE_AUTHOR("Stefan Reinauer <stefan.reinauer@coreboot.org>");
MODULE_LICENSE("GPL");

Loading…
Cancel
Save