* Re: [PATCH] drivers/misc: introduce Freescale Data Collection Manager driver
From: Tabi Timur-B04825 @ 2011-08-01 23:58 UTC (permalink / raw)
To: Mark Brown
Cc: linuxppc-dev@ozlabs.org, linux-kernel@vger.kernel.org,
arnd@arndb.de
In-Reply-To: <20110801234645.GA15792@sirena.org.uk>
Mark Brown wrote:
> On Mon, Aug 01, 2011 at 04:48:54PM -0500, Timur Tabi wrote:
>> The Data Collection Manager (DCM) is a feature of the FPGA on some Frees=
cale
>> PowerPC reference boards that can read temperature, current, and voltage
>> settings from the sensors on those boards. This driver exposes the DCM =
via a
>> sysfs interface (/sys/devices/platform/fsl-ocm.0).
>
> This sounds like it should be a hwmon driver.
I didn't see any way to interface the hardware to the hwmon layer in a=20
manner that provides the information that our customers went using this=20
hardware.
>> The DCM collects and tallies data over a period of time in the backgroun=
d,
>> without utilizing any resources on the host (CPU, memory, etc). The dat=
a is
>> summarized and made available when data collection stops. This allows p=
ower
>> consumption to be measured while the host is performing some tasks (usua=
lly
>> a benchmark).
>
> Though this is a bit odd for the subsystem I don't think it's too far
> out of what other hwmon chips can do, some of them do have longer term
> stats than just instantaneous readings.
Can you show an example or some documentation? I couldn't find anything=20
remotely like that. I don't even see anything that lets me start/stop=20
monitoring of sensors.
--=20
Timur Tabi
Linux kernel developer at Freescale=
^ permalink raw reply
* [PATCH] drivers/misc: introduce Freescale Data Collection Manager driver
From: Timur Tabi @ 2011-08-01 21:48 UTC (permalink / raw)
To: arnd, grant.likely, linuxppc-dev, linux-kernel
The Data Collection Manager (DCM) is a feature of the FPGA on some Freescale
PowerPC reference boards that can read temperature, current, and voltage
settings from the sensors on those boards. This driver exposes the DCM via a
sysfs interface (/sys/devices/platform/fsl-ocm.0).
The DCM collects and tallies data over a period of time in the background,
without utilizing any resources on the host (CPU, memory, etc). The data is
summarized and made available when data collection stops. This allows power
consumption to be measured while the host is performing some tasks (usually
a benchmark).
Signed-off-by: Timur Tabi <timur@freescale.com>
---
Grant, could you please review the way I instantiate the platform driver and
call the .probe function?
Documentation/misc-devices/fsl_dcm.txt | 50 +++
drivers/misc/Kconfig | 14 +
drivers/misc/Makefile | 1 +
drivers/misc/fsl_dcm.c | 750 ++++++++++++++++++++++++++++++++
4 files changed, 815 insertions(+), 0 deletions(-)
create mode 100644 Documentation/misc-devices/fsl_dcm.txt
create mode 100644 drivers/misc/fsl_dcm.c
diff --git a/Documentation/misc-devices/fsl_dcm.txt b/Documentation/misc-devices/fsl_dcm.txt
new file mode 100644
index 0000000..d679db3
--- /dev/null
+++ b/Documentation/misc-devices/fsl_dcm.txt
@@ -0,0 +1,50 @@
+Freescale Data Collection Manager (DCM) device driver
+
+Inside the FPGA of some Freescale QorIQ (PowerPC) reference boards is a
+microprocessor called the General Purpose Processor (GSMA). Running on
+the GSMA is the Data Collection Manager (DCM), which is used to
+periodically read and tally voltage, current, and temperature measurements
+from the on-board sensors. You can use this feature to measure power
+consumption while running tests, without having the host CPU perform those
+measurements.
+
+Only some Freescale reference boards are supported. In most cases, the
+on-board dip switches need to be changed to enable the DCM.
+
+These are the boards that are currently supported:
+
+* P1022DS
+ Set SW9[7:8] to '10'.
+
+* P3060QDS
+ Set SW9[2] to 0 and SW9[8] to '1'.
+
+* P4080DS
+ Set SW9[7:8] to '10'.
+
+* P5020DS
+ Set SW9[7:8] to '10'.
+
+To use, first send "1" to the 'control' file:
+ echo 1 > /sys/devices/platform/fsl-dcm.0/control
+
+Then perform your benchmarks, when you are done, stop the monitoring:
+ echo 0 > /sys/devices/platform/fsl-dcm.0/control
+
+You can display the current status of data collection
+ cat /sys/devices/platform/fsl-dcm.0/control
+
+To view the results of data collection (after having been stopped):
+ cat /sys/devices/platform/fsl-dcm.0/result
+
+To change the sampling frequency to 10Hz (for example)
+ echo 10 > /sys/devices/platform/fsl-dcm.0/frequency
+
+Empirical evidence shows that the maximum sampling frequency is 48 Hz.
+Anything higher than that is unreliable.
+
+You can display the current sampling frequency:
+ cat /sys/devices/platform/fsl-dcm.0/frequency
+
+To display information about the DCM:
+ cat /sys/devices/platform/fsl-dcm.0/info
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 0a4d86c..93b4952 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -498,6 +498,20 @@ config USB_SWITCH_FSA9480
stereo and mono audio, video, microphone and UART data to use
a common connector port.
+config FSL_DCM
+ tristate "Freescale Data Collection Manager (DCM) driver"
+ depends on FSL_SOC && PPC_85xx
+ help
+ Inside the FPGA of some Freescale QorIQ (PowerPC) reference boards
+ is a microcontroller called the General Purpose Processor (GSMA).
+ Running on the GSMA is the Data Collection Manager (DCM), which is
+ used to periodically read and tally voltage, current, and temperature
+ measurements from the on-board sensors. You can use this feature to
+ measure power consumption while running tests, without having the
+ host CPU perform those measurements.
+
+ See Documentation/misc-devices/fsl_dcm.txt for more information.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 8f3efb6..f039c59 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -47,3 +47,4 @@ obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o
obj-y += lis3lv02d/
obj-y += carma/
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
+obj-$(CONFIG_FSL_DCM) += fsl_dcm.o
diff --git a/drivers/misc/fsl_dcm.c b/drivers/misc/fsl_dcm.c
new file mode 100644
index 0000000..0e50e08
--- /dev/null
+++ b/drivers/misc/fsl_dcm.c
@@ -0,0 +1,750 @@
+/*
+ * Freescale Data Collection Manager (DCM) device driver
+ *
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ * Author: Timur Tabi <timur@freescale.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ * Inside the FPGA of some Freescale QorIQ (PowerPC) reference boards is a
+ * microprocessor called the General Purpose Processor (GSMA). Running on
+ * the GSMA is the Data Collection Manager (DCM), which is used to
+ * periodically read and tally voltage, current, and temperature measurements
+ * from the on-board sensors. You can use this feature to measure power
+ * consumption while running tests, without having the host CPU perform those
+ * measurements.
+ *
+ * See Documentation/misc-devices/fsl_dcm.txt for more information.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/of_platform.h>
+
+/* sysfs commands for 'control' */
+#define SYSFS_DCM_CMD_STOP 0
+#define SYSFS_DCM_CMD_START 1
+
+/* Crecords can be either voltage, current, or temperature */
+enum crecord_type {
+ CR_V, /* voltage */
+ CR_C, /* current */
+ CR_T /* temperature */
+};
+
+#define MAX_FREQUENCY 48 /* max freq (Hz) the DCM supports */
+#define DATA_ADDR 0x80 /* data address in DCM SRAM */
+
+struct crecord {
+ __be16 curr; /* last sample read */
+ __be16 max; /* maximum of all samples read */
+ __be16 qty1; /* number of samples read */
+ u8 qty2;
+ __be32 acc; /* sum of all samples read */
+} __packed;
+#define MAX_CRECORDS ((0x100 - DATA_ADDR) / sizeof(struct crecord))
+
+struct om_info {
+ __be16 version; /* DCM version number */
+ u8 prescale; /* prescale value used (should be 0) */
+ u8 timer; /* timer */
+ u8 count; /* number of CRECORDS */
+ __be16 address; /* address of CRECORD array in SRAM */
+ u8 res[3];
+} __packed;
+
+/**
+ * struct dcm_board - board-specific information
+ * @compatible: the 'compatible' property to search for
+ * @offsets: array of PIXIS address offsets (addr, data, dcmd, omsg, mack)
+ * @mask: enable mask for the OM_ENABLE command
+ * @convert_vout: board-specific function to convert a VOUT to millivolts
+ * @convert_iout: board-specific function to convert an IOUT to milliamps
+ * @convert_tout: board-specific function to convert a TOUT to degrees Celsius
+ * @ical: calibration factor for .convert_iout function
+ * @num: number of crecords (equal to the number of '1's in @mask)
+ * @names: array of names of each crecord
+ * @types: array of types of each crecord
+ */
+struct dcm_board {
+ const char *compatible;
+ unsigned int offsets[5];
+ u16 mask;
+ unsigned int (*convert_vout)(u16 vout);
+ unsigned int (*convert_iout)(u16 iout, unsigned int ical);
+ unsigned int (*convert_tout)(u16 tout);
+ unsigned int ical;
+ unsigned int num;
+ const char *names[MAX_CRECORDS];
+ enum crecord_type types[MAX_CRECORDS];
+};
+
+/* PIXIS register status bits */
+#define PX_OCMD_MSG (1 << 0)
+#define PX_OACK_ERR (1 << 1)
+#define PX_OACK_ACK (1 << 0) /* OACK is sometimes called MACK */
+
+/* DCM commands */
+#define OM_END 0x00
+#define OM_SETDLY 0x01
+#define OM_RST0 0x02
+#define OM_RST1 0x03
+#define OM_CHKDLY 0x04
+#define OM_PWR 0x05
+#define OM_WAKE 0x07
+#define OM_GETMEM 0x08
+#define OM_SETMEM 0x09
+#define OM_SCLR 0x10
+#define OM_START 0x11
+#define OM_STOP 0x12
+#define OM_GET 0x13
+#define OM_ENABLE 0x14
+#define OM_TIMER 0x15
+#define OM_SETV 0x30
+#define OM_INFO 0x31
+
+struct fsl_dcm_data {
+ struct device *dev;
+ const struct dcm_board *board;
+ void __iomem *base; /* PIXIS/QIXIS base address */
+ u8 __iomem *addr; /* SRAM address */
+ u8 __iomem *data; /* SRAM data */
+ u8 __iomem *ocmd; /* DCM command/status */
+ u8 __iomem *omsg; /* DCM message */
+ u8 __iomem *mack; /* DCM acknowledge */
+ struct crecord rec[MAX_CRECORDS];
+ u8 timer;
+ int running;
+};
+
+/*
+ * Converts a 16-bit VOUT value from the Zilker ZL6100 into a voltage value,
+ * in millivolts.
+ */
+static unsigned int voltage_from_zl6100(u16 vout)
+{
+ return (1000UL * vout) / (1 << 13);
+}
+
+/*
+ * Converts a 16-bit IOUT from the Texas Instruments INA220 chip into a
+ * current value, in milliamps. 'ical' is a board-specific calibration
+ * factor.
+ */
+static unsigned int current_from_ina220(u16 iout, unsigned int ical)
+{
+ unsigned long c;
+
+ /* milliamp = 1000 * ((iout / 100) / cal-factor) */
+ /* = (iout * 100000) / ical */
+ c = iout * 1000 * 100;
+ c /= ical;
+
+ return c;
+}
+
+/*
+ * Converts a 16-bit TOUT value from the sensor device into a temperature
+ * value, in degrees Celsius.
+ */
+static unsigned int temp_from_u16(u16 tout)
+{
+ return tout;
+}
+
+/*
+ * Write a byte to an address in SRAM
+ */
+static void write_sram(struct fsl_dcm_data *dcm, u8 offset, u8 v)
+{
+ out_8(dcm->addr, offset);
+ out_8(dcm->data, v);
+}
+
+/*
+ * Read a byte from an address in SRAM
+ */
+static u8 read_sram(struct fsl_dcm_data *dcm, u8 offset)
+{
+ out_8(dcm->addr, offset);
+
+ return in_8(dcm->data);
+}
+
+/*
+ * True TRUE if we can read/write SRAM, FALSE otherwise.
+ *
+ * If the SRAM is unavailable, it's probably because the DCM is busy with it.
+ */
+static int is_sram_available(struct fsl_dcm_data *dcm)
+{
+ u8 ack, cmd;
+
+ cmd = in_8(dcm->ocmd);
+ ack = in_8(dcm->mack);
+
+ if ((cmd & PX_OCMD_MSG) || (ack & PX_OACK_ACK)) {
+ dev_dbg(dcm->dev, "dcm is not ready (cmd=%02X mack=%02X)\n",
+ cmd, ack);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Loads and program into SRAM, then tells the DCM to run it, and then waits
+ * for it to finish.
+ */
+static int run_program(struct fsl_dcm_data *dcm, u8 addr, unsigned int len, ...)
+{
+ u8 v, n;
+ va_list args;
+
+ if (addr + len > 0xff) {
+ dev_err(dcm->dev, "address/length of %u/%u is out of bounds\n",
+ addr, len);
+ return 0;
+ }
+
+ /* load the program into SRAM */
+ va_start(args, len);
+ for (n = addr; n < addr + len; n++) {
+ v = va_arg(args, int);
+ write_sram(dcm, n, v);
+ }
+ va_end(args);
+
+ /* start the DCM */
+ out_8(dcm->omsg, addr);
+ out_8(dcm->ocmd, PX_OCMD_MSG);
+
+ /* wait for ack or error */
+ v = spin_event_timeout(in_8(dcm->mack) & (PX_OACK_ERR | PX_OACK_ACK),
+ 50000, 1000);
+ if ((!v) || (v & PX_OACK_ERR)) {
+ dev_err(dcm->dev, "timeout or error waiting for start ack\n");
+ return 0;
+ }
+
+ /* 4. allow the host to read SRAM */
+ out_8(dcm->ocmd, 0);
+
+ /* 5. wait for DCM to stop (ack == 0) or error (err == 1) */
+ spin_event_timeout(
+ ((v = in_8(dcm->mack)) & (PX_OACK_ERR | PX_OACK_ACK)) != PX_OACK_ACK,
+ 50000, 1000);
+
+ /* 6. check for error or timeout */
+ if (v & (PX_OACK_ERR | PX_OACK_ACK)) {
+ dev_err(dcm->dev, "timeout or error waiting for stop ack\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+#define TRATE0 241120 /* t-rate if prescale==0, in millihertz */
+#define TRATE1 38759330 /* t-rate if prescale==1, in millihertz */
+
+/*
+ * Empirical tests show that any frequency higher than 48Hz is unreliable.
+ */
+static int set_dcm_frequency(struct fsl_dcm_data *dcm, unsigned long frequency)
+{
+ unsigned long timer;
+
+ if (!is_sram_available(dcm)) {
+ dev_err(dcm->dev, "dcm is busy\n");
+ return 0;
+ }
+
+ /* Restrict the frequency to a supported range. */
+ frequency = clamp_t(unsigned long, frequency, 1, MAX_FREQUENCY);
+
+ /* We only support prescale == 0 */
+ timer = TRATE0 / frequency;
+ dcm->timer = (timer / 1000) - 1;
+
+ return run_program(dcm, 0, 6, OM_TIMER, 0, dcm->timer, 0, 0, OM_END);
+}
+
+static int copy_from_sram(struct fsl_dcm_data *dcm, unsigned int addr,
+ void *buf, unsigned int len)
+{
+ u8 *p = buf;
+ unsigned int i;
+
+ if (addr + len > 0xff) {
+ dev_err(dcm->dev, "address/length of %u/%u is out of bounds\n",
+ addr, len);
+ return 0;
+ }
+
+ for (i = 0; i < len; i++)
+ p[i] = read_sram(dcm, addr + i);
+
+ return 1;
+}
+
+/*
+ * Tells the DCM which channels to collect data on.
+ */
+static int select_dcm_channels(struct fsl_dcm_data *dcm, u16 mask)
+{
+ if (!is_sram_available(dcm)) {
+ dev_err(dcm->dev, "dcm is busy\n");
+ return 0;
+ }
+
+ return run_program(dcm, 0, 4, OM_ENABLE,
+ ((mask >> 8) & 0xFF), mask & 0xFF, OM_END);
+}
+
+/*
+ * Tells the DCM to start data collection. If the DCM is currently running,
+ * it is restarted. Any currently collected data is cleared.
+ */
+static int start_data_collection(struct fsl_dcm_data *dcm)
+{
+ if (!is_sram_available(dcm)) {
+ dev_err(dcm->dev, "dcm is busy\n");
+ return 0;
+ }
+
+ if (dcm->running)
+ dev_dbg(dcm->dev, "restarting\n");
+
+ dcm->running = true;
+
+ return run_program(dcm, 0, 4, OM_STOP, OM_SCLR, OM_START, OM_END);
+}
+
+/*
+ * Tells the DCM to stop data collection. Collected data is copied from
+ * SRAM into a local buffer.
+ */
+static int stop_data_collection(struct fsl_dcm_data *dcm)
+{
+ if (!dcm->running) {
+ dev_dbg(dcm->dev, "dcm is already stopped\n");
+ return 1;
+ }
+
+ if (!is_sram_available(dcm)) {
+ dev_err(dcm->dev, "dcm is busy\n");
+ return 0;
+ }
+
+ if (!run_program(dcm, 0, 4, OM_STOP, OM_GET, DATA_ADDR, OM_END)) {
+ dev_err(dcm->dev, "could not stop monitoring\n");
+ return 0;
+ }
+
+ if (!copy_from_sram(dcm, DATA_ADDR, dcm->rec,
+ dcm->board->num * sizeof(struct crecord))) {
+ dev_err(dcm->dev, "could not copy sensor data\n");
+ return 0;
+ }
+
+ dcm->running = 0;
+ return 1;
+}
+
+ssize_t fsl_dcm_sysfs_control_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fsl_dcm_data *dcm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", dcm->running ? "running" : "stoppped");
+}
+
+ssize_t fsl_dcm_sysfs_control_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fsl_dcm_data *dcm = dev_get_drvdata(dev);
+ unsigned long pm_cmd;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &pm_cmd);
+ if (ret)
+ return ret;
+
+ switch (pm_cmd) {
+ case SYSFS_DCM_CMD_START:
+ ret = start_data_collection(dcm);
+ if (!ret)
+ dev_err(dev, "failed to start power monitoring.\n");
+ break;
+ case SYSFS_DCM_CMD_STOP:
+ ret = stop_data_collection(dcm);
+ if (!ret)
+ dev_err(dev, "failed to stop power monitoring\n");
+ break;
+ default:
+ return -EIO;
+ }
+
+ return count;
+}
+
+/* Calculate the average, even if 'count' is zero */
+#define AVG(sum, count) ((sum) / ((count) ?: 1))
+
+static ssize_t fsl_dcm_sysfs_result(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fsl_dcm_data *dcm = dev_get_drvdata(dev);
+ const struct dcm_board *board = dcm->board;
+ unsigned int i;
+ ssize_t len;
+
+ len = sprintf(buf,
+ "Name Num CURR MAX SUM Max Average\n"
+ "====== === ========== ========== =============== ======== ========\n");
+
+ for (i = 0; i < board->num; i++) {
+ unsigned int num, max, avg;
+ const char *unit;
+
+ num = (dcm->rec[i].qty1 << 8) | (dcm->rec[i].qty2);
+
+ switch (board->types[i]) {
+ case CR_V:
+ max = board->convert_vout(dcm->rec[i].max);
+ avg = board->convert_vout(AVG(dcm->rec[i].acc, num));
+ unit = "mV";
+ break;
+ case CR_C:
+ max = board->convert_iout(dcm->rec[i].max, board->ical);
+ avg = board->convert_iout(AVG(dcm->rec[i].acc, num),
+ board->ical);
+ unit = "mA";
+ break;
+ case CR_T:
+ max = board->convert_tout(dcm->rec[i].max);
+ avg = board->convert_tout(AVG(dcm->rec[i].acc, num));
+ unit = "C ";
+ break;
+ default:
+ continue;
+ }
+
+ len += sprintf(buf + len,
+ "%-6s %3u %5u/%04x %5u/%04x %8u/%06x %6d %s %6d %s\n",
+ board->names[i], num,
+ dcm->rec[i].curr, dcm->rec[i].curr,
+ dcm->rec[i].max, dcm->rec[i].max,
+ dcm->rec[i].acc, dcm->rec[i].acc,
+ max, unit, avg, unit);
+ }
+
+ return len;
+}
+
+ssize_t fsl_dcm_sysfs_info(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fsl_dcm_data *dcm = dev_get_drvdata(dev);
+ struct om_info info;
+ ssize_t len;
+
+ if (!is_sram_available(dcm)) {
+ dev_err(dev, "dcm is busy\n");
+ return 0;
+ }
+
+ if (!run_program(dcm, 0, 2, OM_INFO, DATA_ADDR)) {
+ dev_err(dev, "could not run 'info' program\n");
+ return 0;
+ }
+
+ if (!copy_from_sram(dcm, DATA_ADDR, &info, sizeof(info))) {
+ dev_err(dev, "could not copy 'info' data\n");
+ return 0;
+ }
+
+ len = sprintf(buf, "DCM Version: %u\n", info.version);
+ len += sprintf(buf + len, "Prescale: %u\n", info.prescale);
+ len += sprintf(buf + len, "Timer: %u\n", info.timer);
+ len += sprintf(buf + len, "Number of CRECORDs: %u\n", info.count);
+ len += sprintf(buf + len, "CRECORD Address: %u\n", info.address);
+
+ return len;
+}
+
+ssize_t fsl_dcm_sysfs_frequency_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fsl_dcm_data *dcm = dev_get_drvdata(dev);
+ unsigned long frequency;
+
+ frequency = TRATE0 / (dcm->timer + 1);
+
+ return sprintf(buf, "%lu Hz\n", frequency / 1000);
+}
+
+ssize_t fsl_dcm_sysfs_frequency_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fsl_dcm_data *dcm = dev_get_drvdata(dev);
+ unsigned long frequency;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &frequency);
+ if (ret)
+ return ret;
+
+ set_dcm_frequency(dcm, frequency);
+
+ return count;
+}
+
+static DEVICE_ATTR(control, 0666, fsl_dcm_sysfs_control_show,
+ fsl_dcm_sysfs_control_store);
+static DEVICE_ATTR(result, 0444, fsl_dcm_sysfs_result, NULL);
+static DEVICE_ATTR(info, 0444, fsl_dcm_sysfs_info, NULL);
+static DEVICE_ATTR(frequency, 0666, fsl_dcm_sysfs_frequency_show,
+ fsl_dcm_sysfs_frequency_store);
+
+static const struct attribute_group fsl_dcm_attr_group = {
+ .attrs = (struct attribute *[]) {
+ &dev_attr_control.attr,
+ &dev_attr_result.attr,
+ &dev_attr_info.attr,
+ &dev_attr_frequency.attr,
+ NULL,
+ },
+};
+
+static int fsl_dcm_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_dcm_data *dcm;
+ int ret;
+
+ dcm = kzalloc(sizeof(struct fsl_dcm_data), GFP_KERNEL);
+ if (!dcm)
+ return -ENOMEM;
+
+ dcm->base = of_iomap(np, 0);
+ if (!dcm->base) {
+ dev_err(&pdev->dev, "could not map fpga node\n");
+ ret = -ENOMEM;
+ goto error_kzalloc;
+ }
+
+ dcm->dev = &pdev->dev;
+ dcm->board = pdev->dev.platform_data;
+
+ dcm->addr = dcm->base + dcm->board->offsets[0];
+ dcm->data = dcm->base + dcm->board->offsets[1];
+ dcm->ocmd = dcm->base + dcm->board->offsets[2];
+ dcm->omsg = dcm->base + dcm->board->offsets[3];
+ dcm->mack = dcm->base + dcm->board->offsets[4];
+
+ /* Check to make sure the DCM is enable and working */
+ if (!is_sram_available(dcm)) {
+ dev_err(&pdev->dev, "dcm is not responding\n");
+ ret = -ENODEV;
+ goto error_iomap;
+ }
+
+ dev_set_drvdata(&pdev->dev, dcm);
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &fsl_dcm_attr_group);
+ if (ret) {
+ dev_err(&pdev->dev, "could not create sysfs group\n");
+ goto error_iomap;
+ }
+
+ if (!select_dcm_channels(dcm, dcm->board->mask)) {
+ dev_err(&pdev->dev, "could not set crecord mask\n");
+ ret = -ENODEV;
+ goto error_sysfs;
+ }
+
+ /* Set the timer to the fastest support rate. */
+ if (!set_dcm_frequency(dcm, MAX_FREQUENCY)) {
+ dev_err(&pdev->dev, "could not set frequency\n");
+ ret = -ENODEV;
+ goto error_sysfs;
+ }
+
+ return 0;
+
+error_sysfs:
+ sysfs_remove_group(&pdev->dev.kobj, &fsl_dcm_attr_group);
+
+error_iomap:
+ iounmap(dcm->base);
+
+error_kzalloc:
+ kfree(dcm);
+
+ return ret;
+}
+
+static int fsl_dcm_remove(struct platform_device *pdev)
+{
+ struct fsl_dcm_data *dcm = dev_get_drvdata(&pdev->dev);
+
+ stop_data_collection(dcm);
+
+ sysfs_remove_group(&pdev->dev.kobj, &fsl_dcm_attr_group);
+
+ iounmap(dcm->base);
+ kfree(dcm);
+
+ return 0;
+}
+
+static struct platform_driver fsl_dcm_platform_driver = {
+ .probe = fsl_dcm_probe,
+ .remove = fsl_dcm_remove,
+ .driver = {
+ .name = "fsl-dcm",
+ .owner = THIS_MODULE,
+ },
+};
+
+static const struct dcm_board dcm_types[] = {
+ {
+ "fsl,p1022ds-pixis",
+ { 0x0a, 0x0d, 0x14, 0x15, 0x18},
+ 0x155,
+ voltage_from_zl6100,
+ NULL,
+ temp_from_u16,
+ 0,
+ 5,
+ /* Current measurements are not reliable on this board */
+ {"Vdd", "OVdd", "S/XVdd", "GVdd", "CPU_Tj"},
+ {CR_V, CR_V, CR_V, CR_V, CR_T},
+ },
+ {
+ "fsl,p3060qds-pixis",
+ { 0x12, 0x13, 0x14, 0x15, 0x18},
+ 0x1ff,
+ voltage_from_zl6100,
+ current_from_ina220,
+ temp_from_u16,
+ 10328,
+ 9,
+ {"Vdd_CA", "Idd_CA", "Vdd_CB", "Idd_CB", "Vdd_PL", "Idd_PL",
+ "GVdd", "GIdd", "CPU_Tj"},
+ {CR_V, CR_C, CR_V, CR_C, CR_V, CR_C, CR_V, CR_C, CR_T},
+ },
+ {
+ "fsl,p4080ds-pixis",
+ { 0x0a, 0x0d, 0x14, 0x15, 0x18},
+ 0x155,
+ voltage_from_zl6100,
+ NULL,
+ temp_from_u16,
+ 0,
+ 5,
+ /* Current measurements are not reliable on this board */
+ {"Vdd_CA", "Vdd_CB", "Vdd_PL", "GVdd", "CPU_Tj"},
+ {CR_V, CR_V, CR_V, CR_V, CR_T},
+ },
+ {
+ "fsl,p5020ds-pixis",
+ { 0x0a, 0x0d, 0x14, 0x15, 0x18},
+ 0x1ff,
+ voltage_from_zl6100,
+ current_from_ina220,
+ temp_from_u16,
+ 21064,
+ 9,
+ {"Vdd_CA", "Idd_CA", "Vdd_CB", "Idd_CB", "Vdd_PL", "Idd_PL",
+ "GVdd", "GIdd", "CPU_Tj"},
+ {CR_V, CR_C, CR_V, CR_C, CR_V, CR_C, CR_V, CR_C, CR_T},
+ },
+};
+
+static int __init fsl_dcm_init(void)
+{
+ struct platform_device *pdev;
+ struct device_node *np = NULL;
+ unsigned int i;
+ int ret;
+
+ /* Look for a supported platform */
+ for (i = 0; i < ARRAY_SIZE(dcm_types); i++) {
+ np = of_find_compatible_node(NULL, NULL,
+ dcm_types[i].compatible);
+ if (np)
+ break;
+ }
+ if (!np) {
+ pr_debug("fsl-dcm: unsupported platform\n");
+ return -ENODEV;
+ }
+
+ /* We found a supported platform, so register a platform driver */
+ ret = platform_driver_register(&fsl_dcm_platform_driver);
+ if (ret) {
+ pr_err("fsl-dcm: could not register platform driver\n");
+ goto error_np;
+ }
+
+ /* We need to create a device and add the data for this platform */
+ pdev = platform_device_alloc(fsl_dcm_platform_driver.driver.name, 0);
+ if (!pdev) {
+ ret = -ENOMEM;
+ goto error_drv;
+ }
+
+ /* Pass the device_node pointer to the probe function */
+ pdev->dev.of_node = np;
+
+ /* Pass the DCM platform data */
+ ret = platform_device_add_data(pdev, &dcm_types[i],
+ sizeof(struct dcm_board));
+ if (ret) {
+ pr_err("fsl-dcm: could not register platform driver\n");
+ goto error_dev;
+ }
+
+ /* This will call the probe function */
+ ret = platform_device_add(pdev);
+ if (ret) {
+ pr_err("fsl-dcm: could not register platform driver\n");
+ goto error_dev;
+ }
+
+ of_node_put(np);
+
+ return 0;
+
+error_dev:
+ platform_device_unregister(pdev);
+
+error_drv:
+ platform_driver_unregister(&fsl_dcm_platform_driver);
+
+error_np:
+ of_node_put(np);
+
+ return ret;
+}
+
+static void __exit fsl_dcm_exit(void)
+{
+ platform_driver_unregister(&fsl_dcm_platform_driver);
+}
+
+MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
+MODULE_DESCRIPTION("Freescale Data Collection Manager driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(fsl_dcm_init);
+module_exit(fsl_dcm_exit);
--
1.7.3.4
^ permalink raw reply related
* Re: [PATCH 10/15] ps3stor_lib: Add support for multiple regions
From: Andre Heider @ 2011-08-01 21:01 UTC (permalink / raw)
To: Geert Uytterhoeven; +Cc: Geoff Levand, cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <CAMuHMdXJWTw33ehLAPq00WjsebBHtQd5B9nM5Y-Z5JtevzmVqA@mail.gmail.com>
On Mon, Aug 1, 2011 at 10:35 PM, Geert Uytterhoeven
<geert@linux-m68k.org> wrote:
> On Mon, Aug 1, 2011 at 22:03, Andre Heider <a.heider@gmail.com> wrote:
>> =A0 =A0 =A0 =A0priv->gendisk =3D gendisk;
>> +
>> + =A0 =A0 =A0 /* find first accessible region */
>> + =A0 =A0 =A0 for (region_idx =3D 0; region_idx < dev->num_regions; regi=
on_idx++)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (test_bit(region_idx, &dev->accessible_=
regions)) {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 priv->region_idx =3D regio=
n_idx;
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
>> +
>
> Why not
>
> priv->region_idx =3D __ffs(dev->accessible_regions);
Right, thanks. Will change that for v2
^ permalink raw reply
* Re: [Cbe-oss-dev] [PATCH 06/15] ps3flash: Fix region align checks
From: Geert Uytterhoeven @ 2011-08-01 21:00 UTC (permalink / raw)
To: Andre Heider; +Cc: Geoff Levand, cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <CAHsu+b-xtj8DvLS-v_5-QZ3Erc-9jpFd0rodeoc4RU9YPho3tw@mail.gmail.com>
On Mon, Aug 1, 2011 at 22:56, Andre Heider <a.heider@gmail.com> wrote:
> On Mon, Aug 1, 2011 at 10:29 PM, Geert Uytterhoeven
> <geert@linux-m68k.org> wrote:
>> On Mon, Aug 1, 2011 at 22:02, Andre Heider <a.heider@gmail.com> wrote:
>>> The region fields used by the align checks are set in
>>> ps3stor_setup(), so move those after that call.
>>
>> Are you sure?
>> Aren't they set in
>> arch/powerpc/platforms/ps3/device-init.c:ps3_setup_storage_dev()?
>
> Hm right, unfortunate commit message... :)
> dev->region_idx is set in ps3stor_probe_access(), which is called from
> ps3stor_setup().
> So the code always checked the first region, where it should check the
> one beeing used.
IC. You're right. That's indeed a bug.
Gr{oetje,eeting}s,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k=
.org
In personal conversations with technical people, I call myself a hacker. Bu=
t
when I'm talking to journalists I just say "programmer" or something like t=
hat.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0=C2=A0 =C2=A0=C2=A0 -- Linus Torvalds
^ permalink raw reply
* Re: [Cbe-oss-dev] [PATCH 09/15] ps3: Limit the number of regions per storage device
From: Andre Heider @ 2011-08-01 20:58 UTC (permalink / raw)
To: Geert Uytterhoeven; +Cc: Geoff Levand, cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <CAMuHMdVq+WSjZKVZk1riEFX6NgW6Y+8EhCc0MqMtuDK5KAD7dw@mail.gmail.com>
On Mon, Aug 1, 2011 at 10:30 PM, Geert Uytterhoeven
<geert@linux-m68k.org> wrote:
> On Mon, Aug 1, 2011 at 22:03, Andre Heider <a.heider@gmail.com> wrote:
>> There can be only 8 regions, add a sanity check
>
> Why can there be only 8 regions?
I believe lv1 limits it to 8? I might be mistaken here, it mostly is a
check for the patches after this one
^ permalink raw reply
* Re: [Cbe-oss-dev] [PATCH 06/15] ps3flash: Fix region align checks
From: Andre Heider @ 2011-08-01 20:56 UTC (permalink / raw)
To: Geert Uytterhoeven; +Cc: Geoff Levand, cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <CAMuHMdUgK7ZkaNA+DVg0_TL8fz+AfEOTZqW5WdOY5ybwSP=hcA@mail.gmail.com>
On Mon, Aug 1, 2011 at 10:29 PM, Geert Uytterhoeven
<geert@linux-m68k.org> wrote:
> On Mon, Aug 1, 2011 at 22:02, Andre Heider <a.heider@gmail.com> wrote:
>> The region fields used by the align checks are set in
>> ps3stor_setup(), so move those after that call.
>
> Are you sure?
> Aren't they set in
> arch/powerpc/platforms/ps3/device-init.c:ps3_setup_storage_dev()?
Hm right, unfortunate commit message... :)
dev->region_idx is set in ps3stor_probe_access(), which is called from
ps3stor_setup().
So the code always checked the first region, where it should check the
one beeing used.
Will fix the commit message.
Thanks
^ permalink raw reply
* Re: [PATCH 10/15] ps3stor_lib: Add support for multiple regions
From: Geert Uytterhoeven @ 2011-08-01 20:35 UTC (permalink / raw)
To: Andre Heider; +Cc: Geoff Levand, cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-11-git-send-email-a.heider@gmail.com>
On Mon, Aug 1, 2011 at 22:03, Andre Heider <a.heider@gmail.com> wrote:
> Users (ps3disk, ps3flash and ps3rom) retain the old behavior. That is:
> they still only provide access to the first accessible region.
>
> Signed-off-by: Andre Heider <a.heider@gmail.com>
> ---
> =C2=A0arch/powerpc/include/asm/ps3stor.h | =C2=A0 =C2=A04 ++--
> =C2=A0drivers/block/ps3disk.c =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| =
=C2=A0 15 +++++++++++++--
> =C2=A0drivers/char/ps3flash.c =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| =
=C2=A0 23 +++++++++++++++++------
> =C2=A0drivers/ps3/ps3stor_lib.c =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| =C2=
=A0 25 ++++++++++++-------------
> =C2=A0drivers/scsi/ps3rom.c =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0| =C2=A0 11 +++++++----
> =C2=A05 files changed, 51 insertions(+), 27 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/ps3stor.h b/arch/powerpc/include/as=
m/ps3stor.h
> index d51e53c..9871c05 100644
> --- a/arch/powerpc/include/asm/ps3stor.h
> +++ b/arch/powerpc/include/asm/ps3stor.h
> @@ -51,7 +51,6 @@ struct ps3_storage_device {
>
> =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned int num_regions;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned long accessible_regions;
> - =C2=A0 =C2=A0 =C2=A0 unsigned int region_idx; =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* first accessible region */
> =C2=A0 =C2=A0 =C2=A0 =C2=A0struct ps3_storage_region regions[0]; =C2=A0 /=
* Must be last */
> =C2=A0};
>
> @@ -63,7 +62,8 @@ static inline struct ps3_storage_device *to_ps3_storage=
_device(struct device *de
> =C2=A0extern int ps3stor_setup(struct ps3_storage_device *dev,
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 irq_handler_t handler);
> =C2=A0extern void ps3stor_teardown(struct ps3_storage_device *dev);
> -extern u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u6=
4 lpar,
> +extern u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsigned int region=
_idx, u64 lpar,
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0u64 start_sector=
, u64 sectors,
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int write);
> =C2=A0extern u64 ps3stor_send_command(struct ps3_storage_device *dev, u64=
cmd,
> diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
> index 8e1ce2e..96e00ff 100644
> --- a/drivers/block/ps3disk.c
> +++ b/drivers/block/ps3disk.c
> @@ -42,6 +42,7 @@ struct ps3disk_private {
> =C2=A0 =C2=A0 =C2=A0 =C2=A0spinlock_t lock; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0/* Request queue spinlock */
> =C2=A0 =C2=A0 =C2=A0 =C2=A0struct request_queue *queue;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0struct gendisk *gendisk;
> + =C2=A0 =C2=A0 =C2=A0 unsigned int region_idx; =C2=A0 =C2=A0 =C2=A0 =C2=
=A0/* first accessible region */
> =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned int blocking_factor;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0struct request *req;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0u64 raw_capacity;
> @@ -125,7 +126,7 @@ static int ps3disk_submit_request_sg(struct ps3_stora=
ge_device *dev,
> =C2=A0 =C2=A0 =C2=A0 =C2=A0int write =3D rq_data_dir(req), res;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0const char *op =3D write ? "write" : "read";
> =C2=A0 =C2=A0 =C2=A0 =C2=A0u64 start_sector, sectors;
> - =C2=A0 =C2=A0 =C2=A0 unsigned int region_id =3D dev->regions[dev->regio=
n_idx].id;
> + =C2=A0 =C2=A0 =C2=A0 unsigned int region_id =3D dev->regions[priv->regi=
on_idx].id;
>
> =C2=A0#ifdef DEBUG
> =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned int n =3D 0;
> @@ -408,6 +409,7 @@ static int __devinit ps3disk_probe(struct ps3_system_=
bus_device *_dev)
> =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned int devidx;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0struct request_queue *queue;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0struct gendisk *gendisk;
> + =C2=A0 =C2=A0 =C2=A0 unsigned int region_idx;
>
> =C2=A0 =C2=A0 =C2=A0 =C2=A0if (dev->blk_size < 512) {
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0dev_err(&dev->sbd.=
core,
> @@ -482,6 +484,14 @@ static int __devinit ps3disk_probe(struct ps3_system=
_bus_device *_dev)
> =C2=A0 =C2=A0 =C2=A0 =C2=A0}
>
> =C2=A0 =C2=A0 =C2=A0 =C2=A0priv->gendisk =3D gendisk;
> +
> + =C2=A0 =C2=A0 =C2=A0 /* find first accessible region */
> + =C2=A0 =C2=A0 =C2=A0 for (region_idx =3D 0; region_idx < dev->num_regio=
ns; region_idx++)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (test_bit(region_id=
x, &dev->accessible_regions)) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 priv->region_idx =3D region_idx;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 break;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
> +
Why not
priv->region_idx =3D __ffs(dev->accessible_regions);
like the original code in ps3stor_probe_access() used? Cfr. the code
you removed:
> diff --git a/drivers/ps3/ps3stor_lib.c b/drivers/ps3/ps3stor_lib.c
> index af0afa1..5bbc023 100644
> --- a/drivers/ps3/ps3stor_lib.c
> +++ b/drivers/ps3/ps3stor_lib.c
> @@ -124,15 +128,8 @@ static int ps3stor_probe_access(struct ps3_storage_d=
evice *dev)
> =C2=A0 =C2=A0 =C2=A0 =C2=A0n =3D hweight_long(dev->accessible_regions);
> =C2=A0 =C2=A0 =C2=A0 =C2=A0if (n > 1)
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0dev_info(&dev->sbd=
.core,
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0"%s:%u: %lu accessible regions found. Only the first "
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0"one will be used\n",
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0"%s:%u: %lu accessible regions found\n",
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 __func__, __LINE__, n);
> - =C2=A0 =C2=A0 =C2=A0 dev->region_idx =3D __ffs(dev->accessible_regions)=
;
> - =C2=A0 =C2=A0 =C2=A0 dev_info(&dev->sbd.core,
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"First accessibl=
e region has index %u start %llu size %llu\n",
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0dev->region_idx,=
dev->regions[dev->region_idx].start,
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0dev->regions[dev=
->region_idx].size);
> -
> =C2=A0 =C2=A0 =C2=A0 =C2=A0return 0;
> =C2=A0}
>
Same in the other drivers.
Gr{oetje,eeting}s,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k=
.org
In personal conversations with technical people, I call myself a hacker. Bu=
t
when I'm talking to journalists I just say "programmer" or something like t=
hat.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0=C2=A0 =C2=A0=C2=A0 -- Linus Torvalds
^ permalink raw reply
* Re: [Cbe-oss-dev] [PATCH 09/15] ps3: Limit the number of regions per storage device
From: Geert Uytterhoeven @ 2011-08-01 20:30 UTC (permalink / raw)
To: Andre Heider; +Cc: Geoff Levand, cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-10-git-send-email-a.heider@gmail.com>
On Mon, Aug 1, 2011 at 22:03, Andre Heider <a.heider@gmail.com> wrote:
> There can be only 8 regions, add a sanity check
Why can there be only 8 regions?
> Signed-off-by: Andre Heider <a.heider@gmail.com>
> ---
> =C2=A0arch/powerpc/include/asm/ps3stor.h =C2=A0 =C2=A0 =C2=A0 | =C2=A0 =
=C2=A01 +
> =C2=A0arch/powerpc/platforms/ps3/device-init.c | =C2=A0 =C2=A08 ++++++++
> =C2=A02 files changed, 9 insertions(+), 0 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/ps3stor.h b/arch/powerpc/include/as=
m/ps3stor.h
> index 6fcaf71..d51e53c 100644
> --- a/arch/powerpc/include/asm/ps3stor.h
> +++ b/arch/powerpc/include/asm/ps3stor.h
> @@ -25,6 +25,7 @@
>
> =C2=A0#include <asm/ps3.h>
>
> +#define PS3_STORAGE_MAX_REGIONS =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0(8)
>
> =C2=A0struct ps3_storage_region {
> =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned int id;
> diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/plat=
forms/ps3/device-init.c
> index 6c4b583..830d741 100644
> --- a/arch/powerpc/platforms/ps3/device-init.c
> +++ b/arch/powerpc/platforms/ps3/device-init.c
> @@ -349,6 +349,14 @@ static int ps3_setup_storage_dev(const struct ps3_re=
pository_device *repo,
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -ENODEV;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0}
>
> + =C2=A0 =C2=A0 =C2=A0 if (num_regions > PS3_STORAGE_MAX_REGIONS) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 pr_warning("%s:%u: dev=
ice %u:%u reported %u regions, "
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0"limiting to %u\n", __func__, __LINE__,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0num_regions, repo->bus_index, repo->dev_index,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0PS3_STORAGE_MAX_REGIONS);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 num_regions =3D PS3_ST=
ORAGE_MAX_REGIONS;
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> =C2=A0 =C2=A0 =C2=A0 =C2=A0pr_debug("%s:%u: (%u:%u:%u): port %llu blk_siz=
e %llu num_blocks %llu "
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "num_regions %u\n=
", __func__, __LINE__, repo->bus_index,
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 repo->dev_index, =
repo->dev_type, port, blk_size, num_blocks,
Gr{oetje,eeting}s,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k=
.org
In personal conversations with technical people, I call myself a hacker. Bu=
t
when I'm talking to journalists I just say "programmer" or something like t=
hat.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0=C2=A0 =C2=A0=C2=A0 -- Linus Torvalds
^ permalink raw reply
* Re: [Cbe-oss-dev] [PATCH 06/15] ps3flash: Fix region align checks
From: Geert Uytterhoeven @ 2011-08-01 20:29 UTC (permalink / raw)
To: Andre Heider; +Cc: Geoff Levand, cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-7-git-send-email-a.heider@gmail.com>
On Mon, Aug 1, 2011 at 22:02, Andre Heider <a.heider@gmail.com> wrote:
> The region fields used by the align checks are set in
> ps3stor_setup(), so move those after that call.
Are you sure?
Aren't they set in
arch/powerpc/platforms/ps3/device-init.c:ps3_setup_storage_dev()?
> Signed-off-by: Andre Heider <a.heider@gmail.com>
> ---
> =C2=A0drivers/char/ps3flash.c | =C2=A0 30 +++++++++++++++---------------
> =C2=A01 files changed, 15 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c
> index 85c004a..69c734a 100644
> --- a/drivers/char/ps3flash.c
> +++ b/drivers/char/ps3flash.c
> @@ -360,21 +360,6 @@ static int __devinit ps3flash_probe(struct ps3_syste=
m_bus_device *_dev)
> =C2=A0 =C2=A0 =C2=A0 =C2=A0int error;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned long tmp;
>
> - =C2=A0 =C2=A0 =C2=A0 tmp =3D dev->regions[dev->region_idx].start*dev->b=
lk_size;
> - =C2=A0 =C2=A0 =C2=A0 if (tmp % FLASH_BLOCK_SIZE) {
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(&dev->sbd.core=
,
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 "%s:%u region start %lu is not aligned\n", __func__,
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 __LINE__, tmp);
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EINVAL;
> - =C2=A0 =C2=A0 =C2=A0 }
> - =C2=A0 =C2=A0 =C2=A0 tmp =3D dev->regions[dev->region_idx].size*dev->bl=
k_size;
> - =C2=A0 =C2=A0 =C2=A0 if (tmp % FLASH_BLOCK_SIZE) {
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(&dev->sbd.core=
,
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 "%s:%u region size %lu is not aligned\n", __func__,
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 __LINE__, tmp);
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EINVAL;
> - =C2=A0 =C2=A0 =C2=A0 }
> -
> =C2=A0 =C2=A0 =C2=A0 =C2=A0/* use static buffer, kmalloc cannot allocate =
256 KiB */
> =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!ps3flash_bounce_buffer.address)
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -ENODEV;
> @@ -405,6 +390,21 @@ static int __devinit ps3flash_probe(struct ps3_syste=
m_bus_device *_dev)
> =C2=A0 =C2=A0 =C2=A0 =C2=A0if (error)
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto fail_free_pri=
v;
>
> + =C2=A0 =C2=A0 =C2=A0 tmp =3D dev->regions[dev->region_idx].start*dev->b=
lk_size;
> + =C2=A0 =C2=A0 =C2=A0 if (tmp % FLASH_BLOCK_SIZE) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(&dev->sbd.core=
,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 "%s:%u region start %lu is not aligned\n", __func__,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 __LINE__, tmp);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EINVAL;
> + =C2=A0 =C2=A0 =C2=A0 }
> + =C2=A0 =C2=A0 =C2=A0 tmp =3D dev->regions[dev->region_idx].size*dev->bl=
k_size;
> + =C2=A0 =C2=A0 =C2=A0 if (tmp % FLASH_BLOCK_SIZE) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(&dev->sbd.core=
,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 "%s:%u region size %lu is not aligned\n", __func__,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 __LINE__, tmp);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EINVAL;
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> =C2=A0 =C2=A0 =C2=A0 =C2=A0ps3flash_misc.parent =3D &dev->sbd.core;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0error =3D misc_register(&ps3flash_misc);
> =C2=A0 =C2=A0 =C2=A0 =C2=A0if (error) {
Gr{oetje,eeting}s,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k=
.org
In personal conversations with technical people, I call myself a hacker. Bu=
t
when I'm talking to journalists I just say "programmer" or something like t=
hat.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0=C2=A0 =C2=A0=C2=A0 -- Linus Torvalds
^ permalink raw reply
* Re: kvm PCI assignment & VFIO ramblings
From: Alex Williamson @ 2011-08-01 20:27 UTC (permalink / raw)
To: Avi Kivity
Cc: Alexey Kardashevskiy, kvm, Paul Mackerras, David Gibson,
Anthony Liguori, linux-pci@vger.kernel.org, linuxppc-dev
In-Reply-To: <4E356221.6010302@redhat.com>
On Sun, 2011-07-31 at 17:09 +0300, Avi Kivity wrote:
> On 07/30/2011 02:58 AM, Benjamin Herrenschmidt wrote:
> > Due to our paravirt nature, we don't need to masquerade the MSI-X table
> > for example. At all. If the guest configures crap into it, too bad, it
> > can only shoot itself in the foot since the host bridge enforce
> > validation anyways as I explained earlier. Because it's all paravirt, we
> > don't need to "translate" the interrupt vectors& addresses, the guest
> > will call hyercalls to configure things anyways.
>
> So, you have interrupt redirection? That is, MSI-x table values encode
> the vcpu, not pcpu?
>
> Alex, with interrupt redirection, we can skip this as well? Perhaps
> only if the guest enables interrupt redirection?
It's not clear to me how we could skip it. With VT-d, we'd have to
implement an emulated interrupt remapper and hope that the guest picks
unused indexes in the host interrupt remapping table before it could do
anything useful with direct access to the MSI-X table. Maybe AMD IOMMU
makes this easier? Thanks,
Alex
^ permalink raw reply
* [PATCH 15/15] ps3: Add a NOR FLASH driver for PS3s without NAND
From: Andre Heider @ 2011-08-01 20:03 UTC (permalink / raw)
To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>
A gendisk for each accessible region is created, and a default set
of region flags is provided - overwritable via a module param array.
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
arch/powerpc/include/asm/ps3.h | 2 +
arch/powerpc/platforms/ps3/Kconfig | 15 +
arch/powerpc/platforms/ps3/device-init.c | 7 +
arch/powerpc/platforms/ps3/platform.h | 1 +
arch/powerpc/platforms/ps3/system-bus.c | 2 +
drivers/block/Makefile | 1 +
drivers/block/ps3nflash.c | 473 ++++++++++++++++++++++++++++++
7 files changed, 501 insertions(+), 0 deletions(-)
create mode 100644 drivers/block/ps3nflash.c
diff --git a/arch/powerpc/include/asm/ps3.h b/arch/powerpc/include/asm/ps3.h
index 136354a..fe84231 100644
--- a/arch/powerpc/include/asm/ps3.h
+++ b/arch/powerpc/include/asm/ps3.h
@@ -333,6 +333,7 @@ enum ps3_match_id {
PS3_MATCH_ID_SOUND = 9,
PS3_MATCH_ID_GPU = 10,
PS3_MATCH_ID_LPM = 11,
+ PS3_MATCH_ID_STOR_NFLASH = 12,
};
enum ps3_match_sub_id {
@@ -352,6 +353,7 @@ enum ps3_match_sub_id {
#define PS3_MODULE_ALIAS_GPU_FB "ps3:10:1"
#define PS3_MODULE_ALIAS_GPU_RAMDISK "ps3:10:2"
#define PS3_MODULE_ALIAS_LPM "ps3:11:0"
+#define PS3_MODULE_ALIAS_STOR_NFLASH "ps3:12:0"
enum ps3_system_bus_device_type {
PS3_DEVICE_TYPE_IOC0 = 1,
diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig
index f8d865c..9e1f841 100644
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -142,6 +142,21 @@ config PS3_VFLASH
select PS3_FLASH above to add support for FLASH ROM for the
OtherOS LPAR.
+config PS3_NFLASH
+ tristate "PS3 NFLASH Storage Driver"
+ depends on PPC_PS3 && BLOCK && EXPERT
+ select PS3_STORAGE
+ help
+ Include support for the PS3 NOR FLASH Storage.
+
+ This support is required to access the PS3 NOR flash, which can be
+ found on SKU models starting around October 2007.
+
+ The NOR flash contains essential boot loaders. Damaging this area
+ will render the console unbootable.
+
+ If unsure, say N.
+
config PS3_VRAM
tristate "PS3 Video RAM Storage Driver"
depends on FB_PS3=y && BLOCK && m
diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c
index 7a3dbf8..c2fbe33 100644
--- a/arch/powerpc/platforms/ps3/device-init.c
+++ b/arch/powerpc/platforms/ps3/device-init.c
@@ -601,6 +601,13 @@ static int ps3_setup_dynamic_device(const struct ps3_repository_device *repo)
__func__, __LINE__);
break;
+ case PS3_DEV_TYPE_STOR_NFLASH:
+ result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_NFLASH);
+ if (result)
+ pr_debug("%s:%u ps3_setup_storage_dev failed\n",
+ __func__, __LINE__);
+ break;
+
default:
result = 0;
pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__,
diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h
index 1ba15b8..94b5048 100644
--- a/arch/powerpc/platforms/ps3/platform.h
+++ b/arch/powerpc/platforms/ps3/platform.h
@@ -87,6 +87,7 @@ enum ps3_dev_type {
PS3_DEV_TYPE_STOR_ROM = TYPE_ROM, /* 5 */
PS3_DEV_TYPE_SB_GPIO = 6,
PS3_DEV_TYPE_STOR_FLASH = TYPE_RBC, /* 14 */
+ PS3_DEV_TYPE_STOR_NFLASH = 254,
};
int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str,
diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c
index 23083c3..810bbc5 100644
--- a/arch/powerpc/platforms/ps3/system-bus.c
+++ b/arch/powerpc/platforms/ps3/system-bus.c
@@ -174,6 +174,7 @@ int ps3_open_hv_device(struct ps3_system_bus_device *dev)
case PS3_MATCH_ID_STOR_DISK:
case PS3_MATCH_ID_STOR_ROM:
case PS3_MATCH_ID_STOR_FLASH:
+ case PS3_MATCH_ID_STOR_NFLASH:
return ps3_open_hv_device_sb(dev);
case PS3_MATCH_ID_SOUND:
@@ -212,6 +213,7 @@ int ps3_close_hv_device(struct ps3_system_bus_device *dev)
case PS3_MATCH_ID_STOR_DISK:
case PS3_MATCH_ID_STOR_ROM:
case PS3_MATCH_ID_STOR_FLASH:
+ case PS3_MATCH_ID_STOR_NFLASH:
return ps3_close_hv_device_sb(dev);
case PS3_MATCH_ID_SOUND:
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 8b02899..ab2bb06 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_BLK_DEV_FD) += floppy.o
obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o
obj-$(CONFIG_PS3_DISK) += ps3disk.o
obj-$(CONFIG_PS3_VFLASH) += ps3vflash.o
+obj-$(CONFIG_PS3_NFLASH) += ps3nflash.o
obj-$(CONFIG_PS3_VRAM) += ps3vram.o
obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o
obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o
diff --git a/drivers/block/ps3nflash.c b/drivers/block/ps3nflash.c
new file mode 100644
index 0000000..5a7127e
--- /dev/null
+++ b/drivers/block/ps3nflash.c
@@ -0,0 +1,473 @@
+/*
+ * PS3 NOR FLASH Storage Driver
+ *
+ * Copyright (C) 2011 Andre Heider <a.heider@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/ata.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+
+#include <asm/lv1call.h>
+#include <asm/ps3stor.h>
+#include <asm/firmware.h>
+
+
+#define DEVICE_NAME "ps3nflash"
+
+#define BOUNCE_SIZE (64*1024)
+
+#define PS3NFLASH_MAX_DISKS 16
+#define PS3NFLASH_MINORS 16
+
+
+#define PS3NFLASH_NAME "ps3n%c%c"
+
+
+static unsigned int region_flags[] = {
+ PS3_STORAGE_FLAG_SKIP_ACL,
+ PS3_STORAGE_FLAG_SKIP_ACL,
+ PS3_STORAGE_FLAG_SKIP_ACL,
+ PS3_STORAGE_FLAG_SKIP_ACL,
+ PS3_STORAGE_FLAG_DEFAULT,
+ PS3_STORAGE_FLAG_DEFAULT,
+ PS3_STORAGE_FLAG_DEFAULT,
+ PS3_STORAGE_FLAG_DEFAULT,
+};
+module_param_array(region_flags, uint, NULL, S_IRUGO);
+MODULE_PARM_DESC(region_flags, "Region flags");
+
+struct ps3nflash_private {
+ spinlock_t lock; /* Request queue spinlock */
+ struct request_queue *queue;
+ unsigned int blocking_factor;
+ struct request *req;
+ unsigned int devidx;
+ struct gendisk *gendisk[0]; /* Must be last */
+};
+
+static int ps3nflash_major;
+
+
+static const struct block_device_operations ps3nflash_fops = {
+ .owner = THIS_MODULE,
+};
+
+
+static void ps3nflash_scatter_gather(struct ps3_storage_device *dev,
+ struct request *req, int gather)
+{
+ unsigned int offset = 0;
+ struct req_iterator iter;
+ struct bio_vec *bvec;
+ unsigned int i = 0;
+ size_t size;
+ void *buf;
+
+ rq_for_each_segment(bvec, req, iter) {
+ unsigned long flags;
+ dev_dbg(&dev->sbd.core,
+ "%s:%u: bio %u: %u segs %u sectors from %lu\n",
+ __func__, __LINE__, i, bio_segments(iter.bio),
+ bio_sectors(iter.bio), iter.bio->bi_sector);
+
+ size = bvec->bv_len;
+ buf = bvec_kmap_irq(bvec, &flags);
+ if (gather)
+ memcpy(dev->bounce_buf+offset, buf, size);
+ else
+ memcpy(buf, dev->bounce_buf+offset, size);
+ offset += size;
+ flush_kernel_dcache_page(bvec->bv_page);
+ bvec_kunmap_irq(buf, &flags);
+ i++;
+ }
+}
+
+static int ps3nflash_submit_request_sg(struct ps3_storage_device *dev,
+ struct request *req)
+{
+ struct ps3nflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+ int write = rq_data_dir(req), res;
+ const char *op = write ? "write" : "read";
+ u64 start_sector, sectors;
+ unsigned int region_idx = MINOR(disk_devt(req->rq_disk)) &
+ (PS3NFLASH_MINORS - 1);
+ unsigned int region_id = dev->regions[region_idx].id;
+ u64 flags = dev->regions[region_idx].flags;
+
+#ifdef DEBUG
+ unsigned int n = 0;
+ struct bio_vec *bv;
+ struct req_iterator iter;
+
+ rq_for_each_segment(bv, req, iter)
+ n++;
+ dev_dbg(&dev->sbd.core,
+ "%s:%u: %s req has %u bvecs for %u sectors\n",
+ __func__, __LINE__, op, n, blk_rq_sectors(req));
+#endif
+
+ start_sector = blk_rq_pos(req) * priv->blocking_factor;
+ sectors = blk_rq_sectors(req) * priv->blocking_factor;
+ dev_dbg(&dev->sbd.core, "%s:%u: %s %llu sectors starting at %llu\n",
+ __func__, __LINE__, op, sectors, start_sector);
+
+ if (write) {
+ ps3nflash_scatter_gather(dev, req, 1);
+
+ res = lv1_storage_write(dev->sbd.dev_id, region_id,
+ start_sector, sectors, flags,
+ dev->bounce_lpar, &dev->tag);
+ } else {
+ res = lv1_storage_read(dev->sbd.dev_id, region_id,
+ start_sector, sectors, flags,
+ dev->bounce_lpar, &dev->tag);
+ }
+ if (res) {
+ dev_err(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__,
+ __LINE__, op, res);
+ __blk_end_request_all(req, -EIO);
+ return 0;
+ }
+
+ priv->req = req;
+ return 1;
+}
+
+static void ps3nflash_do_request(struct ps3_storage_device *dev,
+ struct request_queue *q)
+{
+ struct request *req;
+
+ dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
+
+ while ((req = blk_fetch_request(q))) {
+ if (req->cmd_type == REQ_TYPE_FS) {
+ if (ps3nflash_submit_request_sg(dev, req))
+ break;
+ } else {
+ blk_dump_rq_flags(req, DEVICE_NAME " bad request");
+ __blk_end_request_all(req, -EIO);
+ continue;
+ }
+ }
+}
+
+static void ps3nflash_request(struct request_queue *q)
+{
+ struct ps3_storage_device *dev = q->queuedata;
+ struct ps3nflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+
+ if (priv->req) {
+ dev_dbg(&dev->sbd.core, "%s:%u busy\n", __func__, __LINE__);
+ return;
+ }
+
+ ps3nflash_do_request(dev, q);
+}
+
+static irqreturn_t ps3nflash_interrupt(int irq, void *data)
+{
+ struct ps3_storage_device *dev = data;
+ struct ps3nflash_private *priv;
+ struct request *req;
+ int res, read, error;
+ u64 tag, status;
+ const char *op;
+
+ res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status);
+
+ if (tag != dev->tag)
+ dev_err(&dev->sbd.core,
+ "%s:%u: tag mismatch, got %llx, expected %llx\n",
+ __func__, __LINE__, tag, dev->tag);
+
+ if (res) {
+ dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%llx\n",
+ __func__, __LINE__, res, status);
+ return IRQ_HANDLED;
+ }
+
+ priv = ps3_system_bus_get_drvdata(&dev->sbd);
+ req = priv->req;
+ if (!req) {
+ dev_dbg(&dev->sbd.core,
+ "%s:%u non-block layer request completed\n", __func__,
+ __LINE__);
+ dev->lv1_status = status;
+ complete(&dev->done);
+ return IRQ_HANDLED;
+ }
+
+ read = !rq_data_dir(req);
+ op = read ? "read" : "write";
+
+ if (status) {
+ dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__,
+ __LINE__, op, status);
+ error = -EIO;
+ } else {
+ dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__,
+ __LINE__, op);
+ error = 0;
+ if (read)
+ ps3nflash_scatter_gather(dev, req, 0);
+ }
+
+ spin_lock(&priv->lock);
+ __blk_end_request_all(req, error);
+ priv->req = NULL;
+ ps3nflash_do_request(dev, priv->queue);
+ spin_unlock(&priv->lock);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned long ps3nflash_mask;
+
+static DEFINE_MUTEX(ps3nflash_mask_mutex);
+
+static int __devinit ps3nflash_probe(struct ps3_system_bus_device *_dev)
+{
+ struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+ struct ps3nflash_private *priv;
+ int error;
+ unsigned int devidx;
+ struct request_queue *queue;
+ struct gendisk *gendisk;
+ u64 raw_capacity;
+ unsigned int region_idx;
+
+ if (dev->blk_size < 512) {
+ dev_err(&dev->sbd.core,
+ "%s:%u: cannot handle block size %llu\n", __func__,
+ __LINE__, dev->blk_size);
+ return -EINVAL;
+ }
+
+ BUILD_BUG_ON(PS3NFLASH_MAX_DISKS > BITS_PER_LONG);
+ mutex_lock(&ps3nflash_mask_mutex);
+ devidx = find_first_zero_bit(&ps3nflash_mask, PS3NFLASH_MAX_DISKS);
+ if (devidx >= PS3NFLASH_MAX_DISKS) {
+ dev_err(&dev->sbd.core, "%s:%u: Too many disks\n", __func__,
+ __LINE__);
+ mutex_unlock(&ps3nflash_mask_mutex);
+ return -ENOSPC;
+ }
+ __set_bit(devidx, &ps3nflash_mask);
+ mutex_unlock(&ps3nflash_mask_mutex);
+
+ priv = kzalloc(sizeof(*priv) +
+ dev->num_regions * sizeof(struct gendisk),
+ GFP_KERNEL);
+ if (!priv) {
+ error = -ENOMEM;
+ goto fail;
+ }
+
+ ps3_system_bus_set_drvdata(_dev, priv);
+ spin_lock_init(&priv->lock);
+
+ dev->bounce_size = BOUNCE_SIZE;
+ dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA);
+ if (!dev->bounce_buf) {
+ error = -ENOMEM;
+ goto fail_free_priv;
+ }
+
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
+ dev->regions[region_idx].flags = region_flags[region_idx];
+
+ error = ps3stor_setup(dev, ps3nflash_interrupt);
+ if (error)
+ goto fail_free_bounce;
+
+ priv->devidx = devidx;
+
+ queue = blk_init_queue(ps3nflash_request, &priv->lock);
+ if (!queue) {
+ dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n",
+ __func__, __LINE__);
+ error = -ENOMEM;
+ goto fail_teardown;
+ }
+
+ priv->queue = queue;
+ queue->queuedata = dev;
+
+ blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH);
+
+ blk_queue_max_hw_sectors(queue, dev->bounce_size >> 9);
+ blk_queue_segment_boundary(queue, -1UL);
+ blk_queue_dma_alignment(queue, dev->blk_size-1);
+ blk_queue_logical_block_size(queue, dev->blk_size);
+
+ blk_queue_flush(queue, REQ_FLUSH);
+
+ blk_queue_max_segments(queue, -1);
+ blk_queue_max_segment_size(queue, dev->bounce_size);
+
+ if (test_bit(0, &dev->accessible_regions) == 0) {
+ raw_capacity = 0;
+ for (region_idx = 0; region_idx < dev->num_regions;
+ region_idx++)
+ if (test_bit(region_idx, &dev->accessible_regions))
+ raw_capacity += dev->regions[0].size;
+ } else {
+ raw_capacity = dev->regions[0].size;
+ }
+
+ dev_info(&dev->sbd.core, "%llu MiB\n", raw_capacity >> 11);
+
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+ if (test_bit(region_idx, &dev->accessible_regions) == 0)
+ continue;
+
+ gendisk = alloc_disk(PS3NFLASH_MINORS *
+ PS3_STORAGE_MAX_REGIONS);
+ if (!gendisk) {
+ dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n",
+ __func__, __LINE__);
+ error = -ENOMEM;
+ goto fail_cleanup_queue;
+ }
+
+ priv->gendisk[region_idx] = gendisk;
+ gendisk->major = ps3nflash_major;
+ gendisk->first_minor = devidx * PS3NFLASH_MINORS + region_idx;
+ gendisk->fops = &ps3nflash_fops;
+ gendisk->queue = queue;
+ gendisk->private_data = dev;
+ gendisk->driverfs_dev = &dev->sbd.core;
+
+ snprintf(gendisk->disk_name, sizeof(gendisk->disk_name),
+ PS3NFLASH_NAME, '1' + devidx, 'a' + region_idx);
+
+ priv->blocking_factor = dev->blk_size >> 9;
+ set_capacity(gendisk, dev->regions[region_idx].size *
+ priv->blocking_factor);
+
+ dev_info(&dev->sbd.core,
+ "%s (%lu MiB region)\n",
+ gendisk->disk_name, get_capacity(gendisk) >> 11);
+
+ add_disk(gendisk);
+ }
+
+ return 0;
+
+fail_cleanup_queue:
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
+ if (priv->gendisk[region_idx])
+ del_gendisk(priv->gendisk[region_idx]);
+
+ blk_cleanup_queue(queue);
+fail_teardown:
+ ps3stor_teardown(dev);
+fail_free_bounce:
+ kfree(dev->bounce_buf);
+fail_free_priv:
+ kfree(priv);
+ ps3_system_bus_set_drvdata(_dev, NULL);
+fail:
+ mutex_lock(&ps3nflash_mask_mutex);
+ __clear_bit(devidx, &ps3nflash_mask);
+ mutex_unlock(&ps3nflash_mask_mutex);
+ return error;
+}
+
+static int ps3nflash_remove(struct ps3_system_bus_device *_dev)
+{
+ struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+ struct ps3nflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+ unsigned int region_idx;
+
+ mutex_lock(&ps3nflash_mask_mutex);
+ __clear_bit(priv->devidx, &ps3nflash_mask);
+ mutex_unlock(&ps3nflash_mask_mutex);
+
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+ if (test_bit(region_idx, &dev->accessible_regions) == 0)
+ continue;
+
+ del_gendisk(priv->gendisk[region_idx]);
+ }
+
+ blk_cleanup_queue(priv->queue);
+
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+ if (test_bit(region_idx, &dev->accessible_regions) == 0)
+ continue;
+
+ put_disk(priv->gendisk[region_idx]);
+ }
+
+ ps3stor_teardown(dev);
+ kfree(dev->bounce_buf);
+ kfree(priv);
+ ps3_system_bus_set_drvdata(_dev, NULL);
+ return 0;
+}
+
+static struct ps3_system_bus_driver ps3nflash = {
+ .match_id = PS3_MATCH_ID_STOR_NFLASH,
+ .core.name = DEVICE_NAME,
+ .core.owner = THIS_MODULE,
+ .probe = ps3nflash_probe,
+ .remove = ps3nflash_remove,
+ .shutdown = ps3nflash_remove,
+};
+
+
+static int __init ps3nflash_init(void)
+{
+ int error;
+
+ if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
+ return -ENODEV;
+
+ error = register_blkdev(0, DEVICE_NAME);
+ if (error <= 0) {
+ printk(KERN_ERR "%s:%u: register_blkdev failed %d\n", __func__,
+ __LINE__, error);
+ return error;
+ }
+ ps3nflash_major = error;
+
+ pr_info("%s:%u: registered block device major %d\n", __func__,
+ __LINE__, ps3nflash_major);
+
+ error = ps3_system_bus_driver_register(&ps3nflash);
+ if (error)
+ unregister_blkdev(ps3nflash_major, DEVICE_NAME);
+
+ return error;
+}
+
+static void __exit ps3nflash_exit(void)
+{
+ ps3_system_bus_driver_unregister(&ps3nflash);
+ unregister_blkdev(ps3nflash_major, DEVICE_NAME);
+}
+
+module_init(ps3nflash_init);
+module_exit(ps3nflash_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PS3 NOR FLASH Storage Driver");
+MODULE_AUTHOR("Andre Heider");
+MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_NFLASH);
--
1.7.5.4
^ permalink raw reply related
* [PATCH 14/15] ps3: Add a vflash driver for lpars other than OtherOS
From: Andre Heider @ 2011-08-01 20:03 UTC (permalink / raw)
To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>
This driver refuses to work on OtherOS, and hence complements the
ps3flash driver - which only works on OtherOS.
A gendisk for each accessible region is created, and a default set
of region flags is provided - overwritable via a module param array.
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
arch/powerpc/platforms/ps3/Kconfig | 16 +-
drivers/block/Makefile | 1 +
drivers/block/ps3vflash.c | 508 ++++++++++++++++++++++++++++++++++++
3 files changed, 524 insertions(+), 1 deletions(-)
create mode 100644 drivers/block/ps3vflash.c
diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig
index 5eb956a..f8d865c 100644
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -121,13 +121,27 @@ config PS3_FLASH
This support is required to access the PS3 FLASH ROM, which
contains the boot loader and some boot options.
- This driver only supports the deprecated OtherOS LPAR.
+ This driver only supports the deprecated OtherOS LPAR, select
+ PS3_VFLASH below for all other LPARs.
In general, all users will say Y or M.
As this driver needs a fixed buffer of 256 KiB of memory, it can
be disabled on the kernel command line using "ps3flash=off", to
not allocate this fixed buffer.
+config PS3_VFLASH
+ tristate "PS3 VFLASH Storage Driver"
+ depends on PPC_PS3 && BLOCK
+ select PS3_STORAGE
+ help
+ Include support for the PS3 virtual FLASH Storage.
+
+ This support is required to access the PS3 virtual FLASH ROM, which
+ contains the boot loader and some boot options.
+ This driver only supports LPARs other than the deprecated OtherOS,
+ select PS3_FLASH above to add support for FLASH ROM for the
+ OtherOS LPAR.
+
config PS3_VRAM
tristate "PS3 Video RAM Storage Driver"
depends on FB_PS3=y && BLOCK && m
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 40528ba..8b02899 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_BLK_DEV_SWIM) += swim_mod.o
obj-$(CONFIG_BLK_DEV_FD) += floppy.o
obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o
obj-$(CONFIG_PS3_DISK) += ps3disk.o
+obj-$(CONFIG_PS3_VFLASH) += ps3vflash.o
obj-$(CONFIG_PS3_VRAM) += ps3vram.o
obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o
obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o
diff --git a/drivers/block/ps3vflash.c b/drivers/block/ps3vflash.c
new file mode 100644
index 0000000..a59e9f7
--- /dev/null
+++ b/drivers/block/ps3vflash.c
@@ -0,0 +1,508 @@
+/*
+ * PS3 VFLASH Storage Driver
+ *
+ * Copyright (C) 2011 Andre Heider <a.heider@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/ata.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+
+#include <asm/lv1call.h>
+#include <asm/ps3stor.h>
+#include <asm/firmware.h>
+
+
+#define DEVICE_NAME "ps3vflash"
+
+#define BOUNCE_SIZE (64*1024)
+
+#define PS3VFLASH_MAX_DISKS 16
+#define PS3VFLASH_MINORS 16
+
+
+#define PS3VFLASH_NAME "ps3v%c%c"
+
+
+static unsigned int region_flags[] = {
+ PS3_STORAGE_FLAG_SKIP_ACL | PS3_STORAGE_FLAG_ALT_KEY, /* raw vflash */
+ PS3_STORAGE_FLAG_SKIP_ACL, /* lpar 1 */
+ PS3_STORAGE_FLAG_ALT_KEY, /* dev_flash */
+ PS3_STORAGE_FLAG_ALT_KEY, /* dev_flash2 */
+ PS3_STORAGE_FLAG_ALT_KEY, /* dev_flash3 */
+ PS3_STORAGE_FLAG_DEFAULT, /* otheros */
+ PS3_STORAGE_FLAG_SKIP_ACL, /* unknown */
+ PS3_STORAGE_FLAG_DEFAULT, /* n/a */
+};
+module_param_array(region_flags, uint, NULL, S_IRUGO);
+MODULE_PARM_DESC(region_flags, "Region flags");
+
+struct ps3vflash_private {
+ spinlock_t lock; /* Request queue spinlock */
+ struct request_queue *queue;
+ unsigned int blocking_factor;
+ struct request *req;
+ unsigned int devidx;
+ struct gendisk *gendisk[0]; /* Must be last */
+};
+
+
+#define LV1_STORAGE_ATA_FLUSH_CACHE (0x31)
+
+static int ps3vflash_major;
+
+
+static const struct block_device_operations ps3vflash_fops = {
+ .owner = THIS_MODULE,
+};
+
+
+static void ps3vflash_scatter_gather(struct ps3_storage_device *dev,
+ struct request *req, int gather)
+{
+ unsigned int offset = 0;
+ struct req_iterator iter;
+ struct bio_vec *bvec;
+ unsigned int i = 0;
+ size_t size;
+ void *buf;
+
+ rq_for_each_segment(bvec, req, iter) {
+ unsigned long flags;
+ dev_dbg(&dev->sbd.core,
+ "%s:%u: bio %u: %u segs %u sectors from %lu\n",
+ __func__, __LINE__, i, bio_segments(iter.bio),
+ bio_sectors(iter.bio), iter.bio->bi_sector);
+
+ size = bvec->bv_len;
+ buf = bvec_kmap_irq(bvec, &flags);
+ if (gather)
+ memcpy(dev->bounce_buf+offset, buf, size);
+ else
+ memcpy(buf, dev->bounce_buf+offset, size);
+ offset += size;
+ flush_kernel_dcache_page(bvec->bv_page);
+ bvec_kunmap_irq(buf, &flags);
+ i++;
+ }
+}
+
+static int ps3vflash_submit_request_sg(struct ps3_storage_device *dev,
+ struct request *req)
+{
+ struct ps3vflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+ int write = rq_data_dir(req), res;
+ const char *op = write ? "write" : "read";
+ u64 start_sector, sectors;
+ unsigned int region_idx = MINOR(disk_devt(req->rq_disk)) &
+ (PS3VFLASH_MINORS - 1);
+ unsigned int region_id = dev->regions[region_idx].id;
+ u64 flags = dev->regions[region_idx].flags;
+
+#ifdef DEBUG
+ unsigned int n = 0;
+ struct bio_vec *bv;
+ struct req_iterator iter;
+
+ rq_for_each_segment(bv, req, iter)
+ n++;
+ dev_dbg(&dev->sbd.core,
+ "%s:%u: %s req has %u bvecs for %u sectors\n",
+ __func__, __LINE__, op, n, blk_rq_sectors(req));
+#endif
+
+ start_sector = blk_rq_pos(req) * priv->blocking_factor;
+ sectors = blk_rq_sectors(req) * priv->blocking_factor;
+ dev_dbg(&dev->sbd.core, "%s:%u: %s %llu sectors starting at %llu\n",
+ __func__, __LINE__, op, sectors, start_sector);
+
+ if (write) {
+ ps3vflash_scatter_gather(dev, req, 1);
+
+ res = lv1_storage_write(dev->sbd.dev_id, region_id,
+ start_sector, sectors, flags,
+ dev->bounce_lpar, &dev->tag);
+ } else {
+ res = lv1_storage_read(dev->sbd.dev_id, region_id,
+ start_sector, sectors, flags,
+ dev->bounce_lpar, &dev->tag);
+ }
+ if (res) {
+ dev_err(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__,
+ __LINE__, op, res);
+ __blk_end_request_all(req, -EIO);
+ return 0;
+ }
+
+ priv->req = req;
+ return 1;
+}
+
+static int ps3vflash_submit_flush_request(struct ps3_storage_device *dev,
+ struct request *req)
+{
+ struct ps3vflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+ u64 res;
+
+ dev_dbg(&dev->sbd.core, "%s:%u: flush request\n", __func__, __LINE__);
+
+ res = lv1_storage_send_device_command(dev->sbd.dev_id,
+ LV1_STORAGE_ATA_FLUSH_CACHE,
+ 0, 0, 0, 0, &dev->tag);
+ if (res) {
+ dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%llx\n",
+ __func__, __LINE__, res);
+ __blk_end_request_all(req, -EIO);
+ return 0;
+ }
+
+ priv->req = req;
+ return 1;
+}
+
+static void ps3vflash_do_request(struct ps3_storage_device *dev,
+ struct request_queue *q)
+{
+ struct request *req;
+
+ dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
+
+ while ((req = blk_fetch_request(q))) {
+ if (req->cmd_flags & REQ_FLUSH) {
+ if (ps3vflash_submit_flush_request(dev, req))
+ break;
+ } else if (req->cmd_type == REQ_TYPE_FS) {
+ if (ps3vflash_submit_request_sg(dev, req))
+ break;
+ } else {
+ blk_dump_rq_flags(req, DEVICE_NAME " bad request");
+ __blk_end_request_all(req, -EIO);
+ continue;
+ }
+ }
+}
+
+static void ps3vflash_request(struct request_queue *q)
+{
+ struct ps3_storage_device *dev = q->queuedata;
+ struct ps3vflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+
+ if (priv->req) {
+ dev_dbg(&dev->sbd.core, "%s:%u busy\n", __func__, __LINE__);
+ return;
+ }
+
+ ps3vflash_do_request(dev, q);
+}
+
+static irqreturn_t ps3vflash_interrupt(int irq, void *data)
+{
+ struct ps3_storage_device *dev = data;
+ struct ps3vflash_private *priv;
+ struct request *req;
+ int res, read, error;
+ u64 tag, status;
+ const char *op;
+
+ res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status);
+
+ if (tag != dev->tag)
+ dev_err(&dev->sbd.core,
+ "%s:%u: tag mismatch, got %llx, expected %llx\n",
+ __func__, __LINE__, tag, dev->tag);
+
+ if (res) {
+ dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%llx\n",
+ __func__, __LINE__, res, status);
+ return IRQ_HANDLED;
+ }
+
+ priv = ps3_system_bus_get_drvdata(&dev->sbd);
+ req = priv->req;
+ if (!req) {
+ dev_dbg(&dev->sbd.core,
+ "%s:%u non-block layer request completed\n", __func__,
+ __LINE__);
+ dev->lv1_status = status;
+ complete(&dev->done);
+ return IRQ_HANDLED;
+ }
+
+ if (req->cmd_flags & REQ_FLUSH) {
+ read = 0;
+ op = "flush";
+ } else {
+ read = !rq_data_dir(req);
+ op = read ? "read" : "write";
+ }
+ if (status) {
+ dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__,
+ __LINE__, op, status);
+ error = -EIO;
+ } else {
+ dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__,
+ __LINE__, op);
+ error = 0;
+ if (read)
+ ps3vflash_scatter_gather(dev, req, 0);
+ }
+
+ spin_lock(&priv->lock);
+ __blk_end_request_all(req, error);
+ priv->req = NULL;
+ ps3vflash_do_request(dev, priv->queue);
+ spin_unlock(&priv->lock);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned long ps3vflash_mask;
+
+static DEFINE_MUTEX(ps3vflash_mask_mutex);
+
+static int __devinit ps3vflash_probe(struct ps3_system_bus_device *_dev)
+{
+ struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+ struct ps3vflash_private *priv;
+ int error;
+ unsigned int devidx;
+ struct request_queue *queue;
+ struct gendisk *gendisk;
+ u64 raw_capacity;
+ unsigned int region_idx;
+
+ if (dev->blk_size < 512) {
+ dev_err(&dev->sbd.core,
+ "%s:%u: cannot handle block size %llu\n", __func__,
+ __LINE__, dev->blk_size);
+ return -EINVAL;
+ }
+
+ BUILD_BUG_ON(PS3VFLASH_MAX_DISKS > BITS_PER_LONG);
+ mutex_lock(&ps3vflash_mask_mutex);
+ devidx = find_first_zero_bit(&ps3vflash_mask, PS3VFLASH_MAX_DISKS);
+ if (devidx >= PS3VFLASH_MAX_DISKS) {
+ dev_err(&dev->sbd.core, "%s:%u: Too many disks\n", __func__,
+ __LINE__);
+ mutex_unlock(&ps3vflash_mask_mutex);
+ return -ENOSPC;
+ }
+ __set_bit(devidx, &ps3vflash_mask);
+ mutex_unlock(&ps3vflash_mask_mutex);
+
+ priv = kzalloc(sizeof(*priv) +
+ dev->num_regions * sizeof(struct gendisk),
+ GFP_KERNEL);
+ if (!priv) {
+ error = -ENOMEM;
+ goto fail;
+ }
+
+ ps3_system_bus_set_drvdata(_dev, priv);
+ spin_lock_init(&priv->lock);
+
+ dev->bounce_size = BOUNCE_SIZE;
+ dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA);
+ if (!dev->bounce_buf) {
+ error = -ENOMEM;
+ goto fail_free_priv;
+ }
+
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
+ dev->regions[region_idx].flags = region_flags[region_idx];
+
+ error = ps3stor_setup(dev, ps3vflash_interrupt);
+ if (error)
+ goto fail_free_bounce;
+
+ priv->devidx = devidx;
+
+ queue = blk_init_queue(ps3vflash_request, &priv->lock);
+ if (!queue) {
+ dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n",
+ __func__, __LINE__);
+ error = -ENOMEM;
+ goto fail_teardown;
+ }
+
+ priv->queue = queue;
+ queue->queuedata = dev;
+
+ blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH);
+
+ blk_queue_max_hw_sectors(queue, dev->bounce_size >> 9);
+ blk_queue_segment_boundary(queue, -1UL);
+ blk_queue_dma_alignment(queue, dev->blk_size-1);
+ blk_queue_logical_block_size(queue, dev->blk_size);
+
+ blk_queue_flush(queue, REQ_FLUSH);
+
+ blk_queue_max_segments(queue, -1);
+ blk_queue_max_segment_size(queue, dev->bounce_size);
+
+ if (test_bit(0, &dev->accessible_regions) == 0) {
+ raw_capacity = 0;
+ for (region_idx = 0; region_idx < dev->num_regions;
+ region_idx++)
+ if (test_bit(region_idx, &dev->accessible_regions))
+ raw_capacity += dev->regions[0].size;
+ } else {
+ raw_capacity = dev->regions[0].size;
+ }
+
+ dev_info(&dev->sbd.core, "%llu MiB\n", raw_capacity >> 11);
+
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+ if (test_bit(region_idx, &dev->accessible_regions) == 0)
+ continue;
+
+ gendisk = alloc_disk(PS3VFLASH_MINORS *
+ PS3_STORAGE_MAX_REGIONS);
+ if (!gendisk) {
+ dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n",
+ __func__, __LINE__);
+ error = -ENOMEM;
+ goto fail_cleanup_queue;
+ }
+
+ priv->gendisk[region_idx] = gendisk;
+ gendisk->major = ps3vflash_major;
+ gendisk->first_minor = devidx * PS3VFLASH_MINORS + region_idx;
+ gendisk->fops = &ps3vflash_fops;
+ gendisk->queue = queue;
+ gendisk->private_data = dev;
+ gendisk->driverfs_dev = &dev->sbd.core;
+
+ snprintf(gendisk->disk_name, sizeof(gendisk->disk_name),
+ PS3VFLASH_NAME, '1' + devidx, 'a' + region_idx);
+
+ priv->blocking_factor = dev->blk_size >> 9;
+ set_capacity(gendisk, dev->regions[region_idx].size *
+ priv->blocking_factor);
+
+ dev_info(&dev->sbd.core,
+ "%s (%lu MiB region)\n",
+ gendisk->disk_name, get_capacity(gendisk) >> 11);
+
+ add_disk(gendisk);
+ }
+
+ return 0;
+
+fail_cleanup_queue:
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
+ if (priv->gendisk[region_idx])
+ del_gendisk(priv->gendisk[region_idx]);
+
+ blk_cleanup_queue(queue);
+fail_teardown:
+ ps3stor_teardown(dev);
+fail_free_bounce:
+ kfree(dev->bounce_buf);
+fail_free_priv:
+ kfree(priv);
+ ps3_system_bus_set_drvdata(_dev, NULL);
+fail:
+ mutex_lock(&ps3vflash_mask_mutex);
+ __clear_bit(devidx, &ps3vflash_mask);
+ mutex_unlock(&ps3vflash_mask_mutex);
+ return error;
+}
+
+static int ps3vflash_remove(struct ps3_system_bus_device *_dev)
+{
+ struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+ struct ps3vflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+ unsigned int region_idx;
+
+ mutex_lock(&ps3vflash_mask_mutex);
+ __clear_bit(priv->devidx, &ps3vflash_mask);
+ mutex_unlock(&ps3vflash_mask_mutex);
+
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+ if (test_bit(region_idx, &dev->accessible_regions) == 0)
+ continue;
+
+ del_gendisk(priv->gendisk[region_idx]);
+ }
+
+ blk_cleanup_queue(priv->queue);
+
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+ if (test_bit(region_idx, &dev->accessible_regions) == 0)
+ continue;
+
+ put_disk(priv->gendisk[region_idx]);
+ }
+
+ ps3stor_teardown(dev);
+ kfree(dev->bounce_buf);
+ kfree(priv);
+ ps3_system_bus_set_drvdata(_dev, NULL);
+ return 0;
+}
+
+static struct ps3_system_bus_driver ps3vflash = {
+ .match_id = PS3_MATCH_ID_STOR_FLASH,
+ .core.name = DEVICE_NAME,
+ .core.owner = THIS_MODULE,
+ .probe = ps3vflash_probe,
+ .remove = ps3vflash_remove,
+ .shutdown = ps3vflash_remove,
+};
+
+
+static int __init ps3vflash_init(void)
+{
+ int error;
+
+ if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
+ return -ENODEV;
+
+ if (ps3_get_ss_laid() == PS3_SS_LAID_OTHEROS)
+ return -ENODEV;
+
+ error = register_blkdev(0, DEVICE_NAME);
+ if (error <= 0) {
+ printk(KERN_ERR "%s:%u: register_blkdev failed %d\n", __func__,
+ __LINE__, error);
+ return error;
+ }
+ ps3vflash_major = error;
+
+ pr_info("%s:%u: registered block device major %d\n", __func__,
+ __LINE__, ps3vflash_major);
+
+ error = ps3_system_bus_driver_register(&ps3vflash);
+ if (error)
+ unregister_blkdev(ps3vflash_major, DEVICE_NAME);
+
+ return error;
+}
+
+static void __exit ps3vflash_exit(void)
+{
+ ps3_system_bus_driver_unregister(&ps3vflash);
+ unregister_blkdev(ps3vflash_major, DEVICE_NAME);
+}
+
+module_init(ps3vflash_init);
+module_exit(ps3vflash_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PS3 VFLASH Storage Driver");
+MODULE_AUTHOR("Andre Heider");
+MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_FLASH);
--
1.7.5.4
^ permalink raw reply related
* [PATCH 13/15] ps3disk: Use region flags
From: Andre Heider @ 2011-08-01 20:03 UTC (permalink / raw)
To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>
Provide a set of default region flags and make them overwritable via
a module parameter array.
Set PS3_STORAGE_FLAG_SKIP_ACL for region 0, so it can be accessed
from the GameOS lpar.
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
drivers/block/ps3disk.c | 16 ++++++++++++++++
1 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
index 30dae10..483806e 100644
--- a/drivers/block/ps3disk.c
+++ b/drivers/block/ps3disk.c
@@ -39,6 +39,19 @@
#define PS3DISK_NAME "ps3d%c%c"
+static unsigned int region_flags[] = {
+ PS3_STORAGE_FLAG_SKIP_ACL, /* raw disk */
+ PS3_STORAGE_FLAG_DEFAULT, /* core os */
+ PS3_STORAGE_FLAG_DEFAULT, /* gameos pup */
+ PS3_STORAGE_FLAG_DEFAULT, /* n/a */
+ PS3_STORAGE_FLAG_DEFAULT, /* n/a */
+ PS3_STORAGE_FLAG_DEFAULT, /* n/a */
+ PS3_STORAGE_FLAG_DEFAULT, /* n/a */
+ PS3_STORAGE_FLAG_DEFAULT, /* n/a */
+};
+module_param_array(region_flags, uint, NULL, S_IRUGO);
+MODULE_PARM_DESC(region_flags, "Region flags");
+
struct ps3disk_private {
spinlock_t lock; /* Request queue spinlock */
struct request_queue *queue;
@@ -453,6 +466,9 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
goto fail_free_priv;
}
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
+ dev->regions[region_idx].flags = region_flags[region_idx];
+
error = ps3stor_setup(dev, ps3disk_interrupt);
if (error)
goto fail_free_bounce;
--
1.7.5.4
^ permalink raw reply related
* [PATCH 12/15] ps3stor_lib: Add support for storage access flags
From: Andre Heider @ 2011-08-01 20:03 UTC (permalink / raw)
To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>
Users can now set the access flags in the region struct. This is
required for accessing the first region, or selecting an alternative
decryption key for the vflash partitions.
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
arch/powerpc/include/asm/ps3stor.h | 8 +++++++-
arch/powerpc/platforms/ps3/device-init.c | 1 +
drivers/block/ps3disk.c | 5 +++--
drivers/ps3/ps3stor_lib.c | 5 +++--
4 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/arch/powerpc/include/asm/ps3stor.h b/arch/powerpc/include/asm/ps3stor.h
index 9871c05..f29aa37 100644
--- a/arch/powerpc/include/asm/ps3stor.h
+++ b/arch/powerpc/include/asm/ps3stor.h
@@ -25,12 +25,18 @@
#include <asm/ps3.h>
-#define PS3_STORAGE_MAX_REGIONS (8)
+#define PS3_STORAGE_MAX_REGIONS (8)
+
+#define PS3_STORAGE_FLAG_DEFAULT (0)
+#define PS3_STORAGE_FLAG_SKIP_ACL (1 << 1)
+#define PS3_STORAGE_FLAG_ALT_KEY (1 << 2)
+#define PS3_STORAGE_FLAG_UNENCRYPTED (1 << 5)
struct ps3_storage_region {
unsigned int id;
u64 start;
u64 size;
+ u64 flags;
};
struct ps3_storage_device {
diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c
index 830d741..7a3dbf8 100644
--- a/arch/powerpc/platforms/ps3/device-init.c
+++ b/arch/powerpc/platforms/ps3/device-init.c
@@ -409,6 +409,7 @@ static int ps3_setup_storage_dev(const struct ps3_repository_device *repo,
p->regions[i].id = id;
p->regions[i].start = start;
p->regions[i].size = size;
+ p->regions[i].flags = PS3_STORAGE_FLAG_DEFAULT;
}
result = ps3_system_bus_device_register(&p->sbd);
diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
index cba8b45..30dae10 100644
--- a/drivers/block/ps3disk.c
+++ b/drivers/block/ps3disk.c
@@ -130,6 +130,7 @@ static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
unsigned int region_idx = MINOR(disk_devt(req->rq_disk)) &
(PS3DISK_MINORS - 1);
unsigned int region_id = dev->regions[region_idx].id;
+ u64 flags = dev->regions[region_idx].flags;
#ifdef DEBUG
unsigned int n = 0;
@@ -152,11 +153,11 @@ static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
ps3disk_scatter_gather(dev, req, 1);
res = lv1_storage_write(dev->sbd.dev_id, region_id,
- start_sector, sectors, 0,
+ start_sector, sectors, flags,
dev->bounce_lpar, &dev->tag);
} else {
res = lv1_storage_read(dev->sbd.dev_id, region_id,
- start_sector, sectors, 0,
+ start_sector, sectors, flags,
dev->bounce_lpar, &dev->tag);
}
if (res) {
diff --git a/drivers/ps3/ps3stor_lib.c b/drivers/ps3/ps3stor_lib.c
index 5bbc023..8bb54ac 100644
--- a/drivers/ps3/ps3stor_lib.c
+++ b/drivers/ps3/ps3stor_lib.c
@@ -275,6 +275,7 @@ u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev,
u64 start_sector, u64 sectors, int write)
{
unsigned int region_id = dev->regions[region_idx].id;
+ u64 flags = dev->regions[region_idx].flags;
const char *op = write ? "write" : "read";
int res;
@@ -283,10 +284,10 @@ u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev,
init_completion(&dev->done);
res = write ? lv1_storage_write(dev->sbd.dev_id, region_id,
- start_sector, sectors, 0, lpar,
+ start_sector, sectors, flags, lpar,
&dev->tag)
: lv1_storage_read(dev->sbd.dev_id, region_id,
- start_sector, sectors, 0, lpar,
+ start_sector, sectors, flags, lpar,
&dev->tag);
if (res) {
dev_dbg(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__,
--
1.7.5.4
^ permalink raw reply related
* [PATCH 11/15] ps3disk: Provide a gendisk per accessible region
From: Andre Heider @ 2011-08-01 20:03 UTC (permalink / raw)
To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>
This changes the behavior to name the block devices for lpars
other than OtherOS. Instead of a single disk with an alphanumeric
suffix (/dev/ps3da), disks are now numeric, while each
accessible region gets its own alphanumeric suffix:
/dev/ps3d1a
/dev/ps3d1b
The old behavior for OtherOS is kept:
- only one region will be exposed as block device
- the block device name stays the same
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
drivers/block/ps3disk.c | 118 ++++++++++++++++++++++++++++++----------------
1 files changed, 77 insertions(+), 41 deletions(-)
diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
index 96e00ff..cba8b45 100644
--- a/drivers/block/ps3disk.c
+++ b/drivers/block/ps3disk.c
@@ -35,18 +35,19 @@
#define PS3DISK_MINORS 16
-#define PS3DISK_NAME "ps3d%c"
+#define PS3DISK_NAME_OTHEROS "ps3d%c"
+#define PS3DISK_NAME "ps3d%c%c"
struct ps3disk_private {
spinlock_t lock; /* Request queue spinlock */
struct request_queue *queue;
- struct gendisk *gendisk;
- unsigned int region_idx; /* first accessible region */
unsigned int blocking_factor;
struct request *req;
+ unsigned int devidx;
u64 raw_capacity;
unsigned char model[ATA_ID_PROD_LEN+1];
+ struct gendisk *gendisk[0]; /* Must be last */
};
@@ -126,7 +127,9 @@ static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
int write = rq_data_dir(req), res;
const char *op = write ? "write" : "read";
u64 start_sector, sectors;
- unsigned int region_id = dev->regions[priv->region_idx].id;
+ unsigned int region_idx = MINOR(disk_devt(req->rq_disk)) &
+ (PS3DISK_MINORS - 1);
+ unsigned int region_id = dev->regions[region_idx].id;
#ifdef DEBUG
unsigned int n = 0;
@@ -410,6 +413,7 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
struct request_queue *queue;
struct gendisk *gendisk;
unsigned int region_idx;
+ unsigned int otheros = ps3_get_ss_laid() == PS3_SS_LAID_OTHEROS;
if (dev->blk_size < 512) {
dev_err(&dev->sbd.core,
@@ -430,7 +434,9 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
__set_bit(devidx, &ps3disk_mask);
mutex_unlock(&ps3disk_mask_mutex);
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc(sizeof(*priv) +
+ dev->num_regions * sizeof(struct gendisk),
+ GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto fail;
@@ -450,6 +456,7 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
if (error)
goto fail_free_bounce;
+ priv->devidx = devidx;
ps3disk_identify(dev);
queue = blk_init_queue(ps3disk_request, &priv->lock);
@@ -475,45 +482,60 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
blk_queue_max_segments(queue, -1);
blk_queue_max_segment_size(queue, dev->bounce_size);
- gendisk = alloc_disk(PS3DISK_MINORS);
- if (!gendisk) {
- dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n", __func__,
- __LINE__);
- error = -ENOMEM;
- goto fail_cleanup_queue;
- }
+ dev_info(&dev->sbd.core, "%s (%llu MiB)\n",
+ priv->model, priv->raw_capacity >> 11);
- priv->gendisk = gendisk;
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+ if (test_bit(region_idx, &dev->accessible_regions) == 0)
+ continue;
- /* find first accessible region */
- for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
- if (test_bit(region_idx, &dev->accessible_regions)) {
- priv->region_idx = region_idx;
- break;
+ gendisk = alloc_disk(PS3DISK_MINORS * PS3_STORAGE_MAX_REGIONS);
+ if (!gendisk) {
+ dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n",
+ __func__, __LINE__);
+ error = -ENOMEM;
+ goto fail_cleanup_queue;
}
- gendisk->major = ps3disk_major;
- gendisk->first_minor = devidx * PS3DISK_MINORS;
- gendisk->fops = &ps3disk_fops;
- gendisk->queue = queue;
- gendisk->private_data = dev;
- gendisk->driverfs_dev = &dev->sbd.core;
- snprintf(gendisk->disk_name, sizeof(gendisk->disk_name), PS3DISK_NAME,
- devidx+'a');
- priv->blocking_factor = dev->blk_size >> 9;
- set_capacity(gendisk,
- dev->regions[priv->region_idx].size *
- priv->blocking_factor);
-
- dev_info(&dev->sbd.core,
- "%s is a %s (%llu MiB total, %lu MiB for OtherOS)\n",
- gendisk->disk_name, priv->model, priv->raw_capacity >> 11,
- get_capacity(gendisk) >> 11);
-
- add_disk(gendisk);
+ priv->gendisk[region_idx] = gendisk;
+ gendisk->major = ps3disk_major;
+ gendisk->first_minor = devidx * PS3DISK_MINORS + region_idx;
+ gendisk->fops = &ps3disk_fops;
+ gendisk->queue = queue;
+ gendisk->private_data = dev;
+ gendisk->driverfs_dev = &dev->sbd.core;
+
+ if (otheros) {
+ /* keep the old block device name for OtherOS */
+ snprintf(gendisk->disk_name, sizeof(gendisk->disk_name),
+ PS3DISK_NAME_OTHEROS, 'a' + devidx);
+ } else {
+ snprintf(gendisk->disk_name, sizeof(gendisk->disk_name),
+ PS3DISK_NAME, '1' + devidx, 'a' + region_idx);
+ }
+
+ priv->blocking_factor = dev->blk_size >> 9;
+ set_capacity(gendisk, dev->regions[region_idx].size *
+ priv->blocking_factor);
+
+ dev_info(&dev->sbd.core,
+ "%s (%lu MiB region)\n",
+ gendisk->disk_name, get_capacity(gendisk) >> 11);
+
+ add_disk(gendisk);
+
+ /* keep old behavior for OtherOS - only one region */
+ if (otheros)
+ break;
+ }
+
return 0;
fail_cleanup_queue:
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
+ if (priv->gendisk[region_idx])
+ del_gendisk(priv->gendisk[region_idx]);
+
blk_cleanup_queue(queue);
fail_teardown:
ps3stor_teardown(dev);
@@ -533,14 +555,28 @@ static int ps3disk_remove(struct ps3_system_bus_device *_dev)
{
struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+ unsigned int region_idx;
mutex_lock(&ps3disk_mask_mutex);
- __clear_bit(MINOR(disk_devt(priv->gendisk)) / PS3DISK_MINORS,
- &ps3disk_mask);
+ __clear_bit(priv->devidx, &ps3disk_mask);
mutex_unlock(&ps3disk_mask_mutex);
- del_gendisk(priv->gendisk);
+
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+ if (test_bit(region_idx, &dev->accessible_regions) == 0)
+ continue;
+
+ del_gendisk(priv->gendisk[region_idx]);
+ }
+
blk_cleanup_queue(priv->queue);
- put_disk(priv->gendisk);
+
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+ if (test_bit(region_idx, &dev->accessible_regions) == 0)
+ continue;
+
+ put_disk(priv->gendisk[region_idx]);
+ }
+
dev_notice(&dev->sbd.core, "Synchronizing disk cache\n");
ps3disk_sync_cache(dev);
ps3stor_teardown(dev);
--
1.7.5.4
^ permalink raw reply related
* [PATCH 10/15] ps3stor_lib: Add support for multiple regions
From: Andre Heider @ 2011-08-01 20:03 UTC (permalink / raw)
To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>
Users (ps3disk, ps3flash and ps3rom) retain the old behavior. That is:
they still only provide access to the first accessible region.
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
arch/powerpc/include/asm/ps3stor.h | 4 ++--
drivers/block/ps3disk.c | 15 +++++++++++++--
drivers/char/ps3flash.c | 23 +++++++++++++++++------
drivers/ps3/ps3stor_lib.c | 25 ++++++++++++-------------
drivers/scsi/ps3rom.c | 11 +++++++----
5 files changed, 51 insertions(+), 27 deletions(-)
diff --git a/arch/powerpc/include/asm/ps3stor.h b/arch/powerpc/include/asm/ps3stor.h
index d51e53c..9871c05 100644
--- a/arch/powerpc/include/asm/ps3stor.h
+++ b/arch/powerpc/include/asm/ps3stor.h
@@ -51,7 +51,6 @@ struct ps3_storage_device {
unsigned int num_regions;
unsigned long accessible_regions;
- unsigned int region_idx; /* first accessible region */
struct ps3_storage_region regions[0]; /* Must be last */
};
@@ -63,7 +62,8 @@ static inline struct ps3_storage_device *to_ps3_storage_device(struct device *de
extern int ps3stor_setup(struct ps3_storage_device *dev,
irq_handler_t handler);
extern void ps3stor_teardown(struct ps3_storage_device *dev);
-extern u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar,
+extern u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev,
+ unsigned int region_idx, u64 lpar,
u64 start_sector, u64 sectors,
int write);
extern u64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd,
diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
index 8e1ce2e..96e00ff 100644
--- a/drivers/block/ps3disk.c
+++ b/drivers/block/ps3disk.c
@@ -42,6 +42,7 @@ struct ps3disk_private {
spinlock_t lock; /* Request queue spinlock */
struct request_queue *queue;
struct gendisk *gendisk;
+ unsigned int region_idx; /* first accessible region */
unsigned int blocking_factor;
struct request *req;
u64 raw_capacity;
@@ -125,7 +126,7 @@ static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
int write = rq_data_dir(req), res;
const char *op = write ? "write" : "read";
u64 start_sector, sectors;
- unsigned int region_id = dev->regions[dev->region_idx].id;
+ unsigned int region_id = dev->regions[priv->region_idx].id;
#ifdef DEBUG
unsigned int n = 0;
@@ -408,6 +409,7 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
unsigned int devidx;
struct request_queue *queue;
struct gendisk *gendisk;
+ unsigned int region_idx;
if (dev->blk_size < 512) {
dev_err(&dev->sbd.core,
@@ -482,6 +484,14 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
}
priv->gendisk = gendisk;
+
+ /* find first accessible region */
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
+ if (test_bit(region_idx, &dev->accessible_regions)) {
+ priv->region_idx = region_idx;
+ break;
+ }
+
gendisk->major = ps3disk_major;
gendisk->first_minor = devidx * PS3DISK_MINORS;
gendisk->fops = &ps3disk_fops;
@@ -492,7 +502,8 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
devidx+'a');
priv->blocking_factor = dev->blk_size >> 9;
set_capacity(gendisk,
- dev->regions[dev->region_idx].size*priv->blocking_factor);
+ dev->regions[priv->region_idx].size *
+ priv->blocking_factor);
dev_info(&dev->sbd.core,
"%s is a %s (%llu MiB total, %lu MiB for OtherOS)\n",
diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c
index b1e8659..47b1dc7 100644
--- a/drivers/char/ps3flash.c
+++ b/drivers/char/ps3flash.c
@@ -36,6 +36,7 @@
struct ps3flash_private {
struct mutex mutex; /* Bounce buffer mutex */
u64 chunk_sectors;
+ unsigned int region_idx; /* first accessible region */
int tag; /* Start sector of buffer, -1 if invalid */
bool dirty;
};
@@ -46,7 +47,8 @@ static int ps3flash_read_write_sectors(struct ps3_storage_device *dev,
u64 start_sector, int write)
{
struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
- u64 res = ps3stor_read_write_sectors(dev, dev->bounce_lpar,
+ u64 res = ps3stor_read_write_sectors(dev, priv->region_idx,
+ dev->bounce_lpar,
start_sector, priv->chunk_sectors,
write);
if (res) {
@@ -98,6 +100,7 @@ static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector)
static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin)
{
struct ps3_storage_device *dev = ps3flash_dev;
+ struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
loff_t res;
mutex_lock(&file->f_mapping->host->i_mutex);
@@ -106,7 +109,7 @@ static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin)
offset += file->f_pos;
break;
case 2:
- offset += dev->regions[dev->region_idx].size*dev->blk_size;
+ offset += dev->regions[priv->region_idx].size*dev->blk_size;
break;
}
if (offset < 0) {
@@ -136,7 +139,7 @@ static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf,
"%s:%u: Reading %zu bytes at position %lld to U0x%p/K0x%p\n",
__func__, __LINE__, count, *pos, userbuf, kernelbuf);
- size = dev->regions[dev->region_idx].size*dev->blk_size;
+ size = dev->regions[priv->region_idx].size*dev->blk_size;
if (*pos >= size || !count)
return 0;
@@ -205,7 +208,7 @@ static ssize_t ps3flash_write(const char __user *userbuf,
"%s:%u: Writing %zu bytes at position %lld from U0x%p/K0x%p\n",
__func__, __LINE__, count, *pos, userbuf, kernelbuf);
- size = dev->regions[dev->region_idx].size*dev->blk_size;
+ size = dev->regions[priv->region_idx].size*dev->blk_size;
if (*pos >= size || !count)
return 0;
@@ -359,6 +362,7 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev)
struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
struct ps3flash_private *priv;
int error;
+ unsigned int region_idx;
unsigned long tmp;
/* use static buffer, kmalloc cannot allocate 256 KiB */
@@ -391,14 +395,21 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev)
if (error)
goto fail_free_priv;
- tmp = dev->regions[dev->region_idx].start*dev->blk_size;
+ /* find first accessible region */
+ for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
+ if (test_bit(region_idx, &dev->accessible_regions)) {
+ priv->region_idx = region_idx;
+ break;
+ }
+
+ tmp = dev->regions[priv->region_idx].start*dev->blk_size;
if (tmp % FLASH_BLOCK_SIZE) {
dev_err(&dev->sbd.core,
"%s:%u region start %lu is not aligned\n", __func__,
__LINE__, tmp);
return -EINVAL;
}
- tmp = dev->regions[dev->region_idx].size*dev->blk_size;
+ tmp = dev->regions[priv->region_idx].size*dev->blk_size;
if (tmp % FLASH_BLOCK_SIZE) {
dev_err(&dev->sbd.core,
"%s:%u region size %lu is not aligned\n", __func__,
diff --git a/drivers/ps3/ps3stor_lib.c b/drivers/ps3/ps3stor_lib.c
index af0afa1..5bbc023 100644
--- a/drivers/ps3/ps3stor_lib.c
+++ b/drivers/ps3/ps3stor_lib.c
@@ -101,9 +101,8 @@ static int ps3stor_probe_access(struct ps3_storage_device *dev)
"%s:%u: checking accessibility of region %u\n",
__func__, __LINE__, i);
- dev->region_idx = i;
- res = ps3stor_read_write_sectors(dev, dev->bounce_lpar, 0, 1,
- 0);
+ res = ps3stor_read_write_sectors(dev, i, dev->bounce_lpar,
+ 0, 1, 0);
if (res) {
dev_dbg(&dev->sbd.core, "%s:%u: read failed, "
"region %u is not accessible\n", __func__,
@@ -117,6 +116,11 @@ static int ps3stor_probe_access(struct ps3_storage_device *dev)
/* We can access at least one region */
error = 0;
+
+ dev_info(&dev->sbd.core,
+ "Accessible region found: #%u start %llu size %llu\n",
+ i, dev->regions[i].start, dev->regions[i].size);
+
}
if (error)
return error;
@@ -124,15 +128,8 @@ static int ps3stor_probe_access(struct ps3_storage_device *dev)
n = hweight_long(dev->accessible_regions);
if (n > 1)
dev_info(&dev->sbd.core,
- "%s:%u: %lu accessible regions found. Only the first "
- "one will be used\n",
+ "%s:%u: %lu accessible regions found\n",
__func__, __LINE__, n);
- dev->region_idx = __ffs(dev->accessible_regions);
- dev_info(&dev->sbd.core,
- "First accessible region has index %u start %llu size %llu\n",
- dev->region_idx, dev->regions[dev->region_idx].start,
- dev->regions[dev->region_idx].size);
-
return 0;
}
@@ -264,6 +261,7 @@ EXPORT_SYMBOL_GPL(ps3stor_teardown);
/**
* ps3stor_read_write_sectors - read/write from/to a storage device
* @dev: Pointer to a struct ps3_storage_device
+ * @region_idx: Index of the region to access
* @lpar: HV logical partition address
* @start_sector: First sector to read/write
* @sectors: Number of sectors to read/write
@@ -272,10 +270,11 @@ EXPORT_SYMBOL_GPL(ps3stor_teardown);
* Returns 0 for success, -1 in case of failure to submit the command, or
* an LV1 status value in case of other errors
*/
-u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar,
+u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev,
+ unsigned int region_idx, u64 lpar,
u64 start_sector, u64 sectors, int write)
{
- unsigned int region_id = dev->regions[dev->region_idx].id;
+ unsigned int region_id = dev->regions[region_idx].id;
const char *op = write ? "write" : "read";
int res;
diff --git a/drivers/scsi/ps3rom.c b/drivers/scsi/ps3rom.c
index cd178b9..68db03c 100644
--- a/drivers/scsi/ps3rom.c
+++ b/drivers/scsi/ps3rom.c
@@ -39,6 +39,7 @@
#define PS3ROM_MAX_SECTORS (BOUNCE_SIZE >> 9)
+#define PS3ROM_REGION_IDX 0
struct ps3rom_private {
struct ps3_storage_device *dev;
@@ -177,8 +178,9 @@ static int ps3rom_read_request(struct ps3_storage_device *dev,
__func__, __LINE__, sectors, start_sector);
res = lv1_storage_read(dev->sbd.dev_id,
- dev->regions[dev->region_idx].id, start_sector,
- sectors, 0, dev->bounce_lpar, &dev->tag);
+ dev->regions[PS3ROM_REGION_IDX].id,
+ start_sector, sectors, 0,
+ dev->bounce_lpar, &dev->tag);
if (res) {
dev_err(&dev->sbd.core, "%s:%u: read failed %d\n", __func__,
__LINE__, res);
@@ -200,8 +202,9 @@ static int ps3rom_write_request(struct ps3_storage_device *dev,
scsi_sg_copy_to_buffer(cmd, dev->bounce_buf, dev->bounce_size);
res = lv1_storage_write(dev->sbd.dev_id,
- dev->regions[dev->region_idx].id, start_sector,
- sectors, 0, dev->bounce_lpar, &dev->tag);
+ dev->regions[PS3ROM_REGION_IDX].id,
+ start_sector, sectors, 0,
+ dev->bounce_lpar, &dev->tag);
if (res) {
dev_err(&dev->sbd.core, "%s:%u: write failed %d\n", __func__,
__LINE__, res);
--
1.7.5.4
^ permalink raw reply related
* [PATCH 09/15] ps3: Limit the number of regions per storage device
From: Andre Heider @ 2011-08-01 20:03 UTC (permalink / raw)
To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>
There can be only 8 regions, add a sanity check
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
arch/powerpc/include/asm/ps3stor.h | 1 +
arch/powerpc/platforms/ps3/device-init.c | 8 ++++++++
2 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/include/asm/ps3stor.h b/arch/powerpc/include/asm/ps3stor.h
index 6fcaf71..d51e53c 100644
--- a/arch/powerpc/include/asm/ps3stor.h
+++ b/arch/powerpc/include/asm/ps3stor.h
@@ -25,6 +25,7 @@
#include <asm/ps3.h>
+#define PS3_STORAGE_MAX_REGIONS (8)
struct ps3_storage_region {
unsigned int id;
diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c
index 6c4b583..830d741 100644
--- a/arch/powerpc/platforms/ps3/device-init.c
+++ b/arch/powerpc/platforms/ps3/device-init.c
@@ -349,6 +349,14 @@ static int ps3_setup_storage_dev(const struct ps3_repository_device *repo,
return -ENODEV;
}
+ if (num_regions > PS3_STORAGE_MAX_REGIONS) {
+ pr_warning("%s:%u: device %u:%u reported %u regions, "
+ "limiting to %u\n", __func__, __LINE__,
+ num_regions, repo->bus_index, repo->dev_index,
+ PS3_STORAGE_MAX_REGIONS);
+ num_regions = PS3_STORAGE_MAX_REGIONS;
+ }
+
pr_debug("%s:%u: (%u:%u:%u): port %llu blk_size %llu num_blocks %llu "
"num_regions %u\n", __func__, __LINE__, repo->bus_index,
repo->dev_index, repo->dev_type, port, blk_size, num_blocks,
--
1.7.5.4
^ permalink raw reply related
* [PATCH 08/15] ps3: Only prealloc the flash bounce buffer for the OtherOS lpar
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>
It's only used by the ps3flash driver, which only supports the
OtherOS lpar.
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
arch/powerpc/platforms/ps3/setup.c | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c
index f430279..923be8a 100644
--- a/arch/powerpc/platforms/ps3/setup.c
+++ b/arch/powerpc/platforms/ps3/setup.c
@@ -233,7 +233,10 @@ static void __init ps3_setup_arch(void)
#endif
prealloc_ps3fb_videomemory();
- prealloc_ps3flash_bounce_buffer();
+
+ /* the ps3flash driver only works for OtherOS */
+ if (ps3_get_ss_laid() == PS3_SS_LAID_OTHEROS)
+ prealloc_ps3flash_bounce_buffer();
ppc_md.power_save = ps3_power_save;
ps3_os_area_init();
--
1.7.5.4
^ permalink raw reply related
* [PATCH 07/15] ps3flash: Refuse to work in lpars other than OtherOS
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>
The driver implements a character and misc device, meant for the
axed OtherOS to exchange various settings with GameOS.
Since Firmware 3.21 there is no GameOS support anymore to write these
settings, so limit the driver to the OtherOS environment.
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
arch/powerpc/platforms/ps3/Kconfig | 1 +
drivers/char/ps3flash.c | 7 +++++++
2 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig
index 84df5c8..5eb956a 100644
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -121,6 +121,7 @@ config PS3_FLASH
This support is required to access the PS3 FLASH ROM, which
contains the boot loader and some boot options.
+ This driver only supports the deprecated OtherOS LPAR.
In general, all users will say Y or M.
As this driver needs a fixed buffer of 256 KiB of memory, it can
diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c
index 69c734a..b1e8659 100644
--- a/drivers/char/ps3flash.c
+++ b/drivers/char/ps3flash.c
@@ -25,6 +25,7 @@
#include <asm/lv1call.h>
#include <asm/ps3stor.h>
+#include <asm/firmware.h>
#define DEVICE_NAME "ps3flash"
@@ -455,6 +456,12 @@ static struct ps3_system_bus_driver ps3flash = {
static int __init ps3flash_init(void)
{
+ if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
+ return -ENODEV;
+
+ if (ps3_get_ss_laid() != PS3_SS_LAID_OTHEROS)
+ return -ENODEV;
+
return ps3_system_bus_driver_register(&ps3flash);
}
--
1.7.5.4
^ permalink raw reply related
* [PATCH 06/15] ps3flash: Fix region align checks
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>
The region fields used by the align checks are set in
ps3stor_setup(), so move those after that call.
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
drivers/char/ps3flash.c | 30 +++++++++++++++---------------
1 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c
index 85c004a..69c734a 100644
--- a/drivers/char/ps3flash.c
+++ b/drivers/char/ps3flash.c
@@ -360,21 +360,6 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev)
int error;
unsigned long tmp;
- tmp = dev->regions[dev->region_idx].start*dev->blk_size;
- if (tmp % FLASH_BLOCK_SIZE) {
- dev_err(&dev->sbd.core,
- "%s:%u region start %lu is not aligned\n", __func__,
- __LINE__, tmp);
- return -EINVAL;
- }
- tmp = dev->regions[dev->region_idx].size*dev->blk_size;
- if (tmp % FLASH_BLOCK_SIZE) {
- dev_err(&dev->sbd.core,
- "%s:%u region size %lu is not aligned\n", __func__,
- __LINE__, tmp);
- return -EINVAL;
- }
-
/* use static buffer, kmalloc cannot allocate 256 KiB */
if (!ps3flash_bounce_buffer.address)
return -ENODEV;
@@ -405,6 +390,21 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev)
if (error)
goto fail_free_priv;
+ tmp = dev->regions[dev->region_idx].start*dev->blk_size;
+ if (tmp % FLASH_BLOCK_SIZE) {
+ dev_err(&dev->sbd.core,
+ "%s:%u region start %lu is not aligned\n", __func__,
+ __LINE__, tmp);
+ return -EINVAL;
+ }
+ tmp = dev->regions[dev->region_idx].size*dev->blk_size;
+ if (tmp % FLASH_BLOCK_SIZE) {
+ dev_err(&dev->sbd.core,
+ "%s:%u region size %lu is not aligned\n", __func__,
+ __LINE__, tmp);
+ return -EINVAL;
+ }
+
ps3flash_misc.parent = &dev->sbd.core;
error = misc_register(&ps3flash_misc);
if (error) {
--
1.7.5.4
^ permalink raw reply related
* [PATCH 05/15] ps3: Detect the current lpar environment
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>
There is more than the OtherOS lpar the kernel can be launched in.
Detect it by reading the ss laid repository node, and be verbose about
it.
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
arch/powerpc/include/asm/ps3.h | 7 +++++++
arch/powerpc/platforms/ps3/platform.h | 4 ++++
arch/powerpc/platforms/ps3/repository.c | 19 +++++++++++++++++++
arch/powerpc/platforms/ps3/setup.c | 22 ++++++++++++++++++++++
4 files changed, 52 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/include/asm/ps3.h b/arch/powerpc/include/asm/ps3.h
index 7f065e1..136354a 100644
--- a/arch/powerpc/include/asm/ps3.h
+++ b/arch/powerpc/include/asm/ps3.h
@@ -39,6 +39,13 @@ union ps3_firmware_version {
void ps3_get_firmware_version(union ps3_firmware_version *v);
int ps3_compare_firmware_version(u16 major, u16 minor, u16 rev);
+enum ps3_ss_laid {
+ PS3_SS_LAID_GAMEOS = 0x1070000002000001UL,
+ PS3_SS_LAID_OTHEROS = 0x1080000004000001UL,
+};
+
+enum ps3_ss_laid ps3_get_ss_laid(void);
+
/* 'Other OS' area */
enum ps3_param_av_multi_out {
diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h
index 9a196a8..1ba15b8 100644
--- a/arch/powerpc/platforms/ps3/platform.h
+++ b/arch/powerpc/platforms/ps3/platform.h
@@ -232,4 +232,8 @@ int ps3_repository_read_spu_resource_id(unsigned int res_index,
int ps3_repository_read_vuart_av_port(unsigned int *port);
int ps3_repository_read_vuart_sysmgr_port(unsigned int *port);
+/* repository ss info */
+
+int ps3_repository_read_ss_laid(enum ps3_ss_laid *laid);
+
#endif
diff --git a/arch/powerpc/platforms/ps3/repository.c b/arch/powerpc/platforms/ps3/repository.c
index 5e304c2..6fa3e96 100644
--- a/arch/powerpc/platforms/ps3/repository.c
+++ b/arch/powerpc/platforms/ps3/repository.c
@@ -1002,6 +1002,25 @@ int ps3_repository_read_lpm_privileges(unsigned int be_index, u64 *lpar,
lpar, rights);
}
+/**
+ * ps3_repository_read_ss_laid - Read the lpar auth id
+ */
+
+int ps3_repository_read_ss_laid(enum ps3_ss_laid *laid)
+{
+ int result;
+ u64 id, v1;
+
+ lv1_get_logical_partition_id(&id);
+ result = read_node(PS3_LPAR_ID_PME,
+ make_first_field("ss", 0),
+ make_field("laid", 0),
+ id, 0,
+ &v1, NULL);
+ *laid = v1;
+ return result;
+}
+
#if defined(DEBUG)
int ps3_repository_dump_resource_info(const struct ps3_repository_device *repo)
diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c
index 149bea2..f430279 100644
--- a/arch/powerpc/platforms/ps3/setup.c
+++ b/arch/powerpc/platforms/ps3/setup.c
@@ -47,6 +47,7 @@ DEFINE_MUTEX(ps3_gpu_mutex);
EXPORT_SYMBOL_GPL(ps3_gpu_mutex);
static union ps3_firmware_version ps3_firmware_version;
+static enum ps3_ss_laid ps3_ss_laid;
void ps3_get_firmware_version(union ps3_firmware_version *v)
{
@@ -68,6 +69,12 @@ int ps3_compare_firmware_version(u16 major, u16 minor, u16 rev)
}
EXPORT_SYMBOL_GPL(ps3_compare_firmware_version);
+enum ps3_ss_laid ps3_get_ss_laid(void)
+{
+ return ps3_ss_laid;
+}
+EXPORT_SYMBOL_GPL(ps3_get_ss_laid);
+
static void ps3_power_save(void)
{
/*
@@ -192,6 +199,7 @@ static int ps3_set_dabr(unsigned long dabr)
static void __init ps3_setup_arch(void)
{
+ const char *laid_str;
DBG(" -> %s:%d\n", __func__, __LINE__);
@@ -200,6 +208,20 @@ static void __init ps3_setup_arch(void)
ps3_firmware_version.major, ps3_firmware_version.minor,
ps3_firmware_version.rev);
+ ps3_repository_read_ss_laid(&ps3_ss_laid);
+ switch (ps3_ss_laid) {
+ case PS3_SS_LAID_GAMEOS:
+ laid_str = "GameOS";
+ break;
+ case PS3_SS_LAID_OTHEROS:
+ laid_str = "OtherOS";
+ break;
+ default:
+ laid_str = "unknown";
+ break;
+ }
+ printk(KERN_INFO "Running in %s lpar\n", laid_str);
+
ps3_spu_set_platform();
#ifdef CONFIG_SMP
--
1.7.5.4
^ permalink raw reply related
* [PATCH 04/15] ps3: MEMORY_HOTPLUG is not a requirement anymore
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
arch/powerpc/platforms/ps3/Kconfig | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig
index 476d9d9..84df5c8 100644
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -7,7 +7,6 @@ config PPC_PS3
select USB_OHCI_BIG_ENDIAN_MMIO
select USB_ARCH_HAS_EHCI
select USB_EHCI_BIG_ENDIAN_MMIO
- select MEMORY_HOTPLUG
select PPC_PCI_CHOICE
help
This option enables support for the Sony PS3 game console
--
1.7.5.4
^ permalink raw reply related
* [PATCH 03/15] [PS3] Add region 1 memory early
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>
From: Hector Martin <hector@marcansoft.com>
Signed-off-by: Hector Martin <hector@marcansoft.com>
[a.heider: Various cleanups to make checkpatch.pl happy]
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
arch/powerpc/platforms/ps3/mm.c | 62 +++++++--------------------------------
1 files changed, 11 insertions(+), 51 deletions(-)
diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c
index 30bb096..15876d6 100644
--- a/arch/powerpc/platforms/ps3/mm.c
+++ b/arch/powerpc/platforms/ps3/mm.c
@@ -331,57 +331,6 @@ static int ps3_mm_get_devtree_highmem(struct mem_region *r)
}
}
-/**
- * ps3_mm_add_memory - hot add memory
- */
-
-static int __init ps3_mm_add_memory(void)
-{
- int result;
- unsigned long start_addr;
- unsigned long start_pfn;
- unsigned long nr_pages;
-
- if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
- return -ENODEV;
-
- BUG_ON(!mem_init_done);
-
- if (!map.r1.size) {
- DBG("%s:%d: no region 1, not adding memory\n",
- __func__, __LINE__);
- return 0;
- }
-
- start_addr = map.rm.size;
- start_pfn = start_addr >> PAGE_SHIFT;
- nr_pages = (map.r1.size + PAGE_SIZE - 1) >> PAGE_SHIFT;
-
- DBG("%s:%d: start_addr %lxh, start_pfn %lxh, nr_pages %lxh\n",
- __func__, __LINE__, start_addr, start_pfn, nr_pages);
-
- result = add_memory(0, start_addr, map.r1.size);
-
- if (result) {
- pr_err("%s:%d: add_memory failed: (%d)\n",
- __func__, __LINE__, result);
- return result;
- }
-
- memblock_add(start_addr, map.r1.size);
- memblock_analyze();
-
- result = online_pages(start_pfn, nr_pages);
-
- if (result)
- pr_err("%s:%d: online_pages failed: (%d)\n",
- __func__, __LINE__, result);
-
- return result;
-}
-
-device_initcall(ps3_mm_add_memory);
-
/*============================================================================*/
/* dma routines */
/*============================================================================*/
@@ -1280,6 +1229,17 @@ void __init ps3_mm_init(void)
/* correct map.total for the real total amount of memory we use */
map.total = map.rm.size + map.r1.size;
+ if (!map.r1.size) {
+ DBG("%s:%d: no region 1, not adding memory\n",
+ __func__, __LINE__);
+ } else {
+ DBG("%s:%d: adding memory: start %llxh, size %llxh\n",
+ __func__, __LINE__, map.rm.size, map.r1.size);
+
+ memblock_add(map.rm.size, map.r1.size);
+ memblock_analyze();
+ }
+
DBG(" <- %s:%d\n", __func__, __LINE__);
}
--
1.7.5.4
^ permalink raw reply related
* [PATCH 02/15] [PS3] Get lv1 high memory region from devtree
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>
From: Hector Martin <hector@marcansoft.com>
This lets the bootloader preallocate the high lv1 region and pass its
location to the kernel through the devtree. Thus, it can be used to hold
the initrd. If the property doesn't exist, the kernel retains the old
behavior and attempts to allocate the region itself.
Signed-off-by: Hector Martin <hector@marcansoft.com>
[a.heider: Various cleanups to make checkpatch.pl happy]
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
arch/powerpc/platforms/ps3/mm.c | 61 +++++++++++++++++++++++++++++++++++++-
1 files changed, 59 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c
index c204588..30bb096 100644
--- a/arch/powerpc/platforms/ps3/mm.c
+++ b/arch/powerpc/platforms/ps3/mm.c
@@ -110,6 +110,7 @@ struct map {
u64 htab_size;
struct mem_region rm;
struct mem_region r1;
+ int destroy_r1;
};
#define debug_dump_map(x) _debug_dump_map(x, __func__, __LINE__)
@@ -287,6 +288,49 @@ static void ps3_mm_region_destroy(struct mem_region *r)
}
}
+static int ps3_mm_scan_memory(unsigned long node, const char *uname,
+ int depth, void *data)
+{
+ struct mem_region *r = data;
+ void *p;
+ u64 prop[2];
+ unsigned long l;
+ char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+
+ if (type == NULL)
+ return 0;
+ if (strcmp(type, "memory") != 0)
+ return 0;
+
+ p = of_get_flat_dt_prop(node, "sony,lv1-highmem", &l);
+ if (p == NULL)
+ return 0;
+
+ BUG_ON(l != sizeof(prop));
+ memcpy(prop, p, sizeof(prop));
+
+ r->base = prop[0];
+ r->size = prop[1];
+ r->offset = r->base - map.rm.size;
+
+ return -1;
+}
+
+static int ps3_mm_get_devtree_highmem(struct mem_region *r)
+{
+ r->size = r->base = r->offset = 0;
+ of_scan_flat_dt(ps3_mm_scan_memory, r);
+
+ if (r->base && r->size) {
+ DBG("%s:%d got high region from devtree: %llxh %llxh\n",
+ __func__, __LINE__, r->base, r->size);
+ return 0;
+ } else {
+ DBG("%s:%d no high region in devtree...\n", __func__, __LINE__);
+ return -1;
+ }
+}
+
/**
* ps3_mm_add_memory - hot add memory
*/
@@ -303,6 +347,12 @@ static int __init ps3_mm_add_memory(void)
BUG_ON(!mem_init_done);
+ if (!map.r1.size) {
+ DBG("%s:%d: no region 1, not adding memory\n",
+ __func__, __LINE__);
+ return 0;
+ }
+
start_addr = map.rm.size;
start_pfn = start_addr >> PAGE_SHIFT;
nr_pages = (map.r1.size + PAGE_SIZE - 1) >> PAGE_SHIFT;
@@ -1219,7 +1269,13 @@ void __init ps3_mm_init(void)
/* arrange to do this in ps3_mm_add_memory */
- ps3_mm_region_create(&map.r1, map.total - map.rm.size);
+
+ if (ps3_mm_get_devtree_highmem(&map.r1) == 0) {
+ map.destroy_r1 = 0;
+ } else {
+ ps3_mm_region_create(&map.r1, map.total - map.rm.size);
+ map.destroy_r1 = 1;
+ }
/* correct map.total for the real total amount of memory we use */
map.total = map.rm.size + map.r1.size;
@@ -1233,5 +1289,6 @@ void __init ps3_mm_init(void)
void ps3_mm_shutdown(void)
{
- ps3_mm_region_destroy(&map.r1);
+ if (map.destroy_r1)
+ ps3_mm_region_destroy(&map.r1);
}
--
1.7.5.4
^ permalink raw reply related
* [PATCH 01/15] [PS3] Add udbg driver using the PS3 gelic Ethernet device
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>
From: Hector Martin <hector@marcansoft.com>
Signed-off-by: Hector Martin <hector@marcansoft.com>
[a.heider: Various cleanups to make checkpatch.pl happy]
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
arch/powerpc/Kconfig.debug | 8 +
arch/powerpc/include/asm/udbg.h | 1 +
arch/powerpc/kernel/udbg.c | 2 +
arch/powerpc/platforms/ps3/Kconfig | 12 ++
arch/powerpc/platforms/ps3/Makefile | 1 +
arch/powerpc/platforms/ps3/gelic_udbg.c | 272 +++++++++++++++++++++++++++++++
drivers/net/ps3_gelic_net.c | 3 +
drivers/net/ps3_gelic_net.h | 6 +
8 files changed, 305 insertions(+), 0 deletions(-)
create mode 100644 arch/powerpc/platforms/ps3/gelic_udbg.c
diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug
index 2d38a50..47edab6 100644
--- a/arch/powerpc/Kconfig.debug
+++ b/arch/powerpc/Kconfig.debug
@@ -267,6 +267,14 @@ config PPC_EARLY_DEBUG_USBGECKO
Select this to enable early debugging for Nintendo GameCube/Wii
consoles via an external USB Gecko adapter.
+config PPC_EARLY_DEBUG_PS3GELIC
+ bool "Early debugging through the PS3 Ethernet port"
+ depends on PPC_PS3
+ select PS3GELIC_UDBG
+ help
+ Select this to enable early debugging for the PlayStation3 via
+ UDP broadcasts sent out through the Ethernet port.
+
endchoice
config PPC_EARLY_DEBUG_44x_PHYSLOW
diff --git a/arch/powerpc/include/asm/udbg.h b/arch/powerpc/include/asm/udbg.h
index 11ae699..58f0ab9 100644
--- a/arch/powerpc/include/asm/udbg.h
+++ b/arch/powerpc/include/asm/udbg.h
@@ -52,6 +52,7 @@ extern void __init udbg_init_44x_as1(void);
extern void __init udbg_init_40x_realmode(void);
extern void __init udbg_init_cpm(void);
extern void __init udbg_init_usbgecko(void);
+extern void __init udbg_init_ps3gelic(void);
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_UDBG_H */
diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c
index e39cad8..9ea9897 100644
--- a/arch/powerpc/kernel/udbg.c
+++ b/arch/powerpc/kernel/udbg.c
@@ -62,6 +62,8 @@ void __init udbg_early_init(void)
udbg_init_cpm();
#elif defined(CONFIG_PPC_EARLY_DEBUG_USBGECKO)
udbg_init_usbgecko();
+#elif defined(CONFIG_PPC_EARLY_DEBUG_PS3GELIC)
+ udbg_init_ps3gelic();
#endif
#ifdef CONFIG_PPC_EARLY_DEBUG
diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig
index dfe316b..476d9d9 100644
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -148,4 +148,16 @@ config PS3_LPM
profiling support of the Cell processor with programs like
oprofile and perfmon2, then say Y or M, otherwise say N.
+config PS3GELIC_UDBG
+ bool "PS3 udbg output via UDP broadcasts on Ethernet"
+ depends on PPC_PS3
+ help
+ Enables udbg early debugging output by sending broadcast UDP
+ via the Ethernet port (UDP port number 18194).
+
+ This driver uses a trivial implementation and is independent
+ from the main network driver.
+
+ If in doubt, say N here.
+
endmenu
diff --git a/arch/powerpc/platforms/ps3/Makefile b/arch/powerpc/platforms/ps3/Makefile
index ac1bdf8..02b9e63 100644
--- a/arch/powerpc/platforms/ps3/Makefile
+++ b/arch/powerpc/platforms/ps3/Makefile
@@ -2,6 +2,7 @@ obj-y += setup.o mm.o time.o hvcall.o htab.o repository.o
obj-y += interrupt.o exports.o os-area.o
obj-y += system-bus.o
+obj-$(CONFIG_PS3GELIC_UDBG) += gelic_udbg.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_SPU_BASE) += spu.o
obj-y += device-init.o
diff --git a/arch/powerpc/platforms/ps3/gelic_udbg.c b/arch/powerpc/platforms/ps3/gelic_udbg.c
new file mode 100644
index 0000000..9b383bd
--- /dev/null
+++ b/arch/powerpc/platforms/ps3/gelic_udbg.c
@@ -0,0 +1,272 @@
+/*
+ * arch/powerpc/platforms/ps3/gelic_udbg.c
+ *
+ * udbg debug output routine via GELIC UDP broadcasts
+ * Copyright (C) 2010 Hector Martin <hector@marcansoft.com>
+ * Copyright (C) 2011 Andre Heider <a.heider@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ */
+
+#include <asm/io.h>
+#include <asm/udbg.h>
+#include <asm/lv1call.h>
+
+#define GELIC_BUS_ID 1
+#define GELIC_DEVICE_ID 0
+#define GELIC_DEBUG_PORT 18194
+#define GELIC_MAX_MESSAGE_SIZE 1000
+
+#define GELIC_LV1_GET_MAC_ADDRESS 1
+#define GELIC_LV1_GET_VLAN_ID 4
+#define GELIC_LV1_VLAN_TX_ETHERNET_0 2
+
+#define GELIC_DESCR_DMA_STAT_MASK 0xf0000000
+#define GELIC_DESCR_DMA_CARDOWNED 0xa0000000
+
+#define GELIC_DESCR_TX_DMA_IKE 0x00080000
+#define GELIC_DESCR_TX_DMA_NO_CHKSUM 0x00000000
+#define GELIC_DESCR_TX_DMA_FRAME_TAIL 0x00040000
+
+#define GELIC_DESCR_DMA_CMD_NO_CHKSUM (GELIC_DESCR_DMA_CARDOWNED | \
+ GELIC_DESCR_TX_DMA_IKE | \
+ GELIC_DESCR_TX_DMA_NO_CHKSUM)
+
+static u64 bus_addr;
+
+struct gelic_descr {
+ /* as defined by the hardware */
+ u32 buf_addr;
+ u32 buf_size;
+ u32 next_descr_addr;
+ u32 dmac_cmd_status;
+ u32 result_size;
+ u32 valid_size; /* all zeroes for tx */
+ u32 data_status;
+ u32 data_error; /* all zeroes for tx */
+} __attribute__((aligned(32)));
+
+struct debug_block {
+ struct gelic_descr descr;
+ u8 pkt[1520];
+} __packed;
+
+struct ethhdr {
+ u8 dest[6];
+ u8 src[6];
+ u16 type;
+} __packed;
+
+struct vlantag {
+ u16 vlan;
+ u16 subtype;
+} __packed;
+
+struct iphdr {
+ u8 ver_len;
+ u8 dscp_ecn;
+ u16 total_length;
+ u16 ident;
+ u16 frag_off_flags;
+ u8 ttl;
+ u8 proto;
+ u16 checksum;
+ u32 src;
+ u32 dest;
+} __packed;
+
+struct udphdr {
+ u16 src;
+ u16 dest;
+ u16 len;
+ u16 checksum;
+} __packed;
+
+static __iomem struct ethhdr *h_eth;
+static __iomem struct vlantag *h_vlan;
+static __iomem struct iphdr *h_ip;
+static __iomem struct udphdr *h_udp;
+
+static __iomem char *pmsg;
+static __iomem char *pmsgc;
+
+static __iomem struct debug_block dbg __attribute__((aligned(32)));
+
+static int header_size;
+
+static void map_dma_mem(int bus_id, int dev_id, void *start, size_t len,
+ u64 *real_bus_addr)
+{
+ s64 result;
+ u64 real_addr = ((u64)start) & 0x0fffffffffffffffUL;
+ u64 real_end = real_addr + len;
+ u64 map_start = real_addr & ~0xfff;
+ u64 map_end = (real_end + 0xfff) & ~0xfff;
+ u64 bus_addr = 0;
+
+ u64 flags = 0xf800000000000000UL;
+
+ result = lv1_allocate_device_dma_region(bus_id, dev_id,
+ map_end - map_start, 12, 0,
+ &bus_addr);
+ if (result)
+ lv1_panic(0);
+
+ result = lv1_map_device_dma_region(bus_id, dev_id, map_start,
+ bus_addr, map_end - map_start,
+ flags);
+ if (result)
+ lv1_panic(0);
+
+ *real_bus_addr = bus_addr + real_addr - map_start;
+}
+
+static int unmap_dma_mem(int bus_id, int dev_id, u64 bus_addr, size_t len)
+{
+ s64 result;
+ u64 real_bus_addr;
+
+ real_bus_addr = bus_addr & ~0xfff;
+ len += bus_addr - real_bus_addr;
+ len = (len + 0xfff) & ~0xfff;
+
+ result = lv1_unmap_device_dma_region(bus_id, dev_id, real_bus_addr,
+ len);
+ if (result)
+ return result;
+
+ return lv1_free_device_dma_region(bus_id, dev_id, real_bus_addr);
+}
+
+static void gelic_debug_init(void)
+{
+ s64 result;
+ u64 v2;
+ u64 mac;
+ u64 vlan_id;
+
+ result = lv1_open_device(GELIC_BUS_ID, GELIC_DEVICE_ID, 0);
+ if (result)
+ lv1_panic(0);
+
+ map_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID, &dbg, sizeof(dbg),
+ &bus_addr);
+
+ memset(&dbg, 0, sizeof(dbg));
+
+ dbg.descr.buf_addr = bus_addr + offsetof(struct debug_block, pkt);
+
+ wmb();
+
+ result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID,
+ GELIC_LV1_GET_MAC_ADDRESS, 0, 0, 0,
+ &mac, &v2);
+ if (result)
+ lv1_panic(0);
+
+ mac <<= 16;
+
+ h_eth = (struct ethhdr *)dbg.pkt;
+
+ memset(&h_eth->dest, 0xff, 6);
+ memcpy(&h_eth->src, &mac, 6);
+
+ header_size = sizeof(struct ethhdr);
+
+ result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID,
+ GELIC_LV1_GET_VLAN_ID,
+ GELIC_LV1_VLAN_TX_ETHERNET_0, 0, 0,
+ &vlan_id, &v2);
+ if (result == 0) {
+ h_eth->type = 0x8100;
+
+ header_size += sizeof(struct vlantag);
+ h_vlan = (struct vlantag *)(h_eth + 1);
+ h_vlan->vlan = vlan_id;
+ h_vlan->subtype = 0x0800;
+ h_ip = (struct iphdr *)(h_vlan + 1);
+ } else {
+ h_eth->type = 0x0800;
+ h_ip = (struct iphdr *)(h_eth + 1);
+ }
+
+ header_size += sizeof(struct iphdr);
+ h_ip->ver_len = 0x45;
+ h_ip->ttl = 10;
+ h_ip->proto = 0x11;
+ h_ip->src = 0x00000000;
+ h_ip->dest = 0xffffffff;
+
+ header_size += sizeof(struct udphdr);
+ h_udp = (struct udphdr *)(h_ip + 1);
+ h_udp->src = GELIC_DEBUG_PORT;
+ h_udp->dest = GELIC_DEBUG_PORT;
+
+ pmsgc = pmsg = (char *)(h_udp + 1);
+}
+
+static void gelic_debug_shutdown(void)
+{
+ if (bus_addr)
+ unmap_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID,
+ bus_addr, sizeof(dbg));
+ lv1_close_device(GELIC_BUS_ID, GELIC_DEVICE_ID);
+}
+
+static void gelic_sendbuf(int msgsize)
+{
+ u16 *p;
+ u32 sum;
+ int i;
+
+ dbg.descr.buf_size = header_size + msgsize;
+ h_ip->total_length = msgsize + sizeof(struct udphdr) +
+ sizeof(struct iphdr);
+ h_udp->len = msgsize + sizeof(struct udphdr);
+
+ h_ip->checksum = 0;
+ sum = 0;
+ p = (u16 *)h_ip;
+ for (i = 0; i < 5; i++)
+ sum += *p++;
+ h_ip->checksum = ~(sum + (sum >> 16));
+
+ dbg.descr.dmac_cmd_status = GELIC_DESCR_DMA_CMD_NO_CHKSUM |
+ GELIC_DESCR_TX_DMA_FRAME_TAIL;
+ dbg.descr.result_size = 0;
+ dbg.descr.data_status = 0;
+
+ wmb();
+
+ lv1_net_start_tx_dma(GELIC_BUS_ID, GELIC_DEVICE_ID, bus_addr, 0);
+
+ while ((dbg.descr.dmac_cmd_status & GELIC_DESCR_DMA_STAT_MASK) ==
+ GELIC_DESCR_DMA_CARDOWNED)
+ cpu_relax();
+}
+
+static void ps3gelic_udbg_putc(char ch)
+{
+ *pmsgc++ = ch;
+ if (ch == '\n' || (pmsgc-pmsg) >= GELIC_MAX_MESSAGE_SIZE) {
+ gelic_sendbuf(pmsgc-pmsg);
+ pmsgc = pmsg;
+ }
+}
+
+void __init udbg_init_ps3gelic(void)
+{
+ gelic_debug_init();
+ udbg_putc = ps3gelic_udbg_putc;
+}
+
+void udbg_shutdown_ps3gelic(void)
+{
+ udbg_putc = NULL;
+ gelic_debug_shutdown();
+}
+EXPORT_SYMBOL(udbg_shutdown_ps3gelic);
diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c
index 035a676..4488655 100644
--- a/drivers/net/ps3_gelic_net.c
+++ b/drivers/net/ps3_gelic_net.c
@@ -1688,6 +1688,9 @@ static int __devinit ps3_gelic_driver_probe(struct ps3_system_bus_device *dev)
int result;
pr_debug("%s: called\n", __func__);
+
+ udbg_shutdown_ps3gelic();
+
result = ps3_open_hv_device(dev);
if (result) {
diff --git a/drivers/net/ps3_gelic_net.h b/drivers/net/ps3_gelic_net.h
index 07bdc17..57ef974 100644
--- a/drivers/net/ps3_gelic_net.h
+++ b/drivers/net/ps3_gelic_net.h
@@ -360,6 +360,12 @@ static inline void *port_priv(struct gelic_port *port)
return port->priv;
}
+#ifdef CONFIG_PPC_EARLY_DEBUG_PS3GELIC
+extern void udbg_shutdown_ps3gelic(void);
+#else
+static inline void udbg_shutdown_ps3gelic(void) {}
+#endif
+
extern int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask);
/* shared netdev ops */
extern void gelic_card_up(struct gelic_card *card);
--
1.7.5.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox